build/patch/kernel/archive/sunxi-5.15/patches.megous/regulator-Add-simple-driver-for-enabling-a-regulator-from-users.patch

196 lines
5.4 KiB
Diff
Raw Normal View History

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