From a94a7483a91cb6a0d15a4413e8fc853f33a4e1b9 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 30 May 2018 10:11:43 +0800 Subject: [PATCH 01/56] mmc: core: Adjust and reuse the macro of R1_STATUS(x) R1_STATUS(x) now is only used by ioctl_rpmb_card_status_poll(), which checks all bits as possible. But according to the spec, bit 17 and bit 18 should be ignored, as well bit 14 which is reserved(must be set to 0) quoting from the spec and these rule apply to all places checking the device status. So change its checking from 0xFFFFE000 to 0xFFF9A000. As a bonus, we reuse it for mmc_do_erase() as well as mmc_switch_status_error(). (1) Currently mmc_switch_status_error() doesn't check bit 25, but it means device is locked but not unlocked by CMD42 prior to any operations which need check busy, which is also not allowed. (2) mmc_do_erase() also forgot to to check bit 15, WP_ERASE_SKIP. The spec says "Only partial address space was erased due to existing write protected blocks.", which obviously means we should fail this I/O. Otherwise, the partial erased data stored in nonvalatile flash violates the data integrity from the view of I/O owner, which probably confuse it when further used. So reusing R1_STATUS for them not only improve the readability but also slove real problems. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 2 +- drivers/mmc/core/mmc_ops.c | 2 +- include/linux/mmc/mmc.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 281826d1fcca..6780c2b81050 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2078,7 +2078,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; /* Do not retry else we can't see errors */ err = mmc_wait_for_cmd(card->host, &cmd, 0); - if (err || (cmd.resp[0] & 0xFDF92000)) { + if (err || R1_STATUS(cmd.resp[0])) { pr_err("error %d requesting status %#x\n", err, cmd.resp[0]); err = -EIO; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 42d6aa89a48a..873b2aa0c155 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -417,7 +417,7 @@ static int mmc_switch_status_error(struct mmc_host *host, u32 status) if (status & R1_SPI_ILLEGAL_COMMAND) return -EBADMSG; } else { - if (status & 0xFDFFA000) + if (R1_STATUS(status)) pr_warn("%s: unexpected status %#x after switch\n", mmc_hostname(host), status); if (status & R1_SWITCH_ERROR) diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 3ffc27aaeeaf..897a87c4c827 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -144,7 +144,7 @@ static inline bool mmc_op_multi(u32 opcode) #define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ #define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ #define R1_ERASE_RESET (1 << 13) /* sr, c */ -#define R1_STATUS(x) (x & 0xFFFFE000) +#define R1_STATUS(x) (x & 0xFFF9A000) #define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ #define R1_READY_FOR_DATA (1 << 8) /* sx, a */ #define R1_SWITCH_ERROR (1 << 7) /* sx, c */ From 75067aba7afbeca2101f50f78ac1b7c036778c44 Mon Sep 17 00:00:00 2001 From: Aapo Vienamo Date: Mon, 4 Jun 2018 18:35:40 +0300 Subject: [PATCH 02/56] mmc: tegra: Use sdhci_pltfm_clk_get_max_clock The sdhci get_max_clock callback is set to sdhci_pltfm_clk_get_max_clock and tegra_sdhci_get_max_clock is removed. It appears that the shdci-tegra specific callback was originally introduced due to the requirement that the host clock has to be twice the bus clock on DDR50 mode. As far as I can tell the only effect the removal has on DDR50 mode is in cases where the parent clock is unable to supply the requested clock rate, causing the DDR50 mode to run at a lower frequency. Currently the DDR50 mode isn't enabled on any of the SoCs and would also require configuring the SDHCI clock divider register to function properly. The problem with tegra_sdhci_get_max_clock is that it divides the clock rate by two and thus artificially limits the maximum frequency of faster signaling modes which don't have the host-bus frequency ratio requirement of DDR50 such as SDR104 and HS200. Furthermore, the call to clk_round_rate() may return an error which isn't handled by tegra_sdhci_get_max_clock. Signed-off-by: Aapo Vienamo Acked-by: Thierry Reding Tested-by: Thierry Reding Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 970d38f68939..c8745b5547d8 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -234,17 +234,6 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, sdhci_set_uhs_signaling(host, timing); } -static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) -{ - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - - /* - * DDR modes require the host to run at double the card frequency, so - * the maximum rate we can support is half of the module input clock. - */ - return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; -} - static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) { u32 reg; @@ -309,7 +298,7 @@ static const struct sdhci_ops tegra_sdhci_ops = { .platform_execute_tuning = tegra_sdhci_execute_tuning, .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, .voltage_switch = tegra_sdhci_voltage_switch, - .get_max_clock = tegra_sdhci_get_max_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, }; static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { @@ -357,7 +346,7 @@ static const struct sdhci_ops tegra114_sdhci_ops = { .platform_execute_tuning = tegra_sdhci_execute_tuning, .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, .voltage_switch = tegra_sdhci_voltage_switch, - .get_max_clock = tegra_sdhci_get_max_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, }; static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { From ecf7c7c5855aca18ddde534d7bfff111ce0cb26f Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 12 Jun 2018 12:55:23 +0200 Subject: [PATCH 03/56] mmc: dw_mmc-exynos: fix potential external abort in resume_noirq() dw_mci_exynos_resume_noirq() performs DWMMC register access without ensuring that respective clocks are enabled. This might cause external abort on some systems (observed on Exynos5433 based boards). Fix this by forcing a PM runtime active state before register access. Using SET_NOIRQ_SYSTEM_SLEEP_PM_OPS allows also to cleanup conditional code a bit. Suggested-by: Ulf Hansson Signed-off-by: Marek Szyprowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 33 +++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index a84aa3f1ae85..ab47b018716a 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -175,6 +175,20 @@ static int dw_mci_exynos_runtime_resume(struct device *dev) return ret; } +#endif /* CONFIG_PM */ + +#ifdef CONFIG_PM_SLEEP +/** + * dw_mci_exynos_suspend_noirq - Exynos-specific suspend code + * + * This ensures that device will be in runtime active state in + * dw_mci_exynos_resume_noirq after calling pm_runtime_force_resume() + */ +static int dw_mci_exynos_suspend_noirq(struct device *dev) +{ + pm_runtime_get_noresume(dev); + return pm_runtime_force_suspend(dev); +} /** * dw_mci_exynos_resume_noirq - Exynos-specific resume code @@ -186,12 +200,16 @@ static int dw_mci_exynos_runtime_resume(struct device *dev) * * We run this code on all exynos variants because it doesn't hurt. */ - static int dw_mci_exynos_resume_noirq(struct device *dev) { struct dw_mci *host = dev_get_drvdata(dev); struct dw_mci_exynos_priv_data *priv = host->priv; u32 clksel; + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) @@ -207,11 +225,11 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) mci_writel(host, CLKSEL, clksel); } + pm_runtime_put(dev); + return 0; } -#else -#define dw_mci_exynos_resume_noirq NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing) { @@ -553,14 +571,11 @@ static int dw_mci_exynos_remove(struct platform_device *pdev) } static const struct dev_pm_ops dw_mci_exynos_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq, + dw_mci_exynos_resume_noirq) SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_exynos_runtime_resume, NULL) - .resume_noirq = dw_mci_exynos_resume_noirq, - .thaw_noirq = dw_mci_exynos_resume_noirq, - .restore_noirq = dw_mci_exynos_resume_noirq, }; static struct platform_driver dw_mci_exynos_pltfm_driver = { From ba6c7ac3a2f421635ae4446269526359d8bff721 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Mon, 18 Jun 2018 14:57:49 +0200 Subject: [PATCH 04/56] mmc: core: more fine-grained hooks for HS400 tuning This adds two new HS400 tuning operations: * hs400_downgrade * hs400_complete These supplement the existing HS400 operation: * prepare_hs400_tuning This is motivated by a requirement of Renesas SDHI for the following: 1. Disabling SCC before selecting to HS if selection of HS400 has occurred. This can be done in an implementation of prepare_hs400_tuning_downgrade 2. Updating registers after switching to HS400 This can be done in an implementation of complete_hs400_tuning If hs400_downgrade or hs400_complete are not implemented then they are not called. Thus means there should be no affect for existing drivers as none implemt these ops. Signed-off-by: Simon Horman Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 10 ++++++++++ include/linux/mmc/host.h | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4466f5de54d4..63a52379e8ab 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1169,6 +1169,10 @@ static int mmc_select_hs400(struct mmc_card *card) /* Set host controller to HS timing */ mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + /* Prepare host to downgrade to HS timing */ + if (host->ops->hs400_downgrade) + host->ops->hs400_downgrade(host); + /* Reduce frequency to HS frequency */ max_dtr = card->ext_csd.hs_max_dtr; mmc_set_clock(host, max_dtr); @@ -1209,6 +1213,9 @@ static int mmc_select_hs400(struct mmc_card *card) if (err) goto out_err; + if (host->ops->hs400_complete) + host->ops->hs400_complete(host); + return 0; out_err: @@ -1256,6 +1263,9 @@ int mmc_hs400_to_hs200(struct mmc_card *card) mmc_set_timing(host, MMC_TIMING_MMC_HS); + if (host->ops->hs400_downgrade) + host->ops->hs400_downgrade(host); + err = mmc_switch_status(card); if (err) goto out_err; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 64300a48dcce..a39e2925c84c 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -146,6 +146,13 @@ struct mmc_host_ops { /* Prepare HS400 target operating frequency depending host driver */ int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); + + /* Prepare for switching from HS400 to HS200 */ + void (*hs400_downgrade)(struct mmc_host *host); + + /* Complete selection of HS400 */ + void (*hs400_complete)(struct mmc_host *host); + /* Prepare enhanced strobe depending host driver */ void (*hs400_enhanced_strobe)(struct mmc_host *host, struct mmc_ios *ios); From db924bba47c8031188545c84bf943f4058b659d0 Mon Sep 17 00:00:00 2001 From: Masaharu Hayakawa Date: Mon, 18 Jun 2018 14:57:50 +0200 Subject: [PATCH 05/56] mmc: tmio: add eMMC HS400 mode support This patch adds processing for selecting HS400 mode. Signed-off-by: Masaharu Hayakawa Signed-off-by: Simon Horman Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc.h | 6 ++++ drivers/mmc/host/tmio_mmc_core.c | 47 ++++++++++++++++++++++++++++++-- include/linux/mfd/tmio.h | 3 ++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index e7d651352dc9..5d141f79e175 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -46,6 +46,7 @@ #define CTL_DMA_ENABLE 0xd8 #define CTL_RESET_SD 0xe0 #define CTL_VERSION 0xe2 +#define CTL_SDIF_MODE 0xe6 #define CTL_SDIO_REGS 0x100 #define CTL_CLK_AND_WAIT_CTL 0x138 #define CTL_RESET_SDIO 0x1e0 @@ -191,6 +192,11 @@ struct tmio_mmc_host { /* Tuning values: 1 for success, 0 for failure */ DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long)); unsigned int tap_num; + unsigned long tap_set; + + void (*prepare_hs400_tuning)(struct tmio_mmc_host *host); + void (*hs400_downgrade)(struct tmio_mmc_host *host); + void (*hs400_complete)(struct tmio_mmc_host *host); const struct tmio_mmc_dma_ops *dma_ops; }; diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 308029930304..416f9e078fda 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -199,6 +199,14 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, tmio_mmc_clk_stop(host); return; } + /* + * Both HS400 and HS200/SD104 set 200MHz, but some devices need to + * set 400MHz to distinguish the CPG settings in HS400. + */ + if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && + host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400 && + new_clock == 200000000) + new_clock = 400000000; if (host->clk_update) clock = host->clk_update(host, new_clock) / 512; @@ -209,8 +217,13 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, clock <<= 1; /* 1/1 clock is option */ - if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) && ((clk >> 22) & 0x1)) - clk |= 0xff; + if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) && + ((clk >> 22) & 0x1)) { + if (!(host->mmc->ios.timing == MMC_TIMING_MMC_HS400)) + clk |= 0xff; + else + clk &= ~0xff; + } if (host->set_clk_div) host->set_clk_div(host->pdev, (clk >> 22) & 1); @@ -1087,6 +1100,33 @@ static int tmio_multi_io_quirk(struct mmc_card *card, return blk_size; } +static int tmio_mmc_prepare_hs400_tuning(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + + if (host->prepare_hs400_tuning) + host->prepare_hs400_tuning(host); + + return 0; +} + +static void tmio_mmc_hs400_downgrade(struct mmc_host *mmc) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + + if (host->hs400_downgrade) + host->hs400_downgrade(host); +} + +static void tmio_mmc_hs400_complete(struct mmc_host *mmc) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + + if (host->hs400_complete) + host->hs400_complete(host); +} + static const struct mmc_host_ops tmio_mmc_ops = { .request = tmio_mmc_request, .set_ios = tmio_mmc_set_ios, @@ -1096,6 +1136,9 @@ static const struct mmc_host_ops tmio_mmc_ops = { .multi_io_quirk = tmio_multi_io_quirk, .hw_reset = tmio_mmc_hw_reset, .execute_tuning = tmio_mmc_execute_tuning, + .prepare_hs400_tuning = tmio_mmc_prepare_hs400_tuning, + .hs400_downgrade = tmio_mmc_hs400_downgrade, + .hs400_complete = tmio_mmc_hs400_complete, }; static int tmio_mmc_init_ocr(struct tmio_mmc_host *host) diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 91f92215ca74..77866214ab51 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -90,6 +90,9 @@ /* Some controllers have a CBSY bit */ #define TMIO_MMC_HAVE_CBSY BIT(11) +/* Some controllers that support HS400 use use 4 taps while others use 8. */ +#define TMIO_MMC_HAVE_4TAP_HS400 BIT(13) + int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base); int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base); void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state); From 26eb2607fa281d98eed28c8c3949f6786695bf7a Mon Sep 17 00:00:00 2001 From: Masaharu Hayakawa Date: Mon, 18 Jun 2018 14:57:51 +0200 Subject: [PATCH 06/56] mmc: renesas_sdhi: add eMMC HS400 mode support This patch adds processing for selecting HS400 mode. Signed-off-by: Masaharu Hayakawa Signed-off-by: Simon Horman Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 127 +++++++++++++++--- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 20 ++- drivers/mmc/host/renesas_sdhi_sys_dmac.c | 17 ++- 3 files changed, 138 insertions(+), 26 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 45c015da2e75..384ae6cfa289 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -212,6 +212,7 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, #define SH_MOBILE_SDHI_SCC_CKSEL 0x006 #define SH_MOBILE_SDHI_SCC_RVSCNTL 0x008 #define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A +#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E /* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */ #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0) @@ -224,6 +225,9 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, #define SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN BIT(0) /* Definitions for values the SH_MOBILE_SDHI_SCC_RVSREQ register */ #define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR BIT(2) +/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT2 register */ +#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4) +#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31) static inline u32 sd_scc_read32(struct tmio_mmc_host *host, struct renesas_sdhi *priv, int addr) @@ -244,33 +248,30 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host) priv = host_to_priv(host); - /* set sampling clock selection range */ - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, - 0x8 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT); - /* Initialize SCC */ sd_ctrl_write32_as_16_and_16(host, CTL_STATUS, 0x0); - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, - SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN | - sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL)); - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + /* set sampling clock selection range */ + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, + SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN | + 0x8 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | - sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos); + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + /* Read TAPNUM */ return (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL) >> SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) & @@ -286,13 +287,95 @@ static void renesas_sdhi_prepare_tuning(struct tmio_mmc_host *host, sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, tap); } +static void renesas_sdhi_hs400_complete(struct tmio_mmc_host *host) +{ + struct renesas_sdhi *priv = host_to_priv(host); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + + /* Set HS400 mode */ + sd_ctrl_write16(host, CTL_SDIF_MODE, 0x0001 | + sd_ctrl_read16(host, CTL_SDIF_MODE)); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, + (SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | + SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) | + sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2)); + + /* Set the sampling clock selection range of HS400 mode */ + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, + SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN | + 0x4 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT); + + + if (host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400) + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, + host->tap_set / 2); + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, + SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | + sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); +} + +static void renesas_sdhi_reset_scc(struct tmio_mmc_host *host, + struct renesas_sdhi *priv) +{ + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, + ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL & + sd_scc_read32(host, priv, + SH_MOBILE_SDHI_SCC_CKSEL)); +} + +static void renesas_sdhi_disable_scc(struct tmio_mmc_host *host) +{ + struct renesas_sdhi *priv = host_to_priv(host); + + renesas_sdhi_reset_scc(host, priv); + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL, + ~SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN & + sd_scc_read32(host, priv, + SH_MOBILE_SDHI_SCC_DTCNTL)); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); +} + +static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host, + struct renesas_sdhi *priv) +{ + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); + + /* Reset HS400 mode */ + sd_ctrl_write16(host, CTL_SDIF_MODE, ~0x0001 & + sd_ctrl_read16(host, CTL_SDIF_MODE)); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2, + ~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | + SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) & + sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2)); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | + sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); +} + +static void renesas_sdhi_prepare_hs400_tuning(struct tmio_mmc_host *host) +{ + renesas_sdhi_reset_hs400_mode(host, host_to_priv(host)); +} + #define SH_MOBILE_SDHI_MAX_TAP 3 static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) { struct renesas_sdhi *priv = host_to_priv(host); unsigned long tap_cnt; /* counter of tuning success */ - unsigned long tap_set; /* tap position */ unsigned long tap_start;/* start position of tuning success */ unsigned long tap_end; /* end position of tuning success */ unsigned long ntap; /* temporary counter of tuning success */ @@ -330,12 +413,12 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) } if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP) - tap_set = (tap_start + tap_end) / 2 % host->tap_num; + host->tap_set = (tap_start + tap_end) / 2 % host->tap_num; else return -EIO; /* Set SCC */ - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, tap_set); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, host->tap_set); /* Enable auto re-tuning */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, @@ -368,13 +451,8 @@ static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host) priv = host_to_priv(host); - /* Reset SCC */ - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & - sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); - - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, - ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL & - sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL)); + renesas_sdhi_reset_scc(host, priv); + renesas_sdhi_reset_hs400_mode(host, priv); sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); @@ -592,7 +670,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, /* Enable tuning iff we have an SCC and a supported mode */ if (of_data && of_data->scc_offset && (host->mmc->caps & MMC_CAP_UHS_SDR104 || - host->mmc->caps2 & MMC_CAP2_HS200_1_8V_SDR)) { + host->mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | + MMC_CAP2_HS400_1_8V))) { const struct renesas_sdhi_scc *taps = of_data->taps; bool hit = false; @@ -616,6 +695,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, host->select_tuning = renesas_sdhi_select_tuning; host->check_scc_error = renesas_sdhi_check_scc_error; host->hw_reset = renesas_sdhi_hw_reset; + host->prepare_hs400_tuning = + renesas_sdhi_prepare_hs400_tuning; + host->hs400_downgrade = renesas_sdhi_disable_scc; + host->hs400_complete = renesas_sdhi_hs400_complete; } i = 0; diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index d032bd63444d..35cc0de6be67 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -82,6 +82,22 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { }, }; +static const struct renesas_sdhi_of_data of_rcar_r8a7795_compatible = { + .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | + TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | + TMIO_MMC_HAVE_4TAP_HS400, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | + MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, + .bus_shift = 2, + .scc_offset = 0x1000, + .taps = rcar_gen3_scc_taps, + .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), + /* DMAC can handle 0xffffffff blk count but only 1 segment */ + .max_blk_count = 0xffffffff, + .max_segs = 1, +}; + static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, @@ -98,8 +114,8 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { }; static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { - { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, - { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, }, + { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, {}, }; diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c index 4bb46c489d71..890f192dedbd 100644 --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -78,6 +78,19 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = { }, }; +static const struct renesas_sdhi_of_data of_rcar_r8a7795_compatible = { + .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | + TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | + TMIO_MMC_HAVE_4TAP_HS400, + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | + MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, + .bus_shift = 2, + .scc_offset = 0x1000, + .taps = rcar_gen3_scc_taps, + .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), +}; + static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, @@ -104,8 +117,8 @@ static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = { { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, }, - { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, }, - { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_r8a7795_compatible, }, + { .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_r8a7795_compatible, }, { .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, }, { .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, From f15358885dda65ed930885f9c19d37398fb06d48 Mon Sep 17 00:00:00 2001 From: Sayali Lokhande Date: Tue, 19 Jun 2018 11:09:18 +0530 Subject: [PATCH 07/56] mmc: sdhci-msm: Define new Register address map For SDCC version 5.0.0, MCI registers are removed from SDCC interface and some registers are moved to HC. Define a new data structure where we can statically define the address offsets for the registers in different SDCC versions. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath Reviewed-by: Evan Green Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 89 ++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 646bf377ba77..e6efbe6f270a 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -137,6 +137,95 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +struct sdhci_msm_offset { + u32 core_hc_mode; + u32 core_mci_data_cnt; + u32 core_mci_status; + u32 core_mci_fifo_cnt; + u32 core_mci_version; + u32 core_generics; + u32 core_testbus_config; + u32 core_testbus_sel2_bit; + u32 core_testbus_ena; + u32 core_testbus_sel2; + u32 core_pwrctl_status; + u32 core_pwrctl_mask; + u32 core_pwrctl_clear; + u32 core_pwrctl_ctl; + u32 core_sdcc_debug_reg; + u32 core_dll_config; + u32 core_dll_status; + u32 core_vendor_spec; + u32 core_vendor_spec_adma_err_addr0; + u32 core_vendor_spec_adma_err_addr1; + u32 core_vendor_spec_func2; + u32 core_vendor_spec_capabilities0; + u32 core_ddr_200_cfg; + u32 core_vendor_spec3; + u32 core_dll_config_2; + u32 core_ddr_config; + u32 core_ddr_config_2; +}; + +static const struct sdhci_msm_offset sdhci_msm_v5_offset = { + .core_mci_data_cnt = 0x35c, + .core_mci_status = 0x324, + .core_mci_fifo_cnt = 0x308, + .core_mci_version = 0x318, + .core_generics = 0x320, + .core_testbus_config = 0x32c, + .core_testbus_sel2_bit = 3, + .core_testbus_ena = (1 << 31), + .core_testbus_sel2 = (1 << 3), + .core_pwrctl_status = 0x240, + .core_pwrctl_mask = 0x244, + .core_pwrctl_clear = 0x248, + .core_pwrctl_ctl = 0x24c, + .core_sdcc_debug_reg = 0x358, + .core_dll_config = 0x200, + .core_dll_status = 0x208, + .core_vendor_spec = 0x20c, + .core_vendor_spec_adma_err_addr0 = 0x214, + .core_vendor_spec_adma_err_addr1 = 0x218, + .core_vendor_spec_func2 = 0x210, + .core_vendor_spec_capabilities0 = 0x21c, + .core_ddr_200_cfg = 0x224, + .core_vendor_spec3 = 0x250, + .core_dll_config_2 = 0x254, + .core_ddr_config = 0x258, + .core_ddr_config_2 = 0x25c, +}; + +static const struct sdhci_msm_offset sdhci_msm_mci_offset = { + .core_hc_mode = 0x78, + .core_mci_data_cnt = 0x30, + .core_mci_status = 0x34, + .core_mci_fifo_cnt = 0x44, + .core_mci_version = 0x050, + .core_generics = 0x70, + .core_testbus_config = 0x0cc, + .core_testbus_sel2_bit = 4, + .core_testbus_ena = (1 << 3), + .core_testbus_sel2 = (1 << 4), + .core_pwrctl_status = 0xdc, + .core_pwrctl_mask = 0xe0, + .core_pwrctl_clear = 0xe4, + .core_pwrctl_ctl = 0xe8, + .core_sdcc_debug_reg = 0x124, + .core_dll_config = 0x100, + .core_dll_status = 0x108, + .core_vendor_spec = 0x10c, + .core_vendor_spec_adma_err_addr0 = 0x114, + .core_vendor_spec_adma_err_addr1 = 0x118, + .core_vendor_spec_func2 = 0x110, + .core_vendor_spec_capabilities0 = 0x11c, + .core_ddr_200_cfg = 0x184, + .core_vendor_spec3 = 0x1b0, + .core_dll_config_2 = 0x1b4, + .core_ddr_config = 0x1b8, + .core_ddr_config_2 = 0x1bc, +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ From 6ed4bb4387033a12694e7d766a3612e6cd42a4c0 Mon Sep 17 00:00:00 2001 From: Vijay Viswanath Date: Tue, 19 Jun 2018 11:09:19 +0530 Subject: [PATCH 08/56] mmc: sdhci-msm: Add msm version specific ops and data structures In addition to offsets of certain registers changing, the registers in core_mem have been shifted to HC mem as well. To access these registers, define msm version specific functions. These functions can be loaded into the function pointers at the time of probe based on the msm version detected. Also defind new data structure to hold version specific Ops and register addresses. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath Reviewed-by: Evan Green Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 75 ++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index e6efbe6f270a..8f4026f6fed4 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -226,6 +226,22 @@ static const struct sdhci_msm_offset sdhci_msm_mci_offset = { .core_ddr_config_2 = 0x1bc, }; +struct sdhci_msm_variant_ops { + u32 (*msm_readl_relaxed)(struct sdhci_host *host, u32 offset); + void (*msm_writel_relaxed)(u32 val, struct sdhci_host *host, + u32 offset); +}; + +/* + * From V5, register spaces have changed. Wrap this info in a structure + * and choose the data_structure based on version info mentioned in DT. + */ +struct sdhci_msm_variant_info { + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -245,8 +261,45 @@ struct sdhci_msm_host { wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; u32 caps_0; + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; }; +/* + * APIs to read/write to vendor specific registers which were there in the + * core_mem region before MCI was removed. + */ +static u32 sdhci_msm_mci_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return readl_relaxed(msm_host->core_mem + offset); +} + +static u32 sdhci_msm_v5_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + return readl_relaxed(host->ioaddr + offset); +} + +static void sdhci_msm_mci_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + writel_relaxed(val, msm_host->core_mem + offset); +} + +static void sdhci_msm_v5_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + writel_relaxed(val, host->ioaddr + offset); +} + static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, unsigned int clock) { @@ -1481,6 +1534,28 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); } +static const struct sdhci_msm_variant_ops mci_var_ops = { + .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_ops v5_var_ops = { + .msm_readl_relaxed = sdhci_msm_v5_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_v5_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_mci_var = { + .mci_removed = false, + .var_ops = &mci_var_ops, + .offset = &sdhci_msm_mci_offset, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_v5_var = { + .mci_removed = true, + .var_ops = &v5_var_ops, + .offset = &sdhci_msm_v5_offset, +}; + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, From e0e4eee21c700bf583fa9fa92be900cc0028323b Mon Sep 17 00:00:00 2001 From: Sayali Lokhande Date: Tue, 19 Jun 2018 11:09:20 +0530 Subject: [PATCH 09/56] Documentation: sdhci-msm: Add new compatible string for SDCC v5 For SDCC version 5.0.0 and higher, new compatible string "qcom,sdhci-msm-v5" is added. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath Acked-by: Rob Herring Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-msm.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index bfdcdc4ccdff..502b3b851ebb 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -4,7 +4,12 @@ This file documents differences between the core properties in mmc.txt and the properties used by the sdhci-msm driver. Required properties: -- compatible: Should contain "qcom,sdhci-msm-v4". +- compatible: Should contain: + "qcom,sdhci-msm-v4" for sdcc versions less than 5.0 + "qcom,sdhci-msm-v5" for sdcc versions >= 5.0 + For SDCC version 5.0.0, MCI registers are removed from SDCC + interface and some registers are moved to HC. New compatible + string is added to support this change - "qcom,sdhci-msm-v5". - reg: Base address and length of the register in the following order: - Host controller register map (required) - SD Core register map (required) From bc99266bbdd2fb302d2c85baae456577610db7b9 Mon Sep 17 00:00:00 2001 From: Sayali Lokhande Date: Tue, 19 Jun 2018 11:09:21 +0530 Subject: [PATCH 10/56] mmc: host: Register changes for sdcc V5 Add support to use the new compatible string "qcom,sdhci-msm-v5". Based on the msm variant, pick the relevant variant data and use it for register read/write to msm specific registers. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath Reviewed-by: Evan Green Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 345 ++++++++++++++++++++++------------- 1 file changed, 220 insertions(+), 125 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 8f4026f6fed4..a0dc3e151a34 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -33,16 +33,11 @@ #define CORE_MCI_GENERICS 0x70 #define SWITCHABLE_SIGNALING_VOLTAGE BIT(29) -#define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 #define CORE_POWER 0x0 #define CORE_SW_RST BIT(7) #define FF_CLK_SW_RST_DIS BIT(13) -#define CORE_PWRCTL_STATUS 0xdc -#define CORE_PWRCTL_MASK 0xe0 -#define CORE_PWRCTL_CLEAR 0xe4 -#define CORE_PWRCTL_CTL 0xe8 #define CORE_PWRCTL_BUS_OFF BIT(0) #define CORE_PWRCTL_BUS_ON BIT(1) #define CORE_PWRCTL_IO_LOW BIT(2) @@ -63,17 +58,13 @@ #define CORE_CDR_EXT_EN BIT(19) #define CORE_DLL_PDN BIT(29) #define CORE_DLL_RST BIT(30) -#define CORE_DLL_CONFIG 0x100 #define CORE_CMD_DAT_TRACK_SEL BIT(0) -#define CORE_DLL_STATUS 0x108 -#define CORE_DLL_CONFIG_2 0x1b4 #define CORE_DDR_CAL_EN BIT(0) #define CORE_FLL_CYCLE_CNT BIT(18) #define CORE_DLL_CLOCK_DISABLE BIT(21) -#define CORE_VENDOR_SPEC 0x10c -#define CORE_VENDOR_SPEC_POR_VAL 0xa1c +#define CORE_VENDOR_SPEC_POR_VAL 0xa1c #define CORE_CLK_PWRSAVE BIT(1) #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) @@ -111,17 +102,14 @@ #define CORE_CDC_SWITCH_BYPASS_OFF BIT(0) #define CORE_CDC_SWITCH_RC_EN BIT(1) -#define CORE_DDR_200_CFG 0x184 #define CORE_CDC_T4_DLY_SEL BIT(0) #define CORE_CMDIN_RCLK_EN BIT(1) #define CORE_START_CDC_TRAFFIC BIT(6) -#define CORE_VENDOR_SPEC3 0x1b0 + #define CORE_PWRSAVE_DLL BIT(3) -#define CORE_DDR_CONFIG 0x1b8 #define DDR_CONFIG_POR_VAL 0x80040853 -#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c #define INVALID_TUNING_PHASE -1 #define SDHCI_MSM_MIN_CLOCK 400000 @@ -137,6 +125,12 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +#define msm_host_readl(msm_host, host, offset) \ + msm_host->var_ops->msm_readl_relaxed(host, offset) + +#define msm_host_writel(msm_host, val, host, offset) \ + msm_host->var_ops->msm_writel_relaxed(val, host, offset) + struct sdhci_msm_offset { u32 core_hc_mode; u32 core_mci_data_cnt; @@ -266,6 +260,14 @@ struct sdhci_msm_host { const struct sdhci_msm_offset *offset; }; +static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return msm_host->offset; +} + /* * APIs to read/write to vendor specific registers which were there in the * core_mem region before MCI was removed. @@ -347,10 +349,12 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) u32 wait_cnt = 50; u8 ck_out_en; struct mmc_host *mmc = host->mmc; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); /* Poll for CK_OUT_EN bit. max. poll time = 50us */ - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); while (ck_out_en != poll) { if (--wait_cnt == 0) { @@ -360,8 +364,8 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) } udelay(1); - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); } return 0; @@ -377,16 +381,18 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) unsigned long flags; u32 config; struct mmc_host *mmc = host->mmc; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); if (phase > 0xf) return -EINVAL; spin_lock_irqsave(&host->lock, flags); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN); config |= (CORE_CDR_EXT_EN | CORE_DLL_EN); - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */ rc = msm_dll_poll_ck_out_en(host, 0); @@ -397,24 +403,24 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) * Write the selected DLL clock output phase (0 ... 15) * to CDR_SELEXT bit field of DLL_CONFIG register. */ - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config &= ~CDR_SELEXT_MASK; config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_CK_OUT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */ rc = msm_dll_poll_ck_out_en(host, 1); if (rc) goto err_out; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_CDR_EN; config &= ~CORE_CDR_EXT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); goto out; err_out: @@ -540,6 +546,8 @@ static int msm_find_most_appropriate_phase(struct sdhci_host *host, static inline void msm_cm_dll_set_freq(struct sdhci_host *host) { u32 mclk_freq = 0, config; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); /* Program the MCLK value to MCLK_FREQ bit field */ if (host->clock <= 112000000) @@ -559,10 +567,10 @@ static inline void msm_cm_dll_set_freq(struct sdhci_host *host) else if (host->clock <= 200000000) mclk_freq = 7; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config &= ~CMUX_SHIFT_PHASE_MASK; config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); } /* Initialize the DLL (Programmable Delay Line) */ @@ -574,6 +582,8 @@ static int msm_init_cm_dll(struct sdhci_host *host) int wait_cnt = 50; unsigned long flags; u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; spin_lock_irqsave(&host->lock, flags); @@ -582,34 +592,43 @@ static int msm_init_cm_dll(struct sdhci_host *host) * tuning is in progress. Keeping PWRSAVE ON may * turn off the clock. */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_CLK_PWRSAVE; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); if (msm_host->use_14lpp_dll_reset) { - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config &= ~CORE_CK_OUT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config |= CORE_DLL_CLOCK_DISABLE; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_2); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_RST; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_PDN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); msm_cm_dll_set_freq(host); if (msm_host->use_14lpp_dll_reset && !IS_ERR_OR_NULL(msm_host->xo_clk)) { u32 mclk_freq = 0; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config &= CORE_FLL_CYCLE_CNT; if (config) mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 8), @@ -618,40 +637,52 @@ static int msm_init_cm_dll(struct sdhci_host *host) mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 4), clk_get_rate(msm_host->xo_clk)); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config &= ~(0xFF << 10); config |= mclk_freq << 10; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_2); /* wait for 5us before enabling DLL clock */ udelay(5); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config &= ~CORE_DLL_RST; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config &= ~CORE_DLL_PDN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); if (msm_host->use_14lpp_dll_reset) { msm_cm_dll_set_freq(host); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config &= ~CORE_DLL_CLOCK_DISABLE; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_2); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_CK_OUT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ - while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) & + while (!(readl_relaxed(host->ioaddr + msm_offset->core_dll_status) & CORE_DLL_LOCK)) { /* max. wait for 50us sec for LOCK bit to be set */ if (--wait_cnt == 0) { @@ -672,19 +703,21 @@ static void msm_hc_select_default(struct sdhci_host *host) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; if (!msm_host->use_cdclp533) { config = readl_relaxed(host->ioaddr + - CORE_VENDOR_SPEC3); + msm_offset->core_vendor_spec3); config &= ~CORE_PWRSAVE_DLL; writel_relaxed(config, host->ioaddr + - CORE_VENDOR_SPEC3); + msm_offset->core_vendor_spec3); } - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_HC_MCLK_SEL_MASK; config |= CORE_HC_MCLK_SEL_DFLT; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); /* * Disable HC_SELECT_IN to be able to use the UHS mode select @@ -693,10 +726,10 @@ static void msm_hc_select_default(struct sdhci_host *host) * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field * in VENDOR_SPEC_FUNC */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_HC_SELECT_IN_EN; config &= ~CORE_HC_SELECT_IN_MASK; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); /* * Make sure above writes impacting free running MCLK are completed @@ -712,32 +745,36 @@ static void msm_hc_select_hs400(struct sdhci_host *host) struct mmc_ios ios = host->mmc->ios; u32 config, dll_lock; int rc; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; /* Select the divided clock (free running MCLK/2) */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_HC_MCLK_SEL_MASK; config |= CORE_HC_MCLK_SEL_HS400; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); /* * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC * register */ if ((msm_host->tuning_done || ios.enhanced_strobe) && !msm_host->calibration_done) { - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec); config |= CORE_HC_SELECT_IN_HS400; config |= CORE_HC_SELECT_IN_EN; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + + msm_offset->core_vendor_spec); } if (!msm_host->clk_rate && !msm_host->use_cdclp533) { /* * Poll on DLL_LOCK or DDR_DLL_LOCK bits in - * CORE_DLL_STATUS to be set. This should get set + * core_dll_status to be set. This should get set * within 15 us at 200 MHz. */ rc = readl_relaxed_poll_timeout(host->ioaddr + - CORE_DLL_STATUS, + msm_offset->core_dll_status, dll_lock, (dll_lock & (CORE_DLL_LOCK | @@ -789,6 +826,8 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 config, calib_done; int ret; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); @@ -805,13 +844,13 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) if (ret) goto out; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_CMD_DAT_TRACK_SEL; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); config &= ~CORE_CDC_T4_DLY_SEL; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG); config &= ~CORE_CDC_SWITCH_BYPASS_OFF; @@ -821,9 +860,9 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) config |= CORE_CDC_SWITCH_RC_EN; writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_GEN_CFG); - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); config &= ~CORE_START_CDC_TRAFFIC; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); /* Perform CDC Register Initialization Sequence */ @@ -875,9 +914,9 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) goto out; } - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); config |= CORE_START_CDC_TRAFFIC; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); out: pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc), __func__, ret); @@ -889,32 +928,38 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) struct mmc_host *mmc = host->mmc; u32 dll_status, config; int ret; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); /* - * Currently the CORE_DDR_CONFIG register defaults to desired + * Currently the core_ddr_config register defaults to desired * configuration on reset. Currently reprogramming the power on * reset (POR) value in case it might have been modified by * bootloaders. In the future, if this changes, then the desired * values will need to be programmed appropriately. */ - writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + CORE_DDR_CONFIG); + writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + + msm_offset->core_ddr_config); if (mmc->ios.enhanced_strobe) { - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_ddr_200_cfg); config |= CORE_CMDIN_RCLK_EN; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_ddr_200_cfg); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2); config |= CORE_DDR_CAL_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config_2); - ret = readl_relaxed_poll_timeout(host->ioaddr + CORE_DLL_STATUS, - dll_status, - (dll_status & CORE_DDR_DLL_LOCK), - 10, 1000); + ret = readl_relaxed_poll_timeout(host->ioaddr + + msm_offset->core_dll_status, + dll_status, + (dll_status & CORE_DDR_DLL_LOCK), + 10, 1000); if (ret == -ETIMEDOUT) { pr_err("%s: %s: CM_DLL_SDC4 calibration was not completed\n", @@ -922,9 +967,9 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) goto out; } - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC3); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec3); config |= CORE_PWRSAVE_DLL; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC3); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec3); /* * Drain writebuffer to ensure above DLL calibration @@ -944,6 +989,8 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) struct mmc_host *mmc = host->mmc; int ret; u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); @@ -961,9 +1008,11 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) msm_host->saved_tuning_phase); if (ret) goto out; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_CMD_DAT_TRACK_SEL; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); } if (msm_host->use_cdclp533) @@ -1093,6 +1142,8 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u16 ctrl_2; u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); /* Select Bus Speed Mode for host */ @@ -1133,13 +1184,17 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, * DLL is not required for clock <= 100MHz * Thus, make sure DLL it is disabled when not required */ - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_RST; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_PDN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); /* * The DLL needs to be restored and CDCLP533 recalibrated @@ -1181,7 +1236,9 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); bool done = false; - u32 val; + u32 val = SWITCHABLE_SIGNALING_VOLTAGE; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", mmc_hostname(host->mmc), __func__, req_type, @@ -1190,8 +1247,12 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) /* * The power interrupt will not be generated for signal voltage * switches if SWITCHABLE_SIGNALING_VOLTAGE in MCI_GENERICS is not set. + * Since sdhci-msm-v5, this bit has been removed and SW must consider + * it as always set. */ - val = readl(msm_host->core_mem + CORE_MCI_GENERICS); + if (!msm_host->mci_removed) + val = msm_host_readl(msm_host, host, + msm_offset->core_generics); if ((req_type & REQ_IO_HIGH || req_type & REQ_IO_LOW) && !(val & SWITCHABLE_SIGNALING_VOLTAGE)) { return; @@ -1239,12 +1300,14 @@ static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n", - mmc_hostname(host->mmc), - readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS), - readl_relaxed(msm_host->core_mem + CORE_PWRCTL_MASK), - readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL)); + mmc_hostname(host->mmc), + msm_host_readl(msm_host, host, msm_offset->core_pwrctl_status), + msm_host_readl(msm_host, host, msm_offset->core_pwrctl_mask), + msm_host_readl(msm_host, host, msm_offset->core_pwrctl_ctl)); } static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) @@ -1255,11 +1318,14 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) int retry = 10; u32 pwr_state = 0, io_level = 0; u32 config; + const struct sdhci_msm_offset *msm_offset = msm_host->offset; - irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); + irq_status = msm_host_readl(msm_host, host, + msm_offset->core_pwrctl_status); irq_status &= INT_MASK; - writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + msm_host_writel(msm_host, irq_status, host, + msm_offset->core_pwrctl_clear); /* * There is a rare HW scenario where the first clear pulse could be @@ -1268,8 +1334,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) * sure status register is cleared. Otherwise, this will result in * a spurious power IRQ resulting in system instability. */ - while (irq_status & readl_relaxed(msm_host->core_mem + - CORE_PWRCTL_STATUS)) { + while (irq_status & msm_host_readl(msm_host, host, + msm_offset->core_pwrctl_status)) { if (retry == 0) { pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n", mmc_hostname(host->mmc), irq_status); @@ -1277,8 +1343,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) WARN_ON(1); break; } - writel_relaxed(irq_status, - msm_host->core_mem + CORE_PWRCTL_CLEAR); + msm_host_writel(msm_host, irq_status, host, + msm_offset->core_pwrctl_clear); retry--; udelay(10); } @@ -1309,7 +1375,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) * report back if it succeded or not to this register. The voltage * switches are handled by the sdhci core, so just report success. */ - writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + msm_host_writel(msm_host, irq_ack, host, + msm_offset->core_pwrctl_ctl); /* * If we don't have info regarding the voltage levels supported by @@ -1328,7 +1395,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) * controllers with only 1.8V, we will set the IO PAD bit * without waiting for a REQ_IO_LOW. */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec); new_config = config; if ((io_level & REQ_IO_HIGH) && @@ -1339,8 +1407,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) new_config |= CORE_IO_PAD_PWR_SWITCH; if (config ^ new_config) - writel_relaxed(new_config, - host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(new_config, host->ioaddr + + msm_offset->core_vendor_spec); } if (pwr_state) @@ -1501,6 +1569,7 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) struct regulator *supply = mmc->supply.vqmmc; u32 caps = 0, config; struct sdhci_host *host = mmc_priv(mmc); + const struct sdhci_msm_offset *msm_offset = msm_host->offset; if (!IS_ERR(mmc->supply.vqmmc)) { if (regulator_is_supported_voltage(supply, 1700000, 1950000)) @@ -1520,7 +1589,8 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) */ u32 io_level = msm_host->curr_io_level; - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec); config |= CORE_IO_PAD_PWR_SWITCH_EN; if ((io_level & REQ_IO_HIGH) && (caps & CORE_3_0V_SUPPORT)) @@ -1528,7 +1598,8 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) else if ((io_level & REQ_IO_LOW) || (caps & CORE_1_8V_SUPPORT)) config |= CORE_IO_PAD_PWR_SWITCH; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, + host->ioaddr + msm_offset->core_vendor_spec); } msm_host->caps_0 |= caps; pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); @@ -1557,7 +1628,8 @@ static const struct sdhci_msm_variant_info sdhci_msm_v5_var = { }; static const struct of_device_id sdhci_msm_dt_match[] = { - { .compatible = "qcom,sdhci-msm-v4" }, + {.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var}, + {.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var}, {}, }; @@ -1593,6 +1665,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) u16 host_version, core_minor; u32 core_version, config; u8 core_major; + const struct sdhci_msm_offset *msm_offset; + const struct sdhci_msm_variant_info *var_info; host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host)); if (IS_ERR(host)) @@ -1608,6 +1682,18 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (ret) goto pltfm_free; + /* + * Based on the compatible string, load the required msm host info from + * the data associated with the version info. + */ + var_info = of_device_get_match_data(&pdev->dev); + + msm_host->mci_removed = var_info->mci_removed; + msm_host->var_ops = var_info->var_ops; + msm_host->offset = var_info->offset; + + msm_offset = msm_host->offset; + sdhci_get_of_property(pdev); msm_host->saved_tuning_phase = INVALID_TUNING_PHASE; @@ -1672,32 +1758,40 @@ static int sdhci_msm_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "TCXO clk not present (%d)\n", ret); } - core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); - msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres); + if (!msm_host->mci_removed) { + core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); + msm_host->core_mem = devm_ioremap_resource(&pdev->dev, + core_memres); - if (IS_ERR(msm_host->core_mem)) { - dev_err(&pdev->dev, "Failed to remap registers\n"); - ret = PTR_ERR(msm_host->core_mem); - goto clk_disable; + if (IS_ERR(msm_host->core_mem)) { + dev_err(&pdev->dev, "Failed to remap registers\n"); + ret = PTR_ERR(msm_host->core_mem); + goto clk_disable; + } } /* Reset the vendor spec register to power on reset state */ writel_relaxed(CORE_VENDOR_SPEC_POR_VAL, - host->ioaddr + CORE_VENDOR_SPEC); + host->ioaddr + msm_offset->core_vendor_spec); - /* Set HC_MODE_EN bit in HC_MODE register */ - writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE)); - - config = readl_relaxed(msm_host->core_mem + CORE_HC_MODE); - config |= FF_CLK_SW_RST_DIS; - writel_relaxed(config, msm_host->core_mem + CORE_HC_MODE); + if (!msm_host->mci_removed) { + /* Set HC_MODE_EN bit in HC_MODE register */ + msm_host_writel(msm_host, HC_MODE_EN, host, + msm_offset->core_hc_mode); + config = msm_host_readl(msm_host, host, + msm_offset->core_hc_mode); + config |= FF_CLK_SW_RST_DIS; + msm_host_writel(msm_host, config, host, + msm_offset->core_hc_mode); + } host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT)); - core_version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION); + core_version = msm_host_readl(msm_host, host, + msm_offset->core_mci_version); core_major = (core_version & CORE_VERSION_MAJOR_MASK) >> CORE_VERSION_MAJOR_SHIFT; core_minor = core_version & CORE_VERSION_MINOR_MASK; @@ -1722,7 +1816,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) config = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES); config |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT; writel_relaxed(config, host->ioaddr + - CORE_VENDOR_SPEC_CAPABILITIES0); + msm_offset->core_vendor_spec_capabilities0); } /* @@ -1751,7 +1845,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) sdhci_msm_init_pwr_irq_wait(msm_host); /* Enable pwr irq interrupts */ - writel_relaxed(INT_MASK, msm_host->core_mem + CORE_PWRCTL_MASK); + msm_host_writel(msm_host, INT_MASK, host, + msm_offset->core_pwrctl_mask); ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, sdhci_msm_pwr_irq, IRQF_ONESHOT, From 5637ffadf6719576fcba0e02026f80fd54466265 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 20 Jun 2018 09:23:13 +0300 Subject: [PATCH 11/56] mmc: sdhci-pci: Add support for Intel ICP Add PCI Ids for Intel ICP. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 2 ++ drivers/mmc/host/sdhci-pci.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 77dd3521daae..3d367613275b 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1500,6 +1500,8 @@ static const struct pci_device_id pci_ids[] = { SDHCI_PCI_DEVICE(INTEL, CNP_EMMC, intel_glk_emmc), SDHCI_PCI_DEVICE(INTEL, CNP_SD, intel_byt_sd), SDHCI_PCI_DEVICE(INTEL, CNPH_SD, intel_byt_sd), + SDHCI_PCI_DEVICE(INTEL, ICP_EMMC, intel_glk_emmc), + SDHCI_PCI_DEVICE(INTEL, ICP_SD, intel_byt_sd), SDHCI_PCI_DEVICE(O2, 8120, o2), SDHCI_PCI_DEVICE(O2, 8220, o2), SDHCI_PCI_DEVICE(O2, 8221, o2), diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index db9cb54ef700..59af2886631f 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -48,6 +48,8 @@ #define PCI_DEVICE_ID_INTEL_CNP_EMMC 0x9dc4 #define PCI_DEVICE_ID_INTEL_CNP_SD 0x9df5 #define PCI_DEVICE_ID_INTEL_CNPH_SD 0xa375 +#define PCI_DEVICE_ID_INTEL_ICP_EMMC 0x34c4 +#define PCI_DEVICE_ID_INTEL_ICP_SD 0x34f8 #define PCI_DEVICE_ID_SYSKONNECT_8000 0x8000 #define PCI_DEVICE_ID_VIA_95D0 0x95d0 From 26c312281336b55eba01ea1e7b85740d383076c6 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Wed, 20 Jun 2018 13:32:51 +0200 Subject: [PATCH 12/56] dt-bindings: mmc: broken clock stable indicator on arasan controllers Some controllers immediately report that their internal clock is stable after activating it even when the clock is not stable. When used in conjunction with older/slower cards, this can result in: mmc0: error -84 whilst initialising SD card This flag allows documenting and thus working around such a hardware defect. Signed-off-by: Helmut Grohne Acked-by: Adrian Hunter Reviewed-by: Rob Herring Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt index 60481bfc3d31..28332b655d2e 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt @@ -39,6 +39,8 @@ Optional Properties: - xlnx,fails-without-test-cd: when present, the controller doesn't work when the CD line is not connected properly, and the line is not connected properly. Test mode can be used to force the controller to function. + - xlnx,int-clock-stable-broken: when present, the controller always reports + that the internal clock is stable even when it is not. Example: sdhci@e0100000 { From 3f2c7d5d158d7df430b5763674be9e0b834dfd6f Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Wed, 20 Jun 2018 13:33:00 +0200 Subject: [PATCH 13/56] mmc: sdhci-of-arasan: Add quirk for unstable clocks Some controllers immediately report SDHCI_CLOCK_INT_STABLE after enabling the clock even when the clock is not stable. When used in conjunction with older/slower cards, this can result in: mmc0: error -84 whilst initialising SD card When the stable reporting is known to be broken, we simply wait for the maximum stabilization period. Signed-off-by: Helmut Grohne Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index e3332a522a5d..a40bcc27f187 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -102,6 +102,9 @@ struct sdhci_arasan_data { /* Controller does not have CD wired and will not function normally without */ #define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0) +/* Controller immediately reports SDHCI_CLOCK_INT_STABLE after enabling the + * internal clock even when the clock isn't stable */ +#define SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE BIT(1) }; static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { @@ -207,6 +210,16 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_set_clock(host, clock); + if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE) + /* + * Some controllers immediately report SDHCI_CLOCK_INT_STABLE + * after enabling the clock even though the clock is not + * stable. Trying to use a clock without waiting here results + * in EILSEQ while detecting some older/slower cards. The + * chosen delay is the maximum delay from sdhci_set_clock. + */ + msleep(20); + if (ctrl_phy) { phy_power_on(sdhci_arasan->phy); sdhci_arasan->is_phy_on = true; @@ -758,6 +771,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev) if (of_property_read_bool(np, "xlnx,fails-without-test-cd")) sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST; + if (of_property_read_bool(np, "xlnx,int-clock-stable-broken")) + sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE; + pltfm_host->clk = clk_xin; if (of_device_is_compatible(pdev->dev.of_node, From 67fdfbdf0115b39b4e9b9d96d240202fa3049b12 Mon Sep 17 00:00:00 2001 From: "yinbo.zhu" Date: Mon, 25 Jun 2018 16:46:24 +0800 Subject: [PATCH 14/56] mmc: sdhci-of-esdhc: modify the sd clock in of_match_node way Convert to use of_match_node method to fix up eSDHC clock for ls1046a/ls1012a/p1010. Also add eSDHC clock fixup for ls1021a according to its datasheet. The maxmum speed for ls1021a eSDHC high speed mode is 46.5MHz. Signed-off-by: Yinbo Zhu Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 85 +++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 4ffa6b173a21..c9685c6e4395 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -29,11 +29,56 @@ #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; unsigned int peripheral_clock; + const struct esdhc_clk_fixup *clk_fixup; }; /** @@ -492,6 +537,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) int pre_div = 1; int div = 1; ktime_t timeout; + long fixup = 0; u32 temp; host->mmc->actual_clock = 0; @@ -505,27 +551,14 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) 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 | @@ -783,6 +816,7 @@ static struct soc_device_attribute soc_incorrect_hostver[] = { 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; @@ -802,6 +836,9 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) else esdhc->quirk_incorrect_hostver = 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)) { @@ -901,14 +938,6 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) 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", From 29772f8a73d88a5de648177d9d822055ab7d1ba6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 26 Jun 2018 16:51:31 +0200 Subject: [PATCH 15/56] mmc: core: Drop the unused mmc_power_save|restore_host() The last user of mmc_power_save|restore_host() APIs is gone, hence let's drop them. Drop also the corresponding bus_ops callback, ->power_save|restore() as those becomes redundant. Cc: Tony Lindgren Cc: Eyal Reizer Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 46 ---------------------------------------- drivers/mmc/core/core.h | 2 -- drivers/mmc/core/sdio.c | 1 - include/linux/mmc/host.h | 3 --- 4 files changed, 52 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 6780c2b81050..50a5c340307b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2716,52 +2716,6 @@ void mmc_stop_host(struct mmc_host *host) mmc_release_host(host); } -int mmc_power_save_host(struct mmc_host *host) -{ - int ret = 0; - - pr_debug("%s: %s: powering down\n", mmc_hostname(host), __func__); - - mmc_bus_get(host); - - if (!host->bus_ops || host->bus_dead) { - mmc_bus_put(host); - return -EINVAL; - } - - if (host->bus_ops->power_save) - ret = host->bus_ops->power_save(host); - - mmc_bus_put(host); - - mmc_power_off(host); - - return ret; -} -EXPORT_SYMBOL(mmc_power_save_host); - -int mmc_power_restore_host(struct mmc_host *host) -{ - int ret; - - pr_debug("%s: %s: powering up\n", mmc_hostname(host), __func__); - - mmc_bus_get(host); - - if (!host->bus_ops || host->bus_dead) { - mmc_bus_put(host); - return -EINVAL; - } - - mmc_power_up(host, host->card->ocr); - ret = host->bus_ops->power_restore(host); - - mmc_bus_put(host); - - return ret; -} -EXPORT_SYMBOL(mmc_power_restore_host); - #ifdef CONFIG_PM_SLEEP /* Do the card removal on suspend if card is assumed removeable * Do that in pm notifier while userspace isn't yet frozen, so we will be able diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 9d8f09ac0821..087ba68b2920 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -28,8 +28,6 @@ struct mmc_bus_ops { int (*resume)(struct mmc_host *); int (*runtime_suspend)(struct mmc_host *); int (*runtime_resume)(struct mmc_host *); - int (*power_save)(struct mmc_host *); - int (*power_restore)(struct mmc_host *); int (*alive)(struct mmc_host *); int (*shutdown)(struct mmc_host *); int (*hw_reset)(struct mmc_host *); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index a86490dbca70..d8e17ea6126d 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1076,7 +1076,6 @@ static const struct mmc_bus_ops mmc_sdio_ops = { .resume = mmc_sdio_resume, .runtime_suspend = mmc_sdio_runtime_suspend, .runtime_resume = mmc_sdio_runtime_resume, - .power_restore = mmc_sdio_power_restore, .alive = mmc_sdio_alive, .hw_reset = mmc_sdio_hw_reset, .sw_reset = mmc_sdio_sw_reset, diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index a39e2925c84c..beed7121c781 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -481,9 +481,6 @@ static inline void *mmc_priv(struct mmc_host *host) #define mmc_classdev(x) (&(x)->class_dev) #define mmc_hostname(x) (dev_name(&(x)->class_dev)) -int mmc_power_save_host(struct mmc_host *host); -int mmc_power_restore_host(struct mmc_host *host); - void mmc_detect_change(struct mmc_host *, unsigned long delay); void mmc_request_done(struct mmc_host *, struct mmc_request *); void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq); From 09c8192be713a30ace01ac00a90bc3361b7fcd12 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 28 Jun 2018 09:31:36 +0200 Subject: [PATCH 16/56] mmc: sdhci-esdhc-imx: support eMMC DDR mode when running at 3.3V The uSDHC supports DDR modes for eMMC devices running at 3.3V. This allows to run eMMC with 3.3V signaling voltage at DDR52 mode: # cat /sys/kernel/debug/mmc1/ios clock: 52000000 Hz vdd: 21 (3.3 ~ 3.4 V) bus mode: 2 (push-pull) chip select: 0 (don't care) power mode: 2 (on) bus width: 3 (8 bits) timing spec: 8 (mmc DDR52) signal voltage: 0 (3.30 V) driver type: 0 (driver type B) Signed-off-by: Stefan Agner Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 4eb3d29ecde1..f9aa832735e4 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1318,7 +1318,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (esdhc_is_usdhc(imx_data)) { host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; - host->mmc->caps |= MMC_CAP_1_8V_DDR; + host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR; if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200)) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; From 86f495c57f7146bc5d363d27483957928bb9d1d8 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 28 Jun 2018 10:13:29 +0200 Subject: [PATCH 17/56] mmc: sdhci-esdhc-imx: get rid of support_vsel The field support_vsel is currently only used in the device tree case. Get rid of it. No change in behavior. Signed-off-by: Stefan Agner Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 ++------ include/linux/platform_data/mmc-esdhc-imx.h | 2 -- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index f9aa832735e4..85fd5a8b0b6d 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1151,18 +1151,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, &boarddata->tuning_start_tap); if (of_find_property(np, "no-1-8-v", NULL)) - boarddata->support_vsel = false; - else - boarddata->support_vsel = true; + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) boarddata->delay_line = 0; mmc_of_parse_voltage(np, &host->ocr_mask); - /* sdr50 and sdr104 need work on 1.8v signal voltage */ - if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data) && - !IS_ERR(imx_data->pins_default)) { + if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pins_default)) { imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, ESDHC_PINCTRL_STATE_100MHZ); imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl, diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h index 7daa78a2f342..640dec8b5b0c 100644 --- a/include/linux/platform_data/mmc-esdhc-imx.h +++ b/include/linux/platform_data/mmc-esdhc-imx.h @@ -34,7 +34,6 @@ enum cd_types { * @cd_gpio: gpio for card_detect interrupt * @wp_type: type of write_protect method (see wp_types enum above) * @cd_type: type of card_detect method (see cd_types enum above) - * @support_vsel: indicate it supports 1.8v switching */ struct esdhc_platform_data { @@ -43,7 +42,6 @@ struct esdhc_platform_data { enum wp_types wp_type; enum cd_types cd_type; int max_bus_width; - bool support_vsel; unsigned int delay_line; unsigned int tuning_step; /* The delay cell steps in tuning procedure */ unsigned int tuning_start_tap; /* The start delay cell point in tuning procedure */ From d92eaf57a85c27890a21cb0b417498b8e903db44 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 30 Jun 2018 20:13:59 +0200 Subject: [PATCH 18/56] mmc: pxamci: remove irq from private context This seems to be a left-over from times before the IRQ was handled by devm functions. Remove it. Signed-off-by: Daniel Mack Acked-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 6c94474e36f4..f0968882dbd3 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -58,7 +58,6 @@ struct pxamci_host { void __iomem *base; struct clk *clk; unsigned long clkrate; - int irq; unsigned int clkrt; unsigned int cmdat; unsigned int imask; @@ -711,7 +710,6 @@ static int pxamci_probe(struct platform_device *pdev) spin_lock_init(&host->lock); host->res = r; - host->irq = irq; host->imask = MMC_I_MASK_ALL; host->base = devm_ioremap_resource(&pdev->dev, r); @@ -729,7 +727,7 @@ static int pxamci_probe(struct platform_device *pdev) writel(64, host->base + MMC_RESTO); writel(host->imask, host->base + MMC_I_MASK); - ret = devm_request_irq(&pdev->dev, host->irq, pxamci_irq, 0, + ret = devm_request_irq(&pdev->dev, irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) goto out; From 140b7fe4d38aa2733b28d5a370c5e5796c508c06 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 30 Jun 2018 20:14:00 +0200 Subject: [PATCH 19/56] mmc: pxamci: remove dma resources from private context These members are no longer in use, so let's remove them. Signed-off-by: Daniel Mack Acked-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index f0968882dbd3..a826c0e8a096 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -71,12 +71,8 @@ struct pxamci_host { struct dma_chan *dma_chan_rx; struct dma_chan *dma_chan_tx; dma_cookie_t dma_cookie; - dma_addr_t sg_dma; unsigned int dma_len; - unsigned int dma_dir; - unsigned int dma_drcmrrx; - unsigned int dma_drcmrtx; struct regulator *vcc; }; From 52c091868e787cd1fa10d43d343f9282860f4a18 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 30 Jun 2018 20:14:01 +0200 Subject: [PATCH 20/56] mmc: pxamci: remove dead code from pxamci_remove() These gpio assignments don't make sense, as they are not used anywhere. Remove the dead code. Signed-off-by: Daniel Mack Acked-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index a826c0e8a096..ee1c32862ef8 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -806,18 +806,12 @@ out: static int pxamci_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); - int gpio_cd = -1, gpio_ro = -1, gpio_power = -1; if (mmc) { struct pxamci_host *host = mmc_priv(mmc); mmc_remove_host(mmc); - if (host->pdata) { - gpio_cd = host->pdata->gpio_card_detect; - gpio_ro = host->pdata->gpio_card_ro; - gpio_power = host->pdata->gpio_power; - } if (host->pdata && host->pdata->exit) host->pdata->exit(&pdev->dev, mmc); @@ -833,6 +827,7 @@ static int pxamci_remove(struct platform_device *pdev) mmc_free_host(mmc); } + return 0; } From 0da5358b29321c4f59cc7b3e00b536c5ec06a8bb Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 30 Jun 2018 20:14:02 +0200 Subject: [PATCH 21/56] mmc: pxamci: fix indenting pxamci_of_init() had some weird indenting. Signed-off-by: Daniel Mack Acked-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index ee1c32862ef8..3df60c7babce 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -593,16 +593,16 @@ MODULE_DEVICE_TABLE(of, pxa_mmc_dt_ids); static int pxamci_of_init(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; - struct pxamci_platform_data *pdata; - u32 tmp; + struct device_node *np = pdev->dev.of_node; + struct pxamci_platform_data *pdata; + u32 tmp; - if (!np) - return 0; + if (!np) + return 0; - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; pdata->gpio_card_detect = of_get_named_gpio(np, "cd-gpios", 0); @@ -616,9 +616,9 @@ static int pxamci_of_init(struct platform_device *pdev) if (of_property_read_u32(np, "pxa-mmc,detect-delay-ms", &tmp) == 0) pdata->detect_delay_ms = tmp; - pdev->dev.platform_data = pdata; + pdev->dev.platform_data = pdata; - return 0; + return 0; } #else static int pxamci_of_init(struct platform_device *pdev) From fa3a5115469c2d1fdb32ea08e8bc6e3b105630a6 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 30 Jun 2018 20:14:03 +0200 Subject: [PATCH 22/56] mmc: pxamci: call mmc_of_parse() Call into mmc_of_parse() from pxamci_of_init(). As it needs a pointer to a struct mmc_host, refactor the code a bit. This allows all generic MMC properties to be set that are described in Documentation/devicetree/bindings/mmc/mmc.txt. Reword the documentation a bit to make that clear. The "cd" and "wp" gpio lookups are removed as the lookup will now be done by mmc_of_parse(). Signed-off-by: Daniel Mack Acked-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/pxa-mmc.txt | 4 ++-- drivers/mmc/host/pxamci.c | 24 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/pxa-mmc.txt b/Documentation/devicetree/bindings/mmc/pxa-mmc.txt index b7025de7dced..f2687752597e 100644 --- a/Documentation/devicetree/bindings/mmc/pxa-mmc.txt +++ b/Documentation/devicetree/bindings/mmc/pxa-mmc.txt @@ -10,8 +10,8 @@ Optional properties: - marvell,detect-delay-ms: sets the detection delay timeout in ms. - marvell,gpio-power: GPIO spec for the card power enable pin -This file documents differences between the core properties in mmc.txt -and the properties used by the pxa-mmc driver. +In addition to the properties described in this docuent, the details +described in mmc.txt are supported. Examples: diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 3df60c7babce..e046e684b3f9 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -591,11 +591,13 @@ static const struct of_device_id pxa_mmc_dt_ids[] = { MODULE_DEVICE_TABLE(of, pxa_mmc_dt_ids); -static int pxamci_of_init(struct platform_device *pdev) +static int pxamci_of_init(struct platform_device *pdev, + struct mmc_host *mmc) { struct device_node *np = pdev->dev.of_node; struct pxamci_platform_data *pdata; u32 tmp; + int ret; if (!np) return 0; @@ -604,11 +606,6 @@ static int pxamci_of_init(struct platform_device *pdev) if (!pdata) return -ENOMEM; - pdata->gpio_card_detect = - of_get_named_gpio(np, "cd-gpios", 0); - pdata->gpio_card_ro = - of_get_named_gpio(np, "wp-gpios", 0); - /* pxa-mmc specific */ pdata->gpio_power = of_get_named_gpio(np, "pxa-mmc,gpio-power", 0); @@ -616,12 +613,17 @@ static int pxamci_of_init(struct platform_device *pdev) if (of_property_read_u32(np, "pxa-mmc,detect-delay-ms", &tmp) == 0) pdata->detect_delay_ms = tmp; + ret = mmc_of_parse(mmc); + if (ret < 0) + return ret; + pdev->dev.platform_data = pdata; return 0; } #else -static int pxamci_of_init(struct platform_device *pdev) +static int pxamci_of_init(struct platform_device *pdev, + struct mmc_host *mmc) { return 0; } @@ -634,10 +636,6 @@ static int pxamci_probe(struct platform_device *pdev) struct resource *r; int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; - ret = pxamci_of_init(pdev); - if (ret) - return ret; - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -672,6 +670,10 @@ static int pxamci_probe(struct platform_device *pdev) */ mmc->max_blk_count = 65535; + ret = pxamci_of_init(pdev, mmc); + if (ret) + return ret; + host = mmc_priv(mmc); host->mmc = mmc; host->pdata = pdev->dev.platform_data; From f37216de6ecc306ff4acd186a88a99906babaf18 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 30 Jun 2018 20:14:04 +0200 Subject: [PATCH 23/56] mmc: pxamci: remove pxa-mmc, gpio-power from devicetree bindings Devicetree-enabled boards should use proper regulators to control the power of cards, not GPIOs, so let's remove this property. The regulator properties are supported by the MMC core and are described in the generic MMC document: Documentation/devicetree/bindings/mmc/mmc.txt Note that devicetree support for PXA platforms hasn't fully landed yet, so this binding does not have any users at this point. Signed-off-by: Daniel Mack Acked-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/pxa-mmc.txt | 2 +- drivers/mmc/host/pxamci.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/pxa-mmc.txt b/Documentation/devicetree/bindings/mmc/pxa-mmc.txt index f2687752597e..5f5c2bec2b8c 100644 --- a/Documentation/devicetree/bindings/mmc/pxa-mmc.txt +++ b/Documentation/devicetree/bindings/mmc/pxa-mmc.txt @@ -8,7 +8,6 @@ Required properties: Optional properties: - marvell,detect-delay-ms: sets the detection delay timeout in ms. -- marvell,gpio-power: GPIO spec for the card power enable pin In addition to the properties described in this docuent, the details described in mmc.txt are supported. @@ -19,6 +18,7 @@ mmc0: mmc@41100000 { compatible = "marvell,pxa-mmc"; reg = <0x41100000 0x1000>; interrupts = <23>; + vmmc-supply = <&mmc_regulator>; cd-gpios = <&gpio 23 0>; wp-gpios = <&gpio 24 0>; }; diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index e046e684b3f9..1e7703116763 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -607,9 +607,6 @@ static int pxamci_of_init(struct platform_device *pdev, return -ENOMEM; /* pxa-mmc specific */ - pdata->gpio_power = - of_get_named_gpio(np, "pxa-mmc,gpio-power", 0); - if (of_property_read_u32(np, "pxa-mmc,detect-delay-ms", &tmp) == 0) pdata->detect_delay_ms = tmp; From 61951fd6cb49593c57da22b780ab9114060032d4 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 30 Jun 2018 20:14:05 +0200 Subject: [PATCH 24/56] mmc: pxamci: let mmc core handle regulators Strip some code by letting the mmc core handle the regulators. The old .gpio_power pdata handling is kept around for now. This also set the voltage on the regulator and handles -EPROBE_DEFER correctly. Signed-off-by: Daniel Mack Acked-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 52 ++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 1e7703116763..ed576809fae8 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -73,58 +73,46 @@ struct pxamci_host { dma_cookie_t dma_cookie; unsigned int dma_len; unsigned int dma_dir; - - struct regulator *vcc; }; -static inline void pxamci_init_ocr(struct pxamci_host *host) +static int pxamci_init_ocr(struct pxamci_host *host) { -#ifdef CONFIG_REGULATOR - host->vcc = devm_regulator_get_optional(mmc_dev(host->mmc), "vmmc"); + struct mmc_host *mmc = host->mmc; + int ret; - if (IS_ERR(host->vcc)) - host->vcc = NULL; - else { - host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc); - if (host->pdata && host->pdata->ocr_mask) - dev_warn(mmc_dev(host->mmc), - "ocr_mask/setpower will not be used\n"); - } -#endif - if (host->vcc == NULL) { + ret = mmc_regulator_get_supply(mmc); + if (ret < 0) + return ret; + + if (IS_ERR(mmc->supply.vmmc)) { /* fall-back to platform data */ - host->mmc->ocr_avail = host->pdata ? + mmc->ocr_avail = host->pdata ? host->pdata->ocr_mask : MMC_VDD_32_33 | MMC_VDD_33_34; } + + return 0; } static inline int pxamci_set_power(struct pxamci_host *host, unsigned char power_mode, unsigned int vdd) { + struct mmc_host *mmc = host->mmc; + struct regulator *supply = mmc->supply.vmmc; int on; - if (host->vcc) { - int ret; + if (!IS_ERR(supply)) + return mmc_regulator_set_ocr(mmc, supply, vdd); - if (power_mode == MMC_POWER_UP) { - ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); - if (ret) - return ret; - } else if (power_mode == MMC_POWER_OFF) { - ret = mmc_regulator_set_ocr(host->mmc, host->vcc, 0); - if (ret) - return ret; - } - } - if (!host->vcc && host->pdata && + if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) { on = ((1 << vdd) & host->pdata->ocr_mask); gpio_set_value(host->pdata->gpio_power, !!on ^ host->pdata->gpio_power_invert); } - if (!host->vcc && host->pdata && host->pdata->setpower) + + if (host->pdata && host->pdata->setpower) return host->pdata->setpower(mmc_dev(host->mmc), vdd); return 0; @@ -691,7 +679,9 @@ static int pxamci_probe(struct platform_device *pdev) mmc->f_min = (host->clkrate + 63) / 64; mmc->f_max = (mmc_has_26MHz()) ? 26000000 : host->clkrate; - pxamci_init_ocr(host); + ret = pxamci_init_ocr(host); + if (ret < 0) + return ret; mmc->caps = 0; host->cmdat = 0; From 38a8dda908683c30bcabe7e03a3418aaf2022f5f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 3 Jul 2018 22:10:26 +0200 Subject: [PATCH 25/56] mmc: pxamci: make GPIO lookups from pdata optional A recent commit introduced a call to mmc_of_parse() and removed the explicit assignment of GPIOs in the pdata structure. This will leave them set to 0, which is a valid GPIO per se, so the code that looks at these members will try to allocate them and fail. To fix this properly, make the following changes: a) Refrain from allocating and assiging a pdata struct from pxamci_of_init(). It's a hack to do it this way anyway. Instead, move the only remaining member of interest in 'struct pxamci_host' and assign the value from either the passed in pdata pointer or with the value read from DT. b) Let the only user of 'detect_delay_ms' look at the member of 'struct pxamci_host', not the pdata. c) Make more code in pxamci_probe() dependent on the presence of actual pdata. This will also ease the removal of pdata one day. Signed-off-by: Daniel Mack Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 94 ++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index ed576809fae8..f1297080d0e0 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -62,6 +62,7 @@ struct pxamci_host { unsigned int cmdat; unsigned int imask; unsigned int power_mode; + unsigned long detect_delay_ms; struct pxamci_platform_data *pdata; struct mmc_request *mrq; @@ -567,7 +568,7 @@ static irqreturn_t pxamci_detect_irq(int irq, void *devid) { struct pxamci_host *host = mmc_priv(devid); - mmc_detect_change(devid, msecs_to_jiffies(host->pdata->detect_delay_ms)); + mmc_detect_change(devid, msecs_to_jiffies(host->detect_delay_ms)); return IRQ_HANDLED; } @@ -583,27 +584,21 @@ static int pxamci_of_init(struct platform_device *pdev, struct mmc_host *mmc) { struct device_node *np = pdev->dev.of_node; - struct pxamci_platform_data *pdata; + struct pxamci_host *host = mmc_priv(mmc); u32 tmp; int ret; if (!np) return 0; - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - /* pxa-mmc specific */ if (of_property_read_u32(np, "pxa-mmc,detect-delay-ms", &tmp) == 0) - pdata->detect_delay_ms = tmp; + host->detect_delay_ms = tmp; ret = mmc_of_parse(mmc); if (ret < 0) return ret; - pdev->dev.platform_data = pdata; - return 0; } #else @@ -619,7 +614,7 @@ static int pxamci_probe(struct platform_device *pdev) struct mmc_host *mmc; struct pxamci_host *host = NULL; struct resource *r; - int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; + int ret, irq; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -734,48 +729,55 @@ static int pxamci_probe(struct platform_device *pdev) } if (host->pdata) { - gpio_cd = host->pdata->gpio_card_detect; - gpio_ro = host->pdata->gpio_card_ro; - gpio_power = host->pdata->gpio_power; - } - if (gpio_is_valid(gpio_power)) { - ret = devm_gpio_request(&pdev->dev, gpio_power, - "mmc card power"); + int gpio_cd = host->pdata->gpio_card_detect; + int gpio_ro = host->pdata->gpio_card_ro; + int gpio_power = host->pdata->gpio_power; + + host->detect_delay_ms = host->pdata->detect_delay_ms; + + if (gpio_is_valid(gpio_power)) { + ret = devm_gpio_request(&pdev->dev, gpio_power, + "mmc card power"); + if (ret) { + dev_err(&pdev->dev, + "Failed requesting gpio_power %d\n", + gpio_power); + goto out; + } + gpio_direction_output(gpio_power, + host->pdata->gpio_power_invert); + } + + if (gpio_is_valid(gpio_ro)) { + ret = mmc_gpio_request_ro(mmc, gpio_ro); + if (ret) { + dev_err(&pdev->dev, + "Failed requesting gpio_ro %d\n", + gpio_ro); + goto out; + } else { + mmc->caps2 |= host->pdata->gpio_card_ro_invert ? + 0 : MMC_CAP2_RO_ACTIVE_HIGH; + } + } + + if (gpio_is_valid(gpio_cd)) + ret = mmc_gpio_request_cd(mmc, gpio_cd, 0); if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_power %d\n", - gpio_power); + dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", + gpio_cd); goto out; } - gpio_direction_output(gpio_power, - host->pdata->gpio_power_invert); - } - if (gpio_is_valid(gpio_ro)) { - ret = mmc_gpio_request_ro(mmc, gpio_ro); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", - gpio_ro); - goto out; - } else { - mmc->caps2 |= host->pdata->gpio_card_ro_invert ? - 0 : MMC_CAP2_RO_ACTIVE_HIGH; - } - } - if (gpio_is_valid(gpio_cd)) - ret = mmc_gpio_request_cd(mmc, gpio_cd, 0); - if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", gpio_cd); - goto out; + if (host->pdata->init) + host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); + + if (gpio_is_valid(gpio_power) && host->pdata->setpower) + dev_warn(&pdev->dev, "gpio_power and setpower() both defined\n"); + if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) + dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined\n"); } - if (host->pdata && host->pdata->init) - host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); - - if (gpio_is_valid(gpio_power) && host->pdata->setpower) - dev_warn(&pdev->dev, "gpio_power and setpower() both defined\n"); - if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) - dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined\n"); - mmc_add_host(mmc); return 0; From 23f3ff72bc6f05116b27e3628868b4912dd9a2b2 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Tue, 3 Jul 2018 22:10:27 +0200 Subject: [PATCH 26/56] mmc: pxamci: provide a short-hand for &pdev->dev Just a cosmetic cleanup with no functional impact. Signed-off-by: Daniel Mack Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index f1297080d0e0..f7ffbf1676b1 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -613,6 +613,7 @@ static int pxamci_probe(struct platform_device *pdev) { struct mmc_host *mmc; struct pxamci_host *host = NULL; + struct device *dev = &pdev->dev; struct resource *r; int ret, irq; @@ -621,7 +622,7 @@ static int pxamci_probe(struct platform_device *pdev) if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); + mmc = mmc_alloc_host(sizeof(struct pxamci_host), dev); if (!mmc) { ret = -ENOMEM; goto out; @@ -659,7 +660,7 @@ static int pxamci_probe(struct platform_device *pdev) host->pdata = pdev->dev.platform_data; host->clkrt = CLKRT_OFF; - host->clk = devm_clk_get(&pdev->dev, NULL); + host->clk = devm_clk_get(dev, NULL); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); host->clk = NULL; @@ -692,7 +693,7 @@ static int pxamci_probe(struct platform_device *pdev) host->res = r; host->imask = MMC_I_MASK_ALL; - host->base = devm_ioremap_resource(&pdev->dev, r); + host->base = devm_ioremap_resource(dev, r); if (IS_ERR(host->base)) { ret = PTR_ERR(host->base); goto out; @@ -707,23 +708,23 @@ static int pxamci_probe(struct platform_device *pdev) writel(64, host->base + MMC_RESTO); writel(host->imask, host->base + MMC_I_MASK); - ret = devm_request_irq(&pdev->dev, irq, pxamci_irq, 0, + ret = devm_request_irq(dev, irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) goto out; platform_set_drvdata(pdev, mmc); - host->dma_chan_rx = dma_request_slave_channel(&pdev->dev, "rx"); + host->dma_chan_rx = dma_request_slave_channel(dev, "rx"); if (host->dma_chan_rx == NULL) { - dev_err(&pdev->dev, "unable to request rx dma channel\n"); + dev_err(dev, "unable to request rx dma channel\n"); ret = -ENODEV; goto out; } - host->dma_chan_tx = dma_request_slave_channel(&pdev->dev, "tx"); + host->dma_chan_tx = dma_request_slave_channel(dev, "tx"); if (host->dma_chan_tx == NULL) { - dev_err(&pdev->dev, "unable to request tx dma channel\n"); + dev_err(dev, "unable to request tx dma channel\n"); ret = -ENODEV; goto out; } @@ -736,10 +737,10 @@ static int pxamci_probe(struct platform_device *pdev) host->detect_delay_ms = host->pdata->detect_delay_ms; if (gpio_is_valid(gpio_power)) { - ret = devm_gpio_request(&pdev->dev, gpio_power, + ret = devm_gpio_request(dev, gpio_power, "mmc card power"); if (ret) { - dev_err(&pdev->dev, + dev_err(dev, "Failed requesting gpio_power %d\n", gpio_power); goto out; @@ -751,7 +752,7 @@ static int pxamci_probe(struct platform_device *pdev) if (gpio_is_valid(gpio_ro)) { ret = mmc_gpio_request_ro(mmc, gpio_ro); if (ret) { - dev_err(&pdev->dev, + dev_err(dev, "Failed requesting gpio_ro %d\n", gpio_ro); goto out; @@ -764,18 +765,18 @@ static int pxamci_probe(struct platform_device *pdev) if (gpio_is_valid(gpio_cd)) ret = mmc_gpio_request_cd(mmc, gpio_cd, 0); if (ret) { - dev_err(&pdev->dev, "Failed requesting gpio_cd %d\n", + dev_err(dev, "Failed requesting gpio_cd %d\n", gpio_cd); goto out; } if (host->pdata->init) - host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); + host->pdata->init(dev, pxamci_detect_irq, mmc); if (gpio_is_valid(gpio_power) && host->pdata->setpower) - dev_warn(&pdev->dev, "gpio_power and setpower() both defined\n"); + dev_warn(dev, "gpio_power and setpower() both defined\n"); if (gpio_is_valid(gpio_ro) && host->pdata->get_ro) - dev_warn(&pdev->dev, "gpio_ro and get_ro() both defined\n"); + dev_warn(dev, "gpio_ro and get_ro() both defined\n"); } mmc_add_host(mmc); From 2061594c612c4535b14bcd062a5bcf308bdd2b98 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 3 Jul 2018 17:56:59 -0500 Subject: [PATCH 27/56] mmc: sdhci-xenon: mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Warning level 2 was used: -Wimplicit-fallthrough=2 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-xenon-phy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c index a35804b203a7..c335052d0c02 100644 --- a/drivers/mmc/host/sdhci-xenon-phy.c +++ b/drivers/mmc/host/sdhci-xenon-phy.c @@ -526,6 +526,7 @@ static bool xenon_emmc_phy_slow_mode(struct sdhci_host *host, ret = true; break; } + /* else: fall through */ default: reg &= ~XENON_TIMING_ADJUST_SLOW_MODE; ret = false; From f01c3684cf048794dced60297c6fc4b805c6787c Mon Sep 17 00:00:00 2001 From: "weiyongjun (A)" Date: Wed, 4 Jul 2018 08:35:17 +0000 Subject: [PATCH 28/56] mmc: sdhci-msm: Remove redundant dev_err call in sdhci_msm_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index a0dc3e151a34..3cc8bfee6c18 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1764,7 +1764,6 @@ static int sdhci_msm_probe(struct platform_device *pdev) core_memres); if (IS_ERR(msm_host->core_mem)) { - dev_err(&pdev->dev, "Failed to remap registers\n"); ret = PTR_ERR(msm_host->core_mem); goto clk_disable; } From 5552d7ad596c3fea953f40fef74170ce0760c04d Mon Sep 17 00:00:00 2001 From: Laurentiu Tudor Date: Wed, 4 Jul 2018 14:34:20 +0300 Subject: [PATCH 29/56] mmc: sdhci-of-esdhc: set proper dma mask for ls104x chips SDHCI controller in ls1043a and ls1046a generate 40-bit wide addresses when doing DMA. Make sure that the corresponding dma mask is correctly configured. Context: when enabling smmu on these chips the following problem is encountered: the smmu input address size is 48 bits so the dma mappings for sdhci end up 48-bit wide. However, on these chips sdhci only use 40-bits of that address size when doing dma. So you end up with a 48-bit address translation in smmu but the device generates transactions with clipped 40-bit addresses, thus smmu context faults are triggered. Setting up the correct dma mask fixes this situation. Signed-off-by: Laurentiu Tudor Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index c9685c6e4395..9cb7554a463d 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "sdhci-pltfm.h" #include "sdhci-esdhc.h" @@ -472,6 +473,11 @@ static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask) static int esdhc_of_enable_dma(struct sdhci_host *host) { u32 value; + struct device *dev = mmc_dev(host->mmc); + + if (of_device_is_compatible(dev->of_node, "fsl,ls1043a-esdhc") || + of_device_is_compatible(dev->of_node, "fsl,ls1046a-esdhc")) + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); value = sdhci_readl(host, ESDHC_DMA_SYSCTL); value |= ESDHC_DMA_SNOOP; From 1b5190c2e74c47ebe4bcecf7a072358ad9f1feaa Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 5 Jul 2018 14:18:19 +0200 Subject: [PATCH 30/56] mmc: sdhci: do not try to use 3.3V signaling if not supported For eMMC devices it is valid to only support 1.8V signaling. When vqmmc is set to a fixed 1.8V regulator the stack tries to set 3.3V initially and prints the following warning: mmc1: Switching to 3.3V signalling voltage failed Clear the MMC_SIGNAL_VOLTAGE_330 flag in case 3.3V is signaling is not available. This prevents the stack from even trying to use 3.3V signaling and avoids the above warning. Signed-off-by: Stefan Agner Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1c828e0e9905..a7b5602ef6f7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3734,14 +3734,21 @@ int sdhci_setup_host(struct sdhci_host *host) mmc_gpio_get_cd(host->mmc) < 0) mmc->caps |= MMC_CAP_NEEDS_POLL; - /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ if (!IS_ERR(mmc->supply.vqmmc)) { ret = regulator_enable(mmc->supply.vqmmc); + + /* If vqmmc provides no 1.8V signalling, then there's no UHS */ if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000, 1950000)) host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + + /* In eMMC case vqmmc might be a fixed 1.8V regulator */ + if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 2700000, + 3600000)) + host->flags &= ~SDHCI_SIGNALING_330; + if (ret) { pr_warn("%s: Failed to enable vqmmc regulator: %d\n", mmc_hostname(mmc), ret); From aaadfa66908460bb4e29eb3904b3d386e6b1835f Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 6 Jul 2018 15:20:47 +0800 Subject: [PATCH 31/56] dt: bindings: Add bindings for SDHCI Synopsys DWC MSHC Synopsys SDHCI compatible DesignWare Cores Mobile Storage Host Controller can support eMMC/SD/SDIO. Add the bindings. Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson --- .../bindings/mmc/sdhci-of-dwcmshc.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-of-dwcmshc.txt diff --git a/Documentation/devicetree/bindings/mmc/sdhci-of-dwcmshc.txt b/Documentation/devicetree/bindings/mmc/sdhci-of-dwcmshc.txt new file mode 100644 index 000000000000..ee4253b33be2 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sdhci-of-dwcmshc.txt @@ -0,0 +1,20 @@ +* Synopsys DesignWare Cores Mobile Storage Host Controller + +Required properties: +- compatible: should be one of the following: + "snps,dwcmshc-sdhci" +- reg: offset and length of the register set for the device. +- interrupts: a single interrupt specifier. +- clocks: Array of clocks required for SDHCI; requires at least one for + core clock. +- clock-names: Array of names corresponding to clocks property; shall be + "core" for core clock and "bus" for optional bus clock. + +Example: + sdhci2: sdhci@aa0000 { + compatible = "snps,dwcmshc-sdhci"; + reg = <0xaa0000 0x1000>; + interrupts = ; + clocks = <&emmcclk>; + bus-width = <8>; + } From e438cf49b3053ee79ad802a72d4a27bff77c86d8 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 6 Jul 2018 15:23:55 +0800 Subject: [PATCH 32/56] mmc: sdhci-of-dwcmshc: add SDHCI OF Synopsys DWC MSHC driver Add a driver for SDHCI OF Synopsys DesignWare Cores Mobile Storage Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson Acked-by: Adrian Hunter --- drivers/mmc/host/Kconfig | 11 +++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-of-dwcmshc.c | 116 ++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 drivers/mmc/host/sdhci-of-dwcmshc.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 0581c199c996..694d0828215d 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -176,6 +176,17 @@ config MMC_SDHCI_OF_HLWD If unsure, say N. +config MMC_SDHCI_OF_DWCMSHC + tristate "SDHCI OF support for the Synopsys DWC MSHC" + depends on MMC_SDHCI_PLTFM + depends on OF + depends on COMMON_CLK + help + This selects Synopsys DesignWare Cores Mobile Storage Controller + support. + If you have a controller with this interface, say Y or M here. + If unsure, say N. + config MMC_SDHCI_CADENCE tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 85dc1322c3de..a18fbba1b97e 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o +obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c new file mode 100644 index 000000000000..1b7cd144fb01 --- /dev/null +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Synopsys DesignWare Cores Mobile Storage Host Controller + * + * Copyright (C) 2018 Synaptics Incorporated + * + * Author: Jisheng Zhang + */ + +#include +#include +#include + +#include "sdhci-pltfm.h" + +struct dwcmshc_priv { + struct clk *bus_clk; +}; + +static const struct sdhci_ops sdhci_dwcmshc_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .reset = sdhci_reset, +}; + +static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { + .ops = &sdhci_dwcmshc_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, +}; + +static int dwcmshc_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_host *host; + struct dwcmshc_priv *priv; + int err; + + host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata, + sizeof(struct dwcmshc_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); + + pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(pltfm_host->clk)) { + err = PTR_ERR(pltfm_host->clk); + dev_err(&pdev->dev, "failed to get core clk: %d\n", err); + goto free_pltfm; + } + err = clk_prepare_enable(pltfm_host->clk); + if (err) + goto free_pltfm; + + priv->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (!IS_ERR(priv->bus_clk)) + clk_prepare_enable(priv->bus_clk); + + err = mmc_of_parse(host->mmc); + if (err) + goto err_clk; + + sdhci_get_of_property(pdev); + + err = sdhci_add_host(host); + if (err) + goto err_clk; + + return 0; + +err_clk: + clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(priv->bus_clk); +free_pltfm: + sdhci_pltfm_free(pdev); + return err; +} + +static int dwcmshc_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + + sdhci_remove_host(host, 0); + + clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(priv->bus_clk); + + sdhci_pltfm_free(pdev); + + return 0; +} + +static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { + { .compatible = "snps,dwcmshc-sdhci" }, + {} +}; +MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); + +static struct platform_driver sdhci_dwcmshc_driver = { + .driver = { + .name = "sdhci-dwcmshc", + .of_match_table = sdhci_dwcmshc_dt_ids, + }, + .probe = dwcmshc_probe, + .remove = dwcmshc_remove, +}; +module_platform_driver(sdhci_dwcmshc_driver); + +MODULE_DESCRIPTION("SDHCI platform driver for Synopsys DWC MSHC"); +MODULE_AUTHOR("Jisheng Zhang "); +MODULE_LICENSE("GPL v2"); From a6e7e407a68eee166c83e93c4ce8041aa00c2875 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 6 Jul 2018 14:40:07 -0300 Subject: [PATCH 33/56] mmc: sdhci-esdhc-imx: Switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Fabio Estevam Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 85fd5a8b0b6d..dc55461ababa 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Freescale eSDHC i.MX controller driver for the platform bus. * @@ -5,10 +6,6 @@ * * Copyright (c) 2010 Pengutronix e.K. * Author: Wolfram Sang - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. */ #include From 152f8204ffcd7b64aa6b36f39ee86fbce3f650b8 Mon Sep 17 00:00:00 2001 From: Prabu Thangamuthu Date: Wed, 11 Jul 2018 13:26:17 +0530 Subject: [PATCH 34/56] mmc: sdhci-pci-dwc-mshc: synopsys dwc mshc support Synopsys has DWC MSHC controller on HPAS-DX platform connected using PCIe interface with SD card slot and eMMC device slots. This patch is to enable SD cards connected on this platform. As Clock generation logic is implemented using MMCM module of HAPS-DX platform, we have separate functions to control the MMCM to generate required clocks with respect to speed mode. Signed-off-by: Prabu Thangamuthu Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- MAINTAINERS | 7 +++ drivers/mmc/host/Makefile | 3 +- drivers/mmc/host/sdhci-pci-core.c | 1 + drivers/mmc/host/sdhci-pci-dwc-mshc.c | 84 +++++++++++++++++++++++++++ drivers/mmc/host/sdhci-pci.h | 3 + 5 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 drivers/mmc/host/sdhci-pci-dwc-mshc.c diff --git a/MAINTAINERS b/MAINTAINERS index 192d7f73fd01..aa50f070b3b5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12745,6 +12745,13 @@ S: Maintained F: drivers/mmc/host/sdhci* F: include/linux/mmc/sdhci* +SYNOPSYS SDHCI COMPLIANT DWC MSHC DRIVER +M: Prabu Thangamuthu +M: Manjunath M B +L: linux-mmc@vger.kernel.org +S: Maintained +F: drivers/mmc/host/sdhci-pci-dwc-mshc.c + SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) SAMSUNG DRIVER M: Ben Dooks M: Jaehoon Chung diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index a18fbba1b97e..ce8398e6f2c0 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -11,7 +11,8 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o -sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o +sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \ + sdhci-pci-dwc-mshc.o obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 3d367613275b..7bfd366d970d 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1513,6 +1513,7 @@ static const struct pci_device_id pci_ids[] = { SDHCI_PCI_DEVICE(O2, SEABIRD0, o2), SDHCI_PCI_DEVICE(O2, SEABIRD1, o2), SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan), + SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps), SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd), /* Generic SD host controller */ {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)}, diff --git a/drivers/mmc/host/sdhci-pci-dwc-mshc.c b/drivers/mmc/host/sdhci-pci-dwc-mshc.c new file mode 100644 index 000000000000..f78d65448d17 --- /dev/null +++ b/drivers/mmc/host/sdhci-pci-dwc-mshc.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SDHCI driver for Synopsys DWC_MSHC controller + * + * Copyright (C) 2018 Synopsys, Inc. (www.synopsys.com) + * + * Authors: + * Prabu Thangamuthu + * Manjunath M B + */ + +#include "sdhci.h" +#include "sdhci-pci.h" + +#define SDHCI_VENDOR_PTR_R 0xE8 + +/* Synopsys vendor specific registers */ +#define SDHC_GPIO_OUT 0x34 +#define SDHC_AT_CTRL_R 0x40 +#define SDHC_SW_TUNE_EN 0x00000010 + +/* MMCM DRP */ +#define SDHC_MMCM_DIV_REG 0x1020 +#define DIV_REG_100_MHZ 0x1145 +#define DIV_REG_200_MHZ 0x1083 +#define SDHC_MMCM_CLKFBOUT 0x1024 +#define CLKFBOUT_100_MHZ 0x0000 +#define CLKFBOUT_200_MHZ 0x0080 +#define SDHC_CCLK_MMCM_RST 0x00000001 + +static void sdhci_snps_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + u32 reg, vendor_ptr; + + vendor_ptr = sdhci_readw(host, SDHCI_VENDOR_PTR_R); + + /* Disable software managed rx tuning */ + reg = sdhci_readl(host, (SDHC_AT_CTRL_R + vendor_ptr)); + reg &= ~SDHC_SW_TUNE_EN; + sdhci_writel(host, reg, (SDHC_AT_CTRL_R + vendor_ptr)); + + if (clock <= 52000000) { + sdhci_set_clock(host, clock); + } else { + /* Assert reset to MMCM */ + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr)); + reg |= SDHC_CCLK_MMCM_RST; + sdhci_writel(host, reg, (SDHC_GPIO_OUT + vendor_ptr)); + + /* Configure MMCM */ + if (clock == 100000000) { + sdhci_writel(host, DIV_REG_100_MHZ, SDHC_MMCM_DIV_REG); + sdhci_writel(host, CLKFBOUT_100_MHZ, + SDHC_MMCM_CLKFBOUT); + } else { + sdhci_writel(host, DIV_REG_200_MHZ, SDHC_MMCM_DIV_REG); + sdhci_writel(host, CLKFBOUT_200_MHZ, + SDHC_MMCM_CLKFBOUT); + } + + /* De-assert reset to MMCM */ + reg = sdhci_readl(host, (SDHC_GPIO_OUT + vendor_ptr)); + reg &= ~SDHC_CCLK_MMCM_RST; + sdhci_writel(host, reg, (SDHC_GPIO_OUT + vendor_ptr)); + + /* Enable clock */ + clk = SDHCI_PROG_CLOCK_MODE | SDHCI_CLOCK_INT_EN | + SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + } +} + +static const struct sdhci_ops sdhci_snps_ops = { + .set_clock = sdhci_snps_set_clock, + .enable_dma = sdhci_pci_enable_dma, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +const struct sdhci_pci_fixes sdhci_snps = { + .ops = &sdhci_snps_ops, +}; diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 59af2886631f..2ef0bdca9197 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -61,6 +61,8 @@ #define PCI_VENDOR_ID_ARASAN 0x16e6 #define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670 +#define PCI_DEVICE_ID_SYNOPSYS_DWC_MSHC 0xc202 + /* * PCI device class and mask */ @@ -184,5 +186,6 @@ int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip); #endif extern const struct sdhci_pci_fixes sdhci_arasan; +extern const struct sdhci_pci_fixes sdhci_snps; #endif /* __SDHCI_PCI_H */ From 127407e36f4fe3a1d5e8b9998b479956ce83a7dc Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 12 Jul 2018 09:39:02 +0200 Subject: [PATCH 35/56] mmc: tegra: prevent HS200 on Tegra 3 The stack assumes that SDHC controller which support SD3.0 (SDR104) do support HS200. This is not the case for Tegra 3, which does support SD 3.0 but only supports eMMC spec 4.41. Use SDHCI_QUIRK2_BROKEN_HS200 to indicate that the controller does not support HS200. Note that commit 156e14b126ff ("mmc: sdhci: fix caps2 for HS200") added the tie between SD3.0 (SDR104) and HS200. I don't think that this is necessarly true. It is fully legitimate to support SD3.0 and not support HS200. The quirk naming suggests something is broken in the controller, but this is not the case: The controller simply does not support HS200. Fixes: 7ad2ed1dfcbe ("mmc: tegra: enable UHS-I modes") Signed-off-by: Stefan Agner Tested-by: Marcel Ziswiler Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index c8745b5547d8..5355d06d0a5e 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -323,7 +323,8 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_BROKEN_HS200, .ops = &tegra_sdhci_ops, }; From e300149e580863b00a736b534c7163dcf0813641 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 12 Jul 2018 09:39:03 +0200 Subject: [PATCH 36/56] mmc: tegra: fix eMMC DDR52 mode Make sure the clock is doubled when using eMMC DDR52 mode. Signed-off-by: Stefan Agner Tested-by: Marcel Ziswiler Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 5355d06d0a5e..e231c3e11bcd 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -228,7 +228,8 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); - if (timing == MMC_TIMING_UHS_DDR50) + if (timing == MMC_TIMING_UHS_DDR50 || + timing == MMC_TIMING_MMC_DDR52) tegra_host->ddr_signaling = true; sdhci_set_uhs_signaling(host, timing); From 726df1d5a62ec8241a2823f6b23728db61178eaa Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 12 Jul 2018 09:39:04 +0200 Subject: [PATCH 37/56] mmc: tegra: prevent ACMD23 on Tegra 3 It seems that SD3.0 advertisement needs to be set for higher eMMC speed modes (namely DDR52) as well. The TRM states that the SD3.0 advertisement bit should be set for all controller instances, even for those not supporting UHS-I mode... When specifying vqmmc-supply as a fixed 1.8V regulator on a Tegra SD/MMC instance which is connected to a eMMC device, the stack enables SD3.0. However, enabling it has consequences: If SDHCI 3.0 support is advertised the stack enables Auto-CMD23. Unfortunately Auto-CMD23 seems not to work well with Tegra 3 currently. It leads to regular warnings: mmc2: Got command interrupt 0x00010000 even though no command operation was in progress. It is not entirely clear why those errors happens. It seems that a Linux 3.1 based downstream kernel which has Auto-CMD23 support does not show those warnings. Use quirk SDHCI_QUIRK2_ACMD23_BROKEN to prevent Auto-CMD23 being used for now. With this the eMMC works stable on high-speed mode while still announcing SD3.0. This allows to use mmc-ddr-1_8v to enables DDR52 mode. In DDR52 mode read speed improves from about 42MiB/s to 72MiB/s on an Apalis T30. Signed-off-by: Stefan Agner Tested-by: Marcel Ziswiler Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index e231c3e11bcd..28b98e26106b 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -325,7 +325,15 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = { SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_BROKEN_HS200, + SDHCI_QUIRK2_BROKEN_HS200 | + /* + * Auto-CMD23 leads to "Got command interrupt 0x00010000 even + * though no command operation was in progress." + * + * The exact reason is unknown, as the same hardware seems + * to support Auto CMD23 on a downstream 3.1 kernel. + */ + SDHCI_QUIRK2_ACMD23_BROKEN, .ops = &tegra_sdhci_ops, }; From 73e736f865780cf01652b863d2016dde480c0f6f Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 12 Jul 2018 10:07:24 +0200 Subject: [PATCH 38/56] mmc: sdhci-esdhc-imx: disable clocks before changing frequency In the uSDHC case (e.g. i.MX 6) clocks only get disabled if frequency is set to 0. However, it could be that the stack asks for a frequency change while clocks are on. In that case the function clears the divider registers (by clearing ESDHC_CLOCK_MASK) while the clock is enabled! This causes a short period of time where the clock is undivided (on a i.MX 6DL a clock of 196MHz has been measured). For older IP variants the driver disables clock by clearing some bits in ESDHC_SYSTEM_CONTROL. Make sure to disable card clock before changing frequency for uSDHC IP variants too. Signed-off-by: Stefan Agner Acked-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index dc55461ababa..9e838409ce69 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -705,14 +705,14 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, int div = 1; u32 temp, val; + if (esdhc_is_usdhc(imx_data)) { + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, + host->ioaddr + ESDHC_VENDOR_SPEC); + } + if (clock == 0) { host->mmc->actual_clock = 0; - - if (esdhc_is_usdhc(imx_data)) { - val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); - writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, - host->ioaddr + ESDHC_VENDOR_SPEC); - } return; } From d47b0586ab31771e49e6b1af049e09bf33fbe867 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Thu, 12 Jul 2018 10:07:25 +0200 Subject: [PATCH 39/56] mmc: sdhci-esdhc-imx: fix indent Fix indent. This also makes disable/enable clock blocks look alike. Signed-off-by: Stefan Agner Acked-by: Dong Aisheng Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 9e838409ce69..f44e49014a44 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -758,7 +758,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, if (esdhc_is_usdhc(imx_data)) { val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, - host->ioaddr + ESDHC_VENDOR_SPEC); + host->ioaddr + ESDHC_VENDOR_SPEC); } mdelay(1); From 44350993d01c901cc201ef2663fd0fded2e03d06 Mon Sep 17 00:00:00 2001 From: Aapo Vienamo Date: Fri, 13 Jul 2018 16:17:45 +0300 Subject: [PATCH 40/56] mmc: tegra: Add and use tegra_sdhci_get_max_clock() Implement and use tegra_sdhci_get_max_clock() which returns the true maximum host clock rate. The issue with tegra_sdhci_get_max_clock() is that it returns the current clock rate of the host instead of the maximum one, which can lead to unnecessarily small clock rates. This differs from the previous implementation of tegra_sdhci_get_max_clock() in that it doesn't divide the result by two. Signed-off-by: Aapo Vienamo Tested-by: Marcel Ziswiler Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 28b98e26106b..ddf001669609 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -235,6 +235,13 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, sdhci_set_uhs_signaling(host, timing); } +static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return clk_round_rate(pltfm_host->clk, UINT_MAX); +} + static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap) { u32 reg; @@ -299,7 +306,7 @@ static const struct sdhci_ops tegra_sdhci_ops = { .platform_execute_tuning = tegra_sdhci_execute_tuning, .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, .voltage_switch = tegra_sdhci_voltage_switch, - .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_max_clock = tegra_sdhci_get_max_clock, }; static const struct sdhci_pltfm_data sdhci_tegra20_pdata = { @@ -356,7 +363,7 @@ static const struct sdhci_ops tegra114_sdhci_ops = { .platform_execute_tuning = tegra_sdhci_execute_tuning, .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, .voltage_switch = tegra_sdhci_voltage_switch, - .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_max_clock = tegra_sdhci_get_max_clock, }; static const struct sdhci_pltfm_data sdhci_tegra114_pdata = { From 57322d542ffe01076c9df10d38e3ec0a58f9d83a Mon Sep 17 00:00:00 2001 From: "ernest.zhang" Date: Mon, 16 Jul 2018 14:26:51 +0800 Subject: [PATCH 41/56] mmc: sdhci: Add support for O2 eMMC HS200 mode When use eMMC as boot device, the eMMC signaling voltage is tied to 1.8v fixed output voltage, bios can set o2 sd host controller PCI configuration register 0x308 bit4 to 1 to let driver skip 3.3v signaling voltage and direct use 1.8v singling voltage in eMMC initialize process. Signed-off-by: ernest.zhang Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-o2micro.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 555970a29c94..ba59db6a126c 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -3,6 +3,7 @@ * * Authors: Peter Guo * Adam Lee + * Ernest Zhang * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -39,6 +40,7 @@ #define O2_SD_MISC_CTRL4 0xFC #define O2_SD_TUNING_CTRL 0x300 #define O2_SD_PLL_SETTING 0x304 +#define O2_SD_MISC_SETTING 0x308 #define O2_SD_CLK_SETTING 0x328 #define O2_SD_CAP_REG2 0x330 #define O2_SD_CAP_REG0 0x334 @@ -184,6 +186,7 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) struct sdhci_pci_chip *chip; struct sdhci_host *host; u32 reg; + int ret; chip = slot->chip; host = slot->host; @@ -197,6 +200,21 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) if (reg & 0x1) host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + if (chip->pdev->device == PCI_DEVICE_ID_O2_SEABIRD0) { + ret = pci_read_config_dword(chip->pdev, + O2_SD_MISC_SETTING, ®); + if (ret) + return -EIO; + if (reg & (1 << 4)) { + pr_info("%s: emmc 1.8v flag is set, force 1.8v signaling voltage\n", + mmc_hostname(host->mmc)); + host->flags &= ~SDHCI_SIGNALING_330; + host->flags |= SDHCI_SIGNALING_180; + host->mmc->caps2 |= MMC_CAP2_NO_SD; + host->mmc->caps2 |= MMC_CAP2_NO_SDIO; + } + } + if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2) break; /* set dll watch dog timer */ From 3665ff03c16ee6436d45a699565ba08f7ebdebb5 Mon Sep 17 00:00:00 2001 From: "ernest.zhang" Date: Mon, 16 Jul 2018 14:26:52 +0800 Subject: [PATCH 42/56] mmc: sdhci: Change O2 Host HS200 mode clock frequency to 200MHz O2 SD Host HS200 mode clock frequency current is 208MHz, should be changed to 200MHz to meet specification. Signed-off-by: ernest.zhang Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-o2micro.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index ba59db6a126c..7b37713d8826 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -311,9 +311,8 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) /* Check Whether subId is 0x11 or 0x12 */ if ((scratch_32 == 0x11) || (scratch_32 == 0x12)) { - scratch_32 = 0x2c280000; + scratch_32 = 0x25100000; - /* Set Base Clock to 208MZ */ o2_pci_set_baseclk(chip, scratch_32); ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG4, @@ -406,7 +405,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) O2_SD_PLL_SETTING, scratch_32); } else { scratch_32 &= 0x0000FFFF; - scratch_32 |= 0x2c280000; + scratch_32 |= 0x25100000; pci_write_config_dword(chip->pdev, O2_SD_PLL_SETTING, scratch_32); From 6663c419a046cdfcf3598b6d0855817b5c6287ae Mon Sep 17 00:00:00 2001 From: "ernest.zhang" Date: Mon, 16 Jul 2018 14:26:53 +0800 Subject: [PATCH 43/56] mmc: sdhci: Export sdhci tuning function symbol Export sdhci tuning function symbols which are used by other SD Host controller driver modules. Signed-off-by: ernest.zhang Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 12 ++++++++---- drivers/mmc/host/sdhci.h | 5 +++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a7b5602ef6f7..bcea179a8d70 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2103,7 +2103,7 @@ static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) return 0; } -static void sdhci_start_tuning(struct sdhci_host *host) +void sdhci_start_tuning(struct sdhci_host *host) { u16 ctrl; @@ -2126,14 +2126,16 @@ static void sdhci_start_tuning(struct sdhci_host *host) sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE); sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE); } +EXPORT_SYMBOL_GPL(sdhci_start_tuning); -static void sdhci_end_tuning(struct sdhci_host *host) +void sdhci_end_tuning(struct sdhci_host *host) { sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } +EXPORT_SYMBOL_GPL(sdhci_end_tuning); -static void sdhci_reset_tuning(struct sdhci_host *host) +void sdhci_reset_tuning(struct sdhci_host *host) { u16 ctrl; @@ -2142,6 +2144,7 @@ static void sdhci_reset_tuning(struct sdhci_host *host) ctrl &= ~SDHCI_CTRL_EXEC_TUNING; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); } +EXPORT_SYMBOL_GPL(sdhci_reset_tuning); static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode) { @@ -2162,7 +2165,7 @@ static void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode) * interrupt setup is different to other commands and there is no timeout * interrupt so special handling is needed. */ -static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode) +void sdhci_send_tuning(struct sdhci_host *host, u32 opcode) { struct mmc_host *mmc = host->mmc; struct mmc_command cmd = {}; @@ -2212,6 +2215,7 @@ static void sdhci_send_tuning(struct sdhci_host *host, u32 opcode) msecs_to_jiffies(50)); } +EXPORT_SYMBOL_GPL(sdhci_send_tuning); static void __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) { diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 23966f887da6..f0bd36ce3817 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -748,4 +748,9 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error, void sdhci_dumpregs(struct sdhci_host *host); +void sdhci_start_tuning(struct sdhci_host *host); +void sdhci_end_tuning(struct sdhci_host *host); +void sdhci_reset_tuning(struct sdhci_host *host); +void sdhci_send_tuning(struct sdhci_host *host, u32 opcode); + #endif /* __SDHCI_HW_H */ From 0086fc217d5d7ac2939c500733d1d046b9ac5012 Mon Sep 17 00:00:00 2001 From: "ernest.zhang" Date: Mon, 16 Jul 2018 14:26:54 +0800 Subject: [PATCH 44/56] mmc: sdhci: Add support for O2 hardware tuning Add hardware tuning function instead of software tuning because O2/Bayhub SD host controller support hardware tuning. Signed-off-by: ernest.zhang Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-o2micro.c | 81 ++++++++++++++++++++++++++++ drivers/mmc/host/sdhci.c | 4 +- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 7b37713d8826..98e26ee1a972 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -17,6 +17,9 @@ */ #include +#include +#include +#include #include "sdhci.h" #include "sdhci-pci.h" @@ -55,6 +58,82 @@ #define O2_SD_VENDOR_SETTING 0x110 #define O2_SD_VENDOR_SETTING2 0x1C8 +#define O2_SD_HW_TUNING_DISABLE BIT(4) + +static void sdhci_o2_set_tuning_mode(struct sdhci_host *host) +{ + u16 reg; + + /* enable hardware tuning */ + reg = sdhci_readw(host, O2_SD_VENDOR_SETTING); + reg &= ~O2_SD_HW_TUNING_DISABLE; + sdhci_writew(host, reg, O2_SD_VENDOR_SETTING); +} + +static void __sdhci_o2_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + int i; + + sdhci_send_tuning(host, MMC_SEND_TUNING_BLOCK_HS200); + + for (i = 0; i < 150; i++) { + u16 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + + if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) { + if (ctrl & SDHCI_CTRL_TUNED_CLK) { + host->tuning_done = true; + return; + } + pr_warn("%s: HW tuning failed !\n", + mmc_hostname(host->mmc)); + break; + } + + mdelay(1); + } + + pr_info("%s: Tuning failed, falling back to fixed sampling clock\n", + mmc_hostname(host->mmc)); + sdhci_reset_tuning(host); +} + +static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct sdhci_host *host = mmc_priv(mmc); + int current_bus_width = 0; + + /* + * This handler only implements the eMMC tuning that is specific to + * this controller. Fall back to the standard method for other TIMING. + */ + if (host->timing != MMC_TIMING_MMC_HS200) + return sdhci_execute_tuning(mmc, opcode); + + if (WARN_ON(opcode != MMC_SEND_TUNING_BLOCK_HS200)) + return -EINVAL; + + /* + * o2 sdhci host didn't support 8bit emmc tuning + */ + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { + current_bus_width = mmc->ios.bus_width; + sdhci_set_bus_width(host, MMC_BUS_WIDTH_4); + } + + sdhci_o2_set_tuning_mode(host); + + sdhci_start_tuning(host); + + __sdhci_o2_execute_tuning(host, opcode); + + sdhci_end_tuning(host); + + if (current_bus_width == MMC_BUS_WIDTH_8) + sdhci_set_bus_width(host, current_bus_width); + + host->flags &= ~SDHCI_HS400_TUNING; + return 0; +} static void o2_pci_set_baseclk(struct sdhci_pci_chip *chip, u32 value) { @@ -215,6 +294,8 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) } } + host->mmc_host_ops.execute_tuning = sdhci_o2_execute_tuning; + if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2) break; /* set dll watch dog timer */ diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index bcea179a8d70..1b3fbd9bd5c5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1029,7 +1029,9 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, if (data == NULL) { if (host->quirks2 & SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) { - sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); + /* must not clear SDHCI_TRANSFER_MODE when tuning */ + if (cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) + sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); } else { /* clear Auto CMD settings for no data CMDs */ mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); From 02a3c0bd607402e4b7a5026f5a498291e8ade6f8 Mon Sep 17 00:00:00 2001 From: "ernest.zhang" Date: Mon, 16 Jul 2018 14:26:55 +0800 Subject: [PATCH 45/56] mmc: sdhci: Add MSI interrupt support for O2 SD host Add MSI interrupt support if the SD host device can support MSI interrupt. Signed-off-by: ernest.zhang Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-o2micro.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index 98e26ee1a972..77e9bc4aaee9 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -260,6 +260,29 @@ static void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32); } +static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip, + struct sdhci_host *host) +{ + int ret; + + ret = pci_find_capability(chip->pdev, PCI_CAP_ID_MSI); + if (!ret) { + pr_info("%s: unsupport msi, use INTx irq\n", + mmc_hostname(host->mmc)); + return; + } + + ret = pci_alloc_irq_vectors(chip->pdev, 1, 1, + PCI_IRQ_MSI | PCI_IRQ_MSIX); + if (ret < 0) { + pr_err("%s: enable PCI MSI failed, err=%d\n", + mmc_hostname(host->mmc), ret); + return; + } + + host->irq = pci_irq_vector(chip->pdev, 0); +} + int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) { struct sdhci_pci_chip *chip; @@ -279,6 +302,8 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) if (reg & 0x1) host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + sdhci_pci_o2_enable_msi(chip, host); + if (chip->pdev->device == PCI_DEVICE_ID_O2_SEABIRD0) { ret = pci_read_config_dword(chip->pdev, O2_SD_MISC_SETTING, ®); From 57d1654ec96a49f5a093f9cbe40718c92ab5daa0 Mon Sep 17 00:00:00 2001 From: Aapo Vienamo Date: Mon, 16 Jul 2018 17:34:29 +0300 Subject: [PATCH 46/56] mmc: tegra: Force correct divider calculation on DDR50/52 Tegra SDHCI controllers require the SDHCI clock divider to be configured to divide the clock by two in DDR50/52 modes. Incorrectly configured clock divider results in corrupted data. Prevent the possibility of incorrectly calculating the divider value due to clock rate rounding or low parent clock frequency by not assigning host->max_clk to clk_get_rate() on tegra_sdhci_set_clock(). See the comments for further details. Fixes: a8e326a ("mmc: tegra: implement module external clock change") Signed-off-by: Aapo Vienamo Acked-by: Jon Hunter Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index ddf001669609..908b23e6a03c 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -210,9 +210,24 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if (!clock) return sdhci_set_clock(host, clock); + /* + * In DDR50/52 modes the Tegra SDHCI controllers require the SDHCI + * divider to be configured to divided the host clock by two. The SDHCI + * clock divider is calculated as part of sdhci_set_clock() by + * sdhci_calc_clk(). The divider is calculated from host->max_clk and + * the requested clock rate. + * + * By setting the host->max_clk to clock * 2 the divider calculation + * will always result in the correct value for DDR50/52 modes, + * regardless of clock rate rounding, which may happen if the value + * from clk_get_rate() is used. + */ host_clk = tegra_host->ddr_signaling ? clock * 2 : clock; clk_set_rate(pltfm_host->clk, host_clk); - host->max_clk = clk_get_rate(pltfm_host->clk); + if (tegra_host->ddr_signaling) + host->max_clk = host_clk; + else + host->max_clk = clk_get_rate(pltfm_host->clk); sdhci_set_clock(host, clock); From ed9067fd5f299db7110861a0434d0e2ffb961649 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 13 Jul 2018 13:15:23 +0200 Subject: [PATCH 47/56] mmc: mmci: Initial support to manage variant specific callbacks To be able to better support different mmci variants, we need to be able to use variant specific callbacks, rather than continue to sprinkle the code with additional variant data. To move in this direction, let's add an optional ->init() callback to the variant data struct, which variants shall use to assign the mmci_host_ops pointer. Using an ->init() callback enables us to partition the code between different files. To allow separate mmci variant files to implement the variant specifics, let's also move the definition of the struct variant_data to the common mmci header file. Signed-off-by: Ulf Hansson Reviewed-by: Ludovic Barre Acked-by: Linus Walleij --- drivers/mmc/host/mmci.c | 75 ++------------------------------------ drivers/mmc/host/mmci.h | 80 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 73 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index f1849775e47e..e907a0a866da 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -48,78 +48,6 @@ static unsigned int fmax = 515633; -/** - * struct variant_data - MMCI variant-specific quirks - * @clkreg: default value for MCICLOCK register - * @clkreg_enable: enable value for MMCICLOCK register - * @clkreg_8bit_bus_enable: enable value for 8 bit bus - * @clkreg_neg_edge_enable: enable value for inverted data/cmd output - * @datalength_bits: number of bits in the MMCIDATALENGTH register - * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY - * is asserted (likewise for RX) - * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY - * is asserted (likewise for RX) - * @data_cmd_enable: enable value for data commands. - * @st_sdio: enable ST specific SDIO logic - * @st_clkdiv: true if using a ST-specific clock divider algorithm - * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. - * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register - * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl - * register - * @datactrl_mask_sdio: SDIO enable mask in datactrl register - * @pwrreg_powerup: power up value for MMCIPOWER register - * @f_max: maximum clk frequency supported by the controller. - * @signal_direction: input/out direction of bus signals can be indicated - * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock - * @busy_detect: true if the variant supports busy detection on DAT0. - * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM - * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register - * indicating that the card is busy - * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for - * getting busy end detection interrupts - * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply - * @explicit_mclk_control: enable explicit mclk control in driver. - * @qcom_fifo: enables qcom specific fifo pio read logic. - * @qcom_dml: enables qcom specific dma glue for dma transfers. - * @reversed_irq_handling: handle data irq before cmd irq. - * @mmcimask1: true if variant have a MMCIMASK1 register. - * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS - * register. - * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register - */ -struct variant_data { - unsigned int clkreg; - unsigned int clkreg_enable; - unsigned int clkreg_8bit_bus_enable; - unsigned int clkreg_neg_edge_enable; - unsigned int datalength_bits; - unsigned int fifosize; - unsigned int fifohalfsize; - unsigned int data_cmd_enable; - unsigned int datactrl_mask_ddrmode; - unsigned int datactrl_mask_sdio; - bool st_sdio; - bool st_clkdiv; - bool blksz_datactrl16; - bool blksz_datactrl4; - u32 pwrreg_powerup; - u32 f_max; - bool signal_direction; - bool pwrreg_clkgate; - bool busy_detect; - u32 busy_dpsm_flag; - u32 busy_detect_flag; - u32 busy_detect_mask; - bool pwrreg_nopower; - bool explicit_mclk_control; - bool qcom_fifo; - bool qcom_dml; - bool reversed_irq_handling; - bool mmcimask1; - u32 start_err; - u32 opendrain; -}; - static struct variant_data variant_arm = { .fifosize = 16 * 4, .fifohalfsize = 8 * 4, @@ -1706,6 +1634,9 @@ static int mmci_probe(struct amba_device *dev, goto clk_disable; } + if (variant->init) + variant->init(host); + /* * The ARM and ST versions of the block have slightly different * clock divider equations which means that the minimum divider diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index f91cdf7f6dae..f2eff0cc6934 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -195,8 +195,85 @@ #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain" struct clk; -struct variant_data; struct dma_chan; +struct mmci_host; + +/** + * struct variant_data - MMCI variant-specific quirks + * @clkreg: default value for MCICLOCK register + * @clkreg_enable: enable value for MMCICLOCK register + * @clkreg_8bit_bus_enable: enable value for 8 bit bus + * @clkreg_neg_edge_enable: enable value for inverted data/cmd output + * @datalength_bits: number of bits in the MMCIDATALENGTH register + * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY + * is asserted (likewise for RX) + * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY + * is asserted (likewise for RX) + * @data_cmd_enable: enable value for data commands. + * @st_sdio: enable ST specific SDIO logic + * @st_clkdiv: true if using a ST-specific clock divider algorithm + * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. + * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register + * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl + * register + * @datactrl_mask_sdio: SDIO enable mask in datactrl register + * @pwrreg_powerup: power up value for MMCIPOWER register + * @f_max: maximum clk frequency supported by the controller. + * @signal_direction: input/out direction of bus signals can be indicated + * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock + * @busy_detect: true if the variant supports busy detection on DAT0. + * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM + * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register + * indicating that the card is busy + * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for + * getting busy end detection interrupts + * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply + * @explicit_mclk_control: enable explicit mclk control in driver. + * @qcom_fifo: enables qcom specific fifo pio read logic. + * @qcom_dml: enables qcom specific dma glue for dma transfers. + * @reversed_irq_handling: handle data irq before cmd irq. + * @mmcimask1: true if variant have a MMCIMASK1 register. + * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS + * register. + * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register + */ +struct variant_data { + unsigned int clkreg; + unsigned int clkreg_enable; + unsigned int clkreg_8bit_bus_enable; + unsigned int clkreg_neg_edge_enable; + unsigned int datalength_bits; + unsigned int fifosize; + unsigned int fifohalfsize; + unsigned int data_cmd_enable; + unsigned int datactrl_mask_ddrmode; + unsigned int datactrl_mask_sdio; + bool st_sdio; + bool st_clkdiv; + bool blksz_datactrl16; + bool blksz_datactrl4; + u32 pwrreg_powerup; + u32 f_max; + bool signal_direction; + bool pwrreg_clkgate; + bool busy_detect; + u32 busy_dpsm_flag; + u32 busy_detect_flag; + u32 busy_detect_mask; + bool pwrreg_nopower; + bool explicit_mclk_control; + bool qcom_fifo; + bool qcom_dml; + bool reversed_irq_handling; + bool mmcimask1; + u32 start_err; + u32 opendrain; + void (*init)(struct mmci_host *host); +}; + +/* mmci variant callbacks */ +struct mmci_host_ops { +}; struct mmci_host_next { struct dma_async_tx_descriptor *dma_desc; @@ -228,6 +305,7 @@ struct mmci_host { u32 mask1_reg; bool vqmmc_enabled; struct mmci_platform_data *plat; + struct mmci_host_ops *ops; struct variant_data *variant; struct pinctrl *pinctrl; struct pinctrl_state *pins_default; From 29aba07aea0e36c996001ca3f8997316ebefe0e1 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 16 Jul 2018 13:08:18 +0200 Subject: [PATCH 48/56] mmc: mmci: Add and implement a ->dma_setup() callback for qcom dml As a first step to improve the variant specific code for mmci, add a ->dma_setup() callback to the struct mmci_host_ops. To show its use, let's deploy the callback for the qcom dml, which involves also to the assign the mmci_host_ops pointer from the variant ->init() callback. Signed-off-by: Ulf Hansson Reviewed-by: Ludovic Barre --- drivers/mmc/host/mmci.c | 7 +++---- drivers/mmc/host/mmci.h | 1 + drivers/mmc/host/mmci_qcom_dml.c | 20 +++++++++++++++----- drivers/mmc/host/mmci_qcom_dml.h | 5 ++--- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index e907a0a866da..1841d250e9e2 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -208,6 +208,7 @@ static struct variant_data variant_qcom = { .mmcimask1 = true, .start_err = MCI_STARTBITERR, .opendrain = MCI_ROD, + .init = qcom_variant_init, }; /* Busy detection for the ST Micro variant */ @@ -417,7 +418,6 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) static void mmci_dma_setup(struct mmci_host *host) { const char *rxname, *txname; - struct variant_data *variant = host->variant; host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); @@ -465,9 +465,8 @@ static void mmci_dma_setup(struct mmci_host *host) host->mmc->max_seg_size = max_seg_size; } - if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel) - if (dml_hw_init(host, host->mmc->parent->of_node)) - variant->qcom_dml = false; + if (host->ops && host->ops->dma_setup) + host->ops->dma_setup(host); } /* diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index f2eff0cc6934..517591d219e9 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -273,6 +273,7 @@ struct variant_data { /* mmci variant callbacks */ struct mmci_host_ops { + void (*dma_setup)(struct mmci_host *host); }; struct mmci_host_next { diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c index 00750c9d3514..be3fab5db83f 100644 --- a/drivers/mmc/host/mmci_qcom_dml.c +++ b/drivers/mmc/host/mmci_qcom_dml.c @@ -119,17 +119,20 @@ static int of_get_dml_pipe_index(struct device_node *np, const char *name) } /* Initialize the dml hardware connected to SD Card controller */ -int dml_hw_init(struct mmci_host *host, struct device_node *np) +static void qcom_dma_setup(struct mmci_host *host) { u32 config; void __iomem *base; int consumer_id, producer_id; + struct device_node *np = host->mmc->parent->of_node; consumer_id = of_get_dml_pipe_index(np, "tx"); producer_id = of_get_dml_pipe_index(np, "rx"); - if (producer_id < 0 || consumer_id < 0) - return -ENODEV; + if (producer_id < 0 || consumer_id < 0) { + host->variant->qcom_dml = false; + return; + } base = host->base + DML_OFFSET; @@ -172,6 +175,13 @@ int dml_hw_init(struct mmci_host *host, struct device_node *np) /* Make sure dml initialization is finished */ mb(); - - return 0; +} + +static struct mmci_host_ops qcom_variant_ops = { + .dma_setup = qcom_dma_setup, +}; + +void qcom_variant_init(struct mmci_host *host) +{ + host->ops = &qcom_variant_ops; } diff --git a/drivers/mmc/host/mmci_qcom_dml.h b/drivers/mmc/host/mmci_qcom_dml.h index 6e405d09d534..fa16f6f4d4ad 100644 --- a/drivers/mmc/host/mmci_qcom_dml.h +++ b/drivers/mmc/host/mmci_qcom_dml.h @@ -16,12 +16,11 @@ #define __MMC_QCOM_DML_H__ #ifdef CONFIG_MMC_QCOM_DML -int dml_hw_init(struct mmci_host *host, struct device_node *np); +void qcom_variant_init(struct mmci_host *host); void dml_start_xfer(struct mmci_host *host, struct mmc_data *data); #else -static inline int dml_hw_init(struct mmci_host *host, struct device_node *np) +static inline void qcom_variant_init(struct mmci_host *host) { - return -ENOSYS; } static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data) { From 88023d43ffe114d4f8c35995151ef6f8633f234a Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 22 Jul 2018 20:54:07 +0800 Subject: [PATCH 49/56] mmc: sunxi: allow 3.3V DDR when DDR is available Some Allwinner boards feature an on-board eMMC with fixed 3.3V voltage (e.g. Banana Pi M2+), and in this case both the eMMC and the SoC are capable of doing 3.3V DDR transmission. Add capability of 3.3V DDR when DDR is available (extra clock or new timing). Signed-off-by: Icenowy Zheng Acked-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 8e7f3e35ee3d..4ea8e2611079 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1388,7 +1388,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev) MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ; if (host->cfg->clk_delays || host->use_new_timings) - mmc->caps |= MMC_CAP_1_8V_DDR; + mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR; ret = mmc_of_parse(mmc); if (ret) From e432e4207c3e76951947dd9a93a86025e075a905 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 21 Jul 2018 13:14:49 +0200 Subject: [PATCH 50/56] mmc: renesas_sdhi: Add r8a77990 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds SDHI support for the R8A77990 SoC (R-Car E3). No driver changes needed for anything except HS400 which we will enable separately later. Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Reviewed-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/tmio_mmc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt index 839f469f4525..c434200d19d5 100644 --- a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt +++ b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt @@ -28,6 +28,7 @@ Required properties: "renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC "renesas,sdhi-r8a77965" - SDHI IP on R8A77965 SoC "renesas,sdhi-r8a77980" - SDHI IP on R8A77980 SoC + "renesas,sdhi-r8a77990" - SDHI IP on R8A77990 SoC "renesas,sdhi-r8a77995" - SDHI IP on R8A77995 SoC "renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller "renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller From 3cc89c12fb2cc3a949465e69ed15e3ea7df8b22b Mon Sep 17 00:00:00 2001 From: Liang Chen Date: Mon, 23 Jul 2018 17:25:20 +0800 Subject: [PATCH 51/56] dt-bindings: mmc: rockchip-dw-mshc: add description for px30 Add "rockchip,px30-dw-mshc", "rockchip,rk3288-dw-mshc" for dwmmc on px30 platform. Acked-by: Rob Herring Signed-off-by: Liang Chen Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt index 8ce49b255974..6f629b12bd69 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt @@ -14,6 +14,7 @@ Required Properties: before RK3288 - "rockchip,rk3288-dw-mshc": for Rockchip RK3288 - "rockchip,rv1108-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RV1108 + - "rockchip,px30-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip PX30 - "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3036 - "rockchip,rk3228-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK322x - "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3328 From 1389690be14601995f55417a129b89257c477997 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 23 Jul 2018 16:34:31 +0100 Subject: [PATCH 52/56] mmc: sunxi: remove output of virtual base address Recent Linux versions refuse to print actual virtual kernel addresses, to not give a hint about the location of the kernel in a randomized virtual address space. This affects the output of the sunxi MMC controller driver, which now produces the rather uninformative line: [ 1.482660] sunxi-mmc 1c0f000.mmc: base:0x(____ptrval____) irq:8 Since the virtual base address is not really interesting in the first place, let's just drop this value. The same applies to Linux' notion of the interrupt number, which is independent from the GIC SPI number. We have the physical address as part of the DT node name, which is way more useful for debugging purposes. To keep a success message in the driver, we make this purpose explicit with the word "initialized", plus print some information that is not too obvious and that we learned while probing the device: the maximum request size and whether it uses the new timing mode. So the output turns into: [ 1.750626] sunxi-mmc 1c0f000.mmc: initialized, max. request size: 16384 KB, uses new timings mode [ 1.786699] sunxi-mmc 1c11000.mmc: initialized, max. request size: 2048 KB Signed-off-by: Andre Przywara Acked-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 4ea8e2611079..568349e1fbc2 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1407,7 +1407,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev) if (ret) goto error_free_dma; - dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq); + dev_info(&pdev->dev, "initialized, max. request size: %u KB%s\n", + mmc->max_req_size >> 10, + host->use_new_timings ? ", uses new timings mode" : ""); + return 0; error_free_dma: From a8f399f624e1ae902828ad788f3601ff7b3e0eed Mon Sep 17 00:00:00 2001 From: Masaharu Hayakawa Date: Tue, 24 Jul 2018 16:51:37 +0200 Subject: [PATCH 53/56] mmc: tmio: Fix tuning flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the return value of mmc_send_tuning() is error other than -EILSEQ, the tuning fails and process goes out of for_loop. The correct processing is to judge their TAP as not good (NG) and continue. Signed-off-by: Masaharu Hayakawa [Niklas: update commit message] Signed-off-by: Niklas Söderlund Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 416f9e078fda..72ac806e0c76 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -818,8 +818,6 @@ static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode) host->prepare_tuning(host, i % host->tap_num); ret = mmc_send_tuning(mmc, opcode, NULL); - if (ret && ret != -EILSEQ) - goto out; if (ret == 0) set_bit(i, host->taps); From 5c99826b27d0afd4c05c2013385b17037f579cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Tue, 24 Jul 2018 16:51:38 +0200 Subject: [PATCH 54/56] mmc: renesas_sdhi: Fix sampling clock position selecting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When tuning each tap is issued CMD19 twice and the result of both runs recorded in host->taps. If the result is different between the two runs the wrong sampling clock position was selected. Fix this by merging the two runs and only keep the result for each tap if it was good in both sets. Signed-off-by: Niklas Söderlund Reviewed-by: Simon Horman Tested-by: Wolfram Sang Reviewed-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 384ae6cfa289..777e32b0e410 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -384,6 +384,18 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) /* Clear SCC_RVSREQ */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); + /* + * When tuning CMD19 is issued twice for each tap, merge the + * result requiring the tap to be good in both runs before + * considering it for tuning selection. + */ + for (i = 0; i < host->tap_num * 2; i++) { + int offset = host->tap_num * (i < host->tap_num ? 1 : -1); + + if (!test_bit(i, host->taps)) + clear_bit(i + offset, host->taps); + } + /* * Find the longest consecutive run of successful probes. If that * is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the From e401bfdadd2dc0f59bf76220236285130f072190 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jul 2018 17:46:14 +0900 Subject: [PATCH 55/56] mmc: tmio: remove unneeded variable in tmio_mmc_start_command() Pass TMIO_MASK_CMD to tmio_mmc_enable_mmc_irqs() directly, and remove the variable, irq_mask. Signed-off-by: Masahiro Yamada Reviewed-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 72ac806e0c76..261b4d62d2b1 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -322,7 +322,6 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, { struct mmc_data *data = host->data; int c = cmd->opcode; - u32 irq_mask = TMIO_MASK_CMD; switch (mmc_resp_type(cmd)) { case MMC_RSP_NONE: c |= RESP_NONE; break; @@ -362,7 +361,7 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, c |= TRANSFER_READ; } - tmio_mmc_enable_mmc_irqs(host, irq_mask); + tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_CMD); /* Fire off the command */ sd_ctrl_write32_as_16_and_16(host, CTL_ARG_REG, cmd->arg); From 7f38abf220e2c800a2c451372e9f07ed5fd0ea49 Mon Sep 17 00:00:00 2001 From: Hongjie Fang Date: Tue, 31 Jul 2018 10:55:09 +0800 Subject: [PATCH 56/56] mmc: core: improve reasonableness of bus width setting for HS400es mmc_select_hs400es() calls mmc_select_bus_width() which will continue to set 4bit transfer mode if fail to set 8bit mode. The bus width should not be set to 4bit in HS400es. When fail to set 8bit mode, need return error directly for HS400es. Signed-off-by: Hongjie Fang Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 63a52379e8ab..bc1bd2c25613 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1348,8 +1348,12 @@ static int mmc_select_hs400es(struct mmc_card *card) goto out_err; err = mmc_select_bus_width(card); - if (err < 0) + if (err != MMC_BUS_WIDTH_8) { + pr_err("%s: switch to 8bit bus width failed, err:%d\n", + mmc_hostname(host), err); + err = err < 0 ? err : -ENOTSUPP; goto out_err; + } /* Switch card to HS mode */ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,