From ae628903ab6cc9a04fdf8d4ff3f0c5b2ffa6f939 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 3 May 2009 20:45:03 +0200 Subject: sdhci: avoid changing voltage needlessly Because of granularity issues, sometimes we told the hardware to change to the voltage we were already at. Rework the logic so this doesn't happen. Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 53 +++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 25 deletions(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9234be2226e7..1432a35690dd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1005,12 +1005,34 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr; - if (host->power == power) + if (power == (unsigned short)-1) + pwr = 0; + else { + switch (1 << power) { + case MMC_VDD_165_195: + pwr = SDHCI_POWER_180; + break; + case MMC_VDD_29_30: + case MMC_VDD_30_31: + pwr = SDHCI_POWER_300; + break; + case MMC_VDD_32_33: + case MMC_VDD_33_34: + pwr = SDHCI_POWER_330; + break; + default: + BUG(); + } + } + + if (host->pwr == pwr) return; - if (power == (unsigned short)-1) { + host->pwr = pwr; + + if (pwr == 0) { sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); - goto out; + return; } /* @@ -1020,35 +1042,16 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); - pwr = SDHCI_POWER_ON; - - switch (1 << power) { - case MMC_VDD_165_195: - pwr |= SDHCI_POWER_180; - break; - case MMC_VDD_29_30: - case MMC_VDD_30_31: - pwr |= SDHCI_POWER_300; - break; - case MMC_VDD_32_33: - case MMC_VDD_33_34: - pwr |= SDHCI_POWER_330; - break; - default: - BUG(); - } - /* * At least the Marvell CaFe chip gets confused if we set the voltage * and set turn on power at the same time, so set the voltage first. */ if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)) - sdhci_writeb(host, pwr & ~SDHCI_POWER_ON, SDHCI_POWER_CONTROL); + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); - sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + pwr |= SDHCI_POWER_ON; -out: - host->power = power; + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); } /*****************************************************************************\ -- cgit v1.2.2 From be3f4ae0c0c56aab903aceaceed4b9d8418e180e Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 8 Jun 2009 23:33:52 +0100 Subject: sdhci: Print ADMA status and pointer on debug If using ADMA, then we should print the ADMA error and current pointer in sdhci_dumpregs() when any debug is requested. Signed-off-by: Ben Dooks Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1432a35690dd..35789c6edc19 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -78,6 +78,11 @@ static void sdhci_dumpregs(struct sdhci_host *host) sdhci_readl(host, SDHCI_CAPABILITIES), sdhci_readl(host, SDHCI_MAX_CURRENT)); + if (host->flags & SDHCI_USE_ADMA) + printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", + readl(host->ioaddr + SDHCI_ADMA_ERROR), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n"); } -- cgit v1.2.2 From 6882a8c071d609f4c088bee41e79572c7cfc1790 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sun, 14 Jun 2009 13:52:38 +0100 Subject: sdhci: Add better ADMA error reporting Update the ADMA error reporting to not only show the overall controller state but also to print the ADMA descriptor list. Signed-off-by: Ben Dooks Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 35789c6edc19..e54d6fba85e1 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1382,6 +1382,35 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) sdhci_finish_command(host); } +#ifdef DEBUG +static void sdhci_show_adma_error(struct sdhci_host *host) +{ + const char *name = mmc_hostname(host->mmc); + u8 *desc = host->adma_desc; + __le32 *dma; + __le16 *len; + u8 attr; + + sdhci_dumpregs(host); + + while (true) { + dma = (__le32 *)(desc + 4); + len = (__le16 *)(desc + 2); + attr = *desc; + + DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", + name, desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr); + + desc += 8; + + if (attr & 2) + break; + } +} +#else +static void sdhci_show_adma_error(struct sdhci_host *host) { } +#endif + static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) { BUG_ON(intmask == 0); @@ -1411,8 +1440,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) host->data->error = -ETIMEDOUT; else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT)) host->data->error = -EILSEQ; - else if (intmask & SDHCI_INT_ADMA_ERROR) + else if (intmask & SDHCI_INT_ADMA_ERROR) { + printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc)); + sdhci_show_adma_error(host); host->data->error = -EIO; + } if (host->data->error) sdhci_finish_data(host); -- cgit v1.2.2 From 1388eefd5a5e6aaa3cb04070bfc2b944c1d24b82 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sun, 14 Jun 2009 12:40:53 +0100 Subject: sdhci: Add SDHCI_QUIRK_NO_MULTIBLOCK quirk Add quirk to show the controller cannot do multi-block IO. This is mainly for the Samsung SDHCI controller that currently cannot manage to do multi-block PIO without timing out. Signed-off-by: Ben Dooks Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e54d6fba85e1..3856669aa7e8 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1834,7 +1834,7 @@ int sdhci_add_host(struct sdhci_host *host) /* * Maximum block count. */ - mmc->max_blk_count = 65535; + mmc->max_blk_count = (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535; /* * Init tasklets. -- cgit v1.2.2 From 5fe23c7f51def59f66bc6aeee988ef1a467a2c8c Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 18 Jun 2009 00:14:08 +0400 Subject: sdhci: Add support for hosts that are only capable of 1-bit transfers Some hosts (hardware configurations, or particular SD/MMC slots) may not support 4-bit bus. For example, on MPC8569E-MDS boards we can switch between serial (1-bit only) and nibble (4-bit) modes, thought we have to disable more peripherals to work in 4-bit mode. Along with some small core changes, this patch modifies sdhci-of driver, so that now it looks for "sdhci,1-bit-only" property in the device-tree, and if specified we enable a proper quirk. Signed-off-by: Anton Vorontsov Acked-by: Grant Likely Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3856669aa7e8..c7586739be1e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1761,7 +1761,10 @@ int sdhci_add_host(struct sdhci_host *host) mmc->ops = &sdhci_ops; mmc->f_min = host->max_clk / 256; mmc->f_max = host->max_clk; - mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; + mmc->caps = MMC_CAP_SDIO_IRQ; + + if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA)) + mmc->caps |= MMC_CAP_4_BIT_DATA; if (caps & SDHCI_CAN_DO_HISPD) mmc->caps |= MMC_CAP_SD_HIGHSPEED; -- cgit v1.2.2 From 557b06971b1f05cbadec2f376a305ee1954e9b0d Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 18 Jun 2009 16:53:38 +0200 Subject: sdhci: Specific quirk vor VIA SDHCI controller in VX855ES The SDHCI controller found in the VX855ES requires 10ms delay between applying power and applying clock. This issue has been discovered and documented by the OLPC XO1.5 team. Signed-off-by: Harald Welte Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c7586739be1e..f4066fdc8906 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1057,6 +1057,13 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) pwr |= SDHCI_POWER_ON; sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + + /* + * Some controllers need an extra 10ms delay of 10ms before they + * can apply clock after applying power + */ + if ((host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)) + mdelay(10); } /*****************************************************************************\ -- cgit v1.2.2 From 11a2f1b78a43d0c2bd026d79b952742c7588f529 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 21 Jun 2009 20:59:33 +0200 Subject: sdhci: remove needless double parenthesis Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host/sdhci.c') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f4066fdc8906..6779b4ecab18 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -584,7 +584,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data) * longer to time out, but that's much better than having a too-short * timeout value. */ - if ((host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)) + if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) return 0xE; /* timeout in us */ @@ -1051,7 +1051,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) * At least the Marvell CaFe chip gets confused if we set the voltage * and set turn on power at the same time, so set the voltage first. */ - if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)) + if (host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER) sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); pwr |= SDHCI_POWER_ON; @@ -1062,7 +1062,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) * Some controllers need an extra 10ms delay of 10ms before they * can apply clock after applying power */ - if ((host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)) + if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) mdelay(10); } -- cgit v1.2.2