xref: /linux/drivers/regulator/pwm-regulator.c (revision 1de7d80246a047edaa4ea7de225437571a3aec34)
1aa66cc66SChris Zhong /*
2aa66cc66SChris Zhong  * Regulator driver for PWM Regulators
3aa66cc66SChris Zhong  *
4aa66cc66SChris Zhong  * Copyright (C) 2014 - STMicroelectronics Inc.
5aa66cc66SChris Zhong  *
6aa66cc66SChris Zhong  * Author: Lee Jones <lee.jones@linaro.org>
7aa66cc66SChris Zhong  *
8aa66cc66SChris Zhong  * This program is free software; you can redistribute it and/or modify
9aa66cc66SChris Zhong  * it under the terms of the GNU General Public License version 2 as
10aa66cc66SChris Zhong  * published by the Free Software Foundation.
11aa66cc66SChris Zhong  */
12aa66cc66SChris Zhong 
134773be18SLee Jones #include <linux/delay.h>
14aa66cc66SChris Zhong #include <linux/module.h>
15aa66cc66SChris Zhong #include <linux/init.h>
16aa66cc66SChris Zhong #include <linux/err.h>
17aa66cc66SChris Zhong #include <linux/regulator/driver.h>
18aa66cc66SChris Zhong #include <linux/regulator/machine.h>
19aa66cc66SChris Zhong #include <linux/regulator/of_regulator.h>
20aa66cc66SChris Zhong #include <linux/of.h>
21aa66cc66SChris Zhong #include <linux/of_device.h>
22aa66cc66SChris Zhong #include <linux/pwm.h>
23aa66cc66SChris Zhong 
24aa66cc66SChris Zhong struct pwm_regulator_data {
254773be18SLee Jones 	/*  Shared */
26aa66cc66SChris Zhong 	struct pwm_device *pwm;
274773be18SLee Jones 
284773be18SLee Jones 	/* Voltage table */
294773be18SLee Jones 	struct pwm_voltages *duty_cycle_table;
30aa66cc66SChris Zhong 	int state;
314773be18SLee Jones 
324773be18SLee Jones 	/* Continuous voltage */
334773be18SLee Jones 	int volt_uV;
34aa66cc66SChris Zhong };
35aa66cc66SChris Zhong 
36aa66cc66SChris Zhong struct pwm_voltages {
37aa66cc66SChris Zhong 	unsigned int uV;
38aa66cc66SChris Zhong 	unsigned int dutycycle;
39aa66cc66SChris Zhong };
40aa66cc66SChris Zhong 
414773be18SLee Jones /**
424773be18SLee Jones  * Voltage table call-backs
434773be18SLee Jones  */
44ab101e35SLee Jones static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
45aa66cc66SChris Zhong {
46ab101e35SLee Jones 	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
47aa66cc66SChris Zhong 
48aa66cc66SChris Zhong 	return drvdata->state;
49aa66cc66SChris Zhong }
50aa66cc66SChris Zhong 
51ab101e35SLee Jones static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
52aa66cc66SChris Zhong 					 unsigned selector)
53aa66cc66SChris Zhong {
54ab101e35SLee Jones 	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
55aa66cc66SChris Zhong 	unsigned int pwm_reg_period;
56aa66cc66SChris Zhong 	int dutycycle;
57aa66cc66SChris Zhong 	int ret;
58aa66cc66SChris Zhong 
59aa66cc66SChris Zhong 	pwm_reg_period = pwm_get_period(drvdata->pwm);
60aa66cc66SChris Zhong 
61aa66cc66SChris Zhong 	dutycycle = (pwm_reg_period *
62aa66cc66SChris Zhong 		    drvdata->duty_cycle_table[selector].dutycycle) / 100;
63aa66cc66SChris Zhong 
64aa66cc66SChris Zhong 	ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period);
65aa66cc66SChris Zhong 	if (ret) {
66ab101e35SLee Jones 		dev_err(&rdev->dev, "Failed to configure PWM\n");
67aa66cc66SChris Zhong 		return ret;
68aa66cc66SChris Zhong 	}
69aa66cc66SChris Zhong 
70aa66cc66SChris Zhong 	drvdata->state = selector;
71aa66cc66SChris Zhong 
72aa66cc66SChris Zhong 	return 0;
73aa66cc66SChris Zhong }
74aa66cc66SChris Zhong 
75ab101e35SLee Jones static int pwm_regulator_list_voltage(struct regulator_dev *rdev,
76aa66cc66SChris Zhong 				      unsigned selector)
77aa66cc66SChris Zhong {
78ab101e35SLee Jones 	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
79aa66cc66SChris Zhong 
80ab101e35SLee Jones 	if (selector >= rdev->desc->n_voltages)
81aa66cc66SChris Zhong 		return -EINVAL;
82aa66cc66SChris Zhong 
83aa66cc66SChris Zhong 	return drvdata->duty_cycle_table[selector].uV;
84aa66cc66SChris Zhong }
854773be18SLee Jones 
86*1de7d802SBoris Brezillon static int pwm_regulator_enable(struct regulator_dev *dev)
87*1de7d802SBoris Brezillon {
88*1de7d802SBoris Brezillon 	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
89*1de7d802SBoris Brezillon 
90*1de7d802SBoris Brezillon 	return pwm_enable(drvdata->pwm);
91*1de7d802SBoris Brezillon }
92*1de7d802SBoris Brezillon 
93*1de7d802SBoris Brezillon static int pwm_regulator_disable(struct regulator_dev *dev)
94*1de7d802SBoris Brezillon {
95*1de7d802SBoris Brezillon 	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
96*1de7d802SBoris Brezillon 
97*1de7d802SBoris Brezillon 	pwm_disable(drvdata->pwm);
98*1de7d802SBoris Brezillon 
99*1de7d802SBoris Brezillon 	return 0;
100*1de7d802SBoris Brezillon }
101*1de7d802SBoris Brezillon 
102*1de7d802SBoris Brezillon static int pwm_regulator_is_enabled(struct regulator_dev *dev)
103*1de7d802SBoris Brezillon {
104*1de7d802SBoris Brezillon 	struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
105*1de7d802SBoris Brezillon 
106*1de7d802SBoris Brezillon 	return pwm_is_enabled(drvdata->pwm);
107*1de7d802SBoris Brezillon }
108*1de7d802SBoris Brezillon 
1094773be18SLee Jones /**
1104773be18SLee Jones  * Continuous voltage call-backs
1114773be18SLee Jones  */
112f3f6439dSLee Jones static int pwm_voltage_to_duty_cycle_percentage(struct regulator_dev *rdev, int req_uV)
1134773be18SLee Jones {
114cae897deSLee Jones 	int min_uV = rdev->constraints->min_uV;
115cae897deSLee Jones 	int max_uV = rdev->constraints->max_uV;
116cae897deSLee Jones 	int diff = max_uV - min_uV;
1174773be18SLee Jones 
118f3f6439dSLee Jones 	return 100 - (((req_uV * 100) - (min_uV * 100)) / diff);
1194773be18SLee Jones }
1204773be18SLee Jones 
1214773be18SLee Jones static int pwm_regulator_get_voltage(struct regulator_dev *rdev)
1224773be18SLee Jones {
1234773be18SLee Jones 	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
1244773be18SLee Jones 
1254773be18SLee Jones 	return drvdata->volt_uV;
1264773be18SLee Jones }
1274773be18SLee Jones 
1284773be18SLee Jones static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
1294773be18SLee Jones 					int min_uV, int max_uV,
1304773be18SLee Jones 					unsigned *selector)
1314773be18SLee Jones {
1324773be18SLee Jones 	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
1334773be18SLee Jones 	unsigned int ramp_delay = rdev->constraints->ramp_delay;
134f3f6439dSLee Jones 	unsigned int period = pwm_get_period(drvdata->pwm);
1354773be18SLee Jones 	int duty_cycle;
1364773be18SLee Jones 	int ret;
1374773be18SLee Jones 
138f3f6439dSLee Jones 	duty_cycle = pwm_voltage_to_duty_cycle_percentage(rdev, min_uV);
1394773be18SLee Jones 
140f3f6439dSLee Jones 	ret = pwm_config(drvdata->pwm, (period / 100) * duty_cycle, period);
1414773be18SLee Jones 	if (ret) {
1424773be18SLee Jones 		dev_err(&rdev->dev, "Failed to configure PWM\n");
1434773be18SLee Jones 		return ret;
1444773be18SLee Jones 	}
1454773be18SLee Jones 
1464773be18SLee Jones 	ret = pwm_enable(drvdata->pwm);
1474773be18SLee Jones 	if (ret) {
1484773be18SLee Jones 		dev_err(&rdev->dev, "Failed to enable PWM\n");
1494773be18SLee Jones 		return ret;
1504773be18SLee Jones 	}
1514773be18SLee Jones 	drvdata->volt_uV = min_uV;
1524773be18SLee Jones 
1534773be18SLee Jones 	/* Delay required by PWM regulator to settle to the new voltage */
1544773be18SLee Jones 	usleep_range(ramp_delay, ramp_delay + 1000);
1554773be18SLee Jones 
1564773be18SLee Jones 	return 0;
1574773be18SLee Jones }
1584773be18SLee Jones 
159f9178dadSLee Jones static struct regulator_ops pwm_regulator_voltage_table_ops = {
160aa66cc66SChris Zhong 	.set_voltage_sel = pwm_regulator_set_voltage_sel,
161aa66cc66SChris Zhong 	.get_voltage_sel = pwm_regulator_get_voltage_sel,
162aa66cc66SChris Zhong 	.list_voltage    = pwm_regulator_list_voltage,
163aa66cc66SChris Zhong 	.map_voltage     = regulator_map_voltage_iterate,
164*1de7d802SBoris Brezillon 	.enable          = pwm_regulator_enable,
165*1de7d802SBoris Brezillon 	.disable         = pwm_regulator_disable,
166*1de7d802SBoris Brezillon 	.is_enabled      = pwm_regulator_is_enabled,
167aa66cc66SChris Zhong };
168aa66cc66SChris Zhong 
1694773be18SLee Jones static struct regulator_ops pwm_regulator_voltage_continuous_ops = {
1704773be18SLee Jones 	.get_voltage = pwm_regulator_get_voltage,
1714773be18SLee Jones 	.set_voltage = pwm_regulator_set_voltage,
172*1de7d802SBoris Brezillon 	.enable          = pwm_regulator_enable,
173*1de7d802SBoris Brezillon 	.disable         = pwm_regulator_disable,
174*1de7d802SBoris Brezillon 	.is_enabled      = pwm_regulator_is_enabled,
1754773be18SLee Jones };
1764773be18SLee Jones 
177b6f55e74SLee Jones static struct regulator_desc pwm_regulator_desc = {
178aa66cc66SChris Zhong 	.name		= "pwm-regulator",
179aa66cc66SChris Zhong 	.type		= REGULATOR_VOLTAGE,
180aa66cc66SChris Zhong 	.owner		= THIS_MODULE,
181aa66cc66SChris Zhong 	.supply_name    = "pwm",
182aa66cc66SChris Zhong };
183aa66cc66SChris Zhong 
184f9178dadSLee Jones static int pwm_regulator_init_table(struct platform_device *pdev,
185f9178dadSLee Jones 				    struct pwm_regulator_data *drvdata)
186f9178dadSLee Jones {
187f9178dadSLee Jones 	struct device_node *np = pdev->dev.of_node;
188f9178dadSLee Jones 	struct pwm_voltages *duty_cycle_table;
18960cb65ebSLee Jones 	unsigned int length = 0;
190f9178dadSLee Jones 	int ret;
191f9178dadSLee Jones 
192f9178dadSLee Jones 	of_find_property(np, "voltage-table", &length);
193f9178dadSLee Jones 
194f9178dadSLee Jones 	if ((length < sizeof(*duty_cycle_table)) ||
195f9178dadSLee Jones 	    (length % sizeof(*duty_cycle_table))) {
196f9178dadSLee Jones 		dev_err(&pdev->dev,
197f9178dadSLee Jones 			"voltage-table length(%d) is invalid\n",
198f9178dadSLee Jones 			length);
199f9178dadSLee Jones 		return -EINVAL;
200f9178dadSLee Jones 	}
201f9178dadSLee Jones 
202f9178dadSLee Jones 	duty_cycle_table = devm_kzalloc(&pdev->dev, length, GFP_KERNEL);
203f9178dadSLee Jones 	if (!duty_cycle_table)
204f9178dadSLee Jones 		return -ENOMEM;
205f9178dadSLee Jones 
206f9178dadSLee Jones 	ret = of_property_read_u32_array(np, "voltage-table",
207f9178dadSLee Jones 					 (u32 *)duty_cycle_table,
208f9178dadSLee Jones 					 length / sizeof(u32));
209f9178dadSLee Jones 	if (ret) {
210f9178dadSLee Jones 		dev_err(&pdev->dev, "Failed to read voltage-table\n");
211f9178dadSLee Jones 		return ret;
212f9178dadSLee Jones 	}
213f9178dadSLee Jones 
214f9178dadSLee Jones 	drvdata->duty_cycle_table	= duty_cycle_table;
215f9178dadSLee Jones 	pwm_regulator_desc.ops		= &pwm_regulator_voltage_table_ops;
216f9178dadSLee Jones 	pwm_regulator_desc.n_voltages	= length / sizeof(*duty_cycle_table);
217f9178dadSLee Jones 
218f9178dadSLee Jones 	return 0;
219f9178dadSLee Jones }
220f9178dadSLee Jones 
2214773be18SLee Jones static int pwm_regulator_init_continuous(struct platform_device *pdev,
2224773be18SLee Jones 					 struct pwm_regulator_data *drvdata)
2234773be18SLee Jones {
2244773be18SLee Jones 	pwm_regulator_desc.ops = &pwm_regulator_voltage_continuous_ops;
2254773be18SLee Jones 	pwm_regulator_desc.continuous_voltage_range = true;
2264773be18SLee Jones 
2274773be18SLee Jones 	return 0;
2284773be18SLee Jones }
2294773be18SLee Jones 
230aa66cc66SChris Zhong static int pwm_regulator_probe(struct platform_device *pdev)
231aa66cc66SChris Zhong {
2325ad2cb14SLee Jones 	const struct regulator_init_data *init_data;
233aa66cc66SChris Zhong 	struct pwm_regulator_data *drvdata;
234aa66cc66SChris Zhong 	struct regulator_dev *regulator;
235aa66cc66SChris Zhong 	struct regulator_config config = { };
236aa66cc66SChris Zhong 	struct device_node *np = pdev->dev.of_node;
237f9178dadSLee Jones 	int ret;
238aa66cc66SChris Zhong 
239aa66cc66SChris Zhong 	if (!np) {
240aa66cc66SChris Zhong 		dev_err(&pdev->dev, "Device Tree node missing\n");
241aa66cc66SChris Zhong 		return -EINVAL;
242aa66cc66SChris Zhong 	}
243aa66cc66SChris Zhong 
244aa66cc66SChris Zhong 	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
245aa66cc66SChris Zhong 	if (!drvdata)
246aa66cc66SChris Zhong 		return -ENOMEM;
247aa66cc66SChris Zhong 
2484773be18SLee Jones 	if (of_find_property(np, "voltage-table", NULL))
249f9178dadSLee Jones 		ret = pwm_regulator_init_table(pdev, drvdata);
2504773be18SLee Jones 	else
2514773be18SLee Jones 		ret = pwm_regulator_init_continuous(pdev, drvdata);
252f9178dadSLee Jones 	if (ret)
253aa66cc66SChris Zhong 		return ret;
254aa66cc66SChris Zhong 
2555ad2cb14SLee Jones 	init_data = of_get_regulator_init_data(&pdev->dev, np,
256b6f55e74SLee Jones 					       &pwm_regulator_desc);
2575ad2cb14SLee Jones 	if (!init_data)
258aa66cc66SChris Zhong 		return -ENOMEM;
259aa66cc66SChris Zhong 
260aa66cc66SChris Zhong 	config.of_node = np;
261aa66cc66SChris Zhong 	config.dev = &pdev->dev;
262aa66cc66SChris Zhong 	config.driver_data = drvdata;
2635ad2cb14SLee Jones 	config.init_data = init_data;
264aa66cc66SChris Zhong 
265aa66cc66SChris Zhong 	drvdata->pwm = devm_pwm_get(&pdev->dev, NULL);
266aa66cc66SChris Zhong 	if (IS_ERR(drvdata->pwm)) {
267aa66cc66SChris Zhong 		dev_err(&pdev->dev, "Failed to get PWM\n");
268aa66cc66SChris Zhong 		return PTR_ERR(drvdata->pwm);
269aa66cc66SChris Zhong 	}
270aa66cc66SChris Zhong 
271aa66cc66SChris Zhong 	regulator = devm_regulator_register(&pdev->dev,
272b6f55e74SLee Jones 					    &pwm_regulator_desc, &config);
273aa66cc66SChris Zhong 	if (IS_ERR(regulator)) {
274aa66cc66SChris Zhong 		dev_err(&pdev->dev, "Failed to register regulator %s\n",
275b6f55e74SLee Jones 			pwm_regulator_desc.name);
276aa66cc66SChris Zhong 		return PTR_ERR(regulator);
277aa66cc66SChris Zhong 	}
278aa66cc66SChris Zhong 
279aa66cc66SChris Zhong 	return 0;
280aa66cc66SChris Zhong }
281aa66cc66SChris Zhong 
282aa66cc66SChris Zhong static const struct of_device_id pwm_of_match[] = {
283aa66cc66SChris Zhong 	{ .compatible = "pwm-regulator" },
284aa66cc66SChris Zhong 	{ },
285aa66cc66SChris Zhong };
286aa66cc66SChris Zhong MODULE_DEVICE_TABLE(of, pwm_of_match);
287aa66cc66SChris Zhong 
288aa66cc66SChris Zhong static struct platform_driver pwm_regulator_driver = {
289aa66cc66SChris Zhong 	.driver = {
290aa66cc66SChris Zhong 		.name		= "pwm-regulator",
291aa66cc66SChris Zhong 		.of_match_table = of_match_ptr(pwm_of_match),
292aa66cc66SChris Zhong 	},
293aa66cc66SChris Zhong 	.probe = pwm_regulator_probe,
294aa66cc66SChris Zhong };
295aa66cc66SChris Zhong 
296aa66cc66SChris Zhong module_platform_driver(pwm_regulator_driver);
297aa66cc66SChris Zhong 
298aa66cc66SChris Zhong MODULE_LICENSE("GPL");
299aa66cc66SChris Zhong MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
300aa66cc66SChris Zhong MODULE_DESCRIPTION("PWM Regulator Driver");
301aa66cc66SChris Zhong MODULE_ALIAS("platform:pwm-regulator");
302