From b86654e6ffcbf3e299f24d6e7f21cb899edab11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Jirman?= Date: Sat, 5 Oct 2019 15:10:11 +0200 Subject: [PATCH 077/391] 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 --- 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 820c9a078..c3f6166fd 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 b9f5eb35b..340771224 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 000000000..5b835af17 --- /dev/null +++ b/drivers/regulator/userspace-consumer-of.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include + +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 "); +MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators"); +MODULE_LICENSE("GPL"); -- 2.35.3