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(map: rdev->regmap, reg: addr, val: &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, con_id: "enable" , index: 0, |
281 | flags: GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); |
282 | case LM3632_LDO_NEG: |
283 | case LM36274_LDO_NEG: |
284 | return gpiod_get_index_optional(dev, con_id: "enable" , index: 1, |
285 | flags: 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(map: regmap, reg: lm363x_regulator_desc[id].enable_reg, |
309 | mask: ext_en_mask, val: ext_en_mask); |
310 | } |
311 | |
312 | static int lm363x_regulator_probe(struct platform_device *pdev) |
313 | { |
314 | struct ti_lmu *lmu = dev_get_drvdata(dev: 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(ptr: gpiod)) |
332 | return PTR_ERR(ptr: 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(desc: gpiod); |
339 | dev_err(dev, "External pin err: %d\n" , ret); |
340 | return ret; |
341 | } |
342 | } |
343 | |
344 | rdev = devm_regulator_register(dev, regulator_desc: &lm363x_regulator_desc[id], config: &cfg); |
345 | if (IS_ERR(ptr: rdev)) { |
346 | ret = PTR_ERR(ptr: 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 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
359 | }, |
360 | }; |
361 | |
362 | module_platform_driver(lm363x_regulator_driver); |
363 | |
364 | MODULE_DESCRIPTION("TI LM363X Regulator Driver" ); |
365 | MODULE_AUTHOR("Milo Kim" ); |
366 | MODULE_LICENSE("GPL v2" ); |
367 | MODULE_ALIAS("platform:lm363x-regulator" ); |
368 | |