Commit | Line | Data |
---|---|---|
b8c054a5 CH |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | ||
3 | #include <linux/delay.h> | |
4 | #include <linux/gpio/consumer.h> | |
5 | #include <linux/i2c.h> | |
6 | #include <linux/interrupt.h> | |
7 | #include <linux/kernel.h> | |
8 | #include <linux/module.h> | |
9 | #include <linux/property.h> | |
10 | #include <linux/regmap.h> | |
11 | #include <linux/regulator/driver.h> | |
12 | ||
13 | #define RTMV20_REG_DEVINFO 0x00 | |
14 | #define RTMV20_REG_PULSEDELAY 0x01 | |
15 | #define RTMV20_REG_PULSEWIDTH 0x03 | |
16 | #define RTMV20_REG_LDCTRL1 0x05 | |
17 | #define RTMV20_REG_ESPULSEWIDTH 0x06 | |
18 | #define RTMV20_REG_ESLDCTRL1 0x08 | |
19 | #define RTMV20_REG_LBP 0x0A | |
20 | #define RTMV20_REG_LDCTRL2 0x0B | |
21 | #define RTMV20_REG_FSIN1CTRL1 0x0D | |
22 | #define RTMV20_REG_FSIN1CTRL3 0x0F | |
23 | #define RTMV20_REG_FSIN2CTRL1 0x10 | |
24 | #define RTMV20_REG_FSIN2CTRL3 0x12 | |
25 | #define RTMV20_REG_ENCTRL 0x13 | |
26 | #define RTMV20_REG_STRBVSYNDLYL 0x29 | |
27 | #define RTMV20_REG_LDIRQ 0x30 | |
28 | #define RTMV20_REG_LDSTAT 0x40 | |
29 | #define RTMV20_REG_LDMASK 0x50 | |
46639a5e | 30 | #define RTMV20_MAX_REGS (RTMV20_REG_LDMASK + 1) |
b8c054a5 CH |
31 | |
32 | #define RTMV20_VID_MASK GENMASK(7, 4) | |
33 | #define RICHTEK_VID 0x80 | |
34 | #define RTMV20_LDCURR_MASK GENMASK(7, 0) | |
35 | #define RTMV20_DELAY_MASK GENMASK(9, 0) | |
36 | #define RTMV20_WIDTH_MASK GENMASK(13, 0) | |
37 | #define RTMV20_WIDTH2_MASK GENMASK(7, 0) | |
38 | #define RTMV20_LBPLVL_MASK GENMASK(3, 0) | |
39 | #define RTMV20_LBPEN_MASK BIT(7) | |
2b6a761b | 40 | #define RTMV20_STROBEPOL_MASK BIT(0) |
b8c054a5 CH |
41 | #define RTMV20_VSYNPOL_MASK BIT(1) |
42 | #define RTMV20_FSINEN_MASK BIT(7) | |
43 | #define RTMV20_ESEN_MASK BIT(6) | |
44 | #define RTMV20_FSINOUT_MASK BIT(2) | |
45 | #define LDENABLE_MASK (BIT(3) | BIT(0)) | |
46 | ||
47 | #define OTPEVT_MASK BIT(4) | |
48 | #define SHORTEVT_MASK BIT(3) | |
49 | #define OPENEVT_MASK BIT(2) | |
50 | #define LBPEVT_MASK BIT(1) | |
51 | #define OCPEVT_MASK BIT(0) | |
52 | #define FAILEVT_MASK (SHORTEVT_MASK | OPENEVT_MASK | LBPEVT_MASK) | |
53 | ||
54 | #define RTMV20_LSW_MINUA 0 | |
55 | #define RTMV20_LSW_MAXUA 6000000 | |
56 | #define RTMV20_LSW_STEPUA 30000 | |
57 | ||
58 | #define RTMV20_LSW_DEFAULTUA 3000000 | |
59 | ||
60 | #define RTMV20_I2CRDY_TIMEUS 200 | |
61 | #define RTMV20_CSRDY_TIMEUS 2000 | |
62 | ||
63 | struct rtmv20_priv { | |
64 | struct device *dev; | |
65 | struct regmap *regmap; | |
66 | struct gpio_desc *enable_gpio; | |
67 | struct regulator_dev *rdev; | |
68 | }; | |
69 | ||
70 | static int rtmv20_lsw_enable(struct regulator_dev *rdev) | |
71 | { | |
72 | struct rtmv20_priv *priv = rdev_get_drvdata(rdev); | |
73 | int ret; | |
74 | ||
75 | gpiod_set_value(priv->enable_gpio, 1); | |
76 | ||
77 | /* Wait for I2C can be accessed */ | |
78 | usleep_range(RTMV20_I2CRDY_TIMEUS, RTMV20_I2CRDY_TIMEUS + 100); | |
79 | ||
80 | /* HW re-enable, disable cache only and sync regcache here */ | |
81 | regcache_cache_only(priv->regmap, false); | |
82 | ret = regcache_sync(priv->regmap); | |
83 | if (ret) | |
84 | return ret; | |
85 | ||
86 | return regulator_enable_regmap(rdev); | |
87 | } | |
88 | ||
89 | static int rtmv20_lsw_disable(struct regulator_dev *rdev) | |
90 | { | |
91 | struct rtmv20_priv *priv = rdev_get_drvdata(rdev); | |
92 | int ret; | |
93 | ||
94 | ret = regulator_disable_regmap(rdev); | |
95 | if (ret) | |
96 | return ret; | |
97 | ||
98 | /* Mark the regcache as dirty and cache only before HW disabled */ | |
99 | regcache_cache_only(priv->regmap, true); | |
100 | regcache_mark_dirty(priv->regmap); | |
101 | ||
102 | gpiod_set_value(priv->enable_gpio, 0); | |
103 | ||
104 | return 0; | |
105 | } | |
106 | ||
86ab21cc AL |
107 | static int rtmv20_lsw_set_current_limit(struct regulator_dev *rdev, int min_uA, |
108 | int max_uA) | |
109 | { | |
110 | int sel; | |
111 | ||
112 | if (min_uA > RTMV20_LSW_MAXUA || max_uA < RTMV20_LSW_MINUA) | |
113 | return -EINVAL; | |
114 | ||
115 | if (max_uA > RTMV20_LSW_MAXUA) | |
116 | max_uA = RTMV20_LSW_MAXUA; | |
117 | ||
118 | sel = (max_uA - RTMV20_LSW_MINUA) / RTMV20_LSW_STEPUA; | |
119 | ||
120 | /* Ensure the selected setting is still in range */ | |
121 | if ((sel * RTMV20_LSW_STEPUA + RTMV20_LSW_MINUA) < min_uA) | |
122 | return -EINVAL; | |
123 | ||
124 | sel <<= ffs(rdev->desc->csel_mask) - 1; | |
125 | ||
126 | return regmap_update_bits(rdev->regmap, rdev->desc->csel_reg, | |
127 | rdev->desc->csel_mask, sel); | |
128 | } | |
129 | ||
130 | static int rtmv20_lsw_get_current_limit(struct regulator_dev *rdev) | |
131 | { | |
132 | unsigned int val; | |
133 | int ret; | |
134 | ||
135 | ret = regmap_read(rdev->regmap, rdev->desc->csel_reg, &val); | |
136 | if (ret) | |
137 | return ret; | |
138 | ||
139 | val &= rdev->desc->csel_mask; | |
140 | val >>= ffs(rdev->desc->csel_mask) - 1; | |
141 | ||
142 | return val * RTMV20_LSW_STEPUA + RTMV20_LSW_MINUA; | |
143 | } | |
144 | ||
b8c054a5 | 145 | static const struct regulator_ops rtmv20_regulator_ops = { |
86ab21cc AL |
146 | .set_current_limit = rtmv20_lsw_set_current_limit, |
147 | .get_current_limit = rtmv20_lsw_get_current_limit, | |
b8c054a5 CH |
148 | .enable = rtmv20_lsw_enable, |
149 | .disable = rtmv20_lsw_disable, | |
150 | .is_enabled = regulator_is_enabled_regmap, | |
151 | }; | |
152 | ||
153 | static const struct regulator_desc rtmv20_lsw_desc = { | |
154 | .name = "rtmv20,lsw", | |
155 | .of_match = of_match_ptr("lsw"), | |
156 | .type = REGULATOR_CURRENT, | |
157 | .owner = THIS_MODULE, | |
158 | .ops = &rtmv20_regulator_ops, | |
159 | .csel_reg = RTMV20_REG_LDCTRL1, | |
160 | .csel_mask = RTMV20_LDCURR_MASK, | |
161 | .enable_reg = RTMV20_REG_ENCTRL, | |
162 | .enable_mask = LDENABLE_MASK, | |
163 | .enable_time = RTMV20_CSRDY_TIMEUS, | |
164 | }; | |
165 | ||
166 | static irqreturn_t rtmv20_irq_handler(int irq, void *data) | |
167 | { | |
168 | struct rtmv20_priv *priv = data; | |
169 | unsigned int val; | |
170 | int ret; | |
171 | ||
172 | ret = regmap_read(priv->regmap, RTMV20_REG_LDIRQ, &val); | |
173 | if (ret) { | |
174 | dev_err(priv->dev, "Failed to get irq flags\n"); | |
175 | return IRQ_NONE; | |
176 | } | |
177 | ||
178 | if (val & OTPEVT_MASK) | |
179 | regulator_notifier_call_chain(priv->rdev, REGULATOR_EVENT_OVER_TEMP, NULL); | |
180 | ||
181 | if (val & OCPEVT_MASK) | |
182 | regulator_notifier_call_chain(priv->rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); | |
183 | ||
184 | if (val & FAILEVT_MASK) | |
185 | regulator_notifier_call_chain(priv->rdev, REGULATOR_EVENT_FAIL, NULL); | |
186 | ||
187 | return IRQ_HANDLED; | |
188 | } | |
189 | ||
190 | static u32 clamp_to_selector(u32 val, u32 min, u32 max, u32 step) | |
191 | { | |
192 | u32 retval = clamp_val(val, min, max); | |
193 | ||
194 | return (retval - min) / step; | |
195 | } | |
196 | ||
197 | static int rtmv20_properties_init(struct rtmv20_priv *priv) | |
198 | { | |
199 | const struct { | |
200 | const char *name; | |
201 | u32 def; | |
202 | u32 min; | |
203 | u32 max; | |
204 | u32 step; | |
205 | u32 addr; | |
206 | u32 mask; | |
207 | } props[] = { | |
89a5f77e | 208 | { "richtek,ld-pulse-delay-us", 0, 0, 100000, 100, RTMV20_REG_PULSEDELAY, |
b8c054a5 | 209 | RTMV20_DELAY_MASK }, |
89a5f77e | 210 | { "richtek,ld-pulse-width-us", 1200, 0, 10000, 1, RTMV20_REG_PULSEWIDTH, |
b8c054a5 | 211 | RTMV20_WIDTH_MASK }, |
89a5f77e | 212 | { "richtek,fsin1-delay-us", 23000, 0, 100000, 100, RTMV20_REG_FSIN1CTRL1, |
b8c054a5 | 213 | RTMV20_DELAY_MASK }, |
89a5f77e CH |
214 | { "richtek,fsin1-width-us", 160, 40, 10000, 40, RTMV20_REG_FSIN1CTRL3, |
215 | RTMV20_WIDTH2_MASK }, | |
216 | { "richtek,fsin2-delay-us", 23000, 0, 100000, 100, RTMV20_REG_FSIN2CTRL1, | |
b8c054a5 | 217 | RTMV20_DELAY_MASK }, |
89a5f77e CH |
218 | { "richtek,fsin2-width-us", 160, 40, 10000, 40, RTMV20_REG_FSIN2CTRL3, |
219 | RTMV20_WIDTH2_MASK }, | |
220 | { "richtek,es-pulse-width-us", 1200, 0, 10000, 1, RTMV20_REG_ESPULSEWIDTH, | |
b8c054a5 | 221 | RTMV20_WIDTH_MASK }, |
89a5f77e CH |
222 | { "richtek,es-ld-current-microamp", 3000000, 0, 6000000, 30000, |
223 | RTMV20_REG_ESLDCTRL1, RTMV20_LDCURR_MASK }, | |
224 | { "richtek,lbp-level-microvolt", 2700000, 2400000, 3700000, 100000, RTMV20_REG_LBP, | |
b8c054a5 | 225 | RTMV20_LBPLVL_MASK }, |
89a5f77e CH |
226 | { "richtek,lbp-enable", 0, 0, 1, 1, RTMV20_REG_LBP, RTMV20_LBPEN_MASK }, |
227 | { "richtek,strobe-polarity-high", 1, 0, 1, 1, RTMV20_REG_LDCTRL2, | |
228 | RTMV20_STROBEPOL_MASK }, | |
229 | { "richtek,vsync-polarity-high", 1, 0, 1, 1, RTMV20_REG_LDCTRL2, | |
230 | RTMV20_VSYNPOL_MASK }, | |
231 | { "richtek,fsin-enable", 0, 0, 1, 1, RTMV20_REG_ENCTRL, RTMV20_FSINEN_MASK }, | |
232 | { "richtek,fsin-output", 0, 0, 1, 1, RTMV20_REG_ENCTRL, RTMV20_FSINOUT_MASK }, | |
233 | { "richtek,es-enable", 0, 0, 1, 1, RTMV20_REG_ENCTRL, RTMV20_ESEN_MASK }, | |
b8c054a5 CH |
234 | }; |
235 | int i, ret; | |
236 | ||
237 | for (i = 0; i < ARRAY_SIZE(props); i++) { | |
238 | __be16 bval16; | |
239 | u16 val16; | |
240 | u32 temp; | |
241 | int significant_bit = fls(props[i].mask); | |
242 | int shift = ffs(props[i].mask) - 1; | |
243 | ||
244 | if (props[i].max > 1) { | |
245 | ret = device_property_read_u32(priv->dev, props[i].name, &temp); | |
246 | if (ret) | |
247 | temp = props[i].def; | |
248 | } else | |
249 | temp = device_property_read_bool(priv->dev, props[i].name); | |
250 | ||
251 | temp = clamp_to_selector(temp, props[i].min, props[i].max, props[i].step); | |
252 | ||
253 | /* If significant bit is over 8, two byte access, others one */ | |
254 | if (significant_bit > 8) { | |
255 | ret = regmap_raw_read(priv->regmap, props[i].addr, &bval16, sizeof(bval16)); | |
256 | if (ret) | |
257 | return ret; | |
258 | ||
259 | val16 = be16_to_cpu(bval16); | |
260 | val16 &= ~props[i].mask; | |
261 | val16 |= (temp << shift); | |
262 | bval16 = cpu_to_be16(val16); | |
263 | ||
264 | ret = regmap_raw_write(priv->regmap, props[i].addr, &bval16, | |
265 | sizeof(bval16)); | |
266 | } else { | |
267 | ret = regmap_update_bits(priv->regmap, props[i].addr, props[i].mask, | |
268 | temp << shift); | |
269 | } | |
270 | ||
271 | if (ret) | |
272 | return ret; | |
273 | } | |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
278 | static int rtmv20_check_chip_exist(struct rtmv20_priv *priv) | |
279 | { | |
280 | unsigned int val; | |
281 | int ret; | |
282 | ||
283 | ret = regmap_read(priv->regmap, RTMV20_REG_DEVINFO, &val); | |
284 | if (ret) | |
285 | return ret; | |
286 | ||
287 | if ((val & RTMV20_VID_MASK) != RICHTEK_VID) | |
288 | return -ENODEV; | |
289 | ||
290 | return 0; | |
291 | } | |
292 | ||
293 | static bool rtmv20_is_accessible_reg(struct device *dev, unsigned int reg) | |
294 | { | |
295 | switch (reg) { | |
296 | case RTMV20_REG_DEVINFO ... RTMV20_REG_STRBVSYNDLYL: | |
297 | case RTMV20_REG_LDIRQ: | |
298 | case RTMV20_REG_LDSTAT: | |
299 | case RTMV20_REG_LDMASK: | |
300 | return true; | |
301 | } | |
302 | return false; | |
303 | } | |
304 | ||
305 | static bool rtmv20_is_volatile_reg(struct device *dev, unsigned int reg) | |
306 | { | |
307 | if (reg == RTMV20_REG_LDIRQ || reg == RTMV20_REG_LDSTAT) | |
308 | return true; | |
309 | return false; | |
310 | } | |
311 | ||
312 | static const struct regmap_config rtmv20_regmap_config = { | |
313 | .reg_bits = 8, | |
314 | .val_bits = 8, | |
315 | .cache_type = REGCACHE_RBTREE, | |
316 | .max_register = RTMV20_REG_LDMASK, | |
46639a5e | 317 | .num_reg_defaults_raw = RTMV20_MAX_REGS, |
b8c054a5 CH |
318 | |
319 | .writeable_reg = rtmv20_is_accessible_reg, | |
320 | .readable_reg = rtmv20_is_accessible_reg, | |
321 | .volatile_reg = rtmv20_is_volatile_reg, | |
322 | }; | |
323 | ||
324 | static int rtmv20_probe(struct i2c_client *i2c) | |
325 | { | |
326 | struct rtmv20_priv *priv; | |
327 | struct regulator_config config = {}; | |
328 | int ret; | |
329 | ||
330 | priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); | |
331 | if (!priv) | |
332 | return -ENOMEM; | |
333 | ||
334 | priv->dev = &i2c->dev; | |
335 | ||
336 | /* Before regmap register, configure HW enable to make I2C accessible */ | |
337 | priv->enable_gpio = devm_gpiod_get(&i2c->dev, "enable", GPIOD_OUT_HIGH); | |
338 | if (IS_ERR(priv->enable_gpio)) { | |
339 | dev_err(&i2c->dev, "Failed to get enable gpio\n"); | |
340 | return PTR_ERR(priv->enable_gpio); | |
341 | } | |
342 | ||
343 | /* Wait for I2C can be accessed */ | |
344 | usleep_range(RTMV20_I2CRDY_TIMEUS, RTMV20_I2CRDY_TIMEUS + 100); | |
345 | ||
346 | priv->regmap = devm_regmap_init_i2c(i2c, &rtmv20_regmap_config); | |
347 | if (IS_ERR(priv->regmap)) { | |
348 | dev_err(&i2c->dev, "Failed to allocate register map\n"); | |
349 | return PTR_ERR(priv->regmap); | |
350 | } | |
351 | ||
352 | ret = rtmv20_check_chip_exist(priv); | |
353 | if (ret) { | |
354 | dev_err(&i2c->dev, "Chip vendor info is not matched\n"); | |
355 | return ret; | |
356 | } | |
357 | ||
358 | ret = rtmv20_properties_init(priv); | |
359 | if (ret) { | |
360 | dev_err(&i2c->dev, "Failed to init properties\n"); | |
361 | return ret; | |
362 | } | |
363 | ||
364 | /* | |
365 | * keep in shutdown mode to minimize the current consumption | |
366 | * and also mark regcache as dirty | |
367 | */ | |
6228cc8a | 368 | regcache_cache_only(priv->regmap, true); |
b8c054a5 CH |
369 | regcache_mark_dirty(priv->regmap); |
370 | gpiod_set_value(priv->enable_gpio, 0); | |
371 | ||
372 | config.dev = &i2c->dev; | |
373 | config.regmap = priv->regmap; | |
374 | config.driver_data = priv; | |
375 | priv->rdev = devm_regulator_register(&i2c->dev, &rtmv20_lsw_desc, &config); | |
376 | if (IS_ERR(priv->rdev)) { | |
377 | dev_err(&i2c->dev, "Failed to register regulator\n"); | |
378 | return PTR_ERR(priv->rdev); | |
379 | } | |
380 | ||
381 | /* Unmask all events before IRQ registered */ | |
382 | ret = regmap_write(priv->regmap, RTMV20_REG_LDMASK, 0); | |
383 | if (ret) | |
384 | return ret; | |
385 | ||
386 | return devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rtmv20_irq_handler, | |
387 | IRQF_ONESHOT, dev_name(&i2c->dev), priv); | |
388 | } | |
389 | ||
390 | static int __maybe_unused rtmv20_suspend(struct device *dev) | |
391 | { | |
392 | struct i2c_client *i2c = to_i2c_client(dev); | |
393 | ||
394 | /* | |
395 | * When system suspend, disable irq to prevent interrupt trigger | |
396 | * during I2C bus suspend | |
397 | */ | |
398 | disable_irq(i2c->irq); | |
399 | if (device_may_wakeup(dev)) | |
400 | enable_irq_wake(i2c->irq); | |
401 | ||
402 | return 0; | |
403 | } | |
404 | ||
405 | static int __maybe_unused rtmv20_resume(struct device *dev) | |
406 | { | |
407 | struct i2c_client *i2c = to_i2c_client(dev); | |
408 | ||
409 | /* Enable irq after I2C bus already resume */ | |
410 | enable_irq(i2c->irq); | |
411 | if (device_may_wakeup(dev)) | |
412 | disable_irq_wake(i2c->irq); | |
413 | ||
414 | return 0; | |
415 | } | |
416 | ||
417 | static SIMPLE_DEV_PM_OPS(rtmv20_pm, rtmv20_suspend, rtmv20_resume); | |
418 | ||
419 | static const struct of_device_id __maybe_unused rtmv20_of_id[] = { | |
420 | { .compatible = "richtek,rtmv20", }, | |
421 | {} | |
422 | }; | |
423 | MODULE_DEVICE_TABLE(of, rtmv20_of_id); | |
424 | ||
425 | static struct i2c_driver rtmv20_driver = { | |
426 | .driver = { | |
427 | .name = "rtmv20", | |
67dc71c6 | 428 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
b8c054a5 CH |
429 | .of_match_table = of_match_ptr(rtmv20_of_id), |
430 | .pm = &rtmv20_pm, | |
431 | }, | |
964e1865 | 432 | .probe = rtmv20_probe, |
b8c054a5 CH |
433 | }; |
434 | module_i2c_driver(rtmv20_driver); | |
435 | ||
436 | MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); | |
437 | MODULE_DESCRIPTION("Richtek RTMV20 Regulator Driver"); | |
438 | MODULE_LICENSE("GPL v2"); |