summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-16 08:16:55 +0530
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-16 08:16:55 +0530
commit464e2f089ecb81139433666496ecaeece421b314 (patch)
tree241ead8a249d1ff1a04dabe6400a8ab4667a5868
parentfc2ce3ee106f2d53eb344f5c4963c897bbb21634 (diff)
parent576e40d20e100ba62e21bfda94ad948820c90a23 (diff)
Merge tag 'mmc-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson: "MMC core: - Validate host's max_segs to fail gracefully MMC host: - davinci: - Avoid potential NULL dereference in the IRQ handler - Call mmc_add_host() in the correct order during probe - dw_mmc-exynos: - Increase DMA threshold for exynos7870 - renesas_sdhi: - Add support for RZ/G2E, RZ/G2N and R-Car M3Le variants - sdhci-msm: - Add support for Hawi, Eliza and Shikra variants - sdhci-of-k1: - Add support for SD UHS-I modes - Add support for tuning for eMMC HS200 and SD UHS-I" * tag 'mmc-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (24 commits) mmc: dw_mmc: Add desc_num field for clarity dt-bindings: mmc: sdhci-msm: Rename the binding to include 'qcom' prefix mmc: sdhci-of-dwcmshc: use dev_err_probe() to simplify error paths mmc: sdhci-of-dwcmshc: remove redundant IS_ERR() check dt-bindings: mmc: sdhci-msm: qcom: Add Hawi compatible mmc: renesas_sdhi: Add OF entry for RZ/G2E SoC mmc: renesas_sdhi: Add OF entry for RZ/G2N SoC dt-bindings: mmc: sdhci-msm: Add Eliza compatible mmc: davinci: fix mmc_add_host order in probe dt-bindings: mmc: sdhci-msm: Document the Shikra compatible mmc: sdhci-of-k1: add comprehensive SDR tuning support mmc: sdhci-of-k1: add regulator and pinctrl voltage switching support mmc: sdhci-of-k1: enable essential clock infrastructure for SD operation dt-bindings: mmc: spacemit,sdhci: add pinctrl support for voltage switching mmc: via-sdmmc: Simplify initialisation of pci_device_id array mmc: davinci: avoid NULL deref of host->data in IRQ handler memstick: Constify the driver id_table mmc: host: Move MODULE_DEVICE_TABLE next to the table itself mmc: renesas_sdhi: add R-Car M3Le compatibility string dt-bindings: mmc: renesas,sdhi: Document R-Car M3Le support ...
-rw-r--r--Documentation/devicetree/bindings/mmc/qcom,sdhci-msm.yaml (renamed from Documentation/devicetree/bindings/mmc/sdhci-msm.yaml)5
-rw-r--r--Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml1
-rw-r--r--Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml15
-rw-r--r--drivers/memstick/host/tifm_ms.c2
-rw-r--r--drivers/misc/tifm_core.c4
-rw-r--r--drivers/mmc/core/queue.c8
-rw-r--r--drivers/mmc/host/cavium-thunderx.c2
-rw-r--r--drivers/mmc/host/davinci_mmc.c16
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c1
-rw-r--r--drivers/mmc/host/dw_mmc.c20
-rw-r--r--drivers/mmc/host/dw_mmc.h6
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c3
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c36
-rw-r--r--drivers/mmc/host/sdhci-of-k1.c257
-rw-r--r--drivers/mmc/host/tifm_sd.c4
-rw-r--r--drivers/mmc/host/via-sdmmc.c5
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c2
-rw-r--r--include/linux/tifm.h2
18 files changed, 332 insertions, 57 deletions
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/qcom,sdhci-msm.yaml
index 695a95e8f35d..bd558a11b792 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
+++ b/Documentation/devicetree/bindings/mmc/qcom,sdhci-msm.yaml
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
-$id: http://devicetree.org/schemas/mmc/sdhci-msm.yaml#
+$id: http://devicetree.org/schemas/mmc/qcom,sdhci-msm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SDHCI controller (sdhci-msm)
@@ -37,6 +37,8 @@ properties:
- const: qcom,sdhci-msm-v4 # for sdcc versions less than 5.0
- items:
- enum:
+ - qcom,eliza-sdhci
+ - qcom,hawi-sdhci
- qcom,ipq5018-sdhci
- qcom,ipq5210-sdhci
- qcom,ipq5332-sdhci
@@ -62,6 +64,7 @@ properties:
- qcom,sdx55-sdhci
- qcom,sdx65-sdhci
- qcom,sdx75-sdhci
+ - qcom,shikra-sdhci
- qcom,sm6115-sdhci
- qcom,sm6125-sdhci
- qcom,sm6350-sdhci
diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
index 64fac0d11329..4d66966ce290 100644
--- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
+++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml
@@ -52,6 +52,7 @@ properties:
- renesas,sdhi-r8a77980 # R-Car V3H
- renesas,sdhi-r8a77990 # R-Car E3
- renesas,sdhi-r8a77995 # R-Car D3
+ - renesas,sdhi-r8a779md # R-Car M3Le
- const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
- items:
- enum:
diff --git a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml
index 9a055d963a7f..34d202af909f 100644
--- a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml
+++ b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml
@@ -44,6 +44,18 @@ properties:
- const: axi
- const: sdh
+ pinctrl-names:
+ minItems: 1
+ items:
+ - const: default
+ - const: uhs
+
+ pinctrl-0:
+ description: Default pinctrl state for 3.3V operation
+
+ pinctrl-1:
+ description: Optional pinctrl state for 1.8V UHS operation with "uhs" name
+
required:
- compatible
- reg
@@ -62,4 +74,7 @@ examples:
interrupt-parent = <&plic>;
clocks = <&clk_apmu 10>, <&clk_apmu 13>;
clock-names = "core", "io";
+ pinctrl-names = "default", "uhs";
+ pinctrl-0 = <&sdhci_default_cfg>;
+ pinctrl-1 = <&sdhci_uhs_cfg>;
};
diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c
index 0b6a90661eee..0a54586aa54e 100644
--- a/drivers/memstick/host/tifm_ms.c
+++ b/drivers/memstick/host/tifm_ms.c
@@ -647,7 +647,7 @@ static int tifm_ms_resume(struct tifm_dev *sock)
#endif /* CONFIG_PM */
-static struct tifm_device_id tifm_ms_id_tbl[] = {
+static const struct tifm_device_id tifm_ms_id_tbl[] = {
{ TIFM_TYPE_MS }, { 0 }
};
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c
index da0827724a61..aac8f0933e43 100644
--- a/drivers/misc/tifm_core.c
+++ b/drivers/misc/tifm_core.c
@@ -31,7 +31,7 @@ static const char *tifm_media_type_name(unsigned char type, unsigned char nt)
return card_type_name[nt][type - 1];
}
-static int tifm_dev_match(struct tifm_dev *sock, struct tifm_device_id *id)
+static int tifm_dev_match(struct tifm_dev *sock, const struct tifm_device_id *id)
{
if (sock->type == id->type)
return 1;
@@ -43,7 +43,7 @@ static int tifm_bus_match(struct device *dev, const struct device_driver *drv)
struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
const struct tifm_driver *fm_drv = container_of_const(drv, struct tifm_driver,
driver);
- struct tifm_device_id *ids = fm_drv->id_table;
+ const struct tifm_device_id *ids = fm_drv->id_table;
if (ids) {
while (ids->type) {
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index 39fcb662c43f..c9028e4a7e56 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -214,8 +214,14 @@ static int mmc_mq_init_request(struct blk_mq_tag_set *set, struct request *req,
struct mmc_queue *mq = set->driver_data;
struct mmc_card *card = mq->card;
struct mmc_host *host = card->host;
+ u16 sg_len = mmc_get_max_segments(host);
- mq_rq->sg = mmc_alloc_sg(mmc_get_max_segments(host), GFP_KERNEL);
+ if (!sg_len) {
+ dev_err(mmc_dev(host), "Wrong max_segs assigned\n");
+ return -EINVAL;
+ }
+
+ mq_rq->sg = mmc_alloc_sg(sg_len, GFP_KERNEL);
if (!mq_rq->sg)
return -ENOMEM;
diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c
index 1373deb3f531..84ff6d82ae3c 100644
--- a/drivers/mmc/host/cavium-thunderx.c
+++ b/drivers/mmc/host/cavium-thunderx.c
@@ -188,6 +188,7 @@ static const struct pci_device_id thunder_mmc_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa010) },
{ 0, } /* end of table */
};
+MODULE_DEVICE_TABLE(pci, thunder_mmc_id_table);
static struct pci_driver thunder_mmc_driver = {
.name = KBUILD_MODNAME,
@@ -201,4 +202,3 @@ module_pci_driver(thunder_mmc_driver);
MODULE_AUTHOR("Cavium Inc.");
MODULE_DESCRIPTION("Cavium ThunderX eMMC Driver");
MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(pci, thunder_mmc_id_table);
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 42b0118a45a8..cdb9fa94b56d 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -928,7 +928,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
}
}
- if (qstatus & MMCST0_TOUTRD) {
+ if (data && (qstatus & MMCST0_TOUTRD)) {
/* Read data timeout */
data->error = -ETIMEDOUT;
end_transfer = 1;
@@ -940,7 +940,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
davinci_abort_data(host, data);
}
- if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
+ if (data && (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD))) {
/* Data CRC error */
data->error = -EILSEQ;
end_transfer = 1;
@@ -1294,14 +1294,10 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
goto cpu_freq_fail;
}
- ret = mmc_add_host(mmc);
- if (ret < 0)
- goto mmc_add_host_fail;
-
ret = devm_request_irq(&pdev->dev, irq, mmc_davinci_irq, 0,
mmc_hostname(mmc), host);
if (ret)
- goto request_irq_fail;
+ goto mmc_add_host_fail;
if (host->sdio_irq >= 0) {
ret = devm_request_irq(&pdev->dev, host->sdio_irq,
@@ -1311,6 +1307,10 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
mmc->caps |= MMC_CAP_SDIO_IRQ;
}
+ ret = mmc_add_host(mmc);
+ if (ret < 0)
+ goto mmc_add_host_fail;
+
rename_region(mem, mmc_hostname(mmc));
if (mmc->caps & MMC_CAP_8_BIT_DATA)
@@ -1324,8 +1324,6 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
return 0;
-request_irq_fail:
- mmc_remove_host(mmc);
mmc_add_host_fail:
mmc_davinci_cpufreq_deregister(host);
cpu_freq_fail:
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 261344d3a8cf..4b76b997ddc1 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -141,6 +141,7 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) {
/* Quirk needed for certain Exynos SoCs */
host->quirks |= DW_MMC_QUIRK_FIFO64_32;
+ host->dma_threshold = 512;
}
if (priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) {
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 20193ee7b73e..d734d010444d 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -40,7 +40,6 @@
SDMMC_INT_RESP_ERR | SDMMC_INT_HLE)
#define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \
DW_MCI_CMD_ERROR_FLAGS)
-#define DW_MCI_DMA_THRESHOLD 16
#define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */
#define DW_MCI_FREQ_MIN 100000 /* unit: HZ */
@@ -491,12 +490,12 @@ static int dw_mci_idmac_init(struct dw_mci *host)
if (host->dma_64bit_address == 1) {
struct idmac_desc_64addr *p;
- /* Number of descriptors in the ring buffer */
- host->ring_size =
+
+ host->desc_num =
DESC_RING_BUF_SZ / sizeof(struct idmac_desc_64addr);
/* Forward link the descriptor list */
- for (i = 0, p = host->sg_cpu; i < host->ring_size - 1;
+ for (i = 0, p = host->sg_cpu; i < host->desc_num - 1;
i++, p++) {
p->des6 = (host->sg_dma +
(sizeof(struct idmac_desc_64addr) *
@@ -519,13 +518,13 @@ static int dw_mci_idmac_init(struct dw_mci *host)
} else {
struct idmac_desc *p;
- /* Number of descriptors in the ring buffer */
- host->ring_size =
+
+ host->desc_num =
DESC_RING_BUF_SZ / sizeof(struct idmac_desc);
/* Forward link the descriptor list */
for (i = 0, p = host->sg_cpu;
- i < host->ring_size - 1;
+ i < host->desc_num - 1;
i++, p++) {
p->des3 = cpu_to_le32(host->sg_dma +
(sizeof(struct idmac_desc) * (i + 1)));
@@ -821,7 +820,7 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host,
* non-word-aligned buffers or lengths. Also, we don't bother
* with all the DMA setup overhead for short transfers.
*/
- if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD)
+ if (data->blocks * data->blksz < host->dma_threshold)
return -EINVAL;
if (data->blksz & 3)
@@ -2858,10 +2857,10 @@ static int dw_mci_init_host(struct dw_mci *host)
/* Useful defaults if platform data is unset. */
if (host->use_dma == TRANS_MODE_IDMAC) {
- mmc->max_segs = host->ring_size;
+ mmc->max_segs = host->desc_num;
mmc->max_blk_size = 65535;
mmc->max_seg_size = 0x1000;
- mmc->max_req_size = mmc->max_seg_size * host->ring_size;
+ mmc->max_req_size = mmc->max_seg_size * host->desc_num;
mmc->max_blk_count = mmc->max_req_size / 512;
} else if (host->use_dma == TRANS_MODE_EDMAC) {
mmc->max_segs = 64;
@@ -3185,6 +3184,7 @@ struct dw_mci *dw_mci_alloc_host(struct device *dev)
host = mmc_priv(mmc);
host->mmc = mmc;
host->dev = dev;
+ host->dma_threshold = 16;
return host;
}
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 42e58be74ce0..9ffcd3946cff 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -78,8 +78,8 @@ struct dw_mci_dma_slave {
* @sg_cpu: Virtual address of DMA buffer.
* @dma_ops: Pointer to DMA callbacks.
* @cmd_status: Snapshot of SR taken upon completion of the current
- * @ring_size: Buffer size for idma descriptors.
* command. Only valid when EVENT_CMD_COMPLETE is pending.
+ * @desc_num: Number of idmac descriptors available.
* @dms: structure of slave-dma private data.
* @phy_regs: physical address of controller's register map
* @data_status: Snapshot of SR taken upon completion of the current
@@ -107,6 +107,7 @@ struct dw_mci_dma_slave {
* @ciu_clk: Pointer to card interface unit clock instance.
* @fifo_depth: depth of FIFO.
* @data_addr_override: override fifo reg offset with this value.
+ * @dma_threshold: data threshold value in bytes to carry out a DMA transfer.
* @wm_aligned: force fifo watermark equal with data length in PIO mode.
* Set as true if alignment is needed.
* @data_shift: log2 of FIFO item size.
@@ -163,6 +164,7 @@ struct dw_mci {
void __iomem *regs;
void __iomem *fifo_reg;
u32 data_addr_override;
+ u32 dma_threshold;
bool wm_aligned;
struct scatterlist *sg;
@@ -184,7 +186,7 @@ struct dw_mci {
void *sg_cpu;
const struct dw_mci_dma_ops *dma_ops;
/* For idmac */
- unsigned int ring_size;
+ unsigned short desc_num;
/* For edmac */
struct dw_mci_dma_slave *dms;
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 838248bf8dd6..024edc4e5fe6 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -279,6 +279,8 @@ static const struct renesas_sdhi_of_data_with_quirks of_rza2_compatible = {
static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
{ .compatible = "renesas,sdhi-r7s9210", .data = &of_rza2_compatible, },
{ .compatible = "renesas,sdhi-mmc-r8a77470", .data = &of_rcar_gen3_compatible, },
+ { .compatible = "renesas,sdhi-r8a774b1", .data = &of_r8a77965_compatible, },
+ { .compatible = "renesas,sdhi-r8a774c0", .data = &of_r8a77990_compatible, },
{ .compatible = "renesas,sdhi-r8a774e1", .data = &of_r8a7795_compatible, },
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_r8a7795_compatible, },
{ .compatible = "renesas,sdhi-r8a77961", .data = &of_r8a77961_compatible, },
@@ -286,6 +288,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
{ .compatible = "renesas,sdhi-r8a77970", .data = &of_r8a77970_compatible, },
{ .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, },
{ .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, },
+ { .compatible = "renesas,sdhi-r8a779md", .data = &of_rcar_gen3_nohs400_compatible, },
{ .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, },
{ .compatible = "renesas,sdhi-r9a09g057", .data = &of_rzg2l_compatible, },
{ .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, },
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index b9ecd91f44ad..eef53455b8ee 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -918,11 +918,9 @@ static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
return -ENOMEM;
priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
- if (IS_ERR(priv->reset)) {
- err = PTR_ERR(priv->reset);
- dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err);
- return err;
- }
+ if (IS_ERR(priv->reset))
+ return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->reset),
+ "failed to get reset control\n");
err = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
ARRAY_SIZE(clk_ids), clk_ids);
@@ -1779,10 +1777,8 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm
dwc_priv->priv = priv;
ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv);
- if (ret) {
- dev_err(dev, "failed to reset\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to reset\n");
ret = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
ARRAY_SIZE(clk_ids), clk_ids);
@@ -1790,16 +1786,14 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm
return ret;
ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args);
- if (ret) {
- dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Fail to parse 'eswin,hsp-sp-csr' phandle\n");
hsp_regmap = syscon_node_to_regmap(args.np);
if (IS_ERR(hsp_regmap)) {
- dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n");
of_node_put(args.np);
- return PTR_ERR(hsp_regmap);
+ return dev_err_probe(dev, PTR_ERR(hsp_regmap),
+ "Failed to get regmap for 'eswin,hsp-sp-csr'\n");
}
hsp_int_status = args.args[0];
hsp_pwr_ctrl = args.args[1];
@@ -2408,10 +2402,8 @@ static int dwcmshc_probe(struct platform_device *pdev)
u32 extra, caps;
pltfm_data = device_get_match_data(&pdev->dev);
- if (!pltfm_data) {
- dev_err(&pdev->dev, "Error: No device match data found\n");
- return -ENODEV;
- }
+ if (!pltfm_data)
+ return dev_err_probe(&pdev->dev, -ENODEV, "No device match data found\n");
host = sdhci_pltfm_init(pdev, &pltfm_data->pdata,
sizeof(struct dwcmshc_priv));
@@ -2564,8 +2556,7 @@ static int dwcmshc_suspend(struct device *dev)
return ret;
clk_disable_unprepare(pltfm_host->clk);
- if (!IS_ERR(priv->bus_clk))
- clk_disable_unprepare(priv->bus_clk);
+ clk_disable_unprepare(priv->bus_clk);
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
@@ -2608,8 +2599,7 @@ static int dwcmshc_resume(struct device *dev)
disable_other_clks:
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
disable_bus_clk:
- if (!IS_ERR(priv->bus_clk))
- clk_disable_unprepare(priv->bus_clk);
+ clk_disable_unprepare(priv->bus_clk);
disable_clk:
clk_disable_unprepare(pltfm_host->clk);
return ret;
diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c
index 455656f9842d..37b0911e7cf2 100644
--- a/drivers/mmc/host/sdhci-of-k1.c
+++ b/drivers/mmc/host/sdhci-of-k1.c
@@ -16,11 +16,19 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/reset.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include "sdhci.h"
#include "sdhci-pltfm.h"
+#define SPACEMIT_SDHC_OP_EXT_REG 0x108
+#define SDHC_OVRRD_CLK_OEN BIT(11)
+#define SDHC_FORCE_CLK_ON BIT(12)
+
+#define SPACEMIT_SDHC_LEGACY_CTRL_REG 0x10C
+#define SDHC_GEN_PAD_CLK_ON BIT(6)
+
#define SPACEMIT_SDHC_MMC_CTRL_REG 0x114
#define SDHC_MISC_INT_EN BIT(1)
#define SDHC_MISC_INT BIT(2)
@@ -61,9 +69,34 @@
#define SDHC_PHY_DRIVE_SEL GENMASK(2, 0)
#define SDHC_RX_BIAS_CTRL BIT(5)
+#define SPACEMIT_SDHC_RX_CFG_REG 0x118
+#define SDHC_RX_SDCLK_SEL0_MASK GENMASK(1, 0)
+#define SDHC_RX_SDCLK_SEL1_MASK GENMASK(3, 2)
+#define SDHC_RX_SDCLK_SEL1 FIELD_PREP(SDHC_RX_SDCLK_SEL1_MASK, 1)
+
+#define SPACEMIT_SDHC_DLINE_CTRL_REG 0x130
+#define SDHC_DLINE_PU BIT(0)
+#define SDHC_RX_DLINE_CODE_MASK GENMASK(23, 16)
+#define SDHC_TX_DLINE_CODE_MASK GENMASK(31, 24)
+
+#define SPACEMIT_SDHC_DLINE_CFG_REG 0x134
+#define SDHC_RX_DLINE_REG_MASK GENMASK(7, 0)
+#define SDHC_RX_DLINE_GAIN BIT(8)
+#define SDHC_TX_DLINE_REG_MASK GENMASK(23, 16)
+
+#define SPACEMIT_RX_DLINE_REG 9
+#define SPACEMIT_RX_TUNE_DELAY_MIN 0x0
+#define SPACEMIT_RX_TUNE_DELAY_MAX 0xFF
+
+#define SPACEMIT_TX_TUNING_DLINE_REG 0x00
+#define SPACEMIT_TX_TUNING_DELAYCODE 127
+
struct spacemit_sdhci_host {
struct clk *clk_core;
struct clk *clk_io;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pinctrl_default;
+ struct pinctrl_state *pinctrl_uhs;
};
/* All helper functions will update clr/set while preserve rest bits */
@@ -85,6 +118,50 @@ static inline void spacemit_sdhci_clrsetbits(struct sdhci_host *host, u32 clr, u
sdhci_writel(host, val, reg);
}
+static void spacemit_sdhci_set_rx_delay(struct sdhci_host *host, u8 delay)
+{
+ spacemit_sdhci_clrsetbits(host, SDHC_RX_DLINE_CODE_MASK,
+ FIELD_PREP(SDHC_RX_DLINE_CODE_MASK, delay),
+ SPACEMIT_SDHC_DLINE_CTRL_REG);
+}
+
+static void spacemit_sdhci_set_tx_delay(struct sdhci_host *host, u8 delay)
+{
+ spacemit_sdhci_clrsetbits(host, SDHC_TX_DLINE_CODE_MASK,
+ FIELD_PREP(SDHC_TX_DLINE_CODE_MASK, delay),
+ SPACEMIT_SDHC_DLINE_CTRL_REG);
+}
+
+static void spacemit_sdhci_set_tx_dline_reg(struct sdhci_host *host, u8 dline_reg)
+{
+ spacemit_sdhci_clrsetbits(host, SDHC_TX_DLINE_REG_MASK,
+ FIELD_PREP(SDHC_TX_DLINE_REG_MASK, dline_reg),
+ SPACEMIT_SDHC_DLINE_CFG_REG);
+}
+
+static void spacemit_sdhci_tx_tuning_prepare(struct sdhci_host *host)
+{
+ spacemit_sdhci_setbits(host, SDHC_TX_MUX_SEL, SPACEMIT_SDHC_TX_CFG_REG);
+ spacemit_sdhci_setbits(host, SDHC_DLINE_PU, SPACEMIT_SDHC_DLINE_CTRL_REG);
+ udelay(5);
+}
+
+static void spacemit_sdhci_prepare_tuning(struct sdhci_host *host)
+{
+ spacemit_sdhci_clrsetbits(host, SDHC_RX_DLINE_REG_MASK,
+ FIELD_PREP(SDHC_RX_DLINE_REG_MASK, SPACEMIT_RX_DLINE_REG),
+ SPACEMIT_SDHC_DLINE_CFG_REG);
+
+ spacemit_sdhci_setbits(host, SDHC_DLINE_PU, SPACEMIT_SDHC_DLINE_CTRL_REG);
+ udelay(5);
+
+ spacemit_sdhci_clrsetbits(host, SDHC_RX_SDCLK_SEL1_MASK, SDHC_RX_SDCLK_SEL1,
+ SPACEMIT_SDHC_RX_CFG_REG);
+
+ if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200)
+ spacemit_sdhci_setbits(host, SDHC_HS200_USE_RFIFO, SPACEMIT_SDHC_PHY_FUNC_REG);
+}
+
static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask)
{
sdhci_reset(host, mask);
@@ -101,6 +178,12 @@ static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask)
if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC))
spacemit_sdhci_setbits(host, SDHC_MMC_CARD_MODE, SPACEMIT_SDHC_MMC_CTRL_REG);
+
+ spacemit_sdhci_setbits(host, SDHC_GEN_PAD_CLK_ON, SPACEMIT_SDHC_LEGACY_CTRL_REG);
+
+ if (host->mmc->caps2 & MMC_CAP2_NO_MMC)
+ spacemit_sdhci_setbits(host, SDHC_OVRRD_CLK_OEN | SDHC_FORCE_CLK_ON,
+ SPACEMIT_SDHC_OP_EXT_REG);
}
static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
@@ -174,6 +257,111 @@ static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host)
return clk_get_rate(pltfm_host->clk);
}
+static int spacemit_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ int current_len = 0, current_start = 0;
+ int max_pass_len = 0, max_pass_start = 0;
+ struct mmc_host *mmc = host->mmc;
+ struct mmc_ios ios = mmc->ios;
+ u8 final_delay;
+ int ret = 0;
+ int i;
+
+ /*
+ * Tuning is required for SDR50/SDR104, HS200/HS400 cards and
+ * if clock frequency is greater than 100MHz in these modes.
+ */
+ if (host->clock < 100 * 1000 * 1000 ||
+ !(ios.timing == MMC_TIMING_MMC_HS200 ||
+ ios.timing == MMC_TIMING_UHS_SDR50 ||
+ ios.timing == MMC_TIMING_UHS_SDR104))
+ return 0;
+
+ if (mmc->caps2 & MMC_CAP2_NO_MMC) {
+ spacemit_sdhci_set_tx_dline_reg(host, SPACEMIT_TX_TUNING_DLINE_REG);
+ spacemit_sdhci_set_tx_delay(host, SPACEMIT_TX_TUNING_DELAYCODE);
+ spacemit_sdhci_tx_tuning_prepare(host);
+
+ dev_dbg(mmc_dev(host->mmc), "TX tuning: dline_reg=%d, delaycode=%d\n",
+ SPACEMIT_TX_TUNING_DLINE_REG, SPACEMIT_TX_TUNING_DELAYCODE);
+ }
+
+ spacemit_sdhci_prepare_tuning(host);
+
+ for (i = SPACEMIT_RX_TUNE_DELAY_MIN; i <= SPACEMIT_RX_TUNE_DELAY_MAX; i++) {
+ spacemit_sdhci_set_rx_delay(host, i);
+ ret = mmc_send_tuning(host->mmc, opcode, NULL);
+
+ dev_dbg(mmc_dev(host->mmc), "RX delay %d: %s\n",
+ i, ret == 0 ? "pass" : "fail");
+
+ if (ret == 0) {
+ /* Test passed - extend current window */
+ if (current_len == 0)
+ current_start = i;
+ current_len++;
+ } else {
+ /* Test failed - check if current window is best so far */
+ if (current_len > max_pass_len) {
+ max_pass_len = current_len;
+ max_pass_start = current_start;
+ }
+ current_len = 0;
+ }
+ }
+
+ if (current_len > max_pass_len) {
+ max_pass_len = current_len;
+ max_pass_start = current_start;
+ }
+
+ if (max_pass_len < 3) {
+ dev_err(mmc_dev(host->mmc), "Tuning failed: no stable window found\n");
+ return -EIO;
+ }
+
+ final_delay = max_pass_start + max_pass_len / 2;
+ spacemit_sdhci_set_rx_delay(host, final_delay);
+ ret = mmc_send_tuning(host->mmc, opcode, NULL);
+ if (ret) {
+ u8 retry_delays[] = {
+ max_pass_start + max_pass_len / 4,
+ max_pass_start + (3 * max_pass_len) / 4,
+ max_pass_start,
+ max_pass_start + max_pass_len - 1
+ };
+ int retry_count = ARRAY_SIZE(retry_delays);
+
+ dev_warn(mmc_dev(mmc), "Primary delay %d failed, trying alternatives\n",
+ final_delay);
+
+ for (i = 0; i < retry_count; i++) {
+ if (retry_delays[i] >= SPACEMIT_RX_TUNE_DELAY_MIN &&
+ retry_delays[i] <= SPACEMIT_RX_TUNE_DELAY_MAX) {
+ spacemit_sdhci_set_rx_delay(host, retry_delays[i]);
+ ret = mmc_send_tuning(host->mmc, opcode, NULL);
+ if (!ret) {
+ final_delay = retry_delays[i];
+ dev_info(mmc_dev(mmc), "Retry successful with delay %d\n",
+ final_delay);
+ break;
+ }
+ }
+ }
+
+ if (ret) {
+ dev_err(mmc_dev(mmc), "All retry attempts failed\n");
+ return -EIO;
+ }
+ }
+
+ dev_dbg(mmc_dev(host->mmc),
+ "Tuning successful: window %d-%d, using delay %d\n",
+ max_pass_start, max_pass_start + max_pass_len - 1, final_delay);
+
+ return 0;
+}
+
static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -206,6 +394,46 @@ static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc)
SPACEMIT_SDHC_PHY_CTRL_REG);
}
+static int spacemit_sdhci_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 spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host);
+ struct pinctrl_state *state;
+ int ret;
+
+ ret = sdhci_start_signal_voltage_switch(mmc, ios);
+ if (ret)
+ return ret;
+
+ if (!sdhst->pinctrl)
+ return 0;
+
+ /* Select appropriate pinctrl state based on signal voltage */
+ switch (ios->signal_voltage) {
+ case MMC_SIGNAL_VOLTAGE_330:
+ state = sdhst->pinctrl_default;
+ break;
+ case MMC_SIGNAL_VOLTAGE_180:
+ state = sdhst->pinctrl_uhs;
+ break;
+ default:
+ dev_warn(mmc_dev(mmc), "unsupported voltage %d\n", ios->signal_voltage);
+ return 0;
+ }
+
+ ret = pinctrl_select_state(sdhst->pinctrl, state);
+ if (ret) {
+ dev_warn(mmc_dev(mmc), "failed to select pinctrl state: %d\n", ret);
+ return 0;
+ }
+ dev_dbg(mmc_dev(mmc), "switched to %s pinctrl state\n",
+ ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180 ? "UHS" : "default");
+
+ return 0;
+}
+
static inline int spacemit_sdhci_get_clocks(struct device *dev,
struct sdhci_pltfm_host *pltfm_host)
{
@@ -239,12 +467,37 @@ static inline int spacemit_sdhci_get_resets(struct device *dev)
return 0;
}
+static inline void spacemit_sdhci_get_pins(struct device *dev,
+ struct sdhci_pltfm_host *pltfm_host)
+{
+ struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host);
+
+ sdhst->pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(sdhst->pinctrl)) {
+ sdhst->pinctrl = NULL;
+ dev_dbg(dev, "pinctrl not available, voltage switching will work without it\n");
+ return;
+ }
+
+ sdhst->pinctrl_default = pinctrl_lookup_state(sdhst->pinctrl, "default");
+ if (IS_ERR(sdhst->pinctrl_default))
+ sdhst->pinctrl_default = NULL;
+
+ sdhst->pinctrl_uhs = pinctrl_lookup_state(sdhst->pinctrl, "uhs");
+ if (IS_ERR(sdhst->pinctrl_uhs))
+ sdhst->pinctrl_uhs = NULL;
+
+ dev_dbg(dev, "pinctrl setup: default=%p, uhs=%p\n",
+ sdhst->pinctrl_default, sdhst->pinctrl_uhs);
+}
+
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,
+ .platform_execute_tuning = spacemit_sdhci_execute_tuning,
};
static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = {
@@ -311,6 +564,10 @@ static int spacemit_sdhci_probe(struct platform_device *pdev)
host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
+ spacemit_sdhci_get_pins(dev, pltfm_host);
+
+ host->mmc_host_ops.start_signal_voltage_switch = spacemit_sdhci_start_signal_voltage_switch;
+
ret = spacemit_sdhci_get_clocks(dev, pltfm_host);
if (ret)
goto err_pltfm;
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index c1f7d5b37911..28ab2526dab1 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -1041,9 +1041,10 @@ static int tifm_sd_resume(struct tifm_dev *sock)
#endif /* CONFIG_PM */
-static struct tifm_device_id tifm_sd_id_tbl[] = {
+static const struct tifm_device_id tifm_sd_id_tbl[] = {
{ TIFM_TYPE_SD }, { }
};
+MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl);
static struct tifm_driver tifm_sd_driver = {
.driver = {
@@ -1070,7 +1071,6 @@ static void __exit tifm_sd_exit(void)
MODULE_AUTHOR("Alex Dubov");
MODULE_DESCRIPTION("TI FlashMedia SD driver");
MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl);
MODULE_VERSION(DRIVER_VERSION);
module_init(tifm_sd_init);
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index c628b3bbfd7a..8c049f8355cd 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -323,9 +323,8 @@ struct via_crdr_mmc_host {
#define VIA_CMD_TIMEOUT_MS 1000
static const struct pci_device_id via_ids[] = {
- {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_9530,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
- {0,}
+ { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_9530) },
+ { }
};
MODULE_DEVICE_TABLE(pci, via_ids);
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
index 1b1d691e19fc..489daee4f4fc 100644
--- a/drivers/mmc/host/wmt-sdmmc.c
+++ b/drivers/mmc/host/wmt-sdmmc.c
@@ -744,6 +744,7 @@ static const struct of_device_id wmt_mci_dt_ids[] = {
{ .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps },
{ /* Sentinel */ },
};
+MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids);
static int wmt_mci_probe(struct platform_device *pdev)
{
@@ -980,4 +981,3 @@ module_platform_driver(wmt_mci_driver);
MODULE_DESCRIPTION("Wondermedia MMC/SD Driver");
MODULE_AUTHOR("Tony Prisk");
MODULE_LICENSE("GPL v2");
-MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids);
diff --git a/include/linux/tifm.h b/include/linux/tifm.h
index 44073d06710f..752fcfae27fe 100644
--- a/include/linux/tifm.h
+++ b/include/linux/tifm.h
@@ -97,7 +97,7 @@ struct tifm_dev {
};
struct tifm_driver {
- struct tifm_device_id *id_table;
+ const struct tifm_device_id *id_table;
int (*probe)(struct tifm_dev *dev);
void (*remove)(struct tifm_dev *dev);
int (*suspend)(struct tifm_dev *dev,