xref: /openbmc/linux/drivers/regulator/virtual.c (revision 259b93b2)
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 				 &regulator_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, &regulator_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