diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-31 21:36:58 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-31 21:36:58 -0400 |
| commit | 07f00f06ba9a5533d6650d46d3e938f6cbeee97e (patch) | |
| tree | 854142fbef263efe8c5b3e9d7450cb44b7fb84c6 /drivers/mmc/core/mmc.c | |
| parent | 27acbec338113a75b9d72aeb53149a3538031dda (diff) | |
| parent | 6ea6257945188ff7f5d1670d5adc964ac78c590c (diff) | |
| download | linux-07f00f06ba9a5533d6650d46d3e938f6cbeee97e.tar.gz linux-07f00f06ba9a5533d6650d46d3e938f6cbeee97e.tar.bz2 linux-07f00f06ba9a5533d6650d46d3e938f6cbeee97e.zip | |
Merge tag 'mmc-v4.8' of git://git.linaro.org/people/ulf.hansson/mmc
Pull MMC updates from Ulf Hansson:
"MMC core:
- A couple of changes to improve the support for erase/discard/trim cmds
- Add eMMC HS400 enhanced strobe support
- Show OCR and DSR registers in SYSFS for MMC/SD cards
- Correct and improve busy detection logic for MMC switch (CMD6) cmds
- Disable HPI cmds for certain broken Hynix eMMC cards
- Allow MMC hosts to specify non-support for SD and MMC cmds
- Some minor additional fixes
MMC host:
- sdhci: Re-works, fixes and clean-ups
- sdhci: Add HW auto re-tuning support
- sdhci: Re-factor code to prepare for adding support for eMMC CMDQ
- sdhci-esdhc-imx: Fixes and clean-ups
- sdhci-esdhc-imx: Update system PM support
- sdhci-esdhc-imx: Enable HW auto re-tuning
- sdhci-bcm2835: Remove driver as sdhci-iproc is used instead
- sdhci-brcmstb: Add new driver for Broadcom BRCMSTB SoCs
- sdhci-msm: Add support for UHS cards
- sdhci-tegra: Improve support for UHS cards
- sdhci-of-arasan: Update phy support for Rockchip SoCs
- sdhci-of-arasan: Deploy enhanced strobe support
- dw_mmc: Some fixes and clean-ups
- dw_mmc: Enable support for erase/discard/trim cmds
- dw_mmc: Enable CMD23 support
- mediatek: Some fixes related to the eMMC HS400 support
- sh_mmcif: Improve support for HW busy detection
- rtsx_pci: Enable support for erase/discard/trim cmds"
* tag 'mmc-v4.8' of git://git.linaro.org/people/ulf.hansson/mmc: (135 commits)
mmc: rtsx_pci: Remove deprecated create_singlethread_workqueue
mmc: rtsx_pci: Enable MMC_CAP_ERASE to allow erase/discard/trim requests
mmc: rtsx_pci: Use the provided busy timeout from the mmc core
mmc: sdhci-pltfm: Drop define for SDHCI_PLTFM_PMOPS
mmc: sdhci-pltfm: Convert to use the SET_SYSTEM_SLEEP_PM_OPS
mmc: sdhci-pltfm: Make sdhci_pltfm_suspend|resume() static
mmc: sdhci-esdhc-imx: Use common sdhci_suspend|resume_host()
mmc: sdhci-esdhc-imx: Assign system PM ops within #ifdef CONFIG_PM_SLEEP
mmc: sdhci-sirf: Remove non needed #ifdef CONFIG_PM* for dev_pm_ops
mmc: sdhci-s3c: Remove non needed #ifdef CONFIG_PM for dev_pm_ops
mmc: sdhci-pxav3: Remove non needed #ifdef CONFIG_PM for dev_pm_ops
mmc: sdhci-of-esdhc: Simplify code by using SIMPLE_DEV_PM_OPS
mmc: sdhci-acpi: Simplify code by using SET_SYSTEM_SLEEP_PM_OPS
mmc: sdhci-pci-core: Simplify code by using SET_SYSTEM_SLEEP_PM_OPS
mmc: Change the max discard sectors and erase response when HW busy detect
phy: rockchip-emmc: Wait even longer for the DLL to lock
phy: rockchip-emmc: Be tolerant to card clock of 0 in power on
mmc: sdhci-of-arasan: Revert: Always power the PHY off/on when clock changes
mmc: sdhci-msm: Add support for UHS cards
mmc: sdhci-msm: Add set_uhs_signaling() implementation
...
Diffstat (limited to 'drivers/mmc/core/mmc.c')
| -rw-r--r-- | drivers/mmc/core/mmc.c | 253 |
1 files changed, 174 insertions, 79 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 5d438ad3ee32..f2d185cf8a8b 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -45,6 +45,17 @@ static const unsigned int tacc_mant[] = { 35, 40, 45, 50, 55, 60, 70, 80, }; +static const struct mmc_fixup mmc_ext_csd_fixups[] = { + /* + * Certain Hynix eMMC 4.41 cards might get broken when HPI feature + * is used so disable the HPI feature for such buggy cards. + */ + MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_HYNIX, + 0x014a, add_quirk, MMC_QUIRK_BROKEN_HPI, 5), + + END_FIXUP +}; + #define UNSTUFF_BITS(resp,start,size) \ ({ \ const int __size = size; \ @@ -235,6 +246,11 @@ static void mmc_select_card_type(struct mmc_card *card) avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V; } + if ((caps2 & MMC_CAP2_HS400_ES) && + card->ext_csd.strobe_support && + (avail_type & EXT_CSD_CARD_TYPE_HS400)) + avail_type |= EXT_CSD_CARD_TYPE_HS400ES; + card->ext_csd.hs_max_dtr = hs_max_dtr; card->ext_csd.hs200_max_dtr = hs200_max_dtr; card->mmc_avail_type = avail_type; @@ -370,6 +386,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) */ card->ext_csd.rev = ext_csd[EXT_CSD_REV]; + /* fixup device after ext_csd revision field is updated */ + mmc_fixup_device(card, mmc_ext_csd_fixups); + card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0]; card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1]; card->ext_csd.raw_sectors[2] = ext_csd[EXT_CSD_SEC_CNT + 2]; @@ -386,6 +405,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) mmc_card_set_blockaddr(card); } + card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT]; card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE]; mmc_select_card_type(card); @@ -500,7 +520,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->cid.year += 16; /* check whether the eMMC card supports BKOPS */ - if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { + if (!mmc_card_broken_hpi(card) && + ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { card->ext_csd.bkops = 1; card->ext_csd.man_bkops_en = (ext_csd[EXT_CSD_BKOPS_EN] & @@ -513,7 +534,8 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) } /* check whether the eMMC card supports HPI */ - if (!broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) { + if (!mmc_card_broken_hpi(card) && + !broken_hpi && (ext_csd[EXT_CSD_HPI_FEATURES] & 0x1)) { card->ext_csd.hpi = 1; if (ext_csd[EXT_CSD_HPI_FEATURES] & 0x2) card->ext_csd.hpi_cmd = MMC_STOP_TRANSMISSION; @@ -727,6 +749,7 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size); MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult); MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); +MMC_DEV_ATTR(ocr, "%08x\n", card->ocr); static ssize_t mmc_fwrev_show(struct device *dev, struct device_attribute *attr, @@ -744,6 +767,22 @@ static ssize_t mmc_fwrev_show(struct device *dev, static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL); +static ssize_t mmc_dsr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + + if (card->csd.dsr_imp && host->dsr_req) + return sprintf(buf, "0x%x\n", host->dsr); + else + /* return default DSR value */ + return sprintf(buf, "0x%x\n", 0x404); +} + +static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL); + static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, &dev_attr_csd.attr, @@ -762,6 +801,8 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_enhanced_area_size.attr, &dev_attr_raw_rpmb_size_mult.attr, &dev_attr_rel_sectors.attr, + &dev_attr_ocr.attr, + &dev_attr_dsr.attr, NULL, }; ATTRIBUTE_GROUPS(mmc_std); @@ -959,6 +1000,19 @@ static int mmc_select_bus_width(struct mmc_card *card) return err; } +/* Caller must hold re-tuning */ +static int mmc_switch_status(struct mmc_card *card) +{ + u32 status; + int err; + + err = mmc_send_status(card, &status); + if (err) + return err; + + return mmc_switch_status_error(card->host, status); +} + /* * Switch to the high-speed mode */ @@ -969,9 +1023,11 @@ static int mmc_select_hs(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time, - true, true, true); - if (!err) + true, false, true); + if (!err) { mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + err = mmc_switch_status(card); + } return err; } @@ -1047,23 +1103,9 @@ static int mmc_select_hs_ddr(struct mmc_card *card) return err; } -/* Caller must hold re-tuning */ -static int mmc_switch_status(struct mmc_card *card) -{ - u32 status; - int err; - - err = mmc_send_status(card, &status); - if (err) - return err; - - return mmc_switch_status_error(card->host, status); -} - static int mmc_select_hs400(struct mmc_card *card) { struct mmc_host *host = card->host; - bool send_status = true; unsigned int max_dtr; int err = 0; u8 val; @@ -1075,19 +1117,12 @@ static int mmc_select_hs400(struct mmc_card *card) host->ios.bus_width == MMC_BUS_WIDTH_8)) return 0; - if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) - send_status = false; - - /* Reduce frequency to HS frequency */ - max_dtr = card->ext_csd.hs_max_dtr; - mmc_set_clock(host, max_dtr); - /* Switch card to HS mode */ val = EXT_CSD_TIMING_HS; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) { pr_err("%s: switch to high-speed from hs200 failed, err:%d\n", mmc_hostname(host), err); @@ -1097,11 +1132,13 @@ static int mmc_select_hs400(struct mmc_card *card) /* Set host controller to HS timing */ mmc_set_timing(card->host, MMC_TIMING_MMC_HS); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + /* Reduce frequency to HS frequency */ + max_dtr = card->ext_csd.hs_max_dtr; + mmc_set_clock(host, max_dtr); + + err = mmc_switch_status(card); + if (err) + goto out_err; /* Switch card to DDR */ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, @@ -1120,7 +1157,7 @@ static int mmc_select_hs400(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) { pr_err("%s: switch to hs400 failed, err:%d\n", mmc_hostname(host), err); @@ -1131,11 +1168,9 @@ static int mmc_select_hs400(struct mmc_card *card) mmc_set_timing(host, MMC_TIMING_MMC_HS400); mmc_set_bus_speed(card); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + err = mmc_switch_status(card); + if (err) + goto out_err; return 0; @@ -1153,14 +1188,10 @@ int mmc_hs200_to_hs400(struct mmc_card *card) int mmc_hs400_to_hs200(struct mmc_card *card) { struct mmc_host *host = card->host; - bool send_status = true; unsigned int max_dtr; int err; u8 val; - if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) - send_status = false; - /* Reduce frequency to HS */ max_dtr = card->ext_csd.hs_max_dtr; mmc_set_clock(host, max_dtr); @@ -1169,49 +1200,43 @@ int mmc_hs400_to_hs200(struct mmc_card *card) val = EXT_CSD_TIMING_HS; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) goto out_err; mmc_set_timing(host, MMC_TIMING_MMC_DDR52); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + err = mmc_switch_status(card); + if (err) + goto out_err; /* Switch HS DDR to HS */ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) goto out_err; mmc_set_timing(host, MMC_TIMING_MMC_HS); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + err = mmc_switch_status(card); + if (err) + goto out_err; /* Switch HS to HS200 */ val = EXT_CSD_TIMING_HS200 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, - val, card->ext_csd.generic_cmd6_time, true, - send_status, true); + val, card->ext_csd.generic_cmd6_time, + true, false, true); if (err) goto out_err; mmc_set_timing(host, MMC_TIMING_MMC_HS200); - if (!send_status) { - err = mmc_switch_status(card); - if (err) - goto out_err; - } + err = mmc_switch_status(card); + if (err) + goto out_err; mmc_set_bus_speed(card); @@ -1223,6 +1248,78 @@ out_err: return err; } +static int mmc_select_hs400es(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + u8 val; + + if (!(host->caps & MMC_CAP_8_BIT_DATA)) { + err = -ENOTSUPP; + goto out_err; + } + + err = mmc_select_bus_width(card); + if (err < 0) + goto out_err; + + /* Switch card to HS mode */ + err = mmc_select_hs(card); + if (err) { + pr_err("%s: switch to high-speed failed, err:%d\n", + mmc_hostname(host), err); + goto out_err; + } + + err = mmc_switch_status(card); + if (err) + goto out_err; + + /* Switch card to DDR with strobe bit */ + val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE; + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + val, + card->ext_csd.generic_cmd6_time); + if (err) { + pr_err("%s: switch to bus width for hs400es failed, err:%d\n", + mmc_hostname(host), err); + goto out_err; + } + + /* Switch card to HS400 */ + val = EXT_CSD_TIMING_HS400 | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, val, + card->ext_csd.generic_cmd6_time, + true, false, true); + if (err) { + pr_err("%s: switch to hs400es failed, err:%d\n", + mmc_hostname(host), err); + goto out_err; + } + + /* Set host controller to HS400 timing and frequency */ + mmc_set_timing(host, MMC_TIMING_MMC_HS400); + + /* Controller enable enhanced strobe function */ + host->ios.enhanced_strobe = true; + if (host->ops->hs400_enhanced_strobe) + host->ops->hs400_enhanced_strobe(host, &host->ios); + + err = mmc_switch_status(card); + if (err) + goto out_err; + + return 0; + +out_err: + pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host), + __func__, err); + return err; +} + static void mmc_select_driver_type(struct mmc_card *card) { int card_drv_type, drive_strength, drv_type; @@ -1250,7 +1347,6 @@ static void mmc_select_driver_type(struct mmc_card *card) static int mmc_select_hs200(struct mmc_card *card) { struct mmc_host *host = card->host; - bool send_status = true; unsigned int old_timing, old_signal_voltage; int err = -EINVAL; u8 val; @@ -1268,34 +1364,30 @@ static int mmc_select_hs200(struct mmc_card *card) mmc_select_driver_type(card); - if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) - send_status = false; - /* * Set the bus width(4 or 8) with host's support and * switch to HS200 mode if bus width is set successfully. */ err = mmc_select_bus_width(card); - if (err >= 0) { + if (err > 0) { val = EXT_CSD_TIMING_HS200 | card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, - true, send_status, true); + true, false, true); if (err) goto err; old_timing = host->ios.timing; mmc_set_timing(host, MMC_TIMING_MMC_HS200); - if (!send_status) { - err = mmc_switch_status(card); - /* - * mmc_select_timing() assumes timing has not changed if - * it is a switch error. - */ - if (err == -EBADMSG) - mmc_set_timing(host, old_timing); - } + + err = mmc_switch_status(card); + /* + * mmc_select_timing() assumes timing has not changed if + * it is a switch error. + */ + if (err == -EBADMSG) + mmc_set_timing(host, old_timing); } err: if (err) { @@ -1310,7 +1402,7 @@ err: } /* - * Activate High Speed or HS200 mode if supported. + * Activate High Speed, HS200 or HS400ES mode if supported. */ static int mmc_select_timing(struct mmc_card *card) { @@ -1319,7 +1411,9 @@ static int mmc_select_timing(struct mmc_card *card) if (!mmc_can_ext_csd(card)) goto bus_speed; - if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) + err = mmc_select_hs400es(card); + else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) err = mmc_select_hs200(card); else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) err = mmc_select_hs(card); @@ -1583,7 +1677,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } else if (mmc_card_hs(card)) { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); - if (err >= 0) { + if (err > 0) { err = mmc_select_hs_ddr(card); if (err) goto free_card; @@ -1616,7 +1710,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * If cache size is higher than 0, this indicates * the existence of cache and it can be turned on. */ - if (card->ext_csd.cache_size > 0) { + if (!mmc_card_broken_hpi(card) && + card->ext_csd.cache_size > 0) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CACHE_CTRL, 1, card->ext_csd.generic_cmd6_time); |
