From 44f54e701205d78e78bbac67d764a8833c2717c1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 17 Dec 2019 12:40:30 +0100 Subject: mmc: renesas_sdhi: remove double clear of automatic correction hw_reset() clears the automatic correction bit twice. I couldn't find anything in the docs recommending that. Removing one of them didn't cause any regressions here, so keep it simple. Reviewed-by: Yoshihiro Shimoda Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20191217114034.13290-2-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 35cb24cd45b4..519d91ae3373 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -531,10 +531,6 @@ static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host) ~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_RVSCNTL, - ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & - sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); - if (host->pdata->flags & TMIO_MMC_MIN_RCAR2) sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, TMIO_MASK_INIT_RCAR2); -- cgit v1.2.3 From 11a219606a9bc7f1fcdb4fde285100fd8a601d0d Mon Sep 17 00:00:00 2001 From: Takeshi Saito Date: Tue, 17 Dec 2019 12:40:31 +0100 Subject: mmc: renesas_sdhi: Add manual correction This patch adds a manual correction mechanism for SDHI. Currently, SDHI uses automatic TAP position correction. However, TAP position can also be corrected manually via correction error status flags. Signed-off-by: Takeshi Saito Reviewed-by: Yoshihiro Shimoda Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20191217114034.13290-3-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi.h | 1 + drivers/mmc/host/renesas_sdhi_core.c | 61 ++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 10 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index f524251d5113..11a0b2bca3aa 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -57,6 +57,7 @@ struct renesas_sdhi { void __iomem *scc_ctl; u32 scc_tappos; u32 scc_tappos_hs400; + bool doing_tune; }; #define host_to_priv(host) \ diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 519d91ae3373..6cedc0a10593 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -263,6 +263,8 @@ 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) +#define SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP BIT(1) +#define SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPDOWN BIT(0) /* 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) @@ -321,6 +323,8 @@ static void renesas_sdhi_prepare_tuning(struct tmio_mmc_host *host, { struct renesas_sdhi *priv = host_to_priv(host); + priv->doing_tune = true; + /* Set sampling clock position */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, tap); } @@ -426,6 +430,8 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) unsigned long ntap; /* temporary counter of tuning success */ unsigned long i; + priv->doing_tune = false; + /* Clear SCC_RVSREQ */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); @@ -485,6 +491,47 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) return 0; } +static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap) +{ + struct renesas_sdhi *priv = host_to_priv(host); + u32 val; + + val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ); + if (!val) + return false; + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); + + /* Change TAP position according to correction status */ + if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) + return true; /* Need re-tune */ + else if (val & SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP) + host->tap_set = (host->tap_set + 1) % host->tap_num; + else if (val & SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPDOWN) + host->tap_set = (host->tap_set - 1) % host->tap_num; + else + return false; + + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, + host->tap_set / (use_4tap ? 2 : 1)); + + return false; +} + +static bool renesas_sdhi_auto_correction(struct tmio_mmc_host *host) +{ + struct renesas_sdhi *priv = host_to_priv(host); + + /* Check SCC error */ + if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ) & + SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) { + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); + return true; + } + + return false; +} + static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host) { struct renesas_sdhi *priv = host_to_priv(host); @@ -499,20 +546,14 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host) !(host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && !use_4tap)) return false; - if (mmc_doing_retune(host->mmc)) + if (mmc_doing_retune(host->mmc) || priv->doing_tune) return false; - /* Check SCC error */ if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) & - SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN && - sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ) & - SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) { - /* Clear SCC error */ - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); - return true; - } + SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN) + return renesas_sdhi_auto_correction(host); - return false; + return renesas_sdhi_manual_correction(host, use_4tap); } static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host) -- cgit v1.2.3 From 71cfc92751ac7a3185e73cffc43a673b73c39683 Mon Sep 17 00:00:00 2001 From: Takeshi Saito Date: Tue, 17 Dec 2019 12:40:32 +0100 Subject: mmc: renesas_sdhi: only check CMD status for HS400 manual correction R-Car Gen3 cannot use correction error status with HS400. HS200: CMD and DAT signal timing are based on CLK signal. HS400: CMD signal is based on CLK. DAT signal is based on DS signal. In HS400, CMD signal is 200MHz(SDR). DAT signal is 200MHz(DDR). Center position of signal is different between CMD and DAT. TAP position should be adjusted to the center position of CMD signal. DAT sampling timing is adjusted by HS400 calibration circuit regardless of TAP position. Refer to renesas_sdhi_adjust_hs400mode_enable(). However, correction error status contains CMD and DAT status in HS400 (DAT signal is not masked in HS400). Therefore, correction error status cannot use in HS400. It means that auto correction cannot be uses in HS400. Manual correction can change to the correct TAP position by ignoring DAT correction error status and using only CMD correction status. Signed-off-by: Takeshi Saito [wsa: refactored patch from BSP] Signed-off-by: Wolfram Sang Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20191217114034.13290-4-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 41 +++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 6cedc0a10593..e5db96fd0a69 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -250,6 +250,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_SMPCMP 0x00C #define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E /* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */ @@ -265,6 +266,10 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, #define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR BIT(2) #define SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP BIT(1) #define SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPDOWN BIT(0) +/* Definitions for values the SH_MOBILE_SDHI_SCC_SMPCMP register */ +#define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR (BIT(24) | BIT(8)) +#define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP BIT(24) +#define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN BIT(8) /* 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) @@ -494,6 +499,7 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap) { struct renesas_sdhi *priv = host_to_priv(host); + unsigned long new_tap = host->tap_set; u32 val; val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ); @@ -503,15 +509,34 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); /* Change TAP position according to correction status */ - if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) - return true; /* Need re-tune */ - else if (val & SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP) - host->tap_set = (host->tap_set + 1) % host->tap_num; - else if (val & SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPDOWN) - host->tap_set = (host->tap_set - 1) % host->tap_num; - else - return false; + if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC && + host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { + /* + * With HS400, the DAT signal is based on DS, not CLK. + * Therefore, use only CMD status. + */ + u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) & + SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR; + if (!smpcmp) + return false; /* no error in CMD signal */ + else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP) + new_tap++; + else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN) + new_tap--; + else + return true; /* need retune */ + } else { + if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) + return true; /* need retune */ + else if (val & SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP) + new_tap++; + else if (val & SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPDOWN) + new_tap--; + else + return false; + } + host->tap_set = (new_tap % host->tap_num); sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, host->tap_set / (use_4tap ? 2 : 1)); -- cgit v1.2.3 From 9b0d6855e756b60dd09c2a3d2a697130ffdc297d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 17 Dec 2019 12:40:33 +0100 Subject: mmc: renesas_sdhi: enforce manual correction for Gen3 HW engineers say that automatic tap correction cannot be used for HS400 in all R-Car Gen3 SoCs. So, check for that SDHI variant and disable it when HS400 is about to be enabled. Reviewed-by: Yoshihiro Shimoda Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20191217114034.13290-5-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index e5db96fd0a69..04f502345b91 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -348,6 +348,12 @@ static void renesas_sdhi_hs400_complete(struct tmio_mmc_host *host) sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos_hs400); + /* Gen3 can't do automatic tap correction with HS400, so disable it */ + if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC) + 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_TMPPORT2, (SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN | SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) | -- cgit v1.2.3 From 6199a10e7e53d8185f25430fbae918d0f105a0ec Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 17 Dec 2019 12:40:34 +0100 Subject: mmc: renesas_sdhi: cleanup SCC defines Use increasing BIT numbers consistently and remove some superfluous comments. Signed-off-by: Wolfram Sang Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20191217114034.13290-6-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 04f502345b91..6a112454ca26 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -253,24 +253,22 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc, #define SH_MOBILE_SDHI_SCC_SMPCMP 0x00C #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) #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16 #define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff -/* Definitions for values the SH_MOBILE_SDHI_SCC_CKSEL register */ #define SH_MOBILE_SDHI_SCC_CKSEL_DTSEL BIT(0) -/* Definitions for values the SH_MOBILE_SDHI_SCC_RVSCNTL register */ + #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) -#define SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP BIT(1) + #define SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPDOWN BIT(0) -/* Definitions for values the SH_MOBILE_SDHI_SCC_SMPCMP register */ -#define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR (BIT(24) | BIT(8)) -#define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP BIT(24) +#define SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP BIT(1) +#define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR BIT(2) + #define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN BIT(8) -/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT2 register */ +#define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP BIT(24) +#define SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR (BIT(8) | BIT(24)) + #define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4) #define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31) -- cgit v1.2.3 From 0c482d829ac413024b9ffa3b292ab8bd998eb62f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 29 Jan 2020 21:37:04 +0100 Subject: mmc: tmio: refactor tuning execution into SDHI driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move Renesas specific code for executing the tuning with a SCC into the SDHI driver and leave only a generic call in the TMIO driver. Simplify the code a little by removing init_tuning() and prepare_tuning() callbacks. The latter is directly folded into the new execute_tuning() callbacks. Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20200129203709.30493-2-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 45 +++++++++++++++++++++++++----------- drivers/mmc/host/tmio_mmc.h | 3 +-- drivers/mmc/host/tmio_mmc_core.c | 33 ++++---------------------- 3 files changed, 37 insertions(+), 44 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 6a112454ca26..b3ab66f963f8 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -321,17 +321,6 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host) SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK; } -static void renesas_sdhi_prepare_tuning(struct tmio_mmc_host *host, - unsigned long tap) -{ - struct renesas_sdhi *priv = host_to_priv(host); - - priv->doing_tune = true; - - /* Set sampling clock position */ - 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); @@ -500,6 +489,37 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) return 0; } +static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) +{ + struct renesas_sdhi *priv = host_to_priv(host); + int i, ret; + + host->tap_num = renesas_sdhi_init_tuning(host); + if (!host->tap_num) + return 0; /* Tuning is not supported */ + + if (host->tap_num * 2 >= sizeof(host->taps) * BITS_PER_BYTE) { + dev_warn_once(&host->pdev->dev, + "Too many taps, skipping tuning. Please consider updating size of taps field of tmio_mmc_host\n"); + return 0; + } + + priv->doing_tune = true; + bitmap_zero(host->taps, host->tap_num * 2); + + /* Issue CMD19 twice for each tap */ + for (i = 0; i < 2 * host->tap_num; i++) { + /* Set sampling clock position */ + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, i % host->tap_num); + + ret = mmc_send_tuning(host->mmc, opcode, NULL); + if (ret == 0) + set_bit(i, host->taps); + } + + return renesas_sdhi_select_tuning(host); +} + static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap) { struct renesas_sdhi *priv = host_to_priv(host); @@ -877,8 +897,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, if (!hit) dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n"); - host->init_tuning = renesas_sdhi_init_tuning; - host->prepare_tuning = renesas_sdhi_prepare_tuning; + host->execute_tuning = renesas_sdhi_execute_tuning; host->select_tuning = renesas_sdhi_select_tuning; host->check_scc_error = renesas_sdhi_check_scc_error; host->prepare_hs400_tuning = diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index c5ba13fae399..bfebbe368f02 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -176,14 +176,13 @@ struct tmio_mmc_host { int (*write16_hook)(struct tmio_mmc_host *host, int addr); void (*reset)(struct tmio_mmc_host *host); void (*hw_reset)(struct tmio_mmc_host *host); - void (*prepare_tuning)(struct tmio_mmc_host *host, unsigned long tap); bool (*check_scc_error)(struct tmio_mmc_host *host); /* * Mandatory callback for tuning to occur which is optional for SDR50 * and mandatory for SDR104. */ - unsigned int (*init_tuning)(struct tmio_mmc_host *host); + int (*execute_tuning)(struct tmio_mmc_host *host, u32 opcode); int (*select_tuning)(struct tmio_mmc_host *host); /* Tuning values: 1 for success, 0 for failure */ diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 1e424bcdbd5f..9108247d8c14 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -718,38 +718,13 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host, static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct tmio_mmc_host *host = mmc_priv(mmc); - int i, ret = 0; - - if (!host->init_tuning || !host->select_tuning) - /* Tuning is not supported */ - goto out; - - host->tap_num = host->init_tuning(host); - if (!host->tap_num) - /* Tuning is not supported */ - goto out; - - if (host->tap_num * 2 >= sizeof(host->taps) * BITS_PER_BYTE) { - dev_warn_once(&host->pdev->dev, - "Too many taps, skipping tuning. Please consider updating size of taps field of tmio_mmc_host\n"); - goto out; - } - - bitmap_zero(host->taps, host->tap_num * 2); - - /* Issue CMD19 twice for each tap */ - for (i = 0; i < 2 * host->tap_num; i++) { - if (host->prepare_tuning) - host->prepare_tuning(host, i % host->tap_num); + int ret; - ret = mmc_send_tuning(mmc, opcode, NULL); - if (ret == 0) - set_bit(i, host->taps); - } + if (!host->execute_tuning) + return 0; - ret = host->select_tuning(host); + ret = host->execute_tuning(host, opcode); -out: if (ret < 0) { dev_warn(&host->pdev->dev, "Tuning procedure failed\n"); tmio_mmc_hw_reset(mmc); -- cgit v1.2.3 From 3a821a8244bcdefb8d9242c49deb6a01fbf5039b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 29 Jan 2020 21:37:05 +0100 Subject: mmc: renesas_sdhi: complain loudly if driver needs update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the tap array in the driver is too low, this is not a warning but an error. Also _once is not helpful, we should make sure it is prominently in the logs. It is safe to do this because this will only show up during SoC enablement when we a new SoCs needs more taps (if that ever will happen). Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20200129203709.30493-3-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index b3ab66f963f8..d63aeb35bd0b 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -499,9 +499,9 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) return 0; /* Tuning is not supported */ if (host->tap_num * 2 >= sizeof(host->taps) * BITS_PER_BYTE) { - dev_warn_once(&host->pdev->dev, - "Too many taps, skipping tuning. Please consider updating size of taps field of tmio_mmc_host\n"); - return 0; + dev_err(&host->pdev->dev, + "Too many taps, please update 'taps' in tmio_mmc_host!\n"); + return -EINVAL; } priv->doing_tune = true; -- cgit v1.2.3 From 64982b9f2f37802b72430aed3796a89a01a088d1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 29 Jan 2020 21:37:06 +0100 Subject: mmc: tmio: give callback a generic name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check_scc_error() is too Renesas specific. Let's just call it check_retune() to make it also easier understandable what it does. Only a rename, no functional change. Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20200129203709.30493-4-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 2 +- drivers/mmc/host/tmio_mmc.h | 2 +- drivers/mmc/host/tmio_mmc_core.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index d63aeb35bd0b..24ee8ac1fe21 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -899,7 +899,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, host->execute_tuning = renesas_sdhi_execute_tuning; host->select_tuning = renesas_sdhi_select_tuning; - host->check_scc_error = renesas_sdhi_check_scc_error; + host->check_retune = renesas_sdhi_check_scc_error; host->prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning; host->hs400_downgrade = renesas_sdhi_disable_scc; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index bfebbe368f02..bdb9973981ff 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -176,7 +176,7 @@ struct tmio_mmc_host { int (*write16_hook)(struct tmio_mmc_host *host, int addr); void (*reset)(struct tmio_mmc_host *host); void (*hw_reset)(struct tmio_mmc_host *host); - bool (*check_scc_error)(struct tmio_mmc_host *host); + bool (*check_retune)(struct tmio_mmc_host *host); /* * Mandatory callback for tuning to occur which is optional for SDR50 diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 9108247d8c14..9bb58c5b6768 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -818,8 +818,8 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host) if (mrq->cmd->error || (mrq->data && mrq->data->error)) tmio_mmc_abort_dma(host); - /* SCC error means retune, but executed command was still successful */ - if (host->check_scc_error && host->check_scc_error(host)) + /* Error means retune, but executed command was still successful */ + if (host->check_retune && host->check_retune(host)) mmc_retune_needed(host->mmc); /* If SET_BLOCK_COUNT, continue with main command */ -- cgit v1.2.3 From a86bf70b7021c93119f81454ad81293c526f3cb8 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 29 Jan 2020 21:37:07 +0100 Subject: mmc: tmio: enforce retune after runtime suspend Currently, select_tuning() is called after RPM resume. But select_tuning() needs some additional function calls to work correctly. Instead of reimplementing the whole postprocessing, just enforce retuning. Signed-off-by: Wolfram Sang Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20200129203709.30493-5-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 1 - drivers/mmc/host/tmio_mmc.h | 1 - drivers/mmc/host/tmio_mmc_core.c | 8 +------- 3 files changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 24ee8ac1fe21..0c9e5e010bda 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -898,7 +898,6 @@ int renesas_sdhi_probe(struct platform_device *pdev, dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n"); host->execute_tuning = renesas_sdhi_execute_tuning; - host->select_tuning = renesas_sdhi_select_tuning; host->check_retune = renesas_sdhi_check_scc_error; host->prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index bdb9973981ff..b6fffd3d2650 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -183,7 +183,6 @@ struct tmio_mmc_host { * and mandatory for SDR104. */ int (*execute_tuning)(struct tmio_mmc_host *host, u32 opcode); - int (*select_tuning)(struct tmio_mmc_host *host); /* Tuning values: 1 for success, 0 for failure */ DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long)); diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 9bb58c5b6768..d18ee2fae746 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -1300,11 +1300,6 @@ int tmio_mmc_host_runtime_suspend(struct device *dev) } EXPORT_SYMBOL_GPL(tmio_mmc_host_runtime_suspend); -static bool tmio_mmc_can_retune(struct tmio_mmc_host *host) -{ - return host->tap_num && mmc_can_retune(host->mmc); -} - int tmio_mmc_host_runtime_resume(struct device *dev) { struct tmio_mmc_host *host = dev_get_drvdata(dev); @@ -1321,8 +1316,7 @@ int tmio_mmc_host_runtime_resume(struct device *dev) tmio_mmc_enable_dma(host, true); - if (tmio_mmc_can_retune(host) && host->select_tuning(host)) - dev_warn(&host->pdev->dev, "Tuning selection failed\n"); + mmc_retune_needed(host->mmc); return 0; } -- cgit v1.2.3 From b2dd9a1325e680654cf5b7d8fc19000114bd145f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 29 Jan 2020 21:37:08 +0100 Subject: mmc: tmio: factor out TAP usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TAPs are Renesas SDHI specific. Now that we moved all handling to the SDHI core, we can also move the definitions from the TMIO struct to the SDHI one. Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20200129203709.30493-6-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi.h | 5 +++++ drivers/mmc/host/renesas_sdhi_core.c | 38 ++++++++++++++++++------------------ drivers/mmc/host/tmio_mmc.h | 5 ----- 3 files changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index 11a0b2bca3aa..7a1a741547f2 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -58,6 +58,11 @@ struct renesas_sdhi { u32 scc_tappos; u32 scc_tappos_hs400; bool doing_tune; + + /* Tuning values: 1 for success, 0 for failure */ + DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long)); + unsigned int tap_num; + unsigned long tap_set; }; #define host_to_priv(host) \ diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 0c9e5e010bda..22eaabe513d0 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -354,7 +354,7 @@ static void renesas_sdhi_hs400_complete(struct tmio_mmc_host *host) if (priv->quirks && priv->quirks->hs400_4taps) sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, - host->tap_set / 2); + priv->tap_set / 2); sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL, SH_MOBILE_SDHI_SCC_CKSEL_DTSEL | @@ -438,11 +438,11 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) * 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); + for (i = 0; i < priv->tap_num * 2; i++) { + int offset = priv->tap_num * (i < priv->tap_num ? 1 : -1); - if (!test_bit(i, host->taps)) - clear_bit(i + offset, host->taps); + if (!test_bit(i, priv->taps)) + clear_bit(i + offset, priv->taps); } /* @@ -454,8 +454,8 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) ntap = 0; tap_start = 0; tap_end = 0; - for (i = 0; i < host->tap_num * 2; i++) { - if (test_bit(i, host->taps)) { + for (i = 0; i < priv->tap_num * 2; i++) { + if (test_bit(i, priv->taps)) { ntap++; } else { if (ntap > tap_cnt) { @@ -474,12 +474,12 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) } if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP) - host->tap_set = (tap_start + tap_end) / 2 % host->tap_num; + priv->tap_set = (tap_start + tap_end) / 2 % priv->tap_num; else return -EIO; /* Set SCC */ - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, host->tap_set); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, priv->tap_set); /* Enable auto re-tuning */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, @@ -494,27 +494,27 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) struct renesas_sdhi *priv = host_to_priv(host); int i, ret; - host->tap_num = renesas_sdhi_init_tuning(host); - if (!host->tap_num) + priv->tap_num = renesas_sdhi_init_tuning(host); + if (!priv->tap_num) return 0; /* Tuning is not supported */ - if (host->tap_num * 2 >= sizeof(host->taps) * BITS_PER_BYTE) { + if (priv->tap_num * 2 >= sizeof(priv->taps) * BITS_PER_BYTE) { dev_err(&host->pdev->dev, "Too many taps, please update 'taps' in tmio_mmc_host!\n"); return -EINVAL; } priv->doing_tune = true; - bitmap_zero(host->taps, host->tap_num * 2); + bitmap_zero(priv->taps, priv->tap_num * 2); /* Issue CMD19 twice for each tap */ - for (i = 0; i < 2 * host->tap_num; i++) { + for (i = 0; i < 2 * priv->tap_num; i++) { /* Set sampling clock position */ - sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, i % host->tap_num); + sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, i % priv->tap_num); ret = mmc_send_tuning(host->mmc, opcode, NULL); if (ret == 0) - set_bit(i, host->taps); + set_bit(i, priv->taps); } return renesas_sdhi_select_tuning(host); @@ -523,7 +523,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap) { struct renesas_sdhi *priv = host_to_priv(host); - unsigned long new_tap = host->tap_set; + unsigned long new_tap = priv->tap_set; u32 val; val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ); @@ -560,9 +560,9 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_ return false; } - host->tap_set = (new_tap % host->tap_num); + priv->tap_set = (new_tap % priv->tap_num); sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, - host->tap_set / (use_4tap ? 2 : 1)); + priv->tap_set / (use_4tap ? 2 : 1)); return false; } diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index b6fffd3d2650..b4cf10109162 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -184,11 +184,6 @@ struct tmio_mmc_host { */ int (*execute_tuning)(struct tmio_mmc_host *host, u32 opcode); - /* 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); -- cgit v1.2.3 From f22084b662e5029f3ec31a9871d9c1c01aaa76de Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 29 Jan 2020 21:37:09 +0100 Subject: mmc: tmio: remove superfluous callback wrappers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After various refactoring, we can populate the mmc_ops callbacks directly and don't need to have wrappers for them anymore. Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20200129203709.30493-7-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 18 +++++++++++------- drivers/mmc/host/tmio_mmc_core.c | 32 +------------------------------- 2 files changed, 12 insertions(+), 38 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 22eaabe513d0..0f07cc1aee34 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -321,8 +321,9 @@ static unsigned int renesas_sdhi_init_tuning(struct tmio_mmc_host *host) SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK; } -static void renesas_sdhi_hs400_complete(struct tmio_mmc_host *host) +static void renesas_sdhi_hs400_complete(struct mmc_host *mmc) { + struct tmio_mmc_host *host = mmc_priv(mmc); struct renesas_sdhi *priv = host_to_priv(host); sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & @@ -376,8 +377,9 @@ static void renesas_sdhi_reset_scc(struct tmio_mmc_host *host, SH_MOBILE_SDHI_SCC_CKSEL)); } -static void renesas_sdhi_disable_scc(struct tmio_mmc_host *host) +static void renesas_sdhi_disable_scc(struct mmc_host *mmc) { + struct tmio_mmc_host *host = mmc_priv(mmc); struct renesas_sdhi *priv = host_to_priv(host); renesas_sdhi_reset_scc(host, priv); @@ -412,9 +414,12 @@ static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host, sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); } -static void renesas_sdhi_prepare_hs400_tuning(struct tmio_mmc_host *host) +static int renesas_sdhi_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) { + struct tmio_mmc_host *host = mmc_priv(mmc); + renesas_sdhi_reset_hs400_mode(host, host_to_priv(host)); + return 0; } #define SH_MOBILE_SDHI_MAX_TAP 3 @@ -899,10 +904,9 @@ int renesas_sdhi_probe(struct platform_device *pdev, host->execute_tuning = renesas_sdhi_execute_tuning; host->check_retune = renesas_sdhi_check_scc_error; - host->prepare_hs400_tuning = - renesas_sdhi_prepare_hs400_tuning; - host->hs400_downgrade = renesas_sdhi_disable_scc; - host->hs400_complete = renesas_sdhi_hs400_complete; + host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning; + host->ops.hs400_downgrade = renesas_sdhi_disable_scc; + host->ops.hs400_complete = renesas_sdhi_hs400_complete; } num_irqs = platform_irq_count(pdev); diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index d18ee2fae746..9520bd94cf43 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -997,34 +997,7 @@ 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 = { +static struct mmc_host_ops tmio_mmc_ops = { .request = tmio_mmc_request, .set_ios = tmio_mmc_set_ios, .get_ro = tmio_mmc_get_ro, @@ -1033,9 +1006,6 @@ 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) -- cgit v1.2.3 From 8d2e334377dbe645415fbe031711324bc2281907 Mon Sep 17 00:00:00 2001 From: Manish Narani Date: Tue, 21 Jan 2020 15:51:34 +0530 Subject: mmc: sdhci-of-arasan: Add support for DLL reset for ZynqMP platforms The DLL resets are required while executing the auto tuning procedure in ZynqMP. This patch adds code to support the same. Signed-off-by: Manish Narani Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1579602095-30060-4-git-send-email-manish.narani@xilinx.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index e49b44b4d82e..39176ab5ca1f 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -757,6 +757,50 @@ static const struct clk_ops zynqmp_sampleclk_ops = { .set_phase = sdhci_zynqmp_sampleclk_set_phase, }; +static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + struct sdhci_arasan_zynqmp_clk_data *zynqmp_clk_data = + sdhci_arasan->clk_data.clk_of_data; + const struct zynqmp_eemi_ops *eemi_ops = zynqmp_clk_data->eemi_ops; + u16 clk; + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN); + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + /* Issue DLL Reset */ + eemi_ops->ioctl(deviceid, IOCTL_SD_DLL_RESET, + PM_DLL_RESET_PULSE, 0, NULL); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + + sdhci_enable_clk(host, clk); +} + +static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + struct clk_hw *hw = &sdhci_arasan->clk_data.sdcardclk_hw; + const char *clk_name = clk_hw_get_name(hw); + u32 device_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : + NODE_SD_1; + int err; + + arasan_zynqmp_dll_reset(host, device_id); + + err = sdhci_execute_tuning(mmc, opcode); + if (err) + return err; + + arasan_zynqmp_dll_reset(host, device_id); + + return 0; +} + /** * sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier * @@ -1247,6 +1291,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev) zynqmp_clk_data->eemi_ops = eemi_ops; sdhci_arasan->clk_data.clk_of_data = zynqmp_clk_data; + host->mmc_host_ops.execute_tuning = + arasan_zynqmp_execute_tuning; } arasan_dt_parse_clk_phases(&pdev->dev, &sdhci_arasan->clk_data); -- cgit v1.2.3 From 2a2b821607aea16c9c20f9f4d45096dc860fd33a Mon Sep 17 00:00:00 2001 From: Manish Narani Date: Tue, 21 Jan 2020 15:51:35 +0530 Subject: mmc: sdhci-of-arasan: Remove quirk for broken base clock This patch removes quirk which indicates a broken base clock. This was making the kernel report wrong base clock of ~187MHz instead of 200MHz even as the measurement on the hardware was showing 200MHz. Signed-off-by: Manish Narani Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1579602095-30060-5-git-send-email-manish.narani@xilinx.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 39176ab5ca1f..0146d7dd315b 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -358,6 +358,17 @@ static struct sdhci_arasan_of_data sdhci_arasan_data = { .pdata = &sdhci_arasan_pdata, }; +static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = { + .ops = &sdhci_arasan_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC, +}; + +static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = { + .pdata = &sdhci_arasan_zynqmp_pdata, +}; + static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask) { int cmd_error = 0; @@ -553,7 +564,7 @@ static const struct of_device_id sdhci_arasan_of_match[] = { }, { .compatible = "xlnx,zynqmp-8.9a", - .data = &sdhci_arasan_data, + .data = &sdhci_arasan_zynqmp_data, }, { /* sentinel */ } }; -- cgit v1.2.3 From 219c02ca1cf65c457f31ec32523a75972019a862 Mon Sep 17 00:00:00 2001 From: Ritesh Harjani Date: Fri, 7 Feb 2020 17:34:28 +0530 Subject: mmc: sdhci-msm: Don't enable PWRSAVE_DLL for certain sdhc hosts SDHC core with new 14lpp and later tech DLL should not enable PWRSAVE_DLL since such controller's internal gating cannot meet following MCLK requirement: When MCLK is gated OFF, it is not gated for less than 0.5us and MCLK must be switched on for at-least 1us before DATA starts coming. Adding support for this requirement. Signed-off-by: Ritesh Harjani Signed-off-by: Veerabhadrarao Badiganti Reviewed-by: Can Guo Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1581077075-26011-1-git-send-email-vbadigan@codeaurora.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 3955fa5db43c..53b79ee37fdd 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -977,9 +977,21 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) goto out; } - config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec3); - config |= CORE_PWRSAVE_DLL; - writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec3); + /* + * Set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3. + * When MCLK is gated OFF, it is not gated for less than 0.5us + * and MCLK must be switched on for at-least 1us before DATA + * starts coming. Controllers with 14lpp and later tech DLL cannot + * guarantee above requirement. So PWRSAVE_DLL should not be + * turned on for host controllers using this DLL. + */ + if (!msm_host->use_14lpp_dll_reset) { + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec3); + config |= CORE_PWRSAVE_DLL; + writel_relaxed(config, host->ioaddr + + msm_offset->core_vendor_spec3); + } /* * Drain writebuffer to ensure above DLL calibration -- cgit v1.2.3 From 511ce378e16f07b66ab78118587b7cc6ac197364 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 12 Feb 2020 12:12:56 +0800 Subject: mmc: Add MMC host software queue support Now the MMC read/write stack will always wait for previous request is completed by mmc_blk_rw_wait(), before sending a new request to hardware, or queue a work to complete request, that will bring context switching overhead and spend some extra time to poll the card for busy completion for I/O writes via sending CMD13, especially for high I/O per second rates, to affect the IO performance. Thus this patch introduces MMC software queue interface based on the hardware command queue engine's interfaces, which is similar with the hardware command queue engine's idea, that can remove the context switching. Moreover we set the default queue depth as 64 for software queue, which allows more requests to be prepared, merged and inserted into IO scheduler to improve performance, but we only allow 2 requests in flight, that is enough to let the irq handler always trigger the next request without a context switch, as well as avoiding a long latency. Moreover the host controller should support HW busy detection for I/O operations when enabling the host software queue. That means, the host controller must not complete a data transfer request, until after the card stops signals busy. From the fio testing data in cover letter, we can see the software queue can improve some performance with 4K block size, increasing about 16% for random read, increasing about 90% for random write, though no obvious improvement for sequential read and write. Moreover we can expand the software queue interface to support MMC packed request or packed command in future. Reviewed-by: Arnd Bergmann Signed-off-by: Baolin Wang Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/4409c1586a9b3ed20d57ad2faf6c262fc3ccb6e2.1581478568.git.baolin.wang7@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 61 ++++++++ drivers/mmc/core/mmc.c | 18 ++- drivers/mmc/core/queue.c | 22 ++- drivers/mmc/host/Kconfig | 11 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/cqhci.c | 8 +- drivers/mmc/host/mmc_hsq.c | 344 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/mmc_hsq.h | 30 ++++ 8 files changed, 483 insertions(+), 12 deletions(-) create mode 100644 drivers/mmc/host/mmc_hsq.c create mode 100644 drivers/mmc/host/mmc_hsq.h (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 663d87924e5e..55d52fc46758 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -168,6 +168,11 @@ MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device"); static inline int mmc_blk_part_switch(struct mmc_card *card, unsigned int part_type); +static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, + struct mmc_card *card, + int disable_multi, + struct mmc_queue *mq); +static void mmc_blk_hsq_req_done(struct mmc_request *mrq); static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) { @@ -1532,9 +1537,30 @@ static int mmc_blk_cqe_issue_flush(struct mmc_queue *mq, struct request *req) return mmc_blk_cqe_start_req(mq->card->host, mrq); } +static int mmc_blk_hsq_issue_rw_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req); + struct mmc_host *host = mq->card->host; + int err; + + mmc_blk_rw_rq_prep(mqrq, mq->card, 0, mq); + mqrq->brq.mrq.done = mmc_blk_hsq_req_done; + mmc_pre_req(host, &mqrq->brq.mrq); + + err = mmc_cqe_start_req(host, &mqrq->brq.mrq); + if (err) + mmc_post_req(host, &mqrq->brq.mrq, err); + + return err; +} + static int mmc_blk_cqe_issue_rw_rq(struct mmc_queue *mq, struct request *req) { struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req); + struct mmc_host *host = mq->card->host; + + if (host->hsq_enabled) + return mmc_blk_hsq_issue_rw_rq(mq, req); mmc_blk_data_prep(mq, mqrq, 0, NULL, NULL); @@ -1920,6 +1946,41 @@ static void mmc_blk_urgent_bkops(struct mmc_queue *mq, mmc_run_bkops(mq->card); } +static void mmc_blk_hsq_req_done(struct mmc_request *mrq) +{ + struct mmc_queue_req *mqrq = + container_of(mrq, struct mmc_queue_req, brq.mrq); + struct request *req = mmc_queue_req_to_req(mqrq); + struct request_queue *q = req->q; + struct mmc_queue *mq = q->queuedata; + struct mmc_host *host = mq->card->host; + unsigned long flags; + + if (mmc_blk_rq_error(&mqrq->brq) || + mmc_blk_urgent_bkops_needed(mq, mqrq)) { + spin_lock_irqsave(&mq->lock, flags); + mq->recovery_needed = true; + mq->recovery_req = req; + spin_unlock_irqrestore(&mq->lock, flags); + + host->cqe_ops->cqe_recovery_start(host); + + schedule_work(&mq->recovery_work); + return; + } + + mmc_blk_rw_reset_success(mq, req); + + /* + * Block layer timeouts race with completions which means the normal + * completion path cannot be used during recovery. + */ + if (mq->in_recovery) + mmc_blk_cqe_complete_rq(mq, req); + else + blk_mq_complete_request(req); +} + void mmc_blk_mq_complete(struct request *req) { struct mmc_queue *mq = req->q->queuedata; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index de14b5845f52..24223cb150d4 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1851,15 +1851,19 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ card->reenable_cmdq = card->ext_csd.cmdq_en; - if (card->ext_csd.cmdq_en && !host->cqe_enabled) { + if (host->cqe_ops && !host->cqe_enabled) { err = host->cqe_ops->cqe_enable(host, card); - if (err) { - pr_err("%s: Failed to enable CQE, error %d\n", - mmc_hostname(host), err); - } else { + if (!err) { host->cqe_enabled = true; - pr_info("%s: Command Queue Engine enabled\n", - mmc_hostname(host)); + + if (card->ext_csd.cmdq_en) { + pr_info("%s: Command Queue Engine enabled\n", + mmc_hostname(host)); + } else { + host->hsq_enabled = true; + pr_info("%s: Host Software Queue enabled\n", + mmc_hostname(host)); + } } } diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index 9edc08685e86..25bee3daf9e2 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -62,7 +62,7 @@ enum mmc_issue_type mmc_issue_type(struct mmc_queue *mq, struct request *req) { struct mmc_host *host = mq->card->host; - if (mq->use_cqe) + if (mq->use_cqe && !host->hsq_enabled) return mmc_cqe_issue_type(host, req); if (req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_WRITE) @@ -124,12 +124,14 @@ static enum blk_eh_timer_return mmc_mq_timed_out(struct request *req, { struct request_queue *q = req->q; struct mmc_queue *mq = q->queuedata; + struct mmc_card *card = mq->card; + struct mmc_host *host = card->host; unsigned long flags; int ret; spin_lock_irqsave(&mq->lock, flags); - if (mq->recovery_needed || !mq->use_cqe) + if (mq->recovery_needed || !mq->use_cqe || host->hsq_enabled) ret = BLK_EH_RESET_TIMER; else ret = mmc_cqe_timed_out(req); @@ -144,12 +146,13 @@ static void mmc_mq_recovery_handler(struct work_struct *work) struct mmc_queue *mq = container_of(work, struct mmc_queue, recovery_work); struct request_queue *q = mq->queue; + struct mmc_host *host = mq->card->host; mmc_get_card(mq->card, &mq->ctx); mq->in_recovery = true; - if (mq->use_cqe) + if (mq->use_cqe && !host->hsq_enabled) mmc_blk_cqe_recovery(mq); else mmc_blk_mq_recovery(mq); @@ -160,6 +163,9 @@ static void mmc_mq_recovery_handler(struct work_struct *work) mq->recovery_needed = false; spin_unlock_irq(&mq->lock); + if (host->hsq_enabled) + host->cqe_ops->cqe_recovery_finish(host); + mmc_put_card(mq->card, &mq->ctx); blk_mq_run_hw_queues(q, true); @@ -279,6 +285,14 @@ static blk_status_t mmc_mq_queue_rq(struct blk_mq_hw_ctx *hctx, } break; case MMC_ISSUE_ASYNC: + /* + * For MMC host software queue, we only allow 2 requests in + * flight to avoid a long latency. + */ + if (host->hsq_enabled && mq->in_flight[issue_type] > 2) { + spin_unlock_irq(&mq->lock); + return BLK_STS_RESOURCE; + } break; default: /* @@ -430,7 +444,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card) * The queue depth for CQE must match the hardware because the request * tag is used to index the hardware queue. */ - if (mq->use_cqe) + if (mq->use_cqe && !host->hsq_enabled) mq->tag_set.queue_depth = min_t(int, card->ext_csd.cmdq_depth, host->cqe_qdepth); else diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 3a5089f0332c..fcb685efe17f 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -949,6 +949,17 @@ config MMC_CQHCI If unsure, say N. +config MMC_HSQ + tristate "MMC Host Software Queue support" + help + This selects the MMC Host Software Queue support. This may increase + performance, if the host controller and its driver supports it. + + If you have a controller/driver supporting this interface, say Y or M + here. + + If unsure, say N. + config MMC_TOSHIBA_PCI tristate "Toshiba Type A SD/MMC Card Interface Driver" depends on PCI diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 21d9089e5eda..b929ef941208 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o obj-$(CONFIG_MMC_SDHCI_SPRD) += sdhci-sprd.o obj-$(CONFIG_MMC_CQHCI) += cqhci.o +obj-$(CONFIG_MMC_HSQ) += mmc_hsq.o ifeq ($(CONFIG_CB710_DEBUG),y) CFLAGS-cb710-mmc += -DDEBUG diff --git a/drivers/mmc/host/cqhci.c b/drivers/mmc/host/cqhci.c index 5047f7343ffc..e2ea2c4b6b94 100644 --- a/drivers/mmc/host/cqhci.c +++ b/drivers/mmc/host/cqhci.c @@ -321,14 +321,20 @@ static int cqhci_enable(struct mmc_host *mmc, struct mmc_card *card) struct cqhci_host *cq_host = mmc->cqe_private; int err; + if (!card->ext_csd.cmdq_en) + return -EINVAL; + if (cq_host->enabled) return 0; cq_host->rca = card->rca; err = cqhci_host_alloc_tdl(cq_host); - if (err) + if (err) { + pr_err("%s: Failed to enable CQE, error %d\n", + mmc_hostname(mmc), err); return err; + } __cqhci_enable(cq_host); diff --git a/drivers/mmc/host/mmc_hsq.c b/drivers/mmc/host/mmc_hsq.c new file mode 100644 index 000000000000..fc82593b9e27 --- /dev/null +++ b/drivers/mmc/host/mmc_hsq.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * + * MMC software queue support based on command queue interfaces + * + * Copyright (C) 2019 Linaro, Inc. + * Author: Baolin Wang + */ + +#include +#include + +#include "mmc_hsq.h" + +#define HSQ_NUM_SLOTS 64 +#define HSQ_INVALID_TAG HSQ_NUM_SLOTS + +static void mmc_hsq_pump_requests(struct mmc_hsq *hsq) +{ + struct mmc_host *mmc = hsq->mmc; + struct hsq_slot *slot; + unsigned long flags; + + spin_lock_irqsave(&hsq->lock, flags); + + /* Make sure we are not already running a request now */ + if (hsq->mrq) { + spin_unlock_irqrestore(&hsq->lock, flags); + return; + } + + /* Make sure there are remain requests need to pump */ + if (!hsq->qcnt || !hsq->enabled) { + spin_unlock_irqrestore(&hsq->lock, flags); + return; + } + + slot = &hsq->slot[hsq->next_tag]; + hsq->mrq = slot->mrq; + hsq->qcnt--; + + spin_unlock_irqrestore(&hsq->lock, flags); + + mmc->ops->request(mmc, hsq->mrq); +} + +static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains) +{ + struct hsq_slot *slot; + int tag; + + /* + * If there are no remain requests in software queue, then set a invalid + * tag. + */ + if (!remains) { + hsq->next_tag = HSQ_INVALID_TAG; + return; + } + + /* + * Increasing the next tag and check if the corresponding request is + * available, if yes, then we found a candidate request. + */ + if (++hsq->next_tag != HSQ_INVALID_TAG) { + slot = &hsq->slot[hsq->next_tag]; + if (slot->mrq) + return; + } + + /* Othersie we should iterate all slots to find a available tag. */ + for (tag = 0; tag < HSQ_NUM_SLOTS; tag++) { + slot = &hsq->slot[tag]; + if (slot->mrq) + break; + } + + if (tag == HSQ_NUM_SLOTS) + tag = HSQ_INVALID_TAG; + + hsq->next_tag = tag; +} + +static void mmc_hsq_post_request(struct mmc_hsq *hsq) +{ + unsigned long flags; + int remains; + + spin_lock_irqsave(&hsq->lock, flags); + + remains = hsq->qcnt; + hsq->mrq = NULL; + + /* Update the next available tag to be queued. */ + mmc_hsq_update_next_tag(hsq, remains); + + if (hsq->waiting_for_idle && !remains) { + hsq->waiting_for_idle = false; + wake_up(&hsq->wait_queue); + } + + /* Do not pump new request in recovery mode. */ + if (hsq->recovery_halt) { + spin_unlock_irqrestore(&hsq->lock, flags); + return; + } + + spin_unlock_irqrestore(&hsq->lock, flags); + + /* + * Try to pump new request to host controller as fast as possible, + * after completing previous request. + */ + if (remains > 0) + mmc_hsq_pump_requests(hsq); +} + +/** + * mmc_hsq_finalize_request - finalize one request if the request is done + * @mmc: the host controller + * @mrq: the request need to be finalized + * + * Return true if we finalized the corresponding request in software queue, + * otherwise return false. + */ +bool mmc_hsq_finalize_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct mmc_hsq *hsq = mmc->cqe_private; + unsigned long flags; + + spin_lock_irqsave(&hsq->lock, flags); + + if (!hsq->enabled || !hsq->mrq || hsq->mrq != mrq) { + spin_unlock_irqrestore(&hsq->lock, flags); + return false; + } + + /* + * Clear current completed slot request to make a room for new request. + */ + hsq->slot[hsq->next_tag].mrq = NULL; + + spin_unlock_irqrestore(&hsq->lock, flags); + + mmc_cqe_request_done(mmc, hsq->mrq); + + mmc_hsq_post_request(hsq); + + return true; +} +EXPORT_SYMBOL_GPL(mmc_hsq_finalize_request); + +static void mmc_hsq_recovery_start(struct mmc_host *mmc) +{ + struct mmc_hsq *hsq = mmc->cqe_private; + unsigned long flags; + + spin_lock_irqsave(&hsq->lock, flags); + + hsq->recovery_halt = true; + + spin_unlock_irqrestore(&hsq->lock, flags); +} + +static void mmc_hsq_recovery_finish(struct mmc_host *mmc) +{ + struct mmc_hsq *hsq = mmc->cqe_private; + int remains; + + spin_lock_irq(&hsq->lock); + + hsq->recovery_halt = false; + remains = hsq->qcnt; + + spin_unlock_irq(&hsq->lock); + + /* + * Try to pump new request if there are request pending in software + * queue after finishing recovery. + */ + if (remains > 0) + mmc_hsq_pump_requests(hsq); +} + +static int mmc_hsq_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct mmc_hsq *hsq = mmc->cqe_private; + int tag = mrq->tag; + + spin_lock_irq(&hsq->lock); + + if (!hsq->enabled) { + spin_unlock_irq(&hsq->lock); + return -ESHUTDOWN; + } + + /* Do not queue any new requests in recovery mode. */ + if (hsq->recovery_halt) { + spin_unlock_irq(&hsq->lock); + return -EBUSY; + } + + hsq->slot[tag].mrq = mrq; + + /* + * Set the next tag as current request tag if no available + * next tag. + */ + if (hsq->next_tag == HSQ_INVALID_TAG) + hsq->next_tag = tag; + + hsq->qcnt++; + + spin_unlock_irq(&hsq->lock); + + mmc_hsq_pump_requests(hsq); + + return 0; +} + +static void mmc_hsq_post_req(struct mmc_host *mmc, struct mmc_request *mrq) +{ + if (mmc->ops->post_req) + mmc->ops->post_req(mmc, mrq, 0); +} + +static bool mmc_hsq_queue_is_idle(struct mmc_hsq *hsq, int *ret) +{ + bool is_idle; + + spin_lock_irq(&hsq->lock); + + is_idle = (!hsq->mrq && !hsq->qcnt) || + hsq->recovery_halt; + + *ret = hsq->recovery_halt ? -EBUSY : 0; + hsq->waiting_for_idle = !is_idle; + + spin_unlock_irq(&hsq->lock); + + return is_idle; +} + +static int mmc_hsq_wait_for_idle(struct mmc_host *mmc) +{ + struct mmc_hsq *hsq = mmc->cqe_private; + int ret; + + wait_event(hsq->wait_queue, + mmc_hsq_queue_is_idle(hsq, &ret)); + + return ret; +} + +static void mmc_hsq_disable(struct mmc_host *mmc) +{ + struct mmc_hsq *hsq = mmc->cqe_private; + u32 timeout = 500; + int ret; + + spin_lock_irq(&hsq->lock); + + if (!hsq->enabled) { + spin_unlock_irq(&hsq->lock); + return; + } + + spin_unlock_irq(&hsq->lock); + + ret = wait_event_timeout(hsq->wait_queue, + mmc_hsq_queue_is_idle(hsq, &ret), + msecs_to_jiffies(timeout)); + if (ret == 0) { + pr_warn("could not stop mmc software queue\n"); + return; + } + + spin_lock_irq(&hsq->lock); + + hsq->enabled = false; + + spin_unlock_irq(&hsq->lock); +} + +static int mmc_hsq_enable(struct mmc_host *mmc, struct mmc_card *card) +{ + struct mmc_hsq *hsq = mmc->cqe_private; + + spin_lock_irq(&hsq->lock); + + if (hsq->enabled) { + spin_unlock_irq(&hsq->lock); + return -EBUSY; + } + + hsq->enabled = true; + + spin_unlock_irq(&hsq->lock); + + return 0; +} + +static const struct mmc_cqe_ops mmc_hsq_ops = { + .cqe_enable = mmc_hsq_enable, + .cqe_disable = mmc_hsq_disable, + .cqe_request = mmc_hsq_request, + .cqe_post_req = mmc_hsq_post_req, + .cqe_wait_for_idle = mmc_hsq_wait_for_idle, + .cqe_recovery_start = mmc_hsq_recovery_start, + .cqe_recovery_finish = mmc_hsq_recovery_finish, +}; + +int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc) +{ + hsq->num_slots = HSQ_NUM_SLOTS; + hsq->next_tag = HSQ_INVALID_TAG; + + hsq->slot = devm_kcalloc(mmc_dev(mmc), hsq->num_slots, + sizeof(struct hsq_slot), GFP_KERNEL); + if (!hsq->slot) + return -ENOMEM; + + hsq->mmc = mmc; + hsq->mmc->cqe_private = hsq; + mmc->cqe_ops = &mmc_hsq_ops; + + spin_lock_init(&hsq->lock); + init_waitqueue_head(&hsq->wait_queue); + + return 0; +} +EXPORT_SYMBOL_GPL(mmc_hsq_init); + +void mmc_hsq_suspend(struct mmc_host *mmc) +{ + mmc_hsq_disable(mmc); +} +EXPORT_SYMBOL_GPL(mmc_hsq_suspend); + +int mmc_hsq_resume(struct mmc_host *mmc) +{ + return mmc_hsq_enable(mmc, NULL); +} +EXPORT_SYMBOL_GPL(mmc_hsq_resume); diff --git a/drivers/mmc/host/mmc_hsq.h b/drivers/mmc/host/mmc_hsq.h new file mode 100644 index 000000000000..18b9cf55925f --- /dev/null +++ b/drivers/mmc/host/mmc_hsq.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef LINUX_MMC_HSQ_H +#define LINUX_MMC_HSQ_H + +struct hsq_slot { + struct mmc_request *mrq; +}; + +struct mmc_hsq { + struct mmc_host *mmc; + struct mmc_request *mrq; + wait_queue_head_t wait_queue; + struct hsq_slot *slot; + spinlock_t lock; + + int next_tag; + int num_slots; + int qcnt; + + bool enabled; + bool waiting_for_idle; + bool recovery_halt; +}; + +int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc); +void mmc_hsq_suspend(struct mmc_host *mmc); +int mmc_hsq_resume(struct mmc_host *mmc); +bool mmc_hsq_finalize_request(struct mmc_host *mmc, struct mmc_request *mrq); + +#endif -- cgit v1.2.3 From 045d705dc1fba0d881fe22ad76ebe1b44647cdac Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 12 Feb 2020 12:12:57 +0800 Subject: mmc: core: Enable the MMC host software queue for the SD card Enable the MMC host software queue for the SD card if the host controller supports the MMC host software queue. On my Spreadtrum platform, I did not see any obvious performance changes in 4K block size when changing to use hsq for the SD cards, I think the reason is the SD card works at a low speed on my platform, and most of time is spent in the hardware. But we can see some obvious improvements when enabling the packed request based on hsq, that's why we still add hsq support for the SD cards. Signed-off-by: Baolin Wang Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/0065b4631fef2d61c3b89d14a4ea4f2b7499ea56.1581478568.git.baolin.wang7@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index fe914ff5f5d6..76c7add367d5 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1082,6 +1082,16 @@ retry: } } + if (host->cqe_ops && !host->cqe_enabled) { + err = host->cqe_ops->cqe_enable(host, card); + if (!err) { + host->cqe_enabled = true; + host->hsq_enabled = true; + pr_info("%s: Host Software Queue enabled\n", + mmc_hostname(host)); + } + } + if (host->caps2 & MMC_CAP2_AVOID_3_3V && host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) { pr_err("%s: Host failed to negotiate down from 3.3V\n", -- cgit v1.2.3 From 1774b0021405b4d312499095f6d3919b5bdf3e3b Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 12 Feb 2020 12:12:58 +0800 Subject: mmc: host: sdhci: Add request_done ops for struct sdhci_ops Add request_done ops for struct sdhci_ops as a preparation in case some host controllers have different method to complete one request, such as supporting request completion of MMC software queue. Suggested-by: Adrian Hunter Signed-off-by: Baolin Wang Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/1539c801c8bbdbcd1d86f8c2dab375f5803c765a.1581478568.git.baolin.wang7@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 12 ++++++++++-- drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 63db84481dff..9761981768f5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2944,7 +2944,10 @@ static bool sdhci_request_done(struct sdhci_host *host) spin_unlock_irqrestore(&host->lock, flags); - mmc_request_done(host->mmc, mrq); + if (host->ops->request_done) + host->ops->request_done(host, mrq); + else + mmc_request_done(host->mmc, mrq); return false; } @@ -3372,7 +3375,12 @@ out: /* Process mrqs ready for immediate completion */ for (i = 0; i < SDHCI_MAX_MRQS; i++) { - if (mrqs_done[i]) + if (!mrqs_done[i]) + continue; + + if (host->ops->request_done) + host->ops->request_done(host, mrqs_done[i]); + else mmc_request_done(host->mmc, mrqs_done[i]); } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index a6a3ddcf97e7..3e95f74cd05a 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -654,6 +654,8 @@ struct sdhci_ops { void (*voltage_switch)(struct sdhci_host *host); void (*adma_write_desc)(struct sdhci_host *host, void **desc, dma_addr_t addr, int len, unsigned int cmd); + void (*request_done)(struct sdhci_host *host, + struct mmc_request *mrq); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- cgit v1.2.3 From 4730831c7d2e28c2e912b2c71066b9cf072f7625 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 12 Feb 2020 12:12:59 +0800 Subject: mmc: host: sdhci: Add a variable to defer to complete requests if needed When using the host software queue, it will trigger the next request in irq handler without a context switch. But the sdhci_request() can not be called in interrupt context when using host software queue for some host drivers, due to the get_cd() ops can be sleepable. But for some host drivers, such as Spreadtrum host driver, the card is nonremovable, so the get_cd() ops is not sleepable, which means we can complete the data request and trigger the next request in irq handler to remove the context switch for the Spreadtrum host driver. As suggested by Adrian, we should introduce a request_atomic() API to indicate that a request can be called in interrupt context to remove the context switch when using mmc host software queue. But this should be done in another thread to convert the users of mmc host software queue. Thus we can introduce a variable in struct sdhci_host to indicate that we will always to defer to complete requests when using the host software queue. Suggested-by: Adrian Hunter Signed-off-by: Baolin Wang Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/e693e7a29beb3c1922b333f4603ea81f43d5c5b1.1581478568.git.baolin.wang7@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 +- drivers/mmc/host/sdhci.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9761981768f5..9c3745118e49 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3250,7 +3250,7 @@ static inline bool sdhci_defer_done(struct sdhci_host *host, { struct mmc_data *data = mrq->data; - return host->pending_reset || + return host->pending_reset || host->always_defer_done || ((host->flags & SDHCI_REQ_USE_DMA) && data && data->host_cookie == COOKIE_MAPPED); } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 3e95f74cd05a..cac2d97782e6 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -537,6 +537,7 @@ struct sdhci_host { bool irq_wake_enabled; /* IRQ wakeup is enabled */ bool v4_mode; /* Host Version 4 Enable */ bool use_external_dma; /* Host selects to use external DMA */ + bool always_defer_done; /* Always defer to complete requests */ struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */ struct mmc_command *cmd; /* Current command */ -- cgit v1.2.3 From f4498549e1690af7cba1eff8570da9c134cb80b5 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 12 Feb 2020 12:13:00 +0800 Subject: mmc: host: sdhci-sprd: Add software queue support Add software queue support to improve the performance. Signed-off-by: Baolin Wang Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/f629b32943aae9e30ffa17acf4af06c270417001.1581478569.git.baolin.wang7@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 1 + drivers/mmc/host/sdhci-sprd.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index fcb685efe17f..462b5352fea7 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -645,6 +645,7 @@ config MMC_SDHCI_SPRD depends on ARCH_SPRD depends on MMC_SDHCI_PLTFM select MMC_SDHCI_IO_ACCESSORS + select MMC_HSQ help This selects the SDIO Host Controller in Spreadtrum SoCs, this driver supports R11(IP version: R11P0). diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index d07b9793380f..d3462237bca2 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -19,6 +19,7 @@ #include #include "sdhci-pltfm.h" +#include "mmc_hsq.h" /* SDHCI_ARGUMENT2 register high 16bit */ #define SDHCI_SPRD_ARG2_STUFF GENMASK(31, 16) @@ -379,6 +380,16 @@ static unsigned int sdhci_sprd_get_ro(struct sdhci_host *host) return 0; } +static void sdhci_sprd_request_done(struct sdhci_host *host, + struct mmc_request *mrq) +{ + /* Validate if the request was from software queue firstly. */ + if (mmc_hsq_finalize_request(host->mmc, mrq)) + return; + + mmc_request_done(host->mmc, mrq); +} + static struct sdhci_ops sdhci_sprd_ops = { .read_l = sdhci_sprd_readl, .write_l = sdhci_sprd_writel, @@ -392,6 +403,7 @@ static struct sdhci_ops sdhci_sprd_ops = { .hw_reset = sdhci_sprd_hw_reset, .get_max_timeout_count = sdhci_sprd_get_max_timeout_count, .get_ro = sdhci_sprd_get_ro, + .request_done = sdhci_sprd_request_done, }; static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq) @@ -521,6 +533,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev) { struct sdhci_host *host; struct sdhci_sprd_host *sprd_host; + struct mmc_hsq *hsq; struct clk *clk; int ret = 0; @@ -631,6 +644,18 @@ static int sdhci_sprd_probe(struct platform_device *pdev) sprd_host->flags = host->flags; + hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL); + if (!hsq) { + ret = -ENOMEM; + goto err_cleanup_host; + } + + ret = mmc_hsq_init(hsq, host->mmc); + if (ret) + goto err_cleanup_host; + + host->always_defer_done = true; + ret = __sdhci_add_host(host); if (ret) goto err_cleanup_host; @@ -689,6 +714,7 @@ static int sdhci_sprd_runtime_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); + mmc_hsq_suspend(host->mmc); sdhci_runtime_suspend_host(host); clk_disable_unprepare(sprd_host->clk_sdio); @@ -717,6 +743,8 @@ static int sdhci_sprd_runtime_resume(struct device *dev) goto clk_disable; sdhci_runtime_resume_host(host, 1); + mmc_hsq_resume(host->mmc); + return 0; clk_disable: -- cgit v1.2.3 From d46a24a9d2dbe1a14988877e9e602d027cd35d43 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:38 +0100 Subject: mmc: core: Throttle polling rate for CMD6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In mmc_poll_for_busy() we loop continuously, either by sending a CMD13 or by invoking the ->card_busy() host ops, as to detect when the card stops signaling busy. This behaviour is problematic as it may cause CPU hogging, especially when the busy signal time reaches beyond a few ms. Let's fix the issue by adding a throttling mechanism, that inserts a usleep_range() in between the polling attempts. The sleep range starts at 32-64us, but increases for each loop by a factor of 2, up until the range reaches ~32-64ms. In this way, we are able to keep the loop fine-grained enough for short busy signaling times, while also not hogging the CPU for longer times. Note that, this change is inspired by the similar throttling mechanism that we already use for mmc_do_erase(). Reported-by: Michał Mirosław Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-2-ulf.hansson@linaro.org --- drivers/mmc/core/mmc_ops.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index e025604e17d4..da424dcb57ad 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -456,6 +456,7 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, struct mmc_host *host = card->host; int err; unsigned long timeout; + unsigned int udelay = 32, udelay_max = 32768; u32 status = 0; bool expired = false; bool busy = false; @@ -500,6 +501,13 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, mmc_hostname(host), __func__); return -ETIMEDOUT; } + + /* Throttle the polling rate to avoid hogging the CPU. */ + if (busy) { + usleep_range(udelay, udelay * 2); + if (udelay < udelay_max) + udelay *= 2; + } } while (busy); return 0; -- cgit v1.2.3 From ebd4f4bd01a97db44971762ff7a876c06c9b605f Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:39 +0100 Subject: mmc: core: Drop unused define The last user of MMC_OPS_TIMEOUT_MS was recently removed, however the define stayed around. Let's remove it. Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-3-ulf.hansson@linaro.org --- drivers/mmc/core/mmc_ops.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index da424dcb57ad..5ad950e26b15 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -19,7 +19,6 @@ #include "host.h" #include "mmc_ops.h" -#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10min*/ #define MMC_BKOPS_TIMEOUT_MS (120 * 1000) /* 120s */ #define MMC_CACHE_FLUSH_TIMEOUT_MS (30 * 1000) /* 30s */ -- cgit v1.2.3 From 60db8a47497d5e44e4227c9957dae9a2dc72389b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:40 +0100 Subject: mmc: core: Extend mmc_switch_status() to rid of __mmc_switch_status() To simplify code, let's extend mmc_switch_status() to cope with needs addressed in __mmc_switch_status(). Then move all users to the updated mmc_switch_status() API and drop __mmc_switch_status() altogether. Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-4-ulf.hansson@linaro.org --- drivers/mmc/core/mmc.c | 16 ++++++++-------- drivers/mmc/core/mmc_ops.c | 9 ++------- drivers/mmc/core/mmc_ops.h | 3 +-- 3 files changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 24223cb150d4..36846a880d08 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1173,7 +1173,7 @@ static int mmc_select_hs400(struct mmc_card *card) max_dtr = card->ext_csd.hs_max_dtr; mmc_set_clock(host, max_dtr); - err = mmc_switch_status(card); + err = mmc_switch_status(card, true); if (err) goto out_err; @@ -1211,7 +1211,7 @@ static int mmc_select_hs400(struct mmc_card *card) if (host->ops->hs400_complete) host->ops->hs400_complete(host); - err = mmc_switch_status(card); + err = mmc_switch_status(card, true); if (err) goto out_err; @@ -1249,7 +1249,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) mmc_set_timing(host, MMC_TIMING_MMC_DDR52); - err = mmc_switch_status(card); + err = mmc_switch_status(card, true); if (err) goto out_err; @@ -1265,7 +1265,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) if (host->ops->hs400_downgrade) host->ops->hs400_downgrade(host); - err = mmc_switch_status(card); + err = mmc_switch_status(card, true); if (err) goto out_err; @@ -1285,7 +1285,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) * failed. If there really is a problem, we would expect tuning will * fail and the result ends up the same. */ - err = __mmc_switch_status(card, false); + err = mmc_switch_status(card, false); if (err) goto out_err; @@ -1366,7 +1366,7 @@ static int mmc_select_hs400es(struct mmc_card *card) } mmc_set_timing(host, MMC_TIMING_MMC_HS); - err = mmc_switch_status(card); + err = mmc_switch_status(card, true); if (err) goto out_err; @@ -1407,7 +1407,7 @@ static int mmc_select_hs400es(struct mmc_card *card) if (host->ops->hs400_enhanced_strobe) host->ops->hs400_enhanced_strobe(host, &host->ios); - err = mmc_switch_status(card); + err = mmc_switch_status(card, true); if (err) goto out_err; @@ -1468,7 +1468,7 @@ static int mmc_select_hs200(struct mmc_card *card) * switch failed. If there really is a problem, we would expect * tuning will fail and the result ends up the same. */ - err = __mmc_switch_status(card, false); + err = mmc_switch_status(card, false); /* * mmc_select_timing() assumes timing has not changed if diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 5ad950e26b15..9385c4e18675 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -430,7 +430,7 @@ static int mmc_switch_status_error(struct mmc_host *host, u32 status) } /* Caller must hold re-tuning */ -int __mmc_switch_status(struct mmc_card *card, bool crc_err_fatal) +int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal) { u32 status; int err; @@ -444,11 +444,6 @@ int __mmc_switch_status(struct mmc_card *card, bool crc_err_fatal) return mmc_switch_status_error(card->host, status); } -int mmc_switch_status(struct mmc_card *card) -{ - return __mmc_switch_status(card, true); -} - static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, bool send_status, bool retry_crc_err) { @@ -596,7 +591,7 @@ out_tim: mmc_set_timing(host, timing); if (send_status) { - err = mmc_switch_status(card); + err = mmc_switch_status(card, true); if (err && timing) mmc_set_timing(host, old_timing); } diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 8f2f9475716d..09dee8a466a0 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -29,8 +29,7 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_interrupt_hpi(struct mmc_card *card); int mmc_can_ext_csd(struct mmc_card *card); int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); -int mmc_switch_status(struct mmc_card *card); -int __mmc_switch_status(struct mmc_card *card, bool crc_err_fatal); +int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal); int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms, unsigned char timing, bool use_busy_signal, bool send_status, bool retry_crc_err); -- cgit v1.2.3 From 02098ccdd823b598e92508918e20b37a1169df42 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:41 +0100 Subject: mmc: core: Drop redundant in-parameter to __mmc_switch() The use_busy_signal in-parameter is set true by all callers of __mmc_switch(), hence it's redundant so drop it. Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-5-ulf.hansson@linaro.org --- drivers/mmc/core/mmc.c | 22 +++++++++++----------- drivers/mmc/core/mmc_ops.c | 11 +++-------- drivers/mmc/core/mmc_ops.h | 2 +- 3 files changed, 15 insertions(+), 20 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 36846a880d08..de94fbe629bd 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1055,7 +1055,7 @@ static int mmc_select_hs(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time, MMC_TIMING_MMC_HS, - true, true, true); + true, true); if (err) pr_warn("%s: switch to high-speed failed, err:%d\n", mmc_hostname(card->host), err); @@ -1087,7 +1087,7 @@ static int mmc_select_hs_ddr(struct mmc_card *card) ext_csd_bits, card->ext_csd.generic_cmd6_time, MMC_TIMING_MMC_DDR52, - true, true, true); + true, true); if (err) { pr_err("%s: switch to bus width %d ddr failed\n", mmc_hostname(host), 1 << bus_width); @@ -1155,7 +1155,7 @@ static int mmc_select_hs400(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, 0, - true, false, true); + false, true); if (err) { pr_err("%s: switch to high-speed from hs200 failed, err:%d\n", mmc_hostname(host), err); @@ -1197,7 +1197,7 @@ static int mmc_select_hs400(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, 0, - true, false, true); + false, true); if (err) { pr_err("%s: switch to hs400 failed, err:%d\n", mmc_hostname(host), err); @@ -1243,7 +1243,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) val = EXT_CSD_TIMING_HS; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, 0, - true, false, true); + false, true); if (err) goto out_err; @@ -1256,7 +1256,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) /* Switch HS DDR to HS */ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time, - 0, true, false, true); + 0, false, true); if (err) goto out_err; @@ -1274,7 +1274,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, 0, - true, false, true); + false, true); if (err) goto out_err; @@ -1358,7 +1358,7 @@ static int mmc_select_hs400es(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time, 0, - true, false, true); + false, true); if (err) { pr_err("%s: switch to hs for hs400es failed, err:%d\n", mmc_hostname(host), err); @@ -1392,7 +1392,7 @@ static int mmc_select_hs400es(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, 0, - true, false, true); + false, true); if (err) { pr_err("%s: switch to hs400es failed, err:%d\n", mmc_hostname(host), err); @@ -1457,7 +1457,7 @@ static int mmc_select_hs200(struct mmc_card *card) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, 0, - true, false, true); + false, true); if (err) goto err; old_timing = host->ios.timing; @@ -1962,7 +1962,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_POWER_OFF_NOTIFICATION, - notify_type, timeout, 0, true, false, false); + notify_type, timeout, 0, false, false); if (err) pr_err("%s: Power Off Notification timed out, %u\n", mmc_hostname(card->host), timeout); diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 9385c4e18675..cdd438ddac92 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -516,7 +516,6 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, * @timeout_ms: timeout (ms) for operation performed by register write, * timeout of zero implies maximum possible timeout * @timing: new timing to change to - * @use_busy_signal: use the busy signal as response type * @send_status: send status cmd to poll for busy * @retry_crc_err: retry when CRC errors when polling with CMD13 for busy * @@ -524,12 +523,12 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, */ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms, unsigned char timing, - bool use_busy_signal, bool send_status, bool retry_crc_err) + bool send_status, bool retry_crc_err) { struct mmc_host *host = card->host; int err; struct mmc_command cmd = {}; - bool use_r1b_resp = use_busy_signal; + bool use_r1b_resp = true; unsigned char old_timing = host->ios.timing; mmc_retune_hold(host); @@ -571,10 +570,6 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, if (err) goto out; - /* No need to check card status in case of unblocking command */ - if (!use_busy_signal) - goto out; - /*If SPI or used HW busy detection above, then we don't need to poll. */ if (((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) || mmc_host_is_spi(host)) @@ -605,7 +600,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms) { return __mmc_switch(card, set, index, value, timeout_ms, 0, - true, true, false); + true, false); } EXPORT_SYMBOL_GPL(mmc_switch); diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 09dee8a466a0..de0c509a3a38 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -32,7 +32,7 @@ int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal); int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms, unsigned char timing, - bool use_busy_signal, bool send_status, bool retry_crc_err); + bool send_status, bool retry_crc_err); int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms); void mmc_run_bkops(struct mmc_card *card); -- cgit v1.2.3 From 6972096a03ae40f1365d2829f92fd19202b2b326 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:42 +0100 Subject: mmc: core: Split up mmc_poll_for_busy() To make the code more readable, move the part that gets the busy status of the card out into a separate function, mmc_busy_status(). Then call it from mmc_poll_for_busy(). Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-6-ulf.hansson@linaro.org --- drivers/mmc/core/mmc_ops.c | 47 ++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index cdd438ddac92..3bb337a399f9 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -444,6 +444,34 @@ int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal) return mmc_switch_status_error(card->host, status); } +static int mmc_busy_status(struct mmc_card *card, bool retry_crc_err, + bool *busy) +{ + struct mmc_host *host = card->host; + u32 status = 0; + int err; + + if (host->ops->card_busy) { + *busy = host->ops->card_busy(host); + return 0; + } + + err = mmc_send_status(card, &status); + if (retry_crc_err && err == -EILSEQ) { + *busy = true; + return 0; + } + if (err) + return err; + + err = mmc_switch_status_error(card->host, status); + if (err) + return err; + + *busy = R1_CURRENT_STATE(status) == R1_STATE_PRG; + return 0; +} + static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, bool send_status, bool retry_crc_err) { @@ -451,7 +479,6 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, int err; unsigned long timeout; unsigned int udelay = 32, udelay_max = 32768; - u32 status = 0; bool expired = false; bool busy = false; @@ -473,21 +500,9 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, */ expired = time_after(jiffies, timeout); - if (host->ops->card_busy) { - busy = host->ops->card_busy(host); - } else { - err = mmc_send_status(card, &status); - if (retry_crc_err && err == -EILSEQ) { - busy = true; - } else if (err) { - return err; - } else { - err = mmc_switch_status_error(host, status); - if (err) - return err; - busy = R1_CURRENT_STATE(status) == R1_STATE_PRG; - } - } + err = mmc_busy_status(card, retry_crc_err, &busy); + if (err) + return err; /* Timeout if the device still remains busy. */ if (expired && busy) { -- cgit v1.2.3 From 40c96853fef1bdef13d2cd05b21d4f06b2de6321 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:43 +0100 Subject: mmc: core: Enable re-use of mmc_blk_in_tran_state() To allow subsequent changes to re-use the code from the static function mmc_blk_in_tran_state(), let's move it to a public header. While at it, let's also rename it to mmc_ready_for_data(), as to try to better describe its purpose. Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-7-ulf.hansson@linaro.org --- drivers/mmc/core/block.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 55d52fc46758..7634894df853 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -441,16 +441,6 @@ out: return err; } -static inline bool mmc_blk_in_tran_state(u32 status) -{ - /* - * Some cards mishandle the status bits, so make sure to check both the - * busy indication and the card state. - */ - return status & R1_READY_FOR_DATA && - (R1_CURRENT_STATE(status) == R1_STATE_TRAN); -} - static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, u32 *resp_errs) { @@ -482,13 +472,7 @@ static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, __func__, status); return -ETIMEDOUT; } - - /* - * Some cards mishandle the status bits, - * so make sure to check both the busy - * indication and the card state. - */ - } while (!mmc_blk_in_tran_state(status)); + } while (!mmc_ready_for_data(status)); return err; } @@ -1692,7 +1676,7 @@ static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req) goto error_exit; if (!mmc_host_is_spi(host) && - !mmc_blk_in_tran_state(status)) { + !mmc_ready_for_data(status)) { err = mmc_blk_fix_state(card, req); if (err) goto error_exit; @@ -1752,7 +1736,7 @@ static bool mmc_blk_status_error(struct request *req, u32 status) return brq->cmd.resp[0] & CMD_ERRORS || brq->stop.resp[0] & stop_err_bits || status & stop_err_bits || - (rq_data_dir(req) == WRITE && !mmc_blk_in_tran_state(status)); + (rq_data_dir(req) == WRITE && !mmc_ready_for_data(status)); } static inline bool mmc_blk_cmd_started(struct mmc_blk_request *brq) @@ -1814,7 +1798,7 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req) /* Try to get back to "tran" state */ if (!mmc_host_is_spi(mq->card->host) && - (err || !mmc_blk_in_tran_state(status))) + (err || !mmc_ready_for_data(status))) err = mmc_blk_fix_state(mq->card, req); /* -- cgit v1.2.3 From 2a1c7cda52b7b9db50086a40654dc5c03fca866f Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:44 +0100 Subject: mmc: core: Update CMD13 busy check for CMD6 commands Through mmc_poll_for_busy() a CMD13 may be sent to get the status of the (e)MMC card. If the state of the card is R1_STATE_PRG, the card is considered as being busy, which means we continue to poll with CMD13. This seems to be sufficient, but it's also unnecessary fragile, as it means a new command/request could potentially be sent to the card when it's in an unknown state. To try to improve the situation, but also to move towards a more consistent CMD13 polling behaviour in the mmc core, let's deploy the same policy we use for regular I/O write requests. In other words, let's check that card returns to the R1_STATE_TRAN and that the R1_READY_FOR_DATA bit is set in the CMD13 response, before exiting the polling loop. Note that, potentially this changed behaviour could lead to unnecessary waiting for the timeout to expire, if the card for some reason, moves to an unexpected error state. However, as we bail out from the polling loop when R1_SWITCH_ERROR bit is set or when the CMD13 fails, this shouldn't be an issue. Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-8-ulf.hansson@linaro.org --- drivers/mmc/core/mmc_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 3bb337a399f9..4566f43305d5 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -468,7 +468,7 @@ static int mmc_busy_status(struct mmc_card *card, bool retry_crc_err, if (err) return err; - *busy = R1_CURRENT_STATE(status) == R1_STATE_PRG; + *busy = !mmc_ready_for_data(status); return 0; } -- cgit v1.2.3 From 0d84c3e6a5b2cd4ddd68b7ef7cf7c2dafd5146ef Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:45 +0100 Subject: mmc: core: Convert to mmc_poll_for_busy() for erase/trim/discard Rather than open coding the polling loop in mmc_do_erase(), let's convert to use mmc_poll_for_busy(). To allow a slightly different error parsing during polling, compared to the __mmc_switch() case, a new in-parameter to mmc_poll_for_busy() is needed, but other than that the conversion is straight forward. Besides addressing the open coding issue, moving to mmc_poll_for_busy() for erase/trim/discard improves the behaviour according to below. - Adds support for polling via the optional ->card_busy() host ops. - Returns zero to indicate success when the final polling attempt finds the card non-busy, even if the timeout expired. - Exits the polling loop when state moves to R1_STATE_TRAN, rather than when leaving R1_STATE_PRG. - Decreases the starting range for throttling to 32-64us. Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-9-ulf.hansson@linaro.org --- drivers/mmc/core/core.c | 36 ++---------------------------------- drivers/mmc/core/mmc_ops.c | 30 ++++++++++++++++++++++++------ drivers/mmc/core/mmc_ops.h | 7 +++++++ 3 files changed, 33 insertions(+), 40 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a971c4bcc442..3f7a31456eb4 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1658,8 +1658,6 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, struct mmc_command cmd = {}; unsigned int qty = 0, busy_timeout = 0; bool use_r1b_resp = false; - unsigned long timeout; - int loop_udelay=64, udelay_max=32768; int err; mmc_retune_hold(card->host); @@ -1763,38 +1761,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) goto out; - timeout = jiffies + msecs_to_jiffies(busy_timeout); - do { - memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = card->rca << 16; - 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 || R1_STATUS(cmd.resp[0])) { - pr_err("error %d requesting status %#x\n", - err, cmd.resp[0]); - err = -EIO; - goto out; - } - - /* Timeout if the device never becomes ready for data and - * never leaves the program state. - */ - if (time_after(jiffies, timeout)) { - pr_err("%s: Card stuck in programming state! %s\n", - mmc_hostname(card->host), __func__); - err = -EIO; - goto out; - } - if ((cmd.resp[0] & R1_READY_FOR_DATA) && - R1_CURRENT_STATE(cmd.resp[0]) != R1_STATE_PRG) - break; - - usleep_range(loop_udelay, loop_udelay*2); - if (loop_udelay < udelay_max) - loop_udelay *= 2; - } while (1); + /* Let's poll to find out when the erase operation completes. */ + err = mmc_poll_for_busy(card, busy_timeout, MMC_BUSY_ERASE); out: mmc_retune_release(card->host); diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 4566f43305d5..3f510348b916 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -445,7 +445,7 @@ int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal) } static int mmc_busy_status(struct mmc_card *card, bool retry_crc_err, - bool *busy) + enum mmc_busy_cmd busy_cmd, bool *busy) { struct mmc_host *host = card->host; u32 status = 0; @@ -464,7 +464,17 @@ static int mmc_busy_status(struct mmc_card *card, bool retry_crc_err, if (err) return err; - err = mmc_switch_status_error(card->host, status); + switch (busy_cmd) { + case MMC_BUSY_CMD6: + err = mmc_switch_status_error(card->host, status); + break; + case MMC_BUSY_ERASE: + err = R1_STATUS(status) ? -EIO : 0; + break; + default: + err = -EINVAL; + } + if (err) return err; @@ -472,8 +482,9 @@ static int mmc_busy_status(struct mmc_card *card, bool retry_crc_err, return 0; } -static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, - bool send_status, bool retry_crc_err) +static int __mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, + bool send_status, bool retry_crc_err, + enum mmc_busy_cmd busy_cmd) { struct mmc_host *host = card->host; int err; @@ -500,7 +511,7 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, */ expired = time_after(jiffies, timeout); - err = mmc_busy_status(card, retry_crc_err, &busy); + err = mmc_busy_status(card, retry_crc_err, busy_cmd, &busy); if (err) return err; @@ -522,6 +533,12 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, return 0; } +int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, + enum mmc_busy_cmd busy_cmd) +{ + return __mmc_poll_for_busy(card, timeout_ms, true, false, busy_cmd); +} + /** * __mmc_switch - modify EXT_CSD register * @card: the MMC card associated with the data transfer @@ -591,7 +608,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, goto out_tim; /* Let's try to poll to find out when the command is completed. */ - err = mmc_poll_for_busy(card, timeout_ms, send_status, retry_crc_err); + err = __mmc_poll_for_busy(card, timeout_ms, send_status, retry_crc_err, + MMC_BUSY_CMD6); if (err) goto out; diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index de0c509a3a38..8cd05fb583da 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -10,6 +10,11 @@ #include +enum mmc_busy_cmd { + MMC_BUSY_CMD6, + MMC_BUSY_ERASE, +}; + struct mmc_host; struct mmc_card; @@ -30,6 +35,8 @@ int mmc_interrupt_hpi(struct mmc_card *card); int mmc_can_ext_csd(struct mmc_card *card); int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal); +int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms, + enum mmc_busy_cmd busy_cmd); int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms, unsigned char timing, bool send_status, bool retry_crc_err); -- cgit v1.2.3 From 9f94d04752ad6bf6a704637c61590fe55c4c0d82 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:46 +0100 Subject: mmc: core: Drop redundant out-parameter to mmc_send_hpi_cmd() The 'u32 *status' is unused by the caller, so let's drop it. Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-10-ulf.hansson@linaro.org --- drivers/mmc/core/mmc_ops.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 3f510348b916..7d150e68be08 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -829,7 +829,7 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width) return mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width); } -static int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) +static int mmc_send_hpi_cmd(struct mmc_card *card) { struct mmc_command cmd = {}; unsigned int opcode; @@ -851,8 +851,6 @@ static int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) err, cmd.resp[0]); return err; } - if (status) - *status = cmd.resp[0]; return 0; } @@ -901,7 +899,7 @@ int mmc_interrupt_hpi(struct mmc_card *card) goto out; } - err = mmc_send_hpi_cmd(card, &status); + err = mmc_send_hpi_cmd(card); if (err) goto out; -- cgit v1.2.3 From 490ff95f8e2c89d7541f187bae78d340b20d0ef0 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:47 +0100 Subject: mmc: core: Convert to mmc_poll_for_busy() for HPI commands Rather than open coding the polling loop in mmc_interrupt_hpi(), let's convert to use mmc_poll_for_busy(). Note that, moving to mmc_poll_for_busy() for HPI also improves the behaviour according to below. - Adds support for polling via the optional ->card_busy() host ops. - Require R1_READY_FOR_DATA to be set in the CMD13 response before exiting the polling loop. - Adds a throttling mechanism to avoid CPU hogging when polling. Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-11-ulf.hansson@linaro.org --- drivers/mmc/core/mmc_ops.c | 20 +++++--------------- drivers/mmc/core/mmc_ops.h | 1 + 2 files changed, 6 insertions(+), 15 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 7d150e68be08..0099f39a60a3 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -471,6 +471,8 @@ static int mmc_busy_status(struct mmc_card *card, bool retry_crc_err, case MMC_BUSY_ERASE: err = R1_STATUS(status) ? -EIO : 0; break; + case MMC_BUSY_HPI: + break; default: err = -EINVAL; } @@ -831,6 +833,7 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width) static int mmc_send_hpi_cmd(struct mmc_card *card) { + unsigned int busy_timeout_ms = card->ext_csd.out_of_int_time; struct mmc_command cmd = {}; unsigned int opcode; int err; @@ -852,7 +855,8 @@ static int mmc_send_hpi_cmd(struct mmc_card *card) return err; } - return 0; + /* Let's poll to find out when the HPI request completes. */ + return mmc_poll_for_busy(card, busy_timeout_ms, MMC_BUSY_HPI); } /** @@ -866,7 +870,6 @@ int mmc_interrupt_hpi(struct mmc_card *card) { int err; u32 status; - unsigned long prg_wait; if (!card->ext_csd.hpi_en) { pr_info("%s: HPI enable bit unset\n", mmc_hostname(card->host)); @@ -900,19 +903,6 @@ int mmc_interrupt_hpi(struct mmc_card *card) } err = mmc_send_hpi_cmd(card); - if (err) - goto out; - - prg_wait = jiffies + msecs_to_jiffies(card->ext_csd.out_of_int_time); - do { - err = mmc_send_status(card, &status); - - if (!err && R1_CURRENT_STATE(status) == R1_STATE_TRAN) - break; - if (time_after(jiffies, prg_wait)) - err = -ETIMEDOUT; - } while (!err); - out: return err; } diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 8cd05fb583da..38dcfeeaf6d5 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -13,6 +13,7 @@ enum mmc_busy_cmd { MMC_BUSY_CMD6, MMC_BUSY_ERASE, + MMC_BUSY_HPI, }; struct mmc_host; -- cgit v1.2.3 From 892bf1001459fc38c7e9bc248633681ca628e614 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 4 Feb 2020 09:54:48 +0100 Subject: mmc: core: Fixup support for HW busy detection for HPI commands In case the host specify a max_busy_timeout, we need to validate that the needed timeout for the HPI command conforms to that requirement. If that's not the case, let's convert from a R1B response to a R1 response, as to instruct the host to avoid HW busy detection. Additionally, when R1B is used we must also inform the host about the busy timeout for the command, so let's do that via updating cmd.busy_timeout. Finally, when R1B is used and in case the host supports HW busy detection, there should be no need for doing polling, so then skip that. Signed-off-by: Ulf Hansson Tested-by: Baolin Wang Tested-by: Ludovic Barre Reviewed-by: Ludovic Barre Link: https://lore.kernel.org/r/20200204085449.32585-12-ulf.hansson@linaro.org --- drivers/mmc/core/mmc_ops.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 0099f39a60a3..c75c00b5890d 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -834,27 +834,41 @@ int mmc_bus_test(struct mmc_card *card, u8 bus_width) static int mmc_send_hpi_cmd(struct mmc_card *card) { unsigned int busy_timeout_ms = card->ext_csd.out_of_int_time; + struct mmc_host *host = card->host; + bool use_r1b_resp = true; struct mmc_command cmd = {}; - unsigned int opcode; int err; - opcode = card->ext_csd.hpi_cmd; - if (opcode == MMC_STOP_TRANSMISSION) + cmd.opcode = card->ext_csd.hpi_cmd; + cmd.arg = card->rca << 16 | 1; + + /* + * Make sure the host's max_busy_timeout fit the needed timeout for HPI. + * In case it doesn't, let's instruct the host to avoid HW busy + * detection, by using a R1 response instead of R1B. + */ + if (host->max_busy_timeout && busy_timeout_ms > host->max_busy_timeout) + use_r1b_resp = false; + + if (cmd.opcode == MMC_STOP_TRANSMISSION && use_r1b_resp) { cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - else if (opcode == MMC_SEND_STATUS) + cmd.busy_timeout = busy_timeout_ms; + } else { cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + use_r1b_resp = false; + } - cmd.opcode = opcode; - cmd.arg = card->rca << 16 | 1; - - err = mmc_wait_for_cmd(card->host, &cmd, 0); + err = mmc_wait_for_cmd(host, &cmd, 0); if (err) { - pr_warn("%s: error %d interrupting operation. " - "HPI command response %#x\n", mmc_hostname(card->host), - err, cmd.resp[0]); + pr_warn("%s: HPI error %d. Command response %#x\n", + mmc_hostname(host), err, cmd.resp[0]); return err; } + /* No need to poll when using HW busy detection. */ + if (host->caps & MMC_CAP_WAIT_WHILE_BUSY && use_r1b_resp) + return 0; + /* Let's poll to find out when the HPI request completes. */ return mmc_poll_for_busy(card, busy_timeout_ms, MMC_BUSY_HPI); } -- cgit v1.2.3 From 127e6e98ca9b8ac4f87698ebce1508e3449bb791 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Tue, 28 Jan 2020 10:06:28 +0100 Subject: mmc: mmci_sdmmc: Replace sg_dma_xxx macros sg_dma_xxx should be used after a dma_map_sg call has been done to get bus addresses of each of the SG entries and their lengths. But mmci_host_ops validate_data can be called before dma_map_sg. This patch replaces theses macros by sg->offset and sg->length which are always defined. Signed-off-by: Ludovic Barre Link: https://lore.kernel.org/r/20200128090636.13689-2-ludovic.barre@st.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci_stm32_sdmmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c index a4f7e8e689d3..6ccfbbc82c77 100644 --- a/drivers/mmc/host/mmci_stm32_sdmmc.c +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c @@ -36,8 +36,8 @@ static int sdmmc_idma_validate_data(struct mmci_host *host, * excepted the last element which has no constraint on idmasize */ for_each_sg(data->sg, sg, data->sg_len - 1, i) { - if (!IS_ALIGNED(sg_dma_address(data->sg), sizeof(u32)) || - !IS_ALIGNED(sg_dma_len(data->sg), SDMMC_IDMA_BURST)) { + if (!IS_ALIGNED(data->sg->offset, sizeof(u32)) || + !IS_ALIGNED(data->sg->length, SDMMC_IDMA_BURST)) { dev_err(mmc_dev(host->mmc), "unaligned scatterlist: ofst:%x length:%d\n", data->sg->offset, data->sg->length); @@ -45,7 +45,7 @@ static int sdmmc_idma_validate_data(struct mmci_host *host, } } - if (!IS_ALIGNED(sg_dma_address(data->sg), sizeof(u32))) { + if (!IS_ALIGNED(data->sg->offset, sizeof(u32))) { dev_err(mmc_dev(host->mmc), "unaligned last scatterlist: ofst:%x length:%d\n", data->sg->offset, data->sg->length); -- cgit v1.2.3 From bdbf9faf5f2e6bb0c0243350428c908ac85c16b2 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Tue, 28 Jan 2020 10:06:29 +0100 Subject: mmc: mmci_sdmmc: Rename sdmmc_priv struct to sdmmc_idma This patch renames sdmmc_priv struct to sdmmc_idma which is assigned to host->dma_priv. Signed-off-by: Ludovic Barre Link: https://lore.kernel.org/r/20200128090636.13689-3-ludovic.barre@st.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci_stm32_sdmmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c index 6ccfbbc82c77..df08f6662431 100644 --- a/drivers/mmc/host/mmci_stm32_sdmmc.c +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c @@ -20,7 +20,7 @@ struct sdmmc_lli_desc { u32 idmasize; }; -struct sdmmc_priv { +struct sdmmc_idma { dma_addr_t sg_dma; void *sg_cpu; }; @@ -92,7 +92,7 @@ static void sdmmc_idma_unprep_data(struct mmci_host *host, static int sdmmc_idma_setup(struct mmci_host *host) { - struct sdmmc_priv *idma; + struct sdmmc_idma *idma; idma = devm_kzalloc(mmc_dev(host->mmc), sizeof(*idma), GFP_KERNEL); if (!idma) @@ -123,7 +123,7 @@ static int sdmmc_idma_setup(struct mmci_host *host) static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl) { - struct sdmmc_priv *idma = host->dma_priv; + struct sdmmc_idma *idma = host->dma_priv; struct sdmmc_lli_desc *desc = (struct sdmmc_lli_desc *)idma->sg_cpu; struct mmc_data *data = host->data; struct scatterlist *sg; -- cgit v1.2.3 From 7b9716a0f1610951f98caedac3542313a527db0e Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Tue, 28 Jan 2020 10:06:30 +0100 Subject: mmc: mmci: Add a reference at mmc_host_ops in mmci struct The variant init function may need to add a mmc_host_ops, for example to add the execute_tuning support if this feature is available. This patch adds mmc_host_ops pointer in mmci struct. Signed-off-by: Ludovic Barre Link: https://lore.kernel.org/r/20200128090636.13689-4-ludovic.barre@st.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 4 ++-- drivers/mmc/host/mmci.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index e9ffce8d41ea..d0a041c9e6cd 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1933,6 +1933,8 @@ static int mmci_probe(struct amba_device *dev, host = mmc_priv(mmc); host->mmc = mmc; + host->mmc_ops = &mmci_ops; + mmc->ops = &mmci_ops; /* * Some variant (STM32) doesn't have opendrain bit, nevertheless @@ -2072,8 +2074,6 @@ static int mmci_probe(struct amba_device *dev, host->stop_abort.arg = 0; host->stop_abort.flags = MMC_RSP_R1B | MMC_CMD_AC; - mmc->ops = &mmci_ops; - /* We support these PM capabilities. */ mmc->pm_caps |= MMC_PM_KEEP_POWER; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index ea6a0b5779d4..55acc0971a44 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -407,6 +407,7 @@ struct mmci_host { u32 mask1_reg; u8 vqmmc_enabled:1; struct mmci_platform_data *plat; + struct mmc_host_ops *mmc_ops; struct mmci_host_ops *ops; struct variant_data *variant; struct pinctrl *pinctrl; -- cgit v1.2.3 From 31b963e19491692e56da00b15fd29623f5c5d0cf Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Tue, 28 Jan 2020 10:06:31 +0100 Subject: mmc: mmci: Add private pointer for variant In variant init function, some references may be allocated for variant specific usage. Add a private void* to mmci_host struct allows at variant functions to access on this references by mmci_host structure. Signed-off-by: Ludovic Barre Link: https://lore.kernel.org/r/20200128090636.13689-5-ludovic.barre@st.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 55acc0971a44..ddcdfb827996 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -410,6 +410,7 @@ struct mmci_host { struct mmc_host_ops *mmc_ops; struct mmci_host_ops *ops; struct variant_data *variant; + void *variant_priv; struct pinctrl *pinctrl; struct pinctrl_state *pins_opendrain; -- cgit v1.2.3 From 1103f807a3b9949c667939fed77d77f409029145 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Tue, 28 Jan 2020 10:06:33 +0100 Subject: mmc: mmci_sdmmc: Add execute tuning with delay block The hardware delay block is used to align the sampling clock on the data received by SDMMC. It is mandatory for SDMMC to support the SDR104 mode. The delay block is used to generate an output clock which is dephased from the input clock. The phase of the output clock must be programmed by the execute tuning interface. Signed-off-by: Ludovic Barre Link: https://lore.kernel.org/r/20200128090636.13689-7-ludovic.barre@st.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci_stm32_sdmmc.c | 149 ++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c index df08f6662431..fa875febcc85 100644 --- a/drivers/mmc/host/mmci_stm32_sdmmc.c +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c @@ -3,10 +3,13 @@ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved * Author: Ludovic.barre@st.com for STMicroelectronics. */ +#include #include #include +#include #include #include +#include #include #include #include "mmci.h" @@ -14,6 +17,22 @@ #define SDMMC_LLI_BUF_LEN PAGE_SIZE #define SDMMC_IDMA_BURST BIT(MMCI_STM32_IDMABNDT_SHIFT) +#define DLYB_CR 0x0 +#define DLYB_CR_DEN BIT(0) +#define DLYB_CR_SEN BIT(1) + +#define DLYB_CFGR 0x4 +#define DLYB_CFGR_SEL_MASK GENMASK(3, 0) +#define DLYB_CFGR_UNIT_MASK GENMASK(14, 8) +#define DLYB_CFGR_LNG_MASK GENMASK(27, 16) +#define DLYB_CFGR_LNGF BIT(31) + +#define DLYB_NB_DELAY 11 +#define DLYB_CFGR_SEL_MAX (DLYB_NB_DELAY + 1) +#define DLYB_CFGR_UNIT_MAX 127 + +#define DLYB_LNG_TIMEOUT_US 1000 + struct sdmmc_lli_desc { u32 idmalar; u32 idmabase; @@ -25,6 +44,12 @@ struct sdmmc_idma { void *sg_cpu; }; +struct sdmmc_dlyb { + void __iomem *base; + u32 unit; + u32 max; +}; + static int sdmmc_idma_validate_data(struct mmci_host *host, struct mmc_data *data) { @@ -226,12 +251,24 @@ static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired) mmci_write_clkreg(host, clk); } +static void sdmmc_dlyb_input_ck(struct sdmmc_dlyb *dlyb) +{ + if (!dlyb || !dlyb->base) + return; + + /* Output clock = Input clock */ + writel_relaxed(0, dlyb->base + DLYB_CR); +} + static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr) { struct mmc_ios ios = host->mmc->ios; + struct sdmmc_dlyb *dlyb = host->variant_priv; pwr = host->pwr_reg_add; + sdmmc_dlyb_input_ck(dlyb); + if (ios.power_mode == MMC_POWER_OFF) { /* Only a reset could power-off sdmmc */ reset_control_assert(host->rst); @@ -323,6 +360,102 @@ complete: return true; } +static void sdmmc_dlyb_set_cfgr(struct sdmmc_dlyb *dlyb, + int unit, int phase, bool sampler) +{ + u32 cfgr; + + writel_relaxed(DLYB_CR_SEN | DLYB_CR_DEN, dlyb->base + DLYB_CR); + + cfgr = FIELD_PREP(DLYB_CFGR_UNIT_MASK, unit) | + FIELD_PREP(DLYB_CFGR_SEL_MASK, phase); + writel_relaxed(cfgr, dlyb->base + DLYB_CFGR); + + if (!sampler) + writel_relaxed(DLYB_CR_DEN, dlyb->base + DLYB_CR); +} + +static int sdmmc_dlyb_lng_tuning(struct mmci_host *host) +{ + struct sdmmc_dlyb *dlyb = host->variant_priv; + u32 cfgr; + int i, lng, ret; + + for (i = 0; i <= DLYB_CFGR_UNIT_MAX; i++) { + sdmmc_dlyb_set_cfgr(dlyb, i, DLYB_CFGR_SEL_MAX, true); + + ret = readl_relaxed_poll_timeout(dlyb->base + DLYB_CFGR, cfgr, + (cfgr & DLYB_CFGR_LNGF), + 1, DLYB_LNG_TIMEOUT_US); + if (ret) { + dev_warn(mmc_dev(host->mmc), + "delay line cfg timeout unit:%d cfgr:%d\n", + i, cfgr); + continue; + } + + lng = FIELD_GET(DLYB_CFGR_LNG_MASK, cfgr); + if (lng < BIT(DLYB_NB_DELAY) && lng > 0) + break; + } + + if (i > DLYB_CFGR_UNIT_MAX) + return -EINVAL; + + dlyb->unit = i; + dlyb->max = __fls(lng); + + return 0; +} + +static int sdmmc_dlyb_phase_tuning(struct mmci_host *host, u32 opcode) +{ + struct sdmmc_dlyb *dlyb = host->variant_priv; + int cur_len = 0, max_len = 0, end_of_len = 0; + int phase; + + for (phase = 0; phase <= dlyb->max; phase++) { + sdmmc_dlyb_set_cfgr(dlyb, dlyb->unit, phase, false); + + if (mmc_send_tuning(host->mmc, opcode, NULL)) { + cur_len = 0; + } else { + cur_len++; + if (cur_len > max_len) { + max_len = cur_len; + end_of_len = phase; + } + } + } + + if (!max_len) { + dev_err(mmc_dev(host->mmc), "no tuning point found\n"); + return -EINVAL; + } + + phase = end_of_len - max_len / 2; + sdmmc_dlyb_set_cfgr(dlyb, dlyb->unit, phase, false); + + dev_dbg(mmc_dev(host->mmc), "unit:%d max_dly:%d phase:%d\n", + dlyb->unit, dlyb->max, phase); + + return 0; +} + +static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct mmci_host *host = mmc_priv(mmc); + struct sdmmc_dlyb *dlyb = host->variant_priv; + + if (!dlyb || !dlyb->base) + return -EINVAL; + + if (sdmmc_dlyb_lng_tuning(host)) + return -EINVAL; + + return sdmmc_dlyb_phase_tuning(host, opcode); +} + static struct mmci_host_ops sdmmc_variant_ops = { .validate_data = sdmmc_idma_validate_data, .prep_data = sdmmc_idma_prep_data, @@ -338,5 +471,21 @@ static struct mmci_host_ops sdmmc_variant_ops = { void sdmmc_variant_init(struct mmci_host *host) { + struct device_node *np = host->mmc->parent->of_node; + void __iomem *base_dlyb; + struct sdmmc_dlyb *dlyb; + host->ops = &sdmmc_variant_ops; + + base_dlyb = devm_of_iomap(mmc_dev(host->mmc), np, 1, NULL); + if (IS_ERR(base_dlyb)) + return; + + dlyb = devm_kzalloc(mmc_dev(host->mmc), sizeof(*dlyb), GFP_KERNEL); + if (!dlyb) + return; + + dlyb->base = base_dlyb; + host->variant_priv = dlyb; + host->mmc_ops->execute_tuning = sdmmc_execute_tuning; } -- cgit v1.2.3 From 7577316528816e8e9a49c1b4b5822bacbc62d113 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Tue, 28 Jan 2020 10:06:34 +0100 Subject: mmc: mmci: Add callbacks for to manage signal voltage switch A variant may need to define some actions before and after a voltage switch. This patch adds 2 callbacks to manage signal voltage switch in the struct mmci_host_ops. ->pre_sig_volt_switch() allows to prepare a signal voltage switch before sending the SD_SWITCH_VOLTAGE command (CMD11). ->post_sig_volt_switch callback allows specific actions to be executed, after the I/O signal voltage level has been changed. Signed-off-by: Ludovic Barre Link: https://lore.kernel.org/r/20200128090636.13689-8-ludovic.barre@st.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 8 ++++++++ drivers/mmc/host/mmci.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index d0a041c9e6cd..24e630183ed4 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1217,6 +1218,9 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) writel_relaxed(clks, host->base + MMCIDATATIMER); } + if (host->ops->pre_sig_volt_switch && cmd->opcode == SD_SWITCH_VOLTAGE) + host->ops->pre_sig_volt_switch(host); + if (/*interrupt*/0) c |= MCI_CPSM_INTERRUPT; @@ -1830,6 +1834,7 @@ static int mmci_get_cd(struct mmc_host *mmc) static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) { + struct mmci_host *host = mmc_priv(mmc); int ret = 0; if (!IS_ERR(mmc->supply.vqmmc)) { @@ -1849,6 +1854,9 @@ static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) break; } + if (!ret && host->ops && host->ops->post_sig_volt_switch) + ret = host->ops->post_sig_volt_switch(host, ios); + if (ret) dev_warn(mmc_dev(mmc), "Voltage switch failed\n"); } diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index ddcdfb827996..c3bc0a38d4cb 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -377,6 +377,8 @@ struct mmci_host_ops { void (*set_clkreg)(struct mmci_host *host, unsigned int desired); void (*set_pwrreg)(struct mmci_host *host, unsigned int pwr); bool (*busy_complete)(struct mmci_host *host, u32 status, u32 err_msk); + void (*pre_sig_volt_switch)(struct mmci_host *host); + int (*post_sig_volt_switch)(struct mmci_host *host, struct mmc_ios *ios); }; struct mmci_host { -- cgit v1.2.3 From 94b94a93e3554f291316263a91ada93390befa83 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Tue, 28 Jan 2020 10:06:35 +0100 Subject: mmc: mmci_sdmmc: Implement signal voltage callbacks To prepare the voltage switch procedure, the VSWITCHEN bit must be set before sending the CMD11. To confirm completion of voltage switch, the VSWEND flag must be checked. Signed-off-by: Ludovic Barre Link: https://lore.kernel.org/r/20200128090636.13689-9-ludovic.barre@st.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.h | 4 ++++ drivers/mmc/host/mmci_stm32_sdmmc.c | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index c3bc0a38d4cb..e1a9b96a3396 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -165,6 +165,7 @@ /* Extended status bits for the STM32 variants */ #define MCI_STM32_BUSYD0 BIT(20) #define MCI_STM32_BUSYD0END BIT(21) +#define MCI_STM32_VSWEND BIT(25) #define MMCICLEAR 0x038 #define MCI_CMDCRCFAILCLR (1 << 0) @@ -182,6 +183,9 @@ #define MCI_ST_SDIOITC (1 << 22) #define MCI_ST_CEATAENDC (1 << 23) #define MCI_ST_BUSYENDC (1 << 24) +/* Extended clear bits for the STM32 variants */ +#define MCI_STM32_VSWENDC BIT(25) +#define MCI_STM32_CKSTOPC BIT(26) #define MMCIMASK0 0x03c #define MCI_CMDCRCFAILMASK (1 << 0) diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c index fa875febcc85..f76e82f8f12f 100644 --- a/drivers/mmc/host/mmci_stm32_sdmmc.c +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c @@ -32,6 +32,7 @@ #define DLYB_CFGR_UNIT_MAX 127 #define DLYB_LNG_TIMEOUT_US 1000 +#define SDMMC_VSWEND_TIMEOUT_US 10000 struct sdmmc_lli_desc { u32 idmalar; @@ -265,6 +266,7 @@ static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr) struct mmc_ios ios = host->mmc->ios; struct sdmmc_dlyb *dlyb = host->variant_priv; + /* adds OF options */ pwr = host->pwr_reg_add; sdmmc_dlyb_input_ck(dlyb); @@ -291,6 +293,10 @@ static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr) writel(MCI_IRQENABLE | host->variant->start_err, host->base + MMCIMASK0); + /* preserves voltage switch bits */ + pwr |= host->pwr_reg & (MCI_STM32_VSWITCHEN | + MCI_STM32_VSWITCH); + /* * After a power-cycle state, we must set the SDMMC in * Power-off. The SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK are @@ -456,6 +462,41 @@ static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) return sdmmc_dlyb_phase_tuning(host, opcode); } +static void sdmmc_pre_sig_volt_vswitch(struct mmci_host *host) +{ + /* clear the voltage switch completion flag */ + writel_relaxed(MCI_STM32_VSWENDC, host->base + MMCICLEAR); + /* enable Voltage switch procedure */ + mmci_write_pwrreg(host, host->pwr_reg | MCI_STM32_VSWITCHEN); +} + +static int sdmmc_post_sig_volt_switch(struct mmci_host *host, + struct mmc_ios *ios) +{ + unsigned long flags; + u32 status; + int ret = 0; + + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + spin_lock_irqsave(&host->lock, flags); + mmci_write_pwrreg(host, host->pwr_reg | MCI_STM32_VSWITCH); + spin_unlock_irqrestore(&host->lock, flags); + + /* wait voltage switch completion while 10ms */ + ret = readl_relaxed_poll_timeout(host->base + MMCISTATUS, + status, + (status & MCI_STM32_VSWEND), + 10, SDMMC_VSWEND_TIMEOUT_US); + + writel_relaxed(MCI_STM32_VSWENDC | MCI_STM32_CKSTOPC, + host->base + MMCICLEAR); + mmci_write_pwrreg(host, host->pwr_reg & + ~(MCI_STM32_VSWITCHEN | MCI_STM32_VSWITCH)); + } + + return ret; +} + static struct mmci_host_ops sdmmc_variant_ops = { .validate_data = sdmmc_idma_validate_data, .prep_data = sdmmc_idma_prep_data, @@ -467,6 +508,8 @@ static struct mmci_host_ops sdmmc_variant_ops = { .set_clkreg = mmci_sdmmc_set_clkreg, .set_pwrreg = mmci_sdmmc_set_pwrreg, .busy_complete = sdmmc_busy_complete, + .pre_sig_volt_switch = sdmmc_pre_sig_volt_vswitch, + .post_sig_volt_switch = sdmmc_post_sig_volt_switch, }; void sdmmc_variant_init(struct mmci_host *host) -- cgit v1.2.3 From 7a2a98be672b7f6af3a33525ee1e5104381316a9 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Tue, 28 Jan 2020 10:06:36 +0100 Subject: mmc: mmci: Add support for sdmmc variant revision 2.0 This patch adds a sdmmc variant revision 2.0. This revision is backward compatible with 1.1, but adds DMA linked list support. Signed-off-by: Ludovic Barre Link: https://lore.kernel.org/r/20200128090636.13689-10-ludovic.barre@st.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 24e630183ed4..647567def612 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -275,6 +275,32 @@ static struct variant_data variant_stm32_sdmmc = { .init = sdmmc_variant_init, }; +static struct variant_data variant_stm32_sdmmcv2 = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .f_max = 208000000, + .stm32_clkdiv = true, + .cmdreg_cpsm_enable = MCI_CPSM_STM32_ENABLE, + .cmdreg_lrsp_crc = MCI_CPSM_STM32_LRSP_CRC, + .cmdreg_srsp_crc = MCI_CPSM_STM32_SRSP_CRC, + .cmdreg_srsp = MCI_CPSM_STM32_SRSP, + .cmdreg_stop = MCI_CPSM_STM32_CMDSTOP, + .data_cmd_enable = MCI_CPSM_STM32_CMDTRANS, + .irq_pio_mask = MCI_IRQ_PIO_STM32_MASK, + .datactrl_first = true, + .datacnt_useless = true, + .datalength_bits = 25, + .datactrl_blocksz = 14, + .datactrl_any_blocksz = true, + .stm32_idmabsize_mask = GENMASK(16, 5), + .dma_lli = true, + .busy_timeout = true, + .busy_detect = true, + .busy_detect_flag = MCI_STM32_BUSYD0, + .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, + .init = sdmmc_variant_init, +}; + static struct variant_data variant_qcom = { .fifosize = 16 * 4, .fifohalfsize = 8 * 4, @@ -2343,6 +2369,11 @@ static const struct amba_id mmci_ids[] = { .mask = 0xf0ffffff, .data = &variant_stm32_sdmmc, }, + { + .id = 0x00253180, + .mask = 0xf0ffffff, + .data = &variant_stm32_sdmmcv2, + }, /* Qualcomm variants */ { .id = 0x00051180, -- cgit v1.2.3 From d1709abb8cc39640e9647b3c0df4451d71bf2841 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Thu, 20 Feb 2020 09:30:01 +0800 Subject: mmc: host: hsq: Add missing MODULE_LICENSE() and MODULE_DESCRIPTION() Add missing MODULE_LICENSE() and MODULE_DESCRIPTION() in hsq driver to fix below warning when compiling the hsq as a module. "WARNING: modpost: missing MODULE_LICENSE() in drivers/mmc/host/mmc_hsq.o". Reported-by: Stephen Rothwell Signed-off-by: Baolin Wang Link: https://lore.kernel.org/r/98ce471185f037fce57520763621590588766381.1582161803.git.baolin.wang7@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmc_hsq.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmc_hsq.c b/drivers/mmc/host/mmc_hsq.c index fc82593b9e27..b90b2c97b6cf 100644 --- a/drivers/mmc/host/mmc_hsq.c +++ b/drivers/mmc/host/mmc_hsq.c @@ -9,6 +9,7 @@ #include #include +#include #include "mmc_hsq.h" @@ -342,3 +343,6 @@ int mmc_hsq_resume(struct mmc_host *mmc) return mmc_hsq_enable(mmc, NULL); } EXPORT_SYMBOL_GPL(mmc_hsq_resume); + +MODULE_DESCRIPTION("MMC Host Software Queue support"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 8ee5fc0e0b3bed087b38f46f4c272b64c0b33d47 Mon Sep 17 00:00:00 2001 From: Faiz Abbas Date: Wed, 8 Jan 2020 20:39:19 +0530 Subject: mmc: sdhci_am654: Update OTAPDLY writes According to the latest AM65x Data Manual[1], a different output tap delay value is optimum for a given speed mode. Therefore, deprecate the ti,otap-del-sel binding and introduce a new binding for each of the possible MMC/SD speed modes. If the legacy mode is not found, fall back to old binding to maintain dts compatibility. [1] http://www.ti.com/lit/gpn/am6526 Signed-off-by: Faiz Abbas Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200108150920.14547-3-faiz_abbas@ti.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 123 +++++++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 18 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 3afea580fbea..f8a6aa98a795 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -81,7 +81,8 @@ static struct regmap_config sdhci_am654_regmap_config = { struct sdhci_am654_data { struct regmap *base; - int otap_del_sel; + bool legacy_otapdly; + int otap_del_sel[11]; int trm_icp; int drv_strength; bool dll_on; @@ -98,11 +99,34 @@ struct sdhci_am654_driver_data { #define DLL_PRESENT (1 << 3) }; +struct timing_data { + const char *binding; + u32 capability; +}; + +static const struct timing_data td[] = { + [MMC_TIMING_LEGACY] = {"ti,otap-del-sel-legacy", 0}, + [MMC_TIMING_MMC_HS] = {"ti,otap-del-sel-mmc-hs", MMC_CAP_MMC_HIGHSPEED}, + [MMC_TIMING_SD_HS] = {"ti,otap-del-sel-sd-hs", MMC_CAP_SD_HIGHSPEED}, + [MMC_TIMING_UHS_SDR12] = {"ti,otap-del-sel-sdr12", MMC_CAP_UHS_SDR12}, + [MMC_TIMING_UHS_SDR25] = {"ti,otap-del-sel-sdr25", MMC_CAP_UHS_SDR25}, + [MMC_TIMING_UHS_SDR50] = {"ti,otap-del-sel-sdr50", MMC_CAP_UHS_SDR50}, + [MMC_TIMING_UHS_SDR104] = {"ti,otap-del-sel-sdr104", + MMC_CAP_UHS_SDR104}, + [MMC_TIMING_UHS_DDR50] = {"ti,otap-del-sel-ddr50", MMC_CAP_UHS_DDR50}, + [MMC_TIMING_MMC_DDR52] = {"ti,otap-del-sel-ddr52", MMC_CAP_DDR}, + [MMC_TIMING_MMC_HS200] = {"ti,otap-del-sel-hs200", MMC_CAP2_HS200}, + [MMC_TIMING_MMC_HS400] = {"ti,otap-del-sel-hs400", MMC_CAP2_HS400}, +}; + static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); + unsigned char timing = host->mmc->ios.timing; int sel50, sel100, freqsel; + u32 otap_del_sel; + u32 otap_del_ena; u32 mask, val; int ret; @@ -116,22 +140,29 @@ static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock) if (clock > CLOCK_TOO_SLOW_HZ) { /* Setup DLL Output TAP delay */ + if (sdhci_am654->legacy_otapdly) + otap_del_sel = sdhci_am654->otap_del_sel[0]; + else + otap_del_sel = sdhci_am654->otap_del_sel[timing]; + + otap_del_ena = (timing > MMC_TIMING_UHS_SDR25) ? 1 : 0; + mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; - val = (1 << OTAPDLYENA_SHIFT) | - (sdhci_am654->otap_del_sel << OTAPDLYSEL_SHIFT); - regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val); + val = (otap_del_ena << OTAPDLYENA_SHIFT) | + (otap_del_sel << OTAPDLYSEL_SHIFT); + /* Write to STRBSEL for HS400 speed mode */ - if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { + if (timing == MMC_TIMING_MMC_HS400) { if (sdhci_am654->flags & STRBSEL_4_BIT) - mask = STRBSEL_4BIT_MASK; + mask |= STRBSEL_4BIT_MASK; else - mask = STRBSEL_8BIT_MASK; + mask |= STRBSEL_8BIT_MASK; - regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, - sdhci_am654->strb_sel << - STRBSEL_SHIFT); + val |= sdhci_am654->strb_sel << STRBSEL_SHIFT; } + regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val); + if (sdhci_am654->flags & FREQSEL_2_BIT) { switch (clock) { case 200000000: @@ -198,11 +229,19 @@ static void sdhci_j721e_4bit_set_clock(struct sdhci_host *host, { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); - int val, mask; + unsigned char timing = host->mmc->ios.timing; + u32 otap_del_sel; + u32 mask, val; + + /* Setup DLL Output TAP delay */ + if (sdhci_am654->legacy_otapdly) + otap_del_sel = sdhci_am654->otap_del_sel[0]; + else + otap_del_sel = sdhci_am654->otap_del_sel[timing]; mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK; - val = (1 << OTAPDLYENA_SHIFT) | - (sdhci_am654->otap_del_sel << OTAPDLYSEL_SHIFT); + val = (0x1 << OTAPDLYENA_SHIFT) | + (otap_del_sel << OTAPDLYSEL_SHIFT); regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val); sdhci_set_clock(host, clock); @@ -371,6 +410,55 @@ static int sdhci_am654_cqe_add_host(struct sdhci_host *host) return ret; } +static int sdhci_am654_get_otap_delay(struct sdhci_host *host, + struct sdhci_am654_data *sdhci_am654) +{ + struct device *dev = mmc_dev(host->mmc); + int i; + int ret; + + ret = device_property_read_u32(dev, td[MMC_TIMING_LEGACY].binding, + &sdhci_am654->otap_del_sel[MMC_TIMING_LEGACY]); + if (ret) { + /* + * ti,otap-del-sel-legacy is mandatory, look for old binding + * if not found. + */ + ret = device_property_read_u32(dev, "ti,otap-del-sel", + &sdhci_am654->otap_del_sel[0]); + if (ret) { + dev_err(dev, "Couldn't find otap-del-sel\n"); + + return ret; + } + + dev_info(dev, "Using legacy binding ti,otap-del-sel\n"); + sdhci_am654->legacy_otapdly = true; + + return 0; + } + + for (i = MMC_TIMING_MMC_HS; i <= MMC_TIMING_MMC_HS400; i++) { + + ret = device_property_read_u32(dev, td[i].binding, + &sdhci_am654->otap_del_sel[i]); + if (ret) { + dev_dbg(dev, "Couldn't find %s\n", + td[i].binding); + /* + * Remove the corresponding capability + * if an otap-del-sel value is not found + */ + if (i <= MMC_TIMING_MMC_DDR52) + host->mmc->caps &= ~td[i].capability; + else + host->mmc->caps2 &= ~td[i].capability; + } + } + + return 0; +} + static int sdhci_am654_init(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -419,6 +507,10 @@ static int sdhci_am654_init(struct sdhci_host *host) if (ret) goto err_cleanup_host; + ret = sdhci_am654_get_otap_delay(host, sdhci_am654); + if (ret) + goto err_cleanup_host; + ret = __sdhci_add_host(host); if (ret) goto err_cleanup_host; @@ -437,11 +529,6 @@ static int sdhci_am654_get_of_property(struct platform_device *pdev, int drv_strength; int ret; - ret = device_property_read_u32(dev, "ti,otap-del-sel", - &sdhci_am654->otap_del_sel); - if (ret) - return ret; - if (sdhci_am654->flags & DLL_PRESENT) { ret = device_property_read_u32(dev, "ti,trm-icp", &sdhci_am654->trm_icp); -- cgit v1.2.3 From a161c45f2979566c1dafed7fe9807b01f5b0cf73 Mon Sep 17 00:00:00 2001 From: Faiz Abbas Date: Wed, 8 Jan 2020 20:39:20 +0530 Subject: mmc: sdhci_am654: Enable DLL only for some speed modes Its recommended that DLL must only be enabled for SDR50, DDR50, DDR52, SDR104, HS200 and HS400 speed modes. Move DLL configuration to its own function and call it only in the above speed modes. Signed-off-by: Faiz Abbas Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200108150920.14547-4-faiz_abbas@ti.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 128 ++++++++++++++++++++++------------------- 1 file changed, 68 insertions(+), 60 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index f8a6aa98a795..58183b5f4e82 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -119,16 +119,80 @@ static const struct timing_data td[] = { [MMC_TIMING_MMC_HS400] = {"ti,otap-del-sel-hs400", MMC_CAP2_HS400}, }; +static void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); + int sel50, sel100, freqsel; + u32 mask, val; + int ret; + + if (sdhci_am654->flags & FREQSEL_2_BIT) { + switch (clock) { + case 200000000: + sel50 = 0; + sel100 = 0; + break; + case 100000000: + sel50 = 0; + sel100 = 1; + break; + default: + sel50 = 1; + sel100 = 0; + } + + /* Configure PHY DLL frequency */ + mask = SEL50_MASK | SEL100_MASK; + val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT); + regmap_update_bits(sdhci_am654->base, PHY_CTRL5, mask, val); + + } else { + switch (clock) { + case 200000000: + freqsel = 0x0; + break; + default: + freqsel = 0x4; + } + + regmap_update_bits(sdhci_am654->base, PHY_CTRL5, FREQSEL_MASK, + freqsel << FREQSEL_SHIFT); + } + /* Configure DLL TRIM */ + mask = DLL_TRIM_ICP_MASK; + val = sdhci_am654->trm_icp << DLL_TRIM_ICP_SHIFT; + + /* Configure DLL driver strength */ + mask |= DR_TY_MASK; + val |= sdhci_am654->drv_strength << DR_TY_SHIFT; + regmap_update_bits(sdhci_am654->base, PHY_CTRL1, mask, val); + + /* Enable DLL */ + regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, + 0x1 << ENDLL_SHIFT); + /* + * Poll for DLL ready. Use a one second timeout. + * Works in all experiments done so far + */ + ret = regmap_read_poll_timeout(sdhci_am654->base, PHY_STAT1, val, + val & DLLRDY_MASK, 1000, 1000000); + if (ret) { + dev_err(mmc_dev(host->mmc), "DLL failed to relock\n"); + return; + } + + sdhci_am654->dll_on = true; +} + static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); unsigned char timing = host->mmc->ios.timing; - int sel50, sel100, freqsel; u32 otap_del_sel; u32 otap_del_ena; u32 mask, val; - int ret; if (sdhci_am654->dll_on) { regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0); @@ -163,64 +227,8 @@ static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock) regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val); - if (sdhci_am654->flags & FREQSEL_2_BIT) { - switch (clock) { - case 200000000: - sel50 = 0; - sel100 = 0; - break; - case 100000000: - sel50 = 0; - sel100 = 1; - break; - default: - sel50 = 1; - sel100 = 0; - } - - /* Configure PHY DLL frequency */ - mask = SEL50_MASK | SEL100_MASK; - val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT); - regmap_update_bits(sdhci_am654->base, PHY_CTRL5, mask, - val); - } else { - switch (clock) { - case 200000000: - freqsel = 0x0; - break; - default: - freqsel = 0x4; - } - - regmap_update_bits(sdhci_am654->base, PHY_CTRL5, - FREQSEL_MASK, - freqsel << FREQSEL_SHIFT); - } - - /* Configure DLL TRIM */ - mask = DLL_TRIM_ICP_MASK; - val = sdhci_am654->trm_icp << DLL_TRIM_ICP_SHIFT; - - /* Configure DLL driver strength */ - mask |= DR_TY_MASK; - val |= sdhci_am654->drv_strength << DR_TY_SHIFT; - regmap_update_bits(sdhci_am654->base, PHY_CTRL1, mask, val); - /* Enable DLL */ - regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, - 0x1 << ENDLL_SHIFT); - /* - * Poll for DLL ready. Use a one second timeout. - * Works in all experiments done so far - */ - ret = regmap_read_poll_timeout(sdhci_am654->base, PHY_STAT1, - val, val & DLLRDY_MASK, 1000, - 1000000); - if (ret) { - dev_err(mmc_dev(host->mmc), "DLL failed to relock\n"); - return; - } - - sdhci_am654->dll_on = true; + if (timing > MMC_TIMING_UHS_SDR25) + sdhci_am654_setup_dll(host, clock); } } -- cgit v1.2.3 From e65bb38824711559844ba932132f417bc5a355e2 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:22:40 +0800 Subject: mmc: sdhci: do not enable card detect interrupt for gpio cd type Except SDHCI_QUIRK_BROKEN_CARD_DETECTION and MMC_CAP_NONREMOVABLE, we also do not need to handle controller native card detect interrupt for gpio cd type. If we wrong enabled the card detect interrupt for gpio case, it will cause a lot of unexpected card detect interrupts during data transfer which should not happen. Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/1582100563-20555-2-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9c3745118e49..c59566363a42 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -153,7 +153,7 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) u32 present; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || - !mmc_card_is_removable(host->mmc)) + !mmc_card_is_removable(host->mmc) || mmc_can_gpio_cd(host->mmc)) return; if (enable) { -- cgit v1.2.3 From b62eee9f804ed1aa6a80b0fe4968c1393cd6b21f Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:22:41 +0800 Subject: mmc: sdhci-esdhc-imx: no fail when no pinctrl available When using jailhouse to support two Linux on i.MX8MQ EVK, we use the 1st Linux to configure pinctrl for the 2nd Linux. Then the 2nd Linux could use the mmc without pinctrl driver. So give a warning message when no pinctrl available, but no fail probe. Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Acked-by: Linus Walleij Link: https://lore.kernel.org/r/1582100563-20555-3-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 382f25b2fa45..8c802681ad2c 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1488,7 +1488,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) imx_data->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(imx_data->pinctrl)) { err = PTR_ERR(imx_data->pinctrl); - goto disable_ahb_clk; + dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n"); } if (esdhc_is_usdhc(imx_data)) { -- cgit v1.2.3 From a26a4f1baca55a05aecd8d7181802979a93e3d46 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:22:42 +0800 Subject: mmc: sdhci-esdhci-imx: retune needed for Mega/Mix enabled SoCs For Mega/Mix enabled SoCs like MX7D and MX6SX, uSDHC will lost power in LP mode no matter whether the MMC_KEEP_POWER flag is set or not. This may cause state misalign between kernel and HW, especially for SDIO3.0 WiFi cards. e.g. SDIO WiFi driver usually will keep power during system suspend. And after resume, no card re-enumeration called. But the tuning state is lost due to Mega/Mix. Then CRC error may happen during next data transfer. So we should always fire a mmc_retune_needed() for such type SoC to tell MMC core retuning is needed for next data transfer. mmc: sdhci-esdhci-imx: retune needed for Mega/Mix enabled SoCs Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582100563-20555-4-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 8c802681ad2c..9b5afa2514c6 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -160,6 +160,8 @@ #define ESDHC_FLAG_CQHCI BIT(12) /* need request pmqos during low power */ #define ESDHC_FLAG_PMQOS BIT(13) +/* The IP state got lost in low power mode */ +#define ESDHC_FLAG_STATE_LOST_IN_LPMODE BIT(14) struct esdhc_soc_data { u32 flags; @@ -193,32 +195,37 @@ static const struct esdhc_soc_data usdhc_imx6sl_data = { static const struct esdhc_soc_data usdhc_imx6sx_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING - | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200, + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; static const struct esdhc_soc_data usdhc_imx6ull_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 - | ESDHC_FLAG_ERR010450, + | ESDHC_FLAG_ERR010450 + | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; static const struct esdhc_soc_data usdhc_imx7d_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 - | ESDHC_FLAG_HS400, + | ESDHC_FLAG_HS400 + | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; static struct esdhc_soc_data usdhc_imx7ulp_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 - | ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400, + | ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400 + | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; static struct esdhc_soc_data usdhc_imx8qxp_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES - | ESDHC_FLAG_CQHCI, + | ESDHC_FLAG_CQHCI + | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; struct pltfm_imx_data { @@ -1606,6 +1613,8 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) static int sdhci_esdhc_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); int ret; if (host->mmc->caps2 & MMC_CAP2_CQE) { @@ -1614,6 +1623,12 @@ static int sdhci_esdhc_suspend(struct device *dev) return ret; } + if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) && + (host->tuning_mode != SDHCI_TUNING_MODE_1)) { + mmc_retune_timer_stop(host->mmc); + mmc_retune_needed(host->mmc); + } + if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); -- cgit v1.2.3 From 5c11f1ffb02244e8cafd320fb130090a1d1db342 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:22:43 +0800 Subject: mmc: sdhci-esdhc-imx: restore the per_clk rate in PM_RUNTIME When pm_runtime_suspend is run, a call to SCFW power off the SS (SS is a power domain, usdhc belong to this SS power domain) in which the resource resides is made. The SCFW can power off the SS if no other resource in active in that SS. If so, all state associated with all the resources within the SS that is powered off is lost, this includes the clock rates, clock state etc. When pm_runtime_resume is called, the SS associated with that resource is powered up. But the clocks are left in the default state. This patch restore clock rate in pm_runtime_resume, make sure the clock is right rather than depending on the default state setting by SCFW. Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582100563-20555-5-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 9b5afa2514c6..6bcdc5743d94 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -162,6 +162,8 @@ #define ESDHC_FLAG_PMQOS BIT(13) /* The IP state got lost in low power mode */ #define ESDHC_FLAG_STATE_LOST_IN_LPMODE BIT(14) +/* The IP lost clock rate in PM_RUNTIME */ +#define ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME BIT(15) struct esdhc_soc_data { u32 flags; @@ -225,7 +227,8 @@ static struct esdhc_soc_data usdhc_imx8qxp_data = { | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES | ESDHC_FLAG_CQHCI - | ESDHC_FLAG_STATE_LOST_IN_LPMODE, + | ESDHC_FLAG_STATE_LOST_IN_LPMODE + | ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME, }; struct pltfm_imx_data { @@ -1698,6 +1701,9 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) pm_qos_add_request(&imx_data->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0); + if (imx_data->socdata->flags & ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME) + clk_set_rate(imx_data->clk_per, pltfm_host->clock); + err = clk_prepare_enable(imx_data->clk_ahb); if (err) goto remove_pm_qos_request; -- cgit v1.2.3 From 5bd2acdcdde22231057b12ee8978dd87c29ff322 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:25:49 +0800 Subject: mmc: sdhci-esdhc-imx: add strobe-dll-delay-target support strobe-dll-delay-target is the delay cell add on the strobe line. Strobe line the the uSDHC loopback read clock which is use in HS400 mode. Different strobe-dll-delay-target may need to set for different board/SoC. If this delay cell is not set to an appropriate value, we may see some read operation meet CRC error after HS400 mode select which already pass the tuning. This patch add the strobe-dll-delay-target setting in driver, so that user can easily config this delay cell in dts file. Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582100757-20683-1-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 6bcdc5743d94..3359153304a3 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -73,6 +73,7 @@ #define ESDHC_STROBE_DLL_CTRL 0x70 #define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0) #define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1) +#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT 0x7 #define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3 #define ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT (4 << 20) @@ -993,6 +994,9 @@ static int esdhc_change_pinstate(struct sdhci_host *host, */ static void esdhc_set_strobe_dll(struct sdhci_host *host) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); + u32 strobe_delay; u32 v; /* disable clock before enabling strobe dll */ @@ -1010,9 +1014,13 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host) * enable strobe dll ctrl and adjust the delay target * for the uSDHC loopback read clock */ + if (imx_data->boarddata.strobe_dll_delay_target) + strobe_delay = imx_data->boarddata.strobe_dll_delay_target; + else + strobe_delay = ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT; v = ESDHC_STROBE_DLL_CTRL_ENABLE | ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT | - (7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); + (strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL); /* wait 5us to make sure strobe dll status register stable */ udelay(5); @@ -1338,6 +1346,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, of_property_read_u32(np, "fsl,tuning-start-tap", &boarddata->tuning_start_tap); + of_property_read_u32(np, "fsl,strobe-dll-delay-target", + &boarddata->strobe_dll_delay_target); if (of_find_property(np, "no-1-8-v", NULL)) host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; -- cgit v1.2.3 From f581e9093aa2820c315273fc0cbf7cb0294fd005 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:25:50 +0800 Subject: mmc: sdhci-esdhc-imx: optimize the clock setting When force clock off, check the SDOFF of register PRSSTAT to make sure the clock is gate off. Before force clock on, check the SDSTB of register PRSSTAT to make sure the clock is stable, this will eliminate the clock glitch. Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582100757-20683-2-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 24 +++++++++++++++++++++++- drivers/mmc/host/sdhci-esdhc.h | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 3359153304a3..6ca01b766604 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -312,6 +313,17 @@ static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, i writel(((readl(base) & ~(mask << shift)) | (val << shift)), base); } +static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host) +{ + u32 present_state; + int ret; + + ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, present_state, + (present_state & ESDHC_CLOCK_GATE_OFF), 2, 100); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), "%s: card clock still not gate off in 100us!.\n", __func__); +} + static u32 esdhc_readl_le(struct sdhci_host *host, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -525,6 +537,8 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) else new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON; writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); + if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON)) + esdhc_wait_for_card_clock_gate_off(host); return; case SDHCI_HOST_CONTROL2: new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); @@ -753,12 +767,14 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, int ddr_pre_div = imx_data->is_ddr ? 2 : 1; int pre_div = 1; int div = 1; + int ret; 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); + esdhc_wait_for_card_clock_gate_off(host); } if (clock == 0) { @@ -813,13 +829,18 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, | (pre_div << ESDHC_PREDIV_SHIFT)); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + /* need to wait the bit 3 of the PRSSTAT to be set, make sure card clock is stable */ + ret = readl_poll_timeout(host->ioaddr + ESDHC_PRSSTAT, temp, + (temp & ESDHC_CLOCK_STABLE), 2, 100); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), "card clock still not stable in 100us!.\n"); + 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); } - mdelay(1); } static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) @@ -1003,6 +1024,7 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host) writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, host->ioaddr + ESDHC_VENDOR_SPEC); + esdhc_wait_for_card_clock_gate_off(host); /* force a reset on strobe dll */ writel(ESDHC_STROBE_DLL_CTRL_RESET, diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index 9289bb4d633e..947212f16bc6 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -31,6 +31,7 @@ /* Present State Register */ #define ESDHC_PRSSTAT 0x24 +#define ESDHC_CLOCK_GATE_OFF 0x00000080 #define ESDHC_CLOCK_STABLE 0x00000008 /* Protocol Control Register */ -- cgit v1.2.3 From 373e800b53a86913c3d2e0c06515ee33e9170b77 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:25:51 +0800 Subject: mmc: sdhci-esdhc-imx: optimize the strobe dll setting After set the STROBE SLV delay target value, it need to wait some time to let the usdhc lock the REF and SLV clock. In normal case, 1~2us is enough for imx8/imx6 and imx7d, and 4~5us is enough for imx7ulp, but when do reboot stress test or do the bind/unbind stress test, sometimes need to wait about 10us to get the status lock. This patch optimize delay handle method, only print the warning message if the status is still not lock after 1ms delay. Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582100757-20683-3-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 6ca01b766604..1ec35cc45fc5 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1019,6 +1019,7 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host) struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); u32 strobe_delay; u32 v; + int ret; /* disable clock before enabling strobe dll */ writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) & @@ -1044,15 +1045,13 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host) ESDHC_STROBE_DLL_CTRL_SLV_UPDATE_INT_DEFAULT | (strobe_delay << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL); - /* wait 5us to make sure strobe dll status register stable */ - udelay(5); - v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS); - if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK)) - dev_warn(mmc_dev(host->mmc), - "warning! HS400 strobe DLL status REF not lock!\n"); - if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK)) + + /* wait max 50us to get the REF/SLV lock */ + ret = readl_poll_timeout(host->ioaddr + ESDHC_STROBE_DLL_STATUS, v, + ((v & ESDHC_STROBE_DLL_STS_REF_LOCK) && (v & ESDHC_STROBE_DLL_STS_SLV_LOCK)), 1, 50); + if (ret == -ETIMEDOUT) dev_warn(mmc_dev(host->mmc), - "warning! HS400 strobe DLL status SLV not lock!\n"); + "warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v); } static void esdhc_reset_tuning(struct sdhci_host *host) -- cgit v1.2.3 From 74898cbcbfc4becbae5bba063e2dbcc50bf6e00d Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:25:52 +0800 Subject: mmc: sdhci-esdhc-imx: add flag ESDHC_FLAG_BROKEN_AUTO_CMD23 Since L4.15, community involve the commit 105819c8a545 ("mmc: core: use mrq->sbc when sending CMD23 for RPMB"), let the usdhc to decide whether to use ACMD23 for RPMB. This CMD23 for RPMB need to set the bit 31 to its argument, if not, the RPMB write operation will return general fail. According to the sdhci logic, SDMA mode will disable the ACMD23, and only in ADMA mode, it will chose to use ACMD23 if the host support. But according to debug, and confirm with IC, the imx6qpdl/imx6sx/imx6sl/imx7d do not support the ACMD23 feature completely. These SoCs only use the 16 bit block count of the register 0x4 (BLOCK_ATT) as the CMD23's argument in ACMD23 mode, which means it will ignore the upper 16 bit of the CMD23's argument. This will block the reliable write operation in RPMB, because RPMB reliable write need to set the bit31 of the CMD23's argument. This is the hardware limitation. So for imx6qpdl/imx6sx/imx6sl/imx7d, it need to broke the ACMD23 for eMMC, SD card do not has this limitation, because SD card do not support reliable write. For imx6ul/imx6ull/imx6sll/imx7ulp/imx8, it support the ACMD23 completely, it change to use the 0x0 register (DS_ADDR) to put the CMD23's argument in ADMA mode. This patch add a new flag ESDHC_FLAG_BROKEN_AUTO_CMD23, and add this flag to imx6q/imx6sx/imx6sl/imx7d, and due to the imx6sll share the same compatible string with imx6sx before, and imx6sll do not has this limitation, so add a new compatible string for imx6sll. Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582100757-20683-4-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 1ec35cc45fc5..98b91b6ff2a9 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -166,6 +166,18 @@ #define ESDHC_FLAG_STATE_LOST_IN_LPMODE BIT(14) /* The IP lost clock rate in PM_RUNTIME */ #define ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME BIT(15) +/* + * The IP do not support the ACMD23 feature completely when use ADMA mode. + * In ADMA mode, it only use the 16 bit block count of the register 0x4 + * (BLOCK_ATT) as the CMD23's argument for ACMD23 mode, which means it will + * ignore the upper 16 bit of the CMD23's argument. This will block the reliable + * write operation in RPMB, because RPMB reliable write need to set the bit31 + * of the CMD23's argument. + * imx6qpdl/imx6sx/imx6sl/imx7d has this limitation only for ADMA mode, SDMA + * do not has this limitation. so when these SoC use ADMA mode, it need to + * disable the ACMD23 feature. + */ +#define ESDHC_FLAG_BROKEN_AUTO_CMD23 BIT(16) struct esdhc_soc_data { u32 flags; @@ -188,21 +200,30 @@ static const struct esdhc_soc_data esdhc_imx53_data = { }; static const struct esdhc_soc_data usdhc_imx6q_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING, + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING + | ESDHC_FLAG_BROKEN_AUTO_CMD23, }; static const struct esdhc_soc_data usdhc_imx6sl_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536 - | ESDHC_FLAG_HS200, + | ESDHC_FLAG_HS200 + | ESDHC_FLAG_BROKEN_AUTO_CMD23, }; -static const struct esdhc_soc_data usdhc_imx6sx_data = { +static const struct esdhc_soc_data usdhc_imx6sll_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; +static const struct esdhc_soc_data usdhc_imx6sx_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_STATE_LOST_IN_LPMODE + | ESDHC_FLAG_BROKEN_AUTO_CMD23, +}; + static const struct esdhc_soc_data usdhc_imx6ull_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 @@ -214,7 +235,8 @@ static const struct esdhc_soc_data usdhc_imx7d_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_HS400 - | ESDHC_FLAG_STATE_LOST_IN_LPMODE, + | ESDHC_FLAG_STATE_LOST_IN_LPMODE + | ESDHC_FLAG_BROKEN_AUTO_CMD23, }; static struct esdhc_soc_data usdhc_imx7ulp_data = { @@ -276,6 +298,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, }, { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, }, { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, }, + { .compatible = "fsl,imx6sll-usdhc", .data = &usdhc_imx6sll_data, }, { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, }, { .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, }, { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, }, @@ -1560,6 +1583,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (imx_data->socdata->flags & ESDHC_FLAG_HS400) host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400; + if (imx_data->socdata->flags & ESDHC_FLAG_BROKEN_AUTO_CMD23) + host->quirks2 |= SDHCI_QUIRK2_ACMD23_BROKEN; + if (imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) { host->mmc->caps2 |= MMC_CAP2_HS400_ES; host->mmc_host_ops.hs400_enhanced_strobe = -- cgit v1.2.3 From cde5e8e9ff1461bcc76a090f762cc38b289b825c Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:25:53 +0800 Subject: mmc: sdhci-esdhc-imx: Add an new esdhc_soc_data for i.MX8MM Add new esdhc_soc_data, with compatible string "fsl,imx8mm-usdhc". Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582100757-20683-5-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 98b91b6ff2a9..100c081cfcae 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -255,6 +255,14 @@ static struct esdhc_soc_data usdhc_imx8qxp_data = { | ESDHC_FLAG_CLK_RATE_LOST_IN_PM_RUNTIME, }; +static struct esdhc_soc_data usdhc_imx8mm_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES + | ESDHC_FLAG_CQHCI + | ESDHC_FLAG_STATE_LOST_IN_LPMODE, +}; + struct pltfm_imx_data { u32 scratchpad; struct pinctrl *pinctrl; @@ -304,6 +312,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, }, { .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, }, { .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, }, + { .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); -- cgit v1.2.3 From 982cf37da3ee0f1e3e20d97e19f13cba79be51c7 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:25:54 +0800 Subject: mmc: sdhci-esdhc-imx: clear pending interrupt and halt cqhci On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let the the 1st linux configure power/clock for the 2nd Linux. When the 2nd Linux is booting into rootfs stage, we let the 1st Linux to destroy the 2nd linux, then restart the 2nd linux, we met SDHCI dump as following, after we clear the pending interrupt and halt CQCTL, issue gone. [ 1.334594] mmc2: Got command interrupt 0x00000001 even though no command operation was in progress. [ 1.334595] mmc2: sdhci: ============ SDHCI REGISTER DUMP =========== [ 1.334599] mmc2: sdhci: Sys addr: 0xa05dcc00 | Version: 0x00000002 [ 1.345538] mmc2: sdhci: Blk size: 0x00000200 | Blk cnt: 0x00000000 [ 1.345541] mmc2: sdhci: Argument: 0x00018000 | Trn mode: 0x00000033 [ 1.345543] mmc2: sdhci: Present: 0x01f88008 | Host ctl: 0x00000031 [ 1.345547] mmc2: sdhci: Power: 0x00000002 | Blk gap: 0x00000080 [ 1.357903] mmc2: sdhci: Wake-up: 0x00000008 | Clock: 0x0000003f [ 1.357905] mmc2: sdhci: Timeout: 0x0000008f | Int stat: 0x00000000 [ 1.357908] mmc2: sdhci: Int enab: 0x107f100b | Sig enab: 0x107f100b [ 1.357911] mmc2: sdhci: AC12 err: 0x00000000 | Slot int: 0x00000502 [ 1.370268] mmc2: sdhci: Caps: 0x07eb0000 | Caps_1: 0x0000b400 [ 1.370270] mmc2: sdhci: Cmd: 0x00000d1a | Max curr: 0x00ffffff [ 1.370273] mmc2: sdhci: Resp[0]: 0x00000b00 | Resp[1]: 0xffffffff [ 1.370276] mmc2: sdhci: Resp[2]: 0x328f5903 | Resp[3]: 0x00d00f00 [ 1.382132] mmc2: sdhci: Host ctl2: 0x00000000 [ 1.382135] mmc2: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0xa2040208 [ 2.060932] mmc2: Unexpected interrupt 0x00004000. [ 2.065538] mmc2: sdhci: ============ SDHCI REGISTER DUMP =========== [ 2.071720] mmc2: sdhci: Sys addr: 0x00000000 | Version: 0x00000002 [ 2.077902] mmc2: sdhci: Blk size: 0x00000200 | Blk cnt: 0x00000001 [ 2.084083] mmc2: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000 [ 2.090264] mmc2: sdhci: Present: 0x01f88009 | Host ctl: 0x00000011 [ 2.096446] mmc2: sdhci: Power: 0x00000002 | Blk gap: 0x00000080 [ 2.102627] mmc2: sdhci: Wake-up: 0x00000008 | Clock: 0x000010ff [ 2.108809] mmc2: sdhci: Timeout: 0x0000008f | Int stat: 0x00004000 [ 2.114990] mmc2: sdhci: Int enab: 0x007f1003 | Sig enab: 0x007f1003 [ 2.121171] mmc2: sdhci: AC12 err: 0x00000000 | Slot int: 0x00000502 [ 2.127353] mmc2: sdhci: Caps: 0x07eb0000 | Caps_1: 0x0000b400 [ 2.133534] mmc2: sdhci: Cmd: 0x0000371a | Max curr: 0x00ffffff [ 2.139715] mmc2: sdhci: Resp[0]: 0x00000900 | Resp[1]: 0xffffffff [ 2.145896] mmc2: sdhci: Resp[2]: 0x328f5903 | Resp[3]: 0x00d00f00 [ 2.152077] mmc2: sdhci: Host ctl2: 0x00000000 [ 2.156342] mmc2: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x00000000 Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582100757-20683-6-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 100c081cfcae..106097cbd0d4 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1233,6 +1233,7 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); + struct cqhci_host *cq_host = host->mmc->cqe_private; int tmp; if (esdhc_is_usdhc(imx_data)) { @@ -1309,6 +1310,21 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) tmp &= ~ESDHC_STD_TUNING_EN; writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL); } + + /* + * On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card + * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let the + * the 1st linux configure power/clock for the 2nd Linux. + * + * When the 2nd Linux is booting into rootfs stage, we let the 1st Linux + * to destroy the 2nd linux, then restart the 2nd linux, we met SDHCI dump. + * After we clear the pending interrupt and halt CQCTL, issue gone. + */ + if (cq_host) { + tmp = cqhci_readl(cq_host, CQHCI_IS); + cqhci_writel(cq_host, tmp, CQHCI_IS); + cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL); + } } } -- cgit v1.2.3 From e534b82f24b3b83f65a8f59283624f24992235e5 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:25:55 +0800 Subject: mmc: sdhci-esdhc-imx: clear DMA_SEL when disable DMA mode Currently, when use standard tuning, driver default disable DMA just before send tuning command. But on i.MX8 usdhc, this is not enough. Need also clear DMA_SEL. If not, once the DMA_SEL select AMDA2 before, even dma already disabled, when send tuning command, usdhc will still prefetch the ADMA script from wrong DMA address, then we will see IOMMU report some error which show lack of TLB mapping. Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582100757-20683-7-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 106097cbd0d4..786305309eb0 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -639,10 +639,24 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) * For DMA access restore the levels to default value. */ m = readl(host->ioaddr + ESDHC_WTMK_LVL); - if (val & SDHCI_TRNS_DMA) + if (val & SDHCI_TRNS_DMA) { wml = ESDHC_WTMK_LVL_WML_VAL_DEF; - else + } else { + u8 ctrl; wml = ESDHC_WTMK_LVL_WML_VAL_MAX; + + /* + * Since already disable DMA mode, so also need + * to clear the DMASEL. Otherwise, for standard + * tuning, when send tuning command, usdhc will + * still prefetch the ADMA script from wrong + * DMA address, then we will see IOMMU report + * some error which show lack of TLB mapping. + */ + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_DMA_MASK; + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + } m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK | ESDHC_WTMK_LVL_WR_WML_MASK); m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) | -- cgit v1.2.3 From af8fade4bd7bc7bf49851832a20166213e032978 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Wed, 19 Feb 2020 16:25:56 +0800 Subject: mmc: sdhci-esdhc-imx: restore pin state when resume back In some low power mode, SoC will lose the pin state, so need to restore the pin state when resume back. Signed-off-by: Haibo Chen Link: https://lore.kernel.org/r/1582100757-20683-8-git-send-email-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 786305309eb0..b38b9d7f0a0d 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1731,7 +1731,11 @@ static int sdhci_esdhc_suspend(struct device *dev) if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); - return sdhci_suspend_host(host); + ret = sdhci_suspend_host(host); + if (!ret) + return pinctrl_pm_select_sleep_state(dev); + + return ret; } static int sdhci_esdhc_resume(struct device *dev) @@ -1739,6 +1743,10 @@ static int sdhci_esdhc_resume(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); int ret; + ret = pinctrl_pm_select_default_state(dev); + if (ret) + return ret; + /* re-initialize hw state in case it's lost in low power mode */ sdhci_esdhc_imx_hwinit(host); -- cgit v1.2.3 From 6660d0ae1255ab2142dc3c6c790f3f309f0676bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Pouiller?= Date: Fri, 21 Feb 2020 17:31:47 +0100 Subject: mmc: core: Fix indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sdio_single_irq_set() was indented with a mix of tabs and spaces. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221163147.608677-1-Jerome.Pouiller@silabs.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio_irq.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 900871073bd7..3ffe4ff49aa7 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -276,14 +276,15 @@ static void sdio_single_irq_set(struct mmc_card *card) card->sdio_single_irq = NULL; if ((card->host->caps & MMC_CAP_SDIO_IRQ) && - card->host->sdio_irqs == 1) + card->host->sdio_irqs == 1) { for (i = 0; i < card->sdio_funcs; i++) { - func = card->sdio_func[i]; - if (func && func->irq_handler) { - card->sdio_single_irq = func; - break; - } - } + func = card->sdio_func[i]; + if (func && func->irq_handler) { + card->sdio_single_irq = func; + break; + } + } + } } /** -- cgit v1.2.3 From 55fc7d93a55b70c016982f9d9cba02ac789d88c6 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 24 Feb 2020 17:08:49 +0800 Subject: mmc: sdhci-sprd: Set the missing MMC_CAP_WAIT_WHILE_BUSY flag The Spreadtrum host controller supports HW busy detection for commands with R1B responses, but also for I/O operations. This means when the host gets a transfer complete event, that always indicates the busy signal is released. Let's inform the mmc core about this, via setting the corresponding MMC_CAP_WAIT_WHILE_BUSY flag, as to remove some redundant software busy polling. Signed-off-by: Baolin Wang Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/96f16647f6a6e8cb058c44e46c61b122df027059.1582535202.git.baolin.wang7@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-sprd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index d3462237bca2..2ab42c59e4f8 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -556,7 +556,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev) sdhci_sprd_voltage_switch; host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | - MMC_CAP_ERASE | MMC_CAP_CMD23; + MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY; ret = mmc_of_parse(host->mmc); if (ret) goto pltfm_free; -- cgit v1.2.3 From ea21e9b2b33fd06c131415cc66ca5499ab2aef5f Mon Sep 17 00:00:00 2001 From: Veerabhadrarao Badiganti Date: Wed, 26 Feb 2020 16:27:43 +0530 Subject: mmc: mmc_test: Pass different sg lists for non-blocking requests Supply a separate sg list for each of the request in non-blocking IO test cases where two requests will be issued at same time. Otherwise, sg memory may get unmapped when a request is done while same memory is being accessed by controller from the other request, and it leads to iommu errors with below call stack: __arm_lpae_unmap+0x2e0/0x478 arm_lpae_unmap+0x54/0x70 arm_smmu_unmap+0x64/0xa4 __iommu_unmap+0xb8/0x1f0 iommu_unmap_fast+0x38/0x48 __iommu_dma_unmap+0x88/0x108 iommu_dma_unmap_sg+0x90/0xa4 sdhci_post_req+0x5c/0x78 mmc_test_start_areq+0x10c/0x120 [mmc_test] mmc_test_area_io_seq+0x150/0x264 [mmc_test] mmc_test_rw_multiple+0x174/0x1c0 [mmc_test] mmc_test_rw_multiple_sg_len+0x44/0x6c [mmc_test] mmc_test_profile_sglen_wr_nonblock_perf+0x6c/0x94 [mmc_test] mtf_test_write+0x238/0x3cc [mmc_test] Signed-off-by: Veerabhadrarao Badiganti Reviewed-by: Stephen Boyd Tested-by: Sai Prakash Ranjan Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1582714668-17247-1-git-send-email-vbadigan@codeaurora.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_test.c | 52 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 12 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index 492dd4596314..c21b3cb71775 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -71,6 +71,7 @@ struct mmc_test_mem { * @sg_len: length of currently mapped scatterlist @sg * @mem: allocated memory * @sg: scatterlist + * @sg_areq: scatterlist for non-blocking request */ struct mmc_test_area { unsigned long max_sz; @@ -82,6 +83,7 @@ struct mmc_test_area { unsigned int sg_len; struct mmc_test_mem *mem; struct scatterlist *sg; + struct scatterlist *sg_areq; }; /** @@ -836,14 +838,16 @@ static int mmc_test_start_areq(struct mmc_test_card *test, } static int mmc_test_nonblock_transfer(struct mmc_test_card *test, - struct scatterlist *sg, unsigned sg_len, - unsigned dev_addr, unsigned blocks, - unsigned blksz, int write, int count) + unsigned int dev_addr, int write, + int count) { struct mmc_test_req *rq1, *rq2; struct mmc_request *mrq, *prev_mrq; int i; int ret = RESULT_OK; + struct mmc_test_area *t = &test->area; + struct scatterlist *sg = t->sg; + struct scatterlist *sg_areq = t->sg_areq; rq1 = mmc_test_req_alloc(); rq2 = mmc_test_req_alloc(); @@ -857,8 +861,8 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test, for (i = 0; i < count; i++) { mmc_test_req_reset(container_of(mrq, struct mmc_test_req, mrq)); - mmc_test_prepare_mrq(test, mrq, sg, sg_len, dev_addr, blocks, - blksz, write); + mmc_test_prepare_mrq(test, mrq, sg, t->sg_len, dev_addr, + t->blocks, 512, write); ret = mmc_test_start_areq(test, mrq, prev_mrq); if (ret) goto err; @@ -867,7 +871,8 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test, prev_mrq = &rq2->mrq; swap(mrq, prev_mrq); - dev_addr += blocks; + swap(sg, sg_areq); + dev_addr += t->blocks; } ret = mmc_test_start_areq(test, NULL, prev_mrq); @@ -1396,10 +1401,11 @@ static int mmc_test_no_highmem(struct mmc_test_card *test) * Map sz bytes so that it can be transferred. */ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz, - int max_scatter, int min_sg_len) + int max_scatter, int min_sg_len, bool nonblock) { struct mmc_test_area *t = &test->area; int err; + unsigned int sg_len = 0; t->blocks = sz >> 9; @@ -1411,6 +1417,22 @@ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz, err = mmc_test_map_sg(t->mem, sz, t->sg, 1, t->max_segs, t->max_seg_sz, &t->sg_len, min_sg_len); } + + if (err || !nonblock) + goto err; + + if (max_scatter) { + err = mmc_test_map_sg_max_scatter(t->mem, sz, t->sg_areq, + t->max_segs, t->max_seg_sz, + &sg_len); + } else { + err = mmc_test_map_sg(t->mem, sz, t->sg_areq, 1, t->max_segs, + t->max_seg_sz, &sg_len, min_sg_len); + } + if (!err && sg_len != t->sg_len) + err = -EINVAL; + +err: if (err) pr_info("%s: Failed to map sg list\n", mmc_hostname(test->card->host)); @@ -1440,7 +1462,6 @@ static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz, struct timespec64 ts1, ts2; int ret = 0; int i; - struct mmc_test_area *t = &test->area; /* * In the case of a maximally scattered transfer, the maximum transfer @@ -1458,15 +1479,14 @@ static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz, sz = max_tfr; } - ret = mmc_test_area_map(test, sz, max_scatter, min_sg_len); + ret = mmc_test_area_map(test, sz, max_scatter, min_sg_len, nonblock); if (ret) return ret; if (timed) ktime_get_ts64(&ts1); if (nonblock) - ret = mmc_test_nonblock_transfer(test, t->sg, t->sg_len, - dev_addr, t->blocks, 512, write, count); + ret = mmc_test_nonblock_transfer(test, dev_addr, write, count); else for (i = 0; i < count && ret == 0; i++) { ret = mmc_test_area_transfer(test, dev_addr, write); @@ -1525,6 +1545,7 @@ static int mmc_test_area_cleanup(struct mmc_test_card *test) struct mmc_test_area *t = &test->area; kfree(t->sg); + kfree(t->sg_areq); mmc_test_free_mem(t->mem); return 0; @@ -1584,6 +1605,13 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) goto out_free; } + t->sg_areq = kmalloc_array(t->max_segs, sizeof(*t->sg_areq), + GFP_KERNEL); + if (!t->sg_areq) { + ret = -ENOMEM; + goto out_free; + } + t->dev_addr = mmc_test_capacity(test->card) / 2; t->dev_addr -= t->dev_addr % (t->max_sz >> 9); @@ -2468,7 +2496,7 @@ static int __mmc_test_cmds_during_tfr(struct mmc_test_card *test, if (!(test->card->host->caps & MMC_CAP_CMD_DURING_TFR)) return RESULT_UNSUP_HOST; - ret = mmc_test_area_map(test, sz, 0, 0); + ret = mmc_test_area_map(test, sz, 0, 0, use_areq); if (ret) return ret; -- cgit v1.2.3 From 1a91a36aba9c232c73e4a5fce038147f5d29e566 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 26 Feb 2020 16:31:25 -0600 Subject: mmc: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200226223125.GA20630@embeddedor Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 2 +- drivers/mmc/host/sdhci-cadence.c | 2 +- drivers/mmc/host/sdhci-pci.h | 2 +- drivers/mmc/host/sdhci-pltfm.h | 2 +- drivers/mmc/host/sdhci.h | 2 +- drivers/mmc/host/vub300.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 2a2173d953f5..68cf41747fa5 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -75,7 +75,7 @@ struct sdhci_acpi_host { bool use_runtime_pm; bool is_intel; bool reset_signal_volt_on_suspend; - unsigned long private[0] ____cacheline_aligned; + unsigned long private[] ____cacheline_aligned; }; enum { diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index e573495f8726..6da6d4fb5edd 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -68,7 +68,7 @@ struct sdhci_cdns_priv { void __iomem *hrs_addr; bool enhanced_strobe; unsigned int nr_phy_params; - struct sdhci_cdns_phy_param phy_params[0]; + struct sdhci_cdns_phy_param phy_params[]; }; struct sdhci_cdns_phy_cfg { diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 981bbbe63aff..42ccd123b046 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -163,7 +163,7 @@ struct sdhci_pci_slot { bool cd_override_level; void (*hw_reset)(struct sdhci_host *host); - unsigned long private[0] ____cacheline_aligned; + unsigned long private[] ____cacheline_aligned; }; struct sdhci_pci_chip { diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 2af445b8c325..6301b81cf573 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -25,7 +25,7 @@ struct sdhci_pltfm_host { unsigned int clock; u16 xfer_mode_shadow; - unsigned long private[0] ____cacheline_aligned; + unsigned long private[] ____cacheline_aligned; }; #ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index cac2d97782e6..249635692112 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -614,7 +614,7 @@ struct sdhci_host { u64 data_timeout; - unsigned long private[0] ____cacheline_aligned; + unsigned long private[] ____cacheline_aligned; }; struct sdhci_ops { diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 6ced1b7f642f..a5a90d133f1f 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -95,7 +95,7 @@ struct sd_response_header { u8 port_number; u8 command_type; u8 command_index; - u8 command_response[0]; + u8 command_response[]; } __packed; struct sd_status_header { -- cgit v1.2.3 From 0ffa6cfbd94982e6c028a8924b06a96c1b91bed8 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Mar 2020 19:38:42 +0530 Subject: mmc: cqhci: Add cqhci_deactivate() Host controllers can reset CQHCI either directly or as a consequence of host controller reset. Add cqhci_deactivate() which puts the CQHCI driver into a state that is consistent with that. Signed-off-by: Adrian Hunter Signed-off-by: Veerabhadrarao Badiganti Link: https://lore.kernel.org/r/1583503724-13943-2-git-send-email-vbadigan@codeaurora.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/cqhci.c | 6 +++--- drivers/mmc/host/cqhci.h | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/cqhci.c b/drivers/mmc/host/cqhci.c index e2ea2c4b6b94..d8d024a1682b 100644 --- a/drivers/mmc/host/cqhci.c +++ b/drivers/mmc/host/cqhci.c @@ -298,16 +298,16 @@ static void __cqhci_disable(struct cqhci_host *cq_host) cq_host->activated = false; } -int cqhci_suspend(struct mmc_host *mmc) +int cqhci_deactivate(struct mmc_host *mmc) { struct cqhci_host *cq_host = mmc->cqe_private; - if (cq_host->enabled) + if (cq_host->enabled && cq_host->activated) __cqhci_disable(cq_host); return 0; } -EXPORT_SYMBOL(cqhci_suspend); +EXPORT_SYMBOL(cqhci_deactivate); int cqhci_resume(struct mmc_host *mmc) { diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h index def76e9b5cac..437700179de4 100644 --- a/drivers/mmc/host/cqhci.h +++ b/drivers/mmc/host/cqhci.h @@ -230,7 +230,11 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, int data_error); int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, bool dma64); struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev); -int cqhci_suspend(struct mmc_host *mmc); +int cqhci_deactivate(struct mmc_host *mmc); +static inline int cqhci_suspend(struct mmc_host *mmc) +{ + return cqhci_deactivate(mmc); +} int cqhci_resume(struct mmc_host *mmc); #endif -- cgit v1.2.3 From 5cf583f1fb9c5ca8862c8a1c688874dc85e594f5 Mon Sep 17 00:00:00 2001 From: Veerabhadrarao Badiganti Date: Fri, 6 Mar 2020 19:38:43 +0530 Subject: mmc: sdhci-msm: Deactivate CQE during SDHC reset When SDHC gets reset (E.g. in runtime suspend path), CQE also gets reset and goes to disable state. But s/w state still points it as CQE is in enabled state. Since s/w and h/w states goes out of sync, it results in s/w request timeout for subsequent CQE requests. To synchronize CQE s/w and h/w state during SDHC reset, explicitly deactivate CQE just before SDHC reset. Signed-off-by: Veerabhadrarao Badiganti Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/1583503724-13943-3-git-send-email-vbadigan@codeaurora.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 53b79ee37fdd..09ff7315eb5e 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1823,6 +1823,13 @@ 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 void sdhci_msm_reset(struct sdhci_host *host, u8 mask) +{ + if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL)) + cqhci_deactivate(host->mmc); + sdhci_reset(host, mask); +} + 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, @@ -1861,7 +1868,7 @@ static const struct of_device_id sdhci_msm_dt_match[] = { MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); static const struct sdhci_ops sdhci_msm_ops = { - .reset = sdhci_reset, + .reset = sdhci_msm_reset, .set_clock = sdhci_msm_set_clock, .get_min_clock = sdhci_msm_get_min_clock, .get_max_clock = sdhci_msm_get_max_clock, -- cgit v1.2.3 From d3392339cae94b2ab01646a22f67ce464e46ed68 Mon Sep 17 00:00:00 2001 From: Veerabhadrarao Badiganti Date: Wed, 4 Mar 2020 18:55:20 +0530 Subject: mmc: cqhci: Update cqhci memory ioresource name Update cqhci memory ioresource name from cqhci_mem to cqhci since suffix _mem is redundant. Only sdhci-msm driver is making use of this resource as of now. No other vendor's driver is using it. So this update shouldn't affect any other vendor's cqhci functionality. Signed-off-by: Veerabhadrarao Badiganti Acked-by: Adrian Hunter Reviewed-by: Douglas Anderson Acked-by: Bjorn Andersson Link: https://lore.kernel.org/r/1583328320-9981-1-git-send-email-vbadigan@codeaurora.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/cqhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/cqhci.c b/drivers/mmc/host/cqhci.c index d8d024a1682b..c2239ee2c0ef 100644 --- a/drivers/mmc/host/cqhci.c +++ b/drivers/mmc/host/cqhci.c @@ -1077,7 +1077,7 @@ struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev) /* check and setup CMDQ interface */ cqhci_memres = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "cqhci_mem"); + "cqhci"); if (!cqhci_memres) { dev_dbg(&pdev->dev, "CMDQ not supported\n"); return ERR_PTR(-EINVAL); -- cgit v1.2.3 From fcc958d6dbbdb0ca646806f413611cf48b2ae697 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 2 Mar 2020 10:35:34 +0100 Subject: mmc: renesas_sdhi: Use BITS_PER_LONG helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the existing BITS_PER_LONG helper definition instead of calculating this value. Signed-off-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/20200302093534.9055-1-geert+renesas@glider.be Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index 7a1a741547f2..2a4c83a5f32e 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -60,7 +60,7 @@ struct renesas_sdhi { bool doing_tune; /* Tuning values: 1 for success, 0 for failure */ - DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long)); + DECLARE_BITMAP(taps, BITS_PER_LONG); unsigned int tap_num; unsigned long tap_set; }; -- cgit v1.2.3 From a5d90dc488e417c7f4a08e67936c25a199179cc4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 13 Feb 2020 17:37:15 +0100 Subject: mmc: renesas_sdhi: simplify execute_tuning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After refactoring, 'ret' variable is superfluous. Remove it. Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Link: https://lore.kernel.org/r/20200213163715.8212-1-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 0f07cc1aee34..df826661366f 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -497,7 +497,7 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) { struct renesas_sdhi *priv = host_to_priv(host); - int i, ret; + int i; priv->tap_num = renesas_sdhi_init_tuning(host); if (!priv->tap_num) @@ -517,8 +517,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode) /* Set sampling clock position */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, i % priv->tap_num); - ret = mmc_send_tuning(host->mmc, opcode, NULL); - if (ret == 0) + if (mmc_send_tuning(host->mmc, opcode, NULL) == 0) set_bit(i, priv->taps); } -- cgit v1.2.3 From ee0f309263a69c3860882e41d3e1829c2ffb6479 Mon Sep 17 00:00:00 2001 From: Faiz Abbas Date: Thu, 5 Mar 2020 20:42:28 +0530 Subject: mmc: sdhci-omap: Add Support for Suspend/Resume Add power management ops which save and restore the driver context and facilitate a system suspend and resume. Signed-off-by: Faiz Abbas Link: https://lore.kernel.org/r/20200305151228.24692-1-faiz_abbas@ti.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-omap.c | 57 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index c4978177ef88..1ec74c2d5c17 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -108,6 +108,11 @@ struct sdhci_omap_host { struct pinctrl *pinctrl; struct pinctrl_state **pinctrl_state; bool is_tuning; + /* Omap specific context save */ + u32 con; + u32 hctl; + u32 sysctl; + u32 capa; }; static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host); @@ -1235,12 +1240,64 @@ static int sdhci_omap_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static void sdhci_omap_context_save(struct sdhci_omap_host *omap_host) +{ + omap_host->con = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); + omap_host->hctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL); + omap_host->capa = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA); +} + +static void sdhci_omap_context_restore(struct sdhci_omap_host *omap_host) +{ + sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, omap_host->con); + sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl); + sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, omap_host->capa); +} + +static int __maybe_unused sdhci_omap_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); + + sdhci_suspend_host(host); + + sdhci_omap_context_save(omap_host); + + pinctrl_pm_select_idle_state(dev); + + pm_runtime_force_suspend(dev); + + return 0; +} + +static int __maybe_unused sdhci_omap_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); + + pm_runtime_force_resume(dev); + + pinctrl_pm_select_default_state(dev); + + sdhci_omap_context_restore(omap_host); + + sdhci_resume_host(host); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(sdhci_omap_dev_pm_ops, sdhci_omap_suspend, + sdhci_omap_resume); static struct platform_driver sdhci_omap_driver = { .probe = sdhci_omap_probe, .remove = sdhci_omap_remove, .driver = { .name = "sdhci-omap", + .pm = &sdhci_omap_dev_pm_ops, .of_match_table = omap_sdhci_match, }, }; -- cgit v1.2.3 From 5e958e4aacf44e1cc98b0d88d8e66e98f03bbf66 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Wed, 11 Mar 2020 08:47:54 -0700 Subject: sdhci: tegra: Implement Tegra specific set_timeout callback Tegra host supports HW busy detection and timeouts based on the count programmed in SDHCI_TIMEOUT_CONTROL register and max busy timeout it supports is 11s in finite busy wait mode. Some operations like SLEEP_AWAKE, ERASE and flush cache through SWITCH commands take longer than 11s and Tegra host supports infinite HW busy wait mode where HW waits forever till the card is busy without HW timeout. This patch implements Tegra specific set_timeout sdhci_ops to allow switching between finite and infinite HW busy detection wait modes based on the device command expected operation time. Signed-off-by: Sowjanya Komatineni Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/1583941675-9884-1-git-send-email-skomatineni@nvidia.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index a25c3a4d3f6c..fa8f6a4dcc7c 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -45,6 +45,7 @@ #define SDHCI_TEGRA_CAP_OVERRIDES_DQS_TRIM_SHIFT 8 #define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120 +#define SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT BIT(0) #define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8 #define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10 #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20 @@ -1227,6 +1228,34 @@ static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask) return 0; } +static void tegra_sdhci_set_timeout(struct sdhci_host *host, + struct mmc_command *cmd) +{ + u32 val; + + /* + * HW busy detection timeout is based on programmed data timeout + * counter and maximum supported timeout is 11s which may not be + * enough for long operations like cache flush, sleep awake, erase. + * + * ERASE_TIMEOUT_LIMIT bit of VENDOR_MISC_CTRL register allows + * host controller to wait for busy state until the card is busy + * without HW timeout. + * + * So, use infinite busy wait mode for operations that may take + * more than maximum HW busy timeout of 11s otherwise use finite + * busy wait mode. + */ + val = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); + if (cmd && cmd->busy_timeout >= 11 * HZ) + val |= SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT; + else + val &= ~SDHCI_MISC_CTRL_ERASE_TIMEOUT_LIMIT; + sdhci_writel(host, val, SDHCI_TEGRA_VENDOR_MISC_CTRL); + + __sdhci_set_timeout(host, cmd); +} + static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = { .write_l = tegra_cqhci_writel, .enable = sdhci_tegra_cqe_enable, @@ -1366,6 +1395,7 @@ static const struct sdhci_ops tegra210_sdhci_ops = { .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, .voltage_switch = tegra_sdhci_voltage_switch, .get_max_clock = tegra_sdhci_get_max_clock, + .set_timeout = tegra_sdhci_set_timeout, }; static const struct sdhci_pltfm_data sdhci_tegra210_pdata = { @@ -1403,6 +1433,7 @@ static const struct sdhci_ops tegra186_sdhci_ops = { .voltage_switch = tegra_sdhci_voltage_switch, .get_max_clock = tegra_sdhci_get_max_clock, .irq = sdhci_tegra_cqhci_irq, + .set_timeout = tegra_sdhci_set_timeout, }; static const struct sdhci_pltfm_data sdhci_tegra186_pdata = { -- cgit v1.2.3 From ff124c31ccd706c79c7a87c31a77b80fd89cd48b Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Wed, 11 Mar 2020 08:47:55 -0700 Subject: sdhci: tegra: Enable MMC_CAP_WAIT_WHILE_BUSY host capability Tegra sdhci host supports HW busy detection of the device busy signaling over data0 lane. So, this patch enables host capability MMC_CAP_wAIT_WHILE_BUSY. Signed-off-by: Sowjanya Komatineni Link: https://lore.kernel.org/r/1583941675-9884-2-git-send-email-skomatineni@nvidia.com [Ulf: Lumped together the caps assignments] Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index fa8f6a4dcc7c..3e2c5101291d 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -1583,8 +1583,8 @@ static int sdhci_tegra_probe(struct platform_device *pdev) if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) host->mmc->caps |= MMC_CAP_1_8V_DDR; - /* R1B responses is required to properly manage HW busy detection. */ - host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY; + /* HW busy detection is supported, but R1B responses are required. */ + host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY; tegra_sdhci_parse_dt(host); -- cgit v1.2.3 From 6bbcf74dd929d0b7d8588ffdd38803bb455873f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Mar 2020 09:04:39 +0100 Subject: mmc: vub300: Use scnprintf() for avoiding potential buffer overflow Since snprintf() returns the would-be-output size instead of the actual output size, the succeeding calls may go beyond the given buffer limit. Fix it by replacing with scnprintf(). Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20200311080439.13928-1-tiwai@suse.de Signed-off-by: Ulf Hansson --- drivers/mmc/host/vub300.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index a5a90d133f1f..739cf63ef6e2 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -1363,7 +1363,7 @@ static void download_offload_pseudocode(struct vub300_mmc_host *vub300) int retval; for (n = 0; n < sdio_funcs; n++) { struct sdio_func *sf = card->sdio_func[n]; - l += snprintf(vub300->vub_name + l, + l += scnprintf(vub300->vub_name + l, sizeof(vub300->vub_name) - l, "_%04X%04X", sf->vendor, sf->device); } -- cgit v1.2.3 From 6c92ae1e452ff3f4648b1450c9a3233a2ca53feb Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 6 Mar 2020 18:44:03 +0100 Subject: mmc: sdhci: Introduce sdhci_set_power_and_bus_voltage() Some controllers diverge from the standard way of setting power and need their bus voltage register to be configured regardless of the whether they use regulators. As this is a common pattern across sdhci hosts, create a helper function. Signed-off-by: Nicolas Saenz Julienne Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200306174413.20634-2-nsaenzjulienne@suse.de Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 19 +++++++++++++++++++ drivers/mmc/host/sdhci.h | 3 +++ 2 files changed, 22 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c59566363a42..525e0c971c6a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2010,6 +2010,25 @@ void sdhci_set_power(struct sdhci_host *host, unsigned char mode, } EXPORT_SYMBOL_GPL(sdhci_set_power); +/* + * Some controllers need to configure a valid bus voltage on their power + * register regardless of whether an external regulator is taking care of power + * supply. This helper function takes care of it if set as the controller's + * sdhci_ops.set_power callback. + */ +void sdhci_set_power_and_bus_voltage(struct sdhci_host *host, + unsigned char mode, + unsigned short vdd) +{ + if (!IS_ERR(host->mmc->supply.vmmc)) { + struct mmc_host *mmc = host->mmc; + + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + } + sdhci_set_power_noreg(host, mode, vdd); +} +EXPORT_SYMBOL_GPL(sdhci_set_power_and_bus_voltage); + /*****************************************************************************\ * * * MMC callbacks * diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 249635692112..851b81565f46 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -772,6 +772,9 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock); void sdhci_enable_clk(struct sdhci_host *host, u16 clk); void sdhci_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd); +void sdhci_set_power_and_bus_voltage(struct sdhci_host *host, + unsigned char mode, + unsigned short vdd); void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, unsigned short vdd); void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq); -- cgit v1.2.3 From c2c5252c5c615878ce774867c963e1ad71cf8edd Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 6 Mar 2020 18:44:04 +0100 Subject: mmc: sdhci: arasan: Use sdhci_set_power_and_voltage() The sdhci core provides a helper function with the same functionality as this controller's set_power() callback. Use it instead. Signed-off-by: Nicolas Saenz Julienne Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200306174413.20634-3-nsaenzjulienne@suse.de Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 0146d7dd315b..d4905c106c06 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -325,17 +325,6 @@ static int sdhci_arasan_voltage_switch(struct mmc_host *mmc, return -EINVAL; } -static void sdhci_arasan_set_power(struct sdhci_host *host, unsigned char mode, - unsigned short vdd) -{ - if (!IS_ERR(host->mmc->supply.vmmc)) { - struct mmc_host *mmc = host->mmc; - - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); - } - sdhci_set_power_noreg(host, mode, vdd); -} - static const struct sdhci_ops sdhci_arasan_ops = { .set_clock = sdhci_arasan_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, @@ -343,7 +332,7 @@ static const struct sdhci_ops sdhci_arasan_ops = { .set_bus_width = sdhci_set_bus_width, .reset = sdhci_arasan_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, - .set_power = sdhci_arasan_set_power, + .set_power = sdhci_set_power_and_bus_voltage, }; static const struct sdhci_pltfm_data sdhci_arasan_pdata = { @@ -414,7 +403,7 @@ static const struct sdhci_ops sdhci_arasan_cqe_ops = { .set_bus_width = sdhci_set_bus_width, .reset = sdhci_arasan_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, - .set_power = sdhci_arasan_set_power, + .set_power = sdhci_set_power_and_bus_voltage, .irq = sdhci_arasan_cqhci_irq, }; -- cgit v1.2.3 From d2abc6e2b5c2956d05d588c78c0dfde30749c172 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 6 Mar 2020 18:44:05 +0100 Subject: mmc: sdhci: milbeaut: Use sdhci_set_power_and_voltage() The sdhci core provides a helper function with the same functionality as this controller's set_power() callback. Use it instead. Signed-off-by: Nicolas Saenz Julienne Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200306174413.20634-4-nsaenzjulienne@suse.de Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-milbeaut.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-milbeaut.c b/drivers/mmc/host/sdhci-milbeaut.c index 92f30a1db435..4e7cc0680f94 100644 --- a/drivers/mmc/host/sdhci-milbeaut.c +++ b/drivers/mmc/host/sdhci-milbeaut.c @@ -121,17 +121,6 @@ static void sdhci_milbeaut_reset(struct sdhci_host *host, u8 mask) } } -static void sdhci_milbeaut_set_power(struct sdhci_host *host, - unsigned char mode, unsigned short vdd) -{ - if (!IS_ERR(host->mmc->supply.vmmc)) { - struct mmc_host *mmc = host->mmc; - - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); - } - sdhci_set_power_noreg(host, mode, vdd); -} - static const struct sdhci_ops sdhci_milbeaut_ops = { .voltage_switch = sdhci_milbeaut_soft_voltage_switch, .get_min_clock = sdhci_milbeaut_get_min_clock, @@ -139,7 +128,7 @@ static const struct sdhci_ops sdhci_milbeaut_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_set_uhs_signaling, - .set_power = sdhci_milbeaut_set_power, + .set_power = sdhci_set_power_and_bus_voltage, }; static void sdhci_milbeaut_bridge_reset(struct sdhci_host *host, -- cgit v1.2.3 From 98160562e3bb7dc4c552a277b71f6692cc7c684f Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 6 Mar 2020 18:44:06 +0100 Subject: mmc: sdhci: at91: Use sdhci_set_power_and_voltage() The sdhci core provides a helper function with the same functionality as this controller's set_power() callback. Use it instead. Signed-off-by: Nicolas Saenz Julienne Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200306174413.20634-5-nsaenzjulienne@suse.de Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-at91.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index fcef5c0d0908..e36db6c6618e 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -101,22 +101,6 @@ static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); } -/* - * In this specific implementation of the SDHCI controller, the power register - * needs to have a valid voltage set even when the power supply is managed by - * an external regulator. - */ -static void sdhci_at91_set_power(struct sdhci_host *host, unsigned char mode, - unsigned short vdd) -{ - if (!IS_ERR(host->mmc->supply.vmmc)) { - struct mmc_host *mmc = host->mmc; - - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); - } - sdhci_set_power_noreg(host, mode, vdd); -} - static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) { @@ -146,7 +130,7 @@ static const struct sdhci_ops sdhci_at91_sama5d2_ops = { .set_bus_width = sdhci_set_bus_width, .reset = sdhci_at91_reset, .set_uhs_signaling = sdhci_at91_set_uhs_signaling, - .set_power = sdhci_at91_set_power, + .set_power = sdhci_set_power_and_bus_voltage, }; static const struct sdhci_pltfm_data sdhci_sama5d2_pdata = { -- cgit v1.2.3 From 9d8acdd32cd5719c9160073fbfd85025e1783ae2 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 6 Mar 2020 18:44:09 +0100 Subject: mmc: sdhci: am654: Use sdhci_set_power_and_voltage() The sdhci core provides a helper function with the same functionality as this controller's set_power() callback. Use it instead. Signed-off-by: Nicolas Saenz Julienne Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200306174413.20634-8-nsaenzjulienne@suse.de Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 58183b5f4e82..061b4398a4f1 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -255,17 +255,6 @@ static void sdhci_j721e_4bit_set_clock(struct sdhci_host *host, sdhci_set_clock(host, clock); } -static void sdhci_am654_set_power(struct sdhci_host *host, unsigned char mode, - unsigned short vdd) -{ - if (!IS_ERR(host->mmc->supply.vmmc)) { - struct mmc_host *mmc = host->mmc; - - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); - } - sdhci_set_power_noreg(host, mode, vdd); -} - static void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg) { unsigned char timing = host->mmc->ios.timing; @@ -321,7 +310,7 @@ static struct sdhci_ops sdhci_am654_ops = { .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, .set_uhs_signaling = sdhci_set_uhs_signaling, .set_bus_width = sdhci_set_bus_width, - .set_power = sdhci_am654_set_power, + .set_power = sdhci_set_power_and_bus_voltage, .set_clock = sdhci_am654_set_clock, .write_b = sdhci_am654_write_b, .irq = sdhci_am654_cqhci_irq, @@ -344,7 +333,7 @@ static struct sdhci_ops sdhci_j721e_8bit_ops = { .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, .set_uhs_signaling = sdhci_set_uhs_signaling, .set_bus_width = sdhci_set_bus_width, - .set_power = sdhci_am654_set_power, + .set_power = sdhci_set_power_and_bus_voltage, .set_clock = sdhci_am654_set_clock, .write_b = sdhci_am654_write_b, .irq = sdhci_am654_cqhci_irq, @@ -367,7 +356,7 @@ static struct sdhci_ops sdhci_j721e_4bit_ops = { .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, .set_uhs_signaling = sdhci_set_uhs_signaling, .set_bus_width = sdhci_set_bus_width, - .set_power = sdhci_am654_set_power, + .set_power = sdhci_set_power_and_bus_voltage, .set_clock = sdhci_j721e_4bit_set_clock, .write_b = sdhci_am654_write_b, .irq = sdhci_am654_cqhci_irq, -- cgit v1.2.3 From f87391eec2c5f54269e64d655da19f2c32515e4c Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Fri, 6 Mar 2020 18:44:11 +0100 Subject: mmc: sdhci: iproc: Add custom set_power() callback for bcm2711 The controller needs a valid bus voltage in its power register regardless of whether an external regulator is taking care of the power supply. The sdhci core already provides a helper function for this, sdhci_set_power_and_bus_voltage(), so create a bcm2711 specific 'struct sdhci_ops' which makes use of it. Signed-off-by: Nicolas Saenz Julienne Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200306174413.20634-10-nsaenzjulienne@suse.de Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-iproc.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index f4f5f0a70cda..225603148d7d 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -261,9 +261,24 @@ static const struct sdhci_iproc_data bcm2835_data = { .mmc_caps = 0x00000000, }; +static const struct sdhci_ops sdhci_iproc_bcm2711_ops = { + .read_l = sdhci_iproc_readl, + .read_w = sdhci_iproc_readw, + .read_b = sdhci_iproc_readb, + .write_l = sdhci_iproc_writel, + .write_w = sdhci_iproc_writew, + .write_b = sdhci_iproc_writeb, + .set_clock = sdhci_set_clock, + .set_power = sdhci_set_power_and_bus_voltage, + .get_max_clock = sdhci_iproc_get_max_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + static const struct sdhci_pltfm_data sdhci_bcm2711_pltfm_data = { .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, - .ops = &sdhci_iproc_32only_ops, + .ops = &sdhci_iproc_bcm2711_ops, }; static const struct sdhci_iproc_data bcm2711_data = { -- cgit v1.2.3 From fdd8eef4be53fcb4f50dbe4c6ee7a334a395f9ee Mon Sep 17 00:00:00 2001 From: Cristian Birsan Date: Thu, 12 Mar 2020 14:29:24 +0000 Subject: mmc: sdhci-of-at91: Display clock changes for debug purpose only The sdhci_at91_set_clks_presets() function is called multiple times at runtime and the messages are shown on the console. Display clk mul, gck rate and clk base for debug purpose only. Signed-off-by: Cristian Birsan Signed-off-by: Tudor Ambarus Acked-by: Ludovic Desroches Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20200312142904.232822-1-tudor.ambarus@microchip.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-at91.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index e36db6c6618e..c79bff5e2280 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -189,8 +189,8 @@ static int sdhci_at91_set_clks_presets(struct device *dev) /* Set capabilities in ro mode. */ writel(0, host->ioaddr + SDMMC_CACR); - dev_info(dev, "update clk mul to %u as gck rate is %u Hz and clk base is %u Hz\n", - clk_mul, gck_rate, clk_base_rate); + dev_dbg(dev, "update clk mul to %u as gck rate is %u Hz and clk base is %u Hz\n", + clk_mul, gck_rate, clk_base_rate); /* * We have to set preset values because it depends on the clk_mul -- cgit v1.2.3 From fa0910107a9fea170b817f31da2a65463e00e80e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 12 Mar 2020 20:00:50 +0900 Subject: mmc: sdhci: use FIELD_GET for preset value bit masks Use the FIELD_GET macro to get access to the register fields. Delete the shift macros. Signed-off-by: Masahiro Yamada Link: https://lore.kernel.org/r/20200312110050.21732-1-yamada.masahiro@socionext.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 10 +++++----- drivers/mmc/host/sdhci.h | 10 ++++------ 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 525e0c971c6a..3f716466fcfd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -9,6 +9,7 @@ * - JMicron (hardware and technical support) */ +#include #include #include #include @@ -1766,10 +1767,9 @@ u16 sdhci_calc_clk(struct sdhci_host *host, unsigned int clock, clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); pre_val = sdhci_get_preset_value(host); - div = (pre_val & SDHCI_PRESET_SDCLK_FREQ_MASK) - >> SDHCI_PRESET_SDCLK_FREQ_SHIFT; + div = FIELD_GET(SDHCI_PRESET_SDCLK_FREQ_MASK, pre_val); if (host->clk_mul && - (pre_val & SDHCI_PRESET_CLKGEN_SEL_MASK)) { + (pre_val & SDHCI_PRESET_CLKGEN_SEL)) { clk = SDHCI_PROG_CLOCK_MODE; real_div = div + 1; clk_mul = host->clk_mul; @@ -2246,8 +2246,8 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_enable_preset_value(host, true); preset = sdhci_get_preset_value(host); - ios->drv_type = (preset & SDHCI_PRESET_DRV_MASK) - >> SDHCI_PRESET_DRV_SHIFT; + ios->drv_type = FIELD_GET(SDHCI_PRESET_DRV_MASK, + preset); } /* Re-enable SD Clock */ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 851b81565f46..79dffbb731d3 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -9,6 +9,7 @@ #ifndef __SDHCI_HW_H #define __SDHCI_HW_H +#include #include #include #include @@ -267,12 +268,9 @@ #define SDHCI_PRESET_FOR_SDR104 0x6C #define SDHCI_PRESET_FOR_DDR50 0x6E #define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ -#define SDHCI_PRESET_DRV_MASK 0xC000 -#define SDHCI_PRESET_DRV_SHIFT 14 -#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400 -#define SDHCI_PRESET_CLKGEN_SEL_SHIFT 10 -#define SDHCI_PRESET_SDCLK_FREQ_MASK 0x3FF -#define SDHCI_PRESET_SDCLK_FREQ_SHIFT 0 +#define SDHCI_PRESET_DRV_MASK GENMASK(15, 14) +#define SDHCI_PRESET_CLKGEN_SEL BIT(10) +#define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0) #define SDHCI_SLOT_INT_STATUS 0xFC -- cgit v1.2.3 From 55c2b8b9a383487f4f083f62d163fe3278fece1a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 16 Mar 2020 16:21:52 +0100 Subject: mmc: core: Re-work the code for eMMC sanitize The error path for sanitize operations that completes with -ETIMEDOUT, is tightly coupled with the internal request handling code of the core. More precisely, mmc_wait_for_req_done() checks for specific sanitize errors. This is not only inefficient as it affects all types of requests, but also hackish. Therefore, let's improve the behaviour by moving the error path out of the mmc core. To do that, retuning needs to be held while running the sanitize operation. Moreover, to avoid exporting unnecessary symbols to the mmc block module, let's move the code into the mmc_ops.c file. While updating the actual code, let's also take the opportunity to clean up some of the mess around it. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20200316152152.15122-1-ulf.hansson@linaro.org --- drivers/mmc/core/block.c | 40 ++-------------------------------------- drivers/mmc/core/core.c | 18 ------------------ drivers/mmc/core/mmc_ops.c | 38 +++++++++++++++++++++++++++++++++++--- drivers/mmc/core/mmc_ops.h | 2 +- 4 files changed, 38 insertions(+), 60 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 7634894df853..8499b56a15a8 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -70,7 +70,6 @@ MODULE_ALIAS("mmc:block"); * ample. */ #define MMC_BLK_TIMEOUT_MS (10 * 1000) -#define MMC_SANITIZE_REQ_TIMEOUT 240000 #define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16) #define MMC_EXTRACT_VALUE_FROM_ARG(x) ((x & 0x0000FF00) >> 8) @@ -413,34 +412,6 @@ static int mmc_blk_ioctl_copy_to_user(struct mmc_ioc_cmd __user *ic_ptr, return 0; } -static int ioctl_do_sanitize(struct mmc_card *card) -{ - int err; - - if (!mmc_can_sanitize(card)) { - pr_warn("%s: %s - SANITIZE is not supported\n", - mmc_hostname(card->host), __func__); - err = -EOPNOTSUPP; - goto out; - } - - pr_debug("%s: %s - SANITIZE IN PROGRESS...\n", - mmc_hostname(card->host), __func__); - - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_SANITIZE_START, 1, - MMC_SANITIZE_REQ_TIMEOUT); - - if (err) - pr_err("%s: %s - EXT_CSD_SANITIZE_START failed. err=%d\n", - mmc_hostname(card->host), __func__, err); - - pr_debug("%s: %s - SANITIZE COMPLETED\n", mmc_hostname(card->host), - __func__); -out: - return err; -} - static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms, u32 *resp_errs) { @@ -569,15 +540,8 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, } if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) && - (cmd.opcode == MMC_SWITCH)) { - err = ioctl_do_sanitize(card); - - if (err) - pr_err("%s: ioctl_do_sanitize() failed. err = %d", - __func__, err); - - return err; - } + (cmd.opcode == MMC_SWITCH)) + return mmc_sanitize(card); mmc_wait_for_req(card->host, &mrq); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 3f7a31456eb4..4c5de6d37ac7 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -403,23 +403,6 @@ void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq) cmd = mrq->cmd; - /* - * If host has timed out waiting for the sanitize - * to complete, card might be still in programming state - * so let's try to bring the card out of programming - * state. - */ - if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) { - if (!mmc_interrupt_hpi(host->card)) { - pr_warn("%s: %s: Interrupted sanitize\n", - mmc_hostname(host), __func__); - cmd->error = 0; - break; - } else { - pr_err("%s: %s: Failed to interrupt sanitize\n", - mmc_hostname(host), __func__); - } - } if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) break; @@ -1925,7 +1908,6 @@ int mmc_can_sanitize(struct mmc_card *card) return 1; return 0; } -EXPORT_SYMBOL(mmc_can_sanitize); int mmc_can_secure_erase_trim(struct mmc_card *card) { diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index c75c00b5890d..5bd0ab8b236a 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -21,6 +21,7 @@ #define MMC_BKOPS_TIMEOUT_MS (120 * 1000) /* 120s */ #define MMC_CACHE_FLUSH_TIMEOUT_MS (30 * 1000) /* 30s */ +#define MMC_SANITIZE_TIMEOUT_MS (240 * 1000) /* 240s */ static const u8 tuning_blk_pattern_4bit[] = { 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, @@ -597,9 +598,6 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1; } - if (index == EXT_CSD_SANITIZE_START) - cmd.sanitize_busy = true; - err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); if (err) goto out; @@ -1032,3 +1030,37 @@ int mmc_cmdq_disable(struct mmc_card *card) return mmc_cmdq_switch(card, false); } EXPORT_SYMBOL_GPL(mmc_cmdq_disable); + +int mmc_sanitize(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err; + + if (!mmc_can_sanitize(card)) { + pr_warn("%s: Sanitize not supported\n", mmc_hostname(host)); + return -EOPNOTSUPP; + } + + pr_debug("%s: Sanitize in progress...\n", mmc_hostname(host)); + + mmc_retune_hold(host); + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_SANITIZE_START, + 1, MMC_SANITIZE_TIMEOUT_MS); + if (err) + pr_err("%s: Sanitize failed err=%d\n", mmc_hostname(host), err); + + /* + * If the sanitize operation timed out, the card is probably still busy + * in the R1_STATE_PRG. Rather than continue to wait, let's try to abort + * it with a HPI command to get back into R1_STATE_TRAN. + */ + if (err == -ETIMEDOUT && !mmc_interrupt_hpi(card)) + pr_warn("%s: Sanitize aborted\n", mmc_hostname(host)); + + mmc_retune_release(host); + + pr_debug("%s: Sanitize completed\n", mmc_hostname(host)); + return err; +} +EXPORT_SYMBOL_GPL(mmc_sanitize); diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 38dcfeeaf6d5..632009260e51 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -32,7 +32,6 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); int mmc_spi_set_crc(struct mmc_host *host, int use_crc); int mmc_bus_test(struct mmc_card *card, u8 bus_width); -int mmc_interrupt_hpi(struct mmc_card *card); int mmc_can_ext_csd(struct mmc_card *card); int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); int mmc_switch_status(struct mmc_card *card, bool crc_err_fatal); @@ -47,6 +46,7 @@ void mmc_run_bkops(struct mmc_card *card); int mmc_flush_cache(struct mmc_card *card); int mmc_cmdq_enable(struct mmc_card *card); int mmc_cmdq_disable(struct mmc_card *card); +int mmc_sanitize(struct mmc_card *card); #endif -- cgit v1.2.3 From d4a384cb563e555ce00255f5f496b503e6cc6358 Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Wed, 25 Mar 2020 15:34:08 +0100 Subject: mmc: mmci_sdmmc: Fix clear busyd0end irq flag The busyd0 line transition can be very fast. The busy request may be completed by busy_d0end, without waiting for the busy_d0 steps. Therefore, clear the busyd0end irq flag, even if no busy_status. Fixes: 0e68de6aa7b1 ("mmc: mmci: sdmmc: add busy_complete callback") Cc: stable@vger.kernel.org Signed-off-by: Ludovic Barre Link: https://lore.kernel.org/r/20200325143409.13005-2-ludovic.barre@st.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci_stm32_sdmmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c index f76e82f8f12f..d33e62bd6153 100644 --- a/drivers/mmc/host/mmci_stm32_sdmmc.c +++ b/drivers/mmc/host/mmci_stm32_sdmmc.c @@ -358,11 +358,11 @@ complete: if (host->busy_status) { writel_relaxed(mask & ~host->variant->busy_detect_mask, base + MMCIMASK0); - writel_relaxed(host->variant->busy_detect_mask, - base + MMCICLEAR); host->busy_status = 0; } + writel_relaxed(host->variant->busy_detect_mask, base + MMCICLEAR); + return true; } -- cgit v1.2.3 From 9e2582e57407e390a94de7848a809540694bef35 Mon Sep 17 00:00:00 2001 From: yong mao Date: Fri, 27 Mar 2020 16:51:37 +0800 Subject: mmc: mediatek: fix SDIO irq issue SDIO irq is not triggered by low level, but by falling edge in our previous IC. This mechanism only have one chance to catch the SDIO irq if a SDIO irq comes within the multiple block transmission. This SDIO irq may be easily lost, because falling edge appears only once within 2 clock after data transmission is completed. SDIO irq recheck mechanism will make sure all irqs can be processed correctly. Signed-off-by: Yong Mao Link: https://lore.kernel.org/r/1585299097-6897-2-git-send-email-yong.mao@mediatek.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 7726dcf48f2c..b221c02cc71f 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -128,6 +128,7 @@ #define MSDC_PS_CDSTS (0x1 << 1) /* R */ #define MSDC_PS_CDDEBOUNCE (0xf << 12) /* RW */ #define MSDC_PS_DAT (0xff << 16) /* R */ +#define MSDC_PS_DATA1 (0x1 << 17) /* R */ #define MSDC_PS_CMD (0x1 << 24) /* R */ #define MSDC_PS_WP (0x1 << 31) /* R */ @@ -361,6 +362,7 @@ struct msdc_save_para { struct mtk_mmc_compatible { u8 clk_div_bits; + bool recheck_sdio_irq; bool hs400_tune; /* only used for MT8173 */ u32 pad_tune_reg; bool async_fifo; @@ -436,6 +438,7 @@ struct msdc_host { static const struct mtk_mmc_compatible mt8135_compat = { .clk_div_bits = 8, + .recheck_sdio_irq = false, .hs400_tune = false, .pad_tune_reg = MSDC_PAD_TUNE, .async_fifo = false, @@ -448,6 +451,7 @@ static const struct mtk_mmc_compatible mt8135_compat = { static const struct mtk_mmc_compatible mt8173_compat = { .clk_div_bits = 8, + .recheck_sdio_irq = true, .hs400_tune = true, .pad_tune_reg = MSDC_PAD_TUNE, .async_fifo = false, @@ -460,6 +464,7 @@ static const struct mtk_mmc_compatible mt8173_compat = { static const struct mtk_mmc_compatible mt8183_compat = { .clk_div_bits = 12, + .recheck_sdio_irq = false, .hs400_tune = false, .pad_tune_reg = MSDC_PAD_TUNE0, .async_fifo = true, @@ -472,6 +477,7 @@ static const struct mtk_mmc_compatible mt8183_compat = { static const struct mtk_mmc_compatible mt2701_compat = { .clk_div_bits = 12, + .recheck_sdio_irq = false, .hs400_tune = false, .pad_tune_reg = MSDC_PAD_TUNE0, .async_fifo = true, @@ -484,6 +490,7 @@ static const struct mtk_mmc_compatible mt2701_compat = { static const struct mtk_mmc_compatible mt2712_compat = { .clk_div_bits = 12, + .recheck_sdio_irq = false, .hs400_tune = false, .pad_tune_reg = MSDC_PAD_TUNE0, .async_fifo = true, @@ -496,6 +503,7 @@ static const struct mtk_mmc_compatible mt2712_compat = { static const struct mtk_mmc_compatible mt7622_compat = { .clk_div_bits = 12, + .recheck_sdio_irq = false, .hs400_tune = false, .pad_tune_reg = MSDC_PAD_TUNE0, .async_fifo = true, @@ -508,6 +516,7 @@ static const struct mtk_mmc_compatible mt7622_compat = { static const struct mtk_mmc_compatible mt8516_compat = { .clk_div_bits = 12, + .recheck_sdio_irq = false, .hs400_tune = false, .pad_tune_reg = MSDC_PAD_TUNE0, .async_fifo = true, @@ -518,6 +527,7 @@ static const struct mtk_mmc_compatible mt8516_compat = { static const struct mtk_mmc_compatible mt7620_compat = { .clk_div_bits = 8, + .recheck_sdio_irq = false, .hs400_tune = false, .pad_tune_reg = MSDC_PAD_TUNE, .async_fifo = false, @@ -591,6 +601,7 @@ static void msdc_reset_hw(struct msdc_host *host) static void msdc_cmd_next(struct msdc_host *host, struct mmc_request *mrq, struct mmc_command *cmd); +static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb); static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY | @@ -1007,6 +1018,32 @@ static int msdc_auto_cmd_done(struct msdc_host *host, int events, return cmd->error; } +/** + * msdc_recheck_sdio_irq - recheck whether the SDIO irq is lost + * + * Host controller may lost interrupt in some special case. + * Add SDIO irq recheck mechanism to make sure all interrupts + * can be processed immediately + * + */ +static void msdc_recheck_sdio_irq(struct msdc_host *host) +{ + u32 reg_int, reg_inten, reg_ps; + + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) { + reg_inten = readl(host->base + MSDC_INTEN); + if (reg_inten & MSDC_INTEN_SDIOIRQ) { + reg_int = readl(host->base + MSDC_INT); + reg_ps = readl(host->base + MSDC_PS); + if (!(reg_int & MSDC_INT_SDIOIRQ || + reg_ps & MSDC_PS_DATA1)) { + __msdc_enable_sdio_irq(host, 0); + sdio_signal_irq(host->mmc); + } + } + } +} + static void msdc_track_cmd_data(struct msdc_host *host, struct mmc_command *cmd, struct mmc_data *data) { @@ -1035,6 +1072,8 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) if (host->error) msdc_reset_hw(host); mmc_request_done(host->mmc, mrq); + if (host->dev_comp->recheck_sdio_irq) + msdc_recheck_sdio_irq(host); } /* returns true if command is fully handled; returns false otherwise */ @@ -1393,6 +1432,8 @@ static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb) if (enb) { sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); + if (host->dev_comp->recheck_sdio_irq) + msdc_recheck_sdio_irq(host); } else { sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ); sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); -- cgit v1.2.3 From 92075d98abf0f42db1cb518150364f196d4ad217 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Fri, 27 Mar 2020 12:36:39 +0800 Subject: mmc: cavium-octeon: remove nonsense variable coercion In this function, the variable 'base' is already 'void __iomem *base', and the return function 'devm_platform_ioremap_resource()' also returns this type, so the mandatory definition here is redundant. Signed-off-by: Tang Bin Link: https://lore.kernel.org/r/20200327043639.6564-1-tangbin@cmss.chinamobile.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/cavium-octeon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/cavium-octeon.c b/drivers/mmc/host/cavium-octeon.c index 916746c6c2c7..e299cdd1e619 100644 --- a/drivers/mmc/host/cavium-octeon.c +++ b/drivers/mmc/host/cavium-octeon.c @@ -207,13 +207,13 @@ static int octeon_mmc_probe(struct platform_device *pdev) base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); - host->base = (void __iomem *)base; + host->base = base; host->reg_off = 0; base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(base)) return PTR_ERR(base); - host->dma_base = (void __iomem *)base; + host->dma_base = base; /* * To keep the register addresses shared we intentionaly use * a negative offset here, first register used on Octeon therefore -- cgit v1.2.3