Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: omap_hsmmc: Change while(); loops with finite version omap_hsmmc: recover from transfer failures omap_hsmmc: only MMC1 allows HCTL.SDVS != 1.8V omap_hsmmc: card detect irq bugfix sdhci: fix led naming mmc_test: fix basic read test s3cmci: Fix hangup in do_pio_write() Revert "sdhci: force high speed capability on some controllers" MMC: fix bug - SDHC card capacity not correct
This commit is contained in:
@@ -584,7 +584,7 @@ static int mmc_blk_probe(struct mmc_card *card)
|
|||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
string_get_size(get_capacity(md->disk) << 9, STRING_UNITS_2,
|
string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
|
||||||
cap_str, sizeof(cap_str));
|
cap_str, sizeof(cap_str));
|
||||||
printk(KERN_INFO "%s: %s %s %s %s\n",
|
printk(KERN_INFO "%s: %s %s %s %s\n",
|
||||||
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
|
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
|
||||||
|
@@ -494,7 +494,7 @@ static int mmc_test_basic_read(struct mmc_test_card *test)
|
|||||||
|
|
||||||
sg_init_one(&sg, test->buffer, 512);
|
sg_init_one(&sg, test->buffer, 512);
|
||||||
|
|
||||||
ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
|
ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@@ -55,6 +55,7 @@
|
|||||||
#define VS30 (1 << 25)
|
#define VS30 (1 << 25)
|
||||||
#define SDVS18 (0x5 << 9)
|
#define SDVS18 (0x5 << 9)
|
||||||
#define SDVS30 (0x6 << 9)
|
#define SDVS30 (0x6 << 9)
|
||||||
|
#define SDVS33 (0x7 << 9)
|
||||||
#define SDVSCLR 0xFFFFF1FF
|
#define SDVSCLR 0xFFFFF1FF
|
||||||
#define SDVSDET 0x00000400
|
#define SDVSDET 0x00000400
|
||||||
#define AUTOIDLE 0x1
|
#define AUTOIDLE 0x1
|
||||||
@@ -375,6 +376,32 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status)
|
|||||||
}
|
}
|
||||||
#endif /* CONFIG_MMC_DEBUG */
|
#endif /* CONFIG_MMC_DEBUG */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MMC controller internal state machines reset
|
||||||
|
*
|
||||||
|
* Used to reset command or data internal state machines, using respectively
|
||||||
|
* SRC or SRD bit of SYSCTL register
|
||||||
|
* Can be called from interrupt context
|
||||||
|
*/
|
||||||
|
static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host,
|
||||||
|
unsigned long bit)
|
||||||
|
{
|
||||||
|
unsigned long i = 0;
|
||||||
|
unsigned long limit = (loops_per_jiffy *
|
||||||
|
msecs_to_jiffies(MMC_TIMEOUT_MS));
|
||||||
|
|
||||||
|
OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
||||||
|
OMAP_HSMMC_READ(host->base, SYSCTL) | bit);
|
||||||
|
|
||||||
|
while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) &&
|
||||||
|
(i++ < limit))
|
||||||
|
cpu_relax();
|
||||||
|
|
||||||
|
if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit)
|
||||||
|
dev_err(mmc_dev(host->mmc),
|
||||||
|
"Timeout waiting on controller reset in %s\n",
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MMC controller IRQ handler
|
* MMC controller IRQ handler
|
||||||
@@ -403,21 +430,17 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
|
|||||||
(status & CMD_CRC)) {
|
(status & CMD_CRC)) {
|
||||||
if (host->cmd) {
|
if (host->cmd) {
|
||||||
if (status & CMD_TIMEOUT) {
|
if (status & CMD_TIMEOUT) {
|
||||||
OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
mmc_omap_reset_controller_fsm(host, SRC);
|
||||||
OMAP_HSMMC_READ(host->base,
|
|
||||||
SYSCTL) | SRC);
|
|
||||||
while (OMAP_HSMMC_READ(host->base,
|
|
||||||
SYSCTL) & SRC)
|
|
||||||
;
|
|
||||||
|
|
||||||
host->cmd->error = -ETIMEDOUT;
|
host->cmd->error = -ETIMEDOUT;
|
||||||
} else {
|
} else {
|
||||||
host->cmd->error = -EILSEQ;
|
host->cmd->error = -EILSEQ;
|
||||||
}
|
}
|
||||||
end_cmd = 1;
|
end_cmd = 1;
|
||||||
}
|
}
|
||||||
if (host->data)
|
if (host->data) {
|
||||||
mmc_dma_cleanup(host);
|
mmc_dma_cleanup(host);
|
||||||
|
mmc_omap_reset_controller_fsm(host, SRD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((status & DATA_TIMEOUT) ||
|
if ((status & DATA_TIMEOUT) ||
|
||||||
(status & DATA_CRC)) {
|
(status & DATA_CRC)) {
|
||||||
@@ -426,12 +449,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
|
|||||||
mmc_dma_cleanup(host);
|
mmc_dma_cleanup(host);
|
||||||
else
|
else
|
||||||
host->data->error = -EILSEQ;
|
host->data->error = -EILSEQ;
|
||||||
OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
mmc_omap_reset_controller_fsm(host, SRD);
|
||||||
OMAP_HSMMC_READ(host->base,
|
|
||||||
SYSCTL) | SRD);
|
|
||||||
while (OMAP_HSMMC_READ(host->base,
|
|
||||||
SYSCTL) & SRD)
|
|
||||||
;
|
|
||||||
end_trans = 1;
|
end_trans = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -456,13 +474,20 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Switch MMC operating voltage
|
* Switch MMC interface voltage ... only relevant for MMC1.
|
||||||
|
*
|
||||||
|
* MMC2 and MMC3 use fixed 1.8V levels, and maybe a transceiver.
|
||||||
|
* The MMC2 transceiver controls are used instead of DAT4..DAT7.
|
||||||
|
* Some chips, like eMMC ones, use internal transceivers.
|
||||||
*/
|
*/
|
||||||
static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
|
static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
|
||||||
{
|
{
|
||||||
u32 reg_val = 0;
|
u32 reg_val = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (host->id != OMAP_MMC1_DEVID)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Disable the clocks */
|
/* Disable the clocks */
|
||||||
clk_disable(host->fclk);
|
clk_disable(host->fclk);
|
||||||
clk_disable(host->iclk);
|
clk_disable(host->iclk);
|
||||||
@@ -485,19 +510,26 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
|
|||||||
OMAP_HSMMC_WRITE(host->base, HCTL,
|
OMAP_HSMMC_WRITE(host->base, HCTL,
|
||||||
OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR);
|
OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR);
|
||||||
reg_val = OMAP_HSMMC_READ(host->base, HCTL);
|
reg_val = OMAP_HSMMC_READ(host->base, HCTL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a MMC dual voltage card is detected, the set_ios fn calls
|
* If a MMC dual voltage card is detected, the set_ios fn calls
|
||||||
* this fn with VDD bit set for 1.8V. Upon card removal from the
|
* this fn with VDD bit set for 1.8V. Upon card removal from the
|
||||||
* slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
|
* slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
|
||||||
*
|
*
|
||||||
* Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is
|
* Cope with a bit of slop in the range ... per data sheets:
|
||||||
* set in HCTL.
|
* - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max,
|
||||||
|
* but recommended values are 1.71V to 1.89V
|
||||||
|
* - "3.0V" for vdds_mmc1/vdds_mmc1a can be up to 3.5V max,
|
||||||
|
* but recommended values are 2.7V to 3.3V
|
||||||
|
*
|
||||||
|
* Board setup code shouldn't permit anything very out-of-range.
|
||||||
|
* TWL4030-family VMMC1 and VSIM regulators are fine (avoiding the
|
||||||
|
* middle range) but VSIM can't power DAT4..DAT7 at more than 3V.
|
||||||
*/
|
*/
|
||||||
if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) ||
|
if ((1 << vdd) <= MMC_VDD_23_24)
|
||||||
((1 << vdd) == MMC_VDD_33_34)))
|
|
||||||
reg_val |= SDVS30;
|
|
||||||
if ((1 << vdd) == MMC_VDD_165_195)
|
|
||||||
reg_val |= SDVS18;
|
reg_val |= SDVS18;
|
||||||
|
else
|
||||||
|
reg_val |= SDVS30;
|
||||||
|
|
||||||
OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
|
OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
|
||||||
|
|
||||||
@@ -517,16 +549,15 @@ static void mmc_omap_detect(struct work_struct *work)
|
|||||||
{
|
{
|
||||||
struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
|
struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
|
||||||
mmc_carddetect_work);
|
mmc_carddetect_work);
|
||||||
|
struct omap_mmc_slot_data *slot = &mmc_slot(host);
|
||||||
|
|
||||||
|
host->carddetect = slot->card_detect(slot->card_detect_irq);
|
||||||
|
|
||||||
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
|
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
|
||||||
if (host->carddetect) {
|
if (host->carddetect) {
|
||||||
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
|
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
|
||||||
} else {
|
} else {
|
||||||
OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
mmc_omap_reset_controller_fsm(host, SRD);
|
||||||
OMAP_HSMMC_READ(host->base, SYSCTL) | SRD);
|
|
||||||
while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD)
|
|
||||||
;
|
|
||||||
|
|
||||||
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
|
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -538,7 +569,6 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
|
|||||||
{
|
{
|
||||||
struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id;
|
struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id;
|
||||||
|
|
||||||
host->carddetect = mmc_slot(host).card_detect(irq);
|
|
||||||
schedule_work(&host->mmc_carddetect_work);
|
schedule_work(&host->mmc_carddetect_work);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
@@ -757,10 +787,14 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
case MMC_POWER_OFF:
|
case MMC_POWER_OFF:
|
||||||
mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
|
mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
|
||||||
/*
|
/*
|
||||||
* Reset bus voltage to 3V if it got set to 1.8V earlier.
|
* Reset interface voltage to 3V if it's 1.8V now;
|
||||||
|
* only relevant on MMC-1, the others always use 1.8V.
|
||||||
|
*
|
||||||
* REVISIT: If we are able to detect cards after unplugging
|
* REVISIT: If we are able to detect cards after unplugging
|
||||||
* a 1.8V card, this code should not be needed.
|
* a 1.8V card, this code should not be needed.
|
||||||
*/
|
*/
|
||||||
|
if (host->id != OMAP_MMC1_DEVID)
|
||||||
|
break;
|
||||||
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
|
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
|
||||||
int vdd = fls(host->mmc->ocr_avail) - 1;
|
int vdd = fls(host->mmc->ocr_avail) - 1;
|
||||||
if (omap_mmc_switch_opcond(host, vdd) != 0)
|
if (omap_mmc_switch_opcond(host, vdd) != 0)
|
||||||
@@ -784,7 +818,9 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (host->id == OMAP_MMC1_DEVID) {
|
if (host->id == OMAP_MMC1_DEVID) {
|
||||||
/* Only MMC1 can operate at 3V/1.8V */
|
/* Only MMC1 can interface at 3V without some flavor
|
||||||
|
* of external transceiver; but they all handle 1.8V.
|
||||||
|
*/
|
||||||
if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
|
if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
|
||||||
(ios->vdd == DUAL_VOLT_OCR_BIT)) {
|
(ios->vdd == DUAL_VOLT_OCR_BIT)) {
|
||||||
/*
|
/*
|
||||||
@@ -1137,7 +1173,9 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
|
|||||||
" level suspend\n");
|
" level suspend\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
|
if (host->id == OMAP_MMC1_DEVID
|
||||||
|
&& !(OMAP_HSMMC_READ(host->base, HCTL)
|
||||||
|
& SDVSDET)) {
|
||||||
OMAP_HSMMC_WRITE(host->base, HCTL,
|
OMAP_HSMMC_WRITE(host->base, HCTL,
|
||||||
OMAP_HSMMC_READ(host->base, HCTL)
|
OMAP_HSMMC_READ(host->base, HCTL)
|
||||||
& SDVSCLR);
|
& SDVSCLR);
|
||||||
|
@@ -329,7 +329,7 @@ static void do_pio_write(struct s3cmci_host *host)
|
|||||||
|
|
||||||
to_ptr = host->base + host->sdidata;
|
to_ptr = host->base + host->sdidata;
|
||||||
|
|
||||||
while ((fifo = fifo_free(host))) {
|
while ((fifo = fifo_free(host)) > 3) {
|
||||||
if (!host->pio_bytes) {
|
if (!host->pio_bytes) {
|
||||||
res = get_data_buffer(host, &host->pio_bytes,
|
res = get_data_buffer(host, &host->pio_bytes,
|
||||||
&host->pio_ptr);
|
&host->pio_ptr);
|
||||||
|
@@ -144,8 +144,7 @@ static int jmicron_probe(struct sdhci_pci_chip *chip)
|
|||||||
SDHCI_QUIRK_32BIT_DMA_SIZE |
|
SDHCI_QUIRK_32BIT_DMA_SIZE |
|
||||||
SDHCI_QUIRK_32BIT_ADMA_SIZE |
|
SDHCI_QUIRK_32BIT_ADMA_SIZE |
|
||||||
SDHCI_QUIRK_RESET_AFTER_REQUEST |
|
SDHCI_QUIRK_RESET_AFTER_REQUEST |
|
||||||
SDHCI_QUIRK_BROKEN_SMALL_PIO |
|
SDHCI_QUIRK_BROKEN_SMALL_PIO;
|
||||||
SDHCI_QUIRK_FORCE_HIGHSPEED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -1636,8 +1636,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||||||
mmc->f_max = host->max_clk;
|
mmc->f_max = host->max_clk;
|
||||||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
|
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
|
||||||
|
|
||||||
if ((caps & SDHCI_CAN_DO_HISPD) ||
|
if (caps & SDHCI_CAN_DO_HISPD)
|
||||||
(host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED))
|
|
||||||
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
|
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
|
||||||
|
|
||||||
mmc->ocr_avail = 0;
|
mmc->ocr_avail = 0;
|
||||||
@@ -1723,7 +1722,9 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SDHCI_USE_LEDS_CLASS
|
#ifdef SDHCI_USE_LEDS_CLASS
|
||||||
host->led.name = mmc_hostname(mmc);
|
snprintf(host->led_name, sizeof(host->led_name),
|
||||||
|
"%s::", mmc_hostname(mmc));
|
||||||
|
host->led.name = host->led_name;
|
||||||
host->led.brightness = LED_OFF;
|
host->led.brightness = LED_OFF;
|
||||||
host->led.default_trigger = mmc_hostname(mmc);
|
host->led.default_trigger = mmc_hostname(mmc);
|
||||||
host->led.brightness_set = sdhci_led_control;
|
host->led.brightness_set = sdhci_led_control;
|
||||||
|
@@ -208,8 +208,6 @@ struct sdhci_host {
|
|||||||
#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
|
#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
|
||||||
/* Controller has an issue with buffer bits for small transfers */
|
/* Controller has an issue with buffer bits for small transfers */
|
||||||
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
|
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
|
||||||
/* Controller supports high speed but doesn't have the caps bit set */
|
|
||||||
#define SDHCI_QUIRK_FORCE_HIGHSPEED (1<<14)
|
|
||||||
|
|
||||||
int irq; /* Device IRQ */
|
int irq; /* Device IRQ */
|
||||||
void __iomem * ioaddr; /* Mapped address */
|
void __iomem * ioaddr; /* Mapped address */
|
||||||
@@ -222,6 +220,7 @@ struct sdhci_host {
|
|||||||
|
|
||||||
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
|
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
|
||||||
struct led_classdev led; /* LED control */
|
struct led_classdev led; /* LED control */
|
||||||
|
char led_name[32];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
spinlock_t lock; /* Mutex */
|
spinlock_t lock; /* Mutex */
|
||||||
|
Reference in New Issue
Block a user