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[] = { | |
169 | { "ld-pulse-delay-us", 0, 0, 100000, 100, RTMV20_REG_PULSEDELAY, | |
170 | RTMV20_DELAY_MASK }, | |
171 | { "ld-pulse-width-us", 1200, 0, 10000, 1, RTMV20_REG_PULSEWIDTH, | |
172 | RTMV20_WIDTH_MASK }, | |
173 | { "fsin1-delay-us", 23000, 0, 100000, 100, RTMV20_REG_FSIN1CTRL1, | |
174 | RTMV20_DELAY_MASK }, | |
175 | { "fsin1-width-us", 160, 40, 10000, 40, RTMV20_REG_FSIN1CTRL3, RTMV20_WIDTH2_MASK }, | |
176 | { "fsin2-delay-us", 23000, 0, 100000, 100, RTMV20_REG_FSIN2CTRL1, | |
177 | RTMV20_DELAY_MASK }, | |
178 | { "fsin2-width-us", 160, 40, 10000, 40, RTMV20_REG_FSIN2CTRL3, RTMV20_WIDTH2_MASK }, | |
179 | { "es-pulse-width-us", 1200, 0, 10000, 1, RTMV20_REG_ESPULSEWIDTH, | |
180 | RTMV20_WIDTH_MASK }, | |
181 | { "es-ld-current-microamp", 3000000, 0, 6000000, 30000, RTMV20_REG_ESLDCTRL1, | |
182 | RTMV20_LDCURR_MASK }, | |
183 | { "lbp-level-microvolt", 2700000, 2400000, 3700000, 100000, RTMV20_REG_LBP, | |
184 | RTMV20_LBPLVL_MASK }, | |
185 | { "lbp-enable", 0, 0, 1, 1, RTMV20_REG_LBP, RTMV20_LBPEN_MASK }, | |
186 | { "strobe-polarity-high", 1, 0, 1, 1, RTMV20_REG_LDCTRL2, RTMV20_STROBEPOL_MASK }, | |
187 | { "vsync-polarity-high", 1, 0, 1, 1, RTMV20_REG_LDCTRL2, RTMV20_VSYNPOL_MASK }, | |
188 | { "fsin-enable", 0, 0, 1, 1, RTMV20_REG_ENCTRL, RTMV20_FSINEN_MASK }, | |
189 | { "fsin-output", 0, 0, 1, 1, RTMV20_REG_ENCTRL, RTMV20_FSINOUT_MASK }, | |
190 | { "es-enable", 0, 0, 1, 1, RTMV20_REG_ENCTRL, RTMV20_ESEN_MASK }, | |
191 | }; | |
192 | int i, ret; | |
193 | ||
194 | for (i = 0; i < ARRAY_SIZE(props); i++) { | |
195 | __be16 bval16; | |
196 | u16 val16; | |
197 | u32 temp; | |
198 | int significant_bit = fls(props[i].mask); | |
199 | int shift = ffs(props[i].mask) - 1; | |
200 | ||
201 | if (props[i].max > 1) { | |
202 | ret = device_property_read_u32(priv->dev, props[i].name, &temp); | |
203 | if (ret) | |
204 | temp = props[i].def; | |
205 | } else | |
206 | temp = device_property_read_bool(priv->dev, props[i].name); | |
207 | ||
208 | temp = clamp_to_selector(temp, props[i].min, props[i].max, props[i].step); | |
209 | ||
210 | /* If significant bit is over 8, two byte access, others one */ | |
211 | if (significant_bit > 8) { | |
212 | ret = regmap_raw_read(priv->regmap, props[i].addr, &bval16, sizeof(bval16)); | |
213 | if (ret) | |
214 | return ret; | |
215 | ||
216 | val16 = be16_to_cpu(bval16); | |
217 | val16 &= ~props[i].mask; | |
218 | val16 |= (temp << shift); | |
219 | bval16 = cpu_to_be16(val16); | |
220 | ||
221 | ret = regmap_raw_write(priv->regmap, props[i].addr, &bval16, | |
222 | sizeof(bval16)); | |
223 | } else { | |
224 | ret = regmap_update_bits(priv->regmap, props[i].addr, props[i].mask, | |
225 | temp << shift); | |
226 | } | |
227 | ||
228 | if (ret) | |
229 | return ret; | |
230 | } | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
235 | static int rtmv20_check_chip_exist(struct rtmv20_priv *priv) | |
236 | { | |
237 | unsigned int val; | |
238 | int ret; | |
239 | ||
240 | ret = regmap_read(priv->regmap, RTMV20_REG_DEVINFO, &val); | |
241 | if (ret) | |
242 | return ret; | |
243 | ||
244 | if ((val & RTMV20_VID_MASK) != RICHTEK_VID) | |
245 | return -ENODEV; | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | static bool rtmv20_is_accessible_reg(struct device *dev, unsigned int reg) | |
251 | { | |
252 | switch (reg) { | |
253 | case RTMV20_REG_DEVINFO ... RTMV20_REG_STRBVSYNDLYL: | |
254 | case RTMV20_REG_LDIRQ: | |
255 | case RTMV20_REG_LDSTAT: | |
256 | case RTMV20_REG_LDMASK: | |
257 | return true; | |
258 | } | |
259 | return false; | |
260 | } | |
261 | ||
262 | static bool rtmv20_is_volatile_reg(struct device *dev, unsigned int reg) | |
263 | { | |
264 | if (reg == RTMV20_REG_LDIRQ || reg == RTMV20_REG_LDSTAT) | |
265 | return true; | |
266 | return false; | |
267 | } | |
268 | ||
269 | static const struct regmap_config rtmv20_regmap_config = { | |
270 | .reg_bits = 8, | |
271 | .val_bits = 8, | |
272 | .cache_type = REGCACHE_RBTREE, | |
273 | .max_register = RTMV20_REG_LDMASK, | |
274 | ||
275 | .writeable_reg = rtmv20_is_accessible_reg, | |
276 | .readable_reg = rtmv20_is_accessible_reg, | |
277 | .volatile_reg = rtmv20_is_volatile_reg, | |
278 | }; | |
279 | ||
280 | static int rtmv20_probe(struct i2c_client *i2c) | |
281 | { | |
282 | struct rtmv20_priv *priv; | |
283 | struct regulator_config config = {}; | |
284 | int ret; | |
285 | ||
286 | priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); | |
287 | if (!priv) | |
288 | return -ENOMEM; | |
289 | ||
290 | priv->dev = &i2c->dev; | |
291 | ||
292 | /* Before regmap register, configure HW enable to make I2C accessible */ | |
293 | priv->enable_gpio = devm_gpiod_get(&i2c->dev, "enable", GPIOD_OUT_HIGH); | |
294 | if (IS_ERR(priv->enable_gpio)) { | |
295 | dev_err(&i2c->dev, "Failed to get enable gpio\n"); | |
296 | return PTR_ERR(priv->enable_gpio); | |
297 | } | |
298 | ||
299 | /* Wait for I2C can be accessed */ | |
300 | usleep_range(RTMV20_I2CRDY_TIMEUS, RTMV20_I2CRDY_TIMEUS + 100); | |
301 | ||
302 | priv->regmap = devm_regmap_init_i2c(i2c, &rtmv20_regmap_config); | |
303 | if (IS_ERR(priv->regmap)) { | |
304 | dev_err(&i2c->dev, "Failed to allocate register map\n"); | |
305 | return PTR_ERR(priv->regmap); | |
306 | } | |
307 | ||
308 | ret = rtmv20_check_chip_exist(priv); | |
309 | if (ret) { | |
310 | dev_err(&i2c->dev, "Chip vendor info is not matched\n"); | |
311 | return ret; | |
312 | } | |
313 | ||
314 | ret = rtmv20_properties_init(priv); | |
315 | if (ret) { | |
316 | dev_err(&i2c->dev, "Failed to init properties\n"); | |
317 | return ret; | |
318 | } | |
319 | ||
320 | /* | |
321 | * keep in shutdown mode to minimize the current consumption | |
322 | * and also mark regcache as dirty | |
323 | */ | |
324 | regcache_mark_dirty(priv->regmap); | |
325 | gpiod_set_value(priv->enable_gpio, 0); | |
326 | ||
327 | config.dev = &i2c->dev; | |
328 | config.regmap = priv->regmap; | |
329 | config.driver_data = priv; | |
330 | priv->rdev = devm_regulator_register(&i2c->dev, &rtmv20_lsw_desc, &config); | |
331 | if (IS_ERR(priv->rdev)) { | |
332 | dev_err(&i2c->dev, "Failed to register regulator\n"); | |
333 | return PTR_ERR(priv->rdev); | |
334 | } | |
335 | ||
336 | /* Unmask all events before IRQ registered */ | |
337 | ret = regmap_write(priv->regmap, RTMV20_REG_LDMASK, 0); | |
338 | if (ret) | |
339 | return ret; | |
340 | ||
341 | return devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rtmv20_irq_handler, | |
342 | IRQF_ONESHOT, dev_name(&i2c->dev), priv); | |
343 | } | |
344 | ||
345 | static int __maybe_unused rtmv20_suspend(struct device *dev) | |
346 | { | |
347 | struct i2c_client *i2c = to_i2c_client(dev); | |
348 | ||
349 | /* | |
350 | * When system suspend, disable irq to prevent interrupt trigger | |
351 | * during I2C bus suspend | |
352 | */ | |
353 | disable_irq(i2c->irq); | |
354 | if (device_may_wakeup(dev)) | |
355 | enable_irq_wake(i2c->irq); | |
356 | ||
357 | return 0; | |
358 | } | |
359 | ||
360 | static int __maybe_unused rtmv20_resume(struct device *dev) | |
361 | { | |
362 | struct i2c_client *i2c = to_i2c_client(dev); | |
363 | ||
364 | /* Enable irq after I2C bus already resume */ | |
365 | enable_irq(i2c->irq); | |
366 | if (device_may_wakeup(dev)) | |
367 | disable_irq_wake(i2c->irq); | |
368 | ||
369 | return 0; | |
370 | } | |
371 | ||
372 | static SIMPLE_DEV_PM_OPS(rtmv20_pm, rtmv20_suspend, rtmv20_resume); | |
373 | ||
374 | static const struct of_device_id __maybe_unused rtmv20_of_id[] = { | |
375 | { .compatible = "richtek,rtmv20", }, | |
376 | {} | |
377 | }; | |
378 | MODULE_DEVICE_TABLE(of, rtmv20_of_id); | |
379 | ||
380 | static struct i2c_driver rtmv20_driver = { | |
381 | .driver = { | |
382 | .name = "rtmv20", | |
383 | .of_match_table = of_match_ptr(rtmv20_of_id), | |
384 | .pm = &rtmv20_pm, | |
385 | }, | |
386 | .probe_new = rtmv20_probe, | |
387 | }; | |
388 | module_i2c_driver(rtmv20_driver); | |
389 | ||
390 | MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>"); | |
391 | MODULE_DESCRIPTION("Richtek RTMV20 Regulator Driver"); | |
392 | MODULE_LICENSE("GPL v2"); |