regulator: userspace-consumer: Handle regulator-output DT nodes
authorZev Weiss <zev@bewilderbeest.net>
Mon, 31 Oct 2022 23:37:04 +0000 (16:37 -0700)
committerMark Brown <broonie@kernel.org>
Thu, 3 Nov 2022 13:35:06 +0000 (13:35 +0000)
In addition to adding some fairly simple OF support code, we make some
slight adjustments to the userspace-consumer driver to properly
support use with regulator-output hardware:

 - We now do an exclusive get of the supply regulators so as to
   prevent regulator_init_complete_work from automatically disabling
   them.

 - Instead of assuming that the supply is initially disabled, we now
   query its state to determine the initial value of drvdata->enabled.

Signed-off-by: Zev Weiss <zev@bewilderbeest.net>
Link: https://lore.kernel.org/r/20221031233704.22575-4-zev@bewilderbeest.net
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/regulator/userspace-consumer.c
include/linux/regulator/userspace-consumer.h

index 8ca2866..402c803 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/userspace-consumer.h>
@@ -24,6 +25,7 @@ struct userspace_consumer_data {
 
        struct mutex lock;
        bool enabled;
+       bool no_autoswitch;
 
        int num_supplies;
        struct regulator_bulk_data *supplies;
@@ -96,19 +98,50 @@ static struct attribute *attributes[] = {
        NULL,
 };
 
+static umode_t attr_visible(struct kobject *kobj, struct attribute *attr, int idx)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct userspace_consumer_data *data = dev_get_drvdata(dev);
+
+       /* If a name hasn't been set, don't bother with the attribute */
+       if (attr == &dev_attr_name.attr && !data->name)
+               return 0;
+
+       return attr->mode;
+}
+
 static const struct attribute_group attr_group = {
        .attrs  = attributes,
+       .is_visible =  attr_visible,
 };
 
 static int regulator_userspace_consumer_probe(struct platform_device *pdev)
 {
+       struct regulator_userspace_consumer_data tmpdata;
        struct regulator_userspace_consumer_data *pdata;
        struct userspace_consumer_data *drvdata;
        int ret;
 
        pdata = dev_get_platdata(&pdev->dev);
-       if (!pdata)
+       if (!pdata) {
+               if (!pdev->dev.of_node)
+                       return -EINVAL;
+
+               pdata = &tmpdata;
+               memset(pdata, 0, sizeof(*pdata));
+
+               pdata->no_autoswitch = true;
+               pdata->num_supplies = 1;
+               pdata->supplies = devm_kzalloc(&pdev->dev, sizeof(*pdata->supplies), GFP_KERNEL);
+               if (!pdata->supplies)
+                       return -ENOMEM;
+               pdata->supplies[0].supply = "vout";
+       }
+
+       if (pdata->num_supplies < 1) {
+               dev_err(&pdev->dev, "At least one supply required\n");
                return -EINVAL;
+       }
 
        drvdata = devm_kzalloc(&pdev->dev,
                               sizeof(struct userspace_consumer_data),
@@ -119,21 +152,24 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
        drvdata->name = pdata->name;
        drvdata->num_supplies = pdata->num_supplies;
        drvdata->supplies = pdata->supplies;
+       drvdata->no_autoswitch = pdata->no_autoswitch;
 
        mutex_init(&drvdata->lock);
 
-       ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies,
-                                     drvdata->supplies);
+       ret = devm_regulator_bulk_get_exclusive(&pdev->dev, drvdata->num_supplies,
+                                               drvdata->supplies);
        if (ret) {
                dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret);
                return ret;
        }
 
+       platform_set_drvdata(pdev, drvdata);
+
        ret = sysfs_create_group(&pdev->dev.kobj, &attr_group);
        if (ret != 0)
                return ret;
 
-       if (pdata->init_on) {
+       if (pdata->init_on && !pdata->no_autoswitch) {
                ret = regulator_bulk_enable(drvdata->num_supplies,
                                            drvdata->supplies);
                if (ret) {
@@ -143,8 +179,12 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
                }
        }
 
-       drvdata->enabled = pdata->init_on;
-       platform_set_drvdata(pdev, drvdata);
+       ret = regulator_is_enabled(pdata->supplies[0].consumer);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to get regulator status\n");
+               goto err_enable;
+       }
+       drvdata->enabled = !!ret;
 
        return 0;
 
@@ -160,17 +200,23 @@ static int regulator_userspace_consumer_remove(struct platform_device *pdev)
 
        sysfs_remove_group(&pdev->dev.kobj, &attr_group);
 
-       if (data->enabled)
+       if (data->enabled && !data->no_autoswitch)
                regulator_bulk_disable(data->num_supplies, data->supplies);
 
        return 0;
 }
 
+static const struct of_device_id regulator_userspace_consumer_of_match[] = {
+       { .compatible = "regulator-output", },
+       {},
+};
+
 static struct platform_driver regulator_userspace_consumer_driver = {
        .probe          = regulator_userspace_consumer_probe,
        .remove         = regulator_userspace_consumer_remove,
        .driver         = {
                .name           = "reg-userspace-consumer",
+               .of_match_table = regulator_userspace_consumer_of_match,
        },
 };
 
index b5dba06..2249ee6 100644 (file)
@@ -21,6 +21,7 @@ struct regulator_userspace_consumer_data {
        struct regulator_bulk_data *supplies;
 
        bool init_on;
+       bool no_autoswitch;
 };
 
 #endif /* __REGULATOR_PLATFORM_CONSUMER_H_ */