196 lines
5.4 KiB
Diff
196 lines
5.4 KiB
Diff
|
From 9e2fd36e02a0a638424587fe1d9e63ff2b19cdd6 Mon Sep 17 00:00:00 2001
|
||
|
From: Ondrej Jirman <megous@megous.com>
|
||
|
Date: Sat, 5 Oct 2019 15:10:11 +0200
|
||
|
Subject: [PATCH 192/478] regulator: Add simple driver for enabling a regulator
|
||
|
from userspace
|
||
|
|
||
|
This is just like userspace-consumer, but can be used from device
|
||
|
tree.
|
||
|
|
||
|
Signed-off-by: Ondrej Jirman <megous@megous.com>
|
||
|
---
|
||
|
drivers/regulator/Kconfig | 9 ++
|
||
|
drivers/regulator/Makefile | 1 +
|
||
|
drivers/regulator/userspace-consumer-of.c | 137 ++++++++++++++++++++++
|
||
|
3 files changed, 147 insertions(+)
|
||
|
create mode 100644 drivers/regulator/userspace-consumer-of.c
|
||
|
|
||
|
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
|
||
|
index 4fd13b06231f..271f73561125 100644
|
||
|
--- a/drivers/regulator/Kconfig
|
||
|
+++ b/drivers/regulator/Kconfig
|
||
|
@@ -56,6 +56,15 @@ config REGULATOR_USERSPACE_CONSUMER
|
||
|
|
||
|
If unsure, say no.
|
||
|
|
||
|
+config REGULATOR_USERSPACE_CONSUMER_OF
|
||
|
+ tristate "Userspace regulator consumer support (OF)"
|
||
|
+ help
|
||
|
+ There are some classes of devices that are controlled entirely
|
||
|
+ from user space. Userspace consumer driver provides ability to
|
||
|
+ control power supplies for such devices.
|
||
|
+
|
||
|
+ If unsure, say no.
|
||
|
+
|
||
|
config REGULATOR_88PG86X
|
||
|
tristate "Marvell 88PG86X voltage regulators"
|
||
|
depends on I2C
|
||
|
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
|
||
|
index 9e382b50a5ef..3a7af4b039c5 100644
|
||
|
--- a/drivers/regulator/Makefile
|
||
|
+++ b/drivers/regulator/Makefile
|
||
|
@@ -9,6 +9,7 @@ obj-$(CONFIG_OF) += of_regulator.o
|
||
|
obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
|
||
|
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
|
||
|
obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
|
||
|
+obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER_OF) += userspace-consumer-of.o
|
||
|
|
||
|
obj-$(CONFIG_REGULATOR_88PG86X) += 88pg86x.o
|
||
|
obj-$(CONFIG_REGULATOR_88PM800) += 88pm800-regulator.o
|
||
|
diff --git a/drivers/regulator/userspace-consumer-of.c b/drivers/regulator/userspace-consumer-of.c
|
||
|
new file mode 100644
|
||
|
index 000000000000..5b835af17bea
|
||
|
--- /dev/null
|
||
|
+++ b/drivers/regulator/userspace-consumer-of.c
|
||
|
@@ -0,0 +1,137 @@
|
||
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
+
|
||
|
+#include <linux/err.h>
|
||
|
+#include <linux/mutex.h>
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/platform_device.h>
|
||
|
+#include <linux/regulator/consumer.h>
|
||
|
+#include <linux/slab.h>
|
||
|
+#include <linux/of.h>
|
||
|
+
|
||
|
+struct userspace_consumer_data {
|
||
|
+ struct mutex lock;
|
||
|
+ bool enabled;
|
||
|
+ struct regulator *supply;
|
||
|
+};
|
||
|
+
|
||
|
+static ssize_t reg_show_state(struct device *dev,
|
||
|
+ struct device_attribute *attr, char *buf)
|
||
|
+{
|
||
|
+ struct userspace_consumer_data *data = dev_get_drvdata(dev);
|
||
|
+
|
||
|
+ if (data->enabled)
|
||
|
+ return sprintf(buf, "enabled\n");
|
||
|
+
|
||
|
+ return sprintf(buf, "disabled\n");
|
||
|
+}
|
||
|
+
|
||
|
+static ssize_t reg_set_state(struct device *dev, struct device_attribute *attr,
|
||
|
+ const char *buf, size_t count)
|
||
|
+{
|
||
|
+ struct userspace_consumer_data *data = dev_get_drvdata(dev);
|
||
|
+ bool enabled;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * sysfs_streq() doesn't need the \n's, but we add them so the strings
|
||
|
+ * will be shared with show_state(), above.
|
||
|
+ */
|
||
|
+ if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1"))
|
||
|
+ enabled = true;
|
||
|
+ else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0"))
|
||
|
+ enabled = false;
|
||
|
+ else {
|
||
|
+ dev_err(dev, "Configuring invalid mode\n");
|
||
|
+ return count;
|
||
|
+ }
|
||
|
+
|
||
|
+ mutex_lock(&data->lock);
|
||
|
+ if (enabled != data->enabled) {
|
||
|
+ if (enabled)
|
||
|
+ ret = regulator_enable(data->supply);
|
||
|
+ else
|
||
|
+ ret = regulator_disable(data->supply);
|
||
|
+
|
||
|
+ if (ret == 0)
|
||
|
+ data->enabled = enabled;
|
||
|
+ else
|
||
|
+ dev_err(dev, "Failed to configure state: %d\n", ret);
|
||
|
+ }
|
||
|
+ mutex_unlock(&data->lock);
|
||
|
+
|
||
|
+ return count;
|
||
|
+}
|
||
|
+
|
||
|
+static DEVICE_ATTR(state, 0644, reg_show_state, reg_set_state);
|
||
|
+
|
||
|
+static struct attribute *attributes[] = {
|
||
|
+ &dev_attr_state.attr,
|
||
|
+ NULL,
|
||
|
+};
|
||
|
+
|
||
|
+static const struct attribute_group attr_group = {
|
||
|
+ .attrs = attributes,
|
||
|
+};
|
||
|
+
|
||
|
+static int regulator_userspace_consumer_probe(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct userspace_consumer_data *drvdata;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ drvdata = devm_kzalloc(&pdev->dev,
|
||
|
+ sizeof(struct userspace_consumer_data),
|
||
|
+ GFP_KERNEL);
|
||
|
+ if (drvdata == NULL)
|
||
|
+ return -ENOMEM;
|
||
|
+
|
||
|
+ mutex_init(&drvdata->lock);
|
||
|
+
|
||
|
+ drvdata->supply = devm_regulator_get(&pdev->dev, "controlled");
|
||
|
+ if (IS_ERR(drvdata->supply)) {
|
||
|
+ ret = PTR_ERR(drvdata->supply);
|
||
|
+ if (ret != -EPROBE_DEFER)
|
||
|
+ dev_err(&pdev->dev, "Failed to get supply: %d\n", ret);
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+
|
||
|
+ ret = sysfs_create_group(&pdev->dev.kobj, &attr_group);
|
||
|
+ if (ret != 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ platform_set_drvdata(pdev, drvdata);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int regulator_userspace_consumer_remove(struct platform_device *pdev)
|
||
|
+{
|
||
|
+ struct userspace_consumer_data *data = platform_get_drvdata(pdev);
|
||
|
+
|
||
|
+ sysfs_remove_group(&pdev->dev.kobj, &attr_group);
|
||
|
+
|
||
|
+ if (data->enabled)
|
||
|
+ regulator_disable(data->supply);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static const struct of_device_id ids_of_match[] = {
|
||
|
+ { .compatible = "custom,reg-userspace-consumer", },
|
||
|
+ {},
|
||
|
+};
|
||
|
+MODULE_DEVICE_TABLE(of, ids_of_match);
|
||
|
+
|
||
|
+static struct platform_driver regulator_userspace_consumer_driver = {
|
||
|
+ .probe = regulator_userspace_consumer_probe,
|
||
|
+ .remove = regulator_userspace_consumer_remove,
|
||
|
+ .driver = {
|
||
|
+ .name = "reg-userspace-consumer-of",
|
||
|
+ .of_match_table = of_match_ptr(ids_of_match),
|
||
|
+ },
|
||
|
+};
|
||
|
+
|
||
|
+module_platform_driver(regulator_userspace_consumer_driver);
|
||
|
+
|
||
|
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
|
||
|
+MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators");
|
||
|
+MODULE_LICENSE("GPL");
|
||
|
--
|
||
|
2.35.3
|
||
|
|