diff mbox series

[V2,2/4] mmc: mmci: Add support for probing bus voltage level translator

Message ID 20210124170258.32862-2-marex@denx.de
State New
Headers show
Series [V2,1/4] mmc: mmci: Add bindings to operate CMD, CK, CKIN pins as GPIO | expand

Commit Message

Marek Vasut Jan. 24, 2021, 5:02 p.m. UTC
Add support for testing whether bus voltage level translator is present
and operational. This is useful on systems where the bus voltage level
translator is optional, as the translator can be auto-detected by the
driver and the feedback clock functionality can be disabled if it is
not present.

This requires additional pinmux state, "init", where the CMD, CK, CKIN
lines are not configured, so they can be claimed as GPIOs early on in
probe(). The translator test sets CMD high to avoid interfering with a
card, and then verifies whether signal set on CK is detected on CKIN.
If the signal is detected, translator is present, otherwise the CKIN
feedback clock are disabled.

Tested-by: Yann Gautier <yann.gautier@foss.st.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Alexandre Torgue <alexandre.torgue@st.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Ludovic Barre <ludovic.barre@st.com>
Cc: Ulf Hansson <ulf.hansson@linaro.org>
Cc: linux-stm32@st-md-mailman.stormreply.com
---
V2: Rebase on next-20210122, add TB and RB
---
 drivers/mmc/host/mmci.c | 70 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 65 insertions(+), 5 deletions(-)

Comments

Ulf Hansson Jan. 26, 2021, 9:58 a.m. UTC | #1
On Sun, 24 Jan 2021 at 18:03, Marek Vasut <marex@denx.de> wrote:
>

> Add support for testing whether bus voltage level translator is present

> and operational. This is useful on systems where the bus voltage level

> translator is optional, as the translator can be auto-detected by the

> driver and the feedback clock functionality can be disabled if it is

> not present.

>

> This requires additional pinmux state, "init", where the CMD, CK, CKIN

> lines are not configured, so they can be claimed as GPIOs early on in

> probe(). The translator test sets CMD high to avoid interfering with a

> card, and then verifies whether signal set on CK is detected on CKIN.

> If the signal is detected, translator is present, otherwise the CKIN

> feedback clock are disabled.

>

> Tested-by: Yann Gautier <yann.gautier@foss.st.com>

> Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

> Signed-off-by: Marek Vasut <marex@denx.de>

> Cc: Alexandre Torgue <alexandre.torgue@st.com>

> Cc: Linus Walleij <linus.walleij@linaro.org>

> Cc: Ludovic Barre <ludovic.barre@st.com>

> Cc: Ulf Hansson <ulf.hansson@linaro.org>

> Cc: linux-stm32@st-md-mailman.stormreply.com


Applied for next, thanks!

Kind regards
Uffe


> ---

> V2: Rebase on next-20210122, add TB and RB

> ---

>  drivers/mmc/host/mmci.c | 70 ++++++++++++++++++++++++++++++++++++++---

>  1 file changed, 65 insertions(+), 5 deletions(-)

>

> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c

> index b5a41a7ce165..1bc674577ff9 100644

> --- a/drivers/mmc/host/mmci.c

> +++ b/drivers/mmc/host/mmci.c

> @@ -36,6 +36,7 @@

>  #include <linux/types.h>

>  #include <linux/pinctrl/consumer.h>

>  #include <linux/reset.h>

> +#include <linux/gpio/consumer.h>

>

>  #include <asm/div64.h>

>  #include <asm/io.h>

> @@ -1888,6 +1889,65 @@ static struct mmc_host_ops mmci_ops = {

>         .start_signal_voltage_switch = mmci_sig_volt_switch,

>  };

>

> +static void mmci_probe_level_translator(struct mmc_host *mmc)

> +{

> +       struct device *dev = mmc_dev(mmc);

> +       struct mmci_host *host = mmc_priv(mmc);

> +       struct gpio_desc *cmd_gpio;

> +       struct gpio_desc *ck_gpio;

> +       struct gpio_desc *ckin_gpio;

> +       int clk_hi, clk_lo;

> +

> +       /*

> +        * Assume the level translator is present if st,use-ckin is set.

> +        * This is to cater for DTs which do not implement this test.

> +        */

> +       host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;

> +

> +       cmd_gpio = gpiod_get(dev, "st,cmd", GPIOD_OUT_HIGH);

> +       if (IS_ERR(cmd_gpio))

> +               goto exit_cmd;

> +

> +       ck_gpio = gpiod_get(dev, "st,ck", GPIOD_OUT_HIGH);

> +       if (IS_ERR(ck_gpio))

> +               goto exit_ck;

> +

> +       ckin_gpio = gpiod_get(dev, "st,ckin", GPIOD_IN);

> +       if (IS_ERR(ckin_gpio))

> +               goto exit_ckin;

> +

> +       /* All GPIOs are valid, test whether level translator works */

> +

> +       /* Sample CKIN */

> +       clk_hi = !!gpiod_get_value(ckin_gpio);

> +

> +       /* Set CK low */

> +       gpiod_set_value(ck_gpio, 0);

> +

> +       /* Sample CKIN */

> +       clk_lo = !!gpiod_get_value(ckin_gpio);

> +

> +       /* Tristate all */

> +       gpiod_direction_input(cmd_gpio);

> +       gpiod_direction_input(ck_gpio);

> +

> +       /* Level translator is present if CK signal is propagated to CKIN */

> +       if (!clk_hi || clk_lo) {

> +               host->clk_reg_add &= ~MCI_STM32_CLK_SELCKIN;

> +               dev_warn(dev,

> +                        "Level translator inoperable, CK signal not detected on CKIN, disabling.\n");

> +       }

> +

> +       gpiod_put(ckin_gpio);

> +

> +exit_ckin:

> +       gpiod_put(ck_gpio);

> +exit_ck:

> +       gpiod_put(cmd_gpio);

> +exit_cmd:

> +       pinctrl_select_default_state(dev);

> +}

> +

>  static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)

>  {

>         struct mmci_host *host = mmc_priv(mmc);

> @@ -1913,7 +1973,7 @@ static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)

>         if (of_get_property(np, "st,neg-edge", NULL))

>                 host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE;

>         if (of_get_property(np, "st,use-ckin", NULL))

> -               host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;

> +               mmci_probe_level_translator(mmc);

>

>         if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL))

>                 mmc->caps |= MMC_CAP_MMC_HIGHSPEED;

> @@ -1949,15 +2009,15 @@ static int mmci_probe(struct amba_device *dev,

>         if (!mmc)

>                 return -ENOMEM;

>

> -       ret = mmci_of_parse(np, mmc);

> -       if (ret)

> -               goto host_free;

> -

>         host = mmc_priv(mmc);

>         host->mmc = mmc;

>         host->mmc_ops = &mmci_ops;

>         mmc->ops = &mmci_ops;

>

> +       ret = mmci_of_parse(np, mmc);

> +       if (ret)

> +               goto host_free;

> +

>         /*

>          * Some variant (STM32) doesn't have opendrain bit, nevertheless

>          * pins can be set accordingly using pinctrl

> --

> 2.29.2

>
diff mbox series

Patch

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index b5a41a7ce165..1bc674577ff9 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -36,6 +36,7 @@ 
 #include <linux/types.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/reset.h>
+#include <linux/gpio/consumer.h>
 
 #include <asm/div64.h>
 #include <asm/io.h>
@@ -1888,6 +1889,65 @@  static struct mmc_host_ops mmci_ops = {
 	.start_signal_voltage_switch = mmci_sig_volt_switch,
 };
 
+static void mmci_probe_level_translator(struct mmc_host *mmc)
+{
+	struct device *dev = mmc_dev(mmc);
+	struct mmci_host *host = mmc_priv(mmc);
+	struct gpio_desc *cmd_gpio;
+	struct gpio_desc *ck_gpio;
+	struct gpio_desc *ckin_gpio;
+	int clk_hi, clk_lo;
+
+	/*
+	 * Assume the level translator is present if st,use-ckin is set.
+	 * This is to cater for DTs which do not implement this test.
+	 */
+	host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;
+
+	cmd_gpio = gpiod_get(dev, "st,cmd", GPIOD_OUT_HIGH);
+	if (IS_ERR(cmd_gpio))
+		goto exit_cmd;
+
+	ck_gpio = gpiod_get(dev, "st,ck", GPIOD_OUT_HIGH);
+	if (IS_ERR(ck_gpio))
+		goto exit_ck;
+
+	ckin_gpio = gpiod_get(dev, "st,ckin", GPIOD_IN);
+	if (IS_ERR(ckin_gpio))
+		goto exit_ckin;
+
+	/* All GPIOs are valid, test whether level translator works */
+
+	/* Sample CKIN */
+	clk_hi = !!gpiod_get_value(ckin_gpio);
+
+	/* Set CK low */
+	gpiod_set_value(ck_gpio, 0);
+
+	/* Sample CKIN */
+	clk_lo = !!gpiod_get_value(ckin_gpio);
+
+	/* Tristate all */
+	gpiod_direction_input(cmd_gpio);
+	gpiod_direction_input(ck_gpio);
+
+	/* Level translator is present if CK signal is propagated to CKIN */
+	if (!clk_hi || clk_lo) {
+		host->clk_reg_add &= ~MCI_STM32_CLK_SELCKIN;
+		dev_warn(dev,
+			 "Level translator inoperable, CK signal not detected on CKIN, disabling.\n");
+	}
+
+	gpiod_put(ckin_gpio);
+
+exit_ckin:
+	gpiod_put(ck_gpio);
+exit_ck:
+	gpiod_put(cmd_gpio);
+exit_cmd:
+	pinctrl_select_default_state(dev);
+}
+
 static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)
 {
 	struct mmci_host *host = mmc_priv(mmc);
@@ -1913,7 +1973,7 @@  static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)
 	if (of_get_property(np, "st,neg-edge", NULL))
 		host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE;
 	if (of_get_property(np, "st,use-ckin", NULL))
-		host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;
+		mmci_probe_level_translator(mmc);
 
 	if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL))
 		mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
@@ -1949,15 +2009,15 @@  static int mmci_probe(struct amba_device *dev,
 	if (!mmc)
 		return -ENOMEM;
 
-	ret = mmci_of_parse(np, mmc);
-	if (ret)
-		goto host_free;
-
 	host = mmc_priv(mmc);
 	host->mmc = mmc;
 	host->mmc_ops = &mmci_ops;
 	mmc->ops = &mmci_ops;
 
+	ret = mmci_of_parse(np, mmc);
+	if (ret)
+		goto host_free;
+
 	/*
 	 * Some variant (STM32) doesn't have opendrain bit, nevertheless
 	 * pins can be set accordingly using pinctrl