summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-02-27 09:47:26 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2023-02-27 09:47:26 -0800
commitda15efe1a8513e0a949f429338155a56667dc9c6 (patch)
tree57fb9569f872158b989988b5ed32d18ae599a7df /drivers/mmc
parent6ffb575bebb983b982240372ecf976188451dd1c (diff)
parent571f235163ac83407e212b78719175236962aede (diff)
downloadlwn-da15efe1a8513e0a949f429338155a56667dc9c6.tar.gz
lwn-da15efe1a8513e0a949f429338155a56667dc9c6.zip
Merge tag 'mmc-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson: "MMC core: - Extend slot-gpio to be used for host specific card detect interrupts - Align to common busy polling behaviour for mmc ioctls - Suggest the BFQ I/O scheduler to be built along with MMC/SD support - Add devm_mmc_alloc_host() to enable further cleanups in host drivers MMC host: - atmel-mci: Fix race condition when stopping/starting a command - dw_mmc-starfive: Add new driver to support the StarFive JH7110 variant - dw_mmc-rockchip: Add support for the RK3588 variant - jz4740: Add support for the vqmmc power supply - meson-gx: Convert the DT bindings to the dt-schema - meson-gx: Enable the platform interrupt to be used for card detect - moxart: Set the supported maximum request/block/segment sizes - renesas,sdhi: Add support for the RZ/V2M variants - sdhci: Rework code to drop SDHCI_QUIRK_MISSING_CAPS - sdhci-esdhc-imx: Improve tuning logic support - sdhci-msm: Add support for the IPQ5332 and the IPQ9574 variants - sdhci-of-dwcmshc: Add the missing device table IDs for acpi - sdhci-of-dwcmshc: Improve clock support for the Rockchip variant - sdhci-of-dwcmshc: Enable support of V4 host for the BlueField-3 variant - sdhci-pxav2: Add support for the PXA168 V1 variant - sdhci-pxav2: Add support for SDIO IRQs for the PXA168 V1 variant - uniphier-sd: Add support for SD UHS-I speed modes" * tag 'mmc-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (59 commits) mmc: meson-gx: Use devm_platform_get_and_ioremap_resource() mmc: meson-gx: constify member data of struct meson_host mmc: meson-gx: use devm_clk_get_enabled() for core clock mmc: core: fix return value check in devm_mmc_alloc_host() dt-bindings: mmc: meson-gx: fix interrupt binding mmc: meson-gx: support platform interrupt as card detect interrupt dt-bindings: mmc: meson-gx: support specifying cd interrupt mmc: core: support setting card detect interrupt from drivers mmc: starfive: Add sdio/emmc driver support dt-bindings: mmc: Add StarFive MMC module dt-bindings: mmc: sdhci-msm: Allow 1 icc path dt-bindings: mmc: rockchip-dw-mshc: Add RK3588 compatible string mmc: core: Align to common busy polling behaviour for mmc ioctls dt-bindings: mmc: Add resets property to cadence SDHCI binding mmc: meson-gx: remove meson_mmc_get_cd mmc: moxart: set maximum request/block/segment sizes mmc: sdhci-brcmstb: Use devm_platform_get_and_ioremap_resource() mmc: sdhci-of-dwcmshc: add the missing device table IDs for acpi mmc: sdhci-of-dwcmshc: Update DLL and pre-change delay for rockchip platform mmc: jz4740: Add support for vqmmc power supply ...
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/Kconfig3
-rw-r--r--drivers/mmc/core/block.c25
-rw-r--r--drivers/mmc/core/host.c26
-rw-r--r--drivers/mmc/core/mmc_ops.c1
-rw-r--r--drivers/mmc/core/pwrseq_simple.c4
-rw-r--r--drivers/mmc/core/sdio_io.c2
-rw-r--r--drivers/mmc/core/slot-gpio.c17
-rw-r--r--drivers/mmc/host/Kconfig11
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/atmel-mci.c3
-rw-r--r--drivers/mmc/host/dw_mmc-starfive.c186
-rw-r--r--drivers/mmc/host/jz4740_mmc.c38
-rw-r--r--drivers/mmc/host/meson-gx-mmc.c92
-rw-r--r--drivers/mmc/host/moxart-mmc.c9
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c11
-rw-r--r--drivers/mmc/host/sdhci-brcmstb.c8
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c66
-rw-r--r--drivers/mmc/host/sdhci-iproc.c14
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c19
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c8
-rw-r--r--drivers/mmc/host/sdhci-pxav2.c154
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c4
-rw-r--r--drivers/mmc/host/sdhci-sprd.c6
-rw-r--r--drivers/mmc/host/sdhci.c3
-rw-r--r--drivers/mmc/host/sdhci.h2
-rw-r--r--drivers/mmc/host/uniphier-sd.c83
26 files changed, 656 insertions, 140 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index 6f25c34e4fec..bf4e29ef023c 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -15,7 +15,7 @@ config PWRSEQ_EMMC
config PWRSEQ_SD8787
tristate "HW reset support for SD8787 BT + Wifi module"
- depends on OF && (MWIFIEX || BT_MRVL_SDIO || LIBERTAS_SDIO || WILC1000_SDIO)
+ depends on OF && (MWIFIEX != n || BT_MRVL_SDIO != n || LIBERTAS_SDIO != n || WILC1000_SDIO != n)
help
This selects hardware reset support for the SD8787 BT + Wifi
module. By default this option is set to n.
@@ -37,6 +37,7 @@ config PWRSEQ_SIMPLE
config MMC_BLOCK
tristate "MMC block device driver"
depends on BLOCK
+ imply IOSCHED_BFQ
default y
help
Say Y here to enable the MMC block device driver support.
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 20da7ed43e6d..672ab90c4b2d 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -470,6 +470,8 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
struct mmc_data data = {};
struct mmc_request mrq = {};
struct scatterlist sg;
+ bool r1b_resp, use_r1b_resp = false;
+ unsigned int busy_timeout_ms;
int err;
unsigned int target_part;
@@ -545,6 +547,13 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
(cmd.opcode == MMC_SWITCH))
return mmc_sanitize(card, idata->ic.cmd_timeout_ms);
+ /* If it's an R1B response we need some more preparations. */
+ busy_timeout_ms = idata->ic.cmd_timeout_ms ? : MMC_BLK_TIMEOUT_MS;
+ r1b_resp = (cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B;
+ if (r1b_resp)
+ use_r1b_resp = mmc_prepare_busy_cmd(card->host, &cmd,
+ busy_timeout_ms);
+
mmc_wait_for_req(card->host, &mrq);
memcpy(&idata->ic.response, cmd.resp, sizeof(cmd.resp));
@@ -596,14 +605,14 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
if (idata->ic.postsleep_min_us)
usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us);
- if (idata->rpmb || (cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
- /*
- * Ensure RPMB/R1B command has completed by polling CMD13 "Send Status". Here we
- * allow to override the default timeout value if a custom timeout is specified.
- */
- err = mmc_poll_for_busy(card, idata->ic.cmd_timeout_ms ? : MMC_BLK_TIMEOUT_MS,
- false, MMC_BUSY_IO);
- }
+ /* No need to poll when using HW busy detection. */
+ if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
+ return 0;
+
+ /* Ensure RPMB/R1B command has completed by polling with CMD13. */
+ if (idata->rpmb || r1b_resp)
+ err = mmc_poll_for_busy(card, busy_timeout_ms, false,
+ MMC_BUSY_IO);
return err;
}
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index d17eda753b7e..096093f7be00 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -588,6 +588,32 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
EXPORT_SYMBOL(mmc_alloc_host);
+static void devm_mmc_host_release(struct device *dev, void *res)
+{
+ mmc_free_host(*(struct mmc_host **)res);
+}
+
+struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra)
+{
+ struct mmc_host **dr, *host;
+
+ dr = devres_alloc(devm_mmc_host_release, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return NULL;
+
+ host = mmc_alloc_host(extra, dev);
+ if (!host) {
+ devres_free(dr);
+ return NULL;
+ }
+
+ *dr = host;
+ devres_add(dev, dr);
+
+ return host;
+}
+EXPORT_SYMBOL(devm_mmc_alloc_host);
+
static int mmc_validate_host_caps(struct mmc_host *host)
{
struct device *dev = host->parent;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 81c55bfd6e0c..3b3adbddf664 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -575,6 +575,7 @@ bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd,
cmd->busy_timeout = timeout_ms;
return true;
}
+EXPORT_SYMBOL_GPL(mmc_prepare_busy_cmd);
/**
* __mmc_switch - modify EXT_CSD register
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index 988467fbb621..3bac1e71411b 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -119,14 +119,14 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev)
pwrseq->ext_clk = devm_clk_get(dev, "ext_clock");
if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT)
- return PTR_ERR(pwrseq->ext_clk);
+ return dev_err_probe(dev, PTR_ERR(pwrseq->ext_clk), "external clock not ready\n");
pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(pwrseq->reset_gpios) &&
PTR_ERR(pwrseq->reset_gpios) != -ENOENT &&
PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) {
- return PTR_ERR(pwrseq->reset_gpios);
+ return dev_err_probe(dev, PTR_ERR(pwrseq->reset_gpios), "reset GPIOs not ready\n");
}
device_property_read_u32(dev, "post-power-on-delay-ms",
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 79dbf90216b5..b774bf51981d 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -766,7 +766,7 @@ EXPORT_SYMBOL_GPL(sdio_retune_crc_disable);
* sdio_retune_crc_enable - re-enable retuning on CRC errors
* @func: SDIO function attached to host
*
- * This is the compement to sdio_retune_crc_disable().
+ * This is the complement to sdio_retune_crc_disable().
*/
void sdio_retune_crc_enable(struct sdio_func *func)
{
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index dd2a4b6ab6ad..2a2d949a9344 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -23,6 +23,7 @@ struct mmc_gpio {
char *ro_label;
char *cd_label;
u32 cd_debounce_delay_ms;
+ int cd_irq;
};
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
@@ -53,12 +54,24 @@ int mmc_gpio_alloc(struct mmc_host *host)
ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s ro", devname);
if (!ctx->ro_label)
return -ENOMEM;
+ ctx->cd_irq = -EINVAL;
host->slot.handler_priv = ctx;
host->slot.cd_irq = -EINVAL;
return 0;
}
+void mmc_gpio_set_cd_irq(struct mmc_host *host, int irq)
+{
+ struct mmc_gpio *ctx = host->slot.handler_priv;
+
+ if (!ctx || irq < 0)
+ return;
+
+ ctx->cd_irq = irq;
+}
+EXPORT_SYMBOL(mmc_gpio_set_cd_irq);
+
int mmc_gpio_get_ro(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
@@ -98,7 +111,9 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
* Do not use IRQ if the platform prefers to poll, e.g., because that
* IRQ number is already used by another unit and cannot be shared.
*/
- if (!(host->caps & MMC_CAP_NEEDS_POLL))
+ if (ctx->cd_irq >= 0)
+ irq = ctx->cd_irq;
+ else if (!(host->caps & MMC_CAP_NEEDS_POLL))
irq = gpiod_to_irq(ctx->cd_gpio);
if (irq >= 0) {
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index e13b0b0b8ebb..4745fe217ade 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -348,6 +348,7 @@ config MMC_SDHCI_PXAV2
depends on MMC_SDHCI_PLTFM
depends on ARCH_MMP || COMPILE_TEST
default CPU_PXA910
+ select MMC_SDHCI_IO_ACCESSORS
help
This selects the Marvell(R) PXAV2 SD Host Controller.
If you have a PXA9XX platform with SD Host Controller
@@ -816,6 +817,16 @@ config MMC_DW_ROCKCHIP
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on RK3066, RK3188 and RK3288 SoC's.
+config MMC_DW_STARFIVE
+ tristate "StarFive specific extensions for Synopsys DW Memory Card Interface"
+ depends on SOC_STARFIVE
+ depends on MMC_DW
+ select MMC_DW_PLTFM
+ help
+ This selects support for StarFive JH7110 SoC specific extensions to the
+ Synopsys DesignWare Memory Card Interface driver. Select this option
+ for platforms based on StarFive JH7110 SoC.
+
config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support"
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index b498c17cd124..a693fa3d3f1c 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
+obj-$(CONFIG_MMC_DW_STARFIVE) += dw_mmc-starfive.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index bb9bbf1c927b..dd18440a90c5 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -1817,7 +1817,6 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
state = STATE_WAITING_NOTBUSY;
} else if (host->mrq->stop) {
- atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
atmci_send_stop_cmd(host, data);
state = STATE_SENDING_STOP;
} else {
@@ -1850,8 +1849,6 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
* command to send.
*/
if (host->mrq->stop) {
- atmci_writel(host, ATMCI_IER,
- ATMCI_CMDRDY);
atmci_send_stop_cmd(host, data);
state = STATE_SENDING_STOP;
} else {
diff --git a/drivers/mmc/host/dw_mmc-starfive.c b/drivers/mmc/host/dw_mmc-starfive.c
new file mode 100644
index 000000000000..40f5969b07a6
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-starfive.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * StarFive Designware Mobile Storage Host Controller Driver
+ *
+ * Copyright (c) 2022 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define ALL_INT_CLR 0x1ffff
+#define MAX_DELAY_CHAIN 32
+
+struct starfive_priv {
+ struct device *dev;
+ struct regmap *reg_syscon;
+ u32 syscon_offset;
+ u32 syscon_shift;
+ u32 syscon_mask;
+};
+
+static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+ int ret;
+ unsigned int clock;
+
+ if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50) {
+ clock = (ios->clock > 50000000 && ios->clock <= 52000000) ? 100000000 : ios->clock;
+ ret = clk_set_rate(host->ciu_clk, clock);
+ if (ret)
+ dev_dbg(host->dev, "Use an external frequency divider %uHz\n", ios->clock);
+ host->bus_hz = clk_get_rate(host->ciu_clk);
+ } else {
+ dev_dbg(host->dev, "Using the internal divider\n");
+ }
+}
+
+static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
+ u32 opcode)
+{
+ static const int grade = MAX_DELAY_CHAIN;
+ struct dw_mci *host = slot->host;
+ struct starfive_priv *priv = host->priv;
+ int rise_point = -1, fall_point = -1;
+ int err, prev_err;
+ int i;
+ bool found = 0;
+ u32 regval;
+
+ /*
+ * Use grade as the max delay chain, and use the rise_point and
+ * fall_point to ensure the best sampling point of a data input
+ * signals.
+ */
+ for (i = 0; i < grade; i++) {
+ regval = i << priv->syscon_shift;
+ err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
+ priv->syscon_mask, regval);
+ if (err)
+ return err;
+ mci_writel(host, RINTSTS, ALL_INT_CLR);
+
+ err = mmc_send_tuning(slot->mmc, opcode, NULL);
+ if (!err)
+ found = 1;
+
+ if (i > 0) {
+ if (err && !prev_err)
+ fall_point = i - 1;
+ if (!err && prev_err)
+ rise_point = i;
+ }
+
+ if (rise_point != -1 && fall_point != -1)
+ goto tuning_out;
+
+ prev_err = err;
+ err = 0;
+ }
+
+tuning_out:
+ if (found) {
+ if (rise_point == -1)
+ rise_point = 0;
+ if (fall_point == -1)
+ fall_point = grade - 1;
+ if (fall_point < rise_point) {
+ if ((rise_point + fall_point) >
+ (grade - 1))
+ i = fall_point / 2;
+ else
+ i = (rise_point + grade - 1) / 2;
+ } else {
+ i = (rise_point + fall_point) / 2;
+ }
+
+ regval = i << priv->syscon_shift;
+ err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
+ priv->syscon_mask, regval);
+ if (err)
+ return err;
+ mci_writel(host, RINTSTS, ALL_INT_CLR);
+
+ dev_info(host->dev, "Found valid delay chain! use it [delay=%d]\n", i);
+ } else {
+ dev_err(host->dev, "No valid delay chain! use default\n");
+ err = -EINVAL;
+ }
+
+ mci_writel(host, RINTSTS, ALL_INT_CLR);
+ return err;
+}
+
+static int dw_mci_starfive_parse_dt(struct dw_mci *host)
+{
+ struct of_phandle_args args;
+ struct starfive_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ret = of_parse_phandle_with_fixed_args(host->dev->of_node,
+ "starfive,sysreg", 3, 0, &args);
+ if (ret) {
+ dev_err(host->dev, "Failed to parse starfive,sysreg\n");
+ return -EINVAL;
+ }
+
+ priv->reg_syscon = syscon_node_to_regmap(args.np);
+ of_node_put(args.np);
+ if (IS_ERR(priv->reg_syscon))
+ return PTR_ERR(priv->reg_syscon);
+
+ priv->syscon_offset = args.args[0];
+ priv->syscon_shift = args.args[1];
+ priv->syscon_mask = args.args[2];
+
+ host->priv = priv;
+
+ return 0;
+}
+
+static const struct dw_mci_drv_data starfive_data = {
+ .common_caps = MMC_CAP_CMD23,
+ .set_ios = dw_mci_starfive_set_ios,
+ .parse_dt = dw_mci_starfive_parse_dt,
+ .execute_tuning = dw_mci_starfive_execute_tuning,
+};
+
+static const struct of_device_id dw_mci_starfive_match[] = {
+ { .compatible = "starfive,jh7110-mmc",
+ .data = &starfive_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_starfive_match);
+
+static int dw_mci_starfive_probe(struct platform_device *pdev)
+{
+ return dw_mci_pltfm_register(pdev, &starfive_data);
+}
+
+static struct platform_driver dw_mci_starfive_driver = {
+ .probe = dw_mci_starfive_probe,
+ .remove = dw_mci_pltfm_remove,
+ .driver = {
+ .name = "dwmmc_starfive",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ .of_match_table = dw_mci_starfive_match,
+ },
+};
+module_platform_driver(dw_mci_starfive_driver);
+
+MODULE_DESCRIPTION("StarFive JH7110 Specific DW-MSHC Driver Extension");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dwmmc_starfive");
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index eda1e2ddcaca..698450afa7bb 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -21,6 +21,7 @@
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/scatterlist.h>
#include <asm/cacheflush.h>
@@ -158,6 +159,8 @@ struct jz4740_mmc_host {
struct mmc_request *req;
struct mmc_command *cmd;
+ bool vqmmc_enabled;
+
unsigned long waiting;
uint32_t cmdat;
@@ -935,6 +938,8 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct jz4740_mmc_host *host = mmc_priv(mmc);
+ int ret;
+
if (ios->clock)
jz4740_mmc_set_clock_rate(host, ios->clock);
@@ -947,12 +952,25 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk_prepare_enable(host->clk);
break;
case MMC_POWER_ON:
+ if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
+ ret = regulator_enable(mmc->supply.vqmmc);
+ if (ret)
+ dev_err(&host->pdev->dev, "Failed to set vqmmc power!\n");
+ else
+ host->vqmmc_enabled = true;
+ }
break;
- default:
+ case MMC_POWER_OFF:
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
+ regulator_disable(mmc->supply.vqmmc);
+ host->vqmmc_enabled = false;
+ }
clk_disable_unprepare(host->clk);
break;
+ default:
+ break;
}
switch (ios->bus_width) {
@@ -978,6 +996,23 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable);
}
+static int jz4740_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ int ret;
+
+ /* vqmmc regulator is available */
+ if (!IS_ERR(mmc->supply.vqmmc)) {
+ ret = mmc_regulator_set_vqmmc(mmc, ios);
+ return ret < 0 ? ret : 0;
+ }
+
+ /* no vqmmc regulator, assume fixed regulator at 3/3.3V */
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+ return 0;
+
+ return -EINVAL;
+}
+
static const struct mmc_host_ops jz4740_mmc_ops = {
.request = jz4740_mmc_request,
.pre_req = jz4740_mmc_pre_request,
@@ -986,6 +1021,7 @@ static const struct mmc_host_ops jz4740_mmc_ops = {
.get_ro = mmc_gpio_get_ro,
.get_cd = mmc_gpio_get_cd,
.enable_sdio_irq = jz4740_mmc_enable_sdio_irq,
+ .start_signal_voltage_switch = jz4740_voltage_switch,
};
static const struct of_device_id jz4740_mmc_of_match[] = {
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c
index 5c94ad4661ce..2b963a81c2ad 100644
--- a/drivers/mmc/host/meson-gx-mmc.c
+++ b/drivers/mmc/host/meson-gx-mmc.c
@@ -150,12 +150,11 @@ struct sd_emmc_desc {
struct meson_host {
struct device *dev;
- struct meson_mmc_data *data;
+ const struct meson_mmc_data *data;
struct mmc_host *mmc;
struct mmc_command *cmd;
void __iomem *regs;
- struct clk *core_clk;
struct clk *mux_clk;
struct clk *mmc_clk;
unsigned long req_rate;
@@ -1083,20 +1082,6 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
return IRQ_HANDLED;
}
-/*
- * NOTE: we only need this until the GPIO/pinctrl driver can handle
- * interrupts. For now, the MMC core will use this for polling.
- */
-static int meson_mmc_get_cd(struct mmc_host *mmc)
-{
- int status = mmc_gpio_get_cd(mmc);
-
- if (status == -ENOSYS)
- return 1; /* assume present */
-
- return status;
-}
-
static void meson_mmc_cfg_init(struct meson_host *host)
{
u32 cfg = 0;
@@ -1165,7 +1150,7 @@ static void meson_mmc_ack_sdio_irq(struct mmc_host *mmc)
static const struct mmc_host_ops meson_mmc_ops = {
.request = meson_mmc_request,
.set_ios = meson_mmc_set_ios,
- .get_cd = meson_mmc_get_cd,
+ .get_cd = mmc_gpio_get_cd,
.pre_req = meson_mmc_pre_req,
.post_req = meson_mmc_post_req,
.execute_tuning = meson_mmc_resampling_tuning,
@@ -1180,9 +1165,10 @@ static int meson_mmc_probe(struct platform_device *pdev)
struct resource *res;
struct meson_host *host;
struct mmc_host *mmc;
- int ret;
+ struct clk *core_clk;
+ int cd_irq, ret;
- mmc = mmc_alloc_host(sizeof(struct meson_host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(struct meson_host));
if (!mmc)
return -ENOMEM;
host = mmc_priv(mmc);
@@ -1198,51 +1184,39 @@ static int meson_mmc_probe(struct platform_device *pdev)
host->vqmmc_enabled = false;
ret = mmc_regulator_get_supply(mmc);
if (ret)
- goto free_host;
+ return ret;
ret = mmc_of_parse(mmc);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- dev_warn(&pdev->dev, "error parsing DT: %d\n", ret);
- goto free_host;
- }
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "error parsing DT\n");
mmc->caps |= MMC_CAP_CMD23;
if (mmc->caps & MMC_CAP_SDIO_IRQ)
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
- host->data = (struct meson_mmc_data *)
- of_device_get_match_data(&pdev->dev);
- if (!host->data) {
- ret = -EINVAL;
- goto free_host;
- }
+ host->data = of_device_get_match_data(&pdev->dev);
+ if (!host->data)
+ return -EINVAL;
ret = device_reset_optional(&pdev->dev);
- if (ret) {
- dev_err_probe(&pdev->dev, ret, "device reset failed\n");
- goto free_host;
- }
+ 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);
- if (IS_ERR(host->regs)) {
- ret = PTR_ERR(host->regs);
- goto free_host;
- }
+ host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(host->regs))
+ return PTR_ERR(host->regs);
host->irq = platform_get_irq(pdev, 0);
- if (host->irq <= 0) {
- ret = -EINVAL;
- goto free_host;
- }
+ if (host->irq <= 0)
+ return -EINVAL;
+
+ cd_irq = platform_get_irq_optional(pdev, 1);
+ mmc_gpio_set_cd_irq(mmc, cd_irq);
host->pinctrl = devm_pinctrl_get(&pdev->dev);
- if (IS_ERR(host->pinctrl)) {
- ret = PTR_ERR(host->pinctrl);
- goto free_host;
- }
+ if (IS_ERR(host->pinctrl))
+ return PTR_ERR(host->pinctrl);
host->pins_clk_gate = pinctrl_lookup_state(host->pinctrl,
"clk-gate");
@@ -1252,19 +1226,13 @@ static int meson_mmc_probe(struct platform_device *pdev)
host->pins_clk_gate = NULL;
}
- host->core_clk = devm_clk_get(&pdev->dev, "core");
- if (IS_ERR(host->core_clk)) {
- ret = PTR_ERR(host->core_clk);
- goto free_host;
- }
-
- ret = clk_prepare_enable(host->core_clk);
- if (ret)
- goto free_host;
+ core_clk = devm_clk_get_enabled(&pdev->dev, "core");
+ if (IS_ERR(core_clk))
+ return PTR_ERR(core_clk);
ret = meson_mmc_clk_init(host);
if (ret)
- goto err_core_clk;
+ return ret;
/* set config to sane default */
meson_mmc_cfg_init(host);
@@ -1348,10 +1316,6 @@ err_free_irq:
free_irq(host->irq, host);
err_init_clk:
clk_disable_unprepare(host->mmc_clk);
-err_core_clk:
- clk_disable_unprepare(host->core_clk);
-free_host:
- mmc_free_host(mmc);
return ret;
}
@@ -1366,9 +1330,7 @@ static int meson_mmc_remove(struct platform_device *pdev)
free_irq(host->irq, host);
clk_disable_unprepare(host->mmc_clk);
- clk_disable_unprepare(host->core_clk);
- mmc_free_host(host->mmc);
return 0;
}
diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c
index 52ed30f2d9f4..2d002c81dcf3 100644
--- a/drivers/mmc/host/moxart-mmc.c
+++ b/drivers/mmc/host/moxart-mmc.c
@@ -611,6 +611,9 @@ static int moxart_probe(struct platform_device *pdev)
mmc->f_max = DIV_ROUND_CLOSEST(host->sysclk, 2);
mmc->f_min = DIV_ROUND_CLOSEST(host->sysclk, CLK_DIV_MASK * 2);
mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */
+ mmc->max_blk_size = 2048; /* Max. block length in REG_DATA_CONTROL */
+ mmc->max_req_size = DATA_LEN_MASK; /* bits 0-23 in REG_DATA_LENGTH */
+ mmc->max_blk_count = mmc->max_req_size / 512;
if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER ||
@@ -628,6 +631,8 @@ static int moxart_probe(struct platform_device *pdev)
}
dev_dbg(dev, "PIO mode transfer enabled\n");
host->have_dma = false;
+
+ mmc->max_seg_size = mmc->max_req_size;
} else {
dev_dbg(dev, "DMA channels found (%p,%p)\n",
host->dma_chan_tx, host->dma_chan_rx);
@@ -646,6 +651,10 @@ static int moxart_probe(struct platform_device *pdev)
cfg.src_addr = host->reg_phys + REG_DATA_WINDOW;
cfg.dst_addr = 0;
dmaengine_slave_config(host->dma_chan_rx, &cfg);
+
+ mmc->max_seg_size = min3(mmc->max_req_size,
+ dma_get_max_seg_size(host->dma_chan_rx->device->dev),
+ dma_get_max_seg_size(host->dma_chan_tx->device->dev));
}
if (readl(host->base + REG_BUS_WIDTH) & BUS_WIDTH_4_SUPPORT)
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 29f562115c66..f38003f6b1ca 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -210,6 +210,11 @@ static const struct renesas_sdhi_quirks sdhi_quirks_r8a77990 = {
.manual_tap_correction = true,
};
+static const struct renesas_sdhi_quirks sdhi_quirks_r9a09g011 = {
+ .fixed_addr_mode = true,
+ .hs400_disabled = true,
+};
+
/*
* Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now.
* So, we want to treat them equally and only have a match for ES1.2 to enforce
@@ -251,6 +256,11 @@ static const struct renesas_sdhi_of_data_with_quirks of_r8a77990_compatible = {
.quirks = &sdhi_quirks_r8a77990,
};
+static const struct renesas_sdhi_of_data_with_quirks of_r9a09g011_compatible = {
+ .of_data = &of_data_rcar_gen3,
+ .quirks = &sdhi_quirks_r9a09g011,
+};
+
static const struct renesas_sdhi_of_data_with_quirks of_rcar_gen3_compatible = {
.of_data = &of_data_rcar_gen3,
};
@@ -274,6 +284,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
{ .compatible = "renesas,sdhi-r8a77970", .data = &of_r8a77970_compatible, },
{ .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, },
{ .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, },
+ { .compatible = "renesas,sdhi-r9a09g011", .data = &of_r9a09g011_compatible, },
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
{ .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, },
{},
diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
index f2cf3d70db79..4c22337199cf 100644
--- a/drivers/mmc/host/sdhci-brcmstb.c
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -255,7 +255,6 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
struct sdhci_brcmstb_priv *priv;
u32 actual_clock_mhz;
struct sdhci_host *host;
- struct resource *iomem;
struct clk *clk;
struct clk *base_clk = NULL;
int res;
@@ -291,8 +290,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
}
/* Map in the non-standard CFG registers */
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- priv->cfg_regs = devm_ioremap_resource(&pdev->dev, iomem);
+ priv->cfg_regs = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
if (IS_ERR(priv->cfg_regs)) {
res = PTR_ERR(priv->cfg_regs);
goto err;
@@ -324,13 +322,11 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
* will allow these modes to be specified by device tree
* properties through mmc_of_parse().
*/
- host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+ sdhci_read_caps(host);
if (match_priv->flags & BRCMSTB_MATCH_FLAGS_NO_64BIT)
host->caps &= ~SDHCI_CAN_64BIT;
- host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_DDR50);
- host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
if (match_priv->flags & BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT)
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 9e73c34b6401..58f042fdd4f4 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -338,6 +338,16 @@ struct pltfm_imx_data {
struct clk *clk_ahb;
struct clk *clk_per;
unsigned int actual_clock;
+
+ /*
+ * USDHC has one limition, require the SDIO device a different
+ * register setting. Driver has to recognize card type during
+ * the card init, but at this stage, mmc_host->card is not
+ * available. So involve this field to save the card type
+ * during card init through usdhc_init_card().
+ */
+ unsigned int init_card_type;
+
enum {
NO_CMD_PENDING, /* no multiblock command pending */
MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
@@ -430,9 +440,12 @@ static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
}
/* Enable the auto tuning circuit to check the CMD line and BUS line */
-static inline void usdhc_auto_tuning_mode_sel(struct sdhci_host *host)
+static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host)
{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
u32 buswidth, auto_tune_buswidth;
+ u32 reg;
buswidth = USDHC_GET_BUSWIDTH(readl(host->ioaddr + SDHCI_HOST_CONTROL));
@@ -448,9 +461,27 @@ static inline void usdhc_auto_tuning_mode_sel(struct sdhci_host *host)
break;
}
+ /*
+ * For USDHC, auto tuning circuit can not handle the async sdio
+ * device interrupt correctly. When sdio device use 4 data lines,
+ * async sdio interrupt will use the shared DAT[1], if enable auto
+ * tuning circuit check these 4 data lines, include the DAT[1],
+ * this circuit will detect this interrupt, take this as a data on
+ * DAT[1], and adjust the delay cell wrongly.
+ * This is the hardware design limitation, to avoid this, for sdio
+ * device, config the auto tuning circuit only check DAT[0] and CMD
+ * line.
+ */
+ if (imx_data->init_card_type == MMC_TYPE_SDIO)
+ auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
+
esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
ESDHC_VEND_SPEC2);
+
+ reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
+ writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
}
static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
@@ -682,14 +713,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
} else {
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
- m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
}
if (val & SDHCI_CTRL_EXEC_TUNING) {
v |= ESDHC_MIX_CTRL_EXE_TUNE;
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
- m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
- usdhc_auto_tuning_mode_sel(host);
} else {
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
}
@@ -1023,13 +1051,15 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
/* Reset the tuning circuit */
if (esdhc_is_usdhc(imx_data)) {
+ ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ ctrl &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
- ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
+ writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
@@ -1052,9 +1082,19 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
}
}
+static void usdhc_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
+
+ imx_data->init_card_type = card->type;
+}
+
static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
+ int err;
/*
* i.MX uSDHC internally already uses a fixed optimized timing for
@@ -1069,7 +1109,12 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
* correct delay cell.
*/
esdhc_reset_tuning(host);
- return sdhci_execute_tuning(mmc, opcode);
+ err = sdhci_execute_tuning(mmc, opcode);
+ /* If tuning done, enable auto tuning */
+ if (!err && !host->tuning_err)
+ usdhc_auto_tuning_mode_sel_and_en(host);
+
+ return err;
}
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
@@ -1103,11 +1148,8 @@ static void esdhc_post_tuning(struct sdhci_host *host)
{
u32 reg;
- usdhc_auto_tuning_mode_sel(host);
-
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
- reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
}
@@ -1674,6 +1716,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
* to replace the standard one in sdhci_ops.
*/
host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
+
+ /*
+ * Link usdhc specific mmc_host_ops init card function,
+ * to distinguish the card type.
+ */
+ host->mmc_host_ops.init_card = usdhc_init_card;
}
err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 6db35b1b8557..86eb0045515e 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -18,6 +18,7 @@ struct sdhci_iproc_data {
u32 caps;
u32 caps1;
u32 mmc_caps;
+ bool missing_caps;
};
struct sdhci_iproc_host {
@@ -251,7 +252,6 @@ static const struct sdhci_iproc_data iproc_data = {
static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
- SDHCI_QUIRK_MISSING_CAPS |
SDHCI_QUIRK_NO_HISPD_BIT,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.ops = &sdhci_iproc_32only_ops,
@@ -266,6 +266,7 @@ static const struct sdhci_iproc_data bcm2835_data = {
.caps1 = SDHCI_DRIVER_TYPE_A |
SDHCI_DRIVER_TYPE_C,
.mmc_caps = 0x00000000,
+ .missing_caps = true,
};
static const struct sdhci_ops sdhci_iproc_bcm2711_ops = {
@@ -295,8 +296,7 @@ static const struct sdhci_iproc_data bcm2711_data = {
};
static const struct sdhci_pltfm_data sdhci_bcm7211a0_pltfm_data = {
- .quirks = SDHCI_QUIRK_MISSING_CAPS |
- SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+ .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_BROKEN_DMA |
SDHCI_QUIRK_BROKEN_ADMA,
.ops = &sdhci_iproc_ops,
@@ -315,6 +315,7 @@ static const struct sdhci_iproc_data bcm7211a0_data = {
SDHCI_CAN_DO_HISPD,
.caps1 = SDHCI_DRIVER_TYPE_C |
SDHCI_DRIVER_TYPE_D,
+ .missing_caps = true,
};
static const struct of_device_id sdhci_iproc_of_match[] = {
@@ -397,9 +398,10 @@ static int sdhci_iproc_probe(struct platform_device *pdev)
}
}
- if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) {
- host->caps = iproc_host->data->caps;
- host->caps1 = iproc_host->data->caps1;
+ if (iproc_host->data->missing_caps) {
+ __sdhci_read_caps(host, NULL,
+ &iproc_host->data->caps,
+ &iproc_host->data->caps1);
}
ret = sdhci_add_host(host);
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index a7343d4bc50e..d1490469184b 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -48,6 +48,7 @@
#define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL 29
#define DWCMSHC_EMMC_DLL_START_POINT 16
#define DWCMSHC_EMMC_DLL_INC 8
+#define DWCMSHC_EMMC_DLL_BYPASS BIT(24)
#define DWCMSHC_EMMC_DLL_DLYENA BIT(27)
#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
@@ -60,6 +61,7 @@
#define DLL_RXCLK_NO_INVERTER 1
#define DLL_RXCLK_INVERTER 0
#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8
+#define DLL_RXCLK_ORI_GATE BIT(31)
#define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24)
#define DLL_CMDOUT_SRC_CLK_NEG BIT(28)
#define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29)
@@ -234,9 +236,12 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
sdhci_writel(host, extra, reg);
if (clock <= 52000000) {
- /* Disable DLL and reset both of sample and drive clock */
- sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
- sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK);
+ /*
+ * Disable DLL and reset both of sample and drive clock.
+ * The bypass bit and start bit need to be set if DLL is not locked.
+ */
+ sdhci_writel(host, DWCMSHC_EMMC_DLL_BYPASS | DWCMSHC_EMMC_DLL_START, DWCMSHC_EMMC_DLL_CTRL);
+ sdhci_writel(host, DLL_RXCLK_ORI_GATE, DWCMSHC_EMMC_DLL_RXCLK);
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT);
/*
@@ -279,7 +284,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
}
extra = 0x1 << 16 | /* tune clock stop en */
- 0x2 << 17 | /* pre-change delay */
+ 0x3 << 17 | /* pre-change delay */
0x3 << 19; /* post-change delay */
sdhci_writel(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
@@ -446,6 +451,7 @@ static const struct acpi_device_id sdhci_dwcmshc_acpi_ids[] = {
},
{}
};
+MODULE_DEVICE_TABLE(acpi, sdhci_dwcmshc_acpi_ids);
#endif
static int dwcmshc_probe(struct platform_device *pdev)
@@ -528,6 +534,11 @@ static int dwcmshc_probe(struct platform_device *pdev)
goto err_clk;
}
+#ifdef CONFIG_ACPI
+ if (pltfm_data == &sdhci_dwcmshc_bf3_pdata)
+ sdhci_enable_v4_mode(host);
+#endif
+
host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
err = sdhci_setup_host(host);
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index c359f867df0a..01975d145200 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -251,13 +251,16 @@ static int ricoh_probe(struct sdhci_pci_chip *chip)
static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
{
- slot->host->caps =
+ u32 caps =
FIELD_PREP(SDHCI_TIMEOUT_CLK_MASK, 0x21) |
FIELD_PREP(SDHCI_CLOCK_BASE_MASK, 0x21) |
SDHCI_TIMEOUT_CLK_UNIT |
SDHCI_CAN_VDD_330 |
SDHCI_CAN_DO_HISPD |
SDHCI_CAN_DO_SDMA;
+ u32 caps1 = 0;
+
+ __sdhci_read_caps(slot->host, NULL, &caps, &caps1);
return 0;
}
@@ -286,8 +289,7 @@ static const struct sdhci_pci_fixes sdhci_ricoh_mmc = {
#endif
.quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_CLOCK_BEFORE_RESET |
- SDHCI_QUIRK_NO_CARD_NO_RESET |
- SDHCI_QUIRK_MISSING_CAPS
+ SDHCI_QUIRK_NO_CARD_NO_RESET,
};
static void ene_714_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c
index f18906b5575f..fc306eb1f845 100644
--- a/drivers/mmc/host/sdhci-pxav2.c
+++ b/drivers/mmc/host/sdhci-pxav2.c
@@ -20,6 +20,9 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/mmc.h>
+#include <linux/pinctrl/consumer.h>
#include "sdhci.h"
#include "sdhci-pltfm.h"
@@ -41,6 +44,13 @@
#define MMC_CARD 0x1000
#define MMC_WIDTH 0x0100
+struct sdhci_pxav2_host {
+ struct mmc_request *sdio_mrq;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pins_default;
+ struct pinctrl_state *pins_cmd_gpio;
+};
+
static void pxav2_reset(struct sdhci_host *host, u8 mask)
{
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
@@ -80,6 +90,71 @@ static void pxav2_reset(struct sdhci_host *host, u8 mask)
}
}
+static u16 pxav1_readw(struct sdhci_host *host, int reg)
+{
+ /* Workaround for data abort exception on SDH2 and SDH4 on PXA168 */
+ if (reg == SDHCI_HOST_VERSION)
+ return readl(host->ioaddr + SDHCI_HOST_VERSION - 2) >> 16;
+
+ return readw(host->ioaddr + reg);
+}
+
+static u32 pxav1_irq(struct sdhci_host *host, u32 intmask)
+{
+ struct sdhci_pxav2_host *pxav2_host = sdhci_pltfm_priv(sdhci_priv(host));
+ struct mmc_request *sdio_mrq;
+
+ if (pxav2_host->sdio_mrq && (intmask & SDHCI_INT_CMD_MASK)) {
+ /* The dummy CMD0 for the SDIO workaround just completed */
+ sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, SDHCI_INT_STATUS);
+ intmask &= ~SDHCI_INT_CMD_MASK;
+
+ /* Restore MMC function to CMD pin */
+ if (pxav2_host->pinctrl && pxav2_host->pins_default)
+ pinctrl_select_state(pxav2_host->pinctrl, pxav2_host->pins_default);
+
+ sdio_mrq = pxav2_host->sdio_mrq;
+ pxav2_host->sdio_mrq = NULL;
+ mmc_request_done(host->mmc, sdio_mrq);
+ }
+
+ return intmask;
+}
+
+static void pxav1_request_done(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ u16 tmp;
+ struct sdhci_pxav2_host *pxav2_host;
+
+ /* If this is an SDIO command, perform errata workaround for silicon bug */
+ if (mrq->cmd && !mrq->cmd->error &&
+ (mrq->cmd->opcode == SD_IO_RW_DIRECT ||
+ mrq->cmd->opcode == SD_IO_RW_EXTENDED)) {
+ /* Reset data port */
+ tmp = readw(host->ioaddr + SDHCI_TIMEOUT_CONTROL);
+ tmp |= 0x400;
+ writew(tmp, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
+
+ /* Clock is now stopped, so restart it by sending a dummy CMD0 */
+ pxav2_host = sdhci_pltfm_priv(sdhci_priv(host));
+ pxav2_host->sdio_mrq = mrq;
+
+ /* Set CMD as high output rather than MMC function while we do CMD0 */
+ if (pxav2_host->pinctrl && pxav2_host->pins_cmd_gpio)
+ pinctrl_select_state(pxav2_host->pinctrl, pxav2_host->pins_cmd_gpio);
+
+ sdhci_writel(host, 0, SDHCI_ARGUMENT);
+ sdhci_writew(host, 0, SDHCI_TRANSFER_MODE);
+ sdhci_writew(host, SDHCI_MAKE_CMD(MMC_GO_IDLE_STATE, SDHCI_CMD_RESP_NONE),
+ SDHCI_COMMAND);
+
+ /* Don't finish this request until the dummy CMD0 finishes */
+ return;
+ }
+
+ mmc_request_done(host->mmc, mrq);
+}
+
static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width)
{
u8 ctrl;
@@ -101,6 +176,27 @@ static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width)
writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
}
+struct sdhci_pxa_variant {
+ const struct sdhci_ops *ops;
+ unsigned int extra_quirks;
+};
+
+static const struct sdhci_ops pxav1_sdhci_ops = {
+ .read_w = pxav1_readw,
+ .set_clock = sdhci_set_clock,
+ .irq = pxav1_irq,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .set_bus_width = pxav2_mmc_set_bus_width,
+ .reset = pxav2_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .request_done = pxav1_request_done,
+};
+
+static const struct sdhci_pxa_variant __maybe_unused pxav1_variant = {
+ .ops = &pxav1_sdhci_ops,
+ .extra_quirks = SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_32BIT_DMA_SIZE,
+};
+
static const struct sdhci_ops pxav2_sdhci_ops = {
.set_clock = sdhci_set_clock,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
@@ -109,11 +205,14 @@ static const struct sdhci_ops pxav2_sdhci_ops = {
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
+static const struct sdhci_pxa_variant pxav2_variant = {
+ .ops = &pxav2_sdhci_ops,
+};
+
#ifdef CONFIG_OF
static const struct of_device_id sdhci_pxav2_of_match[] = {
- {
- .compatible = "mrvl,pxav2-mmc",
- },
+ { .compatible = "mrvl,pxav1-mmc", .data = &pxav1_variant, },
+ { .compatible = "mrvl,pxav2-mmc", .data = &pxav2_variant, },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_pxav2_of_match);
@@ -155,40 +254,53 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
{
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
+ struct sdhci_pxav2_host *pxav2_host;
struct device *dev = &pdev->dev;
struct sdhci_host *host = NULL;
- const struct of_device_id *match;
+ const struct sdhci_pxa_variant *variant;
int ret;
- struct clk *clk;
+ struct clk *clk, *clk_core;
- host = sdhci_pltfm_init(pdev, NULL, 0);
+ host = sdhci_pltfm_init(pdev, NULL, sizeof(*pxav2_host));
if (IS_ERR(host))
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
+ pxav2_host = sdhci_pltfm_priv(pltfm_host);
- clk = devm_clk_get(dev, "PXA-SDHCLK");
+ clk = devm_clk_get(dev, "io");
+ if (IS_ERR(clk) && PTR_ERR(clk) != -EPROBE_DEFER)
+ clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk)) {
- dev_err(dev, "failed to get io clock\n");
ret = PTR_ERR(clk);
+ dev_err_probe(dev, ret, "failed to get io clock\n");
goto free;
}
pltfm_host->clk = clk;
ret = clk_prepare_enable(clk);
if (ret) {
- dev_err(&pdev->dev, "failed to enable io clock\n");
+ dev_err(dev, "failed to enable io clock\n");
goto free;
}
+ clk_core = devm_clk_get_optional_enabled(dev, "core");
+ if (IS_ERR(clk_core)) {
+ ret = PTR_ERR(clk_core);
+ dev_err_probe(dev, ret, "failed to enable core clock\n");
+ goto disable_clk;
+ }
+
host->quirks = SDHCI_QUIRK_BROKEN_ADMA
| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
| SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
- match = of_match_device(of_match_ptr(sdhci_pxav2_of_match), &pdev->dev);
- if (match) {
+ variant = of_device_get_match_data(dev);
+ if (variant)
pdata = pxav2_get_mmc_pdata(dev);
- }
+ else
+ variant = &pxav2_variant;
+
if (pdata) {
if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
/* on-chip device */
@@ -208,7 +320,23 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
host->mmc->pm_caps |= pdata->pm_caps;
}
- host->ops = &pxav2_sdhci_ops;
+ host->quirks |= variant->extra_quirks;
+ host->ops = variant->ops;
+
+ /* Set up optional pinctrl for PXA168 SDIO IRQ fix */
+ pxav2_host->pinctrl = devm_pinctrl_get(dev);
+ if (!IS_ERR(pxav2_host->pinctrl)) {
+ pxav2_host->pins_cmd_gpio = pinctrl_lookup_state(pxav2_host->pinctrl,
+ "state_cmd_gpio");
+ if (IS_ERR(pxav2_host->pins_cmd_gpio))
+ pxav2_host->pins_cmd_gpio = NULL;
+ pxav2_host->pins_default = pinctrl_lookup_state(pxav2_host->pinctrl,
+ "default");
+ if (IS_ERR(pxav2_host->pins_default))
+ pxav2_host->pins_default = NULL;
+ } else {
+ pxav2_host->pinctrl = NULL;
+ }
ret = sdhci_add_host(host);
if (ret)
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index a6d89a3f1946..e39dcc998772 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -124,10 +124,8 @@ static int armada_38x_quirks(struct platform_device *pdev,
struct resource *res;
host->quirks &= ~SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
- host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
- host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
- host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+ sdhci_read_caps(host);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"conf-sdio3");
diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c
index 525f979e2a97..7f4ee2e12735 100644
--- a/drivers/mmc/host/sdhci-sprd.c
+++ b/drivers/mmc/host/sdhci-sprd.c
@@ -553,8 +553,7 @@ static void sdhci_sprd_phy_param_parse(struct sdhci_sprd_host *sprd_host,
static const struct sdhci_pltfm_data sdhci_sprd_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
- SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
- SDHCI_QUIRK_MISSING_CAPS,
+ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200 |
SDHCI_QUIRK2_USE_32BIT_BLK_CNT |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
@@ -671,8 +670,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
* will allow these modes to be specified only by device
* tree properties through mmc_of_parse().
*/
- host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
- host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+ sdhci_read_caps(host);
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_DDR50);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f3af1bd0f7b9..3241916141d7 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -4121,9 +4121,6 @@ void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver,
v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
- if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
- return;
-
if (caps) {
host->caps = *caps;
} else {
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 605eaee805f7..f4f2085c274c 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -423,8 +423,6 @@ struct sdhci_host {
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
/* Controller cannot support End Attribute in NOP ADMA descriptor */
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
-/* Controller is missing device caps. Use caps provided by host */
-#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
/* Controller uses Auto CMD12 command to stop the transfer */
#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c
index 3a8defdcca77..61acd69fac0e 100644
--- a/drivers/mmc/host/uniphier-sd.c
+++ b/drivers/mmc/host/uniphier-sd.c
@@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/mfd/syscon.h>
#include <linux/mfd/tmio.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
@@ -15,6 +16,7 @@
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include "tmio_mmc.h"
@@ -48,6 +50,12 @@
#define UNIPHIER_SD_DMA_ADDR_L 0x440
#define UNIPHIER_SD_DMA_ADDR_H 0x444
+/* SD control */
+#define UNIPHIER_SDCTRL_CHOFFSET 0x200
+#define UNIPHIER_SDCTRL_MODE 0x30
+#define UNIPHIER_SDCTRL_MODE_UHS1MOD BIT(15)
+#define UNIPHIER_SDCTRL_MODE_SDRSEL BIT(14)
+
/*
* IP is extended to support various features: built-in DMA engine,
* 1/1024 divisor, etc.
@@ -66,6 +74,8 @@ struct uniphier_sd_priv {
struct reset_control *rst_hw;
struct dma_chan *chan;
enum dma_data_direction dma_dir;
+ struct regmap *sdctrl_regmap;
+ u32 sdctrl_ch;
unsigned long clk_rate;
unsigned long caps;
};
@@ -420,6 +430,42 @@ static void uniphier_sd_hw_reset(struct mmc_host *mmc)
usleep_range(300, 1000);
}
+static void uniphier_sd_speed_switch(struct tmio_mmc_host *host)
+{
+ struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
+ unsigned int offset;
+ u32 val = 0;
+
+ if (!(host->mmc->caps & MMC_CAP_UHS))
+ return;
+
+ if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR50 ||
+ host->mmc->ios.timing == MMC_TIMING_UHS_SDR104)
+ val = UNIPHIER_SDCTRL_MODE_SDRSEL;
+
+ offset = UNIPHIER_SDCTRL_CHOFFSET * priv->sdctrl_ch
+ + UNIPHIER_SDCTRL_MODE;
+ regmap_write_bits(priv->sdctrl_regmap, offset,
+ UNIPHIER_SDCTRL_MODE_SDRSEL, val);
+}
+
+static void uniphier_sd_uhs_enable(struct tmio_mmc_host *host, bool uhs_en)
+{
+ struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
+ unsigned int offset;
+ u32 val;
+
+ if (!(host->mmc->caps & MMC_CAP_UHS))
+ return;
+
+ val = (uhs_en) ? UNIPHIER_SDCTRL_MODE_UHS1MOD : 0;
+
+ offset = UNIPHIER_SDCTRL_CHOFFSET * priv->sdctrl_ch
+ + UNIPHIER_SDCTRL_MODE;
+ regmap_write_bits(priv->sdctrl_regmap, offset,
+ UNIPHIER_SDCTRL_MODE_UHS1MOD, val);
+}
+
static void uniphier_sd_set_clock(struct tmio_mmc_host *host,
unsigned int clock)
{
@@ -433,6 +479,8 @@ static void uniphier_sd_set_clock(struct tmio_mmc_host *host,
tmp &= ~CLK_CTL_SCLKEN;
writel(tmp, host->ctl + (CTL_SD_CARD_CLK_CTL << 1));
+ uniphier_sd_speed_switch(host);
+
if (clock == 0)
return;
@@ -500,14 +548,17 @@ static int uniphier_sd_start_signal_voltage_switch(struct mmc_host *mmc,
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
struct pinctrl_state *pinstate = NULL;
u32 val, tmp;
+ bool uhs_en;
switch (ios->signal_voltage) {
case MMC_SIGNAL_VOLTAGE_330:
val = UNIPHIER_SD_VOLT_330;
+ uhs_en = false;
break;
case MMC_SIGNAL_VOLTAGE_180:
val = UNIPHIER_SD_VOLT_180;
pinstate = priv->pinstate_uhs;
+ uhs_en = true;
break;
default:
return -ENOTSUPP;
@@ -523,12 +574,19 @@ static int uniphier_sd_start_signal_voltage_switch(struct mmc_host *mmc,
else
pinctrl_select_default_state(mmc_dev(mmc));
+ uniphier_sd_uhs_enable(host, uhs_en);
+
return 0;
}
-static int uniphier_sd_uhs_init(struct tmio_mmc_host *host,
- struct uniphier_sd_priv *priv)
+static int uniphier_sd_uhs_init(struct tmio_mmc_host *host)
{
+ struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
+ struct device *dev = &host->pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct of_phandle_args args;
+ int ret;
+
priv->pinctrl = devm_pinctrl_get(mmc_dev(host->mmc));
if (IS_ERR(priv->pinctrl))
return PTR_ERR(priv->pinctrl);
@@ -537,8 +595,20 @@ static int uniphier_sd_uhs_init(struct tmio_mmc_host *host,
if (IS_ERR(priv->pinstate_uhs))
return PTR_ERR(priv->pinstate_uhs);
- host->ops.start_signal_voltage_switch =
- uniphier_sd_start_signal_voltage_switch;
+ ret = of_parse_phandle_with_fixed_args(np,
+ "socionext,syscon-uhs-mode",
+ 1, 0, &args);
+ if (ret) {
+ dev_err(dev, "Can't get syscon-uhs-mode property\n");
+ return ret;
+ }
+ priv->sdctrl_regmap = syscon_node_to_regmap(args.np);
+ of_node_put(args.np);
+ if (IS_ERR(priv->sdctrl_regmap)) {
+ dev_err(dev, "Can't map syscon-uhs-mode\n");
+ return PTR_ERR(priv->sdctrl_regmap);
+ }
+ priv->sdctrl_ch = args.args[0];
return 0;
}
@@ -601,12 +671,15 @@ static int uniphier_sd_probe(struct platform_device *pdev)
}
if (host->mmc->caps & MMC_CAP_UHS) {
- ret = uniphier_sd_uhs_init(host, priv);
+ ret = uniphier_sd_uhs_init(host);
if (ret) {
dev_warn(dev,
"failed to setup UHS (error %d). Disabling UHS.",
ret);
host->mmc->caps &= ~MMC_CAP_UHS;
+ } else {
+ host->ops.start_signal_voltage_switch =
+ uniphier_sd_start_signal_voltage_switch;
}
}