Commit 3078d0c3 authored by Mark Brown's avatar Mark Brown

regulator: Add DT support for regulator-output connectors

Merge series from Zev Weiss <zev@bewilderbeest.net>:

This series adds support for userspace-controlled regulator-supplied
power outputs [2].  This is an important feature for some kinds of BMC
(baseboard management controller) systems where the BMC provides an
operator with manual control of a set of DC power outputs.

As in a broadly similar patchset that was recently almost merged [0],
this takes the approach of providing support by extending the existing
userspace-consumer regulator driver.  A couple questions about the
userspace-consumer driver came up along the way, however.

First, how (if at all) is it currently being used?  It appears the
last in-tree use of it was removed a bit over two years ago in commit
9d323914 ("ARM: pxa: remove Compulab pxa2xx boards").  Aside from
just adding DT support I've made a couple small tweaks to the driver
in patch 3 that I hope are compatible with any current usage, but
without any extant examples to look at it's kind of hard to say.

Second, how critical is its support for controlling multiple
regulators?  (i.e. its use of regulator_bulk_data and friends instead
of a single struct regulator.)  As far as I can see every in-tree use
of it that's ever existed has used num_supplies = 1.  If it's not
important to retain, patch 1 of this series could be supplanted by one
that instead simplifies the driver slightly by removing that
functionality.

The DT binding added in patch 2 is essentially identical to one I
posted in a previous patchset that had an R-B from Rob [1], but has
had some minor rewording and been moved from the extcon subsystem to
the regulator subsystem.
parents be847537 5c51d4af
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/regulator/regulator-output.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Regulator output connector
maintainers:
- Zev Weiss <zev@bewilderbeest.net>
description: |
This describes a power output connector supplied by a regulator,
such as a power outlet on a power distribution unit (PDU). The
connector may be standalone or merely one channel or set of pins
within a ganged physical connector carrying multiple independent
power outputs.
properties:
compatible:
const: regulator-output
vout-supply:
description:
Phandle of the regulator supplying the output.
required:
- compatible
- vout-supply
additionalProperties: false
examples:
- |
output {
compatible = "regulator-output";
vout-supply = <&output_reg>;
};
...@@ -4778,22 +4778,8 @@ static int _notifier_call_chain(struct regulator_dev *rdev, ...@@ -4778,22 +4778,8 @@ static int _notifier_call_chain(struct regulator_dev *rdev,
return blocking_notifier_call_chain(&rdev->notifier, event, data); return blocking_notifier_call_chain(&rdev->notifier, event, data);
} }
/** int _regulator_bulk_get(struct device *dev, int num_consumers,
* regulator_bulk_get - get multiple regulator consumers struct regulator_bulk_data *consumers, enum regulator_get_type get_type)
*
* @dev: Device to supply
* @num_consumers: Number of consumers to register
* @consumers: Configuration of consumers; clients are stored here.
*
* @return 0 on success, an errno on failure.
*
* This helper function allows drivers to get several regulator
* consumers in one operation. If any of the regulators cannot be
* acquired then any regulators that were allocated will be freed
* before returning to the caller.
*/
int regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers)
{ {
int i; int i;
int ret; int ret;
...@@ -4802,8 +4788,8 @@ int regulator_bulk_get(struct device *dev, int num_consumers, ...@@ -4802,8 +4788,8 @@ int regulator_bulk_get(struct device *dev, int num_consumers,
consumers[i].consumer = NULL; consumers[i].consumer = NULL;
for (i = 0; i < num_consumers; i++) { for (i = 0; i < num_consumers; i++) {
consumers[i].consumer = regulator_get(dev, consumers[i].consumer = _regulator_get(dev,
consumers[i].supply); consumers[i].supply, get_type);
if (IS_ERR(consumers[i].consumer)) { if (IS_ERR(consumers[i].consumer)) {
ret = dev_err_probe(dev, PTR_ERR(consumers[i].consumer), ret = dev_err_probe(dev, PTR_ERR(consumers[i].consumer),
"Failed to get supply '%s'", "Failed to get supply '%s'",
...@@ -4830,6 +4816,26 @@ int regulator_bulk_get(struct device *dev, int num_consumers, ...@@ -4830,6 +4816,26 @@ int regulator_bulk_get(struct device *dev, int num_consumers,
return ret; return ret;
} }
/**
* regulator_bulk_get - get multiple regulator consumers
*
* @dev: Device to supply
* @num_consumers: Number of consumers to register
* @consumers: Configuration of consumers; clients are stored here.
*
* @return 0 on success, an errno on failure.
*
* This helper function allows drivers to get several regulator
* consumers in one operation. If any of the regulators cannot be
* acquired then any regulators that were allocated will be freed
* before returning to the caller.
*/
int regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers)
{
return _regulator_bulk_get(dev, num_consumers, consumers, NORMAL_GET);
}
EXPORT_SYMBOL_GPL(regulator_bulk_get); EXPORT_SYMBOL_GPL(regulator_bulk_get);
static void regulator_bulk_enable_async(void *data, async_cookie_t cookie) static void regulator_bulk_enable_async(void *data, async_cookie_t cookie)
......
...@@ -186,23 +186,9 @@ static void devm_regulator_bulk_release(struct device *dev, void *res) ...@@ -186,23 +186,9 @@ static void devm_regulator_bulk_release(struct device *dev, void *res)
regulator_bulk_free(devres->num_consumers, devres->consumers); regulator_bulk_free(devres->num_consumers, devres->consumers);
} }
/** static int _devm_regulator_bulk_get(struct device *dev, int num_consumers,
* devm_regulator_bulk_get - managed get multiple regulator consumers struct regulator_bulk_data *consumers,
* enum regulator_get_type get_type)
* @dev: device to supply
* @num_consumers: number of consumers to register
* @consumers: configuration of consumers; clients are stored here.
*
* @return 0 on success, an errno on failure.
*
* This helper function allows drivers to get several regulator
* consumers in one operation with management, the regulators will
* automatically be freed when the device is unbound. If any of the
* regulators cannot be acquired then any regulators that were
* allocated will be freed before returning to the caller.
*/
int devm_regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers)
{ {
struct regulator_bulk_devres *devres; struct regulator_bulk_devres *devres;
int ret; int ret;
...@@ -212,7 +198,7 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers, ...@@ -212,7 +198,7 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers,
if (!devres) if (!devres)
return -ENOMEM; return -ENOMEM;
ret = regulator_bulk_get(dev, num_consumers, consumers); ret = _regulator_bulk_get(dev, num_consumers, consumers, get_type);
if (!ret) { if (!ret) {
devres->consumers = consumers; devres->consumers = consumers;
devres->num_consumers = num_consumers; devres->num_consumers = num_consumers;
...@@ -223,8 +209,52 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers, ...@@ -223,8 +209,52 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers,
return ret; return ret;
} }
/**
* devm_regulator_bulk_get - managed get multiple regulator consumers
*
* @dev: device to supply
* @num_consumers: number of consumers to register
* @consumers: configuration of consumers; clients are stored here.
*
* @return 0 on success, an errno on failure.
*
* This helper function allows drivers to get several regulator
* consumers in one operation with management, the regulators will
* automatically be freed when the device is unbound. If any of the
* regulators cannot be acquired then any regulators that were
* allocated will be freed before returning to the caller.
*/
int devm_regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers)
{
return _devm_regulator_bulk_get(dev, num_consumers, consumers, NORMAL_GET);
}
EXPORT_SYMBOL_GPL(devm_regulator_bulk_get); EXPORT_SYMBOL_GPL(devm_regulator_bulk_get);
/**
* devm_regulator_bulk_get_exclusive - managed exclusive get of multiple
* regulator consumers
*
* @dev: device to supply
* @num_consumers: number of consumers to register
* @consumers: configuration of consumers; clients are stored here.
*
* @return 0 on success, an errno on failure.
*
* This helper function allows drivers to exclusively get several
* regulator consumers in one operation with management, the regulators
* will automatically be freed when the device is unbound. If any of
* the regulators cannot be acquired then any regulators that were
* allocated will be freed before returning to the caller.
*/
int devm_regulator_bulk_get_exclusive(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers)
{
return _devm_regulator_bulk_get(dev, num_consumers, consumers, EXCLUSIVE_GET);
}
EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_exclusive);
/** /**
* devm_regulator_bulk_get_const - devm_regulator_bulk_get() w/ const data * devm_regulator_bulk_get_const - devm_regulator_bulk_get() w/ const data
* *
......
...@@ -122,4 +122,6 @@ enum regulator_get_type { ...@@ -122,4 +122,6 @@ enum regulator_get_type {
struct regulator *_regulator_get(struct device *dev, const char *id, struct regulator *_regulator_get(struct device *dev, const char *id,
enum regulator_get_type get_type); enum regulator_get_type get_type);
int _regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers, enum regulator_get_type get_type);
#endif #endif
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/regulator/userspace-consumer.h> #include <linux/regulator/userspace-consumer.h>
...@@ -24,6 +25,7 @@ struct userspace_consumer_data { ...@@ -24,6 +25,7 @@ struct userspace_consumer_data {
struct mutex lock; struct mutex lock;
bool enabled; bool enabled;
bool no_autoswitch;
int num_supplies; int num_supplies;
struct regulator_bulk_data *supplies; struct regulator_bulk_data *supplies;
...@@ -96,19 +98,50 @@ static struct attribute *attributes[] = { ...@@ -96,19 +98,50 @@ static struct attribute *attributes[] = {
NULL, 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 = { static const struct attribute_group attr_group = {
.attrs = attributes, .attrs = attributes,
.is_visible = attr_visible,
}; };
static int regulator_userspace_consumer_probe(struct platform_device *pdev) static int regulator_userspace_consumer_probe(struct platform_device *pdev)
{ {
struct regulator_userspace_consumer_data tmpdata;
struct regulator_userspace_consumer_data *pdata; struct regulator_userspace_consumer_data *pdata;
struct userspace_consumer_data *drvdata; struct userspace_consumer_data *drvdata;
int ret; int ret;
pdata = dev_get_platdata(&pdev->dev); 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; return -EINVAL;
}
drvdata = devm_kzalloc(&pdev->dev, drvdata = devm_kzalloc(&pdev->dev,
sizeof(struct userspace_consumer_data), sizeof(struct userspace_consumer_data),
...@@ -119,21 +152,24 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev) ...@@ -119,21 +152,24 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
drvdata->name = pdata->name; drvdata->name = pdata->name;
drvdata->num_supplies = pdata->num_supplies; drvdata->num_supplies = pdata->num_supplies;
drvdata->supplies = pdata->supplies; drvdata->supplies = pdata->supplies;
drvdata->no_autoswitch = pdata->no_autoswitch;
mutex_init(&drvdata->lock); mutex_init(&drvdata->lock);
ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies, ret = devm_regulator_bulk_get_exclusive(&pdev->dev, drvdata->num_supplies,
drvdata->supplies); drvdata->supplies);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret); dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret);
return ret; return ret;
} }
platform_set_drvdata(pdev, drvdata);
ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); ret = sysfs_create_group(&pdev->dev.kobj, &attr_group);
if (ret != 0) if (ret != 0)
return ret; return ret;
if (pdata->init_on) { if (pdata->init_on && !pdata->no_autoswitch) {
ret = regulator_bulk_enable(drvdata->num_supplies, ret = regulator_bulk_enable(drvdata->num_supplies,
drvdata->supplies); drvdata->supplies);
if (ret) { if (ret) {
...@@ -143,8 +179,12 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev) ...@@ -143,8 +179,12 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
} }
} }
drvdata->enabled = pdata->init_on; ret = regulator_is_enabled(pdata->supplies[0].consumer);
platform_set_drvdata(pdev, drvdata); if (ret < 0) {
dev_err(&pdev->dev, "Failed to get regulator status\n");
goto err_enable;
}
drvdata->enabled = !!ret;
return 0; return 0;
...@@ -160,17 +200,23 @@ static int regulator_userspace_consumer_remove(struct platform_device *pdev) ...@@ -160,17 +200,23 @@ static int regulator_userspace_consumer_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &attr_group); 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); regulator_bulk_disable(data->num_supplies, data->supplies);
return 0; return 0;
} }
static const struct of_device_id regulator_userspace_consumer_of_match[] = {
{ .compatible = "regulator-output", },
{},
};
static struct platform_driver regulator_userspace_consumer_driver = { static struct platform_driver regulator_userspace_consumer_driver = {
.probe = regulator_userspace_consumer_probe, .probe = regulator_userspace_consumer_probe,
.remove = regulator_userspace_consumer_remove, .remove = regulator_userspace_consumer_remove,
.driver = { .driver = {
.name = "reg-userspace-consumer", .name = "reg-userspace-consumer",
.of_match_table = regulator_userspace_consumer_of_match,
}, },
}; };
......
...@@ -247,6 +247,8 @@ int __must_check regulator_bulk_get(struct device *dev, int num_consumers, ...@@ -247,6 +247,8 @@ int __must_check regulator_bulk_get(struct device *dev, int num_consumers,
int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers, int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers); struct regulator_bulk_data *consumers);
void devm_regulator_bulk_put(struct regulator_bulk_data *consumers); void devm_regulator_bulk_put(struct regulator_bulk_data *consumers);
int __must_check devm_regulator_bulk_get_exclusive(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers);
int __must_check devm_regulator_bulk_get_const( int __must_check devm_regulator_bulk_get_const(
struct device *dev, int num_consumers, struct device *dev, int num_consumers,
const struct regulator_bulk_data *in_consumers, const struct regulator_bulk_data *in_consumers,
......
...@@ -21,6 +21,7 @@ struct regulator_userspace_consumer_data { ...@@ -21,6 +21,7 @@ struct regulator_userspace_consumer_data {
struct regulator_bulk_data *supplies; struct regulator_bulk_data *supplies;
bool init_on; bool init_on;
bool no_autoswitch;
}; };
#endif /* __REGULATOR_PLATFORM_CONSUMER_H_ */ #endif /* __REGULATOR_PLATFORM_CONSUMER_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment