summaryrefslogtreecommitdiff
path: root/drivers/mmc/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/Kconfig16
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/alcor.c3
-rw-r--r--drivers/mmc/host/atmel-mci.c17
-rw-r--r--drivers/mmc/host/bcm2835.c31
-rw-r--r--drivers/mmc/host/cavium-thunderx.c4
-rw-r--r--drivers/mmc/host/cqhci-crypto.c46
-rw-r--r--drivers/mmc/host/cqhci.h8
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c41
-rw-r--r--drivers/mmc/host/dw_mmc-hi3798mv200.c8
-rw-r--r--drivers/mmc/host/dw_mmc.c125
-rw-r--r--drivers/mmc/host/dw_mmc.h27
-rw-r--r--drivers/mmc/host/jz4740_mmc.c7
-rw-r--r--drivers/mmc/host/meson-mx-sdio.c7
-rw-r--r--drivers/mmc/host/mtk-sd.c261
-rw-r--r--drivers/mmc/host/mvsdio.c6
-rw-r--r--drivers/mmc/host/mxcmmc.c14
-rw-r--r--drivers/mmc/host/omap.c36
-rw-r--r--drivers/mmc/host/renesas_sdhi.h1
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c147
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c2
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c7
-rw-r--r--drivers/mmc/host/sdhci-acpi.c20
-rw-r--r--drivers/mmc/host/sdhci-brcmstb.c10
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c261
-rw-r--r--drivers/mmc/host/sdhci-msm.c115
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c112
-rw-r--r--drivers/mmc/host/sdhci-of-k1.c305
-rw-r--r--drivers/mmc/host/sdhci-omap.c6
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c9
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c1
-rw-r--r--drivers/mmc/host/sdhci-uhs2.c20
-rw-r--r--drivers/mmc/host/sdhci.c24
-rw-r--r--drivers/mmc/host/sdhci.h18
-rw-r--r--drivers/mmc/host/sdhci_am654.c44
-rw-r--r--drivers/mmc/host/sunplus-mmc.c2
-rw-r--r--drivers/mmc/host/tifm_sd.c4
-rw-r--r--drivers/mmc/host/tmio_mmc.h10
-rw-r--r--drivers/mmc/host/tmio_mmc_core.c7
-rw-r--r--drivers/mmc/host/via-sdmmc.c6
-rw-r--r--drivers/mmc/host/vub300.c14
-rw-r--r--drivers/mmc/host/wbsd.c4
42 files changed, 1402 insertions, 405 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 6824131b69b1..c3f0f41a426d 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -250,6 +250,20 @@ 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_K1
+ tristate "SDHCI OF support for the SpacemiT K1 SoC"
+ depends on ARCH_SPACEMIT || COMPILE_TEST
+ depends on MMC_SDHCI_PLTFM
+ depends on OF
+ depends on COMMON_CLK
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ found in the SpacemiT K1 SoC.
+
+ 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
@@ -691,8 +705,8 @@ config MMC_TMIO_CORE
config MMC_SDHI
tristate "Renesas SDHI SD/SDIO controller support"
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
+ depends on (RESET_CONTROLLER && REGULATOR) || !OF
select MMC_TMIO_CORE
- select RESET_CONTROLLER if ARCH_RENESAS
help
This provides support for the SDHI SD/SDIO controller found in
Renesas SuperH, ARM and ARM64 based SoCs
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 5147467ec825..75bafc7b162b 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -88,6 +88,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_K1) += sdhci-of-k1.o
obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o
obj-$(CONFIG_MMC_SDHCI_OF_MA35D1) += sdhci-of-ma35d1.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c
index b6b6dd677ae5..24abd3a93da9 100644
--- a/drivers/mmc/host/alcor.c
+++ b/drivers/mmc/host/alcor.c
@@ -20,6 +20,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/string_choices.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
@@ -208,7 +209,7 @@ static void alcor_trf_block_pio(struct alcor_sdmmc_host *host, bool read)
len = min(host->sg_miter.length, blksize);
dev_dbg(host->dev, "PIO, %s block size: 0x%zx\n",
- read ? "read" : "write", blksize);
+ str_read_write(read), blksize);
host->sg_miter.consumed = len;
host->blocks--;
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index fc360902729d..0e0666c0bb6e 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -714,7 +714,7 @@ static inline unsigned int atmci_convert_chksize(struct atmel_mci *host,
static void atmci_timeout_timer(struct timer_list *t)
{
- struct atmel_mci *host = from_timer(host, t, timer);
+ struct atmel_mci *host = timer_container_of(host, t, timer);
struct device *dev = host->dev;
dev_dbg(dev, "software timeout\n");
@@ -1592,7 +1592,7 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
WARN_ON(host->cmd || host->data);
- del_timer(&host->timer);
+ timer_delete(&host->timer);
/*
* Update the MMC clock rate if necessary. This may be
@@ -1652,7 +1652,8 @@ static void atmci_command_complete(struct atmel_mci *host,
static void atmci_detect_change(struct timer_list *t)
{
- struct atmel_mci_slot *slot = from_timer(slot, t, detect_timer);
+ struct atmel_mci_slot *slot = timer_container_of(slot, t,
+ detect_timer);
bool present;
bool present_old;
@@ -2357,7 +2358,7 @@ static void atmci_cleanup_slot(struct atmel_mci_slot *slot,
if (slot->detect_pin) {
free_irq(gpiod_to_irq(slot->detect_pin), slot);
- del_timer_sync(&slot->detect_timer);
+ timer_delete_sync(&slot->detect_timer);
}
slot->host->slot[id] = NULL;
@@ -2499,8 +2500,10 @@ static int atmci_probe(struct platform_device *pdev)
/* Get MCI capabilities and set operations according to it */
atmci_get_cap(host);
ret = atmci_configure_dma(host);
- if (ret == -EPROBE_DEFER)
+ if (ret == -EPROBE_DEFER) {
+ clk_disable_unprepare(host->mck);
goto err_dma_probe_defer;
+ }
if (ret == 0) {
host->prepare_data = &atmci_prepare_data_dma;
host->submit_data = &atmci_submit_data_dma;
@@ -2583,7 +2586,7 @@ err_init_slot:
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
- del_timer_sync(&host->timer);
+ timer_delete_sync(&host->timer);
if (!IS_ERR(host->dma.chan))
dma_release_channel(host->dma.chan);
err_dma_probe_defer:
@@ -2611,7 +2614,7 @@ static void atmci_remove(struct platform_device *pdev)
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
atmci_readl(host, ATMCI_SR);
- del_timer_sync(&host->timer);
+ timer_delete_sync(&host->timer);
if (!IS_ERR(host->dma.chan))
dma_release_channel(host->dma.chan);
diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c
index 7847f0c8b465..4fced9b36c80 100644
--- a/drivers/mmc/host/bcm2835.c
+++ b/drivers/mmc/host/bcm2835.c
@@ -44,6 +44,7 @@
#include <linux/scatterlist.h>
#include <linux/time.h>
#include <linux/workqueue.h>
+#include <linux/string_choices.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
@@ -391,8 +392,7 @@ static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
if (time_after(jiffies, wait_max)) {
dev_err(dev, "PIO %s timeout - EDM %08x\n",
- is_read ? "read" : "write",
- edm);
+ str_read_write(is_read), edm);
hsts = SDHSTS_REW_TIME_OUT;
break;
}
@@ -435,12 +435,12 @@ static void bcm2835_transfer_pio(struct bcm2835_host *host)
SDHSTS_CRC7_ERROR |
SDHSTS_FIFO_ERROR)) {
dev_err(dev, "%s transfer error - HSTS %08x\n",
- is_read ? "read" : "write", sdhsts);
+ str_read_write(is_read), sdhsts);
host->data->error = -EILSEQ;
} else if ((sdhsts & (SDHSTS_CMD_TIME_OUT |
SDHSTS_REW_TIME_OUT))) {
dev_err(dev, "%s timeout error - HSTS %08x\n",
- is_read ? "read" : "write", sdhsts);
+ str_read_write(is_read), sdhsts);
host->data->error = -ETIMEDOUT;
}
}
@@ -503,7 +503,8 @@ void bcm2835_prepare_dma(struct bcm2835_host *host, struct mmc_data *data)
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
- dma_unmap_sg(dma_chan->device->dev, data->sg, sg_len, dir_data);
+ dma_unmap_sg(dma_chan->device->dev, data->sg, data->sg_len,
+ dir_data);
return;
}
@@ -1343,6 +1344,25 @@ static int bcm2835_add_host(struct bcm2835_host *host)
return 0;
}
+static int bcm2835_suspend(struct device *dev)
+{
+ struct bcm2835_host *host = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(host->clk);
+
+ return 0;
+}
+
+static int bcm2835_resume(struct device *dev)
+{
+ struct bcm2835_host *host = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(host->clk);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(bcm2835_pm_ops, bcm2835_suspend,
+ bcm2835_resume);
+
static int bcm2835_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1471,6 +1491,7 @@ static struct platform_driver bcm2835_driver = {
.name = "sdhost-bcm2835",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = bcm2835_match,
+ .pm = pm_ptr(&bcm2835_pm_ops),
},
};
module_platform_driver(bcm2835_driver);
diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c
index 2e2ff984f0b3..1373deb3f531 100644
--- a/drivers/mmc/host/cavium-thunderx.c
+++ b/drivers/mmc/host/cavium-thunderx.c
@@ -72,7 +72,7 @@ static int thunder_mmc_probe(struct pci_dev *pdev,
if (ret)
return ret;
- ret = pci_request_regions(pdev, KBUILD_MODNAME);
+ ret = pcim_request_all_regions(pdev, KBUILD_MODNAME);
if (ret)
return ret;
@@ -164,7 +164,6 @@ error:
}
}
clk_disable_unprepare(host->clk);
- pci_release_regions(pdev);
return ret;
}
@@ -183,7 +182,6 @@ static void thunder_mmc_remove(struct pci_dev *pdev)
writeq(dma_cfg, host->dma_base + MIO_EMM_DMA_CFG(host));
clk_disable_unprepare(host->clk);
- pci_release_regions(pdev);
}
static const struct pci_device_id thunder_mmc_id_table[] = {
diff --git a/drivers/mmc/host/cqhci-crypto.c b/drivers/mmc/host/cqhci-crypto.c
index d5f4b6972f63..5a467098a0d6 100644
--- a/drivers/mmc/host/cqhci-crypto.c
+++ b/drivers/mmc/host/cqhci-crypto.c
@@ -25,22 +25,16 @@ static const struct cqhci_crypto_alg_entry {
static inline struct cqhci_host *
cqhci_host_from_crypto_profile(struct blk_crypto_profile *profile)
{
- struct mmc_host *mmc =
- container_of(profile, struct mmc_host, crypto_profile);
-
- return mmc->cqe_private;
+ return mmc_from_crypto_profile(profile)->cqe_private;
}
-static int cqhci_crypto_program_key(struct cqhci_host *cq_host,
- const union cqhci_crypto_cfg_entry *cfg,
- int slot)
+static void cqhci_crypto_program_key(struct cqhci_host *cq_host,
+ const union cqhci_crypto_cfg_entry *cfg,
+ int slot)
{
u32 slot_offset = cq_host->crypto_cfg_register + slot * sizeof(*cfg);
int i;
- if (cq_host->ops->program_key)
- return cq_host->ops->program_key(cq_host, cfg, slot);
-
/* Clear CFGE */
cqhci_writel(cq_host, 0, slot_offset + 16 * sizeof(cfg->reg_val[0]));
@@ -55,7 +49,6 @@ static int cqhci_crypto_program_key(struct cqhci_host *cq_host,
/* Write dword 16, which includes the new value of CFGE */
cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[16]),
slot_offset + 16 * sizeof(cfg->reg_val[0]));
- return 0;
}
static int cqhci_crypto_keyslot_program(struct blk_crypto_profile *profile,
@@ -72,7 +65,6 @@ static int cqhci_crypto_keyslot_program(struct blk_crypto_profile *profile,
int i;
int cap_idx = -1;
union cqhci_crypto_cfg_entry cfg = {};
- int err;
BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0);
for (i = 0; i < cq_host->crypto_capabilities.num_crypto_cap; i++) {
@@ -92,17 +84,17 @@ static int cqhci_crypto_keyslot_program(struct blk_crypto_profile *profile,
if (ccap_array[cap_idx].algorithm_id == CQHCI_CRYPTO_ALG_AES_XTS) {
/* In XTS mode, the blk_crypto_key's size is already doubled */
- memcpy(cfg.crypto_key, key->raw, key->size/2);
+ memcpy(cfg.crypto_key, key->bytes, key->size/2);
memcpy(cfg.crypto_key + CQHCI_CRYPTO_KEY_MAX_SIZE/2,
- key->raw + key->size/2, key->size/2);
+ key->bytes + key->size/2, key->size/2);
} else {
- memcpy(cfg.crypto_key, key->raw, key->size);
+ memcpy(cfg.crypto_key, key->bytes, key->size);
}
- err = cqhci_crypto_program_key(cq_host, &cfg, slot);
+ cqhci_crypto_program_key(cq_host, &cfg, slot);
memzero_explicit(&cfg, sizeof(cfg));
- return err;
+ return 0;
}
static int cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot)
@@ -113,7 +105,8 @@ static int cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot)
*/
union cqhci_crypto_cfg_entry cfg = {};
- return cqhci_crypto_program_key(cq_host, &cfg, slot);
+ cqhci_crypto_program_key(cq_host, &cfg, slot);
+ return 0;
}
static int cqhci_crypto_keyslot_evict(struct blk_crypto_profile *profile,
@@ -170,7 +163,6 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
struct mmc_host *mmc = cq_host->mmc;
struct device *dev = mmc_dev(mmc);
struct blk_crypto_profile *profile = &mmc->crypto_profile;
- unsigned int num_keyslots;
unsigned int cap_idx;
enum blk_crypto_mode_num blk_mode_num;
unsigned int slot;
@@ -180,6 +172,9 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
goto out;
+ if (cq_host->ops->uses_custom_crypto_profile)
+ goto profile_initialized;
+
cq_host->crypto_capabilities.reg_val =
cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP));
@@ -198,9 +193,8 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
* CCAP.CFGC is off by one, so the actual number of crypto
* configurations (a.k.a. keyslots) is CCAP.CFGC + 1.
*/
- num_keyslots = cq_host->crypto_capabilities.config_count + 1;
-
- err = devm_blk_crypto_profile_init(dev, profile, num_keyslots);
+ err = devm_blk_crypto_profile_init(
+ dev, profile, cq_host->crypto_capabilities.config_count + 1);
if (err)
goto out;
@@ -210,6 +204,8 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
/* Unfortunately, CQHCI crypto only supports 32 DUN bits. */
profile->max_dun_bytes_supported = 4;
+ profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW;
+
/*
* Cache all the crypto capabilities and advertise the supported crypto
* modes and data unit sizes to the block layer.
@@ -228,9 +224,11 @@ int cqhci_crypto_init(struct cqhci_host *cq_host)
cq_host->crypto_cap_array[cap_idx].sdus_mask * 512;
}
+profile_initialized:
+
/* Clear all the keyslots so that we start in a known state. */
- for (slot = 0; slot < num_keyslots; slot++)
- cqhci_crypto_clear_keyslot(cq_host, slot);
+ for (slot = 0; slot < profile->num_slots; slot++)
+ profile->ll_ops.keyslot_evict(profile, NULL, slot);
/* CQHCI crypto requires the use of 128-bit task descriptors. */
cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h
index fab9d74445ba..ce189a1866b9 100644
--- a/drivers/mmc/host/cqhci.h
+++ b/drivers/mmc/host/cqhci.h
@@ -289,13 +289,11 @@ struct cqhci_host_ops {
u64 *data);
void (*pre_enable)(struct mmc_host *mmc);
void (*post_disable)(struct mmc_host *mmc);
-#ifdef CONFIG_MMC_CRYPTO
- int (*program_key)(struct cqhci_host *cq_host,
- const union cqhci_crypto_cfg_entry *cfg, int slot);
-#endif
void (*set_tran_desc)(struct cqhci_host *cq_host, u8 **desc,
dma_addr_t addr, int len, bool end, bool dma64);
-
+#ifdef CONFIG_MMC_CRYPTO
+ bool uses_custom_crypto_profile;
+#endif
};
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 53d32d0f2709..e3548408ca39 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -27,6 +27,8 @@ enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS5420_SMU,
DW_MCI_TYPE_EXYNOS7,
DW_MCI_TYPE_EXYNOS7_SMU,
+ DW_MCI_TYPE_EXYNOS7870,
+ DW_MCI_TYPE_EXYNOS7870_SMU,
DW_MCI_TYPE_ARTPEC8,
};
@@ -70,6 +72,12 @@ static struct dw_mci_exynos_compatible {
.compatible = "samsung,exynos7-dw-mshc-smu",
.ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU,
}, {
+ .compatible = "samsung,exynos7870-dw-mshc",
+ .ctrl_type = DW_MCI_TYPE_EXYNOS7870,
+ }, {
+ .compatible = "samsung,exynos7870-dw-mshc-smu",
+ .ctrl_type = DW_MCI_TYPE_EXYNOS7870_SMU,
+ }, {
.compatible = "axis,artpec8-dw-mshc",
.ctrl_type = DW_MCI_TYPE_ARTPEC8,
},
@@ -85,6 +93,8 @@ static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host)
return EXYNOS4210_FIXED_CIU_CLK_DIV;
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1;
else
@@ -100,7 +110,8 @@ static void dw_mci_exynos_config_smu(struct dw_mci *host)
* set for non-ecryption mode at this time.
*/
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
- priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) {
mci_writel(host, MPSBEGIN0, 0);
mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX);
mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT |
@@ -126,6 +137,12 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
}
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) {
+ /* Quirk needed for certain Exynos SoCs */
+ host->quirks |= DW_MMC_QUIRK_FIFO64_32;
+ }
+
if (priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) {
/* Quirk needed for the ARTPEC-8 SoC */
host->quirks |= DW_MMC_QUIRK_EXTENDED_TMOUT;
@@ -143,6 +160,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -152,6 +171,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -222,6 +243,8 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -230,6 +253,8 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -409,6 +434,8 @@ static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
else
@@ -422,6 +449,8 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -429,6 +458,8 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -443,6 +474,8 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -453,6 +486,8 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -632,6 +667,10 @@ static const struct of_device_id dw_mci_exynos_match[] = {
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos7-dw-mshc-smu",
.data = &exynos_drv_data, },
+ { .compatible = "samsung,exynos7870-dw-mshc",
+ .data = &exynos_drv_data, },
+ { .compatible = "samsung,exynos7870-dw-mshc-smu",
+ .data = &exynos_drv_data, },
{ .compatible = "axis,artpec8-dw-mshc",
.data = &artpec_drv_data, },
{},
diff --git a/drivers/mmc/host/dw_mmc-hi3798mv200.c b/drivers/mmc/host/dw_mmc-hi3798mv200.c
index cce174b5249b..5791a975a944 100644
--- a/drivers/mmc/host/dw_mmc-hi3798mv200.c
+++ b/drivers/mmc/host/dw_mmc-hi3798mv200.c
@@ -181,7 +181,6 @@ static int dw_mci_hi3798mv200_init(struct dw_mci *host)
{
struct dw_mci_hi3798mv200_priv *priv;
struct device_node *np = host->dev->of_node;
- int ret;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -199,15 +198,12 @@ static int dw_mci_hi3798mv200_init(struct dw_mci *host)
return dev_err_probe(host->dev, PTR_ERR(priv->drive_clk),
"failed to get enabled ciu-drive clock\n");
- priv->crg_reg = syscon_regmap_lookup_by_phandle(np, "hisilicon,sap-dll-reg");
+ priv->crg_reg = syscon_regmap_lookup_by_phandle_args(np, "hisilicon,sap-dll-reg",
+ 1, &priv->sap_dll_offset);
if (IS_ERR(priv->crg_reg))
return dev_err_probe(host->dev, PTR_ERR(priv->crg_reg),
"failed to get CRG reg\n");
- ret = of_property_read_u32_index(np, "hisilicon,sap-dll-reg", 1, &priv->sap_dll_offset);
- if (ret)
- return dev_err_probe(host->dev, ret, "failed to get sample DLL register offset\n");
-
host->priv = priv;
return 0;
}
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 3cbda98d08d2..988492237707 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1875,8 +1875,7 @@ static void dw_mci_init_fault(struct dw_mci *host)
{
host->fail_data_crc = (struct fault_attr) FAULT_ATTR_INITIALIZER;
- hrtimer_init(&host->fault_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- host->fault_timer.function = dw_mci_fault_timer;
+ hrtimer_setup(&host->fault_timer, dw_mci_fault_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
}
#else
static void dw_mci_init_fault(struct dw_mci *host)
@@ -2041,10 +2040,10 @@ static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host)
* Really be certain that the timer has stopped. This is a bit of
* paranoia and could only really happen if we had really bad
* interrupt latency and the interrupt routine and timeout were
- * running concurrently so that the del_timer() in the interrupt
+ * running concurrently so that the timer_delete() in the interrupt
* handler couldn't run.
*/
- WARN_ON(del_timer_sync(&host->cto_timer));
+ WARN_ON(timer_delete_sync(&host->cto_timer));
clear_bit(EVENT_CMD_COMPLETE, &host->pending_events);
return true;
@@ -2056,7 +2055,7 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
return false;
/* Extra paranoia just like dw_mci_clear_pending_cmd_complete() */
- WARN_ON(del_timer_sync(&host->dto_timer));
+ WARN_ON(timer_delete_sync(&host->dto_timer));
clear_bit(EVENT_DATA_COMPLETE, &host->pending_events);
return true;
@@ -2579,6 +2578,91 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
}
}
+static void dw_mci_push_data64_32(struct dw_mci *host, void *buf, int cnt)
+{
+ struct mmc_data *data = host->data;
+ int init_cnt = cnt;
+
+ /* try and push anything in the part_buf */
+ if (unlikely(host->part_buf_count)) {
+ int len = dw_mci_push_part_bytes(host, buf, cnt);
+
+ buf += len;
+ cnt -= len;
+
+ if (host->part_buf_count == 8) {
+ mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
+ host->part_buf_count = 0;
+ }
+ }
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x7)) {
+ while (cnt >= 8) {
+ u64 aligned_buf[16];
+ int len = min(cnt & -8, (int)sizeof(aligned_buf));
+ int items = len >> 3;
+ int i;
+ /* memcpy from input buffer into aligned buffer */
+ memcpy(aligned_buf, buf, len);
+ buf += len;
+ cnt -= len;
+ /* push data from aligned buffer into fifo */
+ for (i = 0; i < items; ++i)
+ mci_fifo_l_writeq(host->fifo_reg, aligned_buf[i]);
+ }
+ } else
+#endif
+ {
+ u64 *pdata = buf;
+
+ for (; cnt >= 8; cnt -= 8)
+ mci_fifo_l_writeq(host->fifo_reg, *pdata++);
+ buf = pdata;
+ }
+ /* put anything remaining in the part_buf */
+ if (cnt) {
+ dw_mci_set_part_bytes(host, buf, cnt);
+ /* Push data if we have reached the expected data length */
+ if ((data->bytes_xfered + init_cnt) ==
+ (data->blksz * data->blocks))
+ mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
+ }
+}
+
+static void dw_mci_pull_data64_32(struct dw_mci *host, void *buf, int cnt)
+{
+#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ if (unlikely((unsigned long)buf & 0x7)) {
+ while (cnt >= 8) {
+ /* pull data from fifo into aligned buffer */
+ u64 aligned_buf[16];
+ int len = min(cnt & -8, (int)sizeof(aligned_buf));
+ int items = len >> 3;
+ int i;
+
+ for (i = 0; i < items; ++i)
+ aligned_buf[i] = mci_fifo_l_readq(host->fifo_reg);
+
+ /* memcpy from aligned buffer into output buffer */
+ memcpy(buf, aligned_buf, len);
+ buf += len;
+ cnt -= len;
+ }
+ } else
+#endif
+ {
+ u64 *pdata = buf;
+
+ for (; cnt >= 8; cnt -= 8)
+ *pdata++ = mci_fifo_l_readq(host->fifo_reg);
+ buf = pdata;
+ }
+ if (cnt) {
+ host->part_buf = mci_fifo_l_readq(host->fifo_reg);
+ dw_mci_pull_final_bytes(host, buf, cnt);
+ }
+}
+
static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt)
{
int len;
@@ -2704,7 +2788,7 @@ done:
static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
{
- del_timer(&host->cto_timer);
+ timer_delete(&host->cto_timer);
if (!host->cmd_status)
host->cmd_status = status;
@@ -2748,13 +2832,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
dw_mci_cmd_interrupt(host, pending);
spin_unlock(&host->irq_lock);
- del_timer(&host->cmd11_timer);
+ timer_delete(&host->cmd11_timer);
}
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
spin_lock(&host->irq_lock);
- del_timer(&host->cto_timer);
+ timer_delete(&host->cto_timer);
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = pending;
smp_wmb(); /* drain writebuffer */
@@ -2767,7 +2851,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
spin_lock(&host->irq_lock);
if (host->quirks & DW_MMC_QUIRK_EXTENDED_TMOUT)
- del_timer(&host->dto_timer);
+ timer_delete(&host->dto_timer);
/* if there is an error report DATA_ERROR */
mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
@@ -2788,7 +2872,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_DATA_OVER) {
spin_lock(&host->irq_lock);
- del_timer(&host->dto_timer);
+ timer_delete(&host->dto_timer);
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
if (!host->data_status)
@@ -3095,7 +3179,7 @@ no_dma:
static void dw_mci_cmd11_timer(struct timer_list *t)
{
- struct dw_mci *host = from_timer(host, t, cmd11_timer);
+ struct dw_mci *host = timer_container_of(host, t, cmd11_timer);
if (host->state != STATE_SENDING_CMD11) {
dev_warn(host->dev, "Unexpected CMD11 timeout\n");
@@ -3109,7 +3193,7 @@ static void dw_mci_cmd11_timer(struct timer_list *t)
static void dw_mci_cto_timer(struct timer_list *t)
{
- struct dw_mci *host = from_timer(host, t, cto_timer);
+ struct dw_mci *host = timer_container_of(host, t, cto_timer);
unsigned long irqflags;
u32 pending;
@@ -3164,7 +3248,7 @@ exit:
static void dw_mci_dto_timer(struct timer_list *t)
{
- struct dw_mci *host = from_timer(host, t, dto_timer);
+ struct dw_mci *host = timer_container_of(host, t, dto_timer);
unsigned long irqflags;
u32 pending;
@@ -3379,8 +3463,13 @@ int dw_mci_probe(struct dw_mci *host)
width = 16;
host->data_shift = 1;
} else if (i == 2) {
- host->push_data = dw_mci_push_data64;
- host->pull_data = dw_mci_pull_data64;
+ if ((host->quirks & DW_MMC_QUIRK_FIFO64_32)) {
+ host->push_data = dw_mci_push_data64_32;
+ host->pull_data = dw_mci_pull_data64_32;
+ } else {
+ host->push_data = dw_mci_push_data64;
+ host->pull_data = dw_mci_pull_data64;
+ }
width = 64;
host->data_shift = 3;
} else {
@@ -3533,7 +3622,7 @@ int dw_mci_runtime_suspend(struct device *dev)
clk_disable_unprepare(host->ciu_clk);
if (host->slot &&
- (mmc_can_gpio_cd(host->slot->mmc) ||
+ (mmc_host_can_gpio_cd(host->slot->mmc) ||
!mmc_card_is_removable(host->slot->mmc)))
clk_disable_unprepare(host->biu_clk);
@@ -3547,7 +3636,7 @@ int dw_mci_runtime_resume(struct device *dev)
struct dw_mci *host = dev_get_drvdata(dev);
if (host->slot &&
- (mmc_can_gpio_cd(host->slot->mmc) ||
+ (mmc_host_can_gpio_cd(host->slot->mmc) ||
!mmc_card_is_removable(host->slot->mmc))) {
ret = clk_prepare_enable(host->biu_clk);
if (ret)
@@ -3601,7 +3690,7 @@ int dw_mci_runtime_resume(struct device *dev)
err:
if (host->slot &&
- (mmc_can_gpio_cd(host->slot->mmc) ||
+ (mmc_host_can_gpio_cd(host->slot->mmc) ||
!mmc_card_is_removable(host->slot->mmc)))
clk_disable_unprepare(host->biu_clk);
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 6447b916990d..5463392dc811 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -281,6 +281,8 @@ struct dw_mci_board {
/* Support for longer data read timeout */
#define DW_MMC_QUIRK_EXTENDED_TMOUT BIT(0)
+/* Force 32-bit access to the FIFO */
+#define DW_MMC_QUIRK_FIFO64_32 BIT(1)
#define DW_MMC_240A 0x240a
#define DW_MMC_280A 0x280a
@@ -472,6 +474,31 @@ struct dw_mci_board {
#define mci_fifo_writel(__value, __reg) __raw_writel(__reg, __value)
#define mci_fifo_writeq(__value, __reg) __raw_writeq(__reg, __value)
+/*
+ * Some dw_mmc devices have 64-bit FIFOs, but expect them to be
+ * accessed using two 32-bit accesses. If such controller is used
+ * with a 64-bit kernel, this has to be done explicitly.
+ */
+static inline u64 mci_fifo_l_readq(void __iomem *addr)
+{
+ u64 ans;
+ u32 proxy[2];
+
+ proxy[0] = mci_fifo_readl(addr);
+ proxy[1] = mci_fifo_readl(addr + 4);
+ memcpy(&ans, proxy, 8);
+ return ans;
+}
+
+static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value)
+{
+ u32 proxy[2];
+
+ memcpy(proxy, &value, 8);
+ mci_fifo_writel(addr, proxy[0]);
+ mci_fifo_writel(addr + 4, proxy[1]);
+}
+
/* Register access macros */
#define mci_readl(dev, reg) \
readl_relaxed((dev)->regs + SDMMC_##reg)
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 596012d5afac..0fbbf57db52e 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -641,7 +641,8 @@ poll_timeout:
static void jz4740_mmc_timeout(struct timer_list *t)
{
- struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer);
+ struct jz4740_mmc_host *host = timer_container_of(host, t,
+ timeout_timer);
if (!test_and_clear_bit(0, &host->waiting))
return;
@@ -862,7 +863,7 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
if (host->req && cmd && irq_reg) {
if (test_and_clear_bit(0, &host->waiting)) {
- del_timer(&host->timeout_timer);
+ timer_delete(&host->timeout_timer);
if (status & JZ_MMC_STATUS_TIMEOUT_RES) {
cmd->error = -ETIMEDOUT;
@@ -1162,7 +1163,7 @@ static void jz4740_mmc_remove(struct platform_device *pdev)
{
struct jz4740_mmc_host *host = platform_get_drvdata(pdev);
- del_timer_sync(&host->timeout_timer);
+ timer_delete_sync(&host->timeout_timer);
jz4740_mmc_set_irq_enabled(host, 0xff, false);
jz4740_mmc_reset(host);
diff --git a/drivers/mmc/host/meson-mx-sdio.c b/drivers/mmc/host/meson-mx-sdio.c
index ad351805eed4..8a49c32fd3f9 100644
--- a/drivers/mmc/host/meson-mx-sdio.c
+++ b/drivers/mmc/host/meson-mx-sdio.c
@@ -446,7 +446,7 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data)
if (WARN_ON(!cmd))
return IRQ_HANDLED;
- del_timer_sync(&host->cmd_timeout);
+ timer_delete_sync(&host->cmd_timeout);
if (cmd->data) {
dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
@@ -467,7 +467,8 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data)
static void meson_mx_mmc_timeout(struct timer_list *t)
{
- struct meson_mx_mmc_host *host = from_timer(host, t, cmd_timeout);
+ struct meson_mx_mmc_host *host = timer_container_of(host, t,
+ cmd_timeout);
unsigned long irqflags;
u32 irqc;
@@ -733,7 +734,7 @@ static void meson_mx_mmc_remove(struct platform_device *pdev)
struct meson_mx_mmc_host *host = platform_get_drvdata(pdev);
struct device *slot_dev = mmc_dev(host->mmc);
- del_timer_sync(&host->cmd_timeout);
+ timer_delete_sync(&host->cmd_timeout);
mmc_remove_host(host->mmc);
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 5ab7a26529a0..d7020e06dd55 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -5,6 +5,7 @@
*/
#include <linux/module.h>
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -83,6 +84,7 @@
#define EMMC51_CFG0 0x204
#define EMMC50_CFG0 0x208
#define EMMC50_CFG1 0x20c
+#define EMMC50_CFG2 0x21c
#define EMMC50_CFG3 0x220
#define SDC_FIFO_CFG 0x228
#define CQHCI_SETTING 0x7fc
@@ -233,7 +235,9 @@
/* MSDC_PATCH_BIT mask */
#define MSDC_PATCH_BIT_ODDSUPP BIT(1) /* RW */
+#define MSDC_PATCH_BIT_DIS_WRMON BIT(2) /* RW */
#define MSDC_PATCH_BIT_RD_DAT_SEL BIT(3) /* RW */
+#define MSDC_PATCH_BIT_DESCUP_SEL BIT(6) /* RW */
#define MSDC_INT_DAT_LATCH_CK_SEL GENMASK(9, 7)
#define MSDC_CKGEN_MSDC_DLY_SEL GENMASK(14, 10)
#define MSDC_PATCH_BIT_IODSSEL BIT(16) /* RW */
@@ -246,10 +250,22 @@
#define MSDC_PATCH_BIT_SPCPUSH BIT(29) /* RW */
#define MSDC_PATCH_BIT_DECRCTMO BIT(30) /* RW */
-#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */
+/* MSDC_PATCH_BIT1 mask */
+#define MSDC_PB1_WRDAT_CRC_TACNTR GENMASK(2, 0) /* RW */
+#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */
#define MSDC_PB1_BUSY_CHECK_SEL BIT(7) /* RW */
#define MSDC_PATCH_BIT1_STOP_DLY GENMASK(11, 8) /* RW */
-
+#define MSDC_PB1_DDR_CMD_FIX_SEL BIT(14) /* RW */
+#define MSDC_PB1_SINGLE_BURST BIT(16) /* RW */
+#define MSDC_PB1_RSVD20 GENMASK(18, 17) /* RW */
+#define MSDC_PB1_AUTO_SYNCST_CLR BIT(19) /* RW */
+#define MSDC_PB1_MARK_POP_WATER BIT(20) /* RW */
+#define MSDC_PB1_LP_DCM_EN BIT(21) /* RW */
+#define MSDC_PB1_RSVD3 BIT(22) /* RW */
+#define MSDC_PB1_AHB_GDMA_HCLK BIT(23) /* RW */
+#define MSDC_PB1_MSDC_CLK_ENFEAT GENMASK(31, 24) /* RW */
+
+/* MSDC_PATCH_BIT2 mask */
#define MSDC_PATCH_BIT2_CFGRESP BIT(15) /* RW */
#define MSDC_PATCH_BIT2_CFGCRCSTS BIT(28) /* RW */
#define MSDC_PB2_SUPPORT_64G BIT(1) /* RW */
@@ -291,7 +307,10 @@
/* EMMC50_CFG1 mask */
#define EMMC50_CFG1_DS_CFG BIT(28) /* RW */
-#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */
+/* EMMC50_CFG2 mask */
+#define EMMC50_CFG2_AXI_SET_LEN GENMASK(27, 24) /* RW */
+
+#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */
#define SDC_FIFO_CFG_WRVALIDSEL BIT(24) /* RW */
#define SDC_FIFO_CFG_RDVALIDSEL BIT(25) /* RW */
@@ -416,6 +435,7 @@ struct mtk_mmc_compatible {
u8 clk_div_bits;
bool recheck_sdio_irq;
bool hs400_tune; /* only used for MT8173 */
+ bool needs_top_base;
u32 pad_tune_reg;
bool async_fifo;
bool data_tune;
@@ -589,6 +609,7 @@ static const struct mtk_mmc_compatible mt7986_compat = {
.clk_div_bits = 12,
.recheck_sdio_irq = true,
.hs400_tune = false,
+ .needs_top_base = true,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
@@ -629,6 +650,7 @@ static const struct mtk_mmc_compatible mt8183_compat = {
.clk_div_bits = 12,
.recheck_sdio_irq = false,
.hs400_tune = false,
+ .needs_top_base = true,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
@@ -655,6 +677,7 @@ static const struct mtk_mmc_compatible mt8196_compat = {
.clk_div_bits = 12,
.recheck_sdio_irq = false,
.hs400_tune = false,
+ .needs_top_base = true,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
@@ -823,12 +846,18 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
static void msdc_prepare_data(struct msdc_host *host, struct mmc_data *data)
{
if (!(data->host_cookie & MSDC_PREPARE_FLAG)) {
- data->host_cookie |= MSDC_PREPARE_FLAG;
data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len,
mmc_get_dma_dir(data));
+ if (data->sg_count)
+ data->host_cookie |= MSDC_PREPARE_FLAG;
}
}
+static bool msdc_data_prepared(struct mmc_data *data)
+{
+ return data->host_cookie & MSDC_PREPARE_FLAG;
+}
+
static void msdc_unprepare_data(struct msdc_host *host, struct mmc_data *data)
{
if (data->host_cookie & MSDC_ASYNC_FLAG)
@@ -923,15 +952,15 @@ static int msdc_ungate_clock(struct msdc_host *host)
static void msdc_new_tx_setting(struct msdc_host *host)
{
+ u32 val;
+
if (!host->top_base)
return;
- sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
- TEST_LOOP_DSCLK_MUX_SEL);
- sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
- TEST_LOOP_LATCH_MUX_SEL);
- sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL,
- TEST_HS400_CMD_LOOP_MUX_SEL);
+ val = readl(host->top_base + LOOP_TEST_CONTROL);
+ val |= TEST_LOOP_DSCLK_MUX_SEL;
+ val |= TEST_LOOP_LATCH_MUX_SEL;
+ val &= ~TEST_HS400_CMD_LOOP_MUX_SEL;
switch (host->timing) {
case MMC_TIMING_LEGACY:
@@ -941,19 +970,18 @@ static void msdc_new_tx_setting(struct msdc_host *host)
case MMC_TIMING_UHS_SDR25:
case MMC_TIMING_UHS_DDR50:
case MMC_TIMING_MMC_DDR52:
- sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL,
- LOOP_EN_SEL_CLK);
+ val &= ~LOOP_EN_SEL_CLK;
break;
case MMC_TIMING_UHS_SDR50:
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200:
case MMC_TIMING_MMC_HS400:
- sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
- LOOP_EN_SEL_CLK);
+ val |= LOOP_EN_SEL_CLK;
break;
default:
break;
}
+ writel(val, host->top_base + LOOP_TEST_CONTROL);
}
static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
@@ -1099,11 +1127,12 @@ static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
u32 resp;
switch (mmc_resp_type(cmd)) {
- /* Actually, R1, R5, R6, R7 are the same */
+ /* Actually, R1, R5, R6, R7 are the same */
case MMC_RSP_R1:
resp = 0x1;
break;
case MMC_RSP_R1B:
+ case MMC_RSP_R1B_NO_CRC:
resp = 0x7;
break;
case MMC_RSP_R2:
@@ -1353,7 +1382,8 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
* CRC error.
*/
msdc_reset_hw(host);
- if (events & MSDC_INT_RSPCRCERR) {
+ if (events & MSDC_INT_RSPCRCERR &&
+ mmc_resp_type(cmd) != MMC_RSP_R1B_NO_CRC) {
cmd->error = -EILSEQ;
host->error |= REQ_CMD_EIO;
} else if (events & MSDC_INT_CMDTMO) {
@@ -1459,8 +1489,19 @@ static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
WARN_ON(!host->hsq_en && host->mrq);
host->mrq = mrq;
- if (mrq->data)
+ if (mrq->data) {
msdc_prepare_data(host, mrq->data);
+ if (!msdc_data_prepared(mrq->data)) {
+ host->mrq = NULL;
+ /*
+ * Failed to prepare DMA area, fail fast before
+ * starting any commands.
+ */
+ mrq->cmd->error = -ENOSPC;
+ mmc_request_done(mmc_from_priv(host), mrq);
+ return;
+ }
+ }
/* if SBC is required, we have HW option and SW option.
* if HW option is enabled, and SBC does not have "special" flags,
@@ -1810,7 +1851,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
static void msdc_init_hw(struct msdc_host *host)
{
- u32 val;
+ u32 val, pb1_val, pb2_val;
u32 tune_reg = host->dev_comp->pad_tune_reg;
struct mmc_host *mmc = mmc_from_priv(host);
@@ -1863,71 +1904,115 @@ static void msdc_init_hw(struct msdc_host *host)
}
writel(0, host->base + MSDC_IOCON);
sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
- writel(0x403c0046, host->base + MSDC_PATCH_BIT);
- sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
- writel(0xffff4089, host->base + MSDC_PATCH_BIT1);
- sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+
+ /*
+ * Patch bit 0 and 1 are completely rewritten, but for patch bit 2
+ * defaults are retained and, if necessary, only some bits are fixed
+ * up: read the PB2 register here for later usage in this function.
+ */
+ pb2_val = readl(host->base + MSDC_PATCH_BIT2);
+
+ /* Enable odd number support for 8-bit data bus */
+ val = MSDC_PATCH_BIT_ODDSUPP;
+
+ /* Disable SD command register write monitor */
+ val |= MSDC_PATCH_BIT_DIS_WRMON;
+
+ /* Issue transfer done interrupt after GPD update */
+ val |= MSDC_PATCH_BIT_DESCUP_SEL;
+
+ /* Extend R1B busy detection delay (in clock cycles) */
+ val |= FIELD_PREP(MSDC_PATCH_BIT_BUSYDLY, 15);
+
+ /* Enable CRC phase timeout during data write operation */
+ val |= MSDC_PATCH_BIT_DECRCTMO;
+
+ /* Set CKGEN delay to one stage */
+ val |= FIELD_PREP(MSDC_CKGEN_MSDC_DLY_SEL, 1);
+
+ /* First MSDC_PATCH_BIT setup is done: pull the trigger! */
+ writel(val, host->base + MSDC_PATCH_BIT);
+
+ /* Set wr data, crc status, cmd response turnaround period for UHS104 */
+ pb1_val = FIELD_PREP(MSDC_PB1_WRDAT_CRC_TACNTR, 1);
+ pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_CMDTA, 1);
+ pb1_val |= MSDC_PB1_DDR_CMD_FIX_SEL;
+
+ /* Support 'single' burst type only when AXI_LEN is 0 */
+ sdr_get_field(host->base + EMMC50_CFG2, EMMC50_CFG2_AXI_SET_LEN, &val);
+ if (!val)
+ pb1_val |= MSDC_PB1_SINGLE_BURST;
+
+ /* Set auto sync state clear, block gap stop clk */
+ pb1_val |= MSDC_PB1_RSVD20 | MSDC_PB1_AUTO_SYNCST_CLR | MSDC_PB1_MARK_POP_WATER;
+
+ /* Set low power DCM, use HCLK for GDMA, use MSDC CLK for everything else */
+ pb1_val |= MSDC_PB1_LP_DCM_EN | MSDC_PB1_RSVD3 |
+ MSDC_PB1_AHB_GDMA_HCLK | MSDC_PB1_MSDC_CLK_ENFEAT;
+
+ /* If needed, enable R1b command busy check at controller init time */
+ if (!host->dev_comp->busy_check)
+ pb1_val |= MSDC_PB1_BUSY_CHECK_SEL;
if (host->dev_comp->stop_clk_fix) {
if (host->dev_comp->stop_dly_sel)
- sdr_set_field(host->base + MSDC_PATCH_BIT1,
- MSDC_PATCH_BIT1_STOP_DLY,
- host->dev_comp->stop_dly_sel);
+ pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_STOP_DLY,
+ host->dev_comp->stop_dly_sel);
- if (host->dev_comp->pop_en_cnt)
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_POP_EN_CNT,
- host->dev_comp->pop_en_cnt);
+ if (host->dev_comp->pop_en_cnt) {
+ pb2_val &= ~MSDC_PB2_POP_EN_CNT;
+ pb2_val |= FIELD_PREP(MSDC_PB2_POP_EN_CNT,
+ host->dev_comp->pop_en_cnt);
+ }
- sdr_clr_bits(host->base + SDC_FIFO_CFG,
- SDC_FIFO_CFG_WRVALIDSEL);
- sdr_clr_bits(host->base + SDC_FIFO_CFG,
- SDC_FIFO_CFG_RDVALIDSEL);
+ sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_WRVALIDSEL);
+ sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_RDVALIDSEL);
}
- if (host->dev_comp->busy_check)
- sdr_clr_bits(host->base + MSDC_PATCH_BIT1, BIT(7));
-
if (host->dev_comp->async_fifo) {
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_RESPWAIT, 3);
- if (host->dev_comp->enhance_rx) {
- if (host->top_base)
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- SDC_RX_ENH_EN);
- else
- sdr_set_bits(host->base + SDC_ADV_CFG0,
- SDC_RX_ENHANCE_EN);
+ /* Set CMD response timeout multiplier to 65 + (16 * 3) cycles */
+ pb2_val &= ~MSDC_PB2_RESPWAIT;
+ pb2_val |= FIELD_PREP(MSDC_PB2_RESPWAIT, 3);
+
+ /* eMMC4.5: Select async FIFO path for CMD resp and CRC status */
+ pb2_val &= ~MSDC_PATCH_BIT2_CFGRESP;
+ pb2_val |= MSDC_PATCH_BIT2_CFGCRCSTS;
+
+ if (!host->dev_comp->enhance_rx) {
+ /* eMMC4.5: Delay 2T for CMD resp and CRC status EN signals */
+ pb2_val &= ~(MSDC_PB2_RESPSTSENSEL | MSDC_PB2_CRCSTSENSEL);
+ pb2_val |= FIELD_PREP(MSDC_PB2_RESPSTSENSEL, 2);
+ pb2_val |= FIELD_PREP(MSDC_PB2_CRCSTSENSEL, 2);
+ } else if (host->top_base) {
+ sdr_set_bits(host->top_base + EMMC_TOP_CONTROL, SDC_RX_ENH_EN);
} else {
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_RESPSTSENSEL, 2);
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_CRCSTSENSEL, 2);
+ sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_RX_ENHANCE_EN);
}
- /* use async fifo, then no need tune internal delay */
- sdr_clr_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PATCH_BIT2_CFGRESP);
- sdr_set_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PATCH_BIT2_CFGCRCSTS);
}
if (host->dev_comp->support_64g)
- sdr_set_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_SUPPORT_64G);
+ pb2_val |= MSDC_PB2_SUPPORT_64G;
+
+ /* Patch Bit 1/2 setup is done: pull the trigger! */
+ writel(pb1_val, host->base + MSDC_PATCH_BIT1);
+ writel(pb2_val, host->base + MSDC_PATCH_BIT2);
+ sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+
if (host->dev_comp->data_tune) {
if (host->top_base) {
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY_SEL);
- sdr_clr_bits(host->top_base + EMMC_TOP_CONTROL,
- DATA_K_VALUE_SEL);
- sdr_set_bits(host->top_base + EMMC_TOP_CMD,
- PAD_CMD_RD_RXDLY_SEL);
+ u32 top_ctl_val = readl(host->top_base + EMMC_TOP_CONTROL);
+ u32 top_cmd_val = readl(host->top_base + EMMC_TOP_CMD);
+
+ top_cmd_val |= PAD_CMD_RD_RXDLY_SEL;
+ top_ctl_val |= PAD_DAT_RD_RXDLY_SEL;
+ top_ctl_val &= ~DATA_K_VALUE_SEL;
if (host->tuning_step > PAD_DELAY_HALF) {
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY2_SEL);
- sdr_set_bits(host->top_base + EMMC_TOP_CMD,
- PAD_CMD_RD_RXDLY2_SEL);
+ top_cmd_val |= PAD_CMD_RD_RXDLY2_SEL;
+ top_ctl_val |= PAD_DAT_RD_RXDLY2_SEL;
}
+
+ writel(top_ctl_val, host->top_base + EMMC_TOP_CONTROL);
+ writel(top_cmd_val, host->top_base + EMMC_TOP_CMD);
} else {
sdr_set_bits(host->base + tune_reg,
MSDC_PAD_TUNE_RD_SEL |
@@ -2137,15 +2222,17 @@ static inline void msdc_set_cmd_delay(struct msdc_host *host, u32 value)
u32 tune_reg = host->dev_comp->pad_tune_reg;
if (host->top_base) {
+ u32 regval = readl(host->top_base + EMMC_TOP_CMD);
+
+ regval &= ~(PAD_CMD_RXDLY | PAD_CMD_RXDLY2);
+
if (value < PAD_DELAY_HALF) {
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY, value);
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2, 0);
+ regval |= FIELD_PREP(PAD_CMD_RXDLY, value);
} else {
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY,
- PAD_DELAY_HALF - 1);
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2,
- value - PAD_DELAY_HALF);
+ regval |= FIELD_PREP(PAD_CMD_RXDLY, PAD_DELAY_HALF - 1);
+ regval |= FIELD_PREP(PAD_CMD_RXDLY2, value - PAD_DELAY_HALF);
}
+ writel(regval, host->top_base + EMMC_TOP_CMD);
} else {
if (value < PAD_DELAY_HALF) {
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY, value);
@@ -2165,17 +2252,18 @@ static inline void msdc_set_data_delay(struct msdc_host *host, u32 value)
u32 tune_reg = host->dev_comp->pad_tune_reg;
if (host->top_base) {
+ u32 regval = readl(host->top_base + EMMC_TOP_CONTROL);
+
+ regval &= ~(PAD_DAT_RD_RXDLY | PAD_DAT_RD_RXDLY2);
+
if (value < PAD_DELAY_HALF) {
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY, value);
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY2, 0);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, value);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value);
} else {
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1);
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF);
}
+ writel(regval, host->top_base + EMMC_TOP_CONTROL);
} else {
if (value < PAD_DELAY_HALF) {
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_DATRRDLY, value);
@@ -2894,9 +2982,13 @@ static int msdc_drv_probe(struct platform_device *pdev)
if (IS_ERR(host->base))
return PTR_ERR(host->base);
- host->top_base = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(host->top_base))
- host->top_base = NULL;
+ host->dev_comp = of_device_get_match_data(&pdev->dev);
+
+ if (host->dev_comp->needs_top_base) {
+ host->top_base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(host->top_base))
+ return PTR_ERR(host->top_base);
+ }
ret = mmc_regulator_get_supply(mmc);
if (ret)
@@ -2958,7 +3050,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
msdc_of_property_parse(pdev, host);
host->dev = &pdev->dev;
- host->dev_comp = of_device_get_match_data(&pdev->dev);
host->src_clk_freq = clk_get_rate(host->src_clk);
/* Set host parameters to mmc */
mmc->ops = &mt_msdc_ops;
@@ -2968,7 +3059,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095);
if (!(mmc->caps & MMC_CAP_NONREMOVABLE) &&
- !mmc_can_gpio_cd(mmc) &&
+ !mmc_host_can_gpio_cd(mmc) &&
host->dev_comp->use_internal_cd) {
/*
* Is removable but no GPIO declared, so
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index b92f3ba38663..101f36de7b63 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -464,7 +464,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
struct mmc_command *cmd = mrq->cmd;
u32 err_status = 0;
- del_timer(&host->timer);
+ timer_delete(&host->timer);
host->mrq = NULL;
host->intr_en &= MVSD_NOR_CARD_INT;
@@ -509,7 +509,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
static void mvsd_timeout_timer(struct timer_list *t)
{
- struct mvsd_host *host = from_timer(host, t, timer);
+ struct mvsd_host *host = timer_container_of(host, t, timer);
void __iomem *iobase = host->base;
struct mmc_request *mrq;
unsigned long flags;
@@ -803,7 +803,7 @@ static void mvsd_remove(struct platform_device *pdev)
struct mvsd_host *host = mmc_priv(mmc);
mmc_remove_host(mmc);
- del_timer_sync(&host->timer);
+ timer_delete_sync(&host->timer);
mvsd_power_down(host);
if (!IS_ERR(host->clk))
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index e7a286c3216f..e588e24256cc 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -352,7 +352,7 @@ static void mxcmci_dma_callback(void *data)
struct mxcmci_host *host = data;
u32 stat;
- del_timer(&host->watchdog);
+ timer_delete(&host->watchdog);
stat = mxcmci_readl(host, MMC_REG_STATUS);
@@ -737,7 +737,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
mxcmci_cmd_done(host, stat);
if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) {
- del_timer(&host->watchdog);
+ timer_delete(&host->watchdog);
mxcmci_data_done(host, stat);
}
@@ -955,7 +955,7 @@ static bool filter(struct dma_chan *chan, void *param)
static void mxcmci_watchdog(struct timer_list *t)
{
- struct mxcmci_host *host = from_timer(host, t, watchdog);
+ struct mxcmci_host *host = timer_container_of(host, t, watchdog);
struct mmc_request *req = host->req;
unsigned int stat = mxcmci_readl(host, MMC_REG_STATUS);
@@ -995,7 +995,7 @@ static int mxcmci_probe(struct platform_device *pdev)
struct mxcmci_host *host;
struct resource *res;
int ret = 0, irq;
- bool dat3_card_detect = false;
+ bool dat3_card_detect;
dma_cap_mask_t mask;
struct imxmmc_platform_data *pdata = pdev->dev.platform_data;
@@ -1048,9 +1048,9 @@ static int mxcmci_probe(struct platform_device *pdev)
if (pdata)
dat3_card_detect = pdata->dat3_card_detect;
- else if (mmc_card_is_removable(mmc)
- && !of_property_read_bool(pdev->dev.of_node, "cd-gpios"))
- dat3_card_detect = true;
+ else
+ dat3_card_detect = mmc_card_is_removable(mmc) &&
+ !of_property_present(pdev->dev.of_node, "cd-gpios");
ret = mmc_regulator_get_supply(mmc);
if (ret)
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 62252ad4e20d..c2be0f04439d 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -214,7 +214,7 @@ static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed)
host->mmc = slot->mmc;
spin_unlock_irqrestore(&host->slot_lock, flags);
no_claim:
- del_timer(&host->clk_timer);
+ timer_delete(&host->clk_timer);
if (host->current_slot != slot || !claimed)
mmc_omap_fclk_offdelay(host->current_slot);
@@ -273,7 +273,7 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
/* Keeps clock running for at least 8 cycles on valid freq */
mod_timer(&host->clk_timer, jiffies + HZ/10);
else {
- del_timer(&host->clk_timer);
+ timer_delete(&host->clk_timer);
mmc_omap_fclk_offdelay(slot);
mmc_omap_fclk_enable(host, 0);
}
@@ -564,7 +564,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
{
host->cmd = NULL;
- del_timer(&host->cmd_abort_timer);
+ timer_delete(&host->cmd_abort_timer);
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
@@ -639,7 +639,8 @@ static void mmc_omap_abort_command(struct work_struct *work)
static void
mmc_omap_cmd_timer(struct timer_list *t)
{
- struct mmc_omap_host *host = from_timer(host, t, cmd_abort_timer);
+ struct mmc_omap_host *host = timer_container_of(host, t,
+ cmd_abort_timer);
unsigned long flags;
spin_lock_irqsave(&host->slot_lock, flags);
@@ -655,7 +656,7 @@ mmc_omap_cmd_timer(struct timer_list *t)
static void
mmc_omap_clk_timer(struct timer_list *t)
{
- struct mmc_omap_host *host = from_timer(host, t, clk_timer);
+ struct mmc_omap_host *host = timer_container_of(host, t, clk_timer);
mmc_omap_fclk_enable(host, 0);
}
@@ -836,7 +837,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
}
if (cmd_error && host->data) {
- del_timer(&host->cmd_abort_timer);
+ timer_delete(&host->cmd_abort_timer);
host->abort = 1;
OMAP_MMC_WRITE(host, IE, 0);
disable_irq_nosync(host->irq);
@@ -879,7 +880,7 @@ void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
static void mmc_omap_cover_timer(struct timer_list *t)
{
- struct mmc_omap_slot *slot = from_timer(slot, t, cover_timer);
+ struct mmc_omap_slot *slot = timer_container_of(slot, t, cover_timer);
queue_work(system_bh_wq, &slot->cover_bh_work);
}
@@ -1272,19 +1273,25 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
/* Check for some optional GPIO controls */
slot->vsd = devm_gpiod_get_index_optional(host->dev, "vsd",
id, GPIOD_OUT_LOW);
- if (IS_ERR(slot->vsd))
- return dev_err_probe(host->dev, PTR_ERR(slot->vsd),
+ if (IS_ERR(slot->vsd)) {
+ r = dev_err_probe(host->dev, PTR_ERR(slot->vsd),
"error looking up VSD GPIO\n");
+ goto err_free_host;
+ }
slot->vio = devm_gpiod_get_index_optional(host->dev, "vio",
id, GPIOD_OUT_LOW);
- if (IS_ERR(slot->vio))
- return dev_err_probe(host->dev, PTR_ERR(slot->vio),
+ if (IS_ERR(slot->vio)) {
+ r = dev_err_probe(host->dev, PTR_ERR(slot->vio),
"error looking up VIO GPIO\n");
+ goto err_free_host;
+ }
slot->cover = devm_gpiod_get_index_optional(host->dev, "cover",
id, GPIOD_IN);
- if (IS_ERR(slot->cover))
- return dev_err_probe(host->dev, PTR_ERR(slot->cover),
+ if (IS_ERR(slot->cover)) {
+ r = dev_err_probe(host->dev, PTR_ERR(slot->cover),
"error looking up cover switch GPIO\n");
+ goto err_free_host;
+ }
host->slots[id] = slot;
@@ -1344,6 +1351,7 @@ err_remove_slot_name:
device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
err_remove_host:
mmc_remove_host(mmc);
+err_free_host:
mmc_free_host(mmc);
return r;
}
@@ -1358,7 +1366,7 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
cancel_work_sync(&slot->cover_bh_work);
- del_timer_sync(&slot->cover_timer);
+ timer_delete_sync(&slot->cover_timer);
flush_workqueue(slot->host->mmc_omap_wq);
mmc_remove_host(mmc);
diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
index f12a87442338..291ddb4ad9be 100644
--- a/drivers/mmc/host/renesas_sdhi.h
+++ b/drivers/mmc/host/renesas_sdhi.h
@@ -95,6 +95,7 @@ struct renesas_sdhi {
struct reset_control *rstc;
struct tmio_mmc_host *host;
+ struct regulator_dev *rdev;
};
#define host_to_priv(host) \
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index f73b84bae0c4..e6fa3ed42560 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -32,6 +32,8 @@
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
#include <linux/reset.h>
#include <linux/sh_dma.h>
#include <linux/slab.h>
@@ -581,12 +583,24 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve)
if (!preserve) {
if (priv->rstc) {
+ u32 sd_status;
+ /*
+ * HW reset might have toggled the regulator state in
+ * HW which regulator core might be unaware of so save
+ * and restore the regulator state during HW reset.
+ */
+ if (priv->rdev)
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+
reset_control_reset(priv->rstc);
/* Unknown why but without polling reset status, it will hang */
read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100,
false, priv->rstc);
/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
+ if (priv->rdev)
+ sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
priv->needs_adjust_hs400 = false;
renesas_sdhi_set_clock(host, host->clk_cache);
@@ -904,6 +918,102 @@ static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
renesas_sdhi_sdbuf_width(host, enable ? width : 16);
}
+static const unsigned int renesas_sdhi_vqmmc_voltages[] = {
+ 3300000, 1800000
+};
+
+static int renesas_sdhi_regulator_disable(struct regulator_dev *rdev)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+ sd_status &= ~SD_STATUS_PWEN;
+ sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+ return 0;
+}
+
+static int renesas_sdhi_regulator_enable(struct regulator_dev *rdev)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+ sd_status |= SD_STATUS_PWEN;
+ sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+ return 0;
+}
+
+static int renesas_sdhi_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+
+ return (sd_status & SD_STATUS_PWEN) ? 1 : 0;
+}
+
+static int renesas_sdhi_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+
+ return (sd_status & SD_STATUS_IOVS) ? 1800000 : 3300000;
+}
+
+static int renesas_sdhi_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV,
+ unsigned int *selector)
+{
+ struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
+ u32 sd_status;
+
+ sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
+ if (min_uV >= 1700000 && max_uV <= 1950000) {
+ sd_status |= SD_STATUS_IOVS;
+ *selector = 1;
+ } else {
+ sd_status &= ~SD_STATUS_IOVS;
+ *selector = 0;
+ }
+ sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
+
+ return 0;
+}
+
+static int renesas_sdhi_regulator_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ if (selector >= ARRAY_SIZE(renesas_sdhi_vqmmc_voltages))
+ return -EINVAL;
+
+ return renesas_sdhi_vqmmc_voltages[selector];
+}
+
+static const struct regulator_ops renesas_sdhi_regulator_voltage_ops = {
+ .enable = renesas_sdhi_regulator_enable,
+ .disable = renesas_sdhi_regulator_disable,
+ .is_enabled = renesas_sdhi_regulator_is_enabled,
+ .list_voltage = renesas_sdhi_regulator_list_voltage,
+ .get_voltage = renesas_sdhi_regulator_get_voltage,
+ .set_voltage = renesas_sdhi_regulator_set_voltage,
+};
+
+static const struct regulator_desc renesas_sdhi_vqmmc_regulator = {
+ .name = "sdhi-vqmmc-regulator",
+ .of_match = of_match_ptr("vqmmc-regulator"),
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .ops = &renesas_sdhi_regulator_voltage_ops,
+ .volt_table = renesas_sdhi_vqmmc_voltages,
+ .n_voltages = ARRAY_SIZE(renesas_sdhi_vqmmc_voltages),
+};
+
int renesas_sdhi_probe(struct platform_device *pdev,
const struct tmio_mmc_dma_ops *dma_ops,
const struct renesas_sdhi_of_data *of_data,
@@ -911,7 +1021,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
{
struct tmio_mmc_data *mmd = pdev->dev.platform_data;
struct tmio_mmc_data *mmc_data;
+ struct regulator_config rcfg = { .dev = &pdev->dev, };
+ struct regulator_dev *rdev;
struct renesas_sdhi_dma *dma_priv;
+ struct device *dev = &pdev->dev;
struct tmio_mmc_host *host;
struct renesas_sdhi *priv;
int num_irqs, irq, ret, i;
@@ -999,7 +1112,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
/* For some SoC, we disable internal WP. GPIO may override this */
- if (mmc_can_gpio_ro(host->mmc))
+ if (mmc_host_can_gpio_ro(host->mmc))
mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT;
/* SDR speeds are only available on Gen2+ */
@@ -1053,6 +1166,19 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (ret)
goto efree;
+ rcfg.of_node = of_get_available_child_by_name(dev->of_node, "vqmmc-regulator");
+ if (rcfg.of_node) {
+ rcfg.driver_data = priv->host;
+ rdev = devm_regulator_register(dev, &renesas_sdhi_vqmmc_regulator, &rcfg);
+ of_node_put(rcfg.of_node);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "regulator register failed err=%ld", PTR_ERR(rdev));
+ ret = PTR_ERR(rdev);
+ goto edisclk;
+ }
+ priv->rdev = rdev;
+ }
+
ver = sd_ctrl_read16(host, CTL_VERSION);
/* GEN2_SDR104 is first known SDHI to use 32bit block count */
if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX)
@@ -1109,29 +1235,24 @@ int renesas_sdhi_probe(struct platform_device *pdev,
sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, host->sdcard_irq_mask_all);
- num_irqs = platform_irq_count(pdev);
- if (num_irqs < 0) {
- ret = num_irqs;
- goto eirq;
- }
-
/* There must be at least one IRQ source */
- if (!num_irqs) {
- ret = -ENXIO;
- goto eirq;
+ num_irqs = platform_irq_count(pdev);
+ if (num_irqs <= 0) {
+ ret = num_irqs ?: -ENOENT;
+ goto edisclk;
}
for (i = 0; i < num_irqs; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0) {
ret = irq;
- goto eirq;
+ goto edisclk;
}
ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, 0,
dev_name(&pdev->dev), host);
if (ret)
- goto eirq;
+ goto edisclk;
}
ret = tmio_mmc_host_probe(host);
@@ -1143,8 +1264,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
return ret;
-eirq:
- tmio_mmc_host_remove(host);
edisclk:
renesas_sdhi_clk_disable(host);
efree:
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index 48d3b0aae5a0..0c6eb60a95fd 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -115,8 +115,6 @@ static int sd_response_type(struct mmc_command *cmd)
return SD_RSP_TYPE_R0;
case MMC_RSP_R1:
return SD_RSP_TYPE_R1;
- case MMC_RSP_R1_NO_CRC:
- return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
case MMC_RSP_R1B:
return SD_RSP_TYPE_R1b;
case MMC_RSP_R2:
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 107c78df53cf..8c35cb85a9c0 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -313,9 +313,6 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host,
case MMC_RSP_R1:
rsp_type = SD_RSP_TYPE_R1;
break;
- case MMC_RSP_R1_NO_CRC:
- rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
- break;
case MMC_RSP_R1B:
rsp_type = SD_RSP_TYPE_R1b;
break;
@@ -1032,9 +1029,7 @@ static int sd_set_power_mode(struct rtsx_usb_sdmmc *host,
err = sd_power_on(host);
}
- if (!err)
- host->power_mode = power_mode;
-
+ host->power_mode = power_mode;
return err;
}
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index d1ce9193ece9..e6c5c82f64fa 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -822,8 +822,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
struct acpi_device *device;
struct sdhci_acpi_host *c;
struct sdhci_host *host;
- struct resource *iomem;
- resource_size_t len;
size_t priv_size;
int quirks = 0;
int err;
@@ -844,17 +842,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (sdhci_acpi_byt_defer(dev))
return -EPROBE_DEFER;
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iomem)
- return -ENOMEM;
-
- len = resource_size(iomem);
- if (len < 0x100)
- dev_err(dev, "Invalid iomem size!\n");
-
- if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev)))
- return -ENOMEM;
-
priv_size = slot ? slot->priv_size : 0;
host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host) + priv_size);
if (IS_ERR(host))
@@ -876,10 +863,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
goto err_free;
}
- host->ioaddr = devm_ioremap(dev, iomem->start,
- resource_size(iomem));
- if (host->ioaddr == NULL) {
- err = -ENOMEM;
+ host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(host->ioaddr)) {
+ err = PTR_ERR(host->ioaddr);
goto err_free;
}
diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
index 0ef4d578ade8..48cdcba0f39c 100644
--- a/drivers/mmc/host/sdhci-brcmstb.c
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -503,8 +503,15 @@ static int sdhci_brcmstb_suspend(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ int ret;
clk_disable_unprepare(priv->base_clk);
+ if (host->mmc->caps2 & MMC_CAP2_CQE) {
+ ret = cqhci_suspend(host->mmc);
+ if (ret)
+ return ret;
+ }
+
return sdhci_pltfm_suspend(dev);
}
@@ -529,6 +536,9 @@ static int sdhci_brcmstb_resume(struct device *dev)
ret = clk_set_rate(priv->base_clk, priv->base_freq_hz);
}
+ if (host->mmc->caps2 & MMC_CAP2_CQE)
+ ret = cqhci_resume(host->mmc);
+
return ret;
}
#endif
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index e23177ea9d91..05dd2b563c02 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -31,7 +31,9 @@
#include "cqhci.h"
#define ESDHC_SYS_CTRL_DTOCV_MASK GENMASK(19, 16)
+#define ESDHC_SYS_CTRL_RST_FIFO BIT(22)
#define ESDHC_SYS_CTRL_IPP_RST_N BIT(23)
+#define ESDHC_SYS_CTRL_RESET_TUNING BIT(28)
#define ESDHC_CTRL_D3CD 0x08
#define ESDHC_BURST_LEN_EN_INCR (1 << 27)
/* VENDOR SPEC register */
@@ -81,7 +83,11 @@
#define ESDHC_TUNE_CTRL_STEP 1
#define ESDHC_TUNE_CTRL_MIN 0
#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
-
+#define ESDHC_TUNE_CTRL_STATUS_TAP_SEL_MASK GENMASK(30, 16)
+#define ESDHC_TUNE_CTRL_STATUS_TAP_SEL_PRE_MASK GENMASK(30, 24)
+#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK GENMASK(14, 8)
+#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK GENMASK(7, 4)
+#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK GENMASK(3, 0)
/* strobe dll register */
#define ESDHC_STROBE_DLL_CTRL 0x70
#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
@@ -104,6 +110,7 @@
#define ESDHC_TUNING_CTRL 0xcc
#define ESDHC_STD_TUNING_EN (1 << 24)
+#define ESDHC_TUNING_WINDOW_MASK GENMASK(22, 20)
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
#define ESDHC_TUNING_START_TAP_MASK 0x7f
@@ -205,6 +212,8 @@
/* The IP does not have GPIO CD wake capabilities */
#define ESDHC_FLAG_SKIP_CD_WAKE BIT(18)
+#define ESDHC_AUTO_TUNING_WINDOW 3
+
enum wp_types {
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
ESDHC_WP_CONTROLLER, /* mmc controller internal WP */
@@ -235,6 +244,8 @@ struct esdhc_platform_data {
unsigned int tuning_step; /* The delay cell steps in tuning procedure */
unsigned int tuning_start_tap; /* The start delay cell point in tuning procedure */
unsigned int strobe_dll_delay_target; /* The delay cell for strobe pad (read clock) */
+ unsigned int saved_tuning_delay_cell; /* save the value of tuning delay cell */
+ unsigned int saved_auto_tuning_window; /* save the auto tuning window width */
};
struct esdhc_soc_data {
@@ -264,35 +275,35 @@ static const struct esdhc_soc_data usdhc_imx6q_data = {
};
static const struct esdhc_soc_data usdhc_imx6sl_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
| ESDHC_FLAG_HS200
| ESDHC_FLAG_BROKEN_AUTO_CMD23,
};
static const struct esdhc_soc_data usdhc_imx6sll_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
};
static const struct esdhc_soc_data usdhc_imx6sx_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_STATE_LOST_IN_LPMODE
| ESDHC_FLAG_BROKEN_AUTO_CMD23,
};
static const struct esdhc_soc_data usdhc_imx6ull_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_ERR010450
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
};
static const struct esdhc_soc_data usdhc_imx7d_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400
| ESDHC_FLAG_STATE_LOST_IN_LPMODE
@@ -308,7 +319,7 @@ static struct esdhc_soc_data usdhc_s32g2_data = {
};
static struct esdhc_soc_data usdhc_imx7ulp_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
@@ -321,7 +332,7 @@ static struct esdhc_soc_data usdhc_imxrt1050_data = {
};
static struct esdhc_soc_data usdhc_imx8qxp_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
| ESDHC_FLAG_STATE_LOST_IN_LPMODE
@@ -330,7 +341,7 @@ static struct esdhc_soc_data usdhc_imx8qxp_data = {
};
static struct esdhc_soc_data usdhc_imx8mm_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
@@ -870,6 +881,11 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
esdhc_clrset_le(host, mask, new_val, reg);
return;
+ case SDHCI_TIMEOUT_CONTROL:
+ esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
+ FIELD_PREP(ESDHC_SYS_CTRL_DTOCV_MASK, val),
+ ESDHC_SYSTEM_CONTROL);
+ return;
case SDHCI_SOFTWARE_RESET:
if (val & SDHCI_RESET_DATA)
new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL);
@@ -1057,7 +1073,7 @@ static void esdhc_reset_tuning(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 ctrl;
+ u32 ctrl, tuning_ctrl, sys_ctrl;
int ret;
/* Reset the tuning circuit */
@@ -1071,6 +1087,21 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
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);
+ /*
+ * enable the std tuning just in case it cleared in
+ * sdhc_esdhc_tuning_restore.
+ */
+ tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
+ if (!(tuning_ctrl & ESDHC_STD_TUNING_EN)) {
+ tuning_ctrl |= ESDHC_STD_TUNING_EN;
+ writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);
+ }
+
+ /* set the reset tuning bit */
+ sys_ctrl = readl(host->ioaddr + ESDHC_SYSTEM_CONTROL);
+ sys_ctrl |= ESDHC_SYS_CTRL_RESET_TUNING;
+ writel(sys_ctrl, host->ioaddr + ESDHC_SYSTEM_CONTROL);
+
ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
@@ -1130,7 +1161,7 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
{
- u32 reg;
+ u32 reg, sys_ctrl;
u8 sw_rst;
int ret;
@@ -1149,10 +1180,21 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
ESDHC_MIX_CTRL_FBCLK_SEL;
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
- writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK, val),
+ host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
dev_dbg(mmc_dev(host->mmc),
"tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
+
+ /* set RST_FIFO to reset the async FIFO, and wat it to self-clear */
+ sys_ctrl = readl(host->ioaddr + ESDHC_SYSTEM_CONTROL);
+ sys_ctrl |= ESDHC_SYS_CTRL_RST_FIFO;
+ writel(sys_ctrl, host->ioaddr + ESDHC_SYSTEM_CONTROL);
+ ret = readl_poll_timeout(host->ioaddr + ESDHC_SYSTEM_CONTROL, sys_ctrl,
+ !(sys_ctrl & ESDHC_SYS_CTRL_RST_FIFO), 10, 100);
+ if (ret == -ETIMEDOUT)
+ dev_warn(mmc_dev(host->mmc),
+ "warning! RST_FIFO not clear in 100us\n");
}
static void esdhc_post_tuning(struct sdhci_host *host)
@@ -1172,9 +1214,10 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
{
int min, max, avg, ret;
int win_length, target_min, target_max, target_win_length;
+ u32 clk_tune_ctrl_status, temp;
- min = ESDHC_TUNE_CTRL_MIN;
- max = ESDHC_TUNE_CTRL_MIN;
+ min = target_min = ESDHC_TUNE_CTRL_MIN;
+ max = target_max = ESDHC_TUNE_CTRL_MIN;
target_win_length = 0;
while (max < ESDHC_TUNE_CTRL_MAX) {
/* find the mininum delay first which can pass tuning */
@@ -1211,6 +1254,30 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
/* use average delay to get the best timing */
avg = (target_min + target_max) / 2;
esdhc_prepare_tuning(host, avg);
+
+ /*
+ * adjust the delay according to tuning window, make preparation
+ * for the auto-tuning logic. According to hardware suggest, need
+ * to config the auto tuning window width to 3, to make the auto
+ * tuning logic have enough space to handle the sample point shift
+ * caused by temperature change.
+ */
+ clk_tune_ctrl_status = FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK,
+ avg - ESDHC_AUTO_TUNING_WINDOW) |
+ FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK,
+ ESDHC_AUTO_TUNING_WINDOW) |
+ FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK,
+ ESDHC_AUTO_TUNING_WINDOW);
+
+ writel(clk_tune_ctrl_status, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ ret = readl_poll_timeout(host->ioaddr + ESDHC_TUNE_CTRL_STATUS, temp,
+ clk_tune_ctrl_status ==
+ FIELD_GET(ESDHC_TUNE_CTRL_STATUS_TAP_SEL_MASK, temp),
+ 1, 10);
+ if (ret == -ETIMEDOUT)
+ dev_warn(mmc_dev(host->mmc),
+ "clock tuning control status not set in 10us\n");
+
ret = mmc_send_tuning(host->mmc, opcode, NULL);
esdhc_post_tuning(host);
@@ -1385,17 +1452,6 @@ static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27;
}
-static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
-
- /* use maximum timeout counter */
- esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK,
- esdhc_is_usdhc(imx_data) ? 0xF0000 : 0xE0000,
- ESDHC_SYSTEM_CONTROL);
-}
-
static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
{
int cmd_error = 0;
@@ -1432,7 +1488,6 @@ static struct sdhci_ops sdhci_esdhc_ops = {
.get_min_clock = esdhc_pltfm_get_min_clock,
.get_max_timeout_count = esdhc_get_max_timeout_count,
.get_ro = esdhc_pltfm_get_ro,
- .set_timeout = esdhc_set_timeout,
.set_bus_width = esdhc_pltfm_set_bus_width,
.set_uhs_signaling = esdhc_set_uhs_signaling,
.reset = esdhc_reset,
@@ -1529,6 +1584,16 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
<< ESDHC_TUNING_STEP_SHIFT;
}
+ /*
+ * Config the tuning window to the hardware suggested value 3.
+ * This tuning window is used for auto tuning logic. The default
+ * tuning window is 2, here change to 3 make the window a bit
+ * wider, give auto tuning enough space to handle the sample
+ * point shift cause by temperature change.
+ */
+ tmp &= ~ESDHC_TUNING_WINDOW_MASK;
+ tmp |= FIELD_PREP(ESDHC_TUNING_WINDOW_MASK, ESDHC_AUTO_TUNING_WINDOW);
+
/* Disable the CMD CRC check for tuning, if not, need to
* add some delay after every tuning command, because
* hardware standard tuning logic will directly go to next
@@ -1569,6 +1634,63 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
}
}
+#ifdef CONFIG_PM_SLEEP
+static void sdhc_esdhc_tuning_save(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 reg;
+
+ /*
+ * SD/eMMC do not need this tuning save because it will re-init
+ * after system resume back.
+ * Here save the tuning delay value for SDIO device since it may
+ * keep power during system PM. And for usdhc, only SDR50 and
+ * SDR104 mode for SDIO device need to do tuning, and need to
+ * save/restore.
+ */
+ if (host->timing == MMC_TIMING_UHS_SDR50 ||
+ host->timing == MMC_TIMING_UHS_SDR104) {
+ reg = readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ reg = FIELD_GET(ESDHC_TUNE_CTRL_STATUS_TAP_SEL_PRE_MASK, reg);
+ imx_data->boarddata.saved_tuning_delay_cell = reg;
+ }
+}
+
+static void sdhc_esdhc_tuning_restore(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 reg;
+
+ if (host->timing == MMC_TIMING_UHS_SDR50 ||
+ host->timing == MMC_TIMING_UHS_SDR104) {
+ /*
+ * restore the tuning delay value actually is a
+ * manual tuning method, so clear the standard
+ * tuning enable bit here. Will set back this
+ * ESDHC_STD_TUNING_EN in esdhc_reset_tuning()
+ * when trigger re-tuning.
+ */
+ reg = readl(host->ioaddr + ESDHC_TUNING_CTRL);
+ reg &= ~ESDHC_STD_TUNING_EN;
+ writel(reg, host->ioaddr + ESDHC_TUNING_CTRL);
+
+ reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ reg |= ESDHC_MIX_CTRL_SMPCLK_SEL | ESDHC_MIX_CTRL_FBCLK_SEL;
+ writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
+
+ writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK,
+ imx_data->boarddata.saved_tuning_delay_cell) |
+ FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK,
+ ESDHC_AUTO_TUNING_WINDOW) |
+ FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK,
+ ESDHC_AUTO_TUNING_WINDOW),
+ host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ }
+}
+#endif
+
static void esdhc_cqe_enable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -1648,7 +1770,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
* Retrieving and requesting the actual WP GPIO will happen
* in the call to mmc_of_parse().
*/
- if (of_property_read_bool(np, "wp-gpios"))
+ if (of_property_present(np, "wp-gpios"))
boarddata->wp_type = ESDHC_WP_GPIO;
of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
@@ -1777,6 +1899,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
* to distinguish the card type.
*/
host->mmc_host_ops.init_card = usdhc_init_card;
+
+ host->max_timeout_count = 0xF;
}
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
@@ -1885,11 +2009,14 @@ static int sdhci_esdhc_suspend(struct device *dev)
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
int ret;
- if (host->mmc->caps2 & MMC_CAP2_CQE) {
- ret = cqhci_suspend(host->mmc);
- if (ret)
- return ret;
- }
+ /*
+ * Switch to runtime resume for two reasons:
+ * 1, there is register access (e.g., wakeup control register), so
+ * need to make sure gate on ipg clock.
+ * 2, make sure the pm_runtime_force_resume() in sdhci_esdhc_resume() really
+ * invoke its ->runtime_resume callback (needs_force_resume = 1).
+ */
+ pm_runtime_get_sync(dev);
if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) &&
(host->tuning_mode != SDHCI_TUNING_MODE_1)) {
@@ -1897,43 +2024,77 @@ static int sdhci_esdhc_suspend(struct device *dev)
mmc_retune_needed(host->mmc);
}
- if (host->tuning_mode != SDHCI_TUNING_MODE_3)
- mmc_retune_needed(host->mmc);
-
- ret = sdhci_suspend_host(host);
- if (ret)
- return ret;
-
- ret = pinctrl_pm_select_sleep_state(dev);
- if (ret)
- return ret;
+ /*
+ * For the device need to keep power during system PM, need
+ * to save the tuning delay value just in case the usdhc
+ * lost power during system PM.
+ */
+ if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) &&
+ esdhc_is_usdhc(imx_data))
+ sdhc_esdhc_tuning_save(host);
+
+ if (device_may_wakeup(dev)) {
+ /* The irqs of imx are not shared. It is safe to disable */
+ disable_irq(host->irq);
+ ret = sdhci_enable_irq_wakeups(host);
+ if (!ret)
+ dev_warn(dev, "Failed to enable irq wakeup\n");
+ } else {
+ /*
+ * For the device which works as wakeup source, no need
+ * to change the pinctrl to sleep state.
+ * e.g. For SDIO device, the interrupt share with data pin,
+ * but the pinctrl sleep state may config the data pin to
+ * other function like GPIO function to save power in PM,
+ * which finally block the SDIO wakeup function.
+ */
+ ret = pinctrl_pm_select_sleep_state(dev);
+ if (ret)
+ return ret;
+ }
ret = mmc_gpio_set_cd_wake(host->mmc, true);
+ /*
+ * Make sure invoke runtime_suspend to gate off clock.
+ * uSDHC IP supports in-band SDIO wakeup even without clock.
+ */
+ pm_runtime_force_suspend(dev);
+
return ret;
}
static int sdhci_esdhc_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
int ret;
- ret = pinctrl_pm_select_default_state(dev);
+ pm_runtime_force_resume(dev);
+
+ ret = mmc_gpio_set_cd_wake(host->mmc, false);
if (ret)
return ret;
/* re-initialize hw state in case it's lost in low power mode */
sdhci_esdhc_imx_hwinit(host);
- ret = sdhci_resume_host(host);
- if (ret)
- return ret;
+ if (host->irq_wake_enabled) {
+ sdhci_disable_irq_wakeups(host);
+ enable_irq(host->irq);
+ }
- if (host->mmc->caps2 & MMC_CAP2_CQE)
- ret = cqhci_resume(host->mmc);
+ /*
+ * restore the saved tuning delay value for the device which keep
+ * power during system PM.
+ */
+ if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) &&
+ esdhc_is_usdhc(imx_data))
+ sdhc_esdhc_tuning_restore(host);
- if (!ret)
- ret = mmc_gpio_set_cd_wake(host->mmc, false);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 1fcaaf683d68..bc6ca49652f8 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1564,6 +1564,7 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ struct mmc_host *mmc = host->mmc;
bool done = false;
u32 val = SWITCHABLE_SIGNALING_VOLTAGE;
const struct sdhci_msm_offset *msm_offset =
@@ -1621,6 +1622,12 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
"%s: pwr_irq for req: (%d) timed out\n",
mmc_hostname(host->mmc), req_type);
}
+
+ if ((req_type & REQ_BUS_ON) && mmc->card && !mmc->ops->get_cd(mmc)) {
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
+ host->pwr = 0;
+ }
+
pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc),
__func__, req_type);
}
@@ -1679,6 +1686,13 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
udelay(10);
}
+ if ((irq_status & CORE_PWRCTL_BUS_ON) && mmc->card &&
+ !mmc->ops->get_cd(mmc)) {
+ msm_host_writel(msm_host, CORE_PWRCTL_BUS_FAIL, host,
+ msm_offset->core_pwrctl_ctl);
+ return;
+ }
+
/* Handle BUS ON/OFF*/
if (irq_status & CORE_PWRCTL_BUS_ON) {
pwr_state = REQ_BUS_ON;
@@ -1856,17 +1870,24 @@ out:
#ifdef CONFIG_MMC_CRYPTO
+static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops; /* forward decl */
+
static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
struct cqhci_host *cq_host)
{
struct mmc_host *mmc = msm_host->mmc;
+ struct blk_crypto_profile *profile = &mmc->crypto_profile;
struct device *dev = mmc_dev(mmc);
struct qcom_ice *ice;
+ union cqhci_crypto_capabilities caps;
+ union cqhci_crypto_cap_entry cap;
+ int err;
+ int i;
if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS))
return 0;
- ice = of_qcom_ice_get(dev);
+ ice = devm_of_qcom_ice_get(dev);
if (ice == ERR_PTR(-EOPNOTSUPP)) {
dev_warn(dev, "Disabling inline encryption support\n");
ice = NULL;
@@ -1875,9 +1896,44 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
if (IS_ERR_OR_NULL(ice))
return PTR_ERR_OR_ZERO(ice);
+ if (qcom_ice_get_supported_key_type(ice) != BLK_CRYPTO_KEY_TYPE_RAW) {
+ dev_warn(dev, "Wrapped keys not supported. Disabling inline encryption support.\n");
+ return 0;
+ }
+
msm_host->ice = ice;
- mmc->caps2 |= MMC_CAP2_CRYPTO;
+ /* Initialize the blk_crypto_profile */
+
+ caps.reg_val = cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP));
+
+ /* The number of keyslots supported is (CFGC+1) */
+ err = devm_blk_crypto_profile_init(dev, profile, caps.config_count + 1);
+ if (err)
+ return err;
+
+ profile->ll_ops = sdhci_msm_crypto_ops;
+ profile->max_dun_bytes_supported = 4;
+ profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW;
+ profile->dev = dev;
+
+ /*
+ * Currently this driver only supports AES-256-XTS. All known versions
+ * of ICE support it, but to be safe make sure it is really declared in
+ * the crypto capability registers. The crypto capability registers
+ * also give the supported data unit size(s).
+ */
+ for (i = 0; i < caps.num_crypto_cap; i++) {
+ cap.reg_val = cpu_to_le32(cqhci_readl(cq_host,
+ CQHCI_CRYPTOCAP +
+ i * sizeof(__le32)));
+ if (cap.algorithm_id == CQHCI_CRYPTO_ALG_AES_XTS &&
+ cap.key_size == CQHCI_CRYPTO_KEY_SIZE_256)
+ profile->modes_supported[BLK_ENCRYPTION_MODE_AES_256_XTS] |=
+ cap.sdus_mask * 512;
+ }
+
+ mmc->caps2 |= MMC_CAP2_CRYPTO;
return 0;
}
@@ -1903,35 +1959,46 @@ static __maybe_unused int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host)
return 0;
}
-/*
- * Program a key into a QC ICE keyslot, or evict a keyslot. QC ICE requires
- * vendor-specific SCM calls for this; it doesn't support the standard way.
- */
-static int sdhci_msm_program_key(struct cqhci_host *cq_host,
- const union cqhci_crypto_cfg_entry *cfg,
- int slot)
+static inline struct sdhci_msm_host *
+sdhci_msm_host_from_crypto_profile(struct blk_crypto_profile *profile)
{
- struct sdhci_host *host = mmc_priv(cq_host->mmc);
+ struct mmc_host *mmc = mmc_from_crypto_profile(profile);
+ struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
- union cqhci_crypto_cap_entry cap;
- if (!(cfg->config_enable & CQHCI_CRYPTO_CONFIGURATION_ENABLE))
- return qcom_ice_evict_key(msm_host->ice, slot);
+ return msm_host;
+}
- /* Only AES-256-XTS has been tested so far. */
- cap = cq_host->crypto_cap_array[cfg->crypto_cap_idx];
- if (cap.algorithm_id != CQHCI_CRYPTO_ALG_AES_XTS ||
- cap.key_size != CQHCI_CRYPTO_KEY_SIZE_256)
- return -EINVAL;
+/*
+ * Program a key into a QC ICE keyslot. QC ICE requires a QC-specific SCM call
+ * for this; it doesn't support the standard way.
+ */
+static int sdhci_msm_ice_keyslot_program(struct blk_crypto_profile *profile,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
+{
+ struct sdhci_msm_host *msm_host =
+ sdhci_msm_host_from_crypto_profile(profile);
- return qcom_ice_program_key(msm_host->ice,
- QCOM_ICE_CRYPTO_ALG_AES_XTS,
- QCOM_ICE_CRYPTO_KEY_SIZE_256,
- cfg->crypto_key,
- cfg->data_unit_size, slot);
+ return qcom_ice_program_key(msm_host->ice, slot, key);
}
+static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile,
+ const struct blk_crypto_key *key,
+ unsigned int slot)
+{
+ struct sdhci_msm_host *msm_host =
+ sdhci_msm_host_from_crypto_profile(profile);
+
+ return qcom_ice_evict_key(msm_host->ice, slot);
+}
+
+static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = {
+ .keyslot_program = sdhci_msm_ice_keyslot_program,
+ .keyslot_evict = sdhci_msm_ice_keyslot_evict,
+};
+
#else /* CONFIG_MMC_CRYPTO */
static inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,
@@ -2037,7 +2104,7 @@ static const struct cqhci_host_ops sdhci_msm_cqhci_ops = {
.enable = sdhci_msm_cqe_enable,
.disable = sdhci_msm_cqe_disable,
#ifdef CONFIG_MMC_CRYPTO
- .program_key = sdhci_msm_program_key,
+ .uses_custom_crypto_profile = true,
#endif
};
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 7ea3da45db32..a20d03fdd6a9 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/sizes.h>
@@ -328,12 +329,17 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
sdhci_request(mmc, mrq);
}
-static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
+static void dwcmshc_phy_init(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 rxsel = PHY_PAD_RXSEL_3V3;
u32 val;
+ if (priv->flags & FLAG_IO_FIXED_1V8 ||
+ host->mmc->ios.timing & MMC_SIGNAL_VOLTAGE_180)
+ rxsel = PHY_PAD_RXSEL_1V8;
+
/* deassert phy reset & set tx drive strength */
val = PHY_CNFG_RSTN_DEASSERT;
val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
@@ -353,7 +359,7 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
/* configure phy pads */
- val = PHY_PAD_RXSEL_1V8;
+ val = rxsel;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
@@ -365,65 +371,22 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
- val = PHY_PAD_RXSEL_1V8;
+ val = rxsel;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
/* enable data strobe mode */
- sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL),
- PHY_DLLDL_CNFG_R);
-
- /* enable phy dll */
- sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
-}
-
-static void dwcmshc_phy_3_3v_init(struct sdhci_host *host)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
- u32 val;
-
- /* deassert phy reset & set tx drive strength */
- val = PHY_CNFG_RSTN_DEASSERT;
- val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
- val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN);
- sdhci_writel(host, val, PHY_CNFG_R);
-
- /* disable delay line */
- sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
-
- /* set delay line */
- sdhci_writeb(host, priv->delay_line, PHY_SDCLKDL_DC_R);
- sdhci_writeb(host, PHY_DLL_CNFG2_JUMPSTEP, PHY_DLL_CNFG2_R);
-
- /* enable delay lane */
- val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
- val &= ~(PHY_SDCLKDL_CNFG_UPDATE);
- sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
-
- /* configure phy pads */
- val = PHY_PAD_RXSEL_3V3;
- val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
- sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
- sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
+ if (rxsel == PHY_PAD_RXSEL_1V8) {
+ u8 sel = FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL);
- val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
-
- val = PHY_PAD_RXSEL_3V3;
- val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
- val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
+ sdhci_writeb(host, sel, PHY_DLLDL_CNFG_R);
+ }
/* enable phy dll */
sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
+
}
static void th1520_sdhci_set_phy(struct sdhci_host *host)
@@ -433,11 +396,7 @@ static void th1520_sdhci_set_phy(struct sdhci_host *host)
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
u16 emmc_ctrl;
- /* Before power on, set PHY configs */
- if (priv->flags & FLAG_IO_FIXED_1V8)
- dwcmshc_phy_1_8v_init(host);
- else
- dwcmshc_phy_3_3v_init(host);
+ dwcmshc_phy_init(host);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
emmc_ctrl = sdhci_readw(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
@@ -787,6 +746,29 @@ static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv
}
}
+static void dwcmshc_rk3576_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
+{
+ struct device *dev = mmc_dev(host->mmc);
+ int ret;
+
+ /*
+ * This works around the design of the RK3576's power domains, which
+ * makes the PD_NVM power domain, which the sdhci controller on the
+ * RK3576 is in, never come back the same way once it's run-time
+ * suspended once. This can happen during early kernel boot if no driver
+ * is using either PD_NVM or its child power domain PD_SDGMAC for a
+ * short moment, leading to it being turned off to save power. By
+ * keeping it on, sdhci suspending won't lead to PD_NVM becoming a
+ * candidate for getting turned off.
+ */
+ ret = dev_pm_genpd_rpm_always_on(dev, true);
+ if (ret && ret != -EOPNOTSUPP)
+ dev_warn(dev, "failed to set PD rpm always on, SoC may hang later: %pe\n",
+ ERR_PTR(ret));
+
+ dwcmshc_rk35xx_postinit(host, dwc_priv);
+}
+
static int th1520_execute_tuning(struct sdhci_host *host, u32 opcode)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1163,7 +1145,7 @@ static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = {
.get_max_clock = dwcmshc_get_max_clock,
.reset = th1520_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
- .voltage_switch = dwcmshc_phy_1_8v_init,
+ .voltage_switch = dwcmshc_phy_init,
.platform_execute_tuning = th1520_execute_tuning,
};
@@ -1218,6 +1200,18 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
.postinit = dwcmshc_rk35xx_postinit,
};
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_rk35xx_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+ },
+ .init = dwcmshc_rk35xx_init,
+ .postinit = dwcmshc_rk3576_postinit,
+};
+
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_th1520_ops,
@@ -1317,6 +1311,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.data = &sdhci_dwcmshc_rk35xx_pdata,
},
{
+ .compatible = "rockchip,rk3576-dwcmshc",
+ .data = &sdhci_dwcmshc_rk3576_pdata,
+ },
+ {
.compatible = "rockchip,rk3568-dwcmshc",
.data = &sdhci_dwcmshc_rk35xx_pdata,
},
diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c
new file mode 100644
index 000000000000..2e5da7c5834c
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of-k1.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd
+ * Copyright (c) 2025 Yixun Lan <dlan@gentoo.org>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/init.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+
+#define SDHC_MMC_CTRL_REG 0x114
+#define MISC_INT_EN BIT(1)
+#define MISC_INT BIT(2)
+#define ENHANCE_STROBE_EN BIT(8)
+#define MMC_HS400 BIT(9)
+#define MMC_HS200 BIT(10)
+#define MMC_CARD_MODE BIT(12)
+
+#define SDHC_TX_CFG_REG 0x11C
+#define TX_INT_CLK_SEL BIT(30)
+#define TX_MUX_SEL BIT(31)
+
+#define SDHC_PHY_CTRL_REG 0x160
+#define PHY_FUNC_EN BIT(0)
+#define PHY_PLL_LOCK BIT(1)
+#define HOST_LEGACY_MODE BIT(31)
+
+#define SDHC_PHY_FUNC_REG 0x164
+#define PHY_TEST_EN BIT(7)
+#define HS200_USE_RFIFO BIT(15)
+
+#define SDHC_PHY_DLLCFG 0x168
+#define DLL_PREDLY_NUM GENMASK(3, 2)
+#define DLL_FULLDLY_RANGE GENMASK(5, 4)
+#define DLL_VREG_CTRL GENMASK(7, 6)
+#define DLL_ENABLE BIT(31)
+
+#define SDHC_PHY_DLLCFG1 0x16C
+#define DLL_REG1_CTRL GENMASK(7, 0)
+#define DLL_REG2_CTRL GENMASK(15, 8)
+#define DLL_REG3_CTRL GENMASK(23, 16)
+#define DLL_REG4_CTRL GENMASK(31, 24)
+
+#define SDHC_PHY_DLLSTS 0x170
+#define DLL_LOCK_STATE BIT(0)
+
+#define SDHC_PHY_PADCFG_REG 0x178
+#define PHY_DRIVE_SEL GENMASK(2, 0)
+#define RX_BIAS_CTRL BIT(5)
+
+struct spacemit_sdhci_host {
+ struct clk *clk_core;
+ struct clk *clk_io;
+};
+
+/* All helper functions will update clr/set while preserve rest bits */
+static inline void spacemit_sdhci_setbits(struct sdhci_host *host, u32 val, int reg)
+{
+ sdhci_writel(host, sdhci_readl(host, reg) | val, reg);
+}
+
+static inline void spacemit_sdhci_clrbits(struct sdhci_host *host, u32 val, int reg)
+{
+ sdhci_writel(host, sdhci_readl(host, reg) & ~val, reg);
+}
+
+static inline void spacemit_sdhci_clrsetbits(struct sdhci_host *host, u32 clr, u32 set, int reg)
+{
+ u32 val = sdhci_readl(host, reg);
+
+ val = (val & ~clr) | set;
+ sdhci_writel(host, val, reg);
+}
+
+static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ if (mask != SDHCI_RESET_ALL)
+ return;
+
+ spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG);
+
+ spacemit_sdhci_clrsetbits(host, PHY_DRIVE_SEL,
+ RX_BIAS_CTRL | FIELD_PREP(PHY_DRIVE_SEL, 4),
+ SDHC_PHY_PADCFG_REG);
+
+ if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC))
+ spacemit_sdhci_setbits(host, MMC_CARD_MODE, SDHC_MMC_CTRL_REG);
+}
+
+static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
+{
+ if (timing == MMC_TIMING_MMC_HS200)
+ spacemit_sdhci_setbits(host, MMC_HS200, SDHC_MMC_CTRL_REG);
+
+ if (timing == MMC_TIMING_MMC_HS400)
+ spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG);
+
+ sdhci_set_uhs_signaling(host, timing);
+
+ if (!(host->mmc->caps2 & MMC_CAP2_NO_SDIO))
+ spacemit_sdhci_setbits(host, SDHCI_CTRL_VDD_180, SDHCI_HOST_CONTROL2);
+}
+
+static void spacemit_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct mmc_host *mmc = host->mmc;
+
+ if (mmc->ios.timing <= MMC_TIMING_UHS_SDR50)
+ spacemit_sdhci_setbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG);
+ else
+ spacemit_sdhci_clrbits(host, TX_INT_CLK_SEL, SDHC_TX_CFG_REG);
+
+ sdhci_set_clock(host, clock);
+};
+
+static void spacemit_sdhci_phy_dll_init(struct sdhci_host *host)
+{
+ u32 state;
+ int ret;
+
+ spacemit_sdhci_clrsetbits(host, DLL_PREDLY_NUM | DLL_FULLDLY_RANGE | DLL_VREG_CTRL,
+ FIELD_PREP(DLL_PREDLY_NUM, 1) |
+ FIELD_PREP(DLL_FULLDLY_RANGE, 1) |
+ FIELD_PREP(DLL_VREG_CTRL, 1),
+ SDHC_PHY_DLLCFG);
+
+ spacemit_sdhci_clrsetbits(host, DLL_REG1_CTRL,
+ FIELD_PREP(DLL_REG1_CTRL, 0x92),
+ SDHC_PHY_DLLCFG1);
+
+ spacemit_sdhci_setbits(host, DLL_ENABLE, SDHC_PHY_DLLCFG);
+
+ ret = readl_poll_timeout(host->ioaddr + SDHC_PHY_DLLSTS, state,
+ state & DLL_LOCK_STATE, 2, 100);
+ if (ret == -ETIMEDOUT)
+ dev_warn(mmc_dev(host->mmc), "fail to lock phy dll in 100us!\n");
+}
+
+static void spacemit_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (!ios->enhanced_strobe) {
+ spacemit_sdhci_clrbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG);
+ return;
+ }
+
+ spacemit_sdhci_setbits(host, ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG);
+ spacemit_sdhci_phy_dll_init(host);
+}
+
+static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+ return clk_get_rate(pltfm_host->clk);
+}
+
+static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ spacemit_sdhci_setbits(host, MMC_HS400, SDHC_MMC_CTRL_REG);
+ host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
+
+ return 0;
+}
+
+static void spacemit_sdhci_post_select_hs400(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ spacemit_sdhci_phy_dll_init(host);
+ host->mmc->caps &= ~MMC_CAP_WAIT_WHILE_BUSY;
+}
+
+static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ spacemit_sdhci_clrbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG);
+ spacemit_sdhci_clrbits(host, MMC_HS400 | MMC_HS200 | ENHANCE_STROBE_EN, SDHC_MMC_CTRL_REG);
+ spacemit_sdhci_clrbits(host, HS200_USE_RFIFO, SDHC_PHY_FUNC_REG);
+
+ udelay(5);
+
+ spacemit_sdhci_setbits(host, PHY_FUNC_EN | PHY_PLL_LOCK, SDHC_PHY_CTRL_REG);
+}
+
+static inline int spacemit_sdhci_get_clocks(struct device *dev,
+ struct sdhci_pltfm_host *pltfm_host)
+{
+ struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host);
+
+ sdhst->clk_core = devm_clk_get_enabled(dev, "core");
+ if (IS_ERR(sdhst->clk_core))
+ return -EINVAL;
+
+ sdhst->clk_io = devm_clk_get_enabled(dev, "io");
+ if (IS_ERR(sdhst->clk_io))
+ return -EINVAL;
+
+ pltfm_host->clk = sdhst->clk_io;
+
+ return 0;
+}
+
+static const struct sdhci_ops spacemit_sdhci_ops = {
+ .get_max_clock = spacemit_sdhci_clk_get_max_clock,
+ .reset = spacemit_sdhci_reset,
+ .set_bus_width = sdhci_set_bus_width,
+ .set_clock = spacemit_sdhci_set_clock,
+ .set_uhs_signaling = spacemit_sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = {
+ .ops = &spacemit_sdhci_ops,
+ .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+ SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+ SDHCI_QUIRK_32BIT_ADMA_SIZE |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA |
+ SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+};
+
+static const struct of_device_id spacemit_sdhci_of_match[] = {
+ { .compatible = "spacemit,k1-sdhci" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match);
+
+static int spacemit_sdhci_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spacemit_sdhci_host *sdhst;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_host *host;
+ struct mmc_host_ops *mops;
+ int ret;
+
+ host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto err_pltfm;
+
+ sdhci_get_of_property(pdev);
+
+ if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) {
+ mops = &host->mmc_host_ops;
+ mops->hs400_prepare_ddr = spacemit_sdhci_pre_select_hs400;
+ mops->hs400_complete = spacemit_sdhci_post_select_hs400;
+ mops->hs400_downgrade = spacemit_sdhci_pre_hs400_to_hs200;
+ mops->hs400_enhanced_strobe = spacemit_sdhci_hs400_enhanced_strobe;
+ }
+
+ host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
+
+ ret = spacemit_sdhci_get_clocks(dev, pltfm_host);
+ if (ret)
+ goto err_pltfm;
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto err_pltfm;
+
+ return 0;
+
+err_pltfm:
+ sdhci_pltfm_free(pdev);
+ return ret;
+}
+
+static struct platform_driver spacemit_sdhci_driver = {
+ .driver = {
+ .name = "sdhci-spacemit",
+ .of_match_table = spacemit_sdhci_of_match,
+ },
+ .probe = spacemit_sdhci_probe,
+ .remove = sdhci_pltfm_remove,
+};
+module_platform_driver(spacemit_sdhci_driver);
+
+MODULE_DESCRIPTION("SpacemiT SDHCI platform driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c
index 54d795205fb4..8897839ab2aa 100644
--- a/drivers/mmc/host/sdhci-omap.c
+++ b/drivers/mmc/host/sdhci-omap.c
@@ -1270,7 +1270,7 @@ static int sdhci_omap_probe(struct platform_device *pdev)
mmc->f_max = 48000000;
}
- if (!mmc_can_gpio_ro(mmc))
+ if (!mmc_host_can_gpio_ro(mmc))
mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
pltfm_host->clk = devm_clk_get(dev, "fck");
@@ -1339,8 +1339,8 @@ static int sdhci_omap_probe(struct platform_device *pdev)
/* R1B responses is required to properly manage HW busy detection. */
mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
- /* Allow card power off and runtime PM for eMMC/SD card devices */
- mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_AGGRESSIVE_PM;
+ /* Enable SDIO card power off. */
+ mmc->caps |= MMC_CAP_POWER_OFF_CARD;
ret = sdhci_setup_host(host);
if (ret)
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 1f0bd723f011..e3877a1c72a9 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -610,8 +610,12 @@ static void sdhci_intel_set_power(struct sdhci_host *host, unsigned char mode,
sdhci_set_power(host, mode, vdd);
- if (mode == MMC_POWER_OFF)
+ if (mode == MMC_POWER_OFF) {
+ if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_APL_SD ||
+ slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BYT_SD)
+ usleep_range(15000, 17500);
return;
+ }
/*
* Bus power might not enable after D3 -> D0 transition due to the
@@ -909,7 +913,8 @@ static bool glk_broken_cqhci(struct sdhci_pci_slot *slot)
{
return slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC &&
(dmi_match(DMI_BIOS_VENDOR, "LENOVO") ||
- dmi_match(DMI_SYS_VENDOR, "IRBIS"));
+ dmi_match(DMI_SYS_VENDOR, "IRBIS") ||
+ dmi_match(DMI_SYS_VENDOR, "Positivo Tecnologia SA"));
}
static bool jsl_broken_hs400es(struct sdhci_pci_slot *slot)
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index 990723a008ae..3fb56face3d8 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -399,6 +399,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
if (!IS_ERR(pxa->clk_core))
clk_prepare_enable(pxa->clk_core);
+ host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
/* enable 1/8V DDR capable */
host->mmc->caps |= MMC_CAP_1_8V_DDR;
diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c
index c53b64d50c0d..0efeb9d0c376 100644
--- a/drivers/mmc/host/sdhci-uhs2.c
+++ b/drivers/mmc/host/sdhci-uhs2.c
@@ -99,8 +99,8 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask)
/* hw clears the bit when it's done */
if (read_poll_timeout_atomic(sdhci_readw, val, !(val & mask), 10,
UHS2_RESET_TIMEOUT_100MS, true, host, SDHCI_UHS2_SW_RESET)) {
- pr_warn("%s: %s: Reset 0x%x never completed. %s: clean reset bit.\n", __func__,
- mmc_hostname(host->mmc), (int)mask, mmc_hostname(host->mmc));
+ pr_debug("%s: %s: Reset 0x%x never completed. %s: clean reset bit.\n", __func__,
+ mmc_hostname(host->mmc), (int)mask, mmc_hostname(host->mmc));
sdhci_writeb(host, 0, SDHCI_UHS2_SW_RESET);
return;
}
@@ -335,8 +335,8 @@ static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IF_DETECT),
100, UHS2_INTERFACE_DETECT_TIMEOUT_100MS, true,
host, SDHCI_PRESENT_STATE)) {
- pr_warn("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc));
- sdhci_dumpregs(host);
+ pr_debug("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc));
+ sdhci_dbg_dumpregs(host, "UHS2 interface detect timeout in 100ms");
return -EIO;
}
@@ -345,8 +345,8 @@ static int sdhci_uhs2_interface_detect(struct sdhci_host *host)
if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_LANE_SYNC),
100, UHS2_LANE_SYNC_TIMEOUT_150MS, true, host, SDHCI_PRESENT_STATE)) {
- pr_warn("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc));
- sdhci_dumpregs(host);
+ pr_debug("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc));
+ sdhci_dbg_dumpregs(host, "UHS2 Lane sync fail in 150ms");
return -EIO;
}
@@ -417,12 +417,12 @@ static int sdhci_uhs2_do_detect_init(struct mmc_host *mmc)
host->ops->uhs2_pre_detect_init(host);
if (sdhci_uhs2_interface_detect(host)) {
- pr_warn("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc));
+ pr_debug("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc));
return -EIO;
}
if (sdhci_uhs2_init(host)) {
- pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
+ pr_debug("%s: UHS2 init fail.\n", mmc_hostname(host->mmc));
return -EIO;
}
@@ -504,8 +504,8 @@ static int sdhci_uhs2_check_dormant(struct sdhci_host *host)
if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IN_DORMANT_STATE),
100, UHS2_CHECK_DORMANT_TIMEOUT_100MS, true, host,
SDHCI_PRESENT_STATE)) {
- pr_warn("%s: UHS2 IN_DORMANT fail in 100ms.\n", mmc_hostname(host->mmc));
- sdhci_dumpregs(host);
+ pr_debug("%s: UHS2 IN_DORMANT fail in 100ms.\n", mmc_hostname(host->mmc));
+ sdhci_dbg_dumpregs(host, "UHS2 IN_DORMANT fail in 100ms");
return -EIO;
}
return 0;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index f4a7733a8ad2..e116f2db34d5 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -158,7 +158,7 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
u32 present;
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
- !mmc_card_is_removable(host->mmc) || mmc_can_gpio_cd(host->mmc))
+ !mmc_card_is_removable(host->mmc) || mmc_host_can_gpio_cd(host->mmc))
return;
if (enable) {
@@ -517,9 +517,9 @@ EXPORT_SYMBOL_GPL(sdhci_mod_timer);
static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq)
{
if (sdhci_data_line_cmd(mrq->cmd))
- del_timer(&host->data_timer);
+ timer_delete(&host->data_timer);
else
- del_timer(&host->timer);
+ timer_delete(&host->timer);
}
static inline bool sdhci_has_requests(struct sdhci_host *host)
@@ -2566,7 +2566,7 @@ int sdhci_get_ro(struct mmc_host *mmc)
is_readonly = 0;
} else if (host->ops->get_ro) {
is_readonly = host->ops->get_ro(host);
- } else if (mmc_can_gpio_ro(mmc)) {
+ } else if (mmc_host_can_gpio_ro(mmc)) {
is_readonly = mmc_gpio_get_ro(mmc);
/* Do not invert twice */
allow_invert = !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
@@ -3240,7 +3240,7 @@ static void sdhci_timeout_timer(struct timer_list *t)
struct sdhci_host *host;
unsigned long flags;
- host = from_timer(host, t, timer);
+ host = timer_container_of(host, t, timer);
spin_lock_irqsave(&host->lock, flags);
@@ -3262,7 +3262,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
struct sdhci_host *host;
unsigned long flags;
- host = from_timer(host, t, data_timer);
+ host = timer_container_of(host, t, data_timer);
spin_lock_irqsave(&host->lock, flags);
@@ -3739,7 +3739,7 @@ static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
{
return mmc_card_is_removable(host->mmc) &&
!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
- !mmc_can_gpio_cd(host->mmc);
+ !mmc_host_can_gpio_cd(host->mmc);
}
/*
@@ -3750,7 +3750,7 @@ static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host)
* sdhci_disable_irq_wakeups() since it will be set by
* sdhci_enable_card_detection() or sdhci_init().
*/
-static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
+bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
{
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE |
SDHCI_WAKE_ON_INT;
@@ -3782,8 +3782,9 @@ static bool sdhci_enable_irq_wakeups(struct sdhci_host *host)
return host->irq_wake_enabled;
}
+EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
-static void sdhci_disable_irq_wakeups(struct sdhci_host *host)
+void sdhci_disable_irq_wakeups(struct sdhci_host *host)
{
u8 val;
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
@@ -3797,6 +3798,7 @@ static void sdhci_disable_irq_wakeups(struct sdhci_host *host)
host->irq_wake_enabled = false;
}
+EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
int sdhci_suspend_host(struct sdhci_host *host)
{
@@ -4971,8 +4973,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
free_irq(host->irq, host);
- del_timer_sync(&host->timer);
- del_timer_sync(&host->data_timer);
+ timer_delete_sync(&host->timer);
+ timer_delete_sync(&host->data_timer);
destroy_workqueue(host->complete_wq);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index cd0e35a80542..70ada1857a4c 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -875,6 +875,8 @@ void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
#ifdef CONFIG_PM
+bool sdhci_enable_irq_wakeups(struct sdhci_host *host);
+void sdhci_disable_irq_wakeups(struct sdhci_host *host);
int sdhci_suspend_host(struct sdhci_host *host);
int sdhci_resume_host(struct sdhci_host *host);
int sdhci_runtime_suspend_host(struct sdhci_host *host);
@@ -898,4 +900,20 @@ void sdhci_switch_external_dma(struct sdhci_host *host, bool en);
void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable);
void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
+#if defined(CONFIG_DYNAMIC_DEBUG) || \
+ (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
+#define SDHCI_DBG_ANYWAY 0
+#elif defined(DEBUG)
+#define SDHCI_DBG_ANYWAY 1
+#else
+#define SDHCI_DBG_ANYWAY 0
+#endif
+
+#define sdhci_dbg_dumpregs(host, fmt) \
+do { \
+ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
+ if (DYNAMIC_DEBUG_BRANCH(descriptor) || SDHCI_DBG_ANYWAY) \
+ sdhci_dumpregs(host); \
+} while (0)
+
#endif /* __SDHCI_HW_H */
diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
index f75c31815ab0..9e94998e8df7 100644
--- a/drivers/mmc/host/sdhci_am654.c
+++ b/drivers/mmc/host/sdhci_am654.c
@@ -155,6 +155,7 @@ struct sdhci_am654_data {
u32 tuning_loop;
#define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0)
+#define SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA BIT(1)
};
struct window {
@@ -166,6 +167,7 @@ struct window {
struct sdhci_am654_driver_data {
const struct sdhci_pltfm_data *pdata;
u32 flags;
+ u32 quirks;
#define IOMUX_PRESENT (1 << 0)
#define FREQSEL_2_BIT (1 << 1)
#define STRBSEL_4_BIT (1 << 2)
@@ -356,6 +358,29 @@ static void sdhci_j721e_4bit_set_clock(struct sdhci_host *host,
sdhci_set_clock(host, clock);
}
+static int sdhci_am654_start_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
+ int ret;
+
+ if ((sdhci_am654->quirks & SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA) &&
+ ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
+ if (!IS_ERR(mmc->supply.vqmmc)) {
+ ret = mmc_regulator_set_vqmmc(mmc, ios);
+ if (ret < 0) {
+ pr_err("%s: Switching to 1.8V signalling voltage failed,\n",
+ mmc_hostname(mmc));
+ return -EIO;
+ }
+ }
+ return 0;
+ }
+
+ return sdhci_start_signal_voltage_switch(mmc, ios);
+}
+
static u8 sdhci_am654_write_power_on(struct sdhci_host *host, u8 val, int reg)
{
writeb(val, host->ioaddr + reg);
@@ -588,7 +613,8 @@ static const struct sdhci_ops sdhci_am654_ops = {
static const struct sdhci_pltfm_data sdhci_am654_pdata = {
.ops = &sdhci_am654_ops,
.quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
};
static const struct sdhci_am654_driver_data sdhci_am654_sr1_drvdata = {
@@ -618,7 +644,8 @@ static const struct sdhci_ops sdhci_j721e_8bit_ops = {
static const struct sdhci_pltfm_data sdhci_j721e_8bit_pdata = {
.ops = &sdhci_j721e_8bit_ops,
.quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
};
static const struct sdhci_am654_driver_data sdhci_j721e_8bit_drvdata = {
@@ -642,7 +669,8 @@ static const struct sdhci_ops sdhci_j721e_4bit_ops = {
static const struct sdhci_pltfm_data sdhci_j721e_4bit_pdata = {
.ops = &sdhci_j721e_4bit_ops,
.quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_DISABLE_HW_TIMEOUT,
};
static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = {
@@ -650,6 +678,12 @@ static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = {
.flags = IOMUX_PRESENT,
};
+static const struct sdhci_am654_driver_data sdhci_am62_4bit_drvdata = {
+ .pdata = &sdhci_j721e_4bit_pdata,
+ .flags = IOMUX_PRESENT,
+ .quirks = SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA,
+};
+
static const struct soc_device_attribute sdhci_am654_devices[] = {
{ .family = "AM65X",
.revision = "SR1.0",
@@ -872,7 +906,7 @@ static const struct of_device_id sdhci_am654_of_match[] = {
},
{
.compatible = "ti,am62-sdhci",
- .data = &sdhci_j721e_4bit_drvdata,
+ .data = &sdhci_am62_4bit_drvdata,
},
{ /* sentinel */ }
};
@@ -906,6 +940,7 @@ static int sdhci_am654_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
sdhci_am654->flags = drvdata->flags;
+ sdhci_am654->quirks = drvdata->quirks;
clk_xin = devm_clk_get(dev, "clk_xin");
if (IS_ERR(clk_xin)) {
@@ -940,6 +975,7 @@ static int sdhci_am654_probe(struct platform_device *pdev)
goto err_pltfm_free;
}
+ host->mmc_host_ops.start_signal_voltage_switch = sdhci_am654_start_signal_voltage_switch;
host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning;
pm_runtime_get_noresume(dev);
diff --git a/drivers/mmc/host/sunplus-mmc.c b/drivers/mmc/host/sunplus-mmc.c
index 1cddea615a27..63279760239c 100644
--- a/drivers/mmc/host/sunplus-mmc.c
+++ b/drivers/mmc/host/sunplus-mmc.c
@@ -791,7 +791,7 @@ static int spmmc_get_cd(struct mmc_host *mmc)
{
int ret = 0;
- if (mmc_can_gpio_cd(mmc))
+ if (mmc_host_can_gpio_cd(mmc))
ret = mmc_gpio_get_cd(mmc);
if (ret < 0)
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index aea14bf3e2e8..5e5ec92f80e6 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -735,7 +735,7 @@ static void tifm_sd_end_cmd(struct work_struct *t)
spin_lock_irqsave(&sock->lock, flags);
- del_timer(&host->timer);
+ timer_delete(&host->timer);
mrq = host->req;
host->req = NULL;
@@ -777,7 +777,7 @@ static void tifm_sd_end_cmd(struct work_struct *t)
static void tifm_sd_abort(struct timer_list *t)
{
- struct tifm_sd *host = from_timer(host, t, timer);
+ struct tifm_sd *host = timer_container_of(host, t, timer);
pr_err("%s : card failed to respond for a long period of time "
"(%x, %x)\n",
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index a75755f31d31..41787ea77a13 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -44,6 +44,7 @@
#define CTL_RESET_SD 0xe0
#define CTL_VERSION 0xe2
#define CTL_SDIF_MODE 0xe6 /* only known on R-Car 2+ */
+#define CTL_SD_STATUS 0xf2 /* only known on RZ/{G2L,G3E,V2H} */
/* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */
#define TMIO_STOP_STP BIT(0)
@@ -103,6 +104,10 @@
/* Definitions for values the CTL_SDIF_MODE register can take */
#define SDIF_MODE_HS400 BIT(0) /* only known on R-Car 2+ */
+/* Definitions for values the CTL_SD_STATUS register can take */
+#define SD_STATUS_PWEN BIT(0) /* only known on RZ/{G3E,V2H} */
+#define SD_STATUS_IOVS BIT(16) /* only known on RZ/{G3E,V2H} */
+
/* Define some IRQ masks */
/* This is the mask used at reset by the chip */
#define TMIO_MASK_ALL 0x837f031d
@@ -226,6 +231,11 @@ static inline u32 sd_ctrl_read16_and_16_as_32(struct tmio_mmc_host *host,
ioread16(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
}
+static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
+{
+ return ioread32(host->ctl + (addr << host->bus_shift));
+}
+
static inline void sd_ctrl_read32_rep(struct tmio_mmc_host *host, int addr,
u32 *buf, int count)
{
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 45a474ccab1c..b71241f55df5 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -297,7 +297,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host,
switch (mmc_resp_type(cmd)) {
case MMC_RSP_NONE: c |= RESP_NONE; break;
case MMC_RSP_R1:
- case MMC_RSP_R1_NO_CRC:
c |= RESP_R1; break;
case MMC_RSP_R1B: c |= RESP_R1B; break;
case MMC_RSP_R2: c |= RESP_R2; break;
@@ -1177,14 +1176,14 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
dma_max_mapping_size(&pdev->dev));
mmc->max_seg_size = mmc->max_req_size;
- if (mmc_can_gpio_ro(mmc))
+ if (mmc_host_can_gpio_ro(mmc))
_host->ops.get_ro = mmc_gpio_get_ro;
- if (mmc_can_gpio_cd(mmc))
+ if (mmc_host_can_gpio_cd(mmc))
_host->ops.get_cd = mmc_gpio_get_cd;
/* must be set before tmio_mmc_reset() */
- _host->native_hotplug = !(mmc_can_gpio_cd(mmc) ||
+ _host->native_hotplug = !(mmc_host_can_gpio_cd(mmc) ||
mmc->caps & MMC_CAP_NEEDS_POLL ||
!mmc_card_is_removable(mmc));
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index f77457105ec3..9903966c2f54 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -937,7 +937,7 @@ static void via_sdc_timeout(struct timer_list *t)
struct via_crdr_mmc_host *sdhost;
unsigned long flags;
- sdhost = from_timer(sdhost, t, timer);
+ sdhost = timer_container_of(sdhost, t, timer);
spin_lock_irqsave(&sdhost->lock, flags);
@@ -971,7 +971,7 @@ static void via_sdc_finish_bh_work(struct work_struct *t)
spin_lock_irqsave(&host->lock, flags);
- del_timer(&host->timer);
+ timer_delete(&host->timer);
mrq = host->mrq;
host->mrq = NULL;
host->cmd = NULL;
@@ -1202,7 +1202,7 @@ static void via_sd_remove(struct pci_dev *pcidev)
free_irq(pcidev->irq, sdhost);
- del_timer_sync(&sdhost->timer);
+ timer_delete_sync(&sdhost->timer);
cancel_work_sync(&sdhost->finish_bh_work);
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index fd67c0682b38..f498fe11ecdf 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -740,8 +740,8 @@ static void vub300_deadwork_thread(struct work_struct *work)
static void vub300_inactivity_timer_expired(struct timer_list *t)
{ /* softirq */
- struct vub300_mmc_host *vub300 = from_timer(vub300, t,
- inactivity_timer);
+ struct vub300_mmc_host *vub300 = timer_container_of(vub300, t,
+ inactivity_timer);
if (!vub300->interface) {
kref_put(&vub300->kref, vub300_delete);
} else if (vub300->cmd) {
@@ -1180,8 +1180,8 @@ static void send_command(struct vub300_mmc_host *vub300)
*/
static void vub300_sg_timed_out(struct timer_list *t)
{
- struct vub300_mmc_host *vub300 = from_timer(vub300, t,
- sg_transfer_timer);
+ struct vub300_mmc_host *vub300 = timer_container_of(vub300, t,
+ sg_transfer_timer);
vub300->usb_timed_out = 1;
usb_sg_cancel(&vub300->sg_request);
usb_unlink_urb(vub300->command_out_urb);
@@ -1452,7 +1452,7 @@ static int __command_read_data(struct vub300_mmc_host *vub300,
(linear_length / 16384));
add_timer(&vub300->sg_transfer_timer);
usb_sg_wait(&vub300->sg_request);
- del_timer(&vub300->sg_transfer_timer);
+ timer_delete(&vub300->sg_transfer_timer);
if (vub300->sg_request.status < 0) {
cmd->error = vub300->sg_request.status;
data->bytes_xfered = 0;
@@ -1572,7 +1572,7 @@ static int __command_write_data(struct vub300_mmc_host *vub300,
if (cmd->error) {
data->bytes_xfered = 0;
} else {
- del_timer(&vub300->sg_transfer_timer);
+ timer_delete(&vub300->sg_transfer_timer);
if (vub300->sg_request.status < 0) {
cmd->error = vub300->sg_request.status;
data->bytes_xfered = 0;
@@ -2339,7 +2339,7 @@ static int vub300_probe(struct usb_interface *interface,
return 0;
error6:
- del_timer_sync(&vub300->inactivity_timer);
+ timer_delete_sync(&vub300->inactivity_timer);
error5:
mmc_free_host(mmc);
/*
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index 8b268e8a0ec9..2ae787d966de 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -947,7 +947,7 @@ static const struct mmc_host_ops wbsd_ops = {
static void wbsd_reset_ignore(struct timer_list *t)
{
- struct wbsd_host *host = from_timer(host, t, ignore_timer);
+ struct wbsd_host *host = timer_container_of(host, t, ignore_timer);
BUG_ON(host == NULL);
@@ -1261,7 +1261,7 @@ static void wbsd_free_mmc(struct device *dev)
host = mmc_priv(mmc);
BUG_ON(host == NULL);
- del_timer_sync(&host->ignore_timer);
+ timer_delete_sync(&host->ignore_timer);
mmc_free_host(mmc);
}