Commit | Line | Data |
---|---|---|
e6999e7c CH |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Copyright (C) 2022 Richtek Technology Corp. | |
4 | * | |
5 | * Author: ChiYuan Huang <cy_huang@richtek.com> | |
6 | * | |
7 | */ | |
8 | ||
9 | #include <linux/bits.h> | |
10 | #include <linux/delay.h> | |
11 | #include <linux/gpio/consumer.h> | |
12 | #include <linux/i2c.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/mod_devicetable.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/pm_runtime.h> | |
18 | #include <linux/regmap.h> | |
19 | #include <linux/regulator/consumer.h> | |
20 | #include <linux/regulator/driver.h> | |
21 | #include <linux/regulator/of_regulator.h> | |
22 | ||
23 | #define RT6190_REG_VID 0x00 | |
24 | #define RT6190_REG_OUTV 0x01 | |
25 | #define RT6190_REG_OUTC 0x03 | |
26 | #define RT6190_REG_SET1 0x0D | |
27 | #define RT6190_REG_SET2 0x0E | |
28 | #define RT6190_REG_SET4 0x10 | |
29 | #define RT6190_REG_RATIO 0x11 | |
30 | #define RT6190_REG_OUT_VOLT_L 0x12 | |
31 | #define RT6190_REG_TEMP_H 0x1B | |
32 | #define RT6190_REG_STAT1 0x1C | |
33 | #define RT6190_REG_ALERT1 0x1E | |
34 | #define RT6190_REG_ALERT2 0x1F | |
35 | #define RT6190_REG_MASK2 0x21 | |
36 | #define RT6190_REG_OCPEN 0x28 | |
37 | #define RT6190_REG_SET5 0x29 | |
38 | #define RT6190_REG_VBUSC_ADC 0x32 | |
39 | #define RT6190_REG_BUSC_VOLT_L 0x33 | |
40 | #define RT6190_REG_BUSC_VOLT_H 0x34 | |
41 | #define RT6190_REG_STAT3 0x37 | |
42 | #define RT6190_REG_ALERT3 0x38 | |
43 | #define RT6190_REG_MASK3 0x39 | |
44 | ||
45 | #define RT6190_ENPWM_MASK BIT(7) | |
46 | #define RT6190_ENDCHG_MASK BIT(4) | |
47 | #define RT6190_ALERT_OTPEVT BIT(6) | |
48 | #define RT6190_ALERT_UVPEVT BIT(5) | |
49 | #define RT6190_ALERT_OVPEVT BIT(4) | |
50 | #define RT6190_ENGCP_MASK BIT(1) | |
51 | #define RT6190_FCCM_MASK BIT(7) | |
52 | ||
53 | #define RICHTEK_VID 0x82 | |
54 | #define RT6190_OUT_MIN_UV 3000000 | |
55 | #define RT6190_OUT_MAX_UV 32000000 | |
56 | #define RT6190_OUT_STEP_UV 20000 | |
57 | #define RT6190_OUT_N_VOLT (RT6190_OUT_MAX_UV / RT6190_OUT_STEP_UV + 1) | |
58 | #define RT6190_OUTV_MINSEL 150 | |
59 | #define RT6190_OUT_MIN_UA 306000 | |
60 | #define RT6190_OUT_MAX_UA 12114000 | |
61 | #define RT6190_OUT_STEP_UA 24000 | |
62 | #define RT6190_OUTC_MINSEL 19 | |
63 | #define RT6190_EN_TIME_US 500 | |
64 | ||
65 | #define RT6190_PSM_MODE 0 | |
66 | #define RT6190_FCCM_MODE 1 | |
67 | ||
68 | struct rt6190_data { | |
69 | struct device *dev; | |
70 | struct regmap *regmap; | |
71 | struct gpio_desc *enable_gpio; | |
72 | unsigned int cached_alert_evt; | |
73 | }; | |
74 | ||
75 | static int rt6190_out_set_voltage_sel(struct regulator_dev *rdev, | |
76 | unsigned int selector) | |
77 | { | |
78 | struct regmap *regmap = rdev_get_regmap(rdev); | |
79 | __le16 le_sel = cpu_to_le16(selector); | |
80 | ||
81 | return regmap_raw_write(regmap, RT6190_REG_OUTV, &le_sel, | |
82 | sizeof(le_sel)); | |
83 | } | |
84 | ||
85 | static int rt6190_out_get_voltage_sel(struct regulator_dev *rdev) | |
86 | { | |
87 | struct regmap *regmap = rdev_get_regmap(rdev); | |
88 | __le16 le_sel; | |
89 | int ret; | |
90 | ||
91 | ret = regmap_raw_read(regmap, RT6190_REG_OUTV, &le_sel, sizeof(le_sel)); | |
92 | ||
93 | return ret ?: le16_to_cpu(le_sel); | |
94 | } | |
95 | ||
96 | static int rt6190_out_enable(struct regulator_dev *rdev) | |
97 | { | |
98 | struct rt6190_data *data = rdev_get_drvdata(rdev); | |
99 | struct regmap *regmap = rdev_get_regmap(rdev); | |
100 | u8 out_cfg[4]; | |
101 | int ret; | |
102 | ||
103 | pm_runtime_get_sync(data->dev); | |
104 | ||
105 | /* | |
106 | * From off to on, vout config will restore to IC default. | |
107 | * Read vout configs before enable, and restore them after enable | |
108 | */ | |
109 | ret = regmap_raw_read(regmap, RT6190_REG_OUTV, out_cfg, | |
110 | sizeof(out_cfg)); | |
111 | if (ret) | |
112 | return ret; | |
113 | ||
114 | ret = regulator_enable_regmap(rdev); | |
115 | if (ret) | |
116 | return ret; | |
117 | ||
118 | ret = regmap_raw_write(regmap, RT6190_REG_OUTV, out_cfg, | |
119 | sizeof(out_cfg)); | |
120 | if (ret) | |
121 | return ret; | |
122 | ||
123 | return regmap_update_bits(regmap, RT6190_REG_SET5, RT6190_ENGCP_MASK, | |
124 | RT6190_ENGCP_MASK); | |
125 | } | |
126 | ||
127 | static int rt6190_out_disable(struct regulator_dev *rdev) | |
128 | { | |
129 | struct rt6190_data *data = rdev_get_drvdata(rdev); | |
130 | struct regmap *regmap = rdev_get_regmap(rdev); | |
131 | int ret; | |
132 | ||
133 | ret = regmap_update_bits(regmap, RT6190_REG_SET5, RT6190_ENGCP_MASK, 0); | |
134 | if (ret) | |
135 | return ret; | |
136 | ||
137 | ret = regulator_disable_regmap(rdev); | |
138 | if (ret) | |
139 | return ret; | |
140 | ||
141 | /* cleared cached alert event */ | |
142 | data->cached_alert_evt = 0; | |
143 | ||
144 | pm_runtime_put(data->dev); | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | static int rt6190_out_set_current_limit(struct regulator_dev *rdev, int min_uA, | |
150 | int max_uA) | |
151 | { | |
152 | struct regmap *regmap = rdev_get_regmap(rdev); | |
153 | int csel, clim; | |
154 | __le16 le_csel; | |
155 | ||
156 | if (min_uA < RT6190_OUT_MIN_UA || max_uA > RT6190_OUT_MAX_UA) | |
157 | return -EINVAL; | |
158 | ||
159 | csel = DIV_ROUND_UP(min_uA - RT6190_OUT_MIN_UA, RT6190_OUT_STEP_UA); | |
160 | ||
161 | clim = RT6190_OUT_MIN_UA + RT6190_OUT_STEP_UA * csel; | |
162 | if (clim > max_uA) | |
163 | return -EINVAL; | |
164 | ||
165 | csel += RT6190_OUTC_MINSEL; | |
166 | le_csel = cpu_to_le16(csel); | |
167 | ||
168 | return regmap_raw_write(regmap, RT6190_REG_OUTC, &le_csel, | |
169 | sizeof(le_csel)); | |
170 | } | |
171 | ||
172 | static int rt6190_out_get_current_limit(struct regulator_dev *rdev) | |
173 | { | |
174 | struct regmap *regmap = rdev_get_regmap(rdev); | |
175 | __le16 le_csel; | |
176 | int csel, ret; | |
177 | ||
178 | ret = regmap_raw_read(regmap, RT6190_REG_OUTC, &le_csel, | |
179 | sizeof(le_csel)); | |
180 | if (ret) | |
181 | return ret; | |
182 | ||
183 | csel = le16_to_cpu(le_csel); | |
184 | csel -= RT6190_OUTC_MINSEL; | |
185 | ||
186 | return RT6190_OUT_MIN_UA + RT6190_OUT_STEP_UA * csel; | |
187 | } | |
188 | ||
189 | static int rt6190_out_set_mode(struct regulator_dev *rdev, unsigned int mode) | |
190 | { | |
191 | struct regmap *regmap = rdev_get_regmap(rdev); | |
192 | unsigned int val; | |
193 | ||
194 | switch (mode) { | |
195 | case REGULATOR_MODE_FAST: | |
196 | val = RT6190_FCCM_MASK; | |
197 | break; | |
198 | case REGULATOR_MODE_NORMAL: | |
199 | val = 0; | |
200 | break; | |
201 | default: | |
202 | return -EINVAL; | |
203 | } | |
204 | ||
205 | return regmap_update_bits(regmap, RT6190_REG_SET1, RT6190_FCCM_MASK, | |
206 | val); | |
207 | } | |
208 | ||
209 | static unsigned int rt6190_out_get_mode(struct regulator_dev *rdev) | |
210 | { | |
211 | struct regmap *regmap = rdev_get_regmap(rdev); | |
212 | unsigned int config; | |
213 | int ret; | |
214 | ||
215 | ret = regmap_read(regmap, RT6190_REG_SET1, &config); | |
216 | if (ret) | |
217 | return REGULATOR_MODE_INVALID; | |
218 | ||
219 | if (config & RT6190_FCCM_MASK) | |
220 | return REGULATOR_MODE_FAST; | |
221 | ||
222 | return REGULATOR_MODE_NORMAL; | |
223 | } | |
224 | ||
225 | static int rt6190_out_get_error_flags(struct regulator_dev *rdev, | |
226 | unsigned int *flags) | |
227 | { | |
228 | struct rt6190_data *data = rdev_get_drvdata(rdev); | |
229 | unsigned int state, rpt_flags = 0; | |
230 | int ret; | |
231 | ||
232 | ret = regmap_read(data->regmap, RT6190_REG_STAT1, &state); | |
233 | if (ret) | |
234 | return ret; | |
235 | ||
236 | state |= data->cached_alert_evt; | |
237 | ||
238 | if (state & RT6190_ALERT_OTPEVT) | |
239 | rpt_flags |= REGULATOR_ERROR_OVER_TEMP; | |
240 | ||
241 | if (state & RT6190_ALERT_UVPEVT) | |
242 | rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE; | |
243 | ||
244 | if (state & RT6190_ALERT_OVPEVT) | |
245 | rpt_flags |= REGULATOR_ERROR_REGULATION_OUT; | |
246 | ||
247 | *flags = rpt_flags; | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
252 | static unsigned int rt6190_out_of_map_mode(unsigned int mode) | |
253 | { | |
254 | switch (mode) { | |
255 | case RT6190_PSM_MODE: | |
256 | return REGULATOR_MODE_NORMAL; | |
257 | case RT6190_FCCM_MODE: | |
258 | return REGULATOR_MODE_FAST; | |
259 | default: | |
260 | return REGULATOR_MODE_INVALID; | |
261 | } | |
262 | } | |
263 | ||
264 | static const struct regulator_ops rt6190_regulator_ops = { | |
265 | .list_voltage = regulator_list_voltage_linear, | |
266 | .set_voltage_sel = rt6190_out_set_voltage_sel, | |
267 | .get_voltage_sel = rt6190_out_get_voltage_sel, | |
268 | .enable = rt6190_out_enable, | |
269 | .disable = rt6190_out_disable, | |
270 | .is_enabled = regulator_is_enabled_regmap, | |
271 | .set_current_limit = rt6190_out_set_current_limit, | |
272 | .get_current_limit = rt6190_out_get_current_limit, | |
273 | .set_active_discharge = regulator_set_active_discharge_regmap, | |
274 | .set_mode = rt6190_out_set_mode, | |
275 | .get_mode = rt6190_out_get_mode, | |
276 | .get_error_flags = rt6190_out_get_error_flags, | |
277 | }; | |
278 | ||
279 | static const struct regulator_desc rt6190_regulator_desc = { | |
280 | .name = "rt6190-regulator", | |
281 | .type = REGULATOR_VOLTAGE, | |
282 | .owner = THIS_MODULE, | |
283 | .ops = &rt6190_regulator_ops, | |
284 | .min_uV = RT6190_OUT_MIN_UV, | |
285 | .uV_step = RT6190_OUT_STEP_UV, | |
286 | .n_voltages = RT6190_OUT_N_VOLT, | |
287 | .linear_min_sel = RT6190_OUTV_MINSEL, | |
288 | .enable_reg = RT6190_REG_SET2, | |
289 | .enable_mask = RT6190_ENPWM_MASK, | |
290 | .active_discharge_reg = RT6190_REG_SET2, | |
291 | .active_discharge_mask = RT6190_ENDCHG_MASK, | |
292 | .active_discharge_on = RT6190_ENDCHG_MASK, | |
293 | .of_map_mode = rt6190_out_of_map_mode, | |
294 | }; | |
295 | ||
296 | static bool rt6190_is_volatile_reg(struct device *dev, unsigned int reg) | |
297 | { | |
298 | switch (reg) { | |
299 | case RT6190_REG_OUT_VOLT_L ... RT6190_REG_ALERT2: | |
300 | case RT6190_REG_BUSC_VOLT_L ... RT6190_REG_BUSC_VOLT_H: | |
301 | case RT6190_REG_STAT3 ... RT6190_REG_ALERT3: | |
302 | return true; | |
303 | default: | |
304 | return false; | |
305 | } | |
306 | } | |
307 | ||
308 | static const struct regmap_config rt6190_regmap_config = { | |
309 | .name = "rt6190", | |
310 | .cache_type = REGCACHE_FLAT, | |
311 | .reg_bits = 8, | |
312 | .val_bits = 8, | |
313 | .max_register = RT6190_REG_MASK3, | |
314 | .num_reg_defaults_raw = RT6190_REG_MASK3 + 1, | |
315 | .volatile_reg = rt6190_is_volatile_reg, | |
316 | }; | |
317 | ||
318 | static irqreturn_t rt6190_irq_handler(int irq, void *devid) | |
319 | { | |
320 | struct regulator_dev *rdev = devid; | |
321 | struct rt6190_data *data = rdev_get_drvdata(rdev); | |
322 | unsigned int alert; | |
323 | int ret; | |
324 | ||
325 | ret = regmap_read(data->regmap, RT6190_REG_ALERT1, &alert); | |
326 | if (ret) | |
327 | return IRQ_NONE; | |
328 | ||
329 | /* Write clear alert events */ | |
330 | ret = regmap_write(data->regmap, RT6190_REG_ALERT1, alert); | |
331 | if (ret) | |
332 | return IRQ_NONE; | |
333 | ||
334 | data->cached_alert_evt |= alert; | |
335 | ||
336 | if (alert & RT6190_ALERT_OTPEVT) | |
337 | regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_TEMP, NULL); | |
338 | ||
339 | if (alert & RT6190_ALERT_UVPEVT) | |
340 | regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, NULL); | |
341 | ||
342 | if (alert & RT6190_ALERT_OVPEVT) | |
343 | regulator_notifier_call_chain(rdev, REGULATOR_EVENT_REGULATION_OUT, NULL); | |
344 | ||
345 | return IRQ_HANDLED; | |
346 | } | |
347 | ||
348 | static int rt6190_init_registers(struct regmap *regmap) | |
349 | { | |
350 | int ret; | |
351 | ||
352 | /* Enable_ADC = 1 */ | |
353 | ret = regmap_write(regmap, RT6190_REG_SET4, 0x82); | |
354 | if (ret) | |
355 | return ret; | |
356 | ||
357 | /* Config default VOUT ratio to be higher */ | |
358 | ret = regmap_write(regmap, RT6190_REG_RATIO, 0x20); | |
359 | ||
360 | /* Mask unused alert */ | |
361 | ret = regmap_write(regmap, RT6190_REG_MASK2, 0); | |
362 | if (ret) | |
363 | return ret; | |
364 | ||
365 | /* OCP config */ | |
366 | ret = regmap_write(regmap, RT6190_REG_OCPEN, 0); | |
367 | if (ret) | |
368 | return ret; | |
369 | ||
370 | /* Enable VBUSC ADC */ | |
371 | return regmap_write(regmap, RT6190_REG_VBUSC_ADC, 0x02); | |
372 | } | |
373 | ||
374 | static int rt6190_probe(struct i2c_client *i2c) | |
375 | { | |
376 | struct device *dev = &i2c->dev; | |
377 | struct rt6190_data *data; | |
378 | struct gpio_desc *enable_gpio; | |
379 | struct regmap *regmap; | |
380 | struct regulator_dev *rdev; | |
381 | struct regulator_config cfg = {}; | |
382 | unsigned int vid; | |
383 | int ret; | |
384 | ||
385 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | |
386 | if (!data) | |
387 | return -ENOMEM; | |
388 | ||
389 | enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); | |
390 | if (IS_ERR(enable_gpio)) | |
391 | return dev_err_probe(dev, PTR_ERR(enable_gpio), "Failed to get 'enable' gpio\n"); | |
392 | else if (enable_gpio) | |
393 | usleep_range(RT6190_EN_TIME_US, RT6190_EN_TIME_US * 2); | |
394 | ||
395 | regmap = devm_regmap_init_i2c(i2c, &rt6190_regmap_config); | |
396 | if (IS_ERR(regmap)) | |
397 | return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n"); | |
398 | ||
399 | data->dev = dev; | |
400 | data->enable_gpio = enable_gpio; | |
401 | data->regmap = regmap; | |
402 | i2c_set_clientdata(i2c, data); | |
403 | ||
404 | ret = regmap_read(regmap, RT6190_REG_VID, &vid); | |
405 | if (ret) | |
406 | return dev_err_probe(dev, ret, "Failed to read VID\n"); | |
407 | ||
408 | if (vid != RICHTEK_VID) | |
409 | return dev_err_probe(dev, -ENODEV, "Incorrect VID 0x%02x\n", vid); | |
410 | ||
411 | ret = rt6190_init_registers(regmap); | |
412 | if (ret) | |
413 | return dev_err_probe(dev, ret, "Failed to init registers\n"); | |
414 | ||
415 | pm_runtime_set_active(dev); | |
416 | ret = devm_pm_runtime_enable(dev); | |
417 | if (ret) | |
418 | return dev_err_probe(dev, ret, "Failed to set pm_runtime enable\n"); | |
419 | ||
420 | cfg.dev = dev; | |
421 | cfg.of_node = dev->of_node; | |
422 | cfg.driver_data = data; | |
423 | cfg.init_data = of_get_regulator_init_data(dev, dev->of_node, | |
424 | &rt6190_regulator_desc); | |
425 | ||
426 | rdev = devm_regulator_register(dev, &rt6190_regulator_desc, &cfg); | |
427 | if (IS_ERR(rdev)) | |
428 | return dev_err_probe(dev, PTR_ERR(rdev), "Failed to register regulator\n"); | |
429 | ||
430 | if (i2c->irq) { | |
431 | ret = devm_request_threaded_irq(dev, i2c->irq, NULL, | |
432 | rt6190_irq_handler, | |
433 | IRQF_ONESHOT, dev_name(dev), | |
434 | rdev); | |
435 | if (ret) | |
436 | return dev_err_probe(dev, ret, "Failed to register interrupt\n"); | |
437 | } | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | static int rt6190_runtime_suspend(struct device *dev) | |
443 | { | |
444 | struct rt6190_data *data = dev_get_drvdata(dev); | |
445 | struct regmap *regmap = data->regmap; | |
446 | ||
447 | if (!data->enable_gpio) | |
448 | return 0; | |
449 | ||
450 | regcache_cache_only(regmap, true); | |
451 | regcache_mark_dirty(regmap); | |
452 | ||
453 | gpiod_set_value(data->enable_gpio, 0); | |
454 | ||
455 | return 0; | |
456 | } | |
457 | ||
458 | static int rt6190_runtime_resume(struct device *dev) | |
459 | { | |
460 | struct rt6190_data *data = dev_get_drvdata(dev); | |
461 | struct regmap *regmap = data->regmap; | |
462 | ||
463 | if (!data->enable_gpio) | |
464 | return 0; | |
465 | ||
466 | gpiod_set_value(data->enable_gpio, 1); | |
467 | usleep_range(RT6190_EN_TIME_US, RT6190_EN_TIME_US * 2); | |
468 | ||
469 | regcache_cache_only(regmap, false); | |
470 | return regcache_sync(regmap); | |
471 | } | |
472 | ||
473 | static const struct dev_pm_ops __maybe_unused rt6190_dev_pm = { | |
474 | RUNTIME_PM_OPS(rt6190_runtime_suspend, rt6190_runtime_resume, NULL) | |
475 | }; | |
476 | ||
477 | static const struct of_device_id rt6190_of_dev_table[] = { | |
478 | { .compatible = "richtek,rt6190" }, | |
479 | {} | |
480 | }; | |
481 | MODULE_DEVICE_TABLE(of, rt6190_of_dev_table); | |
482 | ||
483 | static struct i2c_driver rt6190_driver = { | |
484 | .driver = { | |
485 | .name = "rt6190", | |
bdce47bb | 486 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
e6999e7c CH |
487 | .of_match_table = rt6190_of_dev_table, |
488 | .pm = pm_ptr(&rt6190_dev_pm), | |
489 | }, | |
964e1865 | 490 | .probe = rt6190_probe, |
e6999e7c CH |
491 | }; |
492 | module_i2c_driver(rt6190_driver); | |
493 | ||
494 | MODULE_DESCRIPTION("Richtek RT6190 regulator driver"); | |
495 | MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); | |
496 | MODULE_LICENSE("GPL"); |