summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/host/mmci.c70
1 files 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