1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * reg-virtual-consumer.c
5 * Copyright 2008 Wolfson Microelectronics PLC.
7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
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>
18 struct virtual_consumer_data
{
20 struct regulator
*regulator
;
29 static void update_voltage_constraints(struct device
*dev
,
30 struct virtual_consumer_data
*data
)
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
);
42 "regulator_set_voltage() failed: %d\n", ret
);
47 if (data
->min_uV
&& data
->max_uV
&& !data
->enabled
) {
48 dev_dbg(dev
, "Enabling regulator\n");
49 ret
= regulator_enable(data
->regulator
);
53 dev_err(dev
, "regulator_enable() failed: %d\n",
57 if (!(data
->min_uV
&& data
->max_uV
) && data
->enabled
) {
58 dev_dbg(dev
, "Disabling regulator\n");
59 ret
= regulator_disable(data
->regulator
);
61 data
->enabled
= false;
63 dev_err(dev
, "regulator_disable() failed: %d\n",
68 static void update_current_limit_constraints(struct device
*dev
,
69 struct virtual_consumer_data
*data
)
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
);
81 "regulator_set_current_limit() failed: %d\n",
87 if (data
->max_uA
&& !data
->enabled
) {
88 dev_dbg(dev
, "Enabling regulator\n");
89 ret
= regulator_enable(data
->regulator
);
93 dev_err(dev
, "regulator_enable() failed: %d\n",
97 if (!(data
->min_uA
&& data
->max_uA
) && data
->enabled
) {
98 dev_dbg(dev
, "Disabling regulator\n");
99 ret
= regulator_disable(data
->regulator
);
101 data
->enabled
= false;
103 dev_err(dev
, "regulator_disable() failed: %d\n",
108 static ssize_t
show_min_uV(struct device
*dev
,
109 struct device_attribute
*attr
, char *buf
)
111 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
112 return sprintf(buf
, "%d\n", data
->min_uV
);
115 static ssize_t
set_min_uV(struct device
*dev
, struct device_attribute
*attr
,
116 const char *buf
, size_t count
)
118 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
121 if (kstrtol(buf
, 10, &val
) != 0)
124 mutex_lock(&data
->lock
);
127 update_voltage_constraints(dev
, data
);
129 mutex_unlock(&data
->lock
);
134 static ssize_t
show_max_uV(struct device
*dev
,
135 struct device_attribute
*attr
, char *buf
)
137 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
138 return sprintf(buf
, "%d\n", data
->max_uV
);
141 static ssize_t
set_max_uV(struct device
*dev
, struct device_attribute
*attr
,
142 const char *buf
, size_t count
)
144 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
147 if (kstrtol(buf
, 10, &val
) != 0)
150 mutex_lock(&data
->lock
);
153 update_voltage_constraints(dev
, data
);
155 mutex_unlock(&data
->lock
);
160 static ssize_t
show_min_uA(struct device
*dev
,
161 struct device_attribute
*attr
, char *buf
)
163 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
164 return sprintf(buf
, "%d\n", data
->min_uA
);
167 static ssize_t
set_min_uA(struct device
*dev
, struct device_attribute
*attr
,
168 const char *buf
, size_t count
)
170 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
173 if (kstrtol(buf
, 10, &val
) != 0)
176 mutex_lock(&data
->lock
);
179 update_current_limit_constraints(dev
, data
);
181 mutex_unlock(&data
->lock
);
186 static ssize_t
show_max_uA(struct device
*dev
,
187 struct device_attribute
*attr
, char *buf
)
189 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
190 return sprintf(buf
, "%d\n", data
->max_uA
);
193 static ssize_t
set_max_uA(struct device
*dev
, struct device_attribute
*attr
,
194 const char *buf
, size_t count
)
196 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
199 if (kstrtol(buf
, 10, &val
) != 0)
202 mutex_lock(&data
->lock
);
205 update_current_limit_constraints(dev
, data
);
207 mutex_unlock(&data
->lock
);
212 static ssize_t
show_mode(struct device
*dev
,
213 struct device_attribute
*attr
, char *buf
)
215 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
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");
227 return sprintf(buf
, "unknown\n");
231 static ssize_t
set_mode(struct device
*dev
, struct device_attribute
*attr
,
232 const char *buf
, size_t count
)
234 struct virtual_consumer_data
*data
= dev_get_drvdata(dev
);
239 * sysfs_streq() doesn't need the \n's, but we add them so the strings
240 * will be shared with show_mode(), above.
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
;
251 dev_err(dev
, "Configuring invalid mode\n");
255 mutex_lock(&data
->lock
);
256 ret
= regulator_set_mode(data
->regulator
, mode
);
260 dev_err(dev
, "Failed to configure mode: %d\n", ret
);
261 mutex_unlock(&data
->lock
);
266 static DEVICE_ATTR(min_microvolts
, 0664, show_min_uV
, set_min_uV
);
267 static DEVICE_ATTR(max_microvolts
, 0664, show_max_uV
, set_max_uV
);
268 static DEVICE_ATTR(min_microamps
, 0664, show_min_uA
, set_min_uA
);
269 static DEVICE_ATTR(max_microamps
, 0664, show_max_uA
, set_max_uA
);
270 static DEVICE_ATTR(mode
, 0664, show_mode
, set_mode
);
272 static 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
,
281 static const struct attribute_group regulator_virtual_attr_group
= {
282 .attrs
= regulator_virtual_attributes
,
286 static const struct of_device_id regulator_virtual_consumer_of_match
[] = {
287 { .compatible
= "regulator-virtual-consumer" },
290 MODULE_DEVICE_TABLE(of
, regulator_virtual_consumer_of_match
);
293 static int regulator_virtual_probe(struct platform_device
*pdev
)
295 char *reg_id
= dev_get_platdata(&pdev
->dev
);
296 struct virtual_consumer_data
*drvdata
;
302 pr_warn("**********************************************************\n");
303 pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\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");
308 pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
309 pr_warn("**********************************************************\n");
312 drvdata
= devm_kzalloc(&pdev
->dev
, sizeof(struct virtual_consumer_data
),
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.
322 if (!reg_id
&& pdev
->dev
.of_node
)
325 mutex_init(&drvdata
->lock
);
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",
333 ret
= sysfs_create_group(&pdev
->dev
.kobj
,
334 ®ulator_virtual_attr_group
);
337 "Failed to create attribute group: %d\n", ret
);
341 drvdata
->mode
= regulator_get_mode(drvdata
->regulator
);
343 platform_set_drvdata(pdev
, drvdata
);
348 static void regulator_virtual_remove(struct platform_device
*pdev
)
350 struct virtual_consumer_data
*drvdata
= platform_get_drvdata(pdev
);
352 sysfs_remove_group(&pdev
->dev
.kobj
, ®ulator_virtual_attr_group
);
354 if (drvdata
->enabled
)
355 regulator_disable(drvdata
->regulator
);
358 static struct platform_driver regulator_virtual_consumer_driver
= {
359 .probe
= regulator_virtual_probe
,
360 .remove
= regulator_virtual_remove
,
362 .name
= "reg-virt-consumer",
363 .probe_type
= PROBE_PREFER_ASYNCHRONOUS
,
364 .of_match_table
= of_match_ptr(regulator_virtual_consumer_of_match
),
368 module_platform_driver(regulator_virtual_consumer_driver
);
370 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
371 MODULE_DESCRIPTION("Virtual regulator consumer");
372 MODULE_LICENSE("GPL");
373 MODULE_ALIAS("platform:reg-virt-consumer");