summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/sdhci-cadence.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci-cadence.c')
-rw-r--r--drivers/mmc/host/sdhci-cadence.c119
1 files changed, 85 insertions, 34 deletions
diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
index b24aa27da50c..435603c8c00b 100644
--- a/drivers/mmc/host/sdhci-cadence.c
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -11,7 +11,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/platform_device.h>
#include <linux/reset.h>
#include "sdhci-pltfm.h"
@@ -36,6 +36,24 @@
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
#define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6
+/* Read block gap */
+#define SDHCI_CDNS_HRS37 0x94 /* interface mode select */
+#define SDHCI_CDNS_HRS37_MODE_DS 0x0
+#define SDHCI_CDNS_HRS37_MODE_HS 0x1
+#define SDHCI_CDNS_HRS37_MODE_UDS_SDR12 0x8
+#define SDHCI_CDNS_HRS37_MODE_UDS_SDR25 0x9
+#define SDHCI_CDNS_HRS37_MODE_UDS_SDR50 0xa
+#define SDHCI_CDNS_HRS37_MODE_UDS_SDR104 0xb
+#define SDHCI_CDNS_HRS37_MODE_UDS_DDR50 0xc
+#define SDHCI_CDNS_HRS37_MODE_MMC_LEGACY 0x20
+#define SDHCI_CDNS_HRS37_MODE_MMC_SDR 0x21
+#define SDHCI_CDNS_HRS37_MODE_MMC_DDR 0x22
+#define SDHCI_CDNS_HRS37_MODE_MMC_HS200 0x23
+#define SDHCI_CDNS_HRS37_MODE_MMC_HS400 0x24
+#define SDHCI_CDNS_HRS37_MODE_MMC_HS400ES 0x25
+#define SDHCI_CDNS_HRS38 0x98 /* Read block gap coefficient */
+#define SDHCI_CDNS_HRS38_BLKGAP_MAX 0xf
+
/* SRS - Slot Register Set (SDHCI-compatible) */
#define SDHCI_CDNS_SRS_BASE 0x200
@@ -144,7 +162,7 @@ static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
int i;
for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++)
- if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property))
+ if (of_property_present(np, sdhci_cdns_phy_cfgs[i].property))
count++;
return count;
@@ -251,6 +269,43 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
return 0;
}
+/**
+ * sdhci_cdns_tune_blkgap() - tune multi-block read gap
+ * @mmc: MMC host
+ *
+ * Tune delay used in multi block read. To do so,
+ * try sending multi-block read command with incremented gap, unless
+ * it succeeds.
+ *
+ * Return: error code
+ */
+static int sdhci_cdns_tune_blkgap(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ void __iomem *hrs37_reg = priv->hrs_addr + SDHCI_CDNS_HRS37;
+ void __iomem *hrs38_reg = priv->hrs_addr + SDHCI_CDNS_HRS38;
+ int ret;
+ u32 gap;
+
+ /* Currently only needed in HS200 mode */
+ if (host->timing != MMC_TIMING_MMC_HS200)
+ return 0;
+
+ writel(SDHCI_CDNS_HRS37_MODE_MMC_HS200, hrs37_reg);
+
+ for (gap = 0; gap <= SDHCI_CDNS_HRS38_BLKGAP_MAX; gap++) {
+ writel(gap, hrs38_reg);
+ ret = mmc_read_tuning(mmc, 512, 32);
+ if (!ret)
+ break;
+ }
+
+ dev_dbg(mmc_dev(mmc), "read block gap tune %s, gap %d\n", ret ? "failed" : "OK", gap);
+ return ret;
+}
+
/*
* In SD mode, software must not use the hardware tuning and instead perform
* an almost identical procedure to eMMC.
@@ -261,6 +316,7 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
int max_streak = 0;
int end_of_streak = 0;
int i;
+ int ret;
/*
* Do not execute tuning for UHS_SDR50 or UHS_DDR50.
@@ -288,7 +344,11 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
return -EIO;
}
- return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
+ ret = sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
+ if (ret)
+ return ret;
+
+ return sdhci_cdns_tune_blkgap(host->mmc);
}
static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
@@ -433,6 +493,13 @@ static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = {
},
};
+static const struct sdhci_cdns_drv_data sdhci_eyeq_drv_data = {
+ .pltfm_data = {
+ .ops = &sdhci_cdns_ops,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ },
+};
+
static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = {
.pltfm_data = {
.ops = &sdhci_cdns_ops,
@@ -487,14 +554,10 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT;
- clk = devm_clk_get(dev, NULL);
+ clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
- ret = clk_prepare_enable(clk);
- if (ret)
- return ret;
-
data = of_device_get_match_data(dev);
if (!data)
data = &sdhci_cdns_drv_data;
@@ -502,10 +565,8 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node);
host = sdhci_pltfm_init(pdev, &data->pltfm_data,
struct_size(priv, phy_params, nr_phy_params));
- if (IS_ERR(host)) {
- ret = PTR_ERR(host);
- goto disable_clk;
- }
+ if (IS_ERR(host))
+ return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
pltfm_host->clk = clk;
@@ -521,7 +582,7 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
if (data->init) {
ret = data->init(pdev);
if (ret)
- goto free;
+ return ret;
}
sdhci_enable_v4_mode(host);
__sdhci_read_caps(host, &version, NULL, NULL);
@@ -530,37 +591,26 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret)
- goto free;
+ return ret;
sdhci_cdns_phy_param_parse(dev->of_node, priv);
ret = sdhci_cdns_phy_init(priv);
if (ret)
- goto free;
+ return ret;
if (host->mmc->caps & MMC_CAP_HW_RESET) {
priv->rst_hw = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(priv->rst_hw))
return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw),
- "reset controller error\n");
+ "reset controller error\n");
if (priv->rst_hw)
host->mmc_host_ops.card_hw_reset = sdhci_cdns_mmc_hw_reset;
}
- ret = sdhci_add_host(host);
- if (ret)
- goto free;
-
- return 0;
-free:
- sdhci_pltfm_free(pdev);
-disable_clk:
- clk_disable_unprepare(clk);
-
- return ret;
+ return sdhci_add_host(host);
}
-#ifdef CONFIG_PM_SLEEP
static int sdhci_cdns_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@@ -587,11 +637,8 @@ disable_clk:
return ret;
}
-#endif
-static const struct dev_pm_ops sdhci_cdns_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_cdns_pm_ops, sdhci_pltfm_suspend, sdhci_cdns_resume);
static const struct of_device_id sdhci_cdns_match[] = {
{
@@ -602,6 +649,10 @@ static const struct of_device_id sdhci_cdns_match[] = {
.compatible = "amd,pensando-elba-sd4hc",
.data = &sdhci_elba_drv_data,
},
+ {
+ .compatible = "mobileye,eyeq-sd4hc",
+ .data = &sdhci_eyeq_drv_data,
+ },
{ .compatible = "cdns,sd4hc" },
{ /* sentinel */ }
};
@@ -611,11 +662,11 @@ static struct platform_driver sdhci_cdns_driver = {
.driver = {
.name = "sdhci-cdns",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &sdhci_cdns_pm_ops,
+ .pm = pm_sleep_ptr(&sdhci_cdns_pm_ops),
.of_match_table = sdhci_cdns_match,
},
.probe = sdhci_cdns_probe,
- .remove = sdhci_pltfm_unregister,
+ .remove = sdhci_pltfm_remove,
};
module_platform_driver(sdhci_cdns_driver);