]>
Commit | Line | Data |
---|---|---|
8b908520 AL |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Device driver for regulators in Hi6421 IC | |
4 | // | |
5 | // Copyright (c) <2011-2014> HiSilicon Technologies Co., Ltd. | |
6 | // http://www.hisilicon.com | |
7 | // Copyright (c) <2013-2014> Linaro Ltd. | |
8 | // http://www.linaro.org | |
9 | // | |
10 | // Author: Guodong Xu <guodong.xu@linaro.org> | |
87ca186f GX |
11 | |
12 | #include <linux/slab.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/err.h> | |
87ca186f GX |
16 | #include <linux/platform_device.h> |
17 | #include <linux/of.h> | |
87ca186f GX |
18 | #include <linux/regmap.h> |
19 | #include <linux/regulator/driver.h> | |
20 | #include <linux/regulator/machine.h> | |
21 | #include <linux/regulator/of_regulator.h> | |
22 | #include <linux/mfd/hi6421-pmic.h> | |
87ca186f GX |
23 | |
24 | /* | |
25 | * struct hi6421_regulator_pdata - Hi6421 regulator data of platform device | |
26 | * @lock: mutex to serialize regulator enable | |
27 | */ | |
28 | struct hi6421_regulator_pdata { | |
29 | struct mutex lock; | |
30 | }; | |
31 | ||
32 | /* | |
33 | * struct hi6421_regulator_info - hi6421 regulator information | |
87ca186f | 34 | * @desc: regulator description |
87ca186f | 35 | * @mode_mask: ECO mode bitmask of LDOs; for BUCKs, this masks sleep |
97795e4d | 36 | * @eco_microamp: eco mode load upper limit (in uA), valid for LDOs only |
87ca186f GX |
37 | */ |
38 | struct hi6421_regulator_info { | |
87ca186f | 39 | struct regulator_desc desc; |
87ca186f GX |
40 | u8 mode_mask; |
41 | u32 eco_microamp; | |
87ca186f GX |
42 | }; |
43 | ||
44 | /* HI6421 regulators */ | |
45 | enum hi6421_regulator_id { | |
46 | HI6421_LDO0, | |
47 | HI6421_LDO1, | |
48 | HI6421_LDO2, | |
49 | HI6421_LDO3, | |
50 | HI6421_LDO4, | |
51 | HI6421_LDO5, | |
52 | HI6421_LDO6, | |
53 | HI6421_LDO7, | |
54 | HI6421_LDO8, | |
55 | HI6421_LDO9, | |
56 | HI6421_LDO10, | |
57 | HI6421_LDO11, | |
58 | HI6421_LDO12, | |
59 | HI6421_LDO13, | |
60 | HI6421_LDO14, | |
61 | HI6421_LDO15, | |
62 | HI6421_LDO16, | |
63 | HI6421_LDO17, | |
64 | HI6421_LDO18, | |
65 | HI6421_LDO19, | |
66 | HI6421_LDO20, | |
67 | HI6421_LDOAUDIO, | |
68 | HI6421_BUCK0, | |
69 | HI6421_BUCK1, | |
70 | HI6421_BUCK2, | |
71 | HI6421_BUCK3, | |
72 | HI6421_BUCK4, | |
73 | HI6421_BUCK5, | |
74 | HI6421_NUM_REGULATORS, | |
75 | }; | |
76 | ||
87ca186f GX |
77 | /* LDO 0, 4~7, 9~14, 16~20 have same voltage table. */ |
78 | static const unsigned int ldo_0_voltages[] = { | |
79 | 1500000, 1800000, 2400000, 2500000, | |
80 | 2600000, 2700000, 2850000, 3000000, | |
81 | }; | |
82 | ||
83 | /* LDO 8, 15 have same voltage table. */ | |
84 | static const unsigned int ldo_8_voltages[] = { | |
85 | 1500000, 1800000, 2400000, 2600000, | |
86 | 2700000, 2850000, 3000000, 3300000, | |
87 | }; | |
88 | ||
89 | /* Ranges are sorted in ascending order. */ | |
60ab7f41 | 90 | static const struct linear_range ldo_audio_volt_range[] = { |
87ca186f GX |
91 | REGULATOR_LINEAR_RANGE(2800000, 0, 3, 50000), |
92 | REGULATOR_LINEAR_RANGE(3000000, 4, 7, 100000), | |
93 | }; | |
94 | ||
95 | static const unsigned int buck_3_voltages[] = { | |
96 | 950000, 1050000, 1100000, 1117000, | |
97 | 1134000, 1150000, 1167000, 1200000, | |
98 | }; | |
99 | ||
100 | static const unsigned int buck_4_voltages[] = { | |
101 | 1150000, 1200000, 1250000, 1350000, | |
102 | 1700000, 1800000, 1900000, 2000000, | |
103 | }; | |
104 | ||
105 | static const unsigned int buck_5_voltages[] = { | |
106 | 1150000, 1200000, 1250000, 1350000, | |
107 | 1600000, 1700000, 1800000, 1900000, | |
108 | }; | |
109 | ||
110 | static const struct regulator_ops hi6421_ldo_ops; | |
111 | static const struct regulator_ops hi6421_ldo_linear_ops; | |
112 | static const struct regulator_ops hi6421_ldo_linear_range_ops; | |
113 | static const struct regulator_ops hi6421_buck012_ops; | |
114 | static const struct regulator_ops hi6421_buck345_ops; | |
115 | ||
116 | #define HI6421_LDO_ENABLE_TIME (350) | |
117 | /* | |
118 | * _id - LDO id name string | |
29dc269a | 119 | * _match - of match name string |
87ca186f GX |
120 | * v_table - voltage table |
121 | * vreg - voltage select register | |
122 | * vmask - voltage select mask | |
123 | * ereg - enable register | |
124 | * emask - enable mask | |
125 | * odelay - off/on delay time in uS | |
126 | * ecomask - eco mode mask | |
5c5e417b | 127 | * ecoamp - eco mode load uppler limit in uA |
87ca186f | 128 | */ |
29dc269a | 129 | #define HI6421_LDO(_id, _match, v_table, vreg, vmask, ereg, emask, \ |
87ca186f GX |
130 | odelay, ecomask, ecoamp) \ |
131 | [HI6421_##_id] = { \ | |
132 | .desc = { \ | |
133 | .name = #_id, \ | |
29dc269a AL |
134 | .of_match = of_match_ptr(#_match), \ |
135 | .regulators_node = of_match_ptr("regulators"), \ | |
87ca186f GX |
136 | .ops = &hi6421_ldo_ops, \ |
137 | .type = REGULATOR_VOLTAGE, \ | |
138 | .id = HI6421_##_id, \ | |
139 | .owner = THIS_MODULE, \ | |
140 | .n_voltages = ARRAY_SIZE(v_table), \ | |
141 | .volt_table = v_table, \ | |
142 | .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \ | |
143 | .vsel_mask = vmask, \ | |
144 | .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \ | |
145 | .enable_mask = emask, \ | |
146 | .enable_time = HI6421_LDO_ENABLE_TIME, \ | |
147 | .off_on_delay = odelay, \ | |
148 | }, \ | |
149 | .mode_mask = ecomask, \ | |
150 | .eco_microamp = ecoamp, \ | |
87ca186f GX |
151 | } |
152 | ||
153 | /* HI6421 LDO1~3 are linear voltage regulators at fixed uV_step | |
154 | * | |
155 | * _id - LDO id name string | |
29dc269a | 156 | * _match - of match name string |
87ca186f GX |
157 | * _min_uV - minimum voltage supported in uV |
158 | * n_volt - number of votages available | |
159 | * vstep - voltage increase in each linear step in uV | |
160 | * vreg - voltage select register | |
161 | * vmask - voltage select mask | |
162 | * ereg - enable register | |
163 | * emask - enable mask | |
164 | * odelay - off/on delay time in uS | |
165 | * ecomask - eco mode mask | |
5c5e417b | 166 | * ecoamp - eco mode load uppler limit in uA |
87ca186f | 167 | */ |
29dc269a | 168 | #define HI6421_LDO_LINEAR(_id, _match, _min_uV, n_volt, vstep, vreg, vmask,\ |
87ca186f GX |
169 | ereg, emask, odelay, ecomask, ecoamp) \ |
170 | [HI6421_##_id] = { \ | |
171 | .desc = { \ | |
172 | .name = #_id, \ | |
29dc269a AL |
173 | .of_match = of_match_ptr(#_match), \ |
174 | .regulators_node = of_match_ptr("regulators"), \ | |
87ca186f GX |
175 | .ops = &hi6421_ldo_linear_ops, \ |
176 | .type = REGULATOR_VOLTAGE, \ | |
177 | .id = HI6421_##_id, \ | |
178 | .owner = THIS_MODULE, \ | |
179 | .min_uV = _min_uV, \ | |
180 | .n_voltages = n_volt, \ | |
181 | .uV_step = vstep, \ | |
182 | .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \ | |
183 | .vsel_mask = vmask, \ | |
184 | .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \ | |
185 | .enable_mask = emask, \ | |
186 | .enable_time = HI6421_LDO_ENABLE_TIME, \ | |
187 | .off_on_delay = odelay, \ | |
188 | }, \ | |
189 | .mode_mask = ecomask, \ | |
190 | .eco_microamp = ecoamp, \ | |
87ca186f GX |
191 | } |
192 | ||
193 | /* HI6421 LDOAUDIO is a linear voltage regulator with two 4-step ranges | |
194 | * | |
195 | * _id - LDO id name string | |
29dc269a | 196 | * _match - of match name string |
87ca186f | 197 | * n_volt - number of votages available |
60ab7f41 | 198 | * volt_ranges - array of linear_range |
87ca186f GX |
199 | * vstep - voltage increase in each linear step in uV |
200 | * vreg - voltage select register | |
201 | * vmask - voltage select mask | |
202 | * ereg - enable register | |
203 | * emask - enable mask | |
204 | * odelay - off/on delay time in uS | |
205 | * ecomask - eco mode mask | |
5c5e417b | 206 | * ecoamp - eco mode load uppler limit in uA |
87ca186f | 207 | */ |
29dc269a | 208 | #define HI6421_LDO_LINEAR_RANGE(_id, _match, n_volt, volt_ranges, vreg, vmask,\ |
87ca186f GX |
209 | ereg, emask, odelay, ecomask, ecoamp) \ |
210 | [HI6421_##_id] = { \ | |
211 | .desc = { \ | |
212 | .name = #_id, \ | |
29dc269a AL |
213 | .of_match = of_match_ptr(#_match), \ |
214 | .regulators_node = of_match_ptr("regulators"), \ | |
87ca186f GX |
215 | .ops = &hi6421_ldo_linear_range_ops, \ |
216 | .type = REGULATOR_VOLTAGE, \ | |
217 | .id = HI6421_##_id, \ | |
218 | .owner = THIS_MODULE, \ | |
219 | .n_voltages = n_volt, \ | |
220 | .linear_ranges = volt_ranges, \ | |
221 | .n_linear_ranges = ARRAY_SIZE(volt_ranges), \ | |
222 | .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \ | |
223 | .vsel_mask = vmask, \ | |
224 | .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \ | |
225 | .enable_mask = emask, \ | |
226 | .enable_time = HI6421_LDO_ENABLE_TIME, \ | |
227 | .off_on_delay = odelay, \ | |
228 | }, \ | |
229 | .mode_mask = ecomask, \ | |
230 | .eco_microamp = ecoamp, \ | |
87ca186f GX |
231 | } |
232 | ||
233 | /* HI6421 BUCK0/1/2 are linear voltage regulators at fixed uV_step | |
234 | * | |
235 | * _id - BUCK0/1/2 id name string | |
29dc269a | 236 | * _match - of match name string |
87ca186f GX |
237 | * vreg - voltage select register |
238 | * vmask - voltage select mask | |
239 | * ereg - enable register | |
240 | * emask - enable mask | |
241 | * sleepmask - mask of sleep mode | |
242 | * etime - enable time | |
243 | * odelay - off/on delay time in uS | |
244 | */ | |
29dc269a | 245 | #define HI6421_BUCK012(_id, _match, vreg, vmask, ereg, emask, sleepmask,\ |
87ca186f GX |
246 | etime, odelay) \ |
247 | [HI6421_##_id] = { \ | |
248 | .desc = { \ | |
249 | .name = #_id, \ | |
29dc269a AL |
250 | .of_match = of_match_ptr(#_match), \ |
251 | .regulators_node = of_match_ptr("regulators"), \ | |
87ca186f GX |
252 | .ops = &hi6421_buck012_ops, \ |
253 | .type = REGULATOR_VOLTAGE, \ | |
254 | .id = HI6421_##_id, \ | |
255 | .owner = THIS_MODULE, \ | |
256 | .min_uV = 700000, \ | |
257 | .n_voltages = 128, \ | |
258 | .uV_step = 7086, \ | |
259 | .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \ | |
260 | .vsel_mask = vmask, \ | |
261 | .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \ | |
262 | .enable_mask = emask, \ | |
263 | .enable_time = etime, \ | |
264 | .off_on_delay = odelay, \ | |
265 | }, \ | |
266 | .mode_mask = sleepmask, \ | |
87ca186f GX |
267 | } |
268 | ||
269 | /* HI6421 BUCK3/4/5 share similar configurations as LDOs, with exception | |
270 | * that it supports SLEEP mode, so has different .ops. | |
271 | * | |
272 | * _id - LDO id name string | |
29dc269a | 273 | * _match - of match name string |
87ca186f GX |
274 | * v_table - voltage table |
275 | * vreg - voltage select register | |
276 | * vmask - voltage select mask | |
277 | * ereg - enable register | |
278 | * emask - enable mask | |
279 | * odelay - off/on delay time in uS | |
280 | * sleepmask - mask of sleep mode | |
281 | */ | |
29dc269a | 282 | #define HI6421_BUCK345(_id, _match, v_table, vreg, vmask, ereg, emask, \ |
87ca186f GX |
283 | odelay, sleepmask) \ |
284 | [HI6421_##_id] = { \ | |
285 | .desc = { \ | |
286 | .name = #_id, \ | |
29dc269a AL |
287 | .of_match = of_match_ptr(#_match), \ |
288 | .regulators_node = of_match_ptr("regulators"), \ | |
87ca186f GX |
289 | .ops = &hi6421_buck345_ops, \ |
290 | .type = REGULATOR_VOLTAGE, \ | |
291 | .id = HI6421_##_id, \ | |
292 | .owner = THIS_MODULE, \ | |
293 | .n_voltages = ARRAY_SIZE(v_table), \ | |
294 | .volt_table = v_table, \ | |
295 | .vsel_reg = HI6421_REG_TO_BUS_ADDR(vreg), \ | |
296 | .vsel_mask = vmask, \ | |
297 | .enable_reg = HI6421_REG_TO_BUS_ADDR(ereg), \ | |
298 | .enable_mask = emask, \ | |
299 | .enable_time = HI6421_LDO_ENABLE_TIME, \ | |
300 | .off_on_delay = odelay, \ | |
301 | }, \ | |
302 | .mode_mask = sleepmask, \ | |
87ca186f GX |
303 | } |
304 | ||
305 | /* HI6421 regulator information */ | |
306 | static struct hi6421_regulator_info | |
307 | hi6421_regulator_info[HI6421_NUM_REGULATORS] = { | |
29dc269a | 308 | HI6421_LDO(LDO0, hi6421_vout0, ldo_0_voltages, 0x20, 0x07, 0x20, 0x10, |
87ca186f | 309 | 10000, 0x20, 8000), |
29dc269a AL |
310 | HI6421_LDO_LINEAR(LDO1, hi6421_vout1, 1700000, 4, 100000, 0x21, 0x03, |
311 | 0x21, 0x10, 10000, 0x20, 5000), | |
312 | HI6421_LDO_LINEAR(LDO2, hi6421_vout2, 1050000, 8, 50000, 0x22, 0x07, | |
313 | 0x22, 0x10, 20000, 0x20, 8000), | |
314 | HI6421_LDO_LINEAR(LDO3, hi6421_vout3, 1050000, 8, 50000, 0x23, 0x07, | |
315 | 0x23, 0x10, 20000, 0x20, 8000), | |
316 | HI6421_LDO(LDO4, hi6421_vout4, ldo_0_voltages, 0x24, 0x07, 0x24, 0x10, | |
87ca186f | 317 | 20000, 0x20, 8000), |
29dc269a | 318 | HI6421_LDO(LDO5, hi6421_vout5, ldo_0_voltages, 0x25, 0x07, 0x25, 0x10, |
87ca186f | 319 | 20000, 0x20, 8000), |
29dc269a | 320 | HI6421_LDO(LDO6, hi6421_vout6, ldo_0_voltages, 0x26, 0x07, 0x26, 0x10, |
87ca186f | 321 | 20000, 0x20, 8000), |
29dc269a | 322 | HI6421_LDO(LDO7, hi6421_vout7, ldo_0_voltages, 0x27, 0x07, 0x27, 0x10, |
87ca186f | 323 | 20000, 0x20, 5000), |
29dc269a | 324 | HI6421_LDO(LDO8, hi6421_vout8, ldo_8_voltages, 0x28, 0x07, 0x28, 0x10, |
87ca186f | 325 | 20000, 0x20, 8000), |
29dc269a | 326 | HI6421_LDO(LDO9, hi6421_vout9, ldo_0_voltages, 0x29, 0x07, 0x29, 0x10, |
87ca186f | 327 | 40000, 0x20, 8000), |
29dc269a | 328 | HI6421_LDO(LDO10, hi6421_vout10, ldo_0_voltages, 0x2a, 0x07, 0x2a, 0x10, |
87ca186f | 329 | 40000, 0x20, 8000), |
29dc269a | 330 | HI6421_LDO(LDO11, hi6421_vout11, ldo_0_voltages, 0x2b, 0x07, 0x2b, 0x10, |
87ca186f | 331 | 40000, 0x20, 8000), |
29dc269a | 332 | HI6421_LDO(LDO12, hi6421_vout12, ldo_0_voltages, 0x2c, 0x07, 0x2c, 0x10, |
87ca186f | 333 | 40000, 0x20, 8000), |
29dc269a | 334 | HI6421_LDO(LDO13, hi6421_vout13, ldo_0_voltages, 0x2d, 0x07, 0x2d, 0x10, |
87ca186f | 335 | 40000, 0x20, 8000), |
29dc269a | 336 | HI6421_LDO(LDO14, hi6421_vout14, ldo_0_voltages, 0x2e, 0x07, 0x2e, 0x10, |
87ca186f | 337 | 40000, 0x20, 8000), |
29dc269a | 338 | HI6421_LDO(LDO15, hi6421_vout15, ldo_8_voltages, 0x2f, 0x07, 0x2f, 0x10, |
87ca186f | 339 | 40000, 0x20, 8000), |
29dc269a | 340 | HI6421_LDO(LDO16, hi6421_vout16, ldo_0_voltages, 0x30, 0x07, 0x30, 0x10, |
87ca186f | 341 | 40000, 0x20, 8000), |
29dc269a | 342 | HI6421_LDO(LDO17, hi6421_vout17, ldo_0_voltages, 0x31, 0x07, 0x31, 0x10, |
87ca186f | 343 | 40000, 0x20, 8000), |
29dc269a | 344 | HI6421_LDO(LDO18, hi6421_vout18, ldo_0_voltages, 0x32, 0x07, 0x32, 0x10, |
87ca186f | 345 | 40000, 0x20, 8000), |
29dc269a | 346 | HI6421_LDO(LDO19, hi6421_vout19, ldo_0_voltages, 0x33, 0x07, 0x33, 0x10, |
87ca186f | 347 | 40000, 0x20, 8000), |
29dc269a | 348 | HI6421_LDO(LDO20, hi6421_vout20, ldo_0_voltages, 0x34, 0x07, 0x34, 0x10, |
87ca186f | 349 | 40000, 0x20, 8000), |
29dc269a AL |
350 | HI6421_LDO_LINEAR_RANGE(LDOAUDIO, hi6421_vout_audio, 8, |
351 | ldo_audio_volt_range, 0x36, 0x70, 0x36, 0x01, | |
352 | 40000, 0x02, 5000), | |
353 | HI6421_BUCK012(BUCK0, hi6421_buck0, 0x0d, 0x7f, 0x0c, 0x01, 0x10, 400, | |
354 | 20000), | |
355 | HI6421_BUCK012(BUCK1, hi6421_buck1, 0x0f, 0x7f, 0x0e, 0x01, 0x10, 400, | |
356 | 20000), | |
357 | HI6421_BUCK012(BUCK2, hi6421_buck2, 0x11, 0x7f, 0x10, 0x01, 0x10, 350, | |
358 | 100), | |
359 | HI6421_BUCK345(BUCK3, hi6421_buck3, buck_3_voltages, 0x13, 0x07, 0x12, | |
360 | 0x01, 20000, 0x10), | |
361 | HI6421_BUCK345(BUCK4, hi6421_buck4, buck_4_voltages, 0x15, 0x07, 0x14, | |
362 | 0x01, 20000, 0x10), | |
363 | HI6421_BUCK345(BUCK5, hi6421_buck5, buck_5_voltages, 0x17, 0x07, 0x16, | |
364 | 0x01, 20000, 0x10), | |
87ca186f GX |
365 | }; |
366 | ||
367 | static int hi6421_regulator_enable(struct regulator_dev *rdev) | |
368 | { | |
369 | struct hi6421_regulator_pdata *pdata; | |
370 | ||
371 | pdata = dev_get_drvdata(rdev->dev.parent); | |
372 | /* hi6421 spec requires regulator enablement must be serialized: | |
373 | * - Because when BUCK, LDO switching from off to on, it will have | |
374 | * a huge instantaneous current; so you can not turn on two or | |
375 | * more LDO or BUCKs simultaneously, or it may burn the chip. | |
376 | */ | |
377 | mutex_lock(&pdata->lock); | |
378 | ||
379 | /* call regulator regmap helper */ | |
380 | regulator_enable_regmap(rdev); | |
381 | ||
382 | mutex_unlock(&pdata->lock); | |
383 | return 0; | |
384 | } | |
385 | ||
386 | static unsigned int hi6421_regulator_ldo_get_mode(struct regulator_dev *rdev) | |
387 | { | |
388 | struct hi6421_regulator_info *info = rdev_get_drvdata(rdev); | |
389 | u32 reg_val; | |
390 | ||
391 | regmap_read(rdev->regmap, rdev->desc->enable_reg, ®_val); | |
392 | if (reg_val & info->mode_mask) | |
393 | return REGULATOR_MODE_IDLE; | |
ea62f4df GX |
394 | |
395 | return REGULATOR_MODE_NORMAL; | |
87ca186f GX |
396 | } |
397 | ||
398 | static unsigned int hi6421_regulator_buck_get_mode(struct regulator_dev *rdev) | |
399 | { | |
400 | struct hi6421_regulator_info *info = rdev_get_drvdata(rdev); | |
401 | u32 reg_val; | |
402 | ||
403 | regmap_read(rdev->regmap, rdev->desc->enable_reg, ®_val); | |
404 | if (reg_val & info->mode_mask) | |
405 | return REGULATOR_MODE_STANDBY; | |
ea62f4df GX |
406 | |
407 | return REGULATOR_MODE_NORMAL; | |
87ca186f GX |
408 | } |
409 | ||
410 | static int hi6421_regulator_ldo_set_mode(struct regulator_dev *rdev, | |
411 | unsigned int mode) | |
412 | { | |
413 | struct hi6421_regulator_info *info = rdev_get_drvdata(rdev); | |
414 | u32 new_mode; | |
415 | ||
416 | switch (mode) { | |
417 | case REGULATOR_MODE_NORMAL: | |
418 | new_mode = 0; | |
419 | break; | |
420 | case REGULATOR_MODE_IDLE: | |
421 | new_mode = info->mode_mask; | |
422 | break; | |
423 | default: | |
424 | return -EINVAL; | |
425 | } | |
426 | ||
427 | /* set mode */ | |
428 | regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, | |
429 | info->mode_mask, new_mode); | |
430 | ||
431 | return 0; | |
432 | } | |
433 | ||
434 | static int hi6421_regulator_buck_set_mode(struct regulator_dev *rdev, | |
435 | unsigned int mode) | |
436 | { | |
437 | struct hi6421_regulator_info *info = rdev_get_drvdata(rdev); | |
438 | u32 new_mode; | |
439 | ||
440 | switch (mode) { | |
441 | case REGULATOR_MODE_NORMAL: | |
442 | new_mode = 0; | |
443 | break; | |
444 | case REGULATOR_MODE_STANDBY: | |
445 | new_mode = info->mode_mask; | |
446 | break; | |
447 | default: | |
448 | return -EINVAL; | |
449 | } | |
450 | ||
451 | /* set mode */ | |
452 | regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, | |
453 | info->mode_mask, new_mode); | |
454 | ||
455 | return 0; | |
456 | } | |
457 | ||
ea2f7321 BX |
458 | static unsigned int |
459 | hi6421_regulator_ldo_get_optimum_mode(struct regulator_dev *rdev, | |
87ca186f GX |
460 | int input_uV, int output_uV, int load_uA) |
461 | { | |
462 | struct hi6421_regulator_info *info = rdev_get_drvdata(rdev); | |
463 | ||
464 | if (load_uA > info->eco_microamp) | |
465 | return REGULATOR_MODE_NORMAL; | |
ea62f4df GX |
466 | |
467 | return REGULATOR_MODE_IDLE; | |
87ca186f GX |
468 | } |
469 | ||
470 | static const struct regulator_ops hi6421_ldo_ops = { | |
471 | .is_enabled = regulator_is_enabled_regmap, | |
472 | .enable = hi6421_regulator_enable, | |
473 | .disable = regulator_disable_regmap, | |
474 | .list_voltage = regulator_list_voltage_table, | |
475 | .map_voltage = regulator_map_voltage_ascend, | |
476 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
477 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
478 | .get_mode = hi6421_regulator_ldo_get_mode, | |
479 | .set_mode = hi6421_regulator_ldo_set_mode, | |
480 | .get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode, | |
481 | }; | |
482 | ||
483 | static const struct regulator_ops hi6421_ldo_linear_ops = { | |
484 | .is_enabled = regulator_is_enabled_regmap, | |
485 | .enable = hi6421_regulator_enable, | |
486 | .disable = regulator_disable_regmap, | |
487 | .list_voltage = regulator_list_voltage_linear, | |
488 | .map_voltage = regulator_map_voltage_linear, | |
489 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
490 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
491 | .get_mode = hi6421_regulator_ldo_get_mode, | |
492 | .set_mode = hi6421_regulator_ldo_set_mode, | |
493 | .get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode, | |
494 | }; | |
495 | ||
496 | static const struct regulator_ops hi6421_ldo_linear_range_ops = { | |
497 | .is_enabled = regulator_is_enabled_regmap, | |
498 | .enable = hi6421_regulator_enable, | |
499 | .disable = regulator_disable_regmap, | |
500 | .list_voltage = regulator_list_voltage_linear_range, | |
501 | .map_voltage = regulator_map_voltage_linear_range, | |
502 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
503 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
504 | .get_mode = hi6421_regulator_ldo_get_mode, | |
505 | .set_mode = hi6421_regulator_ldo_set_mode, | |
506 | .get_optimum_mode = hi6421_regulator_ldo_get_optimum_mode, | |
507 | }; | |
508 | ||
509 | static const struct regulator_ops hi6421_buck012_ops = { | |
510 | .is_enabled = regulator_is_enabled_regmap, | |
511 | .enable = hi6421_regulator_enable, | |
512 | .disable = regulator_disable_regmap, | |
513 | .list_voltage = regulator_list_voltage_linear, | |
514 | .map_voltage = regulator_map_voltage_linear, | |
515 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
516 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
517 | .get_mode = hi6421_regulator_buck_get_mode, | |
518 | .set_mode = hi6421_regulator_buck_set_mode, | |
519 | }; | |
520 | ||
521 | static const struct regulator_ops hi6421_buck345_ops = { | |
522 | .is_enabled = regulator_is_enabled_regmap, | |
523 | .enable = hi6421_regulator_enable, | |
524 | .disable = regulator_disable_regmap, | |
525 | .list_voltage = regulator_list_voltage_table, | |
526 | .map_voltage = regulator_map_voltage_ascend, | |
527 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
528 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
529 | .get_mode = hi6421_regulator_buck_get_mode, | |
530 | .set_mode = hi6421_regulator_buck_set_mode, | |
531 | }; | |
532 | ||
87ca186f GX |
533 | static int hi6421_regulator_probe(struct platform_device *pdev) |
534 | { | |
29dc269a | 535 | struct hi6421_pmic *pmic = dev_get_drvdata(pdev->dev.parent); |
87ca186f | 536 | struct hi6421_regulator_pdata *pdata; |
29dc269a AL |
537 | struct hi6421_regulator_info *info; |
538 | struct regulator_config config = { }; | |
539 | struct regulator_dev *rdev; | |
540 | int i; | |
87ca186f GX |
541 | |
542 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); | |
543 | if (!pdata) | |
544 | return -ENOMEM; | |
545 | mutex_init(&pdata->lock); | |
546 | platform_set_drvdata(pdev, pdata); | |
547 | ||
87ca186f | 548 | for (i = 0; i < ARRAY_SIZE(hi6421_regulator_info); i++) { |
29dc269a AL |
549 | /* assign per-regulator data */ |
550 | info = &hi6421_regulator_info[i]; | |
551 | ||
552 | config.dev = pdev->dev.parent; | |
553 | config.driver_data = info; | |
554 | config.regmap = pmic->regmap; | |
555 | ||
556 | rdev = devm_regulator_register(&pdev->dev, &info->desc, | |
557 | &config); | |
558 | if (IS_ERR(rdev)) { | |
559 | dev_err(&pdev->dev, "failed to register regulator %s\n", | |
560 | info->desc.name); | |
561 | return PTR_ERR(rdev); | |
562 | } | |
87ca186f GX |
563 | } |
564 | ||
565 | return 0; | |
566 | } | |
567 | ||
a8ea49d7 GX |
568 | static const struct platform_device_id hi6421_regulator_table[] = { |
569 | { .name = "hi6421-regulator" }, | |
570 | {}, | |
571 | }; | |
572 | MODULE_DEVICE_TABLE(platform, hi6421_regulator_table); | |
573 | ||
87ca186f | 574 | static struct platform_driver hi6421_regulator_driver = { |
a8ea49d7 | 575 | .id_table = hi6421_regulator_table, |
87ca186f GX |
576 | .driver = { |
577 | .name = "hi6421-regulator", | |
87ca186f GX |
578 | }, |
579 | .probe = hi6421_regulator_probe, | |
580 | }; | |
581 | module_platform_driver(hi6421_regulator_driver); | |
582 | ||
583 | MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>"); | |
584 | MODULE_DESCRIPTION("Hi6421 regulator driver"); | |
585 | MODULE_LICENSE("GPL v2"); |