diff options
author | Zev Weiss <zev@bewilderbeest.net> | 2022-11-01 02:37:04 +0300 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2022-11-03 16:35:06 +0300 |
commit | 5c51d4afcf3fd36159713556402e16cfab794ae9 (patch) | |
tree | 0c702c68c1b631e042ddc8e23cf776650b40806c /drivers | |
parent | 14b8ad4c2580231fc45c2313ef822a15bb12f63f (diff) | |
download | linux-5c51d4afcf3fd36159713556402e16cfab794ae9.tar.xz |
regulator: userspace-consumer: Handle regulator-output DT nodes
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/regulator/userspace-consumer.c | 60 |
1 files changed, 53 insertions, 7 deletions
diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c index 8ca28664776e..402c8037cf39 100644 --- a/drivers/regulator/userspace-consumer.c +++ b/drivers/regulator/userspace-consumer.c @@ -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, }, }; |