aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/layerscape/patches-4.14/819-sdhc-support-layerscape.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/layerscape/patches-4.14/819-sdhc-support-layerscape.patch')
-rw-r--r--target/linux/layerscape/patches-4.14/819-sdhc-support-layerscape.patch449
1 files changed, 437 insertions, 12 deletions
diff --git a/target/linux/layerscape/patches-4.14/819-sdhc-support-layerscape.patch b/target/linux/layerscape/patches-4.14/819-sdhc-support-layerscape.patch
index f5fc0545be..b41fdca03c 100644
--- a/target/linux/layerscape/patches-4.14/819-sdhc-support-layerscape.patch
+++ b/target/linux/layerscape/patches-4.14/819-sdhc-support-layerscape.patch
@@ -1,18 +1,75 @@
-From f901f791d07deaeba6310ac070769575a0bb790a Mon Sep 17 00:00:00 2001
+From 6ca94d2e7dc72b21703e6d9be4e8ec3ad4a26f41 Mon Sep 17 00:00:00 2001
From: Biwen Li <biwen.li@nxp.com>
-Date: Tue, 30 Oct 2018 18:27:54 +0800
-Subject: [PATCH 36/40] sdhc: support layerscape
+Date: Wed, 17 Apr 2019 18:59:02 +0800
+Subject: [PATCH] sdhc: support layerscape
+
This is an integrated patch of sdhc for layerscape
-Signed-off-by: Yinbo Zhu <yinbo.zhu@nxp.com>
Signed-off-by: Biwen Li <biwen.li@nxp.com>
+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>
---
- drivers/mmc/host/sdhci-of-esdhc.c | 85 +++++++++++++++++++++----------
- 1 file changed, 57 insertions(+), 28 deletions(-)
+ 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(-)
+--- a/drivers/mmc/core/mmc.c
++++ b/drivers/mmc/core/mmc.c
+@@ -1174,6 +1174,9 @@ static int mmc_select_hs400(struct mmc_c
+ 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
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
-@@ -30,11 +30,56 @@
+@@ -30,11 +30,61 @@
#define VENDOR_V_22 0x12
#define VENDOR_V_23 0x13
@@ -64,20 +121,38 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
u8 vendor_ver;
u8 spec_ver;
bool quirk_incorrect_hostver;
++ bool quirk_limited_clk_division;
++ bool quirk_unreliable_pulse_detection;
++ bool quirk_fixup_tuning;
++ bool quirk_incorrect_delay_chain;
unsigned int peripheral_clock;
+ const struct esdhc_clk_fixup *clk_fixup;
++ u32 div_ratio;
};
/**
-@@ -502,6 +547,7 @@ static void esdhc_of_set_clock(struct sd
+@@ -495,13 +545,20 @@ static void esdhc_clock_enable(struct sd
+ }
+ }
+
++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);
int pre_div = 1;
int div = 1;
++ int division;
ktime_t timeout;
+ long fixup = 0;
u32 temp;
host->mmc->actual_clock = 0;
-@@ -515,27 +561,14 @@ static void esdhc_of_set_clock(struct sd
+@@ -515,27 +572,14 @@ static void esdhc_of_set_clock(struct sd
if (esdhc->vendor_ver < VENDOR_V_23)
pre_div = 2;
@@ -112,25 +187,300 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
-@@ -800,6 +833,7 @@ static struct soc_device_attribute soc_i
+@@ -548,9 +592,30 @@ static void esdhc_of_set_clock(struct sd
+ 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--;
+
+@@ -560,6 +625,29 @@ static void esdhc_of_set_clock(struct sd
+ | (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) {
+@@ -575,6 +663,7 @@ static void esdhc_of_set_clock(struct sd
+ udelay(10);
+ }
+
++ temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
+ temp |= ESDHC_CLOCK_SDCLKEN;
+ sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
+ }
+@@ -603,6 +692,8 @@ static void esdhc_pltfm_set_bus_width(st
+
+ 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);
+@@ -617,6 +708,12 @@ static void esdhc_reset(struct sdhci_hos
+ 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);
++ }
+ }
+ }
+
+@@ -628,6 +725,7 @@ static void esdhc_reset(struct sdhci_hos
+ 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", },
+ {}
+ };
+@@ -690,23 +788,91 @@ static int esdhc_signal_voltage_switch(s
+ }
+ }
+
+-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
+@@ -755,7 +921,7 @@ static const struct sdhci_ops sdhci_esdh
+ .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 = {
+@@ -772,7 +938,7 @@ static const struct sdhci_ops sdhci_esdh
+ .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 = {
+@@ -798,8 +964,20 @@ static struct soc_device_attribute soc_i
+ { },
+ };
++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", },
++ { },
++};
++
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;
-@@ -819,6 +853,9 @@ static void esdhc_init(struct platform_d
+@@ -819,6 +997,24 @@ static void esdhc_init(struct platform_d
else
esdhc->quirk_incorrect_hostver = false;
++ 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;
++
+ 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)) {
-@@ -923,14 +960,6 @@ static int sdhci_esdhc_probe(struct plat
+@@ -846,6 +1042,12 @@ static void esdhc_init(struct platform_d
+ }
+ }
+
++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;
+@@ -869,6 +1071,7 @@ static int sdhci_esdhc_probe(struct plat
+ 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);
+@@ -877,6 +1080,11 @@ static int sdhci_esdhc_probe(struct plat
+
+ 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;
+
+@@ -923,14 +1131,6 @@ static int sdhci_esdhc_probe(struct plat
return ret;
}
@@ -145,3 +495,78 @@ Signed-off-by: Biwen Li <biwen.li@nxp.com>
static struct platform_driver sdhci_esdhc_driver = {
.driver = {
.name = "sdhci-esdhc",
+--- a/drivers/mmc/host/sdhci.c
++++ b/drivers/mmc/host/sdhci.c
+@@ -2148,7 +2148,7 @@ static void sdhci_send_tuning(struct sdh
+
+ }
+
+-static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
++static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
+ {
+ int i;
+
+@@ -2165,13 +2165,13 @@ static void __sdhci_execute_tuning(struc
+ pr_info("%s: Tuning timeout, falling back to fixed sampling clock\n",
+ mmc_hostname(host->mmc));
+ 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;
+ }
+
+@@ -2183,6 +2183,7 @@ static void __sdhci_execute_tuning(struc
+ 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)
+@@ -2244,7 +2245,7 @@ int sdhci_execute_tuning(struct mmc_host
+
+ 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);