diff options
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.patch | 449 |
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); |