mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 04:37:32 -04:00
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
...
This commit is contained in:
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, },
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user