diff options
Diffstat (limited to 'drivers/regulator/pwm-regulator.c')
-rw-r--r-- | drivers/regulator/pwm-regulator.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c new file mode 100644 index 000000000000..d3f55eaea058 --- /dev/null +++ b/drivers/regulator/pwm-regulator.c | |||
@@ -0,0 +1,197 @@ | |||
1 | /* | ||
2 | * Regulator driver for PWM Regulators | ||
3 | * | ||
4 | * Copyright (C) 2014 - STMicroelectronics Inc. | ||
5 | * | ||
6 | * Author: Lee Jones <lee.jones@linaro.org> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/regulator/driver.h> | ||
17 | #include <linux/regulator/machine.h> | ||
18 | #include <linux/regulator/of_regulator.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/of_device.h> | ||
21 | #include <linux/pwm.h> | ||
22 | |||
23 | struct pwm_regulator_data { | ||
24 | struct regulator_desc desc; | ||
25 | struct pwm_voltages *duty_cycle_table; | ||
26 | struct pwm_device *pwm; | ||
27 | bool enabled; | ||
28 | int state; | ||
29 | }; | ||
30 | |||
31 | struct pwm_voltages { | ||
32 | unsigned int uV; | ||
33 | unsigned int dutycycle; | ||
34 | }; | ||
35 | |||
36 | static int pwm_regulator_get_voltage_sel(struct regulator_dev *dev) | ||
37 | { | ||
38 | struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev); | ||
39 | |||
40 | return drvdata->state; | ||
41 | } | ||
42 | |||
43 | static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev, | ||
44 | unsigned selector) | ||
45 | { | ||
46 | struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev); | ||
47 | unsigned int pwm_reg_period; | ||
48 | int dutycycle; | ||
49 | int ret; | ||
50 | |||
51 | pwm_reg_period = pwm_get_period(drvdata->pwm); | ||
52 | |||
53 | dutycycle = (pwm_reg_period * | ||
54 | drvdata->duty_cycle_table[selector].dutycycle) / 100; | ||
55 | |||
56 | ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period); | ||
57 | if (ret) { | ||
58 | dev_err(&dev->dev, "Failed to configure PWM\n"); | ||
59 | return ret; | ||
60 | } | ||
61 | |||
62 | drvdata->state = selector; | ||
63 | |||
64 | if (!drvdata->enabled) { | ||
65 | ret = pwm_enable(drvdata->pwm); | ||
66 | if (ret) { | ||
67 | dev_err(&dev->dev, "Failed to enable PWM\n"); | ||
68 | return ret; | ||
69 | } | ||
70 | drvdata->enabled = true; | ||
71 | } | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static int pwm_regulator_list_voltage(struct regulator_dev *dev, | ||
77 | unsigned selector) | ||
78 | { | ||
79 | struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev); | ||
80 | |||
81 | if (selector >= drvdata->desc.n_voltages) | ||
82 | return -EINVAL; | ||
83 | |||
84 | return drvdata->duty_cycle_table[selector].uV; | ||
85 | } | ||
86 | |||
87 | static struct regulator_ops pwm_regulator_voltage_ops = { | ||
88 | .set_voltage_sel = pwm_regulator_set_voltage_sel, | ||
89 | .get_voltage_sel = pwm_regulator_get_voltage_sel, | ||
90 | .list_voltage = pwm_regulator_list_voltage, | ||
91 | .map_voltage = regulator_map_voltage_iterate, | ||
92 | }; | ||
93 | |||
94 | static const struct regulator_desc pwm_regulator_desc = { | ||
95 | .name = "pwm-regulator", | ||
96 | .ops = &pwm_regulator_voltage_ops, | ||
97 | .type = REGULATOR_VOLTAGE, | ||
98 | .owner = THIS_MODULE, | ||
99 | .supply_name = "pwm", | ||
100 | }; | ||
101 | |||
102 | static int pwm_regulator_probe(struct platform_device *pdev) | ||
103 | { | ||
104 | struct pwm_regulator_data *drvdata; | ||
105 | struct property *prop; | ||
106 | struct regulator_dev *regulator; | ||
107 | struct regulator_config config = { }; | ||
108 | struct device_node *np = pdev->dev.of_node; | ||
109 | int length, ret; | ||
110 | |||
111 | if (!np) { | ||
112 | dev_err(&pdev->dev, "Device Tree node missing\n"); | ||
113 | return -EINVAL; | ||
114 | } | ||
115 | |||
116 | drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); | ||
117 | if (!drvdata) | ||
118 | return -ENOMEM; | ||
119 | |||
120 | memcpy(&drvdata->desc, &pwm_regulator_desc, sizeof(pwm_regulator_desc)); | ||
121 | |||
122 | /* determine the number of voltage-table */ | ||
123 | prop = of_find_property(np, "voltage-table", &length); | ||
124 | if (!prop) { | ||
125 | dev_err(&pdev->dev, "No voltage-table\n"); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | if ((length < sizeof(*drvdata->duty_cycle_table)) || | ||
130 | (length % sizeof(*drvdata->duty_cycle_table))) { | ||
131 | dev_err(&pdev->dev, "voltage-table length(%d) is invalid\n", | ||
132 | length); | ||
133 | return -EINVAL; | ||
134 | } | ||
135 | |||
136 | drvdata->desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table); | ||
137 | |||
138 | drvdata->duty_cycle_table = devm_kzalloc(&pdev->dev, | ||
139 | length, GFP_KERNEL); | ||
140 | if (!drvdata->duty_cycle_table) | ||
141 | return -ENOMEM; | ||
142 | |||
143 | /* read voltage table from DT property */ | ||
144 | ret = of_property_read_u32_array(np, "voltage-table", | ||
145 | (u32 *)drvdata->duty_cycle_table, | ||
146 | length / sizeof(u32)); | ||
147 | if (ret < 0) { | ||
148 | dev_err(&pdev->dev, "read voltage-table failed\n"); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | config.init_data = of_get_regulator_init_data(&pdev->dev, np); | ||
153 | if (!config.init_data) | ||
154 | return -ENOMEM; | ||
155 | |||
156 | config.of_node = np; | ||
157 | config.dev = &pdev->dev; | ||
158 | config.driver_data = drvdata; | ||
159 | |||
160 | drvdata->pwm = devm_pwm_get(&pdev->dev, NULL); | ||
161 | if (IS_ERR(drvdata->pwm)) { | ||
162 | dev_err(&pdev->dev, "Failed to get PWM\n"); | ||
163 | return PTR_ERR(drvdata->pwm); | ||
164 | } | ||
165 | |||
166 | regulator = devm_regulator_register(&pdev->dev, | ||
167 | &drvdata->desc, &config); | ||
168 | if (IS_ERR(regulator)) { | ||
169 | dev_err(&pdev->dev, "Failed to register regulator %s\n", | ||
170 | drvdata->desc.name); | ||
171 | return PTR_ERR(regulator); | ||
172 | } | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static const struct of_device_id pwm_of_match[] = { | ||
178 | { .compatible = "pwm-regulator" }, | ||
179 | { }, | ||
180 | }; | ||
181 | MODULE_DEVICE_TABLE(of, pwm_of_match); | ||
182 | |||
183 | static struct platform_driver pwm_regulator_driver = { | ||
184 | .driver = { | ||
185 | .name = "pwm-regulator", | ||
186 | .owner = THIS_MODULE, | ||
187 | .of_match_table = of_match_ptr(pwm_of_match), | ||
188 | }, | ||
189 | .probe = pwm_regulator_probe, | ||
190 | }; | ||
191 | |||
192 | module_platform_driver(pwm_regulator_driver); | ||
193 | |||
194 | MODULE_LICENSE("GPL"); | ||
195 | MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>"); | ||
196 | MODULE_DESCRIPTION("PWM Regulator Driver"); | ||
197 | MODULE_ALIAS("platform:pwm-regulator"); | ||