Commit | Line | Data |
---|---|---|
233cb8a4 CW |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Copyright (C) 2022 Richtek Technology Corp. | |
4 | * | |
5 | * Author: ChiaEn Wu <chiaen_wu@richtek.com> | |
6 | */ | |
7 | ||
8 | #include <linux/bitfield.h> | |
9 | #include <linux/bits.h> | |
10 | #include <linux/devm-helpers.h> | |
11 | #include <linux/gpio/consumer.h> | |
12 | #include <linux/iio/consumer.h> | |
13 | #include <linux/init.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/linear_range.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/power_supply.h> | |
21 | #include <linux/regmap.h> | |
22 | #include <linux/regulator/driver.h> | |
23 | #include <linux/workqueue.h> | |
24 | ||
25 | #define MT6370_REG_CHG_CTRL1 0x111 | |
26 | #define MT6370_REG_CHG_CTRL2 0x112 | |
27 | #define MT6370_REG_CHG_CTRL3 0x113 | |
28 | #define MT6370_REG_CHG_CTRL4 0x114 | |
29 | #define MT6370_REG_CHG_CTRL5 0x115 | |
30 | #define MT6370_REG_CHG_CTRL6 0x116 | |
31 | #define MT6370_REG_CHG_CTRL7 0x117 | |
32 | #define MT6370_REG_CHG_CTRL8 0x118 | |
33 | #define MT6370_REG_CHG_CTRL9 0x119 | |
34 | #define MT6370_REG_CHG_CTRL10 0x11A | |
35 | #define MT6370_REG_DEVICE_TYPE 0x122 | |
36 | #define MT6370_REG_USB_STATUS1 0x127 | |
37 | #define MT6370_REG_CHG_STAT 0x14A | |
38 | #define MT6370_REG_FLED_EN 0x17E | |
39 | #define MT6370_REG_CHG_STAT1 0X1D0 | |
40 | #define MT6370_REG_OVPCTRL_STAT 0x1D8 | |
41 | ||
42 | #define MT6370_VOBST_MASK GENMASK(7, 2) | |
43 | #define MT6370_OTG_PIN_EN_MASK BIT(1) | |
44 | #define MT6370_OPA_MODE_MASK BIT(0) | |
45 | #define MT6370_OTG_OC_MASK GENMASK(2, 0) | |
46 | ||
47 | #define MT6370_MIVR_IBUS_TH_100_mA 100000 | |
48 | #define MT6370_ADC_CHAN_IBUS 5 | |
49 | #define MT6370_ADC_CHAN_MAX 9 | |
50 | ||
51 | enum mt6370_chg_reg_field { | |
52 | /* MT6370_REG_CHG_CTRL2 */ | |
53 | F_IINLMTSEL, F_CFO_EN, F_CHG_EN, | |
54 | /* MT6370_REG_CHG_CTRL3 */ | |
55 | F_IAICR, F_AICR_EN, F_ILIM_EN, | |
56 | /* MT6370_REG_CHG_CTRL4 */ | |
57 | F_VOREG, | |
58 | /* MT6370_REG_CHG_CTRL6 */ | |
59 | F_VMIVR, | |
60 | /* MT6370_REG_CHG_CTRL7 */ | |
61 | F_ICHG, | |
62 | /* MT6370_REG_CHG_CTRL8 */ | |
63 | F_IPREC, | |
64 | /* MT6370_REG_CHG_CTRL9 */ | |
65 | F_IEOC, | |
66 | /* MT6370_REG_DEVICE_TYPE */ | |
67 | F_USBCHGEN, | |
68 | /* MT6370_REG_USB_STATUS1 */ | |
69 | F_USB_STAT, F_CHGDET, | |
70 | /* MT6370_REG_CHG_STAT */ | |
71 | F_CHG_STAT, F_BOOST_STAT, F_VBAT_LVL, | |
72 | /* MT6370_REG_FLED_EN */ | |
73 | F_FL_STROBE, | |
74 | /* MT6370_REG_CHG_STAT1 */ | |
75 | F_CHG_MIVR_STAT, | |
76 | /* MT6370_REG_OVPCTRL_STAT */ | |
77 | F_UVP_D_STAT, | |
78 | F_MAX | |
79 | }; | |
80 | ||
81 | enum mt6370_irq { | |
82 | MT6370_IRQ_ATTACH_I = 0, | |
83 | MT6370_IRQ_UVP_D_EVT, | |
84 | MT6370_IRQ_MIVR, | |
85 | MT6370_IRQ_MAX | |
86 | }; | |
87 | ||
88 | struct mt6370_priv { | |
89 | struct device *dev; | |
90 | struct iio_channel *iio_adcs; | |
91 | struct mutex attach_lock; | |
92 | struct power_supply *psy; | |
93 | struct regmap *regmap; | |
94 | struct regmap_field *rmap_fields[F_MAX]; | |
95 | struct regulator_dev *rdev; | |
96 | struct workqueue_struct *wq; | |
97 | struct work_struct bc12_work; | |
98 | struct delayed_work mivr_dwork; | |
99 | unsigned int irq_nums[MT6370_IRQ_MAX]; | |
100 | int attach; | |
101 | int psy_usb_type; | |
102 | bool pwr_rdy; | |
103 | }; | |
104 | ||
105 | enum mt6370_usb_status { | |
106 | MT6370_USB_STAT_NO_VBUS = 0, | |
107 | MT6370_USB_STAT_VBUS_FLOW_IS_UNDER_GOING, | |
108 | MT6370_USB_STAT_SDP, | |
109 | MT6370_USB_STAT_SDP_NSTD, | |
110 | MT6370_USB_STAT_DCP, | |
111 | MT6370_USB_STAT_CDP, | |
112 | MT6370_USB_STAT_MAX | |
113 | }; | |
114 | ||
115 | struct mt6370_chg_field { | |
116 | const char *name; | |
117 | const struct linear_range *range; | |
118 | struct reg_field field; | |
119 | }; | |
120 | ||
121 | enum { | |
122 | MT6370_RANGE_F_IAICR = 0, | |
123 | MT6370_RANGE_F_VOREG, | |
124 | MT6370_RANGE_F_VMIVR, | |
125 | MT6370_RANGE_F_ICHG, | |
126 | MT6370_RANGE_F_IPREC, | |
127 | MT6370_RANGE_F_IEOC, | |
128 | MT6370_RANGE_F_MAX | |
129 | }; | |
130 | ||
131 | static const struct linear_range mt6370_chg_ranges[MT6370_RANGE_F_MAX] = { | |
132 | LINEAR_RANGE_IDX(MT6370_RANGE_F_IAICR, 100000, 0x0, 0x3F, 50000), | |
133 | LINEAR_RANGE_IDX(MT6370_RANGE_F_VOREG, 3900000, 0x0, 0x51, 10000), | |
134 | LINEAR_RANGE_IDX(MT6370_RANGE_F_VMIVR, 3900000, 0x0, 0x5F, 100000), | |
135 | LINEAR_RANGE_IDX(MT6370_RANGE_F_ICHG, 900000, 0x08, 0x31, 100000), | |
136 | LINEAR_RANGE_IDX(MT6370_RANGE_F_IPREC, 100000, 0x0, 0x0F, 50000), | |
137 | LINEAR_RANGE_IDX(MT6370_RANGE_F_IEOC, 100000, 0x0, 0x0F, 50000), | |
138 | }; | |
139 | ||
140 | #define MT6370_CHG_FIELD(_fd, _reg, _lsb, _msb) \ | |
141 | [_fd] = { \ | |
142 | .name = #_fd, \ | |
143 | .range = NULL, \ | |
144 | .field = REG_FIELD(_reg, _lsb, _msb), \ | |
145 | } | |
146 | ||
147 | #define MT6370_CHG_FIELD_RANGE(_fd, _reg, _lsb, _msb) \ | |
148 | [_fd] = { \ | |
149 | .name = #_fd, \ | |
150 | .range = &mt6370_chg_ranges[MT6370_RANGE_##_fd], \ | |
151 | .field = REG_FIELD(_reg, _lsb, _msb), \ | |
152 | } | |
153 | ||
154 | static const struct mt6370_chg_field mt6370_chg_fields[F_MAX] = { | |
155 | MT6370_CHG_FIELD(F_IINLMTSEL, MT6370_REG_CHG_CTRL2, 2, 3), | |
156 | MT6370_CHG_FIELD(F_CFO_EN, MT6370_REG_CHG_CTRL2, 1, 1), | |
157 | MT6370_CHG_FIELD(F_CHG_EN, MT6370_REG_CHG_CTRL2, 0, 0), | |
158 | MT6370_CHG_FIELD_RANGE(F_IAICR, MT6370_REG_CHG_CTRL3, 2, 7), | |
159 | MT6370_CHG_FIELD(F_AICR_EN, MT6370_REG_CHG_CTRL3, 1, 1), | |
160 | MT6370_CHG_FIELD(F_ILIM_EN, MT6370_REG_CHG_CTRL3, 0, 0), | |
161 | MT6370_CHG_FIELD_RANGE(F_VOREG, MT6370_REG_CHG_CTRL4, 1, 7), | |
162 | MT6370_CHG_FIELD_RANGE(F_VMIVR, MT6370_REG_CHG_CTRL6, 1, 7), | |
163 | MT6370_CHG_FIELD_RANGE(F_ICHG, MT6370_REG_CHG_CTRL7, 2, 7), | |
164 | MT6370_CHG_FIELD_RANGE(F_IPREC, MT6370_REG_CHG_CTRL8, 0, 3), | |
165 | MT6370_CHG_FIELD_RANGE(F_IEOC, MT6370_REG_CHG_CTRL9, 4, 7), | |
166 | MT6370_CHG_FIELD(F_USBCHGEN, MT6370_REG_DEVICE_TYPE, 7, 7), | |
167 | MT6370_CHG_FIELD(F_USB_STAT, MT6370_REG_USB_STATUS1, 4, 6), | |
168 | MT6370_CHG_FIELD(F_CHGDET, MT6370_REG_USB_STATUS1, 3, 3), | |
169 | MT6370_CHG_FIELD(F_CHG_STAT, MT6370_REG_CHG_STAT, 6, 7), | |
170 | MT6370_CHG_FIELD(F_BOOST_STAT, MT6370_REG_CHG_STAT, 3, 3), | |
171 | MT6370_CHG_FIELD(F_VBAT_LVL, MT6370_REG_CHG_STAT, 5, 5), | |
172 | MT6370_CHG_FIELD(F_FL_STROBE, MT6370_REG_FLED_EN, 2, 2), | |
173 | MT6370_CHG_FIELD(F_CHG_MIVR_STAT, MT6370_REG_CHG_STAT1, 6, 6), | |
174 | MT6370_CHG_FIELD(F_UVP_D_STAT, MT6370_REG_OVPCTRL_STAT, 4, 4), | |
175 | }; | |
176 | ||
177 | static inline int mt6370_chg_field_get(struct mt6370_priv *priv, | |
178 | enum mt6370_chg_reg_field fd, | |
179 | unsigned int *val) | |
180 | { | |
181 | int ret; | |
182 | unsigned int reg_val; | |
183 | ||
184 | ret = regmap_field_read(priv->rmap_fields[fd], ®_val); | |
185 | if (ret) | |
186 | return ret; | |
187 | ||
188 | if (mt6370_chg_fields[fd].range) | |
189 | return linear_range_get_value(mt6370_chg_fields[fd].range, | |
190 | reg_val, val); | |
191 | ||
192 | *val = reg_val; | |
193 | return 0; | |
194 | } | |
195 | ||
196 | static inline int mt6370_chg_field_set(struct mt6370_priv *priv, | |
197 | enum mt6370_chg_reg_field fd, | |
198 | unsigned int val) | |
199 | { | |
200 | int ret; | |
201 | bool f; | |
202 | const struct linear_range *r; | |
203 | ||
204 | if (mt6370_chg_fields[fd].range) { | |
205 | r = mt6370_chg_fields[fd].range; | |
206 | ||
207 | if (fd == F_VMIVR) { | |
208 | ret = linear_range_get_selector_high(r, val, &val, &f); | |
209 | if (ret) | |
210 | val = r->max_sel; | |
211 | } else { | |
212 | linear_range_get_selector_within(r, val, &val); | |
213 | } | |
214 | } | |
215 | ||
216 | return regmap_field_write(priv->rmap_fields[fd], val); | |
217 | } | |
218 | ||
219 | enum { | |
220 | MT6370_CHG_STAT_READY = 0, | |
221 | MT6370_CHG_STAT_CHARGE_IN_PROGRESS, | |
222 | MT6370_CHG_STAT_DONE, | |
223 | MT6370_CHG_STAT_FAULT, | |
224 | MT6370_CHG_STAT_MAX | |
225 | }; | |
226 | ||
227 | enum { | |
228 | MT6370_ATTACH_STAT_DETACH = 0, | |
229 | MT6370_ATTACH_STAT_ATTACH_WAIT_FOR_BC12, | |
230 | MT6370_ATTACH_STAT_ATTACH_BC12_DONE, | |
231 | MT6370_ATTACH_STAT_ATTACH_MAX | |
232 | }; | |
233 | ||
234 | static int mt6370_chg_otg_of_parse_cb(struct device_node *of, | |
235 | const struct regulator_desc *rdesc, | |
236 | struct regulator_config *rcfg) | |
237 | { | |
238 | struct mt6370_priv *priv = rcfg->driver_data; | |
239 | ||
240 | rcfg->ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(of), | |
241 | "enable", 0, GPIOD_OUT_LOW | | |
242 | GPIOD_FLAGS_BIT_NONEXCLUSIVE, | |
243 | rdesc->name); | |
244 | if (IS_ERR(rcfg->ena_gpiod)) { | |
245 | rcfg->ena_gpiod = NULL; | |
246 | return 0; | |
247 | } | |
248 | ||
249 | return regmap_update_bits(priv->regmap, MT6370_REG_CHG_CTRL1, | |
250 | MT6370_OTG_PIN_EN_MASK, | |
251 | MT6370_OTG_PIN_EN_MASK); | |
252 | } | |
253 | ||
254 | static void mt6370_chg_bc12_work_func(struct work_struct *work) | |
255 | { | |
256 | struct mt6370_priv *priv = container_of(work, struct mt6370_priv, | |
257 | bc12_work); | |
258 | int ret; | |
259 | bool rpt_psy = false; | |
260 | unsigned int attach, usb_stat; | |
261 | ||
262 | mutex_lock(&priv->attach_lock); | |
263 | attach = priv->attach; | |
264 | ||
265 | switch (attach) { | |
266 | case MT6370_ATTACH_STAT_DETACH: | |
267 | usb_stat = 0; | |
268 | break; | |
269 | case MT6370_ATTACH_STAT_ATTACH_WAIT_FOR_BC12: | |
270 | ret = mt6370_chg_field_set(priv, F_USBCHGEN, attach); | |
271 | if (ret) | |
272 | dev_err(priv->dev, "Failed to enable USB CHG EN\n"); | |
273 | goto bc12_work_func_out; | |
274 | case MT6370_ATTACH_STAT_ATTACH_BC12_DONE: | |
275 | ret = mt6370_chg_field_get(priv, F_USB_STAT, &usb_stat); | |
276 | if (ret) { | |
277 | dev_err(priv->dev, "Failed to get USB status\n"); | |
278 | goto bc12_work_func_out; | |
279 | } | |
280 | break; | |
281 | default: | |
282 | dev_err(priv->dev, "Invalid attach state\n"); | |
283 | goto bc12_work_func_out; | |
284 | } | |
285 | ||
286 | rpt_psy = true; | |
287 | ||
288 | switch (usb_stat) { | |
289 | case MT6370_USB_STAT_SDP: | |
290 | case MT6370_USB_STAT_SDP_NSTD: | |
291 | priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP; | |
292 | break; | |
293 | case MT6370_USB_STAT_DCP: | |
294 | priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_DCP; | |
295 | break; | |
296 | case MT6370_USB_STAT_CDP: | |
297 | priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_CDP; | |
298 | break; | |
299 | case MT6370_USB_STAT_NO_VBUS: | |
300 | case MT6370_USB_STAT_VBUS_FLOW_IS_UNDER_GOING: | |
301 | default: | |
302 | priv->psy_usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; | |
303 | break; | |
304 | } | |
305 | ||
306 | bc12_work_func_out: | |
307 | mutex_unlock(&priv->attach_lock); | |
308 | ||
309 | if (rpt_psy) | |
310 | power_supply_changed(priv->psy); | |
311 | } | |
312 | ||
313 | static int mt6370_chg_toggle_cfo(struct mt6370_priv *priv) | |
314 | { | |
315 | int ret; | |
316 | unsigned int fl_strobe; | |
317 | ||
318 | /* check if flash led in strobe mode */ | |
319 | ret = mt6370_chg_field_get(priv, F_FL_STROBE, &fl_strobe); | |
320 | if (ret) { | |
321 | dev_err(priv->dev, "Failed to get FL_STROBE_EN\n"); | |
322 | return ret; | |
323 | } | |
324 | ||
325 | if (fl_strobe) { | |
326 | dev_err(priv->dev, "Flash led is still in strobe mode\n"); | |
779873ec | 327 | return -EINVAL; |
233cb8a4 CW |
328 | } |
329 | ||
330 | /* cfo off */ | |
331 | ret = mt6370_chg_field_set(priv, F_CFO_EN, 0); | |
332 | if (ret) { | |
333 | dev_err(priv->dev, "Failed to disable CFO_EN\n"); | |
334 | return ret; | |
335 | } | |
336 | ||
337 | /* cfo on */ | |
338 | ret = mt6370_chg_field_set(priv, F_CFO_EN, 1); | |
339 | if (ret) | |
340 | dev_err(priv->dev, "Failed to enable CFO_EN\n"); | |
341 | ||
342 | return ret; | |
343 | } | |
344 | ||
345 | static int mt6370_chg_read_adc_chan(struct mt6370_priv *priv, unsigned int chan, | |
346 | int *val) | |
347 | { | |
348 | int ret; | |
349 | ||
350 | if (chan >= MT6370_ADC_CHAN_MAX) | |
351 | return -EINVAL; | |
352 | ||
353 | ret = iio_read_channel_processed(&priv->iio_adcs[chan], val); | |
354 | if (ret) | |
355 | dev_err(priv->dev, "Failed to read ADC\n"); | |
356 | ||
357 | return ret; | |
358 | } | |
359 | ||
360 | static void mt6370_chg_mivr_dwork_func(struct work_struct *work) | |
361 | { | |
362 | struct mt6370_priv *priv = container_of(work, struct mt6370_priv, | |
363 | mivr_dwork.work); | |
364 | int ret; | |
365 | unsigned int mivr_stat, ibus; | |
366 | ||
367 | ret = mt6370_chg_field_get(priv, F_CHG_MIVR_STAT, &mivr_stat); | |
368 | if (ret) { | |
369 | dev_err(priv->dev, "Failed to get mivr state\n"); | |
370 | goto mivr_handler_out; | |
371 | } | |
372 | ||
373 | if (!mivr_stat) | |
374 | goto mivr_handler_out; | |
375 | ||
376 | ret = mt6370_chg_read_adc_chan(priv, MT6370_ADC_CHAN_IBUS, &ibus); | |
377 | if (ret) { | |
378 | dev_err(priv->dev, "Failed to get ibus\n"); | |
379 | goto mivr_handler_out; | |
380 | } | |
381 | ||
382 | if (ibus < MT6370_MIVR_IBUS_TH_100_mA) { | |
383 | ret = mt6370_chg_toggle_cfo(priv); | |
384 | if (ret) | |
385 | dev_err(priv->dev, "Failed to toggle cfo\n"); | |
386 | } | |
387 | ||
388 | mivr_handler_out: | |
389 | enable_irq(priv->irq_nums[MT6370_IRQ_MIVR]); | |
390 | pm_relax(priv->dev); | |
391 | } | |
392 | ||
393 | static void mt6370_chg_pwr_rdy_check(struct mt6370_priv *priv) | |
394 | { | |
395 | int ret; | |
396 | unsigned int opposite_pwr_rdy, otg_en; | |
397 | union power_supply_propval val; | |
398 | ||
399 | /* Check in OTG mode or not */ | |
400 | ret = mt6370_chg_field_get(priv, F_BOOST_STAT, &otg_en); | |
401 | if (ret) { | |
402 | dev_err(priv->dev, "Failed to get OTG state\n"); | |
403 | return; | |
404 | } | |
405 | ||
406 | if (otg_en) | |
407 | return; | |
408 | ||
409 | ret = mt6370_chg_field_get(priv, F_UVP_D_STAT, &opposite_pwr_rdy); | |
410 | if (ret) { | |
411 | dev_err(priv->dev, "Failed to get opposite power ready state\n"); | |
412 | return; | |
413 | } | |
414 | ||
415 | val.intval = opposite_pwr_rdy ? | |
416 | MT6370_ATTACH_STAT_DETACH : | |
417 | MT6370_ATTACH_STAT_ATTACH_WAIT_FOR_BC12; | |
418 | ||
419 | ret = power_supply_set_property(priv->psy, POWER_SUPPLY_PROP_ONLINE, | |
420 | &val); | |
421 | if (ret) | |
422 | dev_err(priv->dev, "Failed to start attach/detach flow\n"); | |
423 | } | |
424 | ||
425 | static int mt6370_chg_get_online(struct mt6370_priv *priv, | |
426 | union power_supply_propval *val) | |
427 | { | |
428 | mutex_lock(&priv->attach_lock); | |
429 | val->intval = !!priv->attach; | |
430 | mutex_unlock(&priv->attach_lock); | |
431 | ||
432 | return 0; | |
433 | } | |
434 | ||
435 | static int mt6370_chg_get_status(struct mt6370_priv *priv, | |
436 | union power_supply_propval *val) | |
437 | { | |
438 | int ret; | |
439 | unsigned int chg_stat; | |
440 | union power_supply_propval online; | |
441 | ||
442 | ret = power_supply_get_property(priv->psy, POWER_SUPPLY_PROP_ONLINE, | |
443 | &online); | |
444 | if (ret) { | |
445 | dev_err(priv->dev, "Failed to get online status\n"); | |
446 | return ret; | |
447 | } | |
448 | ||
449 | if (!online.intval) { | |
450 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | |
451 | return 0; | |
452 | } | |
453 | ||
454 | ret = mt6370_chg_field_get(priv, F_CHG_STAT, &chg_stat); | |
455 | if (ret) | |
456 | return ret; | |
457 | ||
458 | switch (chg_stat) { | |
459 | case MT6370_CHG_STAT_READY: | |
460 | case MT6370_CHG_STAT_FAULT: | |
461 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | |
462 | return ret; | |
463 | case MT6370_CHG_STAT_CHARGE_IN_PROGRESS: | |
464 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | |
465 | return ret; | |
466 | case MT6370_CHG_STAT_DONE: | |
467 | val->intval = POWER_SUPPLY_STATUS_FULL; | |
468 | return ret; | |
469 | default: | |
470 | val->intval = POWER_SUPPLY_STATUS_UNKNOWN; | |
471 | return ret; | |
472 | } | |
473 | } | |
474 | ||
475 | static int mt6370_chg_get_charge_type(struct mt6370_priv *priv, | |
476 | union power_supply_propval *val) | |
477 | { | |
478 | int type, ret; | |
479 | unsigned int chg_stat, vbat_lvl; | |
480 | ||
481 | ret = mt6370_chg_field_get(priv, F_CHG_STAT, &chg_stat); | |
482 | if (ret) | |
483 | return ret; | |
484 | ||
485 | ret = mt6370_chg_field_get(priv, F_VBAT_LVL, &vbat_lvl); | |
486 | if (ret) | |
487 | return ret; | |
488 | ||
489 | switch (chg_stat) { | |
490 | case MT6370_CHG_STAT_CHARGE_IN_PROGRESS: | |
491 | if (vbat_lvl) | |
492 | type = POWER_SUPPLY_CHARGE_TYPE_FAST; | |
493 | else | |
494 | type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; | |
495 | break; | |
496 | case MT6370_CHG_STAT_READY: | |
497 | case MT6370_CHG_STAT_DONE: | |
498 | case MT6370_CHG_STAT_FAULT: | |
499 | default: | |
500 | type = POWER_SUPPLY_CHARGE_TYPE_NONE; | |
501 | break; | |
502 | } | |
503 | ||
504 | val->intval = type; | |
505 | ||
506 | return 0; | |
507 | } | |
508 | ||
509 | static int mt6370_chg_set_online(struct mt6370_priv *priv, | |
510 | const union power_supply_propval *val) | |
511 | { | |
512 | bool pwr_rdy = !!val->intval; | |
513 | ||
514 | mutex_lock(&priv->attach_lock); | |
515 | if (pwr_rdy == !!priv->attach) { | |
516 | dev_err(priv->dev, "pwr_rdy is same(%d)\n", pwr_rdy); | |
517 | mutex_unlock(&priv->attach_lock); | |
518 | return 0; | |
519 | } | |
520 | ||
521 | priv->attach = pwr_rdy; | |
522 | mutex_unlock(&priv->attach_lock); | |
523 | ||
524 | if (!queue_work(priv->wq, &priv->bc12_work)) | |
525 | dev_err(priv->dev, "bc12 work has already queued\n"); | |
526 | ||
527 | return 0; | |
528 | } | |
529 | ||
530 | static int mt6370_chg_get_property(struct power_supply *psy, | |
531 | enum power_supply_property psp, | |
532 | union power_supply_propval *val) | |
533 | { | |
534 | struct mt6370_priv *priv = power_supply_get_drvdata(psy); | |
535 | ||
536 | switch (psp) { | |
537 | case POWER_SUPPLY_PROP_ONLINE: | |
538 | return mt6370_chg_get_online(priv, val); | |
539 | case POWER_SUPPLY_PROP_STATUS: | |
540 | return mt6370_chg_get_status(priv, val); | |
541 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
542 | return mt6370_chg_get_charge_type(priv, val); | |
543 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | |
544 | return mt6370_chg_field_get(priv, F_ICHG, &val->intval); | |
545 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: | |
546 | val->intval = linear_range_get_max_value(&mt6370_chg_ranges[MT6370_RANGE_F_ICHG]); | |
547 | return 0; | |
548 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | |
549 | return mt6370_chg_field_get(priv, F_VOREG, &val->intval); | |
550 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: | |
551 | val->intval = linear_range_get_max_value(&mt6370_chg_ranges[MT6370_RANGE_F_VOREG]); | |
552 | return 0; | |
553 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: | |
554 | return mt6370_chg_field_get(priv, F_IAICR, &val->intval); | |
555 | case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: | |
556 | return mt6370_chg_field_get(priv, F_VMIVR, &val->intval); | |
557 | case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: | |
558 | return mt6370_chg_field_get(priv, F_IPREC, &val->intval); | |
559 | case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: | |
560 | return mt6370_chg_field_get(priv, F_IEOC, &val->intval); | |
561 | case POWER_SUPPLY_PROP_USB_TYPE: | |
562 | val->intval = priv->psy_usb_type; | |
563 | return 0; | |
564 | default: | |
565 | return -EINVAL; | |
566 | } | |
567 | } | |
568 | ||
569 | static int mt6370_chg_set_property(struct power_supply *psy, | |
570 | enum power_supply_property psp, | |
571 | const union power_supply_propval *val) | |
572 | { | |
573 | struct mt6370_priv *priv = power_supply_get_drvdata(psy); | |
574 | ||
575 | switch (psp) { | |
576 | case POWER_SUPPLY_PROP_ONLINE: | |
577 | return mt6370_chg_set_online(priv, val); | |
578 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | |
579 | return mt6370_chg_field_set(priv, F_ICHG, val->intval); | |
580 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | |
581 | return mt6370_chg_field_set(priv, F_VOREG, val->intval); | |
582 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: | |
583 | return mt6370_chg_field_set(priv, F_IAICR, val->intval); | |
584 | case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: | |
585 | return mt6370_chg_field_set(priv, F_VMIVR, val->intval); | |
586 | case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: | |
587 | return mt6370_chg_field_set(priv, F_IPREC, val->intval); | |
588 | case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: | |
589 | return mt6370_chg_field_set(priv, F_IEOC, val->intval); | |
590 | default: | |
591 | return -EINVAL; | |
592 | } | |
593 | } | |
594 | ||
595 | static int mt6370_chg_property_is_writeable(struct power_supply *psy, | |
596 | enum power_supply_property psp) | |
597 | { | |
598 | switch (psp) { | |
599 | case POWER_SUPPLY_PROP_ONLINE: | |
600 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: | |
601 | case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: | |
602 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: | |
603 | case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: | |
604 | case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: | |
605 | case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: | |
606 | return 1; | |
607 | default: | |
608 | return 0; | |
609 | } | |
610 | } | |
611 | ||
612 | static enum power_supply_property mt6370_chg_properties[] = { | |
613 | POWER_SUPPLY_PROP_ONLINE, | |
614 | POWER_SUPPLY_PROP_STATUS, | |
615 | POWER_SUPPLY_PROP_CHARGE_TYPE, | |
616 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, | |
617 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, | |
618 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, | |
619 | POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, | |
620 | POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, | |
621 | POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, | |
622 | POWER_SUPPLY_PROP_PRECHARGE_CURRENT, | |
623 | POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, | |
624 | POWER_SUPPLY_PROP_USB_TYPE, | |
625 | }; | |
626 | ||
627 | static enum power_supply_usb_type mt6370_chg_usb_types[] = { | |
628 | POWER_SUPPLY_USB_TYPE_UNKNOWN, | |
629 | POWER_SUPPLY_USB_TYPE_SDP, | |
630 | POWER_SUPPLY_USB_TYPE_CDP, | |
631 | POWER_SUPPLY_USB_TYPE_DCP, | |
632 | }; | |
633 | ||
634 | static const struct power_supply_desc mt6370_chg_psy_desc = { | |
635 | .name = "mt6370-charger", | |
636 | .type = POWER_SUPPLY_TYPE_USB, | |
637 | .properties = mt6370_chg_properties, | |
638 | .num_properties = ARRAY_SIZE(mt6370_chg_properties), | |
639 | .get_property = mt6370_chg_get_property, | |
640 | .set_property = mt6370_chg_set_property, | |
641 | .property_is_writeable = mt6370_chg_property_is_writeable, | |
642 | .usb_types = mt6370_chg_usb_types, | |
643 | .num_usb_types = ARRAY_SIZE(mt6370_chg_usb_types), | |
644 | }; | |
645 | ||
646 | static const struct regulator_ops mt6370_chg_otg_ops = { | |
647 | .list_voltage = regulator_list_voltage_linear, | |
648 | .enable = regulator_enable_regmap, | |
649 | .disable = regulator_disable_regmap, | |
650 | .is_enabled = regulator_is_enabled_regmap, | |
651 | .set_voltage_sel = regulator_set_voltage_sel_regmap, | |
652 | .get_voltage_sel = regulator_get_voltage_sel_regmap, | |
653 | .set_current_limit = regulator_set_current_limit_regmap, | |
654 | .get_current_limit = regulator_get_current_limit_regmap, | |
655 | }; | |
656 | ||
657 | static const u32 mt6370_chg_otg_oc_ma[] = { | |
658 | 500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000, | |
659 | }; | |
660 | ||
661 | static const struct regulator_desc mt6370_chg_otg_rdesc = { | |
662 | .of_match = "usb-otg-vbus-regulator", | |
663 | .of_parse_cb = mt6370_chg_otg_of_parse_cb, | |
664 | .name = "mt6370-usb-otg-vbus", | |
665 | .ops = &mt6370_chg_otg_ops, | |
666 | .owner = THIS_MODULE, | |
667 | .type = REGULATOR_VOLTAGE, | |
668 | .min_uV = 4425000, | |
669 | .uV_step = 25000, | |
670 | .n_voltages = 57, | |
671 | .vsel_reg = MT6370_REG_CHG_CTRL5, | |
672 | .vsel_mask = MT6370_VOBST_MASK, | |
673 | .enable_reg = MT6370_REG_CHG_CTRL1, | |
674 | .enable_mask = MT6370_OPA_MODE_MASK, | |
675 | .curr_table = mt6370_chg_otg_oc_ma, | |
676 | .n_current_limits = ARRAY_SIZE(mt6370_chg_otg_oc_ma), | |
677 | .csel_reg = MT6370_REG_CHG_CTRL10, | |
678 | .csel_mask = MT6370_OTG_OC_MASK, | |
679 | }; | |
680 | ||
681 | static int mt6370_chg_init_rmap_fields(struct mt6370_priv *priv) | |
682 | { | |
683 | int i; | |
684 | const struct mt6370_chg_field *fds = mt6370_chg_fields; | |
685 | ||
686 | for (i = 0; i < F_MAX; i++) { | |
687 | priv->rmap_fields[i] = devm_regmap_field_alloc(priv->dev, | |
688 | priv->regmap, | |
689 | fds[i].field); | |
690 | if (IS_ERR(priv->rmap_fields[i])) | |
691 | return dev_err_probe(priv->dev, | |
692 | PTR_ERR(priv->rmap_fields[i]), | |
693 | "Failed to allocate regmapfield[%s]\n", | |
694 | fds[i].name); | |
695 | } | |
696 | ||
697 | return 0; | |
698 | } | |
699 | ||
700 | static int mt6370_chg_init_setting(struct mt6370_priv *priv) | |
701 | { | |
702 | int ret; | |
703 | ||
704 | /* Disable usb_chg_en */ | |
705 | ret = mt6370_chg_field_set(priv, F_USBCHGEN, 0); | |
706 | if (ret) { | |
707 | dev_err(priv->dev, "Failed to disable usb_chg_en\n"); | |
708 | return ret; | |
709 | } | |
710 | ||
711 | /* Disable input current limit */ | |
712 | ret = mt6370_chg_field_set(priv, F_ILIM_EN, 0); | |
713 | if (ret) { | |
714 | dev_err(priv->dev, "Failed to disable input current limit\n"); | |
715 | return ret; | |
716 | } | |
717 | ||
718 | /* ICHG/IEOC Workaround, ICHG can not be set less than 900mA */ | |
719 | ret = mt6370_chg_field_set(priv, F_ICHG, 900000); | |
720 | if (ret) { | |
721 | dev_err(priv->dev, "Failed to set ICHG to 900mA"); | |
722 | return ret; | |
723 | } | |
724 | ||
725 | /* Change input current limit selection to using IAICR results */ | |
726 | ret = mt6370_chg_field_set(priv, F_IINLMTSEL, 2); | |
727 | if (ret) { | |
728 | dev_err(priv->dev, "Failed to set IINLMTSEL\n"); | |
729 | return ret; | |
730 | } | |
731 | ||
732 | return 0; | |
733 | } | |
734 | ||
735 | #define MT6370_CHG_DT_PROP_DECL(_name, _type, _field) \ | |
736 | { \ | |
737 | .name = "mediatek,chg-" #_name, \ | |
738 | .type = MT6370_PARSE_TYPE_##_type, \ | |
739 | .fd = _field, \ | |
740 | } | |
741 | ||
742 | static int mt6370_chg_init_otg_regulator(struct mt6370_priv *priv) | |
743 | { | |
744 | struct regulator_config rcfg = { | |
745 | .dev = priv->dev, | |
746 | .regmap = priv->regmap, | |
747 | .driver_data = priv, | |
748 | }; | |
749 | ||
750 | priv->rdev = devm_regulator_register(priv->dev, &mt6370_chg_otg_rdesc, | |
751 | &rcfg); | |
752 | ||
753 | return PTR_ERR_OR_ZERO(priv->rdev); | |
754 | } | |
755 | ||
756 | static int mt6370_chg_init_psy(struct mt6370_priv *priv) | |
757 | { | |
758 | struct power_supply_config cfg = { | |
759 | .drv_data = priv, | |
760 | .of_node = dev_of_node(priv->dev), | |
761 | }; | |
762 | ||
763 | priv->psy = devm_power_supply_register(priv->dev, &mt6370_chg_psy_desc, | |
764 | &cfg); | |
765 | ||
766 | return PTR_ERR_OR_ZERO(priv->psy); | |
767 | } | |
768 | ||
769 | static void mt6370_chg_destroy_attach_lock(void *data) | |
770 | { | |
771 | struct mutex *attach_lock = data; | |
772 | ||
773 | mutex_destroy(attach_lock); | |
774 | } | |
775 | ||
776 | static void mt6370_chg_destroy_wq(void *data) | |
777 | { | |
778 | struct workqueue_struct *wq = data; | |
779 | ||
780 | flush_workqueue(wq); | |
781 | destroy_workqueue(wq); | |
782 | } | |
783 | ||
784 | static irqreturn_t mt6370_attach_i_handler(int irq, void *data) | |
785 | { | |
786 | struct mt6370_priv *priv = data; | |
787 | unsigned int otg_en; | |
788 | int ret; | |
789 | ||
790 | /* Check in OTG mode or not */ | |
791 | ret = mt6370_chg_field_get(priv, F_BOOST_STAT, &otg_en); | |
792 | if (ret) { | |
793 | dev_err(priv->dev, "Failed to get OTG state\n"); | |
794 | return IRQ_NONE; | |
795 | } | |
796 | ||
797 | if (otg_en) | |
798 | return IRQ_HANDLED; | |
799 | ||
800 | mutex_lock(&priv->attach_lock); | |
801 | priv->attach = MT6370_ATTACH_STAT_ATTACH_BC12_DONE; | |
802 | mutex_unlock(&priv->attach_lock); | |
803 | ||
804 | if (!queue_work(priv->wq, &priv->bc12_work)) | |
805 | dev_err(priv->dev, "bc12 work has already queued\n"); | |
806 | ||
807 | return IRQ_HANDLED; | |
808 | } | |
809 | ||
810 | static irqreturn_t mt6370_uvp_d_evt_handler(int irq, void *data) | |
811 | { | |
812 | struct mt6370_priv *priv = data; | |
813 | ||
814 | mt6370_chg_pwr_rdy_check(priv); | |
815 | ||
816 | return IRQ_HANDLED; | |
817 | } | |
818 | ||
819 | static irqreturn_t mt6370_mivr_handler(int irq, void *data) | |
820 | { | |
821 | struct mt6370_priv *priv = data; | |
822 | ||
823 | pm_stay_awake(priv->dev); | |
824 | disable_irq_nosync(priv->irq_nums[MT6370_IRQ_MIVR]); | |
825 | schedule_delayed_work(&priv->mivr_dwork, msecs_to_jiffies(200)); | |
826 | ||
827 | return IRQ_HANDLED; | |
828 | } | |
829 | ||
830 | #define MT6370_CHG_IRQ(_name) \ | |
831 | { \ | |
832 | .name = #_name, \ | |
833 | .handler = mt6370_##_name##_handler, \ | |
834 | } | |
835 | ||
836 | static int mt6370_chg_init_irq(struct mt6370_priv *priv) | |
837 | { | |
838 | int i, ret; | |
839 | const struct { | |
840 | char *name; | |
841 | irq_handler_t handler; | |
842 | } mt6370_chg_irqs[] = { | |
843 | MT6370_CHG_IRQ(attach_i), | |
844 | MT6370_CHG_IRQ(uvp_d_evt), | |
845 | MT6370_CHG_IRQ(mivr), | |
846 | }; | |
847 | ||
848 | for (i = 0; i < ARRAY_SIZE(mt6370_chg_irqs); i++) { | |
849 | ret = platform_get_irq_byname(to_platform_device(priv->dev), | |
850 | mt6370_chg_irqs[i].name); | |
851 | if (ret < 0) | |
852 | return dev_err_probe(priv->dev, ret, | |
853 | "Failed to get irq %s\n", | |
854 | mt6370_chg_irqs[i].name); | |
855 | ||
856 | priv->irq_nums[i] = ret; | |
857 | ret = devm_request_threaded_irq(priv->dev, ret, NULL, | |
858 | mt6370_chg_irqs[i].handler, | |
859 | IRQF_TRIGGER_FALLING, | |
860 | dev_name(priv->dev), priv); | |
861 | if (ret) | |
862 | return dev_err_probe(priv->dev, ret, | |
863 | "Failed to request irq %s\n", | |
864 | mt6370_chg_irqs[i].name); | |
865 | } | |
866 | ||
867 | return 0; | |
868 | } | |
869 | ||
870 | static int mt6370_chg_probe(struct platform_device *pdev) | |
871 | { | |
872 | struct device *dev = &pdev->dev; | |
873 | struct mt6370_priv *priv; | |
874 | int ret; | |
875 | ||
876 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
877 | if (!priv) | |
878 | return -ENOMEM; | |
879 | ||
880 | priv->dev = &pdev->dev; | |
881 | ||
882 | priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); | |
883 | if (!priv->regmap) | |
884 | return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n"); | |
885 | ||
886 | ret = mt6370_chg_init_rmap_fields(priv); | |
887 | if (ret) | |
888 | return dev_err_probe(dev, ret, "Failed to init regmap fields\n"); | |
889 | ||
890 | platform_set_drvdata(pdev, priv); | |
891 | ||
892 | priv->iio_adcs = devm_iio_channel_get_all(priv->dev); | |
893 | if (IS_ERR(priv->iio_adcs)) | |
894 | return dev_err_probe(dev, PTR_ERR(priv->iio_adcs), | |
895 | "Failed to get iio adc\n"); | |
896 | ||
897 | ret = mt6370_chg_init_otg_regulator(priv); | |
898 | if (ret) | |
899 | return dev_err_probe(dev, ret, "Failed to init OTG regulator\n"); | |
900 | ||
901 | ret = mt6370_chg_init_psy(priv); | |
902 | if (ret) | |
903 | return dev_err_probe(dev, ret, "Failed to init psy\n"); | |
904 | ||
905 | mutex_init(&priv->attach_lock); | |
906 | ret = devm_add_action_or_reset(dev, mt6370_chg_destroy_attach_lock, | |
907 | &priv->attach_lock); | |
908 | if (ret) | |
909 | return dev_err_probe(dev, ret, "Failed to init attach lock\n"); | |
910 | ||
911 | priv->attach = MT6370_ATTACH_STAT_DETACH; | |
912 | ||
913 | priv->wq = create_singlethread_workqueue(dev_name(priv->dev)); | |
5738d49f YY |
914 | if (!priv->wq) |
915 | return dev_err_probe(dev, -ENOMEM, | |
233cb8a4 CW |
916 | "Failed to create workqueue\n"); |
917 | ||
918 | ret = devm_add_action_or_reset(dev, mt6370_chg_destroy_wq, priv->wq); | |
919 | if (ret) | |
920 | return dev_err_probe(dev, ret, "Failed to init wq\n"); | |
921 | ||
922 | ret = devm_work_autocancel(dev, &priv->bc12_work, mt6370_chg_bc12_work_func); | |
923 | if (ret) | |
924 | return dev_err_probe(dev, ret, "Failed to init bc12 work\n"); | |
925 | ||
926 | ret = devm_delayed_work_autocancel(dev, &priv->mivr_dwork, mt6370_chg_mivr_dwork_func); | |
927 | if (ret) | |
928 | return dev_err_probe(dev, ret, "Failed to init mivr delayed work\n"); | |
929 | ||
930 | ret = mt6370_chg_init_setting(priv); | |
931 | if (ret) | |
932 | return dev_err_probe(dev, ret, | |
933 | "Failed to init mt6370 charger setting\n"); | |
934 | ||
935 | ret = mt6370_chg_init_irq(priv); | |
936 | if (ret) | |
937 | return ret; | |
938 | ||
939 | mt6370_chg_pwr_rdy_check(priv); | |
940 | ||
941 | return 0; | |
942 | } | |
943 | ||
944 | static const struct of_device_id mt6370_chg_of_match[] = { | |
945 | { .compatible = "mediatek,mt6370-charger", }, | |
946 | {} | |
947 | }; | |
948 | MODULE_DEVICE_TABLE(of, mt6370_chg_of_match); | |
949 | ||
950 | static struct platform_driver mt6370_chg_driver = { | |
951 | .probe = mt6370_chg_probe, | |
952 | .driver = { | |
953 | .name = "mt6370-charger", | |
954 | .of_match_table = mt6370_chg_of_match, | |
955 | }, | |
956 | }; | |
957 | module_platform_driver(mt6370_chg_driver); | |
958 | ||
959 | MODULE_AUTHOR("ChiaEn Wu <chiaen_wu@richtek.com>"); | |
960 | MODULE_DESCRIPTION("MediaTek MT6370 Charger Driver"); | |
961 | MODULE_LICENSE("GPL v2"); |