On Sun, 24 Jan 2021 at 18:03, Marek Vasut <marex@xxxxxxx> 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@xxxxxxxxxxx> > Reviewed-by: Linus Walleij <linus.walleij@xxxxxxxxxx> > Signed-off-by: Marek Vasut <marex@xxxxxxx> > Cc: Alexandre Torgue <alexandre.torgue@xxxxxx> > Cc: Linus Walleij <linus.walleij@xxxxxxxxxx> > Cc: Ludovic Barre <ludovic.barre@xxxxxx> > Cc: Ulf Hansson <ulf.hansson@xxxxxxxxxx> > Cc: linux-stm32@xxxxxxxxxxxxxxxxxxxxxxxxxxxx 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 >