From 6ca94d2e7dc72b21703e6d9be4e8ec3ad4a26f41 Mon Sep 17 00:00:00 2001 From: Biwen Li 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: Biwen Li Signed-off-by: Mathew McBride Signed-off-by: Ulf Hansson Signed-off-by: Yangbo Lu Signed-off-by: Yinbo Zhu --- 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,61 @@ #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; + 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; }; /** @@ -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; @@ -520,27 +577,14 @@ static void esdhc_of_set_clock(struct sd 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 | @@ -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); @@ -619,6 +710,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); + } } } @@ -630,6 +727,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", }, {} }; @@ -692,23 +790,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 @@ -757,7 +923,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 = { @@ -774,7 +940,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 = { @@ -800,8 +966,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; @@ -821,6 +999,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)) { @@ -848,6 +1044,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; @@ -871,6 +1073,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); @@ -879,6 +1082,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; @@ -925,14 +1133,6 @@ static int sdhci_esdhc_probe(struct plat 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", --- 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_debug("%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);