diff options
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); |