]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0-only | |
2 | /* | |
3 | * TI LM363X Regulator Driver | |
4 | * | |
5 | * Copyright 2015 Texas Instruments | |
6 | * | |
7 | * Author: Milo Kim <milo.kim@ti.com> | |
8 | */ | |
9 | ||
10 | #include <linux/err.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/mfd/ti-lmu.h> | |
13 | #include <linux/mfd/ti-lmu-register.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/of.h> | |
16 | #include <linux/gpio/consumer.h> | |
17 | #include <linux/platform_device.h> | |
18 | #include <linux/regulator/driver.h> | |
19 | #include <linux/regulator/of_regulator.h> | |
20 | #include <linux/slab.h> | |
21 | ||
22 | /* LM3631 */ | |
23 | #define LM3631_BOOST_VSEL_MAX 0x25 | |
24 | #define LM3631_LDO_VSEL_MAX 0x28 | |
25 | #define LM3631_CONT_VSEL_MAX 0x03 | |
26 | #define LM3631_VBOOST_MIN 4500000 | |
27 | #define LM3631_VCONT_MIN 1800000 | |
28 | #define LM3631_VLDO_MIN 4000000 | |
29 | #define ENABLE_TIME_USEC 1000 | |
30 | ||
31 | /* LM3632 */ | |
32 | #define LM3632_BOOST_VSEL_MAX 0x26 | |
33 | #define LM3632_LDO_VSEL_MAX 0x28 | |
34 | #define LM3632_VBOOST_MIN 4500000 | |
35 | #define LM3632_VLDO_MIN 4000000 | |
36 | ||
37 | /* LM36274 */ | |
38 | #define LM36274_BOOST_VSEL_MAX 0x3f | |
39 | #define LM36274_LDO_VSEL_MAX 0x32 | |
40 | #define LM36274_VOLTAGE_MIN 4000000 | |
41 | ||
42 | /* Common */ | |
43 | #define LM363X_STEP_50mV 50000 | |
44 | #define LM363X_STEP_500mV 500000 | |
45 | ||
46 | static const int ldo_cont_enable_time[] = { | |
47 | 0, 2000, 5000, 10000, 20000, 50000, 100000, 200000, | |
48 | }; | |
49 | ||
50 | static int lm363x_regulator_enable_time(struct regulator_dev *rdev) | |
51 | { | |
52 | enum lm363x_regulator_id id = rdev_get_id(rdev); | |
53 | unsigned int val, addr, mask; | |
54 | ||
55 | switch (id) { | |
56 | case LM3631_LDO_CONT: | |
57 | addr = LM3631_REG_ENTIME_VCONT; | |
58 | mask = LM3631_ENTIME_CONT_MASK; | |
59 | break; | |
60 | case LM3631_LDO_OREF: | |
61 | addr = LM3631_REG_ENTIME_VOREF; | |
62 | mask = LM3631_ENTIME_MASK; | |
63 | break; | |
64 | case LM3631_LDO_POS: | |
65 | addr = LM3631_REG_ENTIME_VPOS; | |
66 | mask = LM3631_ENTIME_MASK; | |
67 | break; | |
68 | case LM3631_LDO_NEG: | |
69 | addr = LM3631_REG_ENTIME_VNEG; | |
70 | mask = LM3631_ENTIME_MASK; | |
71 | break; | |
72 | default: | |
73 | return 0; | |
74 | } | |
75 | ||
76 | if (regmap_read(rdev->regmap, addr, &val)) | |
77 | return -EINVAL; | |
78 | ||
79 | val = (val & mask) >> LM3631_ENTIME_SHIFT; | |
80 | ||
81 | if (id == LM3631_LDO_CONT) | |
82 | return ldo_cont_enable_time[val]; | |
83 | else | |
84 | return ENABLE_TIME_USEC * val; | |
85 | } | |
86 | ||
87 | static const struct regulator_ops lm363x_boost_voltage_table_ops = { | |
88 | .list_voltage = regulator_list_voltage_linear, | |
89 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
90 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
91 | }; | |
92 | ||
93 | static const struct regulator_ops lm363x_regulator_voltage_table_ops = { | |
94 | .list_voltage = regulator_list_voltage_linear, | |
95 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
96 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
97 | .enable = regulator_enable_regmap, | |
98 | .disable = regulator_disable_regmap, | |
99 | .is_enabled = regulator_is_enabled_regmap, | |
100 | .enable_time = lm363x_regulator_enable_time, | |
101 | }; | |
102 | ||
103 | static const struct regulator_desc lm363x_regulator_desc[] = { | |
104 | /* LM3631 */ | |
105 | { | |
106 | .name = "vboost", | |
107 | .of_match = "vboost", | |
108 | .id = LM3631_BOOST, | |
109 | .ops = &lm363x_boost_voltage_table_ops, | |
110 | .n_voltages = LM3631_BOOST_VSEL_MAX + 1, | |
111 | .min_uV = LM3631_VBOOST_MIN, | |
112 | .uV_step = LM363X_STEP_50mV, | |
113 | .type = REGULATOR_VOLTAGE, | |
114 | .owner = THIS_MODULE, | |
115 | .vsel_reg = LM3631_REG_VOUT_BOOST, | |
116 | .vsel_mask = LM3631_VOUT_MASK, | |
117 | }, | |
118 | { | |
119 | .name = "ldo_cont", | |
120 | .of_match = "vcont", | |
121 | .id = LM3631_LDO_CONT, | |
122 | .ops = &lm363x_regulator_voltage_table_ops, | |
123 | .n_voltages = LM3631_CONT_VSEL_MAX + 1, | |
124 | .min_uV = LM3631_VCONT_MIN, | |
125 | .uV_step = LM363X_STEP_500mV, | |
126 | .type = REGULATOR_VOLTAGE, | |
127 | .owner = THIS_MODULE, | |
128 | .vsel_reg = LM3631_REG_VOUT_CONT, | |
129 | .vsel_mask = LM3631_VOUT_CONT_MASK, | |
130 | .enable_reg = LM3631_REG_LDO_CTRL2, | |
131 | .enable_mask = LM3631_EN_CONT_MASK, | |
132 | }, | |
133 | { | |
134 | .name = "ldo_oref", | |
135 | .of_match = "voref", | |
136 | .id = LM3631_LDO_OREF, | |
137 | .ops = &lm363x_regulator_voltage_table_ops, | |
138 | .n_voltages = LM3631_LDO_VSEL_MAX + 1, | |
139 | .min_uV = LM3631_VLDO_MIN, | |
140 | .uV_step = LM363X_STEP_50mV, | |
141 | .type = REGULATOR_VOLTAGE, | |
142 | .owner = THIS_MODULE, | |
143 | .vsel_reg = LM3631_REG_VOUT_OREF, | |
144 | .vsel_mask = LM3631_VOUT_MASK, | |
145 | .enable_reg = LM3631_REG_LDO_CTRL1, | |
146 | .enable_mask = LM3631_EN_OREF_MASK, | |
147 | }, | |
148 | { | |
149 | .name = "ldo_vpos", | |
150 | .of_match = "vpos", | |
151 | .id = LM3631_LDO_POS, | |
152 | .ops = &lm363x_regulator_voltage_table_ops, | |
153 | .n_voltages = LM3631_LDO_VSEL_MAX + 1, | |
154 | .min_uV = LM3631_VLDO_MIN, | |
155 | .uV_step = LM363X_STEP_50mV, | |
156 | .type = REGULATOR_VOLTAGE, | |
157 | .owner = THIS_MODULE, | |
158 | .vsel_reg = LM3631_REG_VOUT_POS, | |
159 | .vsel_mask = LM3631_VOUT_MASK, | |
160 | .enable_reg = LM3631_REG_LDO_CTRL1, | |
161 | .enable_mask = LM3631_EN_VPOS_MASK, | |
162 | }, | |
163 | { | |
164 | .name = "ldo_vneg", | |
165 | .of_match = "vneg", | |
166 | .id = LM3631_LDO_NEG, | |
167 | .ops = &lm363x_regulator_voltage_table_ops, | |
168 | .n_voltages = LM3631_LDO_VSEL_MAX + 1, | |
169 | .min_uV = LM3631_VLDO_MIN, | |
170 | .uV_step = LM363X_STEP_50mV, | |
171 | .type = REGULATOR_VOLTAGE, | |
172 | .owner = THIS_MODULE, | |
173 | .vsel_reg = LM3631_REG_VOUT_NEG, | |
174 | .vsel_mask = LM3631_VOUT_MASK, | |
175 | .enable_reg = LM3631_REG_LDO_CTRL1, | |
176 | .enable_mask = LM3631_EN_VNEG_MASK, | |
177 | }, | |
178 | /* LM3632 */ | |
179 | { | |
180 | .name = "vboost", | |
181 | .of_match = "vboost", | |
182 | .id = LM3632_BOOST, | |
183 | .ops = &lm363x_boost_voltage_table_ops, | |
184 | .n_voltages = LM3632_BOOST_VSEL_MAX + 1, | |
185 | .min_uV = LM3632_VBOOST_MIN, | |
186 | .uV_step = LM363X_STEP_50mV, | |
187 | .type = REGULATOR_VOLTAGE, | |
188 | .owner = THIS_MODULE, | |
189 | .vsel_reg = LM3632_REG_VOUT_BOOST, | |
190 | .vsel_mask = LM3632_VOUT_MASK, | |
191 | }, | |
192 | { | |
193 | .name = "ldo_vpos", | |
194 | .of_match = "vpos", | |
195 | .id = LM3632_LDO_POS, | |
196 | .ops = &lm363x_regulator_voltage_table_ops, | |
197 | .n_voltages = LM3632_LDO_VSEL_MAX + 1, | |
198 | .min_uV = LM3632_VLDO_MIN, | |
199 | .uV_step = LM363X_STEP_50mV, | |
200 | .type = REGULATOR_VOLTAGE, | |
201 | .owner = THIS_MODULE, | |
202 | .vsel_reg = LM3632_REG_VOUT_POS, | |
203 | .vsel_mask = LM3632_VOUT_MASK, | |
204 | .enable_reg = LM3632_REG_BIAS_CONFIG, | |
205 | .enable_mask = LM3632_EN_VPOS_MASK, | |
206 | }, | |
207 | { | |
208 | .name = "ldo_vneg", | |
209 | .of_match = "vneg", | |
210 | .id = LM3632_LDO_NEG, | |
211 | .ops = &lm363x_regulator_voltage_table_ops, | |
212 | .n_voltages = LM3632_LDO_VSEL_MAX + 1, | |
213 | .min_uV = LM3632_VLDO_MIN, | |
214 | .uV_step = LM363X_STEP_50mV, | |
215 | .type = REGULATOR_VOLTAGE, | |
216 | .owner = THIS_MODULE, | |
217 | .vsel_reg = LM3632_REG_VOUT_NEG, | |
218 | .vsel_mask = LM3632_VOUT_MASK, | |
219 | .enable_reg = LM3632_REG_BIAS_CONFIG, | |
220 | .enable_mask = LM3632_EN_VNEG_MASK, | |
221 | }, | |
222 | ||
223 | /* LM36274 */ | |
224 | { | |
225 | .name = "vboost", | |
226 | .of_match = "vboost", | |
227 | .id = LM36274_BOOST, | |
228 | .ops = &lm363x_boost_voltage_table_ops, | |
229 | .n_voltages = LM36274_BOOST_VSEL_MAX + 1, | |
230 | .min_uV = LM36274_VOLTAGE_MIN, | |
231 | .uV_step = LM363X_STEP_50mV, | |
232 | .type = REGULATOR_VOLTAGE, | |
233 | .owner = THIS_MODULE, | |
234 | .vsel_reg = LM36274_REG_VOUT_BOOST, | |
235 | .vsel_mask = LM36274_VOUT_MASK, | |
236 | }, | |
237 | { | |
238 | .name = "ldo_vpos", | |
239 | .of_match = "vpos", | |
240 | .id = LM36274_LDO_POS, | |
241 | .ops = &lm363x_regulator_voltage_table_ops, | |
242 | .n_voltages = LM36274_LDO_VSEL_MAX + 1, | |
243 | .min_uV = LM36274_VOLTAGE_MIN, | |
244 | .uV_step = LM363X_STEP_50mV, | |
245 | .type = REGULATOR_VOLTAGE, | |
246 | .owner = THIS_MODULE, | |
247 | .vsel_reg = LM36274_REG_VOUT_POS, | |
248 | .vsel_mask = LM36274_VOUT_MASK, | |
249 | .enable_reg = LM36274_REG_BIAS_CONFIG_1, | |
250 | .enable_mask = LM36274_EN_VPOS_MASK, | |
251 | }, | |
252 | { | |
253 | .name = "ldo_vneg", | |
254 | .of_match = "vneg", | |
255 | .id = LM36274_LDO_NEG, | |
256 | .ops = &lm363x_regulator_voltage_table_ops, | |
257 | .n_voltages = LM36274_LDO_VSEL_MAX + 1, | |
258 | .min_uV = LM36274_VOLTAGE_MIN, | |
259 | .uV_step = LM363X_STEP_50mV, | |
260 | .type = REGULATOR_VOLTAGE, | |
261 | .owner = THIS_MODULE, | |
262 | .vsel_reg = LM36274_REG_VOUT_NEG, | |
263 | .vsel_mask = LM36274_VOUT_MASK, | |
264 | .enable_reg = LM36274_REG_BIAS_CONFIG_1, | |
265 | .enable_mask = LM36274_EN_VNEG_MASK, | |
266 | }, | |
267 | }; | |
268 | ||
269 | static struct gpio_desc *lm363x_regulator_of_get_enable_gpio(struct device *dev, int id) | |
270 | { | |
271 | /* | |
272 | * Check LCM_EN1/2_GPIO is configured. | |
273 | * Those pins are used for enabling VPOS/VNEG LDOs. | |
274 | * Do not use devm* here: the regulator core takes over the | |
275 | * lifecycle management of the GPIO descriptor. | |
276 | */ | |
277 | switch (id) { | |
278 | case LM3632_LDO_POS: | |
279 | case LM36274_LDO_POS: | |
280 | return gpiod_get_index_optional(dev, "enable", 0, | |
281 | GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); | |
282 | case LM3632_LDO_NEG: | |
283 | case LM36274_LDO_NEG: | |
284 | return gpiod_get_index_optional(dev, "enable", 1, | |
285 | GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); | |
286 | default: | |
287 | return NULL; | |
288 | } | |
289 | } | |
290 | ||
291 | static int lm363x_regulator_set_ext_en(struct regmap *regmap, int id) | |
292 | { | |
293 | int ext_en_mask = 0; | |
294 | ||
295 | switch (id) { | |
296 | case LM3632_LDO_POS: | |
297 | case LM3632_LDO_NEG: | |
298 | ext_en_mask = LM3632_EXT_EN_MASK; | |
299 | break; | |
300 | case LM36274_LDO_POS: | |
301 | case LM36274_LDO_NEG: | |
302 | ext_en_mask = LM36274_EXT_EN_MASK; | |
303 | break; | |
304 | default: | |
305 | return -ENODEV; | |
306 | } | |
307 | ||
308 | return regmap_update_bits(regmap, lm363x_regulator_desc[id].enable_reg, | |
309 | ext_en_mask, ext_en_mask); | |
310 | } | |
311 | ||
312 | static int lm363x_regulator_probe(struct platform_device *pdev) | |
313 | { | |
314 | struct ti_lmu *lmu = dev_get_drvdata(pdev->dev.parent); | |
315 | struct regmap *regmap = lmu->regmap; | |
316 | struct regulator_config cfg = { }; | |
317 | struct regulator_dev *rdev; | |
318 | struct device *dev = &pdev->dev; | |
319 | int id = pdev->id; | |
320 | struct gpio_desc *gpiod; | |
321 | int ret; | |
322 | ||
323 | cfg.dev = dev; | |
324 | cfg.regmap = regmap; | |
325 | ||
326 | /* | |
327 | * LM3632 LDOs can be controlled by external pin. | |
328 | * Register update is required if the pin is used. | |
329 | */ | |
330 | gpiod = lm363x_regulator_of_get_enable_gpio(dev, id); | |
331 | if (IS_ERR(gpiod)) | |
332 | return PTR_ERR(gpiod); | |
333 | ||
334 | if (gpiod) { | |
335 | cfg.ena_gpiod = gpiod; | |
336 | ret = lm363x_regulator_set_ext_en(regmap, id); | |
337 | if (ret) { | |
338 | gpiod_put(gpiod); | |
339 | dev_err(dev, "External pin err: %d\n", ret); | |
340 | return ret; | |
341 | } | |
342 | } | |
343 | ||
344 | rdev = devm_regulator_register(dev, &lm363x_regulator_desc[id], &cfg); | |
345 | if (IS_ERR(rdev)) { | |
346 | ret = PTR_ERR(rdev); | |
347 | dev_err(dev, "[%d] regulator register err: %d\n", id, ret); | |
348 | return ret; | |
349 | } | |
350 | ||
351 | return 0; | |
352 | } | |
353 | ||
354 | static struct platform_driver lm363x_regulator_driver = { | |
355 | .probe = lm363x_regulator_probe, | |
356 | .driver = { | |
357 | .name = "lm363x-regulator", | |
358 | }, | |
359 | }; | |
360 | ||
361 | module_platform_driver(lm363x_regulator_driver); | |
362 | ||
363 | MODULE_DESCRIPTION("TI LM363X Regulator Driver"); | |
364 | MODULE_AUTHOR("Milo Kim"); | |
365 | MODULE_LICENSE("GPL v2"); | |
366 | MODULE_ALIAS("platform:lm363x-regulator"); |