1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Driver for LP8727 Micro/Mini USB IC with integrated charger |
4 | * |
5 | * Copyright (C) 2011 Texas Instruments |
6 | * Copyright (C) 2011 National Semiconductor |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/i2c.h> |
13 | #include <linux/power_supply.h> |
14 | #include <linux/platform_data/lp8727.h> |
15 | #include <linux/of.h> |
16 | |
17 | #define LP8788_NUM_INTREGS 2 |
18 | #define DEFAULT_DEBOUNCE_MSEC 270 |
19 | |
20 | /* Registers */ |
21 | #define LP8727_CTRL1 0x1 |
22 | #define LP8727_CTRL2 0x2 |
23 | #define LP8727_SWCTRL 0x3 |
24 | #define LP8727_INT1 0x4 |
25 | #define LP8727_INT2 0x5 |
26 | #define LP8727_STATUS1 0x6 |
27 | #define LP8727_STATUS2 0x7 |
28 | #define LP8727_CHGCTRL2 0x9 |
29 | |
30 | /* CTRL1 register */ |
31 | #define LP8727_CP_EN BIT(0) |
32 | #define LP8727_ADC_EN BIT(1) |
33 | #define LP8727_ID200_EN BIT(4) |
34 | |
35 | /* CTRL2 register */ |
36 | #define LP8727_CHGDET_EN BIT(1) |
37 | #define LP8727_INT_EN BIT(6) |
38 | |
39 | /* SWCTRL register */ |
40 | #define LP8727_SW_DM1_DM (0x0 << 0) |
41 | #define LP8727_SW_DM1_HiZ (0x7 << 0) |
42 | #define LP8727_SW_DP2_DP (0x0 << 3) |
43 | #define LP8727_SW_DP2_HiZ (0x7 << 3) |
44 | |
45 | /* INT1 register */ |
46 | #define LP8727_IDNO (0xF << 0) |
47 | #define LP8727_VBUS BIT(4) |
48 | |
49 | /* STATUS1 register */ |
50 | #define LP8727_CHGSTAT (3 << 4) |
51 | #define LP8727_CHPORT BIT(6) |
52 | #define LP8727_DCPORT BIT(7) |
53 | #define LP8727_STAT_EOC 0x30 |
54 | |
55 | /* STATUS2 register */ |
56 | #define LP8727_TEMP_STAT (3 << 5) |
57 | #define LP8727_TEMP_SHIFT 5 |
58 | |
59 | /* CHGCTRL2 register */ |
60 | #define LP8727_ICHG_SHIFT 4 |
61 | |
62 | enum lp8727_dev_id { |
63 | LP8727_ID_NONE, |
64 | LP8727_ID_TA, |
65 | LP8727_ID_DEDICATED_CHG, |
66 | LP8727_ID_USB_CHG, |
67 | LP8727_ID_USB_DS, |
68 | LP8727_ID_MAX, |
69 | }; |
70 | |
71 | enum lp8727_die_temp { |
72 | LP8788_TEMP_75C, |
73 | LP8788_TEMP_95C, |
74 | LP8788_TEMP_115C, |
75 | LP8788_TEMP_135C, |
76 | }; |
77 | |
78 | struct lp8727_psy { |
79 | struct power_supply *ac; |
80 | struct power_supply *usb; |
81 | struct power_supply *batt; |
82 | }; |
83 | |
84 | struct lp8727_chg { |
85 | struct device *dev; |
86 | struct i2c_client *client; |
87 | struct mutex xfer_lock; |
88 | struct lp8727_psy *psy; |
89 | struct lp8727_platform_data *pdata; |
90 | |
91 | /* Charger Data */ |
92 | enum lp8727_dev_id devid; |
93 | struct lp8727_chg_param *chg_param; |
94 | |
95 | /* Interrupt Handling */ |
96 | int irq; |
97 | struct delayed_work work; |
98 | unsigned long debounce_jiffies; |
99 | }; |
100 | |
101 | static int lp8727_read_bytes(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len) |
102 | { |
103 | s32 ret; |
104 | |
105 | mutex_lock(&pchg->xfer_lock); |
106 | ret = i2c_smbus_read_i2c_block_data(client: pchg->client, command: reg, length: len, values: data); |
107 | mutex_unlock(lock: &pchg->xfer_lock); |
108 | |
109 | return (ret != len) ? -EIO : 0; |
110 | } |
111 | |
112 | static inline int lp8727_read_byte(struct lp8727_chg *pchg, u8 reg, u8 *data) |
113 | { |
114 | return lp8727_read_bytes(pchg, reg, data, len: 1); |
115 | } |
116 | |
117 | static int lp8727_write_byte(struct lp8727_chg *pchg, u8 reg, u8 data) |
118 | { |
119 | int ret; |
120 | |
121 | mutex_lock(&pchg->xfer_lock); |
122 | ret = i2c_smbus_write_byte_data(client: pchg->client, command: reg, value: data); |
123 | mutex_unlock(lock: &pchg->xfer_lock); |
124 | |
125 | return ret; |
126 | } |
127 | |
128 | static bool lp8727_is_charger_attached(const char *name, int id) |
129 | { |
130 | if (!strcmp(name, "ac" )) |
131 | return id == LP8727_ID_TA || id == LP8727_ID_DEDICATED_CHG; |
132 | else if (!strcmp(name, "usb" )) |
133 | return id == LP8727_ID_USB_CHG; |
134 | |
135 | return id >= LP8727_ID_TA && id <= LP8727_ID_USB_CHG; |
136 | } |
137 | |
138 | static int lp8727_init_device(struct lp8727_chg *pchg) |
139 | { |
140 | u8 val; |
141 | int ret; |
142 | u8 intstat[LP8788_NUM_INTREGS]; |
143 | |
144 | /* clear interrupts */ |
145 | ret = lp8727_read_bytes(pchg, LP8727_INT1, data: intstat, LP8788_NUM_INTREGS); |
146 | if (ret) |
147 | return ret; |
148 | |
149 | val = LP8727_ID200_EN | LP8727_ADC_EN | LP8727_CP_EN; |
150 | ret = lp8727_write_byte(pchg, LP8727_CTRL1, data: val); |
151 | if (ret) |
152 | return ret; |
153 | |
154 | val = LP8727_INT_EN | LP8727_CHGDET_EN; |
155 | return lp8727_write_byte(pchg, LP8727_CTRL2, data: val); |
156 | } |
157 | |
158 | static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg) |
159 | { |
160 | u8 val; |
161 | |
162 | lp8727_read_byte(pchg, LP8727_STATUS1, data: &val); |
163 | return val & LP8727_DCPORT; |
164 | } |
165 | |
166 | static int lp8727_is_usb_charger(struct lp8727_chg *pchg) |
167 | { |
168 | u8 val; |
169 | |
170 | lp8727_read_byte(pchg, LP8727_STATUS1, data: &val); |
171 | return val & LP8727_CHPORT; |
172 | } |
173 | |
174 | static inline void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw) |
175 | { |
176 | lp8727_write_byte(pchg, LP8727_SWCTRL, data: sw); |
177 | } |
178 | |
179 | static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin) |
180 | { |
181 | struct lp8727_platform_data *pdata = pchg->pdata; |
182 | u8 devid = LP8727_ID_NONE; |
183 | u8 swctrl = LP8727_SW_DM1_HiZ | LP8727_SW_DP2_HiZ; |
184 | |
185 | switch (id) { |
186 | case 0x5: |
187 | devid = LP8727_ID_TA; |
188 | pchg->chg_param = pdata ? pdata->ac : NULL; |
189 | break; |
190 | case 0xB: |
191 | if (lp8727_is_dedicated_charger(pchg)) { |
192 | pchg->chg_param = pdata ? pdata->ac : NULL; |
193 | devid = LP8727_ID_DEDICATED_CHG; |
194 | } else if (lp8727_is_usb_charger(pchg)) { |
195 | pchg->chg_param = pdata ? pdata->usb : NULL; |
196 | devid = LP8727_ID_USB_CHG; |
197 | swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP; |
198 | } else if (vbusin) { |
199 | devid = LP8727_ID_USB_DS; |
200 | swctrl = LP8727_SW_DM1_DM | LP8727_SW_DP2_DP; |
201 | } |
202 | break; |
203 | default: |
204 | devid = LP8727_ID_NONE; |
205 | pchg->chg_param = NULL; |
206 | break; |
207 | } |
208 | |
209 | pchg->devid = devid; |
210 | lp8727_ctrl_switch(pchg, sw: swctrl); |
211 | } |
212 | |
213 | static void lp8727_enable_chgdet(struct lp8727_chg *pchg) |
214 | { |
215 | u8 val; |
216 | |
217 | lp8727_read_byte(pchg, LP8727_CTRL2, data: &val); |
218 | val |= LP8727_CHGDET_EN; |
219 | lp8727_write_byte(pchg, LP8727_CTRL2, data: val); |
220 | } |
221 | |
222 | static void lp8727_delayed_func(struct work_struct *_work) |
223 | { |
224 | struct lp8727_chg *pchg = container_of(_work, struct lp8727_chg, |
225 | work.work); |
226 | u8 intstat[LP8788_NUM_INTREGS]; |
227 | u8 idno; |
228 | u8 vbus; |
229 | |
230 | if (lp8727_read_bytes(pchg, LP8727_INT1, data: intstat, LP8788_NUM_INTREGS)) { |
231 | dev_err(pchg->dev, "can not read INT registers\n" ); |
232 | return; |
233 | } |
234 | |
235 | idno = intstat[0] & LP8727_IDNO; |
236 | vbus = intstat[0] & LP8727_VBUS; |
237 | |
238 | lp8727_id_detection(pchg, id: idno, vbusin: vbus); |
239 | lp8727_enable_chgdet(pchg); |
240 | |
241 | power_supply_changed(psy: pchg->psy->ac); |
242 | power_supply_changed(psy: pchg->psy->usb); |
243 | power_supply_changed(psy: pchg->psy->batt); |
244 | } |
245 | |
246 | static irqreturn_t lp8727_isr_func(int irq, void *ptr) |
247 | { |
248 | struct lp8727_chg *pchg = ptr; |
249 | |
250 | schedule_delayed_work(dwork: &pchg->work, delay: pchg->debounce_jiffies); |
251 | return IRQ_HANDLED; |
252 | } |
253 | |
254 | static int lp8727_setup_irq(struct lp8727_chg *pchg) |
255 | { |
256 | int ret; |
257 | int irq = pchg->client->irq; |
258 | unsigned delay_msec = pchg->pdata ? pchg->pdata->debounce_msec : |
259 | DEFAULT_DEBOUNCE_MSEC; |
260 | |
261 | INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func); |
262 | |
263 | if (irq <= 0) { |
264 | dev_warn(pchg->dev, "invalid irq number: %d\n" , irq); |
265 | return 0; |
266 | } |
267 | |
268 | ret = request_threaded_irq(irq, NULL, thread_fn: lp8727_isr_func, |
269 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
270 | name: "lp8727_irq" , dev: pchg); |
271 | |
272 | if (ret) |
273 | return ret; |
274 | |
275 | pchg->irq = irq; |
276 | pchg->debounce_jiffies = msecs_to_jiffies(m: delay_msec); |
277 | |
278 | return 0; |
279 | } |
280 | |
281 | static void lp8727_release_irq(struct lp8727_chg *pchg) |
282 | { |
283 | cancel_delayed_work_sync(dwork: &pchg->work); |
284 | |
285 | if (pchg->irq) |
286 | free_irq(pchg->irq, pchg); |
287 | } |
288 | |
289 | static enum power_supply_property lp8727_charger_prop[] = { |
290 | POWER_SUPPLY_PROP_ONLINE, |
291 | }; |
292 | |
293 | static enum power_supply_property lp8727_battery_prop[] = { |
294 | POWER_SUPPLY_PROP_STATUS, |
295 | POWER_SUPPLY_PROP_HEALTH, |
296 | POWER_SUPPLY_PROP_PRESENT, |
297 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
298 | POWER_SUPPLY_PROP_CAPACITY, |
299 | POWER_SUPPLY_PROP_TEMP, |
300 | }; |
301 | |
302 | static char *battery_supplied_to[] = { |
303 | "main_batt" , |
304 | }; |
305 | |
306 | static int lp8727_charger_get_property(struct power_supply *psy, |
307 | enum power_supply_property psp, |
308 | union power_supply_propval *val) |
309 | { |
310 | struct lp8727_chg *pchg = dev_get_drvdata(dev: psy->dev.parent); |
311 | |
312 | if (psp != POWER_SUPPLY_PROP_ONLINE) |
313 | return -EINVAL; |
314 | |
315 | val->intval = lp8727_is_charger_attached(name: psy->desc->name, id: pchg->devid); |
316 | |
317 | return 0; |
318 | } |
319 | |
320 | static bool lp8727_is_high_temperature(enum lp8727_die_temp temp) |
321 | { |
322 | switch (temp) { |
323 | case LP8788_TEMP_95C: |
324 | case LP8788_TEMP_115C: |
325 | case LP8788_TEMP_135C: |
326 | return true; |
327 | default: |
328 | return false; |
329 | } |
330 | } |
331 | |
332 | static int lp8727_battery_get_property(struct power_supply *psy, |
333 | enum power_supply_property psp, |
334 | union power_supply_propval *val) |
335 | { |
336 | struct lp8727_chg *pchg = dev_get_drvdata(dev: psy->dev.parent); |
337 | struct lp8727_platform_data *pdata = pchg->pdata; |
338 | enum lp8727_die_temp temp; |
339 | u8 read; |
340 | |
341 | switch (psp) { |
342 | case POWER_SUPPLY_PROP_STATUS: |
343 | if (!lp8727_is_charger_attached(name: psy->desc->name, id: pchg->devid)) { |
344 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
345 | return 0; |
346 | } |
347 | |
348 | lp8727_read_byte(pchg, LP8727_STATUS1, data: &read); |
349 | |
350 | val->intval = (read & LP8727_CHGSTAT) == LP8727_STAT_EOC ? |
351 | POWER_SUPPLY_STATUS_FULL : |
352 | POWER_SUPPLY_STATUS_CHARGING; |
353 | break; |
354 | case POWER_SUPPLY_PROP_HEALTH: |
355 | lp8727_read_byte(pchg, LP8727_STATUS2, data: &read); |
356 | temp = (read & LP8727_TEMP_STAT) >> LP8727_TEMP_SHIFT; |
357 | |
358 | val->intval = lp8727_is_high_temperature(temp) ? |
359 | POWER_SUPPLY_HEALTH_OVERHEAT : |
360 | POWER_SUPPLY_HEALTH_GOOD; |
361 | break; |
362 | case POWER_SUPPLY_PROP_PRESENT: |
363 | if (!pdata) |
364 | return -EINVAL; |
365 | |
366 | if (pdata->get_batt_present) |
367 | val->intval = pdata->get_batt_present(); |
368 | break; |
369 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
370 | if (!pdata) |
371 | return -EINVAL; |
372 | |
373 | if (pdata->get_batt_level) |
374 | val->intval = pdata->get_batt_level(); |
375 | break; |
376 | case POWER_SUPPLY_PROP_CAPACITY: |
377 | if (!pdata) |
378 | return -EINVAL; |
379 | |
380 | if (pdata->get_batt_capacity) |
381 | val->intval = pdata->get_batt_capacity(); |
382 | break; |
383 | case POWER_SUPPLY_PROP_TEMP: |
384 | if (!pdata) |
385 | return -EINVAL; |
386 | |
387 | if (pdata->get_batt_temp) |
388 | val->intval = pdata->get_batt_temp(); |
389 | break; |
390 | default: |
391 | break; |
392 | } |
393 | |
394 | return 0; |
395 | } |
396 | |
397 | static void lp8727_charger_changed(struct power_supply *psy) |
398 | { |
399 | struct lp8727_chg *pchg = dev_get_drvdata(dev: psy->dev.parent); |
400 | u8 eoc_level; |
401 | u8 ichg; |
402 | u8 val; |
403 | |
404 | /* skip if no charger exists */ |
405 | if (!lp8727_is_charger_attached(name: psy->desc->name, id: pchg->devid)) |
406 | return; |
407 | |
408 | /* update charging parameters */ |
409 | if (pchg->chg_param) { |
410 | eoc_level = pchg->chg_param->eoc_level; |
411 | ichg = pchg->chg_param->ichg; |
412 | val = (ichg << LP8727_ICHG_SHIFT) | eoc_level; |
413 | lp8727_write_byte(pchg, LP8727_CHGCTRL2, data: val); |
414 | } |
415 | } |
416 | |
417 | static const struct power_supply_desc lp8727_ac_desc = { |
418 | .name = "ac" , |
419 | .type = POWER_SUPPLY_TYPE_MAINS, |
420 | .properties = lp8727_charger_prop, |
421 | .num_properties = ARRAY_SIZE(lp8727_charger_prop), |
422 | .get_property = lp8727_charger_get_property, |
423 | }; |
424 | |
425 | static const struct power_supply_desc lp8727_usb_desc = { |
426 | .name = "usb" , |
427 | .type = POWER_SUPPLY_TYPE_USB, |
428 | .properties = lp8727_charger_prop, |
429 | .num_properties = ARRAY_SIZE(lp8727_charger_prop), |
430 | .get_property = lp8727_charger_get_property, |
431 | }; |
432 | |
433 | static const struct power_supply_desc lp8727_batt_desc = { |
434 | .name = "main_batt" , |
435 | .type = POWER_SUPPLY_TYPE_BATTERY, |
436 | .properties = lp8727_battery_prop, |
437 | .num_properties = ARRAY_SIZE(lp8727_battery_prop), |
438 | .get_property = lp8727_battery_get_property, |
439 | .external_power_changed = lp8727_charger_changed, |
440 | }; |
441 | |
442 | static int lp8727_register_psy(struct lp8727_chg *pchg) |
443 | { |
444 | struct power_supply_config psy_cfg = {}; /* Only for ac and usb */ |
445 | struct lp8727_psy *psy; |
446 | |
447 | psy = devm_kzalloc(dev: pchg->dev, size: sizeof(*psy), GFP_KERNEL); |
448 | if (!psy) |
449 | return -ENOMEM; |
450 | |
451 | pchg->psy = psy; |
452 | |
453 | psy_cfg.supplied_to = battery_supplied_to; |
454 | psy_cfg.num_supplicants = ARRAY_SIZE(battery_supplied_to); |
455 | |
456 | psy->ac = power_supply_register(parent: pchg->dev, desc: &lp8727_ac_desc, cfg: &psy_cfg); |
457 | if (IS_ERR(ptr: psy->ac)) |
458 | goto err_psy_ac; |
459 | |
460 | psy->usb = power_supply_register(parent: pchg->dev, desc: &lp8727_usb_desc, |
461 | cfg: &psy_cfg); |
462 | if (IS_ERR(ptr: psy->usb)) |
463 | goto err_psy_usb; |
464 | |
465 | psy->batt = power_supply_register(parent: pchg->dev, desc: &lp8727_batt_desc, NULL); |
466 | if (IS_ERR(ptr: psy->batt)) |
467 | goto err_psy_batt; |
468 | |
469 | return 0; |
470 | |
471 | err_psy_batt: |
472 | power_supply_unregister(psy: psy->usb); |
473 | err_psy_usb: |
474 | power_supply_unregister(psy: psy->ac); |
475 | err_psy_ac: |
476 | return -EPERM; |
477 | } |
478 | |
479 | static void lp8727_unregister_psy(struct lp8727_chg *pchg) |
480 | { |
481 | struct lp8727_psy *psy = pchg->psy; |
482 | |
483 | if (!psy) |
484 | return; |
485 | |
486 | power_supply_unregister(psy: psy->ac); |
487 | power_supply_unregister(psy: psy->usb); |
488 | power_supply_unregister(psy: psy->batt); |
489 | } |
490 | |
491 | #ifdef CONFIG_OF |
492 | static struct lp8727_chg_param |
493 | *lp8727_parse_charge_pdata(struct device *dev, struct device_node *np) |
494 | { |
495 | struct lp8727_chg_param *param; |
496 | |
497 | param = devm_kzalloc(dev, size: sizeof(*param), GFP_KERNEL); |
498 | if (!param) |
499 | goto out; |
500 | |
501 | of_property_read_u8(np, propname: "eoc-level" , out_value: (u8 *)¶m->eoc_level); |
502 | of_property_read_u8(np, propname: "charging-current" , out_value: (u8 *)¶m->ichg); |
503 | out: |
504 | return param; |
505 | } |
506 | |
507 | static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev) |
508 | { |
509 | struct device_node *np = dev->of_node; |
510 | struct device_node *child; |
511 | struct lp8727_platform_data *pdata; |
512 | const char *type; |
513 | |
514 | pdata = devm_kzalloc(dev, size: sizeof(*pdata), GFP_KERNEL); |
515 | if (!pdata) |
516 | return ERR_PTR(error: -ENOMEM); |
517 | |
518 | of_property_read_u32(np, propname: "debounce-ms" , out_value: &pdata->debounce_msec); |
519 | |
520 | /* If charging parameter is not defined, just skip parsing the dt */ |
521 | if (of_get_child_count(np) == 0) |
522 | return pdata; |
523 | |
524 | for_each_child_of_node(np, child) { |
525 | of_property_read_string(np: child, propname: "charger-type" , out_string: &type); |
526 | |
527 | if (!strcmp(type, "ac" )) |
528 | pdata->ac = lp8727_parse_charge_pdata(dev, np: child); |
529 | |
530 | if (!strcmp(type, "usb" )) |
531 | pdata->usb = lp8727_parse_charge_pdata(dev, np: child); |
532 | } |
533 | |
534 | return pdata; |
535 | } |
536 | #else |
537 | static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev) |
538 | { |
539 | return NULL; |
540 | } |
541 | #endif |
542 | |
543 | static int lp8727_probe(struct i2c_client *cl) |
544 | { |
545 | struct lp8727_chg *pchg; |
546 | struct lp8727_platform_data *pdata; |
547 | int ret; |
548 | |
549 | if (!i2c_check_functionality(adap: cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) |
550 | return -EIO; |
551 | |
552 | if (cl->dev.of_node) { |
553 | pdata = lp8727_parse_dt(dev: &cl->dev); |
554 | if (IS_ERR(ptr: pdata)) |
555 | return PTR_ERR(ptr: pdata); |
556 | } else { |
557 | pdata = dev_get_platdata(dev: &cl->dev); |
558 | } |
559 | |
560 | pchg = devm_kzalloc(dev: &cl->dev, size: sizeof(*pchg), GFP_KERNEL); |
561 | if (!pchg) |
562 | return -ENOMEM; |
563 | |
564 | pchg->client = cl; |
565 | pchg->dev = &cl->dev; |
566 | pchg->pdata = pdata; |
567 | i2c_set_clientdata(client: cl, data: pchg); |
568 | |
569 | mutex_init(&pchg->xfer_lock); |
570 | |
571 | ret = lp8727_init_device(pchg); |
572 | if (ret) { |
573 | dev_err(pchg->dev, "i2c communication err: %d" , ret); |
574 | return ret; |
575 | } |
576 | |
577 | ret = lp8727_register_psy(pchg); |
578 | if (ret) { |
579 | dev_err(pchg->dev, "power supplies register err: %d" , ret); |
580 | return ret; |
581 | } |
582 | |
583 | ret = lp8727_setup_irq(pchg); |
584 | if (ret) { |
585 | dev_err(pchg->dev, "irq handler err: %d" , ret); |
586 | lp8727_unregister_psy(pchg); |
587 | return ret; |
588 | } |
589 | |
590 | return 0; |
591 | } |
592 | |
593 | static void lp8727_remove(struct i2c_client *cl) |
594 | { |
595 | struct lp8727_chg *pchg = i2c_get_clientdata(client: cl); |
596 | |
597 | lp8727_release_irq(pchg); |
598 | lp8727_unregister_psy(pchg); |
599 | } |
600 | |
601 | static const struct of_device_id lp8727_dt_ids[] __maybe_unused = { |
602 | { .compatible = "ti,lp8727" , }, |
603 | { } |
604 | }; |
605 | MODULE_DEVICE_TABLE(of, lp8727_dt_ids); |
606 | |
607 | static const struct i2c_device_id lp8727_ids[] = { |
608 | {"lp8727" , 0}, |
609 | { } |
610 | }; |
611 | MODULE_DEVICE_TABLE(i2c, lp8727_ids); |
612 | |
613 | static struct i2c_driver lp8727_driver = { |
614 | .driver = { |
615 | .name = "lp8727" , |
616 | .of_match_table = of_match_ptr(lp8727_dt_ids), |
617 | }, |
618 | .probe = lp8727_probe, |
619 | .remove = lp8727_remove, |
620 | .id_table = lp8727_ids, |
621 | }; |
622 | module_i2c_driver(lp8727_driver); |
623 | |
624 | MODULE_DESCRIPTION("TI/National Semiconductor LP8727 charger driver" ); |
625 | MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>" ); |
626 | MODULE_LICENSE("GPL" ); |
627 | |