virtual.c (9611B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * reg-virtual-consumer.c 4 * 5 * Copyright 2008 Wolfson Microelectronics PLC. 6 * 7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 8 */ 9 10#include <linux/err.h> 11#include <linux/mutex.h> 12#include <linux/platform_device.h> 13#include <linux/regulator/consumer.h> 14#include <linux/slab.h> 15#include <linux/module.h> 16#include <linux/of.h> 17 18struct virtual_consumer_data { 19 struct mutex lock; 20 struct regulator *regulator; 21 bool enabled; 22 int min_uV; 23 int max_uV; 24 int min_uA; 25 int max_uA; 26 unsigned int mode; 27}; 28 29static void update_voltage_constraints(struct device *dev, 30 struct virtual_consumer_data *data) 31{ 32 int ret; 33 34 if (data->min_uV && data->max_uV 35 && data->min_uV <= data->max_uV) { 36 dev_dbg(dev, "Requesting %d-%duV\n", 37 data->min_uV, data->max_uV); 38 ret = regulator_set_voltage(data->regulator, 39 data->min_uV, data->max_uV); 40 if (ret != 0) { 41 dev_err(dev, 42 "regulator_set_voltage() failed: %d\n", ret); 43 return; 44 } 45 } 46 47 if (data->min_uV && data->max_uV && !data->enabled) { 48 dev_dbg(dev, "Enabling regulator\n"); 49 ret = regulator_enable(data->regulator); 50 if (ret == 0) 51 data->enabled = true; 52 else 53 dev_err(dev, "regulator_enable() failed: %d\n", 54 ret); 55 } 56 57 if (!(data->min_uV && data->max_uV) && data->enabled) { 58 dev_dbg(dev, "Disabling regulator\n"); 59 ret = regulator_disable(data->regulator); 60 if (ret == 0) 61 data->enabled = false; 62 else 63 dev_err(dev, "regulator_disable() failed: %d\n", 64 ret); 65 } 66} 67 68static void update_current_limit_constraints(struct device *dev, 69 struct virtual_consumer_data *data) 70{ 71 int ret; 72 73 if (data->max_uA 74 && data->min_uA <= data->max_uA) { 75 dev_dbg(dev, "Requesting %d-%duA\n", 76 data->min_uA, data->max_uA); 77 ret = regulator_set_current_limit(data->regulator, 78 data->min_uA, data->max_uA); 79 if (ret != 0) { 80 dev_err(dev, 81 "regulator_set_current_limit() failed: %d\n", 82 ret); 83 return; 84 } 85 } 86 87 if (data->max_uA && !data->enabled) { 88 dev_dbg(dev, "Enabling regulator\n"); 89 ret = regulator_enable(data->regulator); 90 if (ret == 0) 91 data->enabled = true; 92 else 93 dev_err(dev, "regulator_enable() failed: %d\n", 94 ret); 95 } 96 97 if (!(data->min_uA && data->max_uA) && data->enabled) { 98 dev_dbg(dev, "Disabling regulator\n"); 99 ret = regulator_disable(data->regulator); 100 if (ret == 0) 101 data->enabled = false; 102 else 103 dev_err(dev, "regulator_disable() failed: %d\n", 104 ret); 105 } 106} 107 108static ssize_t show_min_uV(struct device *dev, 109 struct device_attribute *attr, char *buf) 110{ 111 struct virtual_consumer_data *data = dev_get_drvdata(dev); 112 return sprintf(buf, "%d\n", data->min_uV); 113} 114 115static ssize_t set_min_uV(struct device *dev, struct device_attribute *attr, 116 const char *buf, size_t count) 117{ 118 struct virtual_consumer_data *data = dev_get_drvdata(dev); 119 long val; 120 121 if (kstrtol(buf, 10, &val) != 0) 122 return count; 123 124 mutex_lock(&data->lock); 125 126 data->min_uV = val; 127 update_voltage_constraints(dev, data); 128 129 mutex_unlock(&data->lock); 130 131 return count; 132} 133 134static ssize_t show_max_uV(struct device *dev, 135 struct device_attribute *attr, char *buf) 136{ 137 struct virtual_consumer_data *data = dev_get_drvdata(dev); 138 return sprintf(buf, "%d\n", data->max_uV); 139} 140 141static ssize_t set_max_uV(struct device *dev, struct device_attribute *attr, 142 const char *buf, size_t count) 143{ 144 struct virtual_consumer_data *data = dev_get_drvdata(dev); 145 long val; 146 147 if (kstrtol(buf, 10, &val) != 0) 148 return count; 149 150 mutex_lock(&data->lock); 151 152 data->max_uV = val; 153 update_voltage_constraints(dev, data); 154 155 mutex_unlock(&data->lock); 156 157 return count; 158} 159 160static ssize_t show_min_uA(struct device *dev, 161 struct device_attribute *attr, char *buf) 162{ 163 struct virtual_consumer_data *data = dev_get_drvdata(dev); 164 return sprintf(buf, "%d\n", data->min_uA); 165} 166 167static ssize_t set_min_uA(struct device *dev, struct device_attribute *attr, 168 const char *buf, size_t count) 169{ 170 struct virtual_consumer_data *data = dev_get_drvdata(dev); 171 long val; 172 173 if (kstrtol(buf, 10, &val) != 0) 174 return count; 175 176 mutex_lock(&data->lock); 177 178 data->min_uA = val; 179 update_current_limit_constraints(dev, data); 180 181 mutex_unlock(&data->lock); 182 183 return count; 184} 185 186static ssize_t show_max_uA(struct device *dev, 187 struct device_attribute *attr, char *buf) 188{ 189 struct virtual_consumer_data *data = dev_get_drvdata(dev); 190 return sprintf(buf, "%d\n", data->max_uA); 191} 192 193static ssize_t set_max_uA(struct device *dev, struct device_attribute *attr, 194 const char *buf, size_t count) 195{ 196 struct virtual_consumer_data *data = dev_get_drvdata(dev); 197 long val; 198 199 if (kstrtol(buf, 10, &val) != 0) 200 return count; 201 202 mutex_lock(&data->lock); 203 204 data->max_uA = val; 205 update_current_limit_constraints(dev, data); 206 207 mutex_unlock(&data->lock); 208 209 return count; 210} 211 212static ssize_t show_mode(struct device *dev, 213 struct device_attribute *attr, char *buf) 214{ 215 struct virtual_consumer_data *data = dev_get_drvdata(dev); 216 217 switch (data->mode) { 218 case REGULATOR_MODE_FAST: 219 return sprintf(buf, "fast\n"); 220 case REGULATOR_MODE_NORMAL: 221 return sprintf(buf, "normal\n"); 222 case REGULATOR_MODE_IDLE: 223 return sprintf(buf, "idle\n"); 224 case REGULATOR_MODE_STANDBY: 225 return sprintf(buf, "standby\n"); 226 default: 227 return sprintf(buf, "unknown\n"); 228 } 229} 230 231static ssize_t set_mode(struct device *dev, struct device_attribute *attr, 232 const char *buf, size_t count) 233{ 234 struct virtual_consumer_data *data = dev_get_drvdata(dev); 235 unsigned int mode; 236 int ret; 237 238 /* 239 * sysfs_streq() doesn't need the \n's, but we add them so the strings 240 * will be shared with show_mode(), above. 241 */ 242 if (sysfs_streq(buf, "fast\n")) 243 mode = REGULATOR_MODE_FAST; 244 else if (sysfs_streq(buf, "normal\n")) 245 mode = REGULATOR_MODE_NORMAL; 246 else if (sysfs_streq(buf, "idle\n")) 247 mode = REGULATOR_MODE_IDLE; 248 else if (sysfs_streq(buf, "standby\n")) 249 mode = REGULATOR_MODE_STANDBY; 250 else { 251 dev_err(dev, "Configuring invalid mode\n"); 252 return count; 253 } 254 255 mutex_lock(&data->lock); 256 ret = regulator_set_mode(data->regulator, mode); 257 if (ret == 0) 258 data->mode = mode; 259 else 260 dev_err(dev, "Failed to configure mode: %d\n", ret); 261 mutex_unlock(&data->lock); 262 263 return count; 264} 265 266static DEVICE_ATTR(min_microvolts, 0664, show_min_uV, set_min_uV); 267static DEVICE_ATTR(max_microvolts, 0664, show_max_uV, set_max_uV); 268static DEVICE_ATTR(min_microamps, 0664, show_min_uA, set_min_uA); 269static DEVICE_ATTR(max_microamps, 0664, show_max_uA, set_max_uA); 270static DEVICE_ATTR(mode, 0664, show_mode, set_mode); 271 272static struct attribute *regulator_virtual_attributes[] = { 273 &dev_attr_min_microvolts.attr, 274 &dev_attr_max_microvolts.attr, 275 &dev_attr_min_microamps.attr, 276 &dev_attr_max_microamps.attr, 277 &dev_attr_mode.attr, 278 NULL 279}; 280 281static const struct attribute_group regulator_virtual_attr_group = { 282 .attrs = regulator_virtual_attributes, 283}; 284 285#ifdef CONFIG_OF 286static const struct of_device_id regulator_virtual_consumer_of_match[] = { 287 { .compatible = "regulator-virtual-consumer" }, 288 {}, 289}; 290MODULE_DEVICE_TABLE(of, regulator_virtual_consumer_of_match); 291#endif 292 293static int regulator_virtual_probe(struct platform_device *pdev) 294{ 295 char *reg_id = dev_get_platdata(&pdev->dev); 296 struct virtual_consumer_data *drvdata; 297 static bool warned; 298 int ret; 299 300 if (!warned) { 301 warned = true; 302 pr_warn("**********************************************************\n"); 303 pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); 304 pr_warn("** **\n"); 305 pr_warn("** regulator-virtual-consumer is only for testing and **\n"); 306 pr_warn("** debugging. Do not use it in a production kernel. **\n"); 307 pr_warn("** **\n"); 308 pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n"); 309 pr_warn("**********************************************************\n"); 310 } 311 312 drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data), 313 GFP_KERNEL); 314 if (drvdata == NULL) 315 return -ENOMEM; 316 317 /* 318 * This virtual consumer does not have any hardware-defined supply 319 * name, so just allow the regulator to be specified in a property 320 * named "default-supply" when we're being probed from devicetree. 321 */ 322 if (!reg_id && pdev->dev.of_node) 323 reg_id = "default"; 324 325 mutex_init(&drvdata->lock); 326 327 drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id); 328 if (IS_ERR(drvdata->regulator)) 329 return dev_err_probe(&pdev->dev, PTR_ERR(drvdata->regulator), 330 "Failed to obtain supply '%s'\n", 331 reg_id); 332 333 ret = sysfs_create_group(&pdev->dev.kobj, 334 ®ulator_virtual_attr_group); 335 if (ret != 0) { 336 dev_err(&pdev->dev, 337 "Failed to create attribute group: %d\n", ret); 338 return ret; 339 } 340 341 drvdata->mode = regulator_get_mode(drvdata->regulator); 342 343 platform_set_drvdata(pdev, drvdata); 344 345 return 0; 346} 347 348static int regulator_virtual_remove(struct platform_device *pdev) 349{ 350 struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev); 351 352 sysfs_remove_group(&pdev->dev.kobj, ®ulator_virtual_attr_group); 353 354 if (drvdata->enabled) 355 regulator_disable(drvdata->regulator); 356 357 return 0; 358} 359 360static struct platform_driver regulator_virtual_consumer_driver = { 361 .probe = regulator_virtual_probe, 362 .remove = regulator_virtual_remove, 363 .driver = { 364 .name = "reg-virt-consumer", 365 .of_match_table = of_match_ptr(regulator_virtual_consumer_of_match), 366 }, 367}; 368 369module_platform_driver(regulator_virtual_consumer_driver); 370 371MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 372MODULE_DESCRIPTION("Virtual regulator consumer"); 373MODULE_LICENSE("GPL"); 374MODULE_ALIAS("platform:reg-virt-consumer");