diff options
47 files changed, 859 insertions, 205 deletions
diff --git a/Documentation/devicetree/bindings/clock/imx8qxp-lpcg.yaml b/Documentation/devicetree/bindings/clock/imx8qxp-lpcg.yaml index 1d5e9bcce4c8..33f3010f48c3 100644 --- a/Documentation/devicetree/bindings/clock/imx8qxp-lpcg.yaml +++ b/Documentation/devicetree/bindings/clock/imx8qxp-lpcg.yaml @@ -62,7 +62,7 @@ examples: }; mmc@5b010000 { - compatible = "fsl,imx8qxp-usdhc"; + compatible = "fsl,imx8qxp-usdhc", "fsl,imx7d-usdhc"; interrupts = <GIC_SPI 232 IRQ_TYPE_LEVEL_HIGH>; reg = <0x5b010000 0x10000>; clocks = <&conn_lpcg IMX_CONN_LPCG_SDHC0_IPG_CLK>, diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml index 10b45966f1b8..e71d13c2d109 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml @@ -21,23 +21,26 @@ description: | properties: compatible: - enum: - - fsl,imx25-esdhc - - fsl,imx35-esdhc - - fsl,imx51-esdhc - - fsl,imx53-esdhc - - fsl,imx6q-usdhc - - fsl,imx6sl-usdhc - - fsl,imx6sx-usdhc - - fsl,imx6ull-usdhc - - fsl,imx7d-usdhc - - fsl,imx7ulp-usdhc - - fsl,imx8mq-usdhc - - fsl,imx8mm-usdhc - - fsl,imx8mn-usdhc - - fsl,imx8mp-usdhc - - fsl,imx8qm-usdhc - - fsl,imx8qxp-usdhc + oneOf: + - enum: + - fsl,imx25-esdhc + - fsl,imx35-esdhc + - fsl,imx51-esdhc + - fsl,imx53-esdhc + - fsl,imx6q-usdhc + - fsl,imx6sl-usdhc + - fsl,imx6sx-usdhc + - fsl,imx6ull-usdhc + - fsl,imx7d-usdhc + - fsl,imx7ulp-usdhc + - items: + - enum: + - fsl,imx8mm-usdhc + - fsl,imx8mn-usdhc + - fsl,imx8mp-usdhc + - fsl,imx8mq-usdhc + - fsl,imx8qxp-usdhc + - const: fsl,imx7d-usdhc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/mmc/microchip,dw-sparx5-sdhci.yaml b/Documentation/devicetree/bindings/mmc/microchip,dw-sparx5-sdhci.yaml new file mode 100644 index 000000000000..55883290543b --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/microchip,dw-sparx5-sdhci.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/microchip,dw-sparx5-sdhci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Sparx5 Mobile Storage Host Controller Binding + +allOf: + - $ref: "mmc-controller.yaml" + +maintainers: + - Lars Povlsen <[email protected]> + +# Everything else is described in the common file +properties: + compatible: + const: microchip,dw-sparx5-sdhci + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + description: + Handle to "core" clock for the sdhci controller. + + clock-names: + items: + - const: core + + microchip,clock-delay: + description: Delay clock to card to meet setup time requirements. + Each step increase by 1.25ns. + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 1 + maximum: 15 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/microchip,sparx5.h> + sdhci0: mmc@600800000 { + compatible = "microchip,dw-sparx5-sdhci"; + reg = <0x00800000 0x1000>; + pinctrl-0 = <&emmc_pins>; + pinctrl-names = "default"; + clocks = <&clks CLK_ID_AUX1>; + clock-names = "core"; + assigned-clocks = <&clks CLK_ID_AUX1>; + assigned-clock-rates = <800000000>; + interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>; + bus-width = <8>; + microchip,clock-delay = <10>; + }; diff --git a/Documentation/devicetree/bindings/mmc/mmc-controller.yaml b/Documentation/devicetree/bindings/mmc/mmc-controller.yaml index b96da0c7f819..f928f66fc59a 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-controller.yaml +++ b/Documentation/devicetree/bindings/mmc/mmc-controller.yaml @@ -14,6 +14,10 @@ description: | that requires the respective functionality should implement them using these definitions. + It is possible to assign a fixed index mmcN to an MMC host controller + (and the corresponding mmcblkN devices) by defining an alias in the + /aliases device tree node. + properties: $nodename: pattern: "^mmc(@.*)?$" diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.yaml b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.yaml index 449215444723..8d625f903856 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.yaml +++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.yaml @@ -20,6 +20,8 @@ properties: reset-gpios: minItems: 1 + # Put some limit to avoid false warnings + maxItems: 32 description: contains a list of GPIO specifiers. The reset GPIOs are asserted at initialization and prior we start the power up procedure of the card. diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index b4c3fd40caeb..6bbf29b5c239 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -50,6 +50,7 @@ properties: - renesas,sdhi-r8a774a1 # RZ/G2M - renesas,sdhi-r8a774b1 # RZ/G2N - renesas,sdhi-r8a774c0 # RZ/G2E + - renesas,sdhi-r8a774e1 # RZ/G2H - renesas,sdhi-r8a7795 # R-Car H3 - renesas,sdhi-r8a7796 # R-Car M3-W - renesas,sdhi-r8a77961 # R-Car M3-W+ diff --git a/Documentation/devicetree/bindings/mmc/sdhci-am654.txt b/Documentation/devicetree/bindings/mmc/sdhci-am654.txt index 6d202f4d9249..b49cbfdd679f 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-am654.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-am654.txt @@ -12,6 +12,8 @@ Required Properties: "ti,am654-sdhci-5.1": SDHCI on AM654 device. "ti,j721e-sdhci-8bit": 8 bit SDHCI on J721E device. "ti,j721e-sdhci-4bit": 4 bit SDHCI on J721E device. + "ti,j7200-sdhci-8bit": 8 bit SDHCI on J7200 device. + "ti,j7200-sdhci-4bit": 4 bit SDHCI on J7200 device. - reg: Must be two entries. - The first should be the sdhci register space - The second should the subsystem/phy register space @@ -33,7 +35,9 @@ Required Properties: These bindings must be provided otherwise the driver will disable the corresponding speed mode (i.e. all nodes must provide at least -legacy) -Optional Properties (Required for ti,am654-sdhci-5.1 and ti,j721e-sdhci-8bit): +Optional Properties (Required for ti,am654-sdhci-5.1, + ti,j721e-sdhci-8bit, + ti,j7200-sdhci-8bit): - ti,trm-icp: DLL trim select - ti,driver-strength-ohm: driver strength in ohms. Valid values are 33, 40, 50, 66 and 100 ohms. diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c index 178954228631..8004dd64d09a 100644 --- a/drivers/memstick/core/ms_block.c +++ b/drivers/memstick/core/ms_block.c @@ -1223,7 +1223,7 @@ static int msb_read_boot_blocks(struct msb_data *msb) } if (be16_to_cpu(page->header.block_id) != MS_BLOCK_BOOT_ID) { - dbg("the pba at %d doesn' contain boot block ID", pba); + dbg("the pba at %d doesn't contain boot block ID", pba); continue; } diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 70207f11a654..c2e70b757dd1 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -68,6 +68,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) { struct mmc_card *card = mmc_dev_to_card(dev); const char *type; + unsigned int i; int retval = 0; switch (card->type) { @@ -98,6 +99,17 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) card->cis.vendor, card->cis.device); if (retval) return retval; + + retval = add_uevent_var(env, "SDIO_REVISION=%u.%u", + card->major_rev, card->minor_rev); + if (retval) + return retval; + + for (i = 0; i < card->num_info; i++) { + retval = add_uevent_var(env, "SDIO_INFO%u=%s", i+1, card->info[i]); + if (retval) + return retval; + } } /* diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index c8fae6611b73..96b2ca1f1b06 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -377,6 +377,20 @@ int mmc_of_parse_voltage(struct device_node *np, u32 *mask) EXPORT_SYMBOL(mmc_of_parse_voltage); /** + * mmc_first_nonreserved_index() - get the first index that is not reserved + */ +static int mmc_first_nonreserved_index(void) +{ + int max; + + max = of_alias_get_highest_id("mmc"); + if (max < 0) + return 0; + + return max + 1; +} + +/** * mmc_alloc_host - initialise the per-host structure. * @extra: sizeof private data structure * @dev: pointer to host device model structure @@ -387,6 +401,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) { int err; struct mmc_host *host; + int alias_id, min_idx, max_idx; host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); if (!host) @@ -395,7 +410,16 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) /* scanning will be enabled when we're ready */ host->rescan_disable = 1; - err = ida_simple_get(&mmc_host_ida, 0, 0, GFP_KERNEL); + alias_id = of_alias_get_id(dev->of_node, "mmc"); + if (alias_id >= 0) { + min_idx = alias_id; + max_idx = alias_id + 1; + } else { + min_idx = mmc_first_nonreserved_index(); + max_idx = 0; + } + + err = ida_simple_get(&mmc_host_ida, min_idx, max_idx, GFP_KERNEL); if (err < 0) { kfree(host); return NULL; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index b3fa193de846..67e95eba0e82 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1168,13 +1168,13 @@ static int mmc_select_hs400(struct mmc_card *card) return err; } - /* Set host controller to HS timing */ - mmc_set_timing(card->host, MMC_TIMING_MMC_HS); - /* Prepare host to downgrade to HS timing */ if (host->ops->hs400_downgrade) host->ops->hs400_downgrade(host); + /* Set host controller to HS timing */ + mmc_set_timing(host, MMC_TIMING_MMC_HS); + /* Reduce frequency to HS frequency */ max_dtr = card->ext_csd.hs_max_dtr; mmc_set_clock(host, max_dtr); @@ -1253,6 +1253,9 @@ int mmc_hs400_to_hs200(struct mmc_card *card) if (err) goto out_err; + if (host->ops->hs400_downgrade) + host->ops->hs400_downgrade(host); + mmc_set_timing(host, MMC_TIMING_MMC_DDR52); err = mmc_switch_status(card, true); @@ -1268,9 +1271,6 @@ int mmc_hs400_to_hs200(struct mmc_card *card) mmc_set_timing(host, MMC_TIMING_MMC_HS); - if (host->ops->hs400_downgrade) - host->ops->hs400_downgrade(host); - err = mmc_switch_status(card, true); if (err) goto out_err; @@ -1763,6 +1763,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, goto free_card; if (mmc_card_hs200(card)) { + host->doing_init_tune = 1; + err = mmc_hs200_tuning(card); if (err) goto free_card; @@ -1770,6 +1772,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_select_hs400(card); if (err) goto free_card; + + host->doing_init_tune = 0; } else if (!mmc_card_hs400es(card)) { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index c21b3cb71775..152e7525ed33 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -2669,22 +2669,22 @@ static const struct mmc_test_case mmc_test_cases[] = { }, { - .name = "Correct xfer_size at write (start failure)", + .name = "Proper xfer_size at write (start failure)", .run = mmc_test_xfersize_write, }, { - .name = "Correct xfer_size at read (start failure)", + .name = "Proper xfer_size at read (start failure)", .run = mmc_test_xfersize_read, }, { - .name = "Correct xfer_size at write (midway failure)", + .name = "Proper xfer_size at write (midway failure)", .run = mmc_test_multi_xfersize_write, }, { - .name = "Correct xfer_size at read (midway failure)", + .name = "Proper xfer_size at read (midway failure)", .run = mmc_test_multi_xfersize_read, }, diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5a2210c25aa7..6f054c449d46 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -709,10 +709,34 @@ static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL); MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor); MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device); +MMC_DEV_ATTR(revision, "%u.%u\n", card->major_rev, card->minor_rev); + +#define sdio_info_attr(num) \ +static ssize_t info##num##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct mmc_card *card = mmc_dev_to_card(dev); \ + \ + if (num > card->num_info) \ + return -ENODATA; \ + if (!card->info[num-1][0]) \ + return 0; \ + return sprintf(buf, "%s\n", card->info[num-1]); \ +} \ +static DEVICE_ATTR_RO(info##num) + +sdio_info_attr(1); +sdio_info_attr(2); +sdio_info_attr(3); +sdio_info_attr(4); static struct attribute *sd_std_attrs[] = { &dev_attr_vendor.attr, &dev_attr_device.attr, + &dev_attr_revision.attr, + &dev_attr_info1.attr, + &dev_attr_info2.attr, + &dev_attr_info3.attr, + &dev_attr_info4.attr, &dev_attr_cid.attr, &dev_attr_csd.attr, &dev_attr_scr.attr, @@ -735,12 +759,18 @@ static struct attribute *sd_std_attrs[] = { static umode_t sd_std_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct mmc_card *card = mmc_dev_to_card(dev); - /* CIS vendor and device ids are available only for Combo cards */ - if ((attr == &dev_attr_vendor.attr || attr == &dev_attr_device.attr) && - card->type != MMC_TYPE_SD_COMBO) + /* CIS vendor and device ids, revision and info string are available only for Combo cards */ + if ((attr == &dev_attr_vendor.attr || + attr == &dev_attr_device.attr || + attr == &dev_attr_revision.attr || + attr == &dev_attr_info1.attr || + attr == &dev_attr_info2.attr || + attr == &dev_attr_info3.attr || + attr == &dev_attr_info4.attr + ) && card->type != MMC_TYPE_SD_COMBO) return 0; return attr->mode; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 7b40553d3934..694a212cbe25 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -29,12 +29,36 @@ MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor); MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device); +MMC_DEV_ATTR(revision, "%u.%u\n", card->major_rev, card->minor_rev); MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr); MMC_DEV_ATTR(rca, "0x%04x\n", card->rca); +#define sdio_info_attr(num) \ +static ssize_t info##num##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct mmc_card *card = mmc_dev_to_card(dev); \ + \ + if (num > card->num_info) \ + return -ENODATA; \ + if (!card->info[num-1][0]) \ + return 0; \ + return sprintf(buf, "%s\n", card->info[num-1]); \ +} \ +static DEVICE_ATTR_RO(info##num) + +sdio_info_attr(1); +sdio_info_attr(2); +sdio_info_attr(3); +sdio_info_attr(4); + static struct attribute *sdio_std_attrs[] = { &dev_attr_vendor.attr, &dev_attr_device.attr, + &dev_attr_revision.attr, + &dev_attr_info1.attr, + &dev_attr_info2.attr, + &dev_attr_info3.attr, + &dev_attr_info4.attr, &dev_attr_ocr.attr, &dev_attr_rca.attr, NULL, diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 3cc928282af7..3d709029e07c 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -28,34 +28,50 @@ #define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) /* show configuration fields */ -#define sdio_config_attr(field, format_string) \ +#define sdio_config_attr(field, format_string, args...) \ static ssize_t \ field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ struct sdio_func *func; \ \ func = dev_to_sdio_func (dev); \ - return sprintf (buf, format_string, func->field); \ + return sprintf(buf, format_string, args); \ } \ static DEVICE_ATTR_RO(field) -sdio_config_attr(class, "0x%02x\n"); -sdio_config_attr(vendor, "0x%04x\n"); -sdio_config_attr(device, "0x%04x\n"); - -static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct sdio_func *func = dev_to_sdio_func (dev); - - return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n", - func->class, func->vendor, func->device); -} -static DEVICE_ATTR_RO(modalias); +sdio_config_attr(class, "0x%02x\n", func->class); +sdio_config_attr(vendor, "0x%04x\n", func->vendor); +sdio_config_attr(device, "0x%04x\n", func->device); +sdio_config_attr(revision, "%u.%u\n", func->major_rev, func->minor_rev); +sdio_config_attr(modalias, "sdio:c%02Xv%04Xd%04X\n", func->class, func->vendor, func->device); + +#define sdio_info_attr(num) \ +static ssize_t info##num##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct sdio_func *func = dev_to_sdio_func(dev); \ + \ + if (num > func->num_info) \ + return -ENODATA; \ + if (!func->info[num-1][0]) \ + return 0; \ + return sprintf(buf, "%s\n", func->info[num-1]); \ +} \ +static DEVICE_ATTR_RO(info##num) + +sdio_info_attr(1); +sdio_info_attr(2); +sdio_info_attr(3); +sdio_info_attr(4); static struct attribute *sdio_dev_attrs[] = { &dev_attr_class.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, + &dev_attr_revision.attr, + &dev_attr_info1.attr, + &dev_attr_info2.attr, + &dev_attr_info3.attr, + &dev_attr_info4.attr, &dev_attr_modalias.attr, NULL, }; @@ -106,6 +122,7 @@ static int sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env) { struct sdio_func *func = dev_to_sdio_func(dev); + unsigned int i; if (add_uevent_var(env, "SDIO_CLASS=%02X", func->class)) @@ -116,6 +133,15 @@ sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env) return -ENOMEM; if (add_uevent_var(env, + "SDIO_REVISION=%u.%u", func->major_rev, func->minor_rev)) + return -ENOMEM; + + for (i = 0; i < func->num_info; i++) { + if (add_uevent_var(env, "SDIO_INFO%u=%s", i+1, func->info[i])) + return -ENOMEM; + } + + if (add_uevent_var(env, "MODALIAS=sdio:c%02Xv%04Xd%04X", func->class, func->vendor, func->device)) return -ENOMEM; diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index e0655278c5c3..44bea5e4aeda 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c @@ -23,9 +23,16 @@ static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func, const unsigned char *buf, unsigned size) { + u8 major_rev, minor_rev; unsigned i, nr_strings; char **buffer, *string; + if (size < 2) + return 0; + + major_rev = buf[0]; + minor_rev = buf[1]; + /* Find all null-terminated (including zero length) strings in the TPLLV1_INFO field. Trailing garbage is ignored. */ buf += 2; @@ -57,9 +64,13 @@ static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func, } if (func) { + func->major_rev = major_rev; + func->minor_rev = minor_rev; func->num_info = nr_strings; func->info = (const char**)buffer; } else { + card->major_rev = major_rev; + card->minor_rev = minor_rev; card->num_info = nr_strings; card->info = (const char**)buffer; } diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9a34c827c96e..0af8b12a8f7d 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -213,6 +213,18 @@ config MMC_SDHCI_OF_DWCMSHC If you have a controller with this interface, say Y or M here. If unsure, say N. +config MMC_SDHCI_OF_SPARX5 + tristate "SDHCI OF support for the MCHP Sparx5 SoC" + depends on MMC_SDHCI_PLTFM + depends on ARCH_SPARX5 + help + This selects the Secure Digital Host Controller Interface (SDHCI) + found in the MCHP Sparx5 SoC. + + If you have a Sparx5 SoC with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_CADENCE tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller" depends on MMC_SDHCI_PLTFM @@ -525,7 +537,7 @@ config MMC_ATMELMCI config MMC_SDHCI_MSM tristate "Qualcomm SDHCI Controller Support" - depends on ARCH_QCOM || (ARM && COMPILE_TEST) + depends on ARCH_QCOM || COMPILE_TEST depends on MMC_SDHCI_PLTFM select MMC_SDHCI_IO_ACCESSORS select MMC_CQHCI @@ -777,7 +789,7 @@ config MMC_CAVIUM_THUNDERX config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" - depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST + depends on ARC || ARM || ARM64 || MIPS || RISCV || CSKY || COMPILE_TEST help This selects support for the Synopsys DesignWare Mobile Storage IP block, this provides host support for SD and MMC interfaces, in both diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 4d5bcb0144a0..451c25fc2c69 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -94,6 +94,7 @@ 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_OF_DWCMSHC) += sdhci-of-dwcmshc.o +obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index a0767790a826..35320bc9dc02 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1406,9 +1406,7 @@ static int bcm2835_probe(struct platform_device *pdev) clk = devm_clk_get(dev, NULL); if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "could not get clk: %d\n", ret); + ret = dev_err_probe(dev, PTR_ERR(clk), "could not get clk\n"); goto err; } diff --git a/drivers/mmc/host/cqhci.c b/drivers/mmc/host/cqhci.c index cfa87dfa73d8..697fe40756bf 100644 --- a/drivers/mmc/host/cqhci.c +++ b/drivers/mmc/host/cqhci.c @@ -376,6 +376,9 @@ static void cqhci_off(struct mmc_host *mmc) else pr_debug("%s: cqhci: CQE off\n", mmc_hostname(mmc)); + if (cq_host->ops->post_disable) + cq_host->ops->post_disable(mmc); + mmc->cqe_on = false; } @@ -580,6 +583,9 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq) __cqhci_enable(cq_host); if (!mmc->cqe_on) { + if (cq_host->ops->pre_enable) + cq_host->ops->pre_enable(mmc); + cqhci_writel(cq_host, 0, CQHCI_CTL); mmc->cqe_on = true; pr_debug("%s: cqhci: CQE on\n", mmc_hostname(mmc)); diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h index 437700179de4..89bf6adbce8c 100644 --- a/drivers/mmc/host/cqhci.h +++ b/drivers/mmc/host/cqhci.h @@ -206,6 +206,8 @@ struct cqhci_host_ops { void (*disable)(struct mmc_host *mmc, bool recovery); void (*update_dcmd_desc)(struct mmc_host *mmc, struct mmc_request *mrq, u64 *data); + void (*pre_enable)(struct mmc_host *mmc); + void (*post_disable)(struct mmc_host *mmc); }; static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index e50a08bce7ef..66d740ee7d45 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -996,7 +996,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id) if (qstatus & MMCST0_RSPDNE) { /* End of command phase */ - end_command = (int) host->cmd; + end_command = host->cmd ? 1 : 0; } if (end_command) @@ -1240,9 +1240,8 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) pdev->id_entry = match->data; ret = mmc_of_parse(mmc); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "could not parse of data: %d\n", ret); + dev_err_probe(&pdev->dev, ret, + "could not parse of data\n"); goto parse_fail; } } else { diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c index eada648b27ec..d9e483432a61 100644 --- a/drivers/mmc/host/dw_mmc-zx.c +++ b/drivers/mmc/host/dw_mmc-zx.c @@ -155,7 +155,6 @@ static int dw_mci_zx_parse_dt(struct dw_mci *host) struct device_node *node; struct dw_mci_zx_priv_data *priv; struct regmap *sysc_base; - int ret; /* syscon is needed only by emmc */ node = of_parse_phandle(np, "zte,aon-syscon", 0); @@ -163,13 +162,9 @@ static int dw_mci_zx_parse_dt(struct dw_mci *host) sysc_base = syscon_node_to_regmap(node); of_node_put(node); - if (IS_ERR(sysc_base)) { - ret = PTR_ERR(sysc_base); - if (ret != -EPROBE_DEFER) - dev_err(host->dev, "Can't get syscon: %d\n", - ret); - return ret; - } + if (IS_ERR(sysc_base)) + return dev_err_probe(host->dev, PTR_ERR(sysc_base), + "Can't get syscon\n"); } else { return 0; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 0fba940544ca..43c5795691fb 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3161,12 +3161,9 @@ int dw_mci_probe(struct dw_mci *host) if (!host->pdata) { host->pdata = dw_mci_parse_dt(host); - if (PTR_ERR(host->pdata) == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (IS_ERR(host->pdata)) { - dev_err(host->dev, "platform data not available\n"); - return -EINVAL; - } + if (IS_ERR(host->pdata)) + return dev_err_probe(host->dev, PTR_ERR(host->pdata), + "platform data not available\n"); } host->biu_clk = devm_clk_get(host->dev, "biu"); diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 81d71010b474..fea0a42010c7 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -991,9 +991,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) ret = mmc_of_parse(mmc); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "could not parse device properties: %d\n", ret); + dev_err_probe(&pdev->dev, ret, "could not parse device properties\n"); goto err_free_host; } diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 08a3b1c05acb..a68b43082f61 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -426,11 +426,9 @@ static int meson_mmc_clk_init(struct meson_host *host) snprintf(name, sizeof(name), "clkin%d", i); clk = devm_clk_get(host->dev, name); - if (IS_ERR(clk)) { - if (clk != ERR_PTR(-EPROBE_DEFER)) - dev_err(host->dev, "Missing clock %s\n", name); - return PTR_ERR(clk); - } + if (IS_ERR(clk)) + return dev_err_probe(host->dev, PTR_ERR(clk), + "Missing clock %s\n", name); mux_parent_names[i] = __clk_get_name(clk); } @@ -521,7 +519,7 @@ static int meson_mmc_resampling_tuning(struct mmc_host *mmc, u32 opcode) val |= ADJUST_ADJ_EN; writel(val, host->regs + host->data->adjust); - if (mmc->doing_retune) + if (mmc_doing_retune(mmc)) dly = FIELD_GET(ADJUST_ADJ_DELAY_MASK, val) + 1; else dly = 0; @@ -1077,12 +1075,8 @@ static int meson_mmc_probe(struct platform_device *pdev) } ret = device_reset_optional(&pdev->dev); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "device reset failed: %d\n", ret); - - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "device reset failed\n"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->regs = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 5055a7eb134a..419d2503472e 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -882,9 +882,9 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, else clock_rate = spi->max_speed_hz; - timeout = data->timeout_ns + + timeout = data->timeout_ns / 1000 + data->timeout_clks * 1000000 / clock_rate; - timeout = usecs_to_jiffies((unsigned int)(timeout / 1000)) + 1; + timeout = usecs_to_jiffies((unsigned int)timeout) + 1; /* Handle scatterlist segments one at a time, with synch for * each 512-byte block diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index b0c27944db7f..ed2b24691b4f 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -2290,6 +2290,26 @@ static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery) } } +static void msdc_cqe_pre_enable(struct mmc_host *mmc) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + u32 reg; + + reg = cqhci_readl(cq_host, CQHCI_CFG); + reg |= CQHCI_ENABLE; + cqhci_writel(cq_host, reg, CQHCI_CFG); +} + +static void msdc_cqe_post_disable(struct mmc_host *mmc) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + u32 reg; + + reg = cqhci_readl(cq_host, CQHCI_CFG); + reg &= ~CQHCI_ENABLE; + cqhci_writel(cq_host, reg, CQHCI_CFG); +} + static const struct mmc_host_ops mt_msdc_ops = { .post_req = msdc_post_req, .pre_req = msdc_pre_req, @@ -2309,6 +2329,8 @@ static const struct mmc_host_ops mt_msdc_ops = { static const struct cqhci_host_ops msdc_cmdq_ops = { .enable = msdc_cqe_enable, .disable = msdc_cqe_disable, + .pre_enable = msdc_cqe_pre_enable, + .post_disable = msdc_cqe_post_disable, }; static void msdc_of_property_parse(struct platform_device *pdev, diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 904f5237d8f7..42eaec40a6d9 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -117,8 +117,12 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host, unsigned int freq, diff, best_freq = 0, diff_min = ~0; int i; - /* tested only on R-Car Gen2+ currently; may work for others */ - if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2)) + /* + * We simply return the current rate if a) we are not on a R-Car Gen2+ + * SoC (may work for others, but untested) or b) if the SCC needs its + * clock during tuning, so we don't change the external clock setup. + */ + if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2) || mmc_doing_tune(host->mmc)) return clk_get_rate(priv->clk); /* @@ -432,6 +436,25 @@ static int renesas_sdhi_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_io return 0; } +static void renesas_sdhi_reset(struct tmio_mmc_host *host) +{ + struct renesas_sdhi *priv = host_to_priv(host); + + renesas_sdhi_reset_scc(host, priv); + renesas_sdhi_reset_hs400_mode(host, priv); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, + ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & + sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); + + if (host->pdata->flags & TMIO_MMC_MIN_RCAR2) + sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, + TMIO_MASK_INIT_RCAR2); +} + #define SH_MOBILE_SDHI_MIN_TAP_ROW 3 static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) @@ -500,10 +523,11 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) return 0; } -static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) +static int renesas_sdhi_execute_tuning(struct mmc_host *mmc, u32 opcode) { + struct tmio_mmc_host *host = mmc_priv(mmc); struct renesas_sdhi *priv = host_to_priv(host); - int i; + int i, ret; priv->tap_num = renesas_sdhi_init_tuning(host); if (!priv->tap_num) @@ -524,14 +548,17 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) /* Set sampling clock position */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, i % priv->tap_num); - if (mmc_send_tuning(host->mmc, opcode, NULL) == 0) + if (mmc_send_tuning(mmc, opcode, NULL) == 0) set_bit(i, priv->taps); if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) == 0) set_bit(i, priv->smpcmp); } - return renesas_sdhi_select_tuning(host); + ret = renesas_sdhi_select_tuning(host); + if (ret < 0) + renesas_sdhi_reset(host); + return ret; } static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap) @@ -631,27 +658,6 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host) return renesas_sdhi_manual_correction(host, use_4tap); } -static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host) -{ - struct renesas_sdhi *priv; - - priv = host_to_priv(host); - - renesas_sdhi_reset_scc(host, priv); - renesas_sdhi_reset_hs400_mode(host, priv); - - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | - sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); - - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, - ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & - sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); - - if (host->pdata->flags & TMIO_MMC_MIN_RCAR2) - sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, - TMIO_MASK_INIT_RCAR2); -} - static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit) { int timeout = 1000; @@ -862,11 +868,9 @@ int renesas_sdhi_probe(struct platform_device *pdev, renesas_sdhi_start_signal_voltage_switch; host->sdcard_irq_setbit_mask = TMIO_STAT_ALWAYS_SET_27; - /* SDR and HS200/400 registers requires HW reset */ if (of_data && of_data->scc_offset) { priv->scc_ctl = host->ctl + of_data->scc_offset; - host->mmc->caps |= MMC_CAP_HW_RESET; - host->hw_reset = renesas_sdhi_hw_reset; + host->reset = renesas_sdhi_reset; } } @@ -943,8 +947,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, if (!hit) dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n"); - host->execute_tuning = renesas_sdhi_execute_tuning; host->check_retune = renesas_sdhi_check_scc_error; + host->ops.execute_tuning = renesas_sdhi_execute_tuning; host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning; host->ops.hs400_downgrade = renesas_sdhi_disable_scc; host->ops.hs400_complete = renesas_sdhi_hs400_complete; diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 444b2769ae2c..a725fb1abce7 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -150,8 +150,8 @@ static void s3cmci_reset(struct s3cmci_host *host); static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) { - u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize; - u32 datcon, datcnt, datsta, fsta, imask; + u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer; + u32 datcon, datcnt, datsta, fsta; con = readl(host->base + S3C2410_SDICON); pre = readl(host->base + S3C2410_SDIPRE); @@ -163,12 +163,10 @@ static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) r2 = readl(host->base + S3C2410_SDIRSP2); r3 = readl(host->base + S3C2410_SDIRSP3); timer = readl(host->base + S3C2410_SDITIMER); - bsize = readl(host->base + S3C2410_SDIBSIZE); datcon = readl(host->base + S3C2410_SDIDCON); datcnt = readl(host->base + S3C2410_SDIDCNT); datsta = readl(host->base + S3C2410_SDIDSTA); fsta = readl(host->base + S3C2410_SDIFSTA); - imask = readl(host->base + host->sdiimsk); dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n", prefix, con, pre, timer); @@ -396,9 +394,6 @@ static void s3cmci_enable_irq(struct s3cmci_host *host, bool more) local_irq_restore(flags); } -/** - * - */ static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer) { unsigned long flags; @@ -1379,7 +1374,7 @@ static int s3cmci_state_show(struct seq_file *seq, void *v) { struct s3cmci_host *host = seq->private; - seq_printf(seq, "Register base = 0x%08x\n", (u32)host->base); + seq_printf(seq, "Register base = 0x%p\n", host->base); seq_printf(seq, "Clock rate = %ld\n", host->clk_rate); seq_printf(seq, "Prescale = %d\n", host->prescaler); seq_printf(seq, "is2440 = %d\n", host->is2440); @@ -1522,7 +1517,7 @@ static int s3cmci_probe_dt(struct s3cmci_host *host) struct mmc_host *mmc = host->mmc; int ret; - host->is2440 = (int) of_device_get_match_data(&pdev->dev); + host->is2440 = (long) of_device_get_match_data(&pdev->dev); ret = mmc_of_parse(mmc); if (ret) diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index ad01f6451a95..876668827580 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -235,13 +235,11 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Probe found match for %s\n", match->compatible); - clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - if (PTR_ERR(clk) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_err(&pdev->dev, "Clock not found in Device Tree\n"); - clk = NULL; - } + clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk), + "Failed to get clock from Device Tree\n"); + res = clk_prepare_enable(clk); if (res) return res; diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index d738907a622f..a2eb36c9b1da 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -987,10 +987,20 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) { u32 reg; + u8 sw_rst; + int ret; /* FIXME: delay a bit for card to be ready for next tuning due to errors */ mdelay(1); + /* IC suggest to reset USDHC before every tuning command */ + esdhc_clrset_le(host, 0xff, SDHCI_RESET_ALL, SDHCI_SOFTWARE_RESET); + ret = readb_poll_timeout(host->ioaddr + SDHCI_SOFTWARE_RESET, sw_rst, + !(sw_rst & SDHCI_RESET_ALL), 10, 100); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), + "warning! RESET_ALL never complete before sending tuning command\n"); + reg = readl(host->ioaddr + ESDHC_MIX_CTRL); reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | ESDHC_MIX_CTRL_FBCLK_SEL; @@ -1643,10 +1653,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) goto disable_ipg_clk; imx_data->pinctrl = devm_pinctrl_get(&pdev->dev); - if (IS_ERR(imx_data->pinctrl)) { - err = PTR_ERR(imx_data->pinctrl); + if (IS_ERR(imx_data->pinctrl)) dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n"); - } if (esdhc_is_usdhc(imx_data)) { host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index e2d8dfe90077..b540aa6faacb 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -283,6 +283,7 @@ static const struct sdhci_pltfm_data sdhci_bcm2711_pltfm_data = { static const struct sdhci_iproc_data bcm2711_data = { .pdata = &sdhci_bcm2711_pltfm_data, + .mmc_caps = MMC_CAP_3_3V_DDR, }; static const struct of_device_id sdhci_iproc_of_match[] = { diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 729868abd2db..23dacab1a399 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -2167,6 +2167,7 @@ static const struct of_device_id sdhci_msm_dt_match[] = { {.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var}, {.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var}, {.compatible = "qcom,sm8250-sdhci", .data = &sm8250_sdhci_var}, + {.compatible = "qcom,sc7180-sdhci", .data = &sdm845_sdhci_var}, {}, }; @@ -2557,6 +2558,7 @@ static struct platform_driver sdhci_msm_driver = { .name = "sdhci_msm", .of_match_table = sdhci_msm_dt_match, .pm = &sdhci_msm_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index f186fbd016b1..3aea46db2ea7 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1543,10 +1543,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev) of_node_put(node); if (IS_ERR(sdhci_arasan->soc_ctl_base)) { - ret = PTR_ERR(sdhci_arasan->soc_ctl_base); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Can't get syscon: %d\n", - ret); + ret = dev_err_probe(&pdev->dev, + PTR_ERR(sdhci_arasan->soc_ctl_base), + "Can't get syscon\n"); goto err_pltfm_free; } } diff --git a/drivers/mmc/host/sdhci-of-sparx5.c b/drivers/mmc/host/sdhci-of-sparx5.c new file mode 100644 index 000000000000..747f108a0ace --- /dev/null +++ b/drivers/mmc/host/sdhci-of-sparx5.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * drivers/mmc/host/sdhci-of-sparx5.c + * + * MCHP Sparx5 SoC Secure Digital Host Controller Interface. + * + * Copyright (c) 2019 Microchip Inc. + * + * Author: Lars Povlsen <[email protected]> + */ + +#include <linux/sizes.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/of_device.h> +#include <linux/mfd/syscon.h> +#include <linux/dma-mapping.h> + +#include "sdhci-pltfm.h" + +#define CPU_REGS_GENERAL_CTRL (0x22 * 4) +#define MSHC_DLY_CC_MASK GENMASK(16, 13) +#define MSHC_DLY_CC_SHIFT 13 +#define MSHC_DLY_CC_MAX 15 + +#define CPU_REGS_PROC_CTRL (0x2C * 4) +#define ACP_CACHE_FORCE_ENA BIT(4) +#define ACP_AWCACHE BIT(3) +#define ACP_ARCACHE BIT(2) +#define ACP_CACHE_MASK (ACP_CACHE_FORCE_ENA|ACP_AWCACHE|ACP_ARCACHE) + +#define MSHC2_VERSION 0x500 /* Off 0x140, reg 0x0 */ +#define MSHC2_TYPE 0x504 /* Off 0x140, reg 0x1 */ +#define MSHC2_EMMC_CTRL 0x52c /* Off 0x140, reg 0xB */ +#define MSHC2_EMMC_CTRL_EMMC_RST_N BIT(2) +#define MSHC2_EMMC_CTRL_IS_EMMC BIT(0) + +struct sdhci_sparx5_data { + struct sdhci_host *host; + struct regmap *cpu_ctrl; + int delay_clock; +}; + +#define BOUNDARY_OK(addr, len) \ + ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) + +/* + * If DMA addr spans 128MB boundary, we split the DMA transfer into two + * so that each DMA transfer doesn't exceed the boundary. + */ +static void sdhci_sparx5_adma_write_desc(struct sdhci_host *host, void **desc, + dma_addr_t addr, int len, + unsigned int cmd) +{ + int tmplen, offset; + + if (likely(!len || BOUNDARY_OK(addr, len))) { + sdhci_adma_write_desc(host, desc, addr, len, cmd); + return; + } + + pr_debug("%s: write_desc: splitting dma len %d, offset %pad\n", + mmc_hostname(host->mmc), len, &addr); + + offset = addr & (SZ_128M - 1); + tmplen = SZ_128M - offset; + sdhci_adma_write_desc(host, desc, addr, tmplen, cmd); + + addr += tmplen; + len -= tmplen; + sdhci_adma_write_desc(host, desc, addr, len, cmd); +} + +static void sparx5_set_cacheable(struct sdhci_host *host, u32 value) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_sparx5_data *sdhci_sparx5 = sdhci_pltfm_priv(pltfm_host); + + pr_debug("%s: Set Cacheable = 0x%x\n", mmc_hostname(host->mmc), value); + + /* Update ACP caching attributes in HW */ + regmap_update_bits(sdhci_sparx5->cpu_ctrl, + CPU_REGS_PROC_CTRL, ACP_CACHE_MASK, value); +} + +static void sparx5_set_delay(struct sdhci_host *host, u8 value) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_sparx5_data *sdhci_sparx5 = sdhci_pltfm_priv(pltfm_host); + + pr_debug("%s: Set DLY_CC = %u\n", mmc_hostname(host->mmc), value); + + /* Update DLY_CC in HW */ + regmap_update_bits(sdhci_sparx5->cpu_ctrl, + CPU_REGS_GENERAL_CTRL, + MSHC_DLY_CC_MASK, + (value << MSHC_DLY_CC_SHIFT)); +} + +static void sdhci_sparx5_set_emmc(struct sdhci_host *host) +{ + if (!mmc_card_is_removable(host->mmc)) { + u8 value; + + value = sdhci_readb(host, MSHC2_EMMC_CTRL); + if (!(value & MSHC2_EMMC_CTRL_IS_EMMC)) { + value |= MSHC2_EMMC_CTRL_IS_EMMC; + pr_debug("%s: Set EMMC_CTRL: 0x%08x\n", + mmc_hostname(host->mmc), value); + sdhci_writeb(host, value, MSHC2_EMMC_CTRL); + } + } +} + +static void sdhci_sparx5_reset_emmc(struct sdhci_host *host) +{ + u8 value; + + pr_debug("%s: Toggle EMMC_CTRL.EMMC_RST_N\n", mmc_hostname(host->mmc)); + value = sdhci_readb(host, MSHC2_EMMC_CTRL) & + ~MSHC2_EMMC_CTRL_EMMC_RST_N; + sdhci_writeb(host, value, MSHC2_EMMC_CTRL); + /* For eMMC, minimum is 1us but give it 10us for good measure */ + usleep_range(10, 20); + sdhci_writeb(host, value | MSHC2_EMMC_CTRL_EMMC_RST_N, + MSHC2_EMMC_CTRL); + /* For eMMC, minimum is 200us but give it 300us for good measure */ + usleep_range(300, 400); +} + +static void sdhci_sparx5_reset(struct sdhci_host *host, u8 mask) +{ + pr_debug("%s: *** RESET: mask %d\n", mmc_hostname(host->mmc), mask); + + sdhci_reset(host, mask); + + /* Be sure CARD_IS_EMMC stays set */ + sdhci_sparx5_set_emmc(host); +} + +static const struct sdhci_ops sdhci_sparx5_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .reset = sdhci_sparx5_reset, + .adma_write_desc = sdhci_sparx5_adma_write_desc, +}; + +static const struct sdhci_pltfm_data sdhci_sparx5_pdata = { + .quirks = 0, + .quirks2 = SDHCI_QUIRK2_HOST_NO_CMD23 | /* Controller issue */ + SDHCI_QUIRK2_NO_1_8_V, /* No sdr104, ddr50, etc */ + .ops = &sdhci_sparx5_ops, +}; + +static int sdhci_sparx5_probe(struct platform_device *pdev) +{ + int ret; + const char *syscon = "microchip,sparx5-cpu-syscon"; + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_sparx5_data *sdhci_sparx5; + struct device_node *np = pdev->dev.of_node; + u32 value; + u32 extra; + + host = sdhci_pltfm_init(pdev, &sdhci_sparx5_pdata, + sizeof(*sdhci_sparx5)); + + if (IS_ERR(host)) + return PTR_ERR(host); + + /* + * extra adma table cnt for cross 128M boundary handling. + */ + extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M); + if (extra > SDHCI_MAX_SEGS) + extra = SDHCI_MAX_SEGS; + host->adma_table_cnt += extra; + + pltfm_host = sdhci_priv(host); + sdhci_sparx5 = sdhci_pltfm_priv(pltfm_host); + sdhci_sparx5->host = host; + + pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(pltfm_host->clk)) { + ret = PTR_ERR(pltfm_host->clk); + dev_err(&pdev->dev, "failed to get core clk: %d\n", ret); + goto free_pltfm; + } + ret = clk_prepare_enable(pltfm_host->clk); + if (ret) + goto free_pltfm; + + if (!of_property_read_u32(np, "microchip,clock-delay", &value) && + (value > 0 && value <= MSHC_DLY_CC_MAX)) + sdhci_sparx5->delay_clock = value; + + sdhci_get_of_property(pdev); + + ret = mmc_of_parse(host->mmc); + if (ret) + goto err_clk; + + sdhci_sparx5->cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon); + if (IS_ERR(sdhci_sparx5->cpu_ctrl)) { + dev_err(&pdev->dev, "No CPU syscon regmap !\n"); + ret = PTR_ERR(sdhci_sparx5->cpu_ctrl); + goto err_clk; + } + + if (sdhci_sparx5->delay_clock >= 0) + sparx5_set_delay(host, sdhci_sparx5->delay_clock); + + if (!mmc_card_is_removable(host->mmc)) { + /* Do a HW reset of eMMC card */ + sdhci_sparx5_reset_emmc(host); + /* Update EMMC_CTRL */ + sdhci_sparx5_set_emmc(host); + /* If eMMC, disable SD and SDIO */ + host->mmc->caps2 |= (MMC_CAP2_NO_SDIO|MMC_CAP2_NO_SD); + } + + ret = sdhci_add_host(host); + if (ret) + goto err_clk; + + /* Set AXI bus master to use un-cached access (for DMA) */ + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA) && + IS_ENABLED(CONFIG_DMA_DECLARE_COHERENT)) + sparx5_set_cacheable(host, ACP_CACHE_FORCE_ENA); + + pr_debug("%s: SDHC version: 0x%08x\n", + mmc_hostname(host->mmc), sdhci_readl(host, MSHC2_VERSION)); + pr_debug("%s: SDHC type: 0x%08x\n", + mmc_hostname(host->mmc), sdhci_readl(host, MSHC2_TYPE)); + + return ret; + +err_clk: + clk_disable_unprepare(pltfm_host->clk); +free_pltfm: + sdhci_pltfm_free(pdev); + return ret; +} + +static const struct of_device_id sdhci_sparx5_of_match[] = { + { .compatible = "microchip,dw-sparx5-sdhci" }, + { } +}; +MODULE_DEVICE_TABLE(of, sdhci_sparx5_of_match); + +static struct platform_driver sdhci_sparx5_driver = { + .driver = { + .name = "sdhci-sparx5", + .of_match_table = sdhci_sparx5_of_match, + .pm = &sdhci_pltfm_pmops, + }, + .probe = sdhci_sparx5_probe, + .remove = sdhci_pltfm_unregister, +}; + +module_platform_driver(sdhci_sparx5_driver); + +MODULE_DESCRIPTION("Sparx5 SDHCI OF driver"); +MODULE_AUTHOR("Lars Povlsen <[email protected]>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index af413805bbf1..d0c8d39d5dbd 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -24,6 +24,8 @@ #include <linux/iopoll.h> #include <linux/gpio.h> #include <linux/pm_runtime.h> +#include <linux/pm_qos.h> +#include <linux/debugfs.h> #include <linux/mmc/slot-gpio.h> #include <linux/mmc/sdhci-pci-data.h> #include <linux/acpi.h> @@ -516,6 +518,8 @@ struct intel_host { bool rpm_retune_ok; u32 glk_rx_ctrl1; u32 glk_tun_val; + u32 active_ltr; + u32 idle_ltr; }; static const guid_t intel_dsm_guid = @@ -760,6 +764,108 @@ static int intel_execute_tuning(struct mmc_host *mmc, u32 opcode) return 0; } +#define INTEL_ACTIVELTR 0x804 +#define INTEL_IDLELTR 0x808 + +#define INTEL_LTR_REQ BIT(15) +#define INTEL_LTR_SCALE_MASK GENMASK(11, 10) +#define INTEL_LTR_SCALE_1US (2 << 10) +#define INTEL_LTR_SCALE_32US (3 << 10) +#define INTEL_LTR_VALUE_MASK GENMASK(9, 0) + +static void intel_cache_ltr(struct sdhci_pci_slot *slot) +{ + struct intel_host *intel_host = sdhci_pci_priv(slot); + struct sdhci_host *host = slot->host; + + intel_host->active_ltr = readl(host->ioaddr + INTEL_ACTIVELTR); + intel_host->idle_ltr = readl(host->ioaddr + INTEL_IDLELTR); +} + +static void intel_ltr_set(struct device *dev, s32 val) +{ + struct sdhci_pci_chip *chip = dev_get_drvdata(dev); + struct sdhci_pci_slot *slot = chip->slots[0]; + struct intel_host *intel_host = sdhci_pci_priv(slot); + struct sdhci_host *host = slot->host; + u32 ltr; + + pm_runtime_get_sync(dev); + + /* + * Program latency tolerance (LTR) accordingly what has been asked + * by the PM QoS layer or disable it in case we were passed + * negative value or PM_QOS_LATENCY_ANY. + */ + ltr = readl(host->ioaddr + INTEL_ACTIVELTR); + + if (val == PM_QOS_LATENCY_ANY || val < 0) { + ltr &= ~INTEL_LTR_REQ; + } else { + ltr |= INTEL_LTR_REQ; + ltr &= ~INTEL_LTR_SCALE_MASK; + ltr &= ~INTEL_LTR_VALUE_MASK; + + if (val > INTEL_LTR_VALUE_MASK) { + val >>= 5; + if (val > INTEL_LTR_VALUE_MASK) + val = INTEL_LTR_VALUE_MASK; + ltr |= INTEL_LTR_SCALE_32US | val; + } else { + ltr |= INTEL_LTR_SCALE_1US | val; + } + } + + if (ltr == intel_host->active_ltr) + goto out; + + writel(ltr, host->ioaddr + INTEL_ACTIVELTR); + writel(ltr, host->ioaddr + INTEL_IDLELTR); + + /* Cache the values into lpss structure */ + intel_cache_ltr(slot); +out: + pm_runtime_put_autosuspend(dev); +} + +static bool intel_use_ltr(struct sdhci_pci_chip *chip) +{ + switch (chip->pdev->device) { + case PCI_DEVICE_ID_INTEL_BYT_EMMC: + case PCI_DEVICE_ID_INTEL_BYT_EMMC2: + case PCI_DEVICE_ID_INTEL_BYT_SDIO: + case PCI_DEVICE_ID_INTEL_BYT_SD: + case PCI_DEVICE_ID_INTEL_BSW_EMMC: + case PCI_DEVICE_ID_INTEL_BSW_SDIO: + case PCI_DEVICE_ID_INTEL_BSW_SD: + return false; + default: + return true; + } +} + +static void intel_ltr_expose(struct sdhci_pci_chip *chip) +{ + struct device *dev = &chip->pdev->dev; + + if (!intel_use_ltr(chip)) + return; + + dev->power.set_latency_tolerance = intel_ltr_set; + dev_pm_qos_expose_latency_tolerance(dev); +} + +static void intel_ltr_hide(struct sdhci_pci_chip *chip) +{ + struct device *dev = &chip->pdev->dev; + + if (!intel_use_ltr(chip)) + return; + + dev_pm_qos_hide_latency_tolerance(dev); + dev->power.set_latency_tolerance = NULL; +} + static void byt_probe_slot(struct sdhci_pci_slot *slot) { struct mmc_host_ops *ops = &slot->host->mmc_host_ops; @@ -774,6 +880,43 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot) ops->start_signal_voltage_switch = intel_start_signal_voltage_switch; device_property_read_u32(dev, "max-frequency", &mmc->f_max); + + if (!mmc->slotno) { + slot->chip->slots[mmc->slotno] = slot; + intel_ltr_expose(slot->chip); + } +} + +static void byt_add_debugfs(struct sdhci_pci_slot *slot) +{ + struct intel_host *intel_host = sdhci_pci_priv(slot); + struct mmc_host *mmc = slot->host->mmc; + struct dentry *dir = mmc->debugfs_root; + + if (!intel_use_ltr(slot->chip)) + return; + + debugfs_create_x32("active_ltr", 0444, dir, &intel_host->active_ltr); + debugfs_create_x32("idle_ltr", 0444, dir, &intel_host->idle_ltr); + + intel_cache_ltr(slot); +} + +static int byt_add_host(struct sdhci_pci_slot *slot) +{ + int ret = sdhci_add_host(slot->host); + + if (!ret) + byt_add_debugfs(slot); + return ret; +} + +static void byt_remove_slot(struct sdhci_pci_slot *slot, int dead) +{ + struct mmc_host *mmc = slot->host->mmc; + + if (!mmc->slotno) + intel_ltr_hide(slot->chip); } static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) @@ -854,6 +997,8 @@ static int glk_emmc_add_host(struct sdhci_pci_slot *slot) if (ret) goto cleanup; + byt_add_debugfs(slot); + return 0; cleanup: @@ -1031,6 +1176,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { #endif .allow_runtime_pm = true, .probe_slot = byt_emmc_probe_slot, + .add_host = byt_add_host, + .remove_slot = byt_remove_slot, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | SDHCI_QUIRK_NO_LED, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | @@ -1044,6 +1191,7 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = { .allow_runtime_pm = true, .probe_slot = glk_emmc_probe_slot, .add_host = glk_emmc_add_host, + .remove_slot = byt_remove_slot, #ifdef CONFIG_PM_SLEEP .suspend = sdhci_cqhci_suspend, .resume = sdhci_cqhci_resume, @@ -1074,6 +1222,8 @@ static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = { SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, .probe_slot = ni_byt_sdio_probe_slot, + .add_host = byt_add_host, + .remove_slot = byt_remove_slot, .ops = &sdhci_intel_byt_ops, .priv_size = sizeof(struct intel_host), }; @@ -1091,6 +1241,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .allow_runtime_pm = true, .probe_slot = byt_sdio_probe_slot, + .add_host = byt_add_host, + .remove_slot = byt_remove_slot, .ops = &sdhci_intel_byt_ops, .priv_size = sizeof(struct intel_host), }; @@ -1110,6 +1262,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { .allow_runtime_pm = true, .own_cd_for_runtime_pm = true, .probe_slot = byt_sd_probe_slot, + .add_host = byt_add_host, + .remove_slot = byt_remove_slot, .ops = &sdhci_intel_byt_ops, .priv_size = sizeof(struct intel_host), }; diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 13fbf70b5fde..428cdc53c858 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -1660,11 +1660,8 @@ static int sdhci_tegra_probe(struct platform_device *pdev) clk = devm_clk_get(mmc_dev(host->mmc), NULL); if (IS_ERR(clk)) { - rc = PTR_ERR(clk); - - if (rc != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get clock: %d\n", rc); - + rc = dev_err_probe(&pdev->dev, PTR_ERR(clk), + "failed to get clock\n"); goto err_clk_get; } clk_prepare_enable(clk); diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index f9d24af12396..c5f47197d177 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -2,10 +2,11 @@ /* * sdhci_am654.c - SDHCI driver for TI's AM654 SOCs * - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com * */ #include <linux/clk.h> +#include <linux/iopoll.h> #include <linux/of.h> #include <linux/module.h> #include <linux/pm_runtime.h> @@ -272,9 +273,19 @@ static void sdhci_j721e_4bit_set_clock(struct sdhci_host *host, sdhci_set_clock(host, clock); } +static u8 sdhci_am654_write_power_on(struct sdhci_host *host, u8 val, int reg) +{ + writeb(val, host->ioaddr + reg); + usleep_range(1000, 10000); + return readb(host->ioaddr + reg); +} + +#define MAX_POWER_ON_TIMEOUT 1500000 /* us */ static void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg) { unsigned char timing = host->mmc->ios.timing; + u8 pwr; + int ret; if (reg == SDHCI_HOST_CONTROL) { switch (timing) { @@ -291,6 +302,19 @@ static void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg) } writeb(val, host->ioaddr + reg); + if (reg == SDHCI_POWER_CONTROL && (val & SDHCI_POWER_ON)) { + /* + * Power on will not happen until the card detect debounce + * timer expires. Wait at least 1.5 seconds for the power on + * bit to be set + */ + ret = read_poll_timeout(sdhci_am654_write_power_on, pwr, + pwr & SDHCI_POWER_ON, 0, + MAX_POWER_ON_TIMEOUT, false, host, val, + reg); + if (ret) + dev_warn(mmc_dev(host->mmc), "Power on failed\n"); + } } static int sdhci_am654_execute_tuning(struct mmc_host *mmc, u32 opcode) diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 93e83ad25976..757c90160ae4 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -77,18 +77,10 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, static void tmio_mmc_reset(struct tmio_mmc_host *host) { - /* FIXME - should we set stop clock reg here */ - sd_ctrl_write16(host, CTL_RESET_SD, 0x0000); sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000); usleep_range(10000, 11000); - sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001); usleep_range(10000, 11000); - - if (host->pdata->flags & TMIO_MMC_SDIO_IRQ) { - sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); - sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); - } } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 0a4f36500add..51b5f388f6d8 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -178,15 +178,8 @@ struct tmio_mmc_host { unsigned int direction, int blk_size); int (*write16_hook)(struct tmio_mmc_host *host, int addr); void (*reset)(struct tmio_mmc_host *host); - void (*hw_reset)(struct tmio_mmc_host *host); bool (*check_retune)(struct tmio_mmc_host *host); - /* - * Mandatory callback for tuning to occur which is optional for SDR50 - * and mandatory for SDR104. - */ - int (*execute_tuning)(struct tmio_mmc_host *host, u32 opcode); - void (*prepare_hs400_tuning)(struct tmio_mmc_host *host); void (*hs400_downgrade)(struct tmio_mmc_host *host); void (*hs400_complete)(struct tmio_mmc_host *host); diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 946fb013c610..0f266cbf82b8 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -172,24 +172,15 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host) sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); usleep_range(10000, 11000); + if (host->reset) + host->reset(host); + if (host->pdata->flags & TMIO_MMC_SDIO_IRQ) { sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); } } -static void tmio_mmc_hw_reset(struct mmc_host *mmc) -{ - struct tmio_mmc_host *host = mmc_priv(mmc); - - host->reset(host); - - tmio_mmc_abort_dma(host); - - if (host->hw_reset) - host->hw_reset(host); -} - static void tmio_mmc_reset_work(struct work_struct *work) { struct tmio_mmc_host *host = container_of(work, struct tmio_mmc_host, @@ -228,11 +219,12 @@ static void tmio_mmc_reset_work(struct work_struct *work) spin_unlock_irqrestore(&host->lock, flags); - tmio_mmc_hw_reset(host->mmc); + tmio_mmc_reset(host); /* Ready for new calls */ host->mrq = NULL; + tmio_mmc_abort_dma(host); mmc_request_done(host->mmc, mrq); } @@ -720,24 +712,6 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host, return 0; } -static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode) -{ - struct tmio_mmc_host *host = mmc_priv(mmc); - int ret; - - if (!host->execute_tuning) - return 0; - - ret = host->execute_tuning(host, opcode); - - if (ret < 0) { - dev_warn(&host->pdev->dev, "Tuning procedure failed\n"); - tmio_mmc_hw_reset(mmc); - } - - return ret; -} - static void tmio_process_mrq(struct tmio_mmc_host *host, struct mmc_request *mrq) { @@ -1011,8 +985,6 @@ static struct mmc_host_ops tmio_mmc_ops = { .get_cd = tmio_mmc_get_cd, .enable_sdio_irq = tmio_mmc_enable_sdio_irq, .multi_io_quirk = tmio_multi_io_quirk, - .hw_reset = tmio_mmc_hw_reset, - .execute_tuning = tmio_mmc_execute_tuning, }; static int tmio_mmc_init_ocr(struct tmio_mmc_host *host) @@ -1156,9 +1128,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) mmc->caps & MMC_CAP_NEEDS_POLL || !mmc_card_is_removable(mmc)); - if (!_host->reset) - _host->reset = tmio_mmc_reset; - /* * On Gen2+, eMMC with NONREMOVABLE currently fails because native * hotplug gets disabled. It seems RuntimePM related yet we need further @@ -1180,7 +1149,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) _host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; _host->set_clock(_host, 0); - tmio_mmc_hw_reset(mmc); + tmio_mmc_reset(_host); _host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK); tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL); @@ -1283,7 +1252,7 @@ int tmio_mmc_host_runtime_resume(struct device *dev) struct tmio_mmc_host *host = dev_get_drvdata(dev); tmio_mmc_clk_enable(host); - tmio_mmc_hw_reset(host->mmc); + tmio_mmc_reset(host); if (host->clk_cache) host->set_clock(host, host->clk_cache); diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c index f82baf99fd69..55efd5c53249 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -409,8 +409,9 @@ static void uniphier_sd_clk_disable(struct tmio_mmc_host *host) clk_disable_unprepare(priv->clk); } -static void uniphier_sd_hw_reset(struct tmio_mmc_host *host) +static void uniphier_sd_hw_reset(struct mmc_host *mmc) { + struct tmio_mmc_host *host = mmc_priv(mmc); struct uniphier_sd_priv *priv = uniphier_sd_priv(host); reset_control_assert(priv->rst_hw); @@ -597,7 +598,7 @@ static int uniphier_sd_probe(struct platform_device *pdev) ret = PTR_ERR(priv->rst_hw); goto free_host; } - host->hw_reset = uniphier_sd_hw_reset; + host->ops.hw_reset = uniphier_sd_hw_reset; } if (host->mmc->caps & MMC_CAP_UHS) { diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 49dab9f42b6d..9b755ea0fa03 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1257,11 +1257,14 @@ static void __maybe_unused via_init_sdc_pm(struct via_crdr_mmc_host *host) static int __maybe_unused via_sd_suspend(struct device *dev) { struct via_crdr_mmc_host *host; + unsigned long flags; host = dev_get_drvdata(dev); + spin_lock_irqsave(&host->lock, flags); via_save_pcictrlreg(host); via_save_sdcreg(host); + spin_unlock_irqrestore(&host->lock, flags); device_wakeup_enable(dev); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 7d46411ffaa2..42df06c6b19c 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -297,6 +297,8 @@ struct mmc_card { struct sdio_cis cis; /* common tuple info */ struct sdio_func *sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */ struct sdio_func *sdio_single_irq; /* SDIO function when only one IRQ active */ + u8 major_rev; /* major revision number */ + u8 minor_rev; /* minor revision number */ unsigned num_info; /* number of info strings */ const char **info; /* info strings */ struct sdio_func_tuple *tuples; /* unknown common tuples */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c5b6e97cb21a..c079b932330f 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -163,6 +163,7 @@ struct mmc_host_ops { int (*select_drive_strength)(struct mmc_card *card, unsigned int max_dtr, int host_drv, int card_drv, int *drv_type); + /* Reset the eMMC card via RST_n */ void (*hw_reset)(struct mmc_host *host); void (*card_event)(struct mmc_host *host); @@ -346,7 +347,7 @@ struct mmc_host { #define MMC_CAP_CD_WAKE (1 << 28) /* Enable card detect wake */ #define MMC_CAP_CMD_DURING_TFR (1 << 29) /* Commands during data transfer */ #define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */ -#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */ +#define MMC_CAP_HW_RESET (1 << 31) /* Reset the eMMC card via RST_n */ u32 caps2; /* More host capabilities */ @@ -399,6 +400,7 @@ struct mmc_host { unsigned int use_spi_crc:1; unsigned int claimed:1; /* host exclusively claimed */ unsigned int bus_dead:1; /* bus has been released */ + unsigned int doing_init_tune:1; /* initial tuning in progress */ unsigned int can_retune:1; /* re-tuning can be used */ unsigned int doing_retune:1; /* re-tuning in progress */ unsigned int retune_now:1; /* do re-tuning at next req */ @@ -594,6 +596,11 @@ static inline bool mmc_doing_retune(struct mmc_host *host) return host->doing_retune == 1; } +static inline bool mmc_doing_tune(struct mmc_host *host) +{ + return host->doing_retune == 1 || host->doing_init_tune == 1; +} + static inline enum dma_data_direction mmc_get_dma_dir(struct mmc_data *data) { return data->flags & MMC_DATA_WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE; diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index fa2aaab5e57a..478855b8e406 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -51,6 +51,8 @@ struct sdio_func { u8 *tmpbuf; /* DMA:able scratch buffer */ + u8 major_rev; /* major revision number */ + u8 minor_rev; /* minor revision number */ unsigned num_info; /* number of info strings */ const char **info; /* info strings */ |