2019-05-06 04:13:14 +00:00
|
|
|
From 6ca94d2e7dc72b21703e6d9be4e8ec3ad4a26f41 Mon Sep 17 00:00:00 2001
|
2018-11-02 03:21:57 +00:00
|
|
|
From: Biwen Li <biwen.li@nxp.com>
|
2019-05-06 04:13:14 +00:00
|
|
|
Date: Wed, 17 Apr 2019 18:59:02 +0800
|
|
|
|
Subject: [PATCH] sdhc: support layerscape
|
|
|
|
|
2018-11-02 03:21:57 +00:00
|
|
|
This is an integrated patch of sdhc for layerscape
|
|
|
|
|
|
|
|
Signed-off-by: Biwen Li <biwen.li@nxp.com>
|
2019-05-06 04:13:14 +00:00
|
|
|
Signed-off-by: Mathew McBride <matt@traverse.com.au>
|
|
|
|
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
|
|
|
|
Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
|
|
|
|
Signed-off-by: Yinbo Zhu <yinbo.zhu@nxp.com>
|
2018-11-02 03:21:57 +00:00
|
|
|
---
|
2019-05-06 04:13:14 +00:00
|
|
|
drivers/mmc/core/mmc.c | 3 +
|
|
|
|
drivers/mmc/host/sdhci-esdhc.h | 25 +++
|
|
|
|
drivers/mmc/host/sdhci-of-esdhc.c | 270 ++++++++++++++++++++++++++----
|
|
|
|
drivers/mmc/host/sdhci.c | 9 +-
|
|
|
|
drivers/mmc/host/sdhci.h | 1 +
|
|
|
|
include/linux/mmc/card.h | 1 +
|
|
|
|
include/linux/mmc/host.h | 2 +
|
|
|
|
7 files changed, 272 insertions(+), 39 deletions(-)
|
2018-11-02 03:21:57 +00:00
|
|
|
|
2019-05-06 04:13:14 +00:00
|
|
|
--- a/drivers/mmc/core/mmc.c
|
|
|
|
+++ b/drivers/mmc/core/mmc.c
|
2020-10-07 14:53:22 +00:00
|
|
|
@@ -1173,6 +1173,9 @@ static int mmc_select_hs400(struct mmc_c
|
2019-05-06 04:13:14 +00:00
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
/* Switch card to DDR */
|
|
|
|
+ if (host->ops->prepare_ddr_to_hs400)
|
|
|
|
+ host->ops->prepare_ddr_to_hs400(host);
|
|
|
|
+
|
|
|
|
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
|
|
|
EXT_CSD_BUS_WIDTH,
|
|
|
|
EXT_CSD_DDR_BUS_WIDTH_8,
|
|
|
|
--- a/drivers/mmc/host/sdhci-esdhc.h
|
|
|
|
+++ b/drivers/mmc/host/sdhci-esdhc.h
|
|
|
|
@@ -59,7 +59,32 @@
|
|
|
|
|
|
|
|
/* Tuning Block Control Register */
|
|
|
|
#define ESDHC_TBCTL 0x120
|
|
|
|
+#define ESDHC_HS400_WNDW_ADJUST 0x00000040
|
|
|
|
+#define ESDHC_HS400_MODE 0x00000010
|
|
|
|
#define ESDHC_TB_EN 0x00000004
|
|
|
|
+#define ESDHC_TBPTR 0x128
|
|
|
|
+
|
|
|
|
+/* SD Clock Control Register */
|
|
|
|
+#define ESDHC_SDCLKCTL 0x144
|
|
|
|
+#define ESDHC_LPBK_CLK_SEL 0x80000000
|
|
|
|
+#define ESDHC_CMD_CLK_CTL 0x00008000
|
|
|
|
+
|
|
|
|
+/* SD Timing Control Register */
|
|
|
|
+#define ESDHC_SDTIMNGCTL 0x148
|
|
|
|
+#define ESDHC_FLW_CTL_BG 0x00008000
|
|
|
|
+
|
|
|
|
+/* DLL Config 0 Register */
|
|
|
|
+#define ESDHC_DLLCFG0 0x160
|
|
|
|
+#define ESDHC_DLL_ENABLE 0x80000000
|
|
|
|
+#define ESDHC_DLL_FREQ_SEL 0x08000000
|
|
|
|
+
|
|
|
|
+/* DLL Config 1 Register */
|
|
|
|
+#define ESDHC_DLLCFG1 0x164
|
|
|
|
+#define ESDHC_DLL_PD_PULSE_STRETCH_SEL 0x80000000
|
|
|
|
+
|
|
|
|
+/* DLL Status 0 Register */
|
|
|
|
+#define ESDHC_DLLSTAT0 0x170
|
|
|
|
+#define ESDHC_DLL_STS_SLV_LOCK 0x08000000
|
|
|
|
|
|
|
|
/* Control Register for DMA transfer */
|
|
|
|
#define ESDHC_DMA_SYSCTL 0x40c
|
2018-11-02 03:21:57 +00:00
|
|
|
--- a/drivers/mmc/host/sdhci-of-esdhc.c
|
|
|
|
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
|
2019-05-06 04:13:14 +00:00
|
|
|
@@ -30,11 +30,61 @@
|
2018-11-02 03:21:57 +00:00
|
|
|
#define VENDOR_V_22 0x12
|
|
|
|
#define VENDOR_V_23 0x13
|
|
|
|
|
|
|
|
+#define MMC_TIMING_NUM (MMC_TIMING_MMC_HS400 + 1)
|
|
|
|
+
|
|
|
|
+struct esdhc_clk_fixup {
|
|
|
|
+ const unsigned int sd_dflt_max_clk;
|
|
|
|
+ const unsigned int max_clk[MMC_TIMING_NUM];
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct esdhc_clk_fixup ls1021a_esdhc_clk = {
|
|
|
|
+ .sd_dflt_max_clk = 25000000,
|
|
|
|
+ .max_clk[MMC_TIMING_MMC_HS] = 46500000,
|
|
|
|
+ .max_clk[MMC_TIMING_SD_HS] = 46500000,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct esdhc_clk_fixup ls1046a_esdhc_clk = {
|
|
|
|
+ .sd_dflt_max_clk = 25000000,
|
|
|
|
+ .max_clk[MMC_TIMING_UHS_SDR104] = 167000000,
|
|
|
|
+ .max_clk[MMC_TIMING_MMC_HS200] = 167000000,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct esdhc_clk_fixup ls1012a_esdhc_clk = {
|
|
|
|
+ .sd_dflt_max_clk = 25000000,
|
|
|
|
+ .max_clk[MMC_TIMING_UHS_SDR104] = 125000000,
|
|
|
|
+ .max_clk[MMC_TIMING_MMC_HS200] = 125000000,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct esdhc_clk_fixup p1010_esdhc_clk = {
|
|
|
|
+ .sd_dflt_max_clk = 20000000,
|
|
|
|
+ .max_clk[MMC_TIMING_LEGACY] = 20000000,
|
|
|
|
+ .max_clk[MMC_TIMING_MMC_HS] = 42000000,
|
|
|
|
+ .max_clk[MMC_TIMING_SD_HS] = 40000000,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct of_device_id sdhci_esdhc_of_match[] = {
|
|
|
|
+ { .compatible = "fsl,ls1021a-esdhc", .data = &ls1021a_esdhc_clk},
|
|
|
|
+ { .compatible = "fsl,ls1046a-esdhc", .data = &ls1046a_esdhc_clk},
|
|
|
|
+ { .compatible = "fsl,ls1012a-esdhc", .data = &ls1012a_esdhc_clk},
|
|
|
|
+ { .compatible = "fsl,p1010-esdhc", .data = &p1010_esdhc_clk},
|
|
|
|
+ { .compatible = "fsl,mpc8379-esdhc" },
|
|
|
|
+ { .compatible = "fsl,mpc8536-esdhc" },
|
|
|
|
+ { .compatible = "fsl,esdhc" },
|
|
|
|
+ { }
|
|
|
|
+};
|
|
|
|
+MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match);
|
|
|
|
+
|
|
|
|
struct sdhci_esdhc {
|
|
|
|
u8 vendor_ver;
|
|
|
|
u8 spec_ver;
|
|
|
|
bool quirk_incorrect_hostver;
|
2019-05-06 04:13:14 +00:00
|
|
|
+ bool quirk_limited_clk_division;
|
|
|
|
+ bool quirk_unreliable_pulse_detection;
|
|
|
|
+ bool quirk_fixup_tuning;
|
|
|
|
+ bool quirk_incorrect_delay_chain;
|
2018-11-02 03:21:57 +00:00
|
|
|
unsigned int peripheral_clock;
|
|
|
|
+ const struct esdhc_clk_fixup *clk_fixup;
|
2019-05-06 04:13:14 +00:00
|
|
|
+ u32 div_ratio;
|
2018-11-02 03:21:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2019-10-14 12:33:42 +00:00
|
|
|
@@ -500,13 +550,20 @@ static void esdhc_clock_enable(struct sd
|
2019-05-06 04:13:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+static struct soc_device_attribute soc_incorrect_delay_chain[] = {
|
|
|
|
+ { .family = "QorIQ LX2160A", .revision = "1.0", },
|
|
|
|
+ { },
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|
|
|
{
|
|
|
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
|
|
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
2018-11-02 03:21:57 +00:00
|
|
|
int pre_div = 1;
|
|
|
|
int div = 1;
|
2019-05-06 04:13:14 +00:00
|
|
|
+ int division;
|
2018-11-02 03:21:57 +00:00
|
|
|
ktime_t timeout;
|
|
|
|
+ long fixup = 0;
|
|
|
|
u32 temp;
|
|
|
|
|
|
|
|
host->mmc->actual_clock = 0;
|
2019-10-14 12:33:42 +00:00
|
|
|
@@ -520,27 +577,14 @@ static void esdhc_of_set_clock(struct sd
|
2018-11-02 03:21:57 +00:00
|
|
|
if (esdhc->vendor_ver < VENDOR_V_23)
|
|
|
|
pre_div = 2;
|
|
|
|
|
|
|
|
- /*
|
|
|
|
- * Limit SD clock to 167MHz for ls1046a according to its datasheet
|
|
|
|
- */
|
|
|
|
- if (clock > 167000000 &&
|
|
|
|
- of_find_compatible_node(NULL, NULL, "fsl,ls1046a-esdhc"))
|
|
|
|
- clock = 167000000;
|
|
|
|
+ if (host->mmc->card && mmc_card_sd(host->mmc->card) &&
|
|
|
|
+ esdhc->clk_fixup && host->mmc->ios.timing == MMC_TIMING_LEGACY)
|
|
|
|
+ fixup = esdhc->clk_fixup->sd_dflt_max_clk;
|
|
|
|
+ else if (esdhc->clk_fixup)
|
|
|
|
+ fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing];
|
|
|
|
|
|
|
|
- /*
|
|
|
|
- * Limit SD clock to 125MHz for ls1012a according to its datasheet
|
|
|
|
- */
|
|
|
|
- if (clock > 125000000 &&
|
|
|
|
- of_find_compatible_node(NULL, NULL, "fsl,ls1012a-esdhc"))
|
|
|
|
- clock = 125000000;
|
|
|
|
-
|
|
|
|
- /* Workaround to reduce the clock frequency for p1010 esdhc */
|
|
|
|
- if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
|
|
|
|
- if (clock > 20000000)
|
|
|
|
- clock -= 5000000;
|
|
|
|
- if (clock > 40000000)
|
|
|
|
- clock -= 5000000;
|
|
|
|
- }
|
|
|
|
+ if (fixup && clock > fixup)
|
|
|
|
+ clock = fixup;
|
|
|
|
|
|
|
|
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
|
|
|
temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
|
2019-10-14 12:33:42 +00:00
|
|
|
@@ -553,9 +597,30 @@ static void esdhc_of_set_clock(struct sd
|
2019-05-06 04:13:14 +00:00
|
|
|
while (host->max_clk / pre_div / div > clock && div < 16)
|
|
|
|
div++;
|
|
|
|
|
|
|
|
+ if (esdhc->quirk_limited_clk_division &&
|
|
|
|
+ clock == MMC_HS200_MAX_DTR &&
|
|
|
|
+ (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 ||
|
|
|
|
+ host->flags & SDHCI_HS400_TUNING)) {
|
|
|
|
+ division = pre_div * div;
|
|
|
|
+ if (division <= 4) {
|
|
|
|
+ pre_div = 4;
|
|
|
|
+ div = 1;
|
|
|
|
+ } else if (division <= 8) {
|
|
|
|
+ pre_div = 4;
|
|
|
|
+ div = 2;
|
|
|
|
+ } else if (division <= 12) {
|
|
|
|
+ pre_div = 4;
|
|
|
|
+ div = 3;
|
|
|
|
+ } else {
|
|
|
|
+ pr_warn("%s: using upsupported clock division.\n",
|
|
|
|
+ mmc_hostname(host->mmc));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
|
|
|
|
clock, host->max_clk / pre_div / div);
|
|
|
|
host->mmc->actual_clock = host->max_clk / pre_div / div;
|
|
|
|
+ esdhc->div_ratio = pre_div * div;
|
|
|
|
pre_div >>= 1;
|
|
|
|
div--;
|
|
|
|
|
2019-10-14 12:33:42 +00:00
|
|
|
@@ -565,6 +630,29 @@ static void esdhc_of_set_clock(struct sd
|
2019-05-06 04:13:14 +00:00
|
|
|
| (pre_div << ESDHC_PREDIV_SHIFT));
|
|
|
|
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
|
|
|
|
|
|
|
+ if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 &&
|
|
|
|
+ clock == MMC_HS200_MAX_DTR) {
|
|
|
|
+ temp = sdhci_readl(host, ESDHC_TBCTL);
|
|
|
|
+ sdhci_writel(host, temp | ESDHC_HS400_MODE, ESDHC_TBCTL);
|
|
|
|
+ temp = sdhci_readl(host, ESDHC_SDCLKCTL);
|
|
|
|
+ sdhci_writel(host, temp | ESDHC_CMD_CLK_CTL, ESDHC_SDCLKCTL);
|
|
|
|
+ esdhc_clock_enable(host, true);
|
|
|
|
+
|
|
|
|
+ temp = sdhci_readl(host, ESDHC_DLLCFG0);
|
|
|
|
+ temp |= ESDHC_DLL_ENABLE;
|
|
|
|
+ if (host->mmc->actual_clock == MMC_HS200_MAX_DTR ||
|
|
|
|
+ esdhc->quirk_incorrect_delay_chain == false)
|
|
|
|
+ temp |= ESDHC_DLL_FREQ_SEL;
|
|
|
|
+ sdhci_writel(host, temp, ESDHC_DLLCFG0);
|
|
|
|
+ temp = sdhci_readl(host, ESDHC_TBCTL);
|
|
|
|
+ sdhci_writel(host, temp | ESDHC_HS400_WNDW_ADJUST, ESDHC_TBCTL);
|
|
|
|
+
|
|
|
|
+ esdhc_clock_enable(host, false);
|
|
|
|
+ temp = sdhci_readl(host, ESDHC_DMA_SYSCTL);
|
|
|
|
+ temp |= ESDHC_FLUSH_ASYNC_FIFO;
|
|
|
|
+ sdhci_writel(host, temp, ESDHC_DMA_SYSCTL);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
/* Wait max 20 ms */
|
|
|
|
timeout = ktime_add_ms(ktime_get(), 20);
|
|
|
|
while (1) {
|
2019-10-14 12:33:42 +00:00
|
|
|
@@ -580,6 +668,7 @@ static void esdhc_of_set_clock(struct sd
|
2019-05-06 04:13:14 +00:00
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
+ temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
|
|
|
temp |= ESDHC_CLOCK_SDCLKEN;
|
|
|
|
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
|
|
|
}
|
2019-10-14 12:33:42 +00:00
|
|
|
@@ -608,6 +697,8 @@ static void esdhc_pltfm_set_bus_width(st
|
2019-05-06 04:13:14 +00:00
|
|
|
|
|
|
|
static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
|
|
|
{
|
|
|
|
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
|
|
+ struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
sdhci_reset(host, mask);
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -619,6 +710,12 @@ static void esdhc_reset(struct sdhci_hos
|
2019-05-06 04:13:14 +00:00
|
|
|
val = sdhci_readl(host, ESDHC_TBCTL);
|
|
|
|
val &= ~ESDHC_TB_EN;
|
|
|
|
sdhci_writel(host, val, ESDHC_TBCTL);
|
|
|
|
+
|
|
|
|
+ if (esdhc->quirk_unreliable_pulse_detection) {
|
|
|
|
+ val = sdhci_readl(host, ESDHC_DLLCFG1);
|
|
|
|
+ val &= ~ESDHC_DLL_PD_PULSE_STRETCH_SEL;
|
|
|
|
+ sdhci_writel(host, val, ESDHC_DLLCFG1);
|
|
|
|
+ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -630,6 +727,7 @@ static void esdhc_reset(struct sdhci_hos
|
2019-05-06 04:13:14 +00:00
|
|
|
static const struct of_device_id scfg_device_ids[] = {
|
|
|
|
{ .compatible = "fsl,t1040-scfg", },
|
|
|
|
{ .compatible = "fsl,ls1012a-scfg", },
|
|
|
|
+ { .compatible = "fsl,ls1043a-scfg", },
|
|
|
|
{ .compatible = "fsl,ls1046a-scfg", },
|
|
|
|
{}
|
|
|
|
};
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -692,23 +790,91 @@ static int esdhc_signal_voltage_switch(s
|
2019-05-06 04:13:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
-static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
|
|
+static struct soc_device_attribute soc_fixup_tuning[] = {
|
|
|
|
+ { .family = "QorIQ T1040", .revision = "1.0", },
|
|
|
|
+ { .family = "QorIQ T2080", .revision = "1.0", },
|
|
|
|
+ { .family = "QorIQ T1023", .revision = "1.0", },
|
|
|
|
+ { .family = "QorIQ LS1021A", .revision = "1.0", },
|
|
|
|
+ { .family = "QorIQ LS1080A", .revision = "1.0", },
|
|
|
|
+ { .family = "QorIQ LS2080A", .revision = "1.0", },
|
|
|
|
+ { .family = "QorIQ LS1012A", .revision = "1.0", },
|
|
|
|
+ { .family = "QorIQ LS1043A", .revision = "1.*", },
|
|
|
|
+ { .family = "QorIQ LS1046A", .revision = "1.0", },
|
|
|
|
+ { },
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable)
|
|
|
|
{
|
|
|
|
- struct sdhci_host *host = mmc_priv(mmc);
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
- /* Use tuning block for tuning procedure */
|
|
|
|
esdhc_clock_enable(host, false);
|
|
|
|
+
|
|
|
|
val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
|
|
|
|
val |= ESDHC_FLUSH_ASYNC_FIFO;
|
|
|
|
sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
|
|
|
|
|
|
|
|
val = sdhci_readl(host, ESDHC_TBCTL);
|
|
|
|
- val |= ESDHC_TB_EN;
|
|
|
|
+ if (enable)
|
|
|
|
+ val |= ESDHC_TB_EN;
|
|
|
|
+ else
|
|
|
|
+ val &= ~ESDHC_TB_EN;
|
|
|
|
sdhci_writel(host, val, ESDHC_TBCTL);
|
|
|
|
+
|
|
|
|
esdhc_clock_enable(host, true);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
|
|
+{
|
|
|
|
+ struct sdhci_host *host = mmc_priv(mmc);
|
|
|
|
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
|
|
|
+ struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
|
|
|
+ bool hs400_tuning;
|
|
|
|
+ u32 val;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (esdhc->quirk_limited_clk_division &&
|
|
|
|
+ host->flags & SDHCI_HS400_TUNING)
|
|
|
|
+ esdhc_of_set_clock(host, host->clock);
|
|
|
|
+
|
|
|
|
+ esdhc_tuning_block_enable(host, true);
|
|
|
|
+
|
|
|
|
+ hs400_tuning = host->flags & SDHCI_HS400_TUNING;
|
|
|
|
+ ret = sdhci_execute_tuning(mmc, opcode);
|
|
|
|
+
|
|
|
|
+ if (hs400_tuning) {
|
|
|
|
+ val = sdhci_readl(host, ESDHC_SDTIMNGCTL);
|
|
|
|
+ val |= ESDHC_FLW_CTL_BG;
|
|
|
|
+ sdhci_writel(host, val, ESDHC_SDTIMNGCTL);
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return sdhci_execute_tuning(mmc, opcode);
|
|
|
|
+ if (host->tuning_err == -EAGAIN && esdhc->quirk_fixup_tuning) {
|
|
|
|
+
|
|
|
|
+ /* program TBPTR[TB_WNDW_END_PTR] = 3*DIV_RATIO and
|
|
|
|
+ * program TBPTR[TB_WNDW_START_PTR] = 5*DIV_RATIO
|
|
|
|
+ */
|
|
|
|
+ val = sdhci_readl(host, ESDHC_TBPTR);
|
|
|
|
+ val = (val & ~((0x7f << 8) | 0x7f)) |
|
|
|
|
+ (3 * esdhc->div_ratio) | ((5 * esdhc->div_ratio) << 8);
|
|
|
|
+ sdhci_writel(host, val, ESDHC_TBPTR);
|
|
|
|
+
|
|
|
|
+ /* program the software tuning mode by setting
|
|
|
|
+ * TBCTL[TB_MODE]=2'h3
|
|
|
|
+ */
|
|
|
|
+ val = sdhci_readl(host, ESDHC_TBCTL);
|
|
|
|
+ val |= 0x3;
|
|
|
|
+ sdhci_writel(host, val, ESDHC_TBCTL);
|
|
|
|
+ sdhci_execute_tuning(mmc, opcode);
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void esdhc_set_uhs_signaling(struct sdhci_host *host,
|
|
|
|
+ unsigned int timing)
|
|
|
|
+{
|
|
|
|
+ if (timing == MMC_TIMING_MMC_HS400)
|
|
|
|
+ esdhc_tuning_block_enable(host, true);
|
|
|
|
+ else
|
|
|
|
+ sdhci_set_uhs_signaling(host, timing);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -757,7 +923,7 @@ static const struct sdhci_ops sdhci_esdh
|
2019-05-06 04:13:14 +00:00
|
|
|
.adma_workaround = esdhc_of_adma_workaround,
|
|
|
|
.set_bus_width = esdhc_pltfm_set_bus_width,
|
|
|
|
.reset = esdhc_reset,
|
|
|
|
- .set_uhs_signaling = sdhci_set_uhs_signaling,
|
|
|
|
+ .set_uhs_signaling = esdhc_set_uhs_signaling,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct sdhci_ops sdhci_esdhc_le_ops = {
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -774,7 +940,7 @@ static const struct sdhci_ops sdhci_esdh
|
2019-05-06 04:13:14 +00:00
|
|
|
.adma_workaround = esdhc_of_adma_workaround,
|
|
|
|
.set_bus_width = esdhc_pltfm_set_bus_width,
|
|
|
|
.reset = esdhc_reset,
|
|
|
|
- .set_uhs_signaling = sdhci_set_uhs_signaling,
|
|
|
|
+ .set_uhs_signaling = esdhc_set_uhs_signaling,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = {
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -800,8 +966,20 @@ static struct soc_device_attribute soc_i
|
2019-05-06 04:13:14 +00:00
|
|
|
{ },
|
|
|
|
};
|
2018-11-02 03:21:57 +00:00
|
|
|
|
2019-05-06 04:13:14 +00:00
|
|
|
+static struct soc_device_attribute soc_fixup_sdhc_clkdivs[] = {
|
|
|
|
+ { .family = "QorIQ LX2160A", .revision = "1.0", },
|
|
|
|
+ { .family = "QorIQ LX2160A", .revision = "2.0", },
|
|
|
|
+ { },
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static struct soc_device_attribute soc_unreliable_pulse_detection[] = {
|
|
|
|
+ { .family = "QorIQ LX2160A", .revision = "1.0", },
|
|
|
|
+ { },
|
|
|
|
+};
|
|
|
|
+
|
2018-11-02 03:21:57 +00:00
|
|
|
static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
|
|
|
|
{
|
|
|
|
+ const struct of_device_id *match;
|
|
|
|
struct sdhci_pltfm_host *pltfm_host;
|
|
|
|
struct sdhci_esdhc *esdhc;
|
|
|
|
struct device_node *np;
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -821,6 +999,24 @@ static void esdhc_init(struct platform_d
|
2018-11-02 03:21:57 +00:00
|
|
|
else
|
|
|
|
esdhc->quirk_incorrect_hostver = false;
|
|
|
|
|
2019-05-06 04:13:14 +00:00
|
|
|
+ if (soc_device_match(soc_fixup_sdhc_clkdivs))
|
|
|
|
+ esdhc->quirk_limited_clk_division = true;
|
|
|
|
+ else
|
|
|
|
+ esdhc->quirk_limited_clk_division = false;
|
|
|
|
+
|
|
|
|
+ if (soc_device_match(soc_unreliable_pulse_detection))
|
|
|
|
+ esdhc->quirk_unreliable_pulse_detection = true;
|
|
|
|
+ else
|
|
|
|
+ esdhc->quirk_unreliable_pulse_detection = false;
|
|
|
|
+
|
|
|
|
+ if (soc_device_match(soc_incorrect_delay_chain))
|
|
|
|
+ esdhc->quirk_incorrect_delay_chain = true;
|
|
|
|
+ else
|
|
|
|
+ esdhc->quirk_incorrect_delay_chain = false;
|
|
|
|
+
|
2018-11-02 03:21:57 +00:00
|
|
|
+ match = of_match_node(sdhci_esdhc_of_match, pdev->dev.of_node);
|
|
|
|
+ if (match)
|
|
|
|
+ esdhc->clk_fixup = match->data;
|
|
|
|
np = pdev->dev.of_node;
|
|
|
|
clk = of_clk_get(np, 0);
|
|
|
|
if (!IS_ERR(clk)) {
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -848,6 +1044,12 @@ static void esdhc_init(struct platform_d
|
2019-05-06 04:13:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+static int esdhc_prepare_ddr_to_hs400(struct mmc_host *mmc)
|
|
|
|
+{
|
|
|
|
+ esdhc_tuning_block_enable(mmc_priv(mmc), false);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static int sdhci_esdhc_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct sdhci_host *host;
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -871,6 +1073,7 @@ static int sdhci_esdhc_probe(struct plat
|
2019-05-06 04:13:14 +00:00
|
|
|
host->mmc_host_ops.start_signal_voltage_switch =
|
|
|
|
esdhc_signal_voltage_switch;
|
|
|
|
host->mmc_host_ops.execute_tuning = esdhc_execute_tuning;
|
|
|
|
+ host->mmc_host_ops.prepare_ddr_to_hs400 = esdhc_prepare_ddr_to_hs400;
|
|
|
|
host->tuning_delay = 1;
|
|
|
|
|
|
|
|
esdhc_init(pdev, host);
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -879,6 +1082,11 @@ static int sdhci_esdhc_probe(struct plat
|
2019-05-06 04:13:14 +00:00
|
|
|
|
|
|
|
pltfm_host = sdhci_priv(host);
|
|
|
|
esdhc = sdhci_pltfm_priv(pltfm_host);
|
|
|
|
+ if (soc_device_match(soc_fixup_tuning))
|
|
|
|
+ esdhc->quirk_fixup_tuning = true;
|
|
|
|
+ else
|
|
|
|
+ esdhc->quirk_fixup_tuning = false;
|
|
|
|
+
|
|
|
|
if (esdhc->vendor_ver == VENDOR_V_22)
|
|
|
|
host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23;
|
|
|
|
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -925,14 +1133,6 @@ static int sdhci_esdhc_probe(struct plat
|
2018-11-02 03:21:57 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
-static const struct of_device_id sdhci_esdhc_of_match[] = {
|
|
|
|
- { .compatible = "fsl,mpc8379-esdhc" },
|
|
|
|
- { .compatible = "fsl,mpc8536-esdhc" },
|
|
|
|
- { .compatible = "fsl,esdhc" },
|
|
|
|
- { }
|
|
|
|
-};
|
|
|
|
-MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match);
|
|
|
|
-
|
|
|
|
static struct platform_driver sdhci_esdhc_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = "sdhci-esdhc",
|
2019-05-06 04:13:14 +00:00
|
|
|
--- a/drivers/mmc/host/sdhci.c
|
|
|
|
+++ b/drivers/mmc/host/sdhci.c
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -2148,7 +2148,7 @@ static void sdhci_send_tuning(struct sdh
|
2019-05-06 04:13:14 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
-static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|
|
|
+static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -2165,13 +2165,13 @@ static void __sdhci_execute_tuning(struc
|
|
|
|
pr_debug("%s: Tuning timeout, falling back to fixed sampling clock\n",
|
|
|
|
mmc_hostname(host->mmc));
|
2019-05-06 04:13:14 +00:00
|
|
|
sdhci_abort_tuning(host, opcode);
|
|
|
|
- return;
|
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
|
|
|
if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) {
|
|
|
|
if (ctrl & SDHCI_CTRL_TUNED_CLK)
|
|
|
|
- return; /* Success! */
|
|
|
|
+ return 0; /* Success! */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -2183,6 +2183,7 @@ static void __sdhci_execute_tuning(struc
|
2019-05-06 04:13:14 +00:00
|
|
|
pr_info("%s: Tuning failed, falling back to fixed sampling clock\n",
|
|
|
|
mmc_hostname(host->mmc));
|
|
|
|
sdhci_reset_tuning(host);
|
|
|
|
+ return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
2020-01-03 23:36:43 +00:00
|
|
|
@@ -2244,7 +2245,7 @@ int sdhci_execute_tuning(struct mmc_host
|
2019-05-06 04:13:14 +00:00
|
|
|
|
|
|
|
sdhci_start_tuning(host);
|
|
|
|
|
|
|
|
- __sdhci_execute_tuning(host, opcode);
|
|
|
|
+ host->tuning_err = __sdhci_execute_tuning(host, opcode);
|
|
|
|
|
|
|
|
sdhci_end_tuning(host);
|
|
|
|
out:
|
|
|
|
--- a/drivers/mmc/host/sdhci.h
|
|
|
|
+++ b/drivers/mmc/host/sdhci.h
|
|
|
|
@@ -545,6 +545,7 @@ struct sdhci_host {
|
|
|
|
|
|
|
|
unsigned int tuning_count; /* Timer count for re-tuning */
|
|
|
|
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
|
|
|
+ unsigned int tuning_err; /* Error code for re-tuning */
|
|
|
|
#define SDHCI_TUNING_MODE_1 0
|
|
|
|
#define SDHCI_TUNING_MODE_2 1
|
|
|
|
#define SDHCI_TUNING_MODE_3 2
|
|
|
|
--- a/include/linux/mmc/card.h
|
|
|
|
+++ b/include/linux/mmc/card.h
|
|
|
|
@@ -156,6 +156,7 @@ struct sd_switch_caps {
|
|
|
|
#define UHS_DDR50_MAX_DTR 50000000
|
|
|
|
#define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR
|
|
|
|
#define UHS_SDR12_MAX_DTR 25000000
|
|
|
|
+#define DEFAULT_SPEED_MAX_DTR UHS_SDR12_MAX_DTR
|
|
|
|
unsigned int sd3_bus_mode;
|
|
|
|
#define UHS_SDR12_BUS_SPEED 0
|
|
|
|
#define HIGH_SPEED_BUS_SPEED 1
|
|
|
|
--- a/include/linux/mmc/host.h
|
|
|
|
+++ b/include/linux/mmc/host.h
|
|
|
|
@@ -145,6 +145,8 @@ struct mmc_host_ops {
|
|
|
|
|
|
|
|
/* Prepare HS400 target operating frequency depending host driver */
|
|
|
|
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
|
|
|
+ int (*prepare_ddr_to_hs400)(struct mmc_host *host);
|
|
|
|
+
|
|
|
|
/* Prepare enhanced strobe depending host driver */
|
|
|
|
void (*hs400_enhanced_strobe)(struct mmc_host *host,
|
|
|
|
struct mmc_ios *ios);
|