kernel: bump 4.14 to 4.14.149
[openwrt/staging/rmilecki.git] / target / linux / layerscape / patches-4.14 / 819-sdhc-support-layerscape.patch
index ed72f31e4a595d72d8f74d6026d8550a79970d13..60d0c889e54aeb5b2fd2367da984201e868b43fa 100644 (file)
@@ -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
+@@ -500,13 +550,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
+@@ -520,27 +577,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 |
-@@ -797,6 +830,7 @@ static struct soc_device_attribute soc_i
+@@ -553,9 +597,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--;
+@@ -565,6 +630,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) {
+@@ -580,6 +668,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);
+ }
+@@ -608,6 +697,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);
+@@ -622,6 +713,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);
++              }
+       }
+ }
+@@ -633,6 +730,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", },
+       {}
+ };
+@@ -695,23 +793,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
+@@ -760,7 +926,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 = {
+@@ -777,7 +943,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 = {
+@@ -803,8 +969,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;
-@@ -816,6 +850,9 @@ static void esdhc_init(struct platform_d
+@@ -824,6 +1002,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)) {
-@@ -915,14 +952,6 @@ static int sdhci_esdhc_probe(struct plat
+@@ -851,6 +1047,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;
+@@ -874,6 +1076,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);
+@@ -882,6 +1085,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;
+@@ -928,14 +1136,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
+@@ -2150,7 +2150,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;
+@@ -2167,13 +2167,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;
+               }
+@@ -2185,6 +2185,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)
+@@ -2246,7 +2247,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);