userspace-consumer.c (4285B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * userspace-consumer.c 4 * 5 * Copyright 2009 CompuLab, Ltd. 6 * 7 * Author: Mike Rapoport <mike@compulab.co.il> 8 * 9 * Based of virtual consumer driver: 10 * Copyright 2008 Wolfson Microelectronics PLC. 11 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 12 */ 13 14#include <linux/err.h> 15#include <linux/mutex.h> 16#include <linux/module.h> 17#include <linux/platform_device.h> 18#include <linux/regulator/consumer.h> 19#include <linux/regulator/userspace-consumer.h> 20#include <linux/slab.h> 21 22struct userspace_consumer_data { 23 const char *name; 24 25 struct mutex lock; 26 bool enabled; 27 28 int num_supplies; 29 struct regulator_bulk_data *supplies; 30}; 31 32static ssize_t name_show(struct device *dev, 33 struct device_attribute *attr, char *buf) 34{ 35 struct userspace_consumer_data *data = dev_get_drvdata(dev); 36 37 return sprintf(buf, "%s\n", data->name); 38} 39 40static ssize_t state_show(struct device *dev, 41 struct device_attribute *attr, char *buf) 42{ 43 struct userspace_consumer_data *data = dev_get_drvdata(dev); 44 45 if (data->enabled) 46 return sprintf(buf, "enabled\n"); 47 48 return sprintf(buf, "disabled\n"); 49} 50 51static ssize_t state_store(struct device *dev, struct device_attribute *attr, 52 const char *buf, size_t count) 53{ 54 struct userspace_consumer_data *data = dev_get_drvdata(dev); 55 bool enabled; 56 int ret; 57 58 /* 59 * sysfs_streq() doesn't need the \n's, but we add them so the strings 60 * will be shared with show_state(), above. 61 */ 62 if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1")) 63 enabled = true; 64 else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0")) 65 enabled = false; 66 else { 67 dev_err(dev, "Configuring invalid mode\n"); 68 return count; 69 } 70 71 mutex_lock(&data->lock); 72 if (enabled != data->enabled) { 73 if (enabled) 74 ret = regulator_bulk_enable(data->num_supplies, 75 data->supplies); 76 else 77 ret = regulator_bulk_disable(data->num_supplies, 78 data->supplies); 79 80 if (ret == 0) 81 data->enabled = enabled; 82 else 83 dev_err(dev, "Failed to configure state: %d\n", ret); 84 } 85 mutex_unlock(&data->lock); 86 87 return count; 88} 89 90static DEVICE_ATTR_RO(name); 91static DEVICE_ATTR_RW(state); 92 93static struct attribute *attributes[] = { 94 &dev_attr_name.attr, 95 &dev_attr_state.attr, 96 NULL, 97}; 98 99static const struct attribute_group attr_group = { 100 .attrs = attributes, 101}; 102 103static int regulator_userspace_consumer_probe(struct platform_device *pdev) 104{ 105 struct regulator_userspace_consumer_data *pdata; 106 struct userspace_consumer_data *drvdata; 107 int ret; 108 109 pdata = dev_get_platdata(&pdev->dev); 110 if (!pdata) 111 return -EINVAL; 112 113 drvdata = devm_kzalloc(&pdev->dev, 114 sizeof(struct userspace_consumer_data), 115 GFP_KERNEL); 116 if (drvdata == NULL) 117 return -ENOMEM; 118 119 drvdata->name = pdata->name; 120 drvdata->num_supplies = pdata->num_supplies; 121 drvdata->supplies = pdata->supplies; 122 123 mutex_init(&drvdata->lock); 124 125 ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies, 126 drvdata->supplies); 127 if (ret) { 128 dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret); 129 return ret; 130 } 131 132 ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); 133 if (ret != 0) 134 return ret; 135 136 if (pdata->init_on) { 137 ret = regulator_bulk_enable(drvdata->num_supplies, 138 drvdata->supplies); 139 if (ret) { 140 dev_err(&pdev->dev, 141 "Failed to set initial state: %d\n", ret); 142 goto err_enable; 143 } 144 } 145 146 drvdata->enabled = pdata->init_on; 147 platform_set_drvdata(pdev, drvdata); 148 149 return 0; 150 151err_enable: 152 sysfs_remove_group(&pdev->dev.kobj, &attr_group); 153 154 return ret; 155} 156 157static int regulator_userspace_consumer_remove(struct platform_device *pdev) 158{ 159 struct userspace_consumer_data *data = platform_get_drvdata(pdev); 160 161 sysfs_remove_group(&pdev->dev.kobj, &attr_group); 162 163 if (data->enabled) 164 regulator_bulk_disable(data->num_supplies, data->supplies); 165 166 return 0; 167} 168 169static struct platform_driver regulator_userspace_consumer_driver = { 170 .probe = regulator_userspace_consumer_probe, 171 .remove = regulator_userspace_consumer_remove, 172 .driver = { 173 .name = "reg-userspace-consumer", 174 }, 175}; 176 177module_platform_driver(regulator_userspace_consumer_driver); 178 179MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); 180MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators"); 181MODULE_LICENSE("GPL");