mfd: madera: Register pinctrl early and add custom states
authorRichard Fitzgerald <rf@opensource.cirrus.com>
Wed, 28 Feb 2018 10:49:40 +0000 (10:49 +0000)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:22:29 +0000 (20:22 +0300)
The pinctrl subsystem tries to bind all referenced pinctrl
settings in a driver's devicetree node before probing it.
If any of the pins don't exist the entire configuration will
be ignored.

Our pinctrl driver is a child of the mfd so will never exist
at this point, so the pinctrl setup will be abandoned. This
means any external pinctrl dependencies, such as the GPIO
controlling reset, won't be applied before the mfd starts probe.

This can be avoided by registering the pinctrl driver early
and then calling pinctrl_get() to cause another evaluation
of pinctrl bindings for this driver. For this to work we
cannot use the "default" pinctrl state because that will be
applied before we have probed the codec sufficiently to
actually apply any settings. Instead there are two new
explicit pinctrl states:

"probe" - external dependencies needed to probe the codec but
excluding the configuration of our own GPIOs. More
generically this excludes pinctrl settings that cannot
be applied until after mfd has probed.

"active" - applied after the probe is completed, includes
all the primary dependencies in "probe" plus any
configuration of our own pinctrl.

Change-Id: I5eab97aa73b9d25edaf6aec3b6922fdcfc3a60a7
Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com>
Documentation/devicetree/bindings/mfd/madera.txt
drivers/mfd/madera-core.c

index d39a15899cc038ed9e87ce307ffd077a735378ff..97680217aa1af6f1949e9d9139509364485e6991 100644 (file)
@@ -57,6 +57,26 @@ Required properties:
   - interrupts : The interrupt line the /IRQ signal for the device is
     connected to.
 
+  - pinctrl-names : Name of pinctrl configurations. Each of these is optional.
+    Legal names are:
+       "probe" - external pinctrl dependencies required to probe this codec,
+               typically these are for the reset GPIO and IRQ pin, though
+               could include clocks. Do NOT include configuration of this
+               codec's own GPIO pins because these can't be applied until
+               after the mfd probe has completed.
+
+       "active" - full set of external and local pinctrl configurations to
+               apply after probe including all the configuration of the GPIO
+               pins on this codec (bindings/pinctrl/cirrus,madera-pinctrl.txt).
+               This must include the external dependencies in the "probe"
+               configuration, since they will still be required after probe.
+
+    Do not use "default" since it is applied too early before the mfd has
+    fully probed the codec.
+
+  - pinctrl-0, pinctrl-1 : handle to pinctrl configurations matching the
+    entries in pinctrl-names
+
 Optional properties:
 
   - MICVDD-supply : Power supply, only need to be specified if
@@ -103,6 +123,18 @@ cs47l85@0 {
        gpio-controller;
        #gpio-cells = <2>;
 
+       pinctrl-names = "probe", "active";
+       pinctrl-0 = <&gpio_0_enable_state>;
+       pinctrl-1 = <&gpio_0_enable_state &cs47l85_gpio_config>;
+
+       cs47l85_gpio_config: cs47l85-gpio-config {
+               aif1 {
+                       groups = "aif1";
+                       function = "aif1";
+                       bias-bus-hold;
+               };
+       };
+
        MICBIAS1 {
                regulator-min-microvolt = <900000>;
                regulator-max-microvolt = <3300000>;
index 96325d8f681b8f9db84d8f013554ead5d2f1ede7..783c2c9b92568fe059a2cf0b025e899dbd88e338 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
@@ -48,6 +49,10 @@ static const struct mfd_cell madera_ldo1_devs[] = {
        { .name = "madera-ldo1" },
 };
 
+static const struct mfd_cell madera_pinctrl_dev[] = {
+       { .name = "madera-pinctrl", },
+};
+
 static const char * const cs47l15_supplies[] = {
        "MICVDD",
        "CPVDD1",
@@ -55,7 +60,6 @@ static const char * const cs47l15_supplies[] = {
 };
 
 static const struct mfd_cell cs47l15_devs[] = {
-       { .name = "madera-pinctrl", },
        { .name = "madera-irq" },
        { .name = "madera-gpio" },
        {
@@ -79,7 +83,6 @@ static const char * const cs47l35_supplies[] = {
 };
 
 static const struct mfd_cell cs47l35_devs[] = {
-       { .name = "madera-pinctrl", },
        { .name = "madera-irq", },
        { .name = "madera-micsupp" },
        { .name = "madera-gpio", },
@@ -107,7 +110,6 @@ static const char * const cs47l85_supplies[] = {
 };
 
 static const struct mfd_cell cs47l85_devs[] = {
-       { .name = "madera-pinctrl", },
        { .name = "madera-irq", },
        { .name = "madera-micsupp", },
        { .name = "madera-gpio", },
@@ -133,7 +135,6 @@ static const char * const cs47l90_supplies[] = {
 };
 
 static const struct mfd_cell cs47l90_devs[] = {
-       { .name = "madera-pinctrl", },
        { .name = "madera-irq", },
        { .name = "madera-micsupp", },
        { .name = "madera-gpio", },
@@ -156,7 +157,6 @@ static const char * const cs47l92_supplies[] = {
 };
 
 static const struct mfd_cell cs47l92_devs[] = {
-       { .name = "madera-pinctrl" },
        { .name = "madera-irq", },
        { .name = "madera-micsupp", },
        { .name = "madera-gpio" },
@@ -600,6 +600,30 @@ static void madera_configure_micbias(struct madera *madera)
        }
 }
 
+static int madera_dev_select_pinctrl(struct madera *madera,
+                                    struct pinctrl *pinctrl,
+                                    const char *name)
+{
+       struct pinctrl_state *pinctrl_state;
+       int ret;
+
+       pinctrl_state = pinctrl_lookup_state(pinctrl, name);
+
+       /* it's ok if it doesn't exist */
+       if (!IS_ERR(pinctrl_state)) {
+               dev_dbg(madera->dev, "Applying pinctrl %s state\n", name);
+               ret = pinctrl_select_state(pinctrl, pinctrl_state);
+               if (ret) {
+                       dev_err(madera->dev,
+                               "Failed to select pinctrl %s state: %d\n",
+                               name, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 int madera_dev_init(struct madera *madera)
 {
        struct device *dev = madera->dev;
@@ -607,10 +631,38 @@ int madera_dev_init(struct madera *madera)
        unsigned int hwid;
        int (*patch_fn)(struct madera *) = NULL;
        const struct mfd_cell *mfd_devs;
+       struct pinctrl *pinctrl;
        int n_devs = 0;
        int i, ret;
 
        dev_set_drvdata(madera->dev, madera);
+
+       /*
+        * Pinctrl subsystem only configures pinctrls if all referenced pins
+        * are registered. Create our pinctrl child now so that its pins exist
+        * otherwise external pinctrl dependencies will fail
+        * Note: Can't devm_ because it is cleaned up after children are already
+        * destroyed
+        */
+       ret = mfd_add_devices(madera->dev, PLATFORM_DEVID_NONE,
+                             madera_pinctrl_dev, 1, NULL, 0, NULL);
+       if (ret) {
+               dev_err(madera->dev, "Failed to add pinctrl child: %d\n", ret);
+               return ret;
+       }
+
+       pinctrl = pinctrl_get(dev);
+       if (IS_ERR(pinctrl)) {
+               ret = PTR_ERR(pinctrl);
+               dev_err(madera->dev, "Failed to get pinctrl: %d\n", ret);
+               goto err_devs;
+       }
+
+       /* Use (optional) minimal config with only external pin bindings */
+       ret = madera_dev_select_pinctrl(madera, pinctrl, "probe");
+       if (ret)
+               goto err_pinctrl;
+
        BLOCKING_INIT_NOTIFIER_HEAD(&madera->notifier);
 
        if (dev_get_platdata(madera->dev)) {
@@ -620,7 +672,7 @@ int madera_dev_init(struct madera *madera)
 
        ret = madera_get_reset_gpio(madera);
        if (ret)
-               return ret;
+               goto err_pinctrl;
 
        madera_prop_get_micbias(madera);
 
@@ -649,19 +701,20 @@ int madera_dev_init(struct madera *madera)
                                      NULL, 0, NULL);
                if (ret) {
                        dev_err(dev, "Failed to add LDO1 child: %d\n", ret);
-                       return ret;
+                       goto err_pinctrl;
                }
                break;
        default:
                dev_err(madera->dev, "Unknown device type %d\n", madera->type);
-               return -ENODEV;
+               ret = -ENODEV;
+               goto err_pinctrl;
        }
 
        ret = devm_regulator_bulk_get(dev, madera->num_core_supplies,
                                      madera->core_supplies);
        if (ret) {
                dev_err(dev, "Failed to request core supplies: %d\n", ret);
-               goto err_devs;
+               goto err_pinctrl;
        }
 
        /*
@@ -674,7 +727,7 @@ int madera_dev_init(struct madera *madera)
        if (IS_ERR(madera->dcvdd)) {
                ret = PTR_ERR(madera->dcvdd);
                dev_err(dev, "Failed to request DCVDD: %d\n", ret);
-               goto err_devs;
+               goto err_pinctrl;
        }
 
        ret = regulator_bulk_enable(madera->num_core_supplies,
@@ -831,6 +884,11 @@ int madera_dev_init(struct madera *madera)
                }
        }
 
+       /* Apply (optional) main pinctrl config, this will configure our pins */
+       ret = madera_dev_select_pinctrl(madera, pinctrl, "active");
+       if (ret)
+               goto err_reset;
+
        /* Init 32k clock sourced from MCLK2 */
        ret = regmap_update_bits(madera->regmap,
                        MADERA_CLOCK_32K_1,
@@ -856,6 +914,8 @@ int madera_dev_init(struct madera *madera)
                goto err_pm_runtime;
        }
 
+       pinctrl_put(pinctrl);
+
        return 0;
 
 err_pm_runtime:
@@ -868,6 +928,8 @@ err_enable:
                               madera->core_supplies);
 err_dcvdd:
        regulator_put(madera->dcvdd);
+err_pinctrl:
+       pinctrl_put(pinctrl);
 err_devs:
        mfd_remove_devices(dev);