diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-11 12:34:22 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-11 12:34:22 +0200 |
commit | f43b179bbd84d2106c6fb8c7f2db1bfacff19f62 (patch) | |
tree | 581e60846d1298276fac0b768ba12e06d7600d1e /drivers/mmc/core | |
parent | Merge tag 'sound-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/t... (diff) | |
parent | ARM: dts: fix MMC2 regulators for Exynos5420 Arndale Octa board (diff) | |
download | linux-f43b179bbd84d2106c6fb8c7f2db1bfacff19f62.tar.xz linux-f43b179bbd84d2106c6fb8c7f2db1bfacff19f62.zip |
Merge tag 'mmc-v3.18-1' of git://git.linaro.org/people/ulf.hansson/mmc
Pull MMC updates from Ulf Hansson:
"MMC core:
- Fix SDIO IRQ bug
- MMC regulator improvements
- Fix slot-gpio card detect bug
- Add support for Driver Stage Register
- Convert the common MMC OF parser to use GPIO descriptors
- Convert MMC_CAP2_NO_MULTI_READ into a callback, ->multi_io_quirk()
- Some additional minor fixes
MMC host:
- mmci: Support Qualcomm specific DML layer for DMA
- dw_mmc: Use common MMC regulators
- dw_mmc: Add support for Rock-chips RK3288
- tmio: Enable runtime PM support
- tmio: Add support for R-Car Gen2 SoCs
- tmio: Several fixes and improvements
- omap_hsmmc: Removed Balaji from MAINTAINERS
- jz4740: add DMA and pre/post support
- sdhci: Add support for Intel Braswell
- sdhci: Several fixes and improvements"
* tag 'mmc-v3.18-1' of git://git.linaro.org/people/ulf.hansson/mmc: (119 commits)
ARM: dts: fix MMC2 regulators for Exynos5420 Arndale Octa board
mmc: sdhci-acpi: Fix Braswell eMMC timeout clock frequency
mmc: sdhci-acpi: Pass HID and UID to probe_slot
mmc: sdhci-acpi: Get UID directly from acpi_device
mmc, sdhci, bcm-kona, LLVMLinux: Remove use of __initconst
mmc: sdhci-pci: Fix Braswell eMMC timeout clock frequency
mmc: sdhci: Let a driver override timeout clock frequency
mmc: sdhci-pci: Add Bay Trail and Braswell SD card detect
mmc: sdhci-pci: Set SDHCI_QUIRK2_STOP_WITH_TC for Intel BYT host controllers
mmc: sdhci-acpi: Add a HID and UID for a SD Card host controller
mmc: sdhci-acpi: Set SDHCI_QUIRK2_STOP_WITH_TC for Intel host controllers
mmc: sdhci: Add quirk for always getting TC with stop cmd
mmc: core: restore detect line inversion semantics
mmc: Fix incorrect warning when setting 0 Hz via debugfs
mmc: Fix use of wrong device in mmc_gpiod_free_cd()
mmc: atmel-mci: fix mismatched section on atmci_cleanup_slot
mmc: rtsx_pci: Set power related cap2 macros
mmc: core: Add new power_mode MMC_POWER_UNDEFINED
mmc: sdhci: execute tuning when device is not busy
mmc: atmel-mci: Release mmc resources on failure in probe
..
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/core.c | 48 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 96 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 263 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 24 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.h | 1 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 41 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 19 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_bus.c | 4 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_irq.c | 11 | ||||
-rw-r--r-- | drivers/mmc/core/slot-gpio.c | 70 |
10 files changed, 371 insertions, 206 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d03a080fb9cd..f26a5f1d926d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -433,8 +433,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host, */ if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) { if (!mmc_interrupt_hpi(host->card)) { - pr_warning("%s: %s: Interrupted sanitize\n", - mmc_hostname(host), __func__); + pr_warn("%s: %s: Interrupted sanitize\n", + mmc_hostname(host), __func__); cmd->error = 0; break; } else { @@ -995,7 +995,7 @@ void mmc_set_chip_select(struct mmc_host *host, int mode) */ static void __mmc_set_clock(struct mmc_host *host, unsigned int hz) { - WARN_ON(hz < host->f_min); + WARN_ON(hz && hz < host->f_min); if (hz > host->f_max) hz = host->f_max; @@ -1221,15 +1221,14 @@ int mmc_regulator_get_ocrmask(struct regulator *supply) int result = 0; int count; int i; + int vdd_uV; + int vdd_mV; count = regulator_count_voltages(supply); if (count < 0) return count; for (i = 0; i < count; i++) { - int vdd_uV; - int vdd_mV; - vdd_uV = regulator_list_voltage(supply, i); if (vdd_uV <= 0) continue; @@ -1238,6 +1237,15 @@ int mmc_regulator_get_ocrmask(struct regulator *supply) result |= mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV); } + if (!result) { + vdd_uV = regulator_get_voltage(supply); + if (vdd_uV <= 0) + return vdd_uV; + + vdd_mV = vdd_uV / 1000; + result = mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV); + } + return result; } EXPORT_SYMBOL_GPL(mmc_regulator_get_ocrmask); @@ -1263,7 +1271,6 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, if (vdd_bit) { int tmp; - int voltage; /* * REVISIT mmc_vddrange_to_ocrmask() may have set some @@ -1280,22 +1287,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, max_uV = min_uV + 100 * 1000; } - /* - * If we're using a fixed/static regulator, don't call - * regulator_set_voltage; it would fail. - */ - voltage = regulator_get_voltage(supply); - - if (!regulator_can_change_voltage(supply)) - min_uV = max_uV = voltage; - - if (voltage < 0) - result = voltage; - else if (voltage < min_uV || voltage > max_uV) - result = regulator_set_voltage(supply, min_uV, max_uV); - else - result = 0; - + result = regulator_set_voltage(supply, min_uV, max_uV); if (result == 0 && !mmc->regulator_enabled) { result = regulator_enable(supply); if (!result) @@ -1425,8 +1417,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) if (!host->ops->start_signal_voltage_switch) return -EPERM; if (!host->ops->card_busy) - pr_warning("%s: cannot verify signal voltage switch\n", - mmc_hostname(host)); + pr_warn("%s: cannot verify signal voltage switch\n", + mmc_hostname(host)); cmd.opcode = SD_SWITCH_VOLTAGE; cmd.arg = 0; @@ -1761,7 +1753,7 @@ void mmc_init_erase(struct mmc_card *card) card->erase_shift = ffs(card->ssr.au) - 1; } else if (card->ext_csd.hc_erase_size) { card->pref_erase = card->ext_csd.hc_erase_size; - } else { + } else if (card->erase_size) { sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11; if (sz < 128) card->pref_erase = 512 * 1024 / 512; @@ -1778,7 +1770,8 @@ void mmc_init_erase(struct mmc_card *card) if (sz) card->pref_erase += card->erase_size - sz; } - } + } else + card->pref_erase = 0; } static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card, @@ -2489,6 +2482,7 @@ void mmc_start_host(struct mmc_host *host) { host->f_init = max(freqs[0], host->f_min); host->rescan_disable = 0; + host->ios.power_mode = MMC_POWER_UNDEFINED; if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) mmc_power_off(host); else diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 95cceae96944..03c53b72a2d6 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -310,9 +310,8 @@ int mmc_of_parse(struct mmc_host *host) { struct device_node *np; u32 bus_width; - bool explicit_inv_wp, gpio_inv_wp = false; - enum of_gpio_flags flags; - int len, ret, gpio; + int len, ret; + bool cap_invert, gpio_invert; if (!host->parent || !host->parent->of_node) return 0; @@ -360,59 +359,62 @@ int mmc_of_parse(struct mmc_host *host) if (of_find_property(np, "non-removable", &len)) { host->caps |= MMC_CAP_NONREMOVABLE; } else { - bool explicit_inv_cd, gpio_inv_cd = false; - - explicit_inv_cd = of_property_read_bool(np, "cd-inverted"); + if (of_property_read_bool(np, "cd-inverted")) + cap_invert = true; + else + cap_invert = false; if (of_find_property(np, "broken-cd", &len)) host->caps |= MMC_CAP_NEEDS_POLL; - gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags); - if (gpio == -EPROBE_DEFER) - return gpio; - if (gpio_is_valid(gpio)) { - if (!(flags & OF_GPIO_ACTIVE_LOW)) - gpio_inv_cd = true; - - ret = mmc_gpio_request_cd(host, gpio, 0); - if (ret < 0) { - dev_err(host->parent, - "Failed to request CD GPIO #%d: %d!\n", - gpio, ret); + ret = mmc_gpiod_request_cd(host, "cd", 0, true, + 0, &gpio_invert); + if (ret) { + if (ret == -EPROBE_DEFER) return ret; - } else { - dev_info(host->parent, "Got CD GPIO #%d.\n", - gpio); + if (ret != -ENOENT) { + dev_err(host->parent, + "Failed to request CD GPIO: %d\n", + ret); } - } - - if (explicit_inv_cd ^ gpio_inv_cd) + } else + dev_info(host->parent, "Got CD GPIO\n"); + + /* + * There are two ways to flag that the CD line is inverted: + * through the cd-inverted flag and by the GPIO line itself + * being inverted from the GPIO subsystem. This is a leftover + * from the times when the GPIO subsystem did not make it + * possible to flag a line as inverted. + * + * If the capability on the host AND the GPIO line are + * both inverted, the end result is that the CD line is + * not inverted. + */ + if (cap_invert ^ gpio_invert) host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; } /* Parse Write Protection */ - explicit_inv_wp = of_property_read_bool(np, "wp-inverted"); - - gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); - if (gpio == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto out; - } - if (gpio_is_valid(gpio)) { - if (!(flags & OF_GPIO_ACTIVE_LOW)) - gpio_inv_wp = true; + if (of_property_read_bool(np, "wp-inverted")) + cap_invert = true; + else + cap_invert = false; - ret = mmc_gpio_request_ro(host, gpio); - if (ret < 0) { - dev_err(host->parent, - "Failed to request WP GPIO: %d!\n", ret); + ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &gpio_invert); + if (ret) { + if (ret == -EPROBE_DEFER) goto out; - } else { - dev_info(host->parent, "Got WP GPIO #%d.\n", - gpio); + if (ret != -ENOENT) { + dev_err(host->parent, + "Failed to request WP GPIO: %d\n", + ret); } - } - if (explicit_inv_wp ^ gpio_inv_wp) + } else + dev_info(host->parent, "Got WP GPIO\n"); + + /* See the comment on CD inversion above */ + if (cap_invert ^ gpio_invert) host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; if (of_find_property(np, "cap-sd-highspeed", &len)) @@ -452,6 +454,14 @@ int mmc_of_parse(struct mmc_host *host) if (of_find_property(np, "mmc-hs400-1_2v", &len)) host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR; + host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr); + if (host->dsr_req && (host->dsr & ~0xffff)) { + dev_err(host->parent, + "device tree specified broken value for DSR: 0x%x, ignoring\n", + host->dsr); + host->dsr_req = 0; + } + return 0; out: diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 1eda8dd8c867..a301a78a2bd1 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -162,6 +162,7 @@ static int mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1); csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); @@ -225,9 +226,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd) "Card will be ignored.\n", mmc_hostname(card->host)); } else { - pr_warning("%s: unable to read " - "EXT_CSD, performance might " - "suffer.\n", + pr_warn("%s: unable to read EXT_CSD, performance might suffer\n", mmc_hostname(card->host)); err = 0; } @@ -298,6 +297,97 @@ static void mmc_select_card_type(struct mmc_card *card) card->mmc_avail_type = avail_type; } +static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd) +{ + u8 hc_erase_grp_sz, hc_wp_grp_sz; + + /* + * Disable these attributes by default + */ + card->ext_csd.enhanced_area_offset = -EINVAL; + card->ext_csd.enhanced_area_size = -EINVAL; + + /* + * Enhanced area feature support -- check whether the eMMC + * card has the Enhanced area enabled. If so, export enhanced + * area offset and size to user by adding sysfs interface. + */ + if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) && + (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) { + if (card->ext_csd.partition_setting_completed) { + hc_erase_grp_sz = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + hc_wp_grp_sz = + ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + + /* + * calculate the enhanced data area offset, in bytes + */ + card->ext_csd.enhanced_area_offset = + (ext_csd[139] << 24) + (ext_csd[138] << 16) + + (ext_csd[137] << 8) + ext_csd[136]; + if (mmc_card_blockaddr(card)) + card->ext_csd.enhanced_area_offset <<= 9; + /* + * calculate the enhanced data area size, in kilobytes + */ + card->ext_csd.enhanced_area_size = + (ext_csd[142] << 16) + (ext_csd[141] << 8) + + ext_csd[140]; + card->ext_csd.enhanced_area_size *= + (size_t)(hc_erase_grp_sz * hc_wp_grp_sz); + card->ext_csd.enhanced_area_size <<= 9; + } else { + pr_warn("%s: defines enhanced area without partition setting complete\n", + mmc_hostname(card->host)); + } + } +} + +static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd) +{ + int idx; + u8 hc_erase_grp_sz, hc_wp_grp_sz; + unsigned int part_size; + + /* + * General purpose partition feature support -- + * If ext_csd has the size of general purpose partitions, + * set size, part_cfg, partition name in mmc_part. + */ + if (ext_csd[EXT_CSD_PARTITION_SUPPORT] & + EXT_CSD_PART_SUPPORT_PART_EN) { + hc_erase_grp_sz = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + hc_wp_grp_sz = + ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + + for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) { + if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] && + !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] && + !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]) + continue; + if (card->ext_csd.partition_setting_completed == 0) { + pr_warn("%s: has partition size defined without partition complete\n", + mmc_hostname(card->host)); + break; + } + part_size = + (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2] + << 16) + + (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] + << 8) + + ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3]; + part_size *= (size_t)(hc_erase_grp_sz * + hc_wp_grp_sz); + mmc_part_add(card, part_size << 19, + EXT_CSD_PART_CONFIG_ACC_GP0 + idx, + "gp%d", idx, false, + MMC_BLK_DATA_AREA_GP); + } + } +} + /* * Decode extended CSD. */ @@ -305,7 +395,6 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) { int err = 0, idx; unsigned int part_size; - u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0; BUG_ON(!card); @@ -402,80 +491,16 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ext_csd[EXT_CSD_TRIM_MULT]; card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT]; if (card->ext_csd.rev >= 4) { - /* - * Enhanced area feature support -- check whether the eMMC - * card has the Enhanced area enabled. If so, export enhanced - * area offset and size to user by adding sysfs interface. - */ - if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) && - (ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) { - hc_erase_grp_sz = - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - hc_wp_grp_sz = - ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] & + EXT_CSD_PART_SETTING_COMPLETED) + card->ext_csd.partition_setting_completed = 1; + else + card->ext_csd.partition_setting_completed = 0; - card->ext_csd.enhanced_area_en = 1; - /* - * calculate the enhanced data area offset, in bytes - */ - card->ext_csd.enhanced_area_offset = - (ext_csd[139] << 24) + (ext_csd[138] << 16) + - (ext_csd[137] << 8) + ext_csd[136]; - if (mmc_card_blockaddr(card)) - card->ext_csd.enhanced_area_offset <<= 9; - /* - * calculate the enhanced data area size, in kilobytes - */ - card->ext_csd.enhanced_area_size = - (ext_csd[142] << 16) + (ext_csd[141] << 8) + - ext_csd[140]; - card->ext_csd.enhanced_area_size *= - (size_t)(hc_erase_grp_sz * hc_wp_grp_sz); - card->ext_csd.enhanced_area_size <<= 9; - } else { - /* - * If the enhanced area is not enabled, disable these - * device attributes. - */ - card->ext_csd.enhanced_area_offset = -EINVAL; - card->ext_csd.enhanced_area_size = -EINVAL; - } + mmc_manage_enhanced_area(card, ext_csd); - /* - * General purpose partition feature support -- - * If ext_csd has the size of general purpose partitions, - * set size, part_cfg, partition name in mmc_part. - */ - if (ext_csd[EXT_CSD_PARTITION_SUPPORT] & - EXT_CSD_PART_SUPPORT_PART_EN) { - if (card->ext_csd.enhanced_area_en != 1) { - hc_erase_grp_sz = - ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; - hc_wp_grp_sz = - ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; - - card->ext_csd.enhanced_area_en = 1; - } + mmc_manage_gp_partitions(card, ext_csd); - for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) { - if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] && - !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] && - !ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]) - continue; - part_size = - (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2] - << 16) + - (ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] - << 8) + - ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3]; - part_size *= (size_t)(hc_erase_grp_sz * - hc_wp_grp_sz); - mmc_part_add(card, part_size << 19, - EXT_CSD_PART_CONFIG_ACC_GP0 + idx, - "gp%d", idx, false, - MMC_BLK_DATA_AREA_GP); - } - } card->ext_csd.sec_trim_mult = ext_csd[EXT_CSD_SEC_TRIM_MULT]; card->ext_csd.sec_erase_mult = @@ -789,8 +814,8 @@ static int __mmc_select_powerclass(struct mmc_card *card, ext_csd->raw_pwr_cl_200_360; break; default: - pr_warning("%s: Voltage range not supported " - "for power class.\n", mmc_hostname(host)); + pr_warn("%s: Voltage range not supported for power class\n", + mmc_hostname(host)); return -EINVAL; } @@ -987,19 +1012,35 @@ static int mmc_select_hs_ddr(struct mmc_card *card) * 1.8V vccq at 3.3V core voltage (vcc) is not required * in the JEDEC spec for DDR. * - * Do not force change in vccq since we are obviously - * working and no change to vccq is needed. + * Even (e)MMC card can support 3.3v to 1.2v vccq, but not all + * host controller can support this, like some of the SDHCI + * controller which connect to an eMMC device. Some of these + * host controller still needs to use 1.8v vccq for supporting + * DDR mode. + * + * So the sequence will be: + * if (host and device can both support 1.2v IO) + * use 1.2v IO; + * else if (host and device can both support 1.8v IO) + * use 1.8v IO; + * so if host and device can only support 3.3v IO, this is the + * last choice. * * WARNING: eMMC rules are NOT the same as SD DDR */ - if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { - err = __mmc_set_signal_voltage(host, - MMC_SIGNAL_VOLTAGE_120); - if (err) - return err; - } + err = -EINVAL; + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); + + if (err && (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_8V)) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); - mmc_set_timing(host, MMC_TIMING_MMC_DDR52); + /* make sure vccq is 3.3v after switching disaster */ + if (err) + err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330); + + if (!err) + mmc_set_timing(host, MMC_TIMING_MMC_DDR52); return err; } @@ -1134,6 +1175,38 @@ bus_speed: return err; } +const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; +EXPORT_SYMBOL(tuning_blk_pattern_4bit); + +const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; +EXPORT_SYMBOL(tuning_blk_pattern_8bit); + /* * Execute tuning sequence to seek the proper bus operating * conditions for HS200 and HS400, which sends CMD21 to the device. @@ -1272,6 +1345,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* + * handling only for cards supporting DSR and hosts requesting + * DSR configuration + */ + if (card->csd.dsr_imp && host->dsr_req) + mmc_set_dsr(host); + + /* * Select card, as all following commands rely on that. */ if (!mmc_host_is_spi(host)) { @@ -1308,7 +1388,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF * bit. This bit will be lost every time after a reset or power off. */ - if (card->ext_csd.enhanced_area_en || + if (card->ext_csd.partition_setting_completed || (card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GROUP_DEF, 1, @@ -1408,8 +1488,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if (err && err != -EBADMSG) goto free_card; if (err) { - pr_warning("%s: Enabling HPI failed\n", - mmc_hostname(card->host)); + pr_warn("%s: Enabling HPI failed\n", + mmc_hostname(card->host)); err = 0; } else card->ext_csd.hpi_en = 1; @@ -1430,9 +1510,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * Only if no error, cache is turned on successfully. */ if (err) { - pr_warning("%s: Cache is supported, " - "but failed to turn on (%d)\n", - mmc_hostname(card->host), err); + pr_warn("%s: Cache is supported, but failed to turn on (%d)\n", + mmc_hostname(card->host), err); card->ext_csd.cache_ctrl = 0; err = 0; } else { diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index f51b5ba3bbea..7911e0510a1d 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -93,6 +93,26 @@ int mmc_deselect_cards(struct mmc_host *host) return _mmc_select_card(host, NULL); } +/* + * Write the value specified in the device tree or board code into the optional + * 16 bit Driver Stage Register. This can be used to tune raise/fall times and + * drive strength of the DAT and CMD outputs. The actual meaning of a given + * value is hardware dependant. + * The presence of the DSR register can be determined from the CSD register, + * bit 76. + */ +int mmc_set_dsr(struct mmc_host *host) +{ + struct mmc_command cmd = {0}; + + cmd.opcode = MMC_SET_DSR; + + cmd.arg = (host->dsr << 16) | 0xffff; + cmd.flags = MMC_RSP_NONE | MMC_CMD_AC; + + return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); +} + int mmc_go_idle(struct mmc_host *host) { int err; @@ -629,8 +649,8 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) int err; if (!card->ext_csd.hpi) { - pr_warning("%s: Card didn't support HPI command\n", - mmc_hostname(card->host)); + pr_warn("%s: Card didn't support HPI command\n", + mmc_hostname(card->host)); return -EINVAL; } diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 80ae9f4e0293..390dac665b2a 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -14,6 +14,7 @@ int mmc_select_card(struct mmc_card *card); int mmc_deselect_cards(struct mmc_host *host); +int mmc_set_dsr(struct mmc_host *host); int mmc_go_idle(struct mmc_host *host); int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); int mmc_all_send_cid(struct mmc_host *host, u32 *cid); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 0c44510bf717..d90a6de7901d 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -127,6 +127,7 @@ static int mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1); csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); @@ -228,8 +229,8 @@ static int mmc_read_ssr(struct mmc_card *card) u32 *ssr; if (!(card->csd.cmdclass & CCC_APP_SPEC)) { - pr_warning("%s: card lacks mandatory SD Status " - "function.\n", mmc_hostname(card->host)); + pr_warn("%s: card lacks mandatory SD Status function\n", + mmc_hostname(card->host)); return 0; } @@ -239,8 +240,8 @@ static int mmc_read_ssr(struct mmc_card *card) err = mmc_app_sd_status(card, ssr); if (err) { - pr_warning("%s: problem reading SD Status " - "register.\n", mmc_hostname(card->host)); + pr_warn("%s: problem reading SD Status register\n", + mmc_hostname(card->host)); err = 0; goto out; } @@ -264,8 +265,8 @@ static int mmc_read_ssr(struct mmc_card *card) card->ssr.erase_offset = eo * 1000; } } else { - pr_warning("%s: SD Status: Invalid Allocation Unit size.\n", - mmc_hostname(card->host)); + pr_warn("%s: SD Status: Invalid Allocation Unit size\n", + mmc_hostname(card->host)); } } out: @@ -285,8 +286,7 @@ static int mmc_read_switch(struct mmc_card *card) return 0; if (!(card->csd.cmdclass & CCC_SWITCH)) { - pr_warning("%s: card lacks mandatory switch " - "function, performance might suffer.\n", + pr_warn("%s: card lacks mandatory switch function, performance might suffer\n", mmc_hostname(card->host)); return 0; } @@ -315,7 +315,7 @@ static int mmc_read_switch(struct mmc_card *card) if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) goto out; - pr_warning("%s: problem reading Bus Speed modes.\n", + pr_warn("%s: problem reading Bus Speed modes\n", mmc_hostname(card->host)); err = 0; @@ -371,8 +371,7 @@ int mmc_sd_switch_hs(struct mmc_card *card) goto out; if ((status[16] & 0xF) != 1) { - pr_warning("%s: Problem switching card " - "into high-speed mode!\n", + pr_warn("%s: Problem switching card into high-speed mode!\n", mmc_hostname(card->host)); err = 0; } else { @@ -439,7 +438,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) return err; if ((status[15] & 0xF) != drive_strength) { - pr_warning("%s: Problem setting drive strength!\n", + pr_warn("%s: Problem setting drive strength!\n", mmc_hostname(card->host)); return 0; } @@ -517,7 +516,7 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status) return err; if ((status[16] & 0xF) != card->sd_bus_speed) - pr_warning("%s: Problem setting bus speed mode!\n", + pr_warn("%s: Problem setting bus speed mode!\n", mmc_hostname(card->host)); else { mmc_set_timing(card->host, timing); @@ -597,7 +596,7 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status) return err; if (((status[15] >> 4) & 0x0F) != current_limit) - pr_warning("%s: Problem setting current limit!\n", + pr_warn("%s: Problem setting current limit!\n", mmc_hostname(card->host)); } @@ -726,8 +725,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) try_again: if (!retries) { ocr &= ~SD_OCR_S18R; - pr_warning("%s: Skipping voltage switch\n", - mmc_hostname(host)); + pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host)); } /* @@ -871,9 +869,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, } if (ro < 0) { - pr_warning("%s: host does not " - "support reading read-only " - "switch. assuming write-enable.\n", + pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n", mmc_hostname(host)); } else if (ro > 0) { mmc_card_set_readonly(card); @@ -954,6 +950,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, } /* + * handling only for cards supporting DSR and hosts requesting + * DSR configuration + */ + if (card->csd.dsr_imp && host->dsr_req) + mmc_set_dsr(host); + + /* * Select card, as all following commands rely on that. */ if (!mmc_host_is_spi(host)) { diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index e636d9e99e4a..2439e717655b 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -216,8 +216,8 @@ static int sdio_enable_wide(struct mmc_card *card) return ret; if ((ctrl & SDIO_BUS_WIDTH_MASK) == SDIO_BUS_WIDTH_RESERVED) - pr_warning("%s: SDIO_CCCR_IF is invalid: 0x%02x\n", - mmc_hostname(card->host), ctrl); + pr_warn("%s: SDIO_CCCR_IF is invalid: 0x%02x\n", + mmc_hostname(card->host), ctrl); /* set as 4-bit bus width */ ctrl &= ~SDIO_BUS_WIDTH_MASK; @@ -605,8 +605,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, try_again: if (!retries) { - pr_warning("%s: Skipping voltage switch\n", - mmc_hostname(host)); + pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host)); ocr &= ~R4_18V_PRESENT; } @@ -992,8 +991,16 @@ static int mmc_sdio_resume(struct mmc_host *host) } } - if (!err && host->sdio_irqs) - wake_up_process(host->sdio_irq_thread); + if (!err && host->sdio_irqs) { + if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { + wake_up_process(host->sdio_irq_thread); + } else if (host->caps & MMC_CAP_SDIO_IRQ) { + mmc_host_clk_hold(host); + host->ops->enable_sdio_irq(host, 1); + mmc_host_clk_release(host); + } + } + mmc_release_host(host); host->pm_flags &= ~MMC_PM_KEEP_POWER; diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 65cf7a7e05ea..6da97b170563 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -178,8 +178,8 @@ static int sdio_bus_remove(struct device *dev) drv->remove(func); if (func->irq_handler) { - pr_warning("WARNING: driver %s did not remove " - "its interrupt handler!\n", drv->name); + pr_warn("WARNING: driver %s did not remove its interrupt handler!\n", + drv->name); sdio_claim_host(func); sdio_release_irq(func); sdio_release_host(func); diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 5cc13c8d35bb..09cc67d028f0 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -69,16 +69,15 @@ static int process_sdio_pending_irqs(struct mmc_host *host) if (pending & (1 << i)) { func = card->sdio_func[i - 1]; if (!func) { - pr_warning("%s: pending IRQ for " - "non-existent function\n", + pr_warn("%s: pending IRQ for non-existent function\n", mmc_card_id(card)); ret = -EINVAL; } else if (func->irq_handler) { func->irq_handler(func); count++; } else { - pr_warning("%s: pending IRQ with no handler\n", - sdio_func_id(func)); + pr_warn("%s: pending IRQ with no handler\n", + sdio_func_id(func)); ret = -EINVAL; } } @@ -208,7 +207,7 @@ static int sdio_card_irq_get(struct mmc_card *card) host->sdio_irqs--; return err; } - } else { + } else if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 1); mmc_host_clk_release(host); @@ -229,7 +228,7 @@ static int sdio_card_irq_put(struct mmc_card *card) if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { atomic_set(&host->sdio_irq_thread_abort, 1); kthread_stop(host->sdio_irq_thread); - } else { + } else if (host->caps & MMC_CAP_SDIO_IRQ) { mmc_host_clk_hold(host); host->ops->enable_sdio_irq(host, 0); mmc_host_clk_release(host); diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 5f89cb83d5f0..69bbf2adb329 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -221,8 +221,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, ctx->override_cd_active_level = true; ctx->cd_gpio = gpio_to_desc(gpio); - mmc_gpiod_request_cd_irq(host); - return 0; } EXPORT_SYMBOL(mmc_gpio_request_cd); @@ -283,6 +281,8 @@ EXPORT_SYMBOL(mmc_gpio_free_cd); * @idx: index of the GPIO to obtain in the consumer * @override_active_level: ignore %GPIO_ACTIVE_LOW flag * @debounce: debounce time in microseconds + * @gpio_invert: will return whether the GPIO line is inverted or not, set + * to NULL to ignore * * Use this function in place of mmc_gpio_request_cd() to use the GPIO * descriptor API. Note that it is paired with mmc_gpiod_free_cd() not @@ -293,7 +293,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd); */ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, - unsigned int debounce) + unsigned int debounce, bool *gpio_invert) { struct mmc_gpio *ctx; struct gpio_desc *desc; @@ -308,20 +308,19 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, if (!con_id) con_id = ctx->cd_label; - desc = devm_gpiod_get_index(host->parent, con_id, idx); + desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN); if (IS_ERR(desc)) return PTR_ERR(desc); - ret = gpiod_direction_input(desc); - if (ret < 0) - return ret; - if (debounce) { ret = gpiod_set_debounce(desc, debounce); if (ret < 0) return ret; } + if (gpio_invert) + *gpio_invert = !gpiod_is_active_low(desc); + ctx->override_cd_active_level = override_active_level; ctx->cd_gpio = desc; @@ -330,6 +329,59 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, EXPORT_SYMBOL(mmc_gpiod_request_cd); /** + * mmc_gpiod_request_ro - request a gpio descriptor for write protection + * @host: mmc host + * @con_id: function within the GPIO consumer + * @idx: index of the GPIO to obtain in the consumer + * @override_active_level: ignore %GPIO_ACTIVE_LOW flag + * @debounce: debounce time in microseconds + * @gpio_invert: will return whether the GPIO line is inverted or not, + * set to NULL to ignore + * + * Use this function in place of mmc_gpio_request_ro() to use the GPIO + * descriptor API. Note that it is paired with mmc_gpiod_free_ro() not + * mmc_gpio_free_ro(). + * + * Returns zero on success, else an error. + */ +int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, + unsigned int idx, bool override_active_level, + unsigned int debounce, bool *gpio_invert) +{ + struct mmc_gpio *ctx; + struct gpio_desc *desc; + int ret; + + ret = mmc_gpio_alloc(host); + if (ret < 0) + return ret; + + ctx = host->slot.handler_priv; + + if (!con_id) + con_id = ctx->ro_label; + + desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + if (debounce) { + ret = gpiod_set_debounce(desc, debounce); + if (ret < 0) + return ret; + } + + if (gpio_invert) + *gpio_invert = !gpiod_is_active_low(desc); + + ctx->override_ro_active_level = override_active_level; + ctx->ro_gpio = desc; + + return 0; +} +EXPORT_SYMBOL(mmc_gpiod_request_ro); + +/** * mmc_gpiod_free_cd - free the card-detection gpio descriptor * @host: mmc host * @@ -348,7 +400,7 @@ void mmc_gpiod_free_cd(struct mmc_host *host) host->slot.cd_irq = -EINVAL; } - devm_gpiod_put(&host->class_dev, ctx->cd_gpio); + devm_gpiod_put(host->parent, ctx->cd_gpio); ctx->cd_gpio = NULL; } |