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