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