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 |
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 --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