12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c080909eSMark Brown /*
3c080909eSMark Brown * reg-virtual-consumer.c
4c080909eSMark Brown *
5c080909eSMark Brown * Copyright 2008 Wolfson Microelectronics PLC.
6c080909eSMark Brown *
7c080909eSMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8c080909eSMark Brown */
9c080909eSMark Brown
10c080909eSMark Brown #include <linux/err.h>
11c080909eSMark Brown #include <linux/mutex.h>
12c080909eSMark Brown #include <linux/platform_device.h>
13c080909eSMark Brown #include <linux/regulator/consumer.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
1565602c32SPaul Gortmaker #include <linux/module.h>
1680c05665SVincent Whitchurch #include <linux/of.h>
17c080909eSMark Brown
18c080909eSMark Brown struct virtual_consumer_data {
19c080909eSMark Brown struct mutex lock;
20c080909eSMark Brown struct regulator *regulator;
214cf95663SDmitry Torokhov bool enabled;
22c080909eSMark Brown int min_uV;
23c080909eSMark Brown int max_uV;
24c080909eSMark Brown int min_uA;
25c080909eSMark Brown int max_uA;
26c080909eSMark Brown unsigned int mode;
27c080909eSMark Brown };
28c080909eSMark Brown
update_voltage_constraints(struct device * dev,struct virtual_consumer_data * data)29a07ac217SMark Brown static void update_voltage_constraints(struct device *dev,
30a07ac217SMark Brown struct virtual_consumer_data *data)
31c080909eSMark Brown {
32c080909eSMark Brown int ret;
33c080909eSMark Brown
34c080909eSMark Brown if (data->min_uV && data->max_uV
35c080909eSMark Brown && data->min_uV <= data->max_uV) {
36a5d2abceSMark Brown dev_dbg(dev, "Requesting %d-%duV\n",
37a5d2abceSMark Brown data->min_uV, data->max_uV);
38c080909eSMark Brown ret = regulator_set_voltage(data->regulator,
39c080909eSMark Brown data->min_uV, data->max_uV);
40c080909eSMark Brown if (ret != 0) {
41a07ac217SMark Brown dev_err(dev,
42a07ac217SMark Brown "regulator_set_voltage() failed: %d\n", ret);
43c080909eSMark Brown return;
44c080909eSMark Brown }
45c080909eSMark Brown }
46c080909eSMark Brown
47c080909eSMark Brown if (data->min_uV && data->max_uV && !data->enabled) {
48a5d2abceSMark Brown dev_dbg(dev, "Enabling regulator\n");
49c080909eSMark Brown ret = regulator_enable(data->regulator);
50c080909eSMark Brown if (ret == 0)
514cf95663SDmitry Torokhov data->enabled = true;
52c080909eSMark Brown else
53a07ac217SMark Brown dev_err(dev, "regulator_enable() failed: %d\n",
54c080909eSMark Brown ret);
55c080909eSMark Brown }
56c080909eSMark Brown
57c080909eSMark Brown if (!(data->min_uV && data->max_uV) && data->enabled) {
58a5d2abceSMark Brown dev_dbg(dev, "Disabling regulator\n");
59c080909eSMark Brown ret = regulator_disable(data->regulator);
60c080909eSMark Brown if (ret == 0)
614cf95663SDmitry Torokhov data->enabled = false;
62c080909eSMark Brown else
63a07ac217SMark Brown dev_err(dev, "regulator_disable() failed: %d\n",
64c080909eSMark Brown ret);
65c080909eSMark Brown }
66c080909eSMark Brown }
67c080909eSMark Brown
update_current_limit_constraints(struct device * dev,struct virtual_consumer_data * data)68a07ac217SMark Brown static void update_current_limit_constraints(struct device *dev,
69a07ac217SMark Brown struct virtual_consumer_data *data)
70c080909eSMark Brown {
71c080909eSMark Brown int ret;
72c080909eSMark Brown
73c080909eSMark Brown if (data->max_uA
74c080909eSMark Brown && data->min_uA <= data->max_uA) {
75a5d2abceSMark Brown dev_dbg(dev, "Requesting %d-%duA\n",
76a5d2abceSMark Brown data->min_uA, data->max_uA);
77c080909eSMark Brown ret = regulator_set_current_limit(data->regulator,
78c080909eSMark Brown data->min_uA, data->max_uA);
79c080909eSMark Brown if (ret != 0) {
80a07ac217SMark Brown dev_err(dev,
81a07ac217SMark Brown "regulator_set_current_limit() failed: %d\n",
82c080909eSMark Brown ret);
83c080909eSMark Brown return;
84c080909eSMark Brown }
85c080909eSMark Brown }
86c080909eSMark Brown
87c080909eSMark Brown if (data->max_uA && !data->enabled) {
88a5d2abceSMark Brown dev_dbg(dev, "Enabling regulator\n");
89c080909eSMark Brown ret = regulator_enable(data->regulator);
90c080909eSMark Brown if (ret == 0)
914cf95663SDmitry Torokhov data->enabled = true;
92c080909eSMark Brown else
93a07ac217SMark Brown dev_err(dev, "regulator_enable() failed: %d\n",
94c080909eSMark Brown ret);
95c080909eSMark Brown }
96c080909eSMark Brown
97c080909eSMark Brown if (!(data->min_uA && data->max_uA) && data->enabled) {
98a5d2abceSMark Brown dev_dbg(dev, "Disabling regulator\n");
99c080909eSMark Brown ret = regulator_disable(data->regulator);
100c080909eSMark Brown if (ret == 0)
1014cf95663SDmitry Torokhov data->enabled = false;
102c080909eSMark Brown else
103a07ac217SMark Brown dev_err(dev, "regulator_disable() failed: %d\n",
104c080909eSMark Brown ret);
105c080909eSMark Brown }
106c080909eSMark Brown }
107c080909eSMark Brown
show_min_uV(struct device * dev,struct device_attribute * attr,char * buf)108c080909eSMark Brown static ssize_t show_min_uV(struct device *dev,
109c080909eSMark Brown struct device_attribute *attr, char *buf)
110c080909eSMark Brown {
111c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
112c080909eSMark Brown return sprintf(buf, "%d\n", data->min_uV);
113c080909eSMark Brown }
114c080909eSMark Brown
set_min_uV(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)115c080909eSMark Brown static ssize_t set_min_uV(struct device *dev, struct device_attribute *attr,
116c080909eSMark Brown const char *buf, size_t count)
117c080909eSMark Brown {
118c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
119c080909eSMark Brown long val;
120c080909eSMark Brown
12185960e7bSAxel Lin if (kstrtol(buf, 10, &val) != 0)
122c080909eSMark Brown return count;
123c080909eSMark Brown
124c080909eSMark Brown mutex_lock(&data->lock);
125c080909eSMark Brown
126c080909eSMark Brown data->min_uV = val;
127a07ac217SMark Brown update_voltage_constraints(dev, data);
128c080909eSMark Brown
129c080909eSMark Brown mutex_unlock(&data->lock);
130c080909eSMark Brown
131c080909eSMark Brown return count;
132c080909eSMark Brown }
133c080909eSMark Brown
show_max_uV(struct device * dev,struct device_attribute * attr,char * buf)134c080909eSMark Brown static ssize_t show_max_uV(struct device *dev,
135c080909eSMark Brown struct device_attribute *attr, char *buf)
136c080909eSMark Brown {
137c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
138c080909eSMark Brown return sprintf(buf, "%d\n", data->max_uV);
139c080909eSMark Brown }
140c080909eSMark Brown
set_max_uV(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)141c080909eSMark Brown static ssize_t set_max_uV(struct device *dev, struct device_attribute *attr,
142c080909eSMark Brown const char *buf, size_t count)
143c080909eSMark Brown {
144c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
145c080909eSMark Brown long val;
146c080909eSMark Brown
14785960e7bSAxel Lin if (kstrtol(buf, 10, &val) != 0)
148c080909eSMark Brown return count;
149c080909eSMark Brown
150c080909eSMark Brown mutex_lock(&data->lock);
151c080909eSMark Brown
152c080909eSMark Brown data->max_uV = val;
153a07ac217SMark Brown update_voltage_constraints(dev, data);
154c080909eSMark Brown
155c080909eSMark Brown mutex_unlock(&data->lock);
156c080909eSMark Brown
157c080909eSMark Brown return count;
158c080909eSMark Brown }
159c080909eSMark Brown
show_min_uA(struct device * dev,struct device_attribute * attr,char * buf)160c080909eSMark Brown static ssize_t show_min_uA(struct device *dev,
161c080909eSMark Brown struct device_attribute *attr, char *buf)
162c080909eSMark Brown {
163c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
164c080909eSMark Brown return sprintf(buf, "%d\n", data->min_uA);
165c080909eSMark Brown }
166c080909eSMark Brown
set_min_uA(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)167c080909eSMark Brown static ssize_t set_min_uA(struct device *dev, struct device_attribute *attr,
168c080909eSMark Brown const char *buf, size_t count)
169c080909eSMark Brown {
170c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
171c080909eSMark Brown long val;
172c080909eSMark Brown
17385960e7bSAxel Lin if (kstrtol(buf, 10, &val) != 0)
174c080909eSMark Brown return count;
175c080909eSMark Brown
176c080909eSMark Brown mutex_lock(&data->lock);
177c080909eSMark Brown
178c080909eSMark Brown data->min_uA = val;
179a07ac217SMark Brown update_current_limit_constraints(dev, data);
180c080909eSMark Brown
181c080909eSMark Brown mutex_unlock(&data->lock);
182c080909eSMark Brown
183c080909eSMark Brown return count;
184c080909eSMark Brown }
185c080909eSMark Brown
show_max_uA(struct device * dev,struct device_attribute * attr,char * buf)186c080909eSMark Brown static ssize_t show_max_uA(struct device *dev,
187c080909eSMark Brown struct device_attribute *attr, char *buf)
188c080909eSMark Brown {
189c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
190c080909eSMark Brown return sprintf(buf, "%d\n", data->max_uA);
191c080909eSMark Brown }
192c080909eSMark Brown
set_max_uA(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)193c080909eSMark Brown static ssize_t set_max_uA(struct device *dev, struct device_attribute *attr,
194c080909eSMark Brown const char *buf, size_t count)
195c080909eSMark Brown {
196c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
197c080909eSMark Brown long val;
198c080909eSMark Brown
19985960e7bSAxel Lin if (kstrtol(buf, 10, &val) != 0)
200c080909eSMark Brown return count;
201c080909eSMark Brown
202c080909eSMark Brown mutex_lock(&data->lock);
203c080909eSMark Brown
204c080909eSMark Brown data->max_uA = val;
205a07ac217SMark Brown update_current_limit_constraints(dev, data);
206c080909eSMark Brown
207c080909eSMark Brown mutex_unlock(&data->lock);
208c080909eSMark Brown
209c080909eSMark Brown return count;
210c080909eSMark Brown }
211c080909eSMark Brown
show_mode(struct device * dev,struct device_attribute * attr,char * buf)212c080909eSMark Brown static ssize_t show_mode(struct device *dev,
213c080909eSMark Brown struct device_attribute *attr, char *buf)
214c080909eSMark Brown {
215c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
216c080909eSMark Brown
217c080909eSMark Brown switch (data->mode) {
218c080909eSMark Brown case REGULATOR_MODE_FAST:
219c080909eSMark Brown return sprintf(buf, "fast\n");
220c080909eSMark Brown case REGULATOR_MODE_NORMAL:
221c080909eSMark Brown return sprintf(buf, "normal\n");
222c080909eSMark Brown case REGULATOR_MODE_IDLE:
223c080909eSMark Brown return sprintf(buf, "idle\n");
224c080909eSMark Brown case REGULATOR_MODE_STANDBY:
225c080909eSMark Brown return sprintf(buf, "standby\n");
226c080909eSMark Brown default:
227c080909eSMark Brown return sprintf(buf, "unknown\n");
228c080909eSMark Brown }
229c080909eSMark Brown }
230c080909eSMark Brown
set_mode(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)231c080909eSMark Brown static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
232c080909eSMark Brown const char *buf, size_t count)
233c080909eSMark Brown {
234c080909eSMark Brown struct virtual_consumer_data *data = dev_get_drvdata(dev);
235c080909eSMark Brown unsigned int mode;
236c080909eSMark Brown int ret;
237c080909eSMark Brown
2389485397aSAndrew Morton /*
2399485397aSAndrew Morton * sysfs_streq() doesn't need the \n's, but we add them so the strings
2409485397aSAndrew Morton * will be shared with show_mode(), above.
2419485397aSAndrew Morton */
242aa61d558SMike Rapoport if (sysfs_streq(buf, "fast\n"))
243c080909eSMark Brown mode = REGULATOR_MODE_FAST;
244aa61d558SMike Rapoport else if (sysfs_streq(buf, "normal\n"))
245c080909eSMark Brown mode = REGULATOR_MODE_NORMAL;
246aa61d558SMike Rapoport else if (sysfs_streq(buf, "idle\n"))
247c080909eSMark Brown mode = REGULATOR_MODE_IDLE;
248aa61d558SMike Rapoport else if (sysfs_streq(buf, "standby\n"))
249c080909eSMark Brown mode = REGULATOR_MODE_STANDBY;
250c080909eSMark Brown else {
251c080909eSMark Brown dev_err(dev, "Configuring invalid mode\n");
252c080909eSMark Brown return count;
253c080909eSMark Brown }
254c080909eSMark Brown
255c080909eSMark Brown mutex_lock(&data->lock);
256c080909eSMark Brown ret = regulator_set_mode(data->regulator, mode);
257c080909eSMark Brown if (ret == 0)
258c080909eSMark Brown data->mode = mode;
259c080909eSMark Brown else
260c080909eSMark Brown dev_err(dev, "Failed to configure mode: %d\n", ret);
261c080909eSMark Brown mutex_unlock(&data->lock);
262c080909eSMark Brown
263c080909eSMark Brown return count;
264c080909eSMark Brown }
265c080909eSMark Brown
26671e06af2SRusty Russell static DEVICE_ATTR(min_microvolts, 0664, show_min_uV, set_min_uV);
26771e06af2SRusty Russell static DEVICE_ATTR(max_microvolts, 0664, show_max_uV, set_max_uV);
26871e06af2SRusty Russell static DEVICE_ATTR(min_microamps, 0664, show_min_uA, set_min_uA);
26971e06af2SRusty Russell static DEVICE_ATTR(max_microamps, 0664, show_max_uA, set_max_uA);
27071e06af2SRusty Russell static DEVICE_ATTR(mode, 0664, show_mode, set_mode);
271c080909eSMark Brown
2724cf95663SDmitry Torokhov static struct attribute *regulator_virtual_attributes[] = {
2734cf95663SDmitry Torokhov &dev_attr_min_microvolts.attr,
2744cf95663SDmitry Torokhov &dev_attr_max_microvolts.attr,
2754cf95663SDmitry Torokhov &dev_attr_min_microamps.attr,
2764cf95663SDmitry Torokhov &dev_attr_max_microamps.attr,
2774cf95663SDmitry Torokhov &dev_attr_mode.attr,
2784cf95663SDmitry Torokhov NULL
279c080909eSMark Brown };
280c080909eSMark Brown
2814cf95663SDmitry Torokhov static const struct attribute_group regulator_virtual_attr_group = {
2824cf95663SDmitry Torokhov .attrs = regulator_virtual_attributes,
2834cf95663SDmitry Torokhov };
2844cf95663SDmitry Torokhov
28580c05665SVincent Whitchurch #ifdef CONFIG_OF
28680c05665SVincent Whitchurch static const struct of_device_id regulator_virtual_consumer_of_match[] = {
28780c05665SVincent Whitchurch { .compatible = "regulator-virtual-consumer" },
28880c05665SVincent Whitchurch {},
28980c05665SVincent Whitchurch };
29080c05665SVincent Whitchurch MODULE_DEVICE_TABLE(of, regulator_virtual_consumer_of_match);
29180c05665SVincent Whitchurch #endif
29280c05665SVincent Whitchurch
regulator_virtual_probe(struct platform_device * pdev)293a5023574SBill Pemberton static int regulator_virtual_probe(struct platform_device *pdev)
294c080909eSMark Brown {
295dff91d0bSJingoo Han char *reg_id = dev_get_platdata(&pdev->dev);
296c080909eSMark Brown struct virtual_consumer_data *drvdata;
297d2fb5487SVincent Whitchurch static bool warned;
2984cf95663SDmitry Torokhov int ret;
299c080909eSMark Brown
300d2fb5487SVincent Whitchurch if (!warned) {
301d2fb5487SVincent Whitchurch warned = true;
302d2fb5487SVincent Whitchurch pr_warn("**********************************************************\n");
303d2fb5487SVincent Whitchurch pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
304d2fb5487SVincent Whitchurch pr_warn("** **\n");
305d2fb5487SVincent Whitchurch pr_warn("** regulator-virtual-consumer is only for testing and **\n");
306d2fb5487SVincent Whitchurch pr_warn("** debugging. Do not use it in a production kernel. **\n");
307d2fb5487SVincent Whitchurch pr_warn("** **\n");
308d2fb5487SVincent Whitchurch pr_warn("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **\n");
309d2fb5487SVincent Whitchurch pr_warn("**********************************************************\n");
310d2fb5487SVincent Whitchurch }
311d2fb5487SVincent Whitchurch
312a81edbdeSAxel Lin drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data),
313a81edbdeSAxel Lin GFP_KERNEL);
3144cf95663SDmitry Torokhov if (drvdata == NULL)
31572b86876SMark Brown return -ENOMEM;
316c080909eSMark Brown
31780c05665SVincent Whitchurch /*
31880c05665SVincent Whitchurch * This virtual consumer does not have any hardware-defined supply
31980c05665SVincent Whitchurch * name, so just allow the regulator to be specified in a property
32080c05665SVincent Whitchurch * named "default-supply" when we're being probed from devicetree.
32180c05665SVincent Whitchurch */
32280c05665SVincent Whitchurch if (!reg_id && pdev->dev.of_node)
32380c05665SVincent Whitchurch reg_id = "default";
32480c05665SVincent Whitchurch
325c080909eSMark Brown mutex_init(&drvdata->lock);
326c080909eSMark Brown
327a81edbdeSAxel Lin drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id);
32875c3543eSVincent Whitchurch if (IS_ERR(drvdata->regulator))
32975c3543eSVincent Whitchurch return dev_err_probe(&pdev->dev, PTR_ERR(drvdata->regulator),
33075c3543eSVincent Whitchurch "Failed to obtain supply '%s'\n",
33175c3543eSVincent Whitchurch reg_id);
332c080909eSMark Brown
3334cf95663SDmitry Torokhov ret = sysfs_create_group(&pdev->dev.kobj,
3344cf95663SDmitry Torokhov ®ulator_virtual_attr_group);
33572b86876SMark Brown if (ret != 0) {
3364cf95663SDmitry Torokhov dev_err(&pdev->dev,
3374cf95663SDmitry Torokhov "Failed to create attribute group: %d\n", ret);
338a81edbdeSAxel Lin return ret;
33972b86876SMark Brown }
340c080909eSMark Brown
341c080909eSMark Brown drvdata->mode = regulator_get_mode(drvdata->regulator);
342c080909eSMark Brown
343c080909eSMark Brown platform_set_drvdata(pdev, drvdata);
344c080909eSMark Brown
345c080909eSMark Brown return 0;
346c080909eSMark Brown }
347c080909eSMark Brown
regulator_virtual_remove(struct platform_device * pdev)3488dc995f5SBill Pemberton static int regulator_virtual_remove(struct platform_device *pdev)
349c080909eSMark Brown {
350c080909eSMark Brown struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev);
351c080909eSMark Brown
3524cf95663SDmitry Torokhov sysfs_remove_group(&pdev->dev.kobj, ®ulator_virtual_attr_group);
3534cf95663SDmitry Torokhov
354c080909eSMark Brown if (drvdata->enabled)
355c080909eSMark Brown regulator_disable(drvdata->regulator);
356c080909eSMark Brown
357c080909eSMark Brown return 0;
358c080909eSMark Brown }
359c080909eSMark Brown
360c080909eSMark Brown static struct platform_driver regulator_virtual_consumer_driver = {
3614cf95663SDmitry Torokhov .probe = regulator_virtual_probe,
3625eb9f2b9SBill Pemberton .remove = regulator_virtual_remove,
363c080909eSMark Brown .driver = {
364c080909eSMark Brown .name = "reg-virt-consumer",
365*259b93b2SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS,
36680c05665SVincent Whitchurch .of_match_table = of_match_ptr(regulator_virtual_consumer_of_match),
367c080909eSMark Brown },
368c080909eSMark Brown };
369c080909eSMark Brown
370005d610fSAxel Lin module_platform_driver(regulator_virtual_consumer_driver);
371c080909eSMark Brown
372c080909eSMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
373c080909eSMark Brown MODULE_DESCRIPTION("Virtual regulator consumer");
374c080909eSMark Brown MODULE_LICENSE("GPL");
37538c53c89SMark Brown MODULE_ALIAS("platform:reg-virt-consumer");
376