diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 16:33:16 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 16:33:16 -0700 |
commit | 85579ad7f1dfc0b72bb243b7227bc4f663035e71 (patch) | |
tree | ef68893bca233ea5683e49117c05b32f8c907d70 /drivers/mmc | |
parent | 3af6e98f25d1f68b9c36beee330342944a4e0048 (diff) | |
parent | 092b6dbe8a4a24c17f2ebfe86995dc994e61f420 (diff) |
Merge tag 'mmc-v4.3' of git://git.linaro.org/people/ulf.hansson/mmc
Pull MMC updates from Ulf Hansson:
"MMC core:
- Fix a race condition in the request handling
- Skip trim commands for some buggy kingston eMMCs
- An optimization and a correction for erase groups
- Set CMD23 quirk for some Sandisk cards
MMC host:
- sdhci: Give GPIO CD higher precedence and don't poll when it's used
- sdhci: Fix DMA memory leakage
- sdhci: Some updates for clock management
- sdhci-of-at91: introduce driver for the Atmel SDMMC
- sdhci-of-arasan: Add support for sdhci-5.1
- sdhci-esdhc-imx: Add support for imx7d which also supports HS400
- sdhci: A collection of fixes and improvements for various sdhci hosts
- omap_hsmmc: Modernization of the regulator code
- dw_mmc: A couple of fixes for DMA and PIO mode
- usdhi6rol0: A few fixes and support probe deferral for regulators
- pxamci: Convert to use dmaengine
- sh_mmcif: Fix the suspend process in a short term solution
- tmio: Adjust timeout for commands
- sunxi: Fix timeout while gating/ungating clock"
* tag 'mmc-v4.3' of git://git.linaro.org/people/ulf.hansson/mmc: (67 commits)
mmc: android-goldfish: remove incorrect __iomem annotation
mmc: core: fix race condition in mmc_wait_data_done
mmc: host: omap_hsmmc: remove CONFIG_REGULATOR check
mmc: host: omap_hsmmc: use ios->vdd for setting vmmc voltage
mmc: host: omap_hsmmc: use regulator_is_enabled to find pbias status
mmc: host: omap_hsmmc: enable/disable vmmc_aux regulator based on previous state
mmc: host: omap_hsmmc: don't use ->set_power to set initial regulator state
mmc: host: omap_hsmmc: avoid pbias regulator enable on power off
mmc: host: omap_hsmmc: add separate function to set pbias
mmc: host: omap_hsmmc: add separate functions for enable/disable supply
mmc: host: omap_hsmmc: return error if any of the regulator APIs fail
mmc: host: omap_hsmmc: remove unnecessary pbias set_voltage
mmc: host: omap_hsmmc: use mmc_host's vmmc and vqmmc
mmc: host: omap_hsmmc: use the ocrmask provided by the vmmc regulator
mmc: host: omap_hsmmc: cleanup omap_hsmmc_reg_get()
mmc: host: omap_hsmmc: return on fatal errors from omap_hsmmc_reg_get
mmc: host: omap_hsmmc: use devm_regulator_get_optional() for vmmc
mmc: sdhci-of-at91: fix platform_no_drv_owner.cocci warnings
mmc: sh_mmcif: Fix suspend process
mmc: usdhi6rol0: fix error return code
...
Diffstat (limited to 'drivers/mmc')
26 files changed, 1035 insertions, 411 deletions
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index a1b820fcb2a6..c742cfd7674e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -47,10 +47,13 @@ #include "queue.h" MODULE_ALIAS("mmc:block"); + +#ifdef KERNEL #ifdef MODULE_PARAM_PREFIX #undef MODULE_PARAM_PREFIX #endif #define MODULE_PARAM_PREFIX "mmcblk." +#endif #define INAND_CMD38_ARG_EXT_CSD 113 #define INAND_CMD38_ARG_ERASE 0x00 @@ -2386,6 +2389,7 @@ force_ro_fail: #define CID_MANFID_TOSHIBA 0x11 #define CID_MANFID_MICRON 0x13 #define CID_MANFID_SAMSUNG 0x15 +#define CID_MANFID_KINGSTON 0x70 static const struct mmc_fixup blk_fixups[] = { @@ -2408,6 +2412,10 @@ static const struct mmc_fixup blk_fixups[] = * * N.B. This doesn't affect SD cards. */ + MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_BLK_NO_CMD23), MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_BLK_NO_CMD23), MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, @@ -2444,6 +2452,15 @@ static const struct mmc_fixup blk_fixups[] = MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + /* + * On Some Kingston eMMCs, performing trim can result in + * unrecoverable data conrruption occasionally due to a firmware bug. + */ + MMC_FIXUP("V10008", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_TRIM_BROKEN), + MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_TRIM_BROKEN), + END_FIXUP }; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9ad73f30f744..0520064dc33b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -358,8 +358,10 @@ EXPORT_SYMBOL(mmc_start_bkops); */ static void mmc_wait_data_done(struct mmc_request *mrq) { - mrq->host->context_info.is_done_rcv = true; - wake_up_interruptible(&mrq->host->context_info.wait); + struct mmc_context_info *context_info = &mrq->host->context_info; + + context_info->is_done_rcv = true; + wake_up_interruptible(&context_info->wait); } static void mmc_wait_done(struct mmc_request *mrq) @@ -2168,6 +2170,7 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, unsigned int arg) { unsigned int rem, to = from + nr; + int err; if (!(card->host->caps & MMC_CAP_ERASE) || !(card->csd.cmdclass & CCC_ERASE)) @@ -2218,6 +2221,22 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, /* 'from' and 'to' are inclusive */ to -= 1; + /* + * Special case where only one erase-group fits in the timeout budget: + * If the region crosses an erase-group boundary on this particular + * case, we will be trimming more than one erase-group which, does not + * fit in the timeout budget of the controller, so we need to split it + * and call mmc_do_erase() twice if necessary. This special case is + * identified by the card->eg_boundary flag. + */ + rem = card->erase_size - (from % card->erase_size); + if ((arg & MMC_TRIM_ARGS) && (card->eg_boundary) && (nr > rem)) { + err = mmc_do_erase(card, from, from + rem - 1, arg); + from += rem; + if ((err) || (to <= from)) + return err; + } + return mmc_do_erase(card, from, to, arg); } EXPORT_SYMBOL(mmc_erase); @@ -2233,7 +2252,8 @@ EXPORT_SYMBOL(mmc_can_erase); int mmc_can_trim(struct mmc_card *card) { - if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) + if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) && + (!(card->quirks & MMC_QUIRK_TRIM_BROKEN))) return 1; return 0; } @@ -2313,16 +2333,28 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card, if (!qty) return 0; + /* + * When specifying a sector range to trim, chances are we might cross + * an erase-group boundary even if the amount of sectors is less than + * one erase-group. + * If we can only fit one erase-group in the controller timeout budget, + * we have to care that erase-group boundaries are not crossed by a + * single trim operation. We flag that special case with "eg_boundary". + * In all other cases we can just decrement qty and pretend that we + * always touch (qty + 1) erase-groups as a simple optimization. + */ if (qty == 1) - return 1; + card->eg_boundary = 1; + else + qty--; /* Convert qty to sectors */ if (card->erase_shift) - max_discard = --qty << card->erase_shift; + max_discard = qty << card->erase_shift; else if (mmc_card_sd(card)) - max_discard = qty; + max_discard = qty + 1; else - max_discard = --qty * card->erase_size; + max_discard = qty * card->erase_size; return max_discard; } diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 99a9c9011c50..abd933b7029b 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -398,7 +398,7 @@ int mmc_of_parse(struct mmc_host *host) { struct device_node *np; u32 bus_width; - int len, ret; + int ret; bool cd_cap_invert, cd_gpio_invert = false; bool ro_cap_invert, ro_gpio_invert = false; @@ -445,12 +445,12 @@ int mmc_of_parse(struct mmc_host *host) */ /* Parse Card Detection */ - if (of_find_property(np, "non-removable", &len)) { + if (of_property_read_bool(np, "non-removable")) { host->caps |= MMC_CAP_NONREMOVABLE; } else { cd_cap_invert = of_property_read_bool(np, "cd-inverted"); - if (of_find_property(np, "broken-cd", &len)) + if (of_property_read_bool(np, "broken-cd")) host->caps |= MMC_CAP_NEEDS_POLL; ret = mmc_gpiod_request_cd(host, "cd", 0, true, @@ -491,41 +491,41 @@ int mmc_of_parse(struct mmc_host *host) if (ro_cap_invert ^ ro_gpio_invert) host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - if (of_find_property(np, "cap-sd-highspeed", &len)) + if (of_property_read_bool(np, "cap-sd-highspeed")) host->caps |= MMC_CAP_SD_HIGHSPEED; - if (of_find_property(np, "cap-mmc-highspeed", &len)) + if (of_property_read_bool(np, "cap-mmc-highspeed")) host->caps |= MMC_CAP_MMC_HIGHSPEED; - if (of_find_property(np, "sd-uhs-sdr12", &len)) + if (of_property_read_bool(np, "sd-uhs-sdr12")) host->caps |= MMC_CAP_UHS_SDR12; - if (of_find_property(np, "sd-uhs-sdr25", &len)) + if (of_property_read_bool(np, "sd-uhs-sdr25")) host->caps |= MMC_CAP_UHS_SDR25; - if (of_find_property(np, "sd-uhs-sdr50", &len)) + if (of_property_read_bool(np, "sd-uhs-sdr50")) host->caps |= MMC_CAP_UHS_SDR50; - if (of_find_property(np, "sd-uhs-sdr104", &len)) + if (of_property_read_bool(np, "sd-uhs-sdr104")) host->caps |= MMC_CAP_UHS_SDR104; - if (of_find_property(np, "sd-uhs-ddr50", &len)) + if (of_property_read_bool(np, "sd-uhs-ddr50")) host->caps |= MMC_CAP_UHS_DDR50; - if (of_find_property(np, "cap-power-off-card", &len)) + if (of_property_read_bool(np, "cap-power-off-card")) host->caps |= MMC_CAP_POWER_OFF_CARD; - if (of_find_property(np, "cap-sdio-irq", &len)) + if (of_property_read_bool(np, "cap-sdio-irq")) host->caps |= MMC_CAP_SDIO_IRQ; - if (of_find_property(np, "full-pwr-cycle", &len)) + if (of_property_read_bool(np, "full-pwr-cycle")) host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE; - if (of_find_property(np, "keep-power-in-suspend", &len)) + if (of_property_read_bool(np, "keep-power-in-suspend")) host->pm_caps |= MMC_PM_KEEP_POWER; - if (of_find_property(np, "enable-sdio-wakeup", &len)) + if (of_property_read_bool(np, "enable-sdio-wakeup")) host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; - if (of_find_property(np, "mmc-ddr-1_8v", &len)) + if (of_property_read_bool(np, "mmc-ddr-1_8v")) host->caps |= MMC_CAP_1_8V_DDR; - if (of_find_property(np, "mmc-ddr-1_2v", &len)) + if (of_property_read_bool(np, "mmc-ddr-1_2v")) host->caps |= MMC_CAP_1_2V_DDR; - if (of_find_property(np, "mmc-hs200-1_8v", &len)) + if (of_property_read_bool(np, "mmc-hs200-1_8v")) host->caps2 |= MMC_CAP2_HS200_1_8V_SDR; - if (of_find_property(np, "mmc-hs200-1_2v", &len)) + if (of_property_read_bool(np, "mmc-hs200-1_2v")) host->caps2 |= MMC_CAP2_HS200_1_2V_SDR; - if (of_find_property(np, "mmc-hs400-1_8v", &len)) + if (of_property_read_bool(np, "mmc-hs400-1_8v")) host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR; - if (of_find_property(np, "mmc-hs400-1_2v", &len)) + if (of_property_read_bool(np, "mmc-hs400-1_2v")) host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR; host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 6a0f9c79be26..8a1e3498261e 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -129,6 +129,14 @@ config MMC_SDHCI_OF_ARASAN If unsure, say N. +config MMC_SDHCI_OF_AT91 + tristate "SDHCI OF support for the Atmel SDMMC controller" + depends on MMC_SDHCI_PLTFM + depends on OF + select MMC_SDHCI_IO_ACCESSORS + help + This selects the Atmel SDMMC driver + config MMC_SDHCI_OF_ESDHC tristate "SDHCI OF support for the Freescale eSDHC controller" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index e928d61c5f4b..4f3452afa6ca 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o +obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c index b1eac719a4cc..dca5518b0139 100644 --- a/drivers/mmc/host/android-goldfish.c +++ b/drivers/mmc/host/android-goldfish.c @@ -118,7 +118,7 @@ struct goldfish_mmc_host { struct mmc_host *mmc; struct device *dev; unsigned char id; /* 16xx chips have 2 MMC blocks */ - void __iomem *virt_base; + void *virt_base; unsigned int phys_base; int irq; unsigned char bus_mode; diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 9a39e0b7e583..bf62e429f7fc 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -29,7 +29,6 @@ #include <linux/slab.h> #include <linux/stat.h> #include <linux/types.h> -#include <linux/platform_data/atmel.h> #include <linux/platform_data/mmc-atmel-mci.h> #include <linux/mmc/host.h> diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index de15121bba7d..bc76aa22473e 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host) /* It is slot 8 on Rockchip SoCs */ host->sdio_id0 = 8; + /* It needs this quirk on all Rockchip SoCs */ + host->pdata->quirks |= DW_MCI_QUIRK_BROKEN_DTO; + return 0; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 40e9d8e45f25..fcbf5524fd31 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -99,6 +99,9 @@ struct idmac_desc { __le32 des3; /* buffer 2 physical address */ }; + +/* Each descriptor can transfer up to 4KB of data in chained mode */ +#define DW_MCI_DESC_DATA_LENGTH 0x1000 #endif /* CONFIG_MMC_DW_IDMAC */ static bool dw_mci_reset(struct dw_mci *host); @@ -235,8 +238,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 cmdr; - cmd->error = -EINPROGRESS; + cmd->error = -EINPROGRESS; cmdr = cmd->opcode; if (cmd->opcode == MMC_STOP_TRANSMISSION || @@ -371,7 +374,7 @@ static void dw_mci_start_command(struct dw_mci *host, cmd->arg, cmd_flags); mci_writel(host, CMDARG, cmd->arg); - wmb(); + wmb(); /* drain writebuffer */ dw_mci_wait_while_busy(host, cmd_flags); mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); @@ -380,6 +383,7 @@ static void dw_mci_start_command(struct dw_mci *host, static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) { struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort; + dw_mci_start_command(host, stop, host->stop_cmdr); } @@ -462,69 +466,102 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host) static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, unsigned int sg_len) { + unsigned int desc_len; int i; + if (host->dma_64bit_address == 1) { - struct idmac_desc_64addr *desc = host->sg_cpu; + struct idmac_desc_64addr *desc_first, *desc_last, *desc; - for (i = 0; i < sg_len; i++, desc++) { + desc_first = desc_last = desc = host->sg_cpu; + + for (i = 0; i < sg_len; i++) { unsigned int length = sg_dma_len(&data->sg[i]); + u64 mem_addr = sg_dma_address(&data->sg[i]); - /* - * Set the OWN bit and disable interrupts for this - * descriptor - */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | - IDMAC_DES0_CH; - /* Buffer length */ - IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, length); - - /* Physical address to DMA to/from */ - desc->des4 = mem_addr & 0xffffffff; - desc->des5 = mem_addr >> 32; + for ( ; length ; desc++) { + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? + length : DW_MCI_DESC_DATA_LENGTH; + + length -= desc_len; + + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + + /* Buffer length */ + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); + + /* Physical address to DMA to/from */ + desc->des4 = mem_addr & 0xffffffff; + desc->des5 = mem_addr >> 32; + + /* Update physical address for the next desc */ + mem_addr += desc_len; + + /* Save pointer to the last descriptor */ + desc_last = desc; + } } /* Set first descriptor */ - desc = host->sg_cpu; - desc->des0 |= IDMAC_DES0_FD; + desc_first->des0 |= IDMAC_DES0_FD; /* Set last descriptor */ - desc = host->sg_cpu + (i - 1) * - sizeof(struct idmac_desc_64addr); - desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc->des0 |= IDMAC_DES0_LD; + desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc_last->des0 |= IDMAC_DES0_LD; } else { - struct idmac_desc *desc = host->sg_cpu; + struct idmac_desc *desc_first, *desc_last, *desc; - for (i = 0; i < sg_len; i++, desc++) { + desc_first = desc_last = desc = host->sg_cpu; + + for (i = 0; i < sg_len; i++) { unsigned int length = sg_dma_len(&data->sg[i]); + u32 mem_addr = sg_dma_address(&data->sg[i]); - /* - * Set the OWN bit and disable interrupts for this - * descriptor - */ - desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | - IDMAC_DES0_DIC | IDMAC_DES0_CH); - /* Buffer length */ - IDMAC_SET_BUFFER1_SIZE(desc, length); + for ( ; length ; desc++) { + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? + length : DW_MCI_DESC_DATA_LENGTH; + + length -= desc_len; + + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | + IDMAC_DES0_DIC | + IDMAC_DES0_CH); - /* Physical address to DMA to/from */ - desc->des2 = cpu_to_le32(mem_addr); + /* Buffer length */ + IDMAC_SET_BUFFER1_SIZE(desc, desc_len); + + /* Physical address to DMA to/from */ + desc->des2 = cpu_to_le32(mem_addr); + + /* Update physical address for the next desc */ + mem_addr += desc_len; + + /* Save pointer to the last descriptor */ + desc_last = desc; + } } /* Set first descriptor */ - desc = host->sg_cpu; - desc->des0 |= cpu_to_le32(IDMAC_DES0_FD); + desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); /* Set last descriptor */ - desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); - desc->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC)); - desc->des0 |= cpu_to_le32(IDMAC_DES0_LD); + desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | + IDMAC_DES0_DIC)); + desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); } - wmb(); + wmb(); /* drain writebuffer */ } static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) @@ -542,6 +579,7 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) temp |= SDMMC_CTRL_USE_IDMAC; mci_writel(host, CTRL, temp); + /* drain writebuffer */ wmb(); /* Enable the IDMAC */ @@ -589,7 +627,9 @@ static int dw_mci_idmac_init(struct dw_mci *host) host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); /* Forward link the descriptor list */ - for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) { + for (i = 0, p = host->sg_cpu; + i < host->ring_size - 1; + i++, p++) { p->des3 = cpu_to_le32(host->sg_dma + (sizeof(struct idmac_desc) * (i + 1))); p->des1 = 0; @@ -718,7 +758,7 @@ static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) u32 fifo_width = 1 << host->data_shift; u32 blksz_depth = blksz / fifo_width, fifoth_val; u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers; - int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1; + int idx = ARRAY_SIZE(mszs) - 1; tx_wmark = (host->fifo_depth) / 2; tx_wmark_invers = host->fifo_depth - tx_wmark; @@ -843,6 +883,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) { unsigned long irqflags; + int flags = SG_MITER_ATOMIC; u32 temp; data->error = -EINPROGRESS; @@ -859,7 +900,6 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) } if (dw_mci_submit_data_dma(host, data)) { - int flags = SG_MITER_ATOMIC; if (host->data->flags & MMC_DATA_READ) flags |= SG_MITER_TO_SG; else @@ -906,7 +946,7 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) unsigned int cmd_status = 0; mci_writel(host, CMDARG, arg); - wmb(); + wmb(); /* drain writebuffer */ dw_mci_wait_while_busy(host, cmd); mci_writel(host, CMD, SDMMC_CMD_START | cmd); @@ -1019,7 +1059,7 @@ static void __dw_mci_start_request(struct dw_mci *host, if (data) { dw_mci_submit_data(host, data); - wmb(); + wmb(); /* drain writebuffer */ } dw_mci_start_command(host, cmd, cmdflags); @@ -1384,14 +1424,15 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; - int err = -ENOSYS; + int err = -EINVAL; if (drv_data && drv_data->execute_tuning) err = drv_data->execute_tuning(slot); return err; } -static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) +static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, + struct mmc_ios *ios) { struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; @@ -1533,6 +1574,20 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) return data->error; } +static void dw_mci_set_drto(struct dw_mci *host) +{ + unsigned int drto_clks; + unsigned int drto_ms; + + drto_clks = mci_readl(host, TMOUT) >> 8; + drto_ms = DIV_ROUND_UP(drto_clks, host->bus_hz / 1000); + + /* add a bit spare time */ + drto_ms += 10; + + mod_timer(&host->dto_timer, jiffies + msecs_to_jiffies(drto_ms)); +} + static void dw_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; @@ -1610,8 +1665,16 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, - &host->pending_events)) + &host->pending_events)) { + /* + * If all data-related interrupts don't come + * within the given time in reading data state. + */ + if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) && + (host->dir_status == DW_MCI_RECV_STATUS)) + dw_mci_set_drto(host); break; + } set_bit(EVENT_XFER_COMPLETE, &host->completed_events); @@ -1644,8 +1707,17 @@ static void dw_mci_tasklet_func(unsigned long priv) case STATE_DATA_BUSY: if (!test_and_clear_bit(EVENT_DATA_COMPLETE, - &host->pending_events)) + &host->pending_events)) { + /* + * If data error interrupt comes but data over + * interrupt doesn't come within the given time. + * in reading data state. + */ + if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) && + (host->dir_status == DW_MCI_RECV_STATUS)) + dw_mci_set_drto(host); break; + } host->data = NULL; set_bit(EVENT_DATA_COMPLETE, &host->completed_events); @@ -1743,7 +1815,7 @@ static int dw_mci_push_part_bytes(struct dw_mci *host, void *buf, int cnt) /* pull first bytes from part_buf, only use during pull */ static int dw_mci_pull_part_bytes(struct dw_mci *host, void *buf, int cnt) { - cnt = min(cnt, (int)host->part_buf_count); + cnt = min_t(int, cnt, host->part_buf_count); if (cnt) { memcpy(buf, (void *)&host->part_buf + host->part_buf_start, cnt); @@ -1769,6 +1841,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) /* try and push anything in the part_buf */ if (unlikely(host->part_buf_count)) { int len = dw_mci_push_part_bytes(host, buf, cnt); + buf += len; cnt -= len; if (host->part_buf_count == 2) { @@ -1795,6 +1868,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) #endif { u16 *pdata = buf; + for (; cnt >= 2; cnt -= 2) mci_fifo_writew(host->fifo_reg, *pdata++); buf = pdata; @@ -1819,6 +1893,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) int len = min(cnt & -2, (int)sizeof(aligned_buf)); int items = len >> 1; int i; + for (i = 0; i < items; ++i) aligned_buf[i] = mci_fifo_readw(host->fifo_reg); /* memcpy from aligned buffer into output buffer */ @@ -1830,6 +1905,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) #endif { u16 *pdata = buf; + for (; cnt >= 2; cnt -= 2) *pdata++ = mci_fifo_readw(host->fifo_reg); buf = pdata; @@ -1848,6 +1924,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) /* try and push anything in the part_buf */ if (unlikely(host->part_buf_count)) { int len = dw_mci_push_part_bytes(host, buf, cnt); + buf += len; cnt -= len; if (host->part_buf_count == 4) { @@ -1874,6 +1951,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) #endif { u32 *pdata = buf; + for (; cnt >= 4; cnt -= 4) mci_fifo_writel(host->fifo_reg, *pdata++); buf = pdata; @@ -1898,6 +1976,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) int len = min(cnt & -4, (int)sizeof(aligned_buf)); int items = len >> 2; int i; + for (i = 0; i < items; ++i) aligned_buf[i] = mci_fifo_readl(host->fifo_reg); /* memcpy from aligned buffer into output buffer */ @@ -1909,6 +1988,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) #endif { u32 *pdata = buf; + for (; cnt >= 4; cnt -= 4) *pdata++ = mci_fifo_readl(host->fifo_reg); buf = pdata; @@ -1927,6 +2007,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) /* try and push anything in the part_buf */ if (unlikely(host->part_buf_count)) { int len = dw_mci_push_part_bytes(host, buf, cnt); + buf += len; cnt -= len; @@ -1954,6 +2035,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) #endif { u64 *pdata = buf; + for (; cnt >= 8; cnt -= 8) mci_fifo_writeq(host->fifo_reg, *pdata++); buf = pdata; @@ -1978,6 +2060,7 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) int len = min(cnt & -8, (int)sizeof(aligned_buf)); int items = len >> 3; int i; + for (i = 0; i < items; ++i) aligned_buf[i] = mci_fifo_readq(host->fifo_reg); @@ -1990,6 +2073,7 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) #endif { u64 *pdata = buf; + for (; cnt >= 8; cnt -= 8) *pdata++ = mci_fifo_readq(host->fifo_reg); buf = pdata; @@ -2065,7 +2149,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host, bool dto) done: sg_miter_stop(sg_miter); host->sg = NULL; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } @@ -2119,7 +2203,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host) done: sg_miter_stop(sg_miter); host->sg = NULL; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } @@ -2128,7 +2212,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) if (!host->cmd_status) host->cmd_status = status; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_CMD_COMPLETE, &host->pending_events); tasklet_schedule(&host->tasklet); @@ -2192,7 +2276,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & DW_MCI_CMD_ERROR_FLAGS) { mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); host->cmd_status = pending; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_CMD_COMPLETE, &host->pending_events); } @@ -2200,16 +2284,19 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* if there is an error report DATA_ERROR */ mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); host->data_status = pending; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_DATA_ERROR, &host->pending_events); tasklet_schedule(&host->tasklet); } if (pending & SDMMC_INT_DATA_OVER) { + if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO) + del_timer(&host->dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) host->data_status = pending; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ if (host->dir_status == DW_MCI_RECV_STATUS) { if (host->sg != NULL) dw_mci_read_data_pio(host, true); @@ -2383,27 +2470,20 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (ret) goto err_host_allocated; - if (host->pdata->blk_settings) { - mmc->max_segs = host->pdata->blk_settings->max_segs; - mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; - mmc->max_blk_count = host->pdata->blk_settings->max_blk_count; - mmc->max_req_size = host->pdata->blk_settings->max_req_size; - mmc->max_seg_size = host->pdata->blk_settings->max_seg_size; - } else { - /* Useful defaults if platform data is unset. */ -#ifdef CONFIG_MMC_DW_IDMAC + /* Useful defaults if platform data is unset. */ + if (host->use_dma) { mmc->max_segs = host->ring_size; mmc->max_blk_size = 65536; mmc->max_seg_size = 0x1000; mmc->max_req_size = mmc->max_seg_size * host->ring_size; mmc->max_blk_count = mmc->max_req_size / 512; -#else + } else { mmc->max_segs = 64; mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ mmc->max_blk_count = 512; - mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_req_size = mmc->max_blk_size * + mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; -#endif /* CONFIG_MMC_DW_IDMAC */ } if (dw_mci_get_cd(mmc)) @@ -2473,8 +2553,8 @@ static void dw_mci_init_dma(struct dw_mci *host) if (host->dma_ops->init && host->dma_ops->start && host->dma_ops->stop && host->dma_ops->cleanup) { if (host->dma_ops->init(host)) { - dev_err(host->dev, "%s: Unable to initialize " - "DMA Controller.\n", __func__); + dev_err(host->dev, "%s: Unable to initialize DMA Controller.\n", + __func__); goto no_dma; } } else { @@ -2488,7 +2568,6 @@ static void dw_mci_init_dma(struct dw_mci *host) no_dma: dev_info(host->dev, "Using PIO mode.\n"); host->use_dma = 0; - return; } static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) @@ -2542,6 +2621,7 @@ static bool dw_mci_reset(struct dw_mci *host) if (host->use_dma) { unsigned long timeout = jiffies + msecs_to_jiffies(500); u32 status; + do { status = mci_readl(host, STATUS); if (!(status & SDMMC_STATUS_DMA_REQ)) @@ -2551,8 +2631,8 @@ static bool dw_mci_reset(struct dw_mci *host) if (status & SDMMC_STATUS_DMA_REQ) { dev_err(host->dev, - "%s: Timeout waiting for dma_req to " - "clear during reset\n", __func__); + "%s: Timeout waiting for dma_req to clear during reset\n", + __func__); goto ciu_out; } @@ -2563,8 +2643,8 @@ static bool dw_mci_reset(struct dw_mci *host) } else { /* if the controller reset bit did clear, then set clock regs */ if (!(mci_readl(host, CTRL) & SDMMC_CTRL_RESET)) { - dev_err(host->dev, "%s: fifo/dma reset bits didn't " - "clear but ciu was reset, doing clock update\n", + dev_err(host->dev, + "%s: fifo/dma reset bits didn't clear but ciu was reset, doing clock update\n", __func__); goto ciu_out; } @@ -2598,6 +2678,28 @@ static void dw_mci_cmd11_timer(unsigned long arg) tasklet_schedule(&host->tasklet); } +static void dw_mci_dto_timer(unsigned long arg) +{ + struct dw_mci *host = (struct dw_mci *)arg; + + switch (host->state) { + case STATE_SENDING_DATA: + case STATE_DATA_BUSY: + /* + * If DTO interrupt does NOT come in sending data state, + * we should notify the driver to terminate current transfer + * and report a data timeout to the core. + */ + host->data_status = SDMMC_INT_DRTO; + set_bit(EVENT_DATA_ERROR, &host->pending_events); + set_bit(EVENT_DATA_COMPLETE, &host->pending_events); + tasklet_schedule(&host->tasklet); + break; + default: + break; + } +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; @@ -2625,8 +2727,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) /* find out number of slots supported */ if (of_property_read_u32(dev->of_node, "num-slots", &pdata->num_slots)) { - dev_info(dev, "num-slots property not found, " - "assuming 1 slot is available\n"); + dev_info(dev, + "num-slots property not found, assuming 1 slot is available\n"); pdata->num_slots = 1; } @@ -2636,8 +2738,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) pdata->quirks |= of_quirks[idx].id; if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth)) - dev_info(dev, "fifo-depth property not found, using " - "value of FIFOTH register as default\n"); + dev_info(dev, + "fifo-depth property not found, using value of FIFOTH register as default\n"); of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms); @@ -2650,8 +2752,10 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) return ERR_PTR(ret); } - if (of_find_property(np, "supports-highspeed", NULL)) + if (of_find_property(np, "supports-highspeed", NULL)) { + dev_info(dev, "supports-highspeed property is deprecated.\n"); pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + } return pdata; } @@ -2706,7 +2810,7 @@ int dw_mci_probe(struct dw_mci *host) } } - if (host->pdata->num_slots > 1) { + if (host->pdata->num_slots < 1) { dev_err(host->dev, "Platform data must supply num_slots.\n"); return -ENODEV; @@ -2774,6 +2878,10 @@ int dw_mci_probe(struct dw_mci *host) host->quirks = host->pdata->quirks; + if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO) + setup_timer(&host->dto_timer, + dw_mci_dto_timer, (unsigned long)host); + spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); INIT_LIST_HEAD(&host->queue); @@ -2874,11 +2982,11 @@ int dw_mci_probe(struct dw_mci *host) mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR | SDMMC_INT_RXDR | DW_MCI_ERROR_FLAGS); - mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */ + /* Enable mci interrupt */ + mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); - dev_info(host->dev, "DW MMC controller at irq %d, " - "%d bit host data width, " - "%u deep fifo\n", + dev_info(host->dev, + "DW MMC controller at irq %d,%d bit host data width,%u deep fifo\n", host->irq, width, fifo_size); /* We need at least one slot to succeed */ @@ -2893,8 +3001,9 @@ int dw_mci_probe(struct dw_mci *host) if (init_slots) { dev_info(host->dev, "%d slots initialized\n", init_slots); } else { - dev_dbg(host->dev, "attempted to initialize %d slots, " - "but failed on all\n", host->num_slots); + dev_dbg(host->dev, + "attempted to initialize %d slots, but failed on all\n", + host->num_slots); goto err_dmaunmap; } @@ -2992,6 +3101,7 @@ int dw_mci_resume(struct dw_mci *host) for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; + if (!slot) continue; if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 68dd6c79c378..b763b11ed9e1 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -948,6 +948,7 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) { struct mmc_data *data = req->data; int i, use_dma = 1, block_size; + struct scatterlist *sg; unsigned sg_len; host->data = data; @@ -972,8 +973,8 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) sg_len = (data->blocks == 1) ? 1 : data->sg_len; /* Only do DMA for entire blocks */ - for (i = 0; i < sg_len; i++) { - if ((data->sg[i].length % block_size) != 0) { + for_each_sg(data->sg, sg, sg_len, i) { + if ((sg->length % block_size) != 0) { use_dma = 0; break; } @@ -1419,8 +1420,10 @@ static int mmc_omap_probe(struct platform_device *pdev) host->reg_shift = (mmc_omap7xx() ? 1 : 2); host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0); - if (!host->mmc_omap_wq) + if (!host->mmc_omap_wq) { + ret = -ENOMEM; goto err_plat_cleanup; + } for (i = 0; i < pdata->nr_slots; i++) { ret = mmc_omap_new_slot(host, i); diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4d1203236890..781e4db31767 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -181,18 +181,9 @@ struct omap_hsmmc_host { struct mmc_data *data; struct clk *fclk; struct clk *dbclk; - /* - * vcc == configured supply - * vcc_aux == optional - * - MMC1, supply for DAT4..DAT7 - * - MMC2/MMC2, external level shifter voltage supply, for - * chip (SDIO, eMMC, etc) or transceiver (MMC2 only) - */ - struct regulator *vcc; - struct regulator *vcc_aux; struct regulator *pbias; - bool pbias_enabled; void __iomem *base; + int vqmmc_enabled; resource_size_t mapbase; spinlock_t irq_lock; /* Prevent races with irq handler */ unsigned int dma_len; @@ -213,7 +204,6 @@ struct omap_hsmmc_host { int context_loss; int protect_card; int reqs_blocked; - int use_reg; int req_in_progress; unsigned long clk_rate; unsigned int flags; @@ -254,32 +244,133 @@ static int omap_hsmmc_get_cover_state(struct device *dev) return mmc_gpio_get_cd(host->mmc); } -#ifdef CONFIG_REGULATOR +static int omap_hsmmc_enable_supply(struct mmc_host *mmc) +{ + int ret; + struct omap_hsmmc_host *host = mmc_priv(mmc); + struct mmc_ios *ios = &mmc->ios; + + if (mmc->supply.vmmc) { + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); + if (ret) + return ret; + } + + /* Enable interface voltage rail, if needed */ + if (mmc->supply.vqmmc && !host->vqmmc_enabled) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret) { + dev_err(mmc_dev(mmc), "vmmc_aux reg enable failed\n"); + goto err_vqmmc; + } + host->vqmmc_enabled = 1; + } + + return 0; + +err_vqmmc: + if (mmc->supply.vmmc) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + + return ret; +} + +static int omap_hsmmc_disable_supply(struct mmc_host *mmc) +{ + int ret; + int status; + struct omap_hsmmc_host *host = mmc_priv(mmc); + + if (mmc->supply.vqmmc && host->vqmmc_enabled) { + ret = regulator_disable(mmc->supply.vqmmc); + if (ret) { + dev_err(mmc_dev(mmc), "vmmc_aux reg disable failed\n"); + return ret; + } + host->vqmmc_enabled = 0; + } + + if (mmc->supply.vmmc) { + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + if (ret) + goto err_set_ocr; + } + + return 0; + +err_set_ocr: + if (mmc->supply.vqmmc) { + status = regulator_enable(mmc->supply.vqmmc); + if (status) + dev_err(mmc_dev(mmc), "vmmc_aux re-enable failed\n"); + } + + return ret; +} + +static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on, + int vdd) +{ + int ret; + + if (!host->pbias) + return 0; + + if (power_on) { + if (vdd <= VDD_165_195) + ret = regulator_set_voltage(host->pbias, VDD_1V8, + VDD_1V8); + else + ret = regulator_set_voltage(host->pbias, VDD_3V0, + VDD_3V0); + if (ret < 0) { + dev_err(host->dev, "pbias set voltage fail\n"); + return ret; + } + + if (!regulator_is_enabled(host->pbias)) { + ret = regulator_enable(host->pbias); + if (ret) { + dev_err(host->dev, "pbias reg enable fail\n"); + return ret; + } + } + } else { + if (regulator_is_enabled(host->pbias)) { + ret = regulator_disable(host->pbias); + if (ret) { + dev_err(host->dev, "pbias reg disable fail\n"); + return ret; + } + } + } + + return 0; +} static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) { struct omap_hsmmc_host *host = platform_get_drvdata(to_platform_device(dev)); + struct mmc_host *mmc = host->mmc; int ret = 0; + if (mmc_pdata(host)->set_power) + return mmc_pdata(host)->set_power(dev, power_on, vdd); + /* * If we don't see a Vcc regulator, assume it's a fixed * voltage always-on regulator. */ - if (!host->vcc) + if (!mmc->supply.vmmc) return 0; if (mmc_pdata(host)->before_set_reg) mmc_pdata(host)->before_set_reg(dev, power_on, vdd); - if (host->pbias) { - if (host->pbias_enabled == 1) { - ret = regulator_disable(host->pbias); - if (!ret) - host->pbias_enabled = 0; - } - regulator_set_voltage(host->pbias, VDD_3V0, VDD_3V0); - } + ret = omap_hsmmc_set_pbias(host, false, 0); + if (ret) + return ret; /* * Assume Vcc regulator is used only to power the card ... OMAP @@ -295,129 +386,138 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) * chips/cards need an interface voltage rail too. */ if (power_on) { - if (host->vcc) - ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); - /* Enable interface voltage rail, if needed */ - if (ret == 0 && host->vcc_aux) { - ret = regulator_enable(host->vcc_aux); - if (ret < 0 && host->vcc) - ret = mmc_regulator_set_ocr(host->mmc, - host->vcc, 0); - } - } else { - /* Shut down the rail */ - if (host->vcc_aux) - ret = regulator_disable(host->vcc_aux); - if (host->vcc) { - /* Then proceed to shut down the local regulator */ - ret = mmc_regulator_set_ocr(host->mmc, - host->vcc, 0); - } - } - - if (host->pbias) { - if (vdd <= VDD_165_195) - ret = regulator_set_voltage(host->pbias, VDD_1V8, - VDD_1V8); - else - ret = regulator_set_voltage(host->pbias, VDD_3V0, - VDD_3V0); - if (ret < 0) - goto error_set_power; + ret = omap_hsmmc_enable_supply(mmc); + if (ret) + return ret; - if (host->pbias_enabled == 0) { - ret = regulator_enable(host->pbias); - if (!ret) - host->pbias_enabled = 1; - } + ret = omap_hsmmc_set_pbias(host, true, vdd); + if (ret) + goto err_set_voltage; + } else { + ret = omap_hsmmc_disable_supply(mmc); + if (ret) + return ret; } if (mmc_pdata(host)->after_set_reg) mmc_pdata(host)->after_set_reg(dev, power_on, vdd); -error_set_power: + return 0; + +err_set_voltage: + omap_hsmmc_disable_supply(mmc); + return ret; } -static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) +static int omap_hsmmc_disable_boot_regulator(struct regulator *reg) { - struct regulator *reg; - int ocr_value = 0; + int ret; - reg = devm_regulator_get(host->dev, "vmmc"); - if (IS_ERR(reg)) { - dev_err(host->dev, "unable to get vmmc regulator %ld\n", - PTR_ERR(reg)); - return PTR_ERR(reg); - } else { - host->vcc = reg; - ocr_value = mmc_regulator_get_ocrmask(reg); - if (!mmc_pdata(host)->ocr_mask) { - mmc_pdata(host)->ocr_mask = ocr_value; - } else { - if (!(mmc_pdata(host)->ocr_mask & ocr_value)) { - dev_err(host->dev, "ocrmask %x is not supported\n", - mmc_pdata(host)->ocr_mask); - mmc_pdata(host)->ocr_mask = 0; - return -EINVAL; - } - } + if (!reg) + return 0; + + if (regulator_is_enabled(reg)) { + ret = regulator_enable(reg); + if (ret) + return ret; + + ret = regulator_disable(reg); + if (ret) + return ret; } - mmc_pdata(host)->set_power = omap_hsmmc_set_power; - /* Allow an aux regulator */ - reg = devm_regulator_get_optional(host->dev, "vmmc_aux"); - host->vcc_aux = IS_ERR(reg) ? NULL : reg; + return 0; +} - reg = devm_regulator_get_optional(host->dev, "pbias"); - host->pbias = IS_ERR(reg) ? NULL : reg; +static int omap_hsmmc_disable_boot_regulators(struct omap_hsmmc_host *host) +{ + struct mmc_host *mmc = host->mmc; + int ret; - /* For eMMC do not power off when not in sleep state */ - if (mmc_pdata(host)->no_regulator_off_init) - return 0; /* - * To disable boot_on regulator, enable regulator - * to increase usecount and then disable it. + * disable regulators enabled during boot and get the usecount + * right so that regulators can be enabled/disabled by checking + * the return value of regulator_is_enabled */ - if ((host->vcc && regulator_is_enabled(host->vcc) > 0) || - (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) { - int vdd = ffs(mmc_pdata(host)->ocr_mask) - 1; + ret = omap_hsmmc_disable_boot_regulator(mmc->supply.vmmc); + if (ret) { + dev_err(host->dev, "fail to disable boot enabled vmmc reg\n"); + return ret; + } + + ret = omap_hsmmc_disable_boot_regulator(mmc->supply.vqmmc); + if (ret) { + dev_err(host->dev, + "fail to disable boot enabled vmmc_aux reg\n"); + return ret; + } - mmc_pdata(host)->set_power(host->dev, 1, vdd); - mmc_pdata(host)->set_power(host->dev, 0, 0); + ret = omap_hsmmc_disable_boot_regulator(host->pbias); + if (ret) { + dev_err(host->dev, + "failed to disable boot enabled pbias reg\n"); + return ret; } return 0; } -static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) +static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { - mmc_pdata(host)->set_power = NULL; -} + int ocr_value = 0; + int ret; + struct mmc_host *mmc = host->mmc; -static inline int omap_hsmmc_have_reg(void) -{ - return 1; -} + if (mmc_pdata(host)->set_power) + return 0; -#else + mmc->supply.vmmc = devm_regulator_get_optional(host->dev, "vmmc"); + if (IS_ERR(mmc->supply.vmmc)) { + ret = PTR_ERR(mmc->supply.vmmc); + if (ret != -ENODEV) + return ret; + dev_dbg(host->dev, "unable to get vmmc regulator %ld\n", + PTR_ERR(mmc->supply.vmmc)); + mmc->supply.vmmc = NULL; + } else { + ocr_value = mmc_regulator_get_ocrmask(mmc->supply.vmmc); + if (ocr_value > 0) + mmc_pdata(host)->ocr_mask = ocr_value; + } -static inline int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) -{ - return -EINVAL; -} + /* Allow an aux regulator */ + mmc->supply.vqmmc = devm_regulator_get_optional(host->dev, "vmmc_aux"); + if (IS_ERR(mmc->supply.vqmmc)) { + ret = PTR_ERR(mmc->supply.vqmmc); + if (ret != -ENODEV) + return ret; + dev_dbg(host->dev, "unable to get vmmc_aux regulator %ld\n", + PTR_ERR(mmc->supply.vqmmc)); + mmc->supply.vqmmc = NULL; + } -static inline void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) -{ -} + host->pbias = devm_regulator_get_optional(host->dev, "pbias"); + if (IS_ERR(host->pbias)) { + ret = PTR_ERR(host->pbias); + if (ret != -ENODEV) + return ret; + dev_dbg(host->dev, "unable to get pbias regulator %ld\n", + PTR_ERR(host->pbias)); + host->pbias = NULL; + } + + /* For eMMC do not power off when not in sleep state */ + if (mmc_pdata(host)->no_regulator_off_init) + return 0; + + ret = omap_hsmmc_disable_boot_regulators(host); + if (ret) + return ret; -static inline int omap_hsmmc_have_reg(void) -{ return 0; } -#endif - static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id); static int omap_hsmmc_gpio_init(struct mmc_host *mmc, @@ -1149,11 +1249,11 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) clk_disable_unprepare(host->dbclk); /* Turn the power off */ - ret = mmc_pdata(host)->set_power(host->dev, 0, 0); + ret = omap_hsmmc_set_power(host->dev, 0, 0); /* Turn the power ON with given VDD 1.8 or 3.0v */ if (!ret) - ret = mmc_pdata(host)->set_power(host->dev, 1, vdd); + ret = omap_hsmmc_set_power(host->dev, 1, vdd); pm_runtime_get_sync(host->dev); if (host->dbclk) clk_prepare_enable(host->dbclk); @@ -1552,10 +1652,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->power_mode != host->power_mode) { switch (ios->power_mode) { case MMC_POWER_OFF: - mmc_pdata(host)->set_power(host->dev, 0, 0); + omap_hsmmc_set_power(host->dev, 0, 0); break; case MMC_POWER_UP: - mmc_pdata(host)->set_power(host->dev, 1, ios->vdd); + omap_hsmmc_set_power(host->dev, 1, ios->vdd); break; case MMC_POWER_ON: do_send_init_stream = 1; @@ -1953,7 +2053,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->base = base + pdata->reg_offset; host->power_mode = MMC_POWER_OFF; host->next_data.cookie = 1; - host->pbias_enabled = 0; + host->vqmmc_enabled = 0; ret = omap_hsmmc_gpio_init(mmc, host, pdata); if (ret) @@ -2078,12 +2178,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev) goto err_irq; } - if (omap_hsmmc_have_reg() && !mmc_pdata(host)->set_power) { - ret = omap_hsmmc_reg_get(host); - if (ret) - goto err_irq; - host->use_reg = 1; - } + ret = omap_hsmmc_reg_get(host); + if (ret) + goto err_irq; mmc->ocr_avail = mmc_pdata(host)->ocr_mask; @@ -2125,8 +2222,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) err_slot_name: mmc_remove_host(mmc); - if (host->use_reg) - omap_hsmmc_reg_put(host); err_irq: device_init_wakeup(&pdev->dev, false); if (host->tx_chan) @@ -2150,8 +2245,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) pm_runtime_get_sync(host->dev); mmc_remove_host(host->mmc); - if (host->use_reg) - omap_hsmmc_reg_put(host); if (host->tx_chan) dma_release_channel(host->tx_chan); diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 1b6d0bfe35f5..1420f29628c7 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -22,7 +22,9 @@ #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/dmaengine.h> #include <linux/dma-mapping.h> +#include <linux/dma/pxa-dma.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/mmc/host.h> @@ -37,7 +39,6 @@ #include <asm/sizes.h> #include <mach/hardware.h> -#include <mach/dma.h> #include <linux/platform_data/mmc-pxamci.h> #include "pxamci.h" @@ -58,7 +59,6 @@ struct pxamci_host { struct clk *clk; unsigned long clkrate; int irq; - int dma; unsigned int clkrt; unsigned int cmdat; unsigned int imask; @@ -69,8 +69,10 @@ struct pxamci_host { struct mmc_command *cmd; struct mmc_data *data; + struct dma_chan *dma_chan_rx; + struct dma_chan *dma_chan_tx; + dma_cookie_t dma_cookie; dma_addr_t sg_dma; - struct pxa_dma_desc *sg_cpu; unsigned int dma_len; unsigned int dma_dir; @@ -173,14 +175,18 @@ static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask) spin_unlock_irqrestore(&host->lock, flags); } +static void pxamci_dma_irq(void *param); + static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) { + struct dma_async_tx_descriptor *tx; + enum dma_data_direction direction; + struct dma_slave_config config; + struct dma_chan *chan; unsigned int nob = data->blocks; unsigned long long clks; unsigned int timeout; - bool dalgn = 0; - u32 dcmd; - int i; + int ret; host->data = data; @@ -195,54 +201,48 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); writel((timeout + 255) / 256, host->base + MMC_RDTO); + memset(&config, 0, sizeof(config)); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.src_addr = host->res->start + MMC_RXFIFO; + config.dst_addr = host->res->start + MMC_TXFIFO; + config.src_maxburst = 32; + config.dst_maxburst = 32; + if (data->flags & MMC_DATA_READ) { host->dma_dir = DMA_FROM_DEVICE; - dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC; - DRCMR(host->dma_drcmrtx) = 0; - DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD; + direction = DMA_DEV_TO_MEM; + chan = host->dma_chan_rx; } else { host->dma_dir = DMA_TO_DEVICE; - dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; - DRCMR(host->dma_drcmrrx) = 0; - DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD; + direction = DMA_MEM_TO_DEV; + chan = host->dma_chan_tx; } - dcmd |= DCMD_BURST32 | DCMD_WIDTH1; + config.direction = direction; + + ret = dmaengine_slave_config(chan, &config); + if (ret < 0) { + dev_err(mmc_dev(host->mmc), "dma slave config failed\n"); + return; + } - host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + host->dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len, host->dma_dir); - for (i = 0; i < host->dma_len; i++) { - unsigned int length = sg_dma_len(&data->sg[i]); - host->sg_cpu[i].dcmd = dcmd | length; - if (length & 31 && !(data->flags & MMC_DATA_READ)) - host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN; - /* Not aligned to 8-byte boundary? */ - if (sg_dma_address(&data->sg[i]) & 0x7) - dalgn = 1; - if (data->flags & MMC_DATA_READ) { - host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; - host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); - } else { - host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]); - host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; - } - host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * - sizeof(struct pxa_dma_desc); + tx = dmaengine_prep_slave_sg(chan, data->sg, host->dma_len, direction, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n"); + return; } - host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP; - wmb(); - /* - * The PXA27x DMA controller encounters overhead when working with - * unaligned (to 8-byte boundaries) data, so switch on byte alignment - * mode only if we have unaligned data. - */ - if (dalgn) - DALGN |= (1 << host->dma); - else - DALGN &= ~(1 << host->dma); - DDADR(host->dma) = host->sg_dma; + if (!(data->flags & MMC_DATA_READ)) { + tx->callback = pxamci_dma_irq; + tx->callback_param = host; + } + + host->dma_cookie = dmaengine_submit(tx); /* * workaround for erratum #91: @@ -251,7 +251,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) * before starting DMA. */ if (!cpu_is_pxa27x() || data->flags & MMC_DATA_READ) - DCSR(host->dma) = DCSR_RUN; + dma_async_issue_pending(chan); } static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) @@ -343,7 +343,7 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) * enable DMA late */ if (cpu_is_pxa27x() && host->data->flags & MMC_DATA_WRITE) - DCSR(host->dma) = DCSR_RUN; + dma_async_issue_pending(host->dma_chan_tx); } else { pxamci_finish_request(host, host->mrq); } @@ -354,13 +354,17 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) { struct mmc_data *data = host->data; + struct dma_chan *chan; if (!data) return 0; - DCSR(host->dma) = 0; - dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - host->dma_dir); + if (data->flags & MMC_DATA_READ) + chan = host->dma_chan_rx; + else + chan = host->dma_chan_tx; + dma_unmap_sg(chan->device->dev, + data->sg, data->sg_len, host->dma_dir); if (stat & STAT_READ_TIME_OUT) data->error = -ETIMEDOUT; @@ -552,20 +556,37 @@ static const struct mmc_host_ops pxamci_ops = { .enable_sdio_irq = pxamci_enable_sdio_irq, }; -static void pxamci_dma_irq(int dma, void *devid) +static void pxamci_dma_irq(void *param) { - struct pxamci_host *host = devid; - int dcsr = DCSR(dma); - DCSR(dma) = dcsr & ~DCSR_STOPIRQEN; + struct pxamci_host *host = param; + struct dma_tx_state state; + enum dma_status status; + struct dma_chan *chan; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + if (!host->data) + goto out_unlock; - if (dcsr & DCSR_ENDINTR) { + if (host->data->flags & MMC_DATA_READ) + chan = host->dma_chan_rx; + else + chan = host->dma_chan_tx; + + status = dmaengine_tx_status(chan, host->dma_cookie, &state); + + if (likely(status == DMA_COMPLETE)) { writel(BUF_PART_FULL, host->base + MMC_PRTBUF); } else { - pr_err("%s: DMA error on channel %d (DCSR=%#x)\n", - mmc_hostname(host->mmc), dma, dcsr); + pr_err("%s: DMA error on %s channel\n", mmc_hostname(host->mmc), + host->data->flags & MMC_DATA_READ ? "rx" : "tx"); host->data->error = -EIO; pxamci_data_done(host, 0); } + +out_unlock: + spin_unlock_irqrestore(&host->lock, flags); } static irqreturn_t pxamci_detect_irq(int irq, void *devid) @@ -625,7 +646,9 @@ static int pxamci_probe(struct platform_device *pdev) struct mmc_host *mmc; struct pxamci_host *host = NULL; struct resource *r, *dmarx, *dmatx; + struct pxad_param param_rx, param_tx; int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; + dma_cap_mask_t mask; ret = pxamci_of_init(pdev); if (ret) @@ -671,7 +694,6 @@ static int pxamci_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; - host->dma = -1; host->pdata = pdev->dev.platform_data; host->clkrt = CLKRT_OFF; @@ -702,12 +724,6 @@ static int pxamci_probe(struct platform_device *pdev) MMC_CAP_SD_HIGHSPEED; } - host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); - if (!host->sg_cpu) { - ret = -ENOMEM; - goto out; - } - spin_lock_init(&host->lock); host->res = r; host->irq = irq; @@ -728,32 +744,45 @@ static int pxamci_probe(struct platform_device *pdev) writel(64, host->base + MMC_RESTO); writel(host->imask, host->base + MMC_I_MASK); - host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, - pxamci_dma_irq, host); - if (host->dma < 0) { - ret = -EBUSY; - goto out; - } - ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) goto out; platform_set_drvdata(pdev, mmc); - dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmarx) { - ret = -ENXIO; + if (!pdev->dev.of_node) { + dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0); + dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!dmarx || !dmatx) { + ret = -ENXIO; + goto out; + } + param_rx.prio = PXAD_PRIO_LOWEST; + param_rx.drcmr = dmarx->start; + param_tx.prio = PXAD_PRIO_LOWEST; + param_tx.drcmr = dmatx->start; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + host->dma_chan_rx = + dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m_rx, &pdev->dev, "rx"); + if (host->dma_chan_rx == NULL) { + dev_err(&pdev->dev, "unable to request rx dma channel\n"); + ret = -ENODEV; goto out; } - host->dma_drcmrrx = dmarx->start; - dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!dmatx) { - ret = -ENXIO; + host->dma_chan_tx = + dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m_tx, &pdev->dev, "tx"); + if (host->dma_chan_tx == NULL) { + dev_err(&pdev->dev, "unable to request tx dma channel\n"); + ret = -ENODEV; goto out; } - host->dma_drcmrtx = dmatx->start; if (host->pdata) { gpio_cd = host->pdata->gpio_card_detect; @@ -814,12 +843,12 @@ err_gpio_ro: gpio_free(gpio_power); out: if (host) { - if (host->dma >= 0) - pxa_free_dma(host->dma); + if (host->dma_chan_rx) + dma_release_channel(host->dma_chan_rx); + if (host->dma_chan_tx) + dma_release_channel(host->dma_chan_tx); if (host->base) iounmap(host->base); - if (host->sg_cpu) - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); if (host->clk) clk_put(host->clk); } @@ -863,13 +892,12 @@ static int pxamci_remove(struct platform_device *pdev) END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, host->base + MMC_I_MASK); - DRCMR(host->dma_drcmrrx) = 0; - DRCMR(host->dma_drcmrtx) = 0; - free_irq(host->irq, host); - pxa_free_dma(host->dma); + dmaengine_terminate_all(host->dma_chan_rx); + dmaengine_terminate_all(host->dma_chan_tx); + dma_release_channel(host->dma_chan_rx); + dma_release_channel(host->dma_chan_tx); iounmap(host->base); - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); clk_put(host->clk); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index c6b9f6492e1a..886d230f41d0 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -32,6 +32,7 @@ #include "sdhci-esdhc.h" #define ESDHC_CTRL_D3CD 0x08 +#define ESDHC_BURST_LEN_EN_INCR (1 << 27) /* VENDOR SPEC register */ #define ESDHC_VENDOR_SPEC 0xc0 #define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1) @@ -44,6 +45,7 @@ #define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22) #define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23) #define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25) +#define ESDHC_MIX_CTRL_HS400_EN (1 << 26) /* Bits 3 and 6 are not SDHCI standard definitions */ #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7 /* Tuning bits */ @@ -60,10 +62,21 @@ #define ESDHC_TUNE_CTRL_MIN 0 #define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1) +/* strobe dll register */ +#define ESDHC_STROBE_DLL_CTRL 0x70 +#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0) +#define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1) +#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3 + +#define ESDHC_STROBE_DLL_STATUS 0x74 +#define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1) +#define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1 + #define ESDHC_TUNING_CTRL 0xcc #define ESDHC_STD_TUNING_EN (1 << 24) /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ #define ESDHC_TUNING_START_TAP 0x1 +#define ESDHC_TUNING_STEP_SHIFT 16 /* pinctrl state */ #define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz" @@ -120,6 +133,11 @@ #define ESDHC_FLAG_ERR004536 BIT(7) /* The IP supports HS200 mode */ #define ESDHC_FLAG_HS200 BIT(8) +/* The IP supports HS400 mode */ +#define ESDHC_FLAG_HS400 BIT(9) + +/* A higher clock ferquency than this rate requires strobell dll control */ +#define ESDHC_STROBE_DLL_CLK_FREQ 100000000 struct esdhc_soc_data { u32 flags; @@ -156,6 +174,12 @@ static struct esdhc_soc_data usdhc_imx6sx_data = { | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200, }; +static struct esdhc_soc_data usdhc_imx7d_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400, +}; + struct pltfm_imx_data { u32 scratchpad; struct pinctrl *pinctrl; @@ -199,6 +223,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, }, { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, }, { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, }, + { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); @@ -274,6 +299,9 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING; + + if (imx_data->socdata->flags & ESDHC_FLAG_HS400) + val |= SDHCI_SUPPORT_HS400; } } @@ -448,6 +476,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR); u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); + u32 tuning_ctrl; if (val & SDHCI_CTRL_TUNED_CLK) { v |= ESDHC_MIX_CTRL_SMPCLK_SEL; } else { @@ -458,6 +487,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) if (val & SDHCI_CTRL_EXEC_TUNING) { v |= ESDHC_MIX_CTRL_EXE_TUNE; m |= ESDHC_MIX_CTRL_FBCLK_SEL; + tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL); + tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP; + if (imx_data->boarddata.tuning_step) + tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT; + writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL); } else { v &= ~ESDHC_MIX_CTRL_EXE_TUNE; } @@ -774,6 +808,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host, break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: + case MMC_TIMING_MMC_HS400: pinctrl = imx_data->pins_200mhz; break; default: @@ -784,24 +819,68 @@ static int esdhc_change_pinstate(struct sdhci_host *host, return pinctrl_select_state(imx_data->pinctrl, pinctrl); } +/* + * For HS400 eMMC, there is a data_strobe line, this signal is generated + * by the device and used for data output and CRC status response output + * in HS400 mode. The frequency of this signal follows the frequency of + * CLK generated by host. Host receive the data which is aligned to the + * edge of data_strobe line. Due to the time delay between CLK line and + * data_strobe line, if the delay time is larger than one clock cycle, + * then CLK and data_strobe line will misaligned, read error shows up. + * So when the CLK is higher than 100MHz, each clock cycle is short enough, + * host should config the delay target. + */ +static void esdhc_set_strobe_dll(struct sdhci_host *host) +{ + u32 v; + + if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) { + /* force a reset on strobe dll */ + writel(ESDHC_STROBE_DLL_CTRL_RESET, + host->ioaddr + ESDHC_STROBE_DLL_CTRL); + /* + * enable strobe dll ctrl and adjust the delay target + * for the uSDHC loopback read clock + */ + v = ESDHC_STROBE_DLL_CTRL_ENABLE | + (7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); + writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL); + /* wait 1us to make sure strobe dll status register stable */ + udelay(1); + v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS); + if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK)) + dev_warn(mmc_dev(host->mmc), + "warning! HS400 strobe DLL status REF not lock!\n"); + if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK)) + dev_warn(mmc_dev(host->mmc), + "warning! HS400 strobe DLL status SLV not lock!\n"); + } +} + static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) { + u32 m; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; struct esdhc_platform_data *boarddata = &imx_data->boarddata; + /* disable ddr mode and disable HS400 mode */ + m = readl(host->ioaddr + ESDHC_MIX_CTRL); + m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN); + imx_data->is_ddr = 0; + switch (timing) { case MMC_TIMING_UHS_SDR12: case MMC_TIMING_UHS_SDR25: case MMC_TIMING_UHS_SDR50: case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: + writel(m, host->ioaddr + ESDHC_MIX_CTRL); break; case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52: - writel(readl(host->ioaddr + ESDHC_MIX_CTRL) | - ESDHC_MIX_CTRL_DDREN, - host->ioaddr + ESDHC_MIX_CTRL); + m |= ESDHC_MIX_CTRL_DDREN; + writel(m, host->ioaddr + ESDHC_MIX_CTRL); imx_data->is_ddr = 1; if (boarddata->delay_line) { u32 v; @@ -813,6 +892,12 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) writel(v, host->ioaddr + ESDHC_DLL_CTRL); } break; + case MMC_TIMING_MMC_HS400: + m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN; + writel(m, host->ioaddr + ESDHC_MIX_CTRL); + imx_data->is_ddr = 1; + esdhc_set_strobe_dll(host); + break; } esdhc_change_pinstate(host, timing); @@ -886,6 +971,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, if (gpio_is_valid(boarddata->wp_gpio)) boarddata->wp_type = ESDHC_WP_GPIO; + of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step); + if (of_find_property(np, "no-1-8-v", NULL)) boarddata->support_vsel = false; else @@ -1073,10 +1160,26 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) * to something insane. Change it back here. */ if (esdhc_is_usdhc(imx_data)) { - writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL); + writel(0x10401040, host->ioaddr + ESDHC_WTMK_LVL); + host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; host->mmc->caps |= MMC_CAP_1_8V_DDR; + /* + * ROM code will change the bit burst_length_enable setting + * to zero if this usdhc is choosed to boot system. Change + * it back here, otherwise it will impact the performance a + * lot. This bit is used to enable/disable the burst length + * for the external AHB2AXI bridge, it's usefully especially + * for INCR transfer because without burst length indicator, + * the AHB2AXI bridge does not know the burst length in + * advance. And without burst length indicator, AHB INCR + * transfer can only be converted to singles on the AXI side. + */ + writel(readl(host->ioaddr + SDHCI_HOST_CONTROL) + | ESDHC_BURST_LEN_EN_INCR, + host->ioaddr + SDHCI_HOST_CONTROL); + if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200)) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; @@ -1100,6 +1203,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536) host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; + if (imx_data->socdata->flags & ESDHC_FLAG_HS400) + host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400; + if (of_id) err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data); else diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a870c42731d7..163ac9974d91 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -21,7 +21,8 @@ #define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \ SDHCI_QUIRK_NO_BUSY_IRQ | \ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ - SDHCI_QUIRK_PIO_NEEDS_DELAY) + SDHCI_QUIRK_PIO_NEEDS_DELAY | \ + SDHCI_QUIRK_NO_HISPD_BIT) #define ESDHC_SYSTEM_CONTROL 0x2c #define ESDHC_CLOCK_MASK 0x0000fff0 diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4a09f7608c66..4bcee033feda 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -489,6 +489,11 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto pclk_disable; } + /* Vote for maximum clock rate for maximum performance */ + ret = clk_set_rate(msm_host->clk, INT_MAX); + if (ret) + dev_warn(&pdev->dev, "core clock boost failed\n"); + ret = clk_prepare_enable(msm_host->clk); if (ret) goto pclk_disable; diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 21c0c08dfe54..75379cb0fb35 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -63,6 +63,9 @@ static struct sdhci_ops sdhci_arasan_ops = { static struct sdhci_pltfm_data sdhci_arasan_pdata = { .ops = &sdhci_arasan_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, }; #ifdef CONFIG_PM_SLEEP @@ -214,6 +217,7 @@ static int sdhci_arasan_remove(struct platform_device *pdev) static const struct of_device_id sdhci_arasan_of_match[] = { { .compatible = "arasan,sdhci-8.9a" }, + { .compatible = "arasan,sdhci-5.1" }, { .compatible = "arasan,sdhci-4.9a" }, { } }; diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c new file mode 100644 index 000000000000..d1556643a41d --- /dev/null +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -0,0 +1,191 @@ +/* + * Atmel SDMMC controller driver. + * + * Copyright (C) 2015 Atmel, + * 2015 Ludovic Desroches <ludovic.desroches@atmel.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mmc/host.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#include "sdhci-pltfm.h" + +#define SDMMC_CACR 0x230 +#define SDMMC_CACR_CAPWREN BIT(0) +#define SDMMC_CACR_KEY (0x46 << 8) + +struct sdhci_at91_priv { + struct clk *hclock; + struct clk *gck; + struct clk *mainck; +}; + +static const struct sdhci_ops sdhci_at91_sama5d2_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static const struct sdhci_pltfm_data soc_data_sama5d2 = { + .ops = &sdhci_at91_sama5d2_ops, +}; + +static const struct of_device_id sdhci_at91_dt_match[] = { + { .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 }, + {} +}; + +static int sdhci_at91_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct sdhci_pltfm_data *soc_data; + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_at91_priv *priv; + unsigned int caps0, caps1; + unsigned int clk_base, clk_mul; + unsigned int gck_rate, real_gck_rate; + int ret; + + match = of_match_device(sdhci_at91_dt_match, &pdev->dev); + if (!match) + return -EINVAL; + soc_data = match->data; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "unable to allocate private data\n"); + return -ENOMEM; + } + + priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); + if (IS_ERR(priv->mainck)) { + dev_err(&pdev->dev, "failed to get baseclk\n"); + return PTR_ERR(priv->mainck); + } + + priv->hclock = devm_clk_get(&pdev->dev, "hclock"); + if (IS_ERR(priv->hclock)) { + dev_err(&pdev->dev, "failed to get hclock\n"); + return PTR_ERR(priv->hclock); + } + + priv->gck = devm_clk_get(&pdev->dev, "multclk"); + if (IS_ERR(priv->gck)) { + dev_err(&pdev->dev, "failed to get multclk\n"); + return PTR_ERR(priv->gck); + } + + host = sdhci_pltfm_init(pdev, soc_data, 0); + if (IS_ERR(host)) + return PTR_ERR(host); + + /* + * The mult clock is provided by as a generated clock by the PMC + * controller. In order to set the rate of gck, we have to get the + * base clock rate and the clock mult from capabilities. + */ + clk_prepare_enable(priv->hclock); + caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES); + caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1); + clk_base = (caps0 & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; + clk_mul = (caps1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT; + gck_rate = clk_base * 1000000 * (clk_mul + 1); + ret = clk_set_rate(priv->gck, gck_rate); + if (ret < 0) { + dev_err(&pdev->dev, "failed to set gck"); + goto hclock_disable_unprepare; + return -EINVAL; + } + /* + * We need to check if we have the requested rate for gck because in + * some cases this rate could be not supported. If it happens, the rate + * is the closest one gck can provide. We have to update the value + * of clk mul. + */ + real_gck_rate = clk_get_rate(priv->gck); + if (real_gck_rate != gck_rate) { + clk_mul = real_gck_rate / (clk_base * 1000000) - 1; + caps1 &= (~SDHCI_CLOCK_MUL_MASK); + caps1 |= ((clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK); + /* Set capabilities in r/w mode. */ + writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR); + writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1); + /* Set capabilities in ro mode. */ + writel(0, host->ioaddr + SDMMC_CACR); + dev_info(&pdev->dev, "update clk mul to %u as gck rate is %u Hz\n", + clk_mul, real_gck_rate); + } + + clk_prepare_enable(priv->mainck); + clk_prepare_enable(priv->gck); + + pltfm_host = sdhci_priv(host); + pltfm_host->priv = priv; + + ret = mmc_of_parse(host->mmc); + if (ret) + goto clocks_disable_unprepare; + + sdhci_get_of_property(pdev); + + ret = sdhci_add_host(host); + if (ret) + goto clocks_disable_unprepare; + + return 0; + +clocks_disable_unprepare: + clk_disable_unprepare(priv->gck); + clk_disable_unprepare(priv->mainck); +hclock_disable_unprepare: + clk_disable_unprepare(priv->hclock); + sdhci_pltfm_free(pdev); + return ret; +} + +static int sdhci_at91_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_at91_priv *priv = pltfm_host->priv; + + sdhci_pltfm_unregister(pdev); + + clk_disable_unprepare(priv->gck); + clk_disable_unprepare(priv->hclock); + clk_disable_unprepare(priv->mainck); + + return 0; +} + +static struct platform_driver sdhci_at91_driver = { + .driver = { + .name = "sdhci-at91", + .of_match_table = sdhci_at91_dt_match, + .pm = SDHCI_PLTFM_PMOPS, + }, + .probe = sdhci_at91_probe, + .remove = sdhci_at91_remove, +}; + +module_platform_driver(sdhci_at91_driver); + +MODULE_DESCRIPTION("SDHCI driver for at91"); +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 797be7549a15..653f335bef15 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -208,6 +208,12 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) if (clock == 0) return; + /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */ + temp = esdhc_readw(host, SDHCI_HOST_VERSION); + temp = (temp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; + if (temp < VENDOR_V_23) + pre_div = 2; + /* Workaround to reduce the clock frequency for p1010 esdhc */ if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { if (clock > 20000000) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 94f54d2772e8..b3b0a3e4fca1 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -618,6 +618,7 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) static const struct sdhci_pci_fixes sdhci_o2 = { .probe = sdhci_pci_o2_probe, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD, .probe_slot = sdhci_pci_o2_probe_slot, .resume = sdhci_pci_o2_resume, }; diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 0110bae25b7e..884294576356 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -161,8 +161,8 @@ static struct sdhci_pltfm_data sdhci_sirf_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_INVERTED_WRITE_PROTECT | - SDHCI_QUIRK_DELAY_AFTER_POWER, + SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }; static int sdhci_sirf_probe(struct platform_device *pdev) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1dbe93232030..64b7fdbd1a9c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -54,8 +54,7 @@ static void sdhci_finish_command(struct sdhci_host *); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); static int sdhci_pre_dma_transfer(struct sdhci_host *host, - struct mmc_data *data, - struct sdhci_host_next *next); + struct mmc_data *data); static int sdhci_do_get_cd(struct sdhci_host *host); #ifdef CONFIG_PM @@ -207,8 +206,7 @@ EXPORT_SYMBOL_GPL(sdhci_reset); static void sdhci_do_reset(struct sdhci_host *host, u8 mask) { if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT)) + if (!sdhci_do_get_cd(host)) return; } @@ -496,7 +494,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, goto fail; BUG_ON(host->align_addr & host->align_mask); - host->sg_count = sdhci_pre_dma_transfer(host, data, NULL); + host->sg_count = sdhci_pre_dma_transfer(host, data); if (host->sg_count < 0) goto unmap_align; @@ -635,9 +633,11 @@ static void sdhci_adma_table_post(struct sdhci_host *host, } } - if (!data->host_cookie) + if (data->host_cookie == COOKIE_MAPPED) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction); + data->host_cookie = COOKIE_UNMAPPED; + } } static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) @@ -833,7 +833,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) } else { int sg_cnt; - sg_cnt = sdhci_pre_dma_transfer(host, data, NULL); + sg_cnt = sdhci_pre_dma_transfer(host, data); if (sg_cnt <= 0) { /* * This only happens when someone fed @@ -949,11 +949,13 @@ static void sdhci_finish_data(struct sdhci_host *host) if (host->flags & SDHCI_USE_ADMA) sdhci_adma_table_post(host, data); else { - if (!data->host_cookie) + if (data->host_cookie == COOKIE_MAPPED) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + data->host_cookie = COOKIE_UNMAPPED; + } } } @@ -1132,6 +1134,7 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host) preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104); break; case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50); break; case MMC_TIMING_MMC_HS400: @@ -1152,6 +1155,7 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) int real_div = div, clk_mul = 1; u16 clk = 0; unsigned long timeout; + bool switch_base_clk = false; host->mmc->actual_clock = 0; @@ -1189,15 +1193,25 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) <= clock) break; } - /* - * Set Programmable Clock Mode in the Clock - * Control register. - */ - clk = SDHCI_PROG_CLOCK_MODE; - real_div = div; - clk_mul = host->clk_mul; - div--; - } else { + if ((host->max_clk * host->clk_mul / div) <= clock) { + /* + * Set Programmable Clock Mode in the Clock + * Control register. + */ + clk = SDHCI_PROG_CLOCK_MODE; + real_div = div; + clk_mul = host->clk_mul; + div--; + } else { + /* + * Divisor can be too small to reach clock + * speed requirement. Then use the base clock. + */ + switch_base_clk = true; + } + } + + if (!host->clk_mul || switch_base_clk) { /* Version 3.00 divisors must be a multiple of 2. */ if (host->max_clk <= clock) div = 1; @@ -1210,6 +1224,9 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) } real_div = div; div >>= 1; + if ((host->quirks2 & SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN) + && !div && host->max_clk <= 25000000) + div = 1; } } else { /* Version 2.00 divisors must be a power of 2. */ @@ -1559,7 +1576,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) (ios->timing == MMC_TIMING_UHS_SDR25) || (ios->timing == MMC_TIMING_UHS_SDR50) || (ios->timing == MMC_TIMING_UHS_SDR104) || - (ios->timing == MMC_TIMING_UHS_DDR50))) { + (ios->timing == MMC_TIMING_UHS_DDR50) || + (ios->timing == MMC_TIMING_MMC_DDR52))) { u16 preset; sdhci_enable_preset_value(host, true); @@ -1601,15 +1619,21 @@ static int sdhci_do_get_cd(struct sdhci_host *host) if (host->flags & SDHCI_DEVICE_DEAD) return 0; - /* If polling/nonremovable, assume that the card is always present. */ - if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || - (host->mmc->caps & MMC_CAP_NONREMOVABLE)) + /* If nonremovable, assume that the card is always present. */ + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) return 1; - /* Try slot gpio detect */ + /* + * Try slot gpio detect, if defined it take precedence + * over build in controller functionality + */ if (!IS_ERR_VALUE(gpio_cd)) return !!gpio_cd; + /* If polling, assume that the card is always present. */ + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + return 1; + /* Host native card detect */ return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); } @@ -2097,49 +2121,36 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, struct mmc_data *data = mrq->data; if (host->flags & SDHCI_REQ_USE_DMA) { - if (data->host_cookie) + if (data->host_cookie == COOKIE_GIVEN || + data->host_cookie == COOKIE_MAPPED) dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, data->flags & MMC_DATA_WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - mrq->data->host_cookie = 0; + data->host_cookie = COOKIE_UNMAPPED; } } static int sdhci_pre_dma_transfer(struct sdhci_host *host, - struct mmc_data *data, - struct sdhci_host_next *next) + struct mmc_data *data) { int sg_count; - if (!next && data->host_cookie && - data->host_cookie != host->next_data.cookie) { - pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n", - __func__, data->host_cookie, host->next_data.cookie); - data->host_cookie = 0; + if (data->host_cookie == COOKIE_MAPPED) { + data->host_cookie = COOKIE_GIVEN; + return data->sg_count; } - /* Check if next job is already prepared */ - if (next || - (!next && data->host_cookie != host->next_data.cookie)) { - sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, - data->flags & MMC_DATA_WRITE ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - - } else { - sg_count = host->next_data.sg_count; - host->next_data.sg_count = 0; - } + WARN_ON(data->host_cookie == COOKIE_GIVEN); + sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + data->flags & MMC_DATA_WRITE ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); if (sg_count == 0) - return -EINVAL; + return -ENOSPC; - if (next) { - next->sg_count = sg_count; - data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; - } else - host->sg_count = sg_count; + data->sg_count = sg_count; + data->host_cookie = COOKIE_MAPPED; return sg_count; } @@ -2149,16 +2160,10 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, { struct sdhci_host *host = mmc_priv(mmc); - if (mrq->data->host_cookie) { - mrq->data->host_cookie = 0; - return; - } + mrq->data->host_cookie = COOKIE_UNMAPPED; if (host->flags & SDHCI_REQ_USE_DMA) - if (sdhci_pre_dma_transfer(host, - mrq->data, - &host->next_data) < 0) - mrq->data->host_cookie = 0; + sdhci_pre_dma_transfer(host, mrq->data); } static void sdhci_card_event(struct mmc_host *mmc) @@ -3030,7 +3035,6 @@ int sdhci_add_host(struct sdhci_host *host) host->max_clk = host->ops->get_max_clock(host); } - host->next_data.cookie = 1; /* * In case of Host Controller v3.00, find out whether clock * multiplier is supported. @@ -3126,7 +3130,8 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && - !(mmc->caps & MMC_CAP_NONREMOVABLE)) + !(mmc->caps & MMC_CAP_NONREMOVABLE) && + IS_ERR_VALUE(mmc_gpio_get_cd(host->mmc))) mmc->caps |= MMC_CAP_NEEDS_POLL; /* If there are external regulators, get them */ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 5521d29368e4..7c02ff46c8ac 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -309,9 +309,10 @@ struct sdhci_adma2_64_desc { */ #define SDHCI_MAX_SEGS 128 -struct sdhci_host_next { - unsigned int sg_count; - s32 cookie; +enum sdhci_cookie { + COOKIE_UNMAPPED, + COOKIE_MAPPED, + COOKIE_GIVEN, }; struct sdhci_host { @@ -409,6 +410,8 @@ struct sdhci_host { #define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13) /* Controller broken with using ACMD23 */ #define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14) +/* Broken Clock divider zero in controller */ +#define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1<<15) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ @@ -503,7 +506,6 @@ struct sdhci_host { unsigned int tuning_mode; /* Re-tuning mode supported by host */ #define SDHCI_TUNING_MODE_1 0 - struct sdhci_host_next next_data; unsigned long private[0] ____cacheline_aligned; }; diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 5a1fdd405b1a..ad9ffea7d659 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1632,7 +1632,9 @@ static int sh_mmcif_suspend(struct device *dev) { struct sh_mmcif_host *host = dev_get_drvdata(dev); + pm_runtime_get_sync(dev); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); + pm_runtime_put(dev); return 0; } diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 4d3e1ffe5508..a7b7a6771598 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -595,7 +595,7 @@ static irqreturn_t sunxi_mmc_handle_manual_stop(int irq, void *dev_id) static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) { - unsigned long expire = jiffies + msecs_to_jiffies(250); + unsigned long expire = jiffies + msecs_to_jiffies(750); u32 rval; rval = mmc_readl(host, REG_CLKCR); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index e3dcf31a8bd6..a10fde40b6c3 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -83,6 +83,8 @@ static int tmio_mmc_next_sg(struct tmio_mmc_host *host) return --host->sg_len; } +#define CMDREQ_TIMEOUT 5000 + #ifdef CONFIG_MMC_DEBUG #define STATUS_TO_TEXT(a, status, i) \ @@ -230,7 +232,7 @@ static void tmio_mmc_reset_work(struct work_struct *work) */ if (IS_ERR_OR_NULL(mrq) || time_is_after_jiffies(host->last_req_ts + - msecs_to_jiffies(2000))) { + msecs_to_jiffies(CMDREQ_TIMEOUT))) { spin_unlock_irqrestore(&host->lock, flags); return; } @@ -818,7 +820,7 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ret = tmio_mmc_start_command(host, mrq->cmd); if (!ret) { schedule_delayed_work(&host->delayed_reset_work, - msecs_to_jiffies(2000)); + msecs_to_jiffies(CMDREQ_TIMEOUT)); return; } diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index 54b082b1804a..4498e92116b8 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -1611,7 +1611,7 @@ static irqreturn_t usdhi6_cd(int irq, void *dev_id) return IRQ_NONE; /* Ack */ - usdhi6_write(host, USDHI6_SD_INFO1, !status); + usdhi6_write(host, USDHI6_SD_INFO1, ~status); if (!work_pending(&mmc->detect.work) && (((status & USDHI6_SD_INFO1_CARD_INSERT) && @@ -1634,6 +1634,7 @@ static void usdhi6_timeout_work(struct work_struct *work) struct usdhi6_host *host = container_of(d, struct usdhi6_host, timeout_work); struct mmc_request *mrq = host->mrq; struct mmc_data *data = mrq ? mrq->data : NULL; + struct scatterlist *sg = host->sg ?: data->sg; dev_warn(mmc_dev(host->mmc), "%s timeout wait %u CMD%d: IRQ 0x%08x:0x%08x, last IRQ 0x%08x\n", @@ -1669,7 +1670,7 @@ static void usdhi6_timeout_work(struct work_struct *work) "%c: page #%u @ +0x%zx %ux%u in SG%u. Current SG %u bytes @ %u\n", data->flags & MMC_DATA_READ ? 'R' : 'W', host->page_idx, host->offset, data->blocks, data->blksz, data->sg_len, - sg_dma_len(host->sg), host->sg->offset); + sg_dma_len(sg), sg->offset); usdhi6_sg_unmap(host, true); /* * If USDHI6_WAIT_FOR_DATA_END times out, we have already unmapped @@ -1715,12 +1716,14 @@ static int usdhi6_probe(struct platform_device *pdev) if (!mmc) return -ENOMEM; + ret = mmc_regulator_get_supply(mmc); + if (ret == -EPROBE_DEFER) + goto e_free_mmc; + ret = mmc_of_parse(mmc); if (ret < 0) goto e_free_mmc; - mmc_regulator_get_supply(mmc); - host = mmc_priv(mmc); host->mmc = mmc; host->wait = USDHI6_WAIT_FOR_REQUEST; @@ -1734,8 +1737,10 @@ static int usdhi6_probe(struct platform_device *pdev) } host->clk = devm_clk_get(dev, NULL); - if (IS_ERR(host->clk)) + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); goto e_free_mmc; + } host->imclk = clk_get_rate(host->clk); |