Commit | Line | Data |
---|---|---|
715ecbc1 LC |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * max77976_charger.c - Driver for the Maxim MAX77976 battery charger | |
4 | * | |
5 | * Copyright (C) 2021 Luca Ceresoli | |
a6c487cd | 6 | * Author: Luca Ceresoli <luca.ceresoli@bootlin.com> |
715ecbc1 LC |
7 | */ |
8 | ||
9 | #include <linux/i2c.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/power_supply.h> | |
12 | #include <linux/regmap.h> | |
13 | ||
14 | #define MAX77976_DRIVER_NAME "max77976-charger" | |
15 | #define MAX77976_CHIP_ID 0x76 | |
16 | ||
17 | static const char *max77976_manufacturer = "Maxim Integrated"; | |
18 | static const char *max77976_model = "MAX77976"; | |
19 | ||
20 | /* -------------------------------------------------------------------------- | |
21 | * Register map | |
22 | */ | |
23 | ||
24 | #define MAX77976_REG_CHIP_ID 0x00 | |
25 | #define MAX77976_REG_CHIP_REVISION 0x01 | |
26 | #define MAX77976_REG_CHG_INT_OK 0x12 | |
27 | #define MAX77976_REG_CHG_DETAILS_01 0x14 | |
28 | #define MAX77976_REG_CHG_CNFG_00 0x16 | |
29 | #define MAX77976_REG_CHG_CNFG_02 0x18 | |
30 | #define MAX77976_REG_CHG_CNFG_06 0x1c | |
31 | #define MAX77976_REG_CHG_CNFG_09 0x1f | |
32 | ||
33 | /* CHG_DETAILS_01.CHG_DTLS values */ | |
34 | enum max77976_charging_state { | |
35 | MAX77976_CHARGING_PREQUALIFICATION = 0x0, | |
36 | MAX77976_CHARGING_FAST_CONST_CURRENT, | |
37 | MAX77976_CHARGING_FAST_CONST_VOLTAGE, | |
38 | MAX77976_CHARGING_TOP_OFF, | |
39 | MAX77976_CHARGING_DONE, | |
40 | MAX77976_CHARGING_RESERVED_05, | |
41 | MAX77976_CHARGING_TIMER_FAULT, | |
42 | MAX77976_CHARGING_SUSPENDED_QBATT_OFF, | |
43 | MAX77976_CHARGING_OFF, | |
44 | MAX77976_CHARGING_RESERVED_09, | |
45 | MAX77976_CHARGING_THERMAL_SHUTDOWN, | |
46 | MAX77976_CHARGING_WATCHDOG_EXPIRED, | |
47 | MAX77976_CHARGING_SUSPENDED_JEITA, | |
48 | MAX77976_CHARGING_SUSPENDED_THM_REMOVAL, | |
49 | MAX77976_CHARGING_SUSPENDED_PIN, | |
50 | MAX77976_CHARGING_RESERVED_0F, | |
51 | }; | |
52 | ||
53 | /* CHG_DETAILS_01.BAT_DTLS values */ | |
54 | enum max77976_battery_state { | |
55 | MAX77976_BATTERY_BATTERY_REMOVAL = 0x0, | |
56 | MAX77976_BATTERY_PREQUALIFICATION, | |
57 | MAX77976_BATTERY_TIMER_FAULT, | |
58 | MAX77976_BATTERY_REGULAR_VOLTAGE, | |
59 | MAX77976_BATTERY_LOW_VOLTAGE, | |
60 | MAX77976_BATTERY_OVERVOLTAGE, | |
61 | MAX77976_BATTERY_RESERVED, | |
62 | MAX77976_BATTERY_BATTERY_ONLY, // No valid adapter is present | |
63 | }; | |
64 | ||
65 | /* CHG_CNFG_00.MODE values */ | |
66 | enum max77976_mode { | |
67 | MAX77976_MODE_CHARGER_BUCK = 0x5, | |
68 | MAX77976_MODE_BOOST = 0x9, | |
69 | }; | |
70 | ||
71 | /* CHG_CNFG_02.CHG_CC: charge current limit, 100..5500 mA, 50 mA steps */ | |
72 | #define MAX77976_CHG_CC_STEP 50000U | |
73 | #define MAX77976_CHG_CC_MIN 100000U | |
74 | #define MAX77976_CHG_CC_MAX 5500000U | |
75 | ||
76 | /* CHG_CNFG_09.CHGIN_ILIM: input current limit, 100..3200 mA, 100 mA steps */ | |
77 | #define MAX77976_CHGIN_ILIM_STEP 100000U | |
78 | #define MAX77976_CHGIN_ILIM_MIN 100000U | |
79 | #define MAX77976_CHGIN_ILIM_MAX 3200000U | |
80 | ||
81 | enum max77976_field_idx { | |
82 | VERSION, REVISION, /* CHIP_REVISION */ | |
83 | CHGIN_OK, /* CHG_INT_OK */ | |
84 | BAT_DTLS, CHG_DTLS, /* CHG_DETAILS_01 */ | |
85 | MODE, /* CHG_CNFG_00 */ | |
86 | CHG_CC, /* CHG_CNFG_02 */ | |
87 | CHGPROT, /* CHG_CNFG_06 */ | |
88 | CHGIN_ILIM, /* CHG_CNFG_09 */ | |
89 | MAX77976_N_REGMAP_FIELDS | |
90 | }; | |
91 | ||
92 | static const struct reg_field max77976_reg_field[MAX77976_N_REGMAP_FIELDS] = { | |
93 | [VERSION] = REG_FIELD(MAX77976_REG_CHIP_REVISION, 4, 7), | |
94 | [REVISION] = REG_FIELD(MAX77976_REG_CHIP_REVISION, 0, 3), | |
95 | [CHGIN_OK] = REG_FIELD(MAX77976_REG_CHG_INT_OK, 6, 6), | |
96 | [CHG_DTLS] = REG_FIELD(MAX77976_REG_CHG_DETAILS_01, 0, 3), | |
97 | [BAT_DTLS] = REG_FIELD(MAX77976_REG_CHG_DETAILS_01, 4, 6), | |
98 | [MODE] = REG_FIELD(MAX77976_REG_CHG_CNFG_00, 0, 3), | |
99 | [CHG_CC] = REG_FIELD(MAX77976_REG_CHG_CNFG_02, 0, 6), | |
100 | [CHGPROT] = REG_FIELD(MAX77976_REG_CHG_CNFG_06, 2, 3), | |
101 | [CHGIN_ILIM] = REG_FIELD(MAX77976_REG_CHG_CNFG_09, 0, 5), | |
102 | }; | |
103 | ||
104 | static const struct regmap_config max77976_regmap_config = { | |
105 | .reg_bits = 8, | |
106 | .val_bits = 8, | |
107 | .max_register = 0x24, | |
108 | }; | |
109 | ||
110 | /* -------------------------------------------------------------------------- | |
111 | * Data structures | |
112 | */ | |
113 | ||
114 | struct max77976 { | |
115 | struct i2c_client *client; | |
116 | struct regmap *regmap; | |
117 | struct regmap_field *rfield[MAX77976_N_REGMAP_FIELDS]; | |
118 | }; | |
119 | ||
120 | /* -------------------------------------------------------------------------- | |
121 | * power_supply properties | |
122 | */ | |
123 | ||
124 | static int max77976_get_status(struct max77976 *chg, int *val) | |
125 | { | |
126 | unsigned int regval; | |
127 | int err; | |
128 | ||
129 | err = regmap_field_read(chg->rfield[CHG_DTLS], ®val); | |
130 | if (err < 0) | |
131 | return err; | |
132 | ||
133 | switch (regval) { | |
134 | case MAX77976_CHARGING_PREQUALIFICATION: | |
135 | case MAX77976_CHARGING_FAST_CONST_CURRENT: | |
136 | case MAX77976_CHARGING_FAST_CONST_VOLTAGE: | |
137 | case MAX77976_CHARGING_TOP_OFF: | |
138 | *val = POWER_SUPPLY_STATUS_CHARGING; | |
139 | break; | |
140 | case MAX77976_CHARGING_DONE: | |
141 | *val = POWER_SUPPLY_STATUS_FULL; | |
142 | break; | |
143 | case MAX77976_CHARGING_TIMER_FAULT: | |
144 | case MAX77976_CHARGING_SUSPENDED_QBATT_OFF: | |
145 | case MAX77976_CHARGING_SUSPENDED_JEITA: | |
146 | case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL: | |
147 | case MAX77976_CHARGING_SUSPENDED_PIN: | |
148 | *val = POWER_SUPPLY_STATUS_NOT_CHARGING; | |
149 | break; | |
150 | case MAX77976_CHARGING_OFF: | |
151 | case MAX77976_CHARGING_THERMAL_SHUTDOWN: | |
152 | case MAX77976_CHARGING_WATCHDOG_EXPIRED: | |
153 | *val = POWER_SUPPLY_STATUS_DISCHARGING; | |
154 | break; | |
155 | default: | |
156 | *val = POWER_SUPPLY_STATUS_UNKNOWN; | |
157 | } | |
158 | ||
159 | return 0; | |
160 | } | |
161 | ||
162 | static int max77976_get_charge_type(struct max77976 *chg, int *val) | |
163 | { | |
164 | unsigned int regval; | |
165 | int err; | |
166 | ||
167 | err = regmap_field_read(chg->rfield[CHG_DTLS], ®val); | |
168 | if (err < 0) | |
169 | return err; | |
170 | ||
171 | switch (regval) { | |
172 | case MAX77976_CHARGING_PREQUALIFICATION: | |
173 | *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; | |
174 | break; | |
175 | case MAX77976_CHARGING_FAST_CONST_CURRENT: | |
176 | case MAX77976_CHARGING_FAST_CONST_VOLTAGE: | |
177 | *val = POWER_SUPPLY_CHARGE_TYPE_FAST; | |
178 | break; | |
179 | case MAX77976_CHARGING_TOP_OFF: | |
180 | *val = POWER_SUPPLY_CHARGE_TYPE_STANDARD; | |
181 | break; | |
182 | case MAX77976_CHARGING_DONE: | |
183 | case MAX77976_CHARGING_TIMER_FAULT: | |
184 | case MAX77976_CHARGING_SUSPENDED_QBATT_OFF: | |
185 | case MAX77976_CHARGING_OFF: | |
186 | case MAX77976_CHARGING_THERMAL_SHUTDOWN: | |
187 | case MAX77976_CHARGING_WATCHDOG_EXPIRED: | |
188 | case MAX77976_CHARGING_SUSPENDED_JEITA: | |
189 | case MAX77976_CHARGING_SUSPENDED_THM_REMOVAL: | |
190 | case MAX77976_CHARGING_SUSPENDED_PIN: | |
191 | *val = POWER_SUPPLY_CHARGE_TYPE_NONE; | |
192 | break; | |
193 | default: | |
194 | *val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; | |
195 | } | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
200 | static int max77976_get_health(struct max77976 *chg, int *val) | |
201 | { | |
202 | unsigned int regval; | |
203 | int err; | |
204 | ||
205 | err = regmap_field_read(chg->rfield[BAT_DTLS], ®val); | |
206 | if (err < 0) | |
207 | return err; | |
208 | ||
209 | switch (regval) { | |
210 | case MAX77976_BATTERY_BATTERY_REMOVAL: | |
211 | *val = POWER_SUPPLY_HEALTH_NO_BATTERY; | |
212 | break; | |
213 | case MAX77976_BATTERY_LOW_VOLTAGE: | |
214 | case MAX77976_BATTERY_REGULAR_VOLTAGE: | |
215 | *val = POWER_SUPPLY_HEALTH_GOOD; | |
216 | break; | |
217 | case MAX77976_BATTERY_TIMER_FAULT: | |
218 | *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; | |
219 | break; | |
220 | case MAX77976_BATTERY_OVERVOLTAGE: | |
221 | *val = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | |
222 | break; | |
223 | case MAX77976_BATTERY_PREQUALIFICATION: | |
224 | case MAX77976_BATTERY_BATTERY_ONLY: | |
225 | *val = POWER_SUPPLY_HEALTH_UNKNOWN; | |
226 | break; | |
227 | default: | |
228 | *val = POWER_SUPPLY_HEALTH_UNKNOWN; | |
229 | } | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | static int max77976_get_online(struct max77976 *chg, int *val) | |
235 | { | |
236 | unsigned int regval; | |
237 | int err; | |
238 | ||
239 | err = regmap_field_read(chg->rfield[CHGIN_OK], ®val); | |
240 | if (err < 0) | |
241 | return err; | |
242 | ||
243 | *val = (regval ? 1 : 0); | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
248 | static int max77976_get_integer(struct max77976 *chg, enum max77976_field_idx fidx, | |
249 | unsigned int clamp_min, unsigned int clamp_max, | |
250 | unsigned int mult, int *val) | |
251 | { | |
252 | unsigned int regval; | |
253 | int err; | |
254 | ||
255 | err = regmap_field_read(chg->rfield[fidx], ®val); | |
256 | if (err < 0) | |
257 | return err; | |
258 | ||
259 | *val = clamp_val(regval * mult, clamp_min, clamp_max); | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
264 | static int max77976_set_integer(struct max77976 *chg, enum max77976_field_idx fidx, | |
265 | unsigned int clamp_min, unsigned int clamp_max, | |
266 | unsigned int div, int val) | |
267 | { | |
268 | unsigned int regval; | |
269 | ||
270 | regval = clamp_val(val, clamp_min, clamp_max) / div; | |
271 | ||
272 | return regmap_field_write(chg->rfield[fidx], regval); | |
273 | } | |
274 | ||
275 | static int max77976_get_property(struct power_supply *psy, | |
276 | enum power_supply_property psp, | |
277 | union power_supply_propval *val) | |
278 | { | |
279 | struct max77976 *chg = power_supply_get_drvdata(psy); | |
280 | int err = 0; | |
281 | ||
282 | switch (psp) { | |
283 | case POWER_SUPPLY_PROP_STATUS: | |
284 | err = max77976_get_status(chg, &val->intval); | |
285 | break; | |
286 | case POWER_SUPPLY_PROP_CHARGE_TYPE: | |
287 | err = max77976_get_charge_type(chg, &val->intval); | |
288 | break; | |
289 | case POWER_SUPPLY_PROP_HEALTH: | |
290 | err = max77976_get_health(chg, &val->intval); | |
291 | break; | |
292 | case POWER_SUPPLY_PROP_ONLINE: | |
293 | err = max77976_get_online(chg, &val->intval); | |
294 | break; | |
295 | case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: | |
296 | val->intval = MAX77976_CHG_CC_MAX; | |
297 | break; | |
298 | case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: | |
299 | err = max77976_get_integer(chg, CHG_CC, | |
300 | MAX77976_CHG_CC_MIN, | |
301 | MAX77976_CHG_CC_MAX, | |
302 | MAX77976_CHG_CC_STEP, | |
303 | &val->intval); | |
304 | break; | |
305 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: | |
306 | err = max77976_get_integer(chg, CHGIN_ILIM, | |
307 | MAX77976_CHGIN_ILIM_MIN, | |
308 | MAX77976_CHGIN_ILIM_MAX, | |
309 | MAX77976_CHGIN_ILIM_STEP, | |
310 | &val->intval); | |
311 | break; | |
312 | case POWER_SUPPLY_PROP_MODEL_NAME: | |
313 | val->strval = max77976_model; | |
314 | break; | |
315 | case POWER_SUPPLY_PROP_MANUFACTURER: | |
316 | val->strval = max77976_manufacturer; | |
317 | break; | |
318 | default: | |
319 | err = -EINVAL; | |
320 | } | |
321 | ||
322 | return err; | |
323 | } | |
324 | ||
325 | static int max77976_set_property(struct power_supply *psy, | |
326 | enum power_supply_property psp, | |
327 | const union power_supply_propval *val) | |
328 | { | |
329 | struct max77976 *chg = power_supply_get_drvdata(psy); | |
330 | int err = 0; | |
331 | ||
332 | switch (psp) { | |
333 | case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: | |
334 | err = max77976_set_integer(chg, CHG_CC, | |
335 | MAX77976_CHG_CC_MIN, | |
336 | MAX77976_CHG_CC_MAX, | |
337 | MAX77976_CHG_CC_STEP, | |
338 | val->intval); | |
339 | break; | |
340 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: | |
341 | err = max77976_set_integer(chg, CHGIN_ILIM, | |
342 | MAX77976_CHGIN_ILIM_MIN, | |
343 | MAX77976_CHGIN_ILIM_MAX, | |
344 | MAX77976_CHGIN_ILIM_STEP, | |
345 | val->intval); | |
346 | break; | |
347 | default: | |
348 | err = -EINVAL; | |
349 | } | |
350 | ||
351 | return err; | |
352 | }; | |
353 | ||
354 | static int max77976_property_is_writeable(struct power_supply *psy, | |
355 | enum power_supply_property psp) | |
356 | { | |
357 | switch (psp) { | |
358 | case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: | |
359 | case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: | |
360 | return true; | |
361 | default: | |
362 | return false; | |
363 | } | |
364 | } | |
365 | ||
366 | static enum power_supply_property max77976_psy_props[] = { | |
367 | POWER_SUPPLY_PROP_STATUS, | |
368 | POWER_SUPPLY_PROP_CHARGE_TYPE, | |
369 | POWER_SUPPLY_PROP_HEALTH, | |
370 | POWER_SUPPLY_PROP_ONLINE, | |
371 | POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, | |
372 | POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, | |
373 | POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, | |
374 | POWER_SUPPLY_PROP_MODEL_NAME, | |
375 | POWER_SUPPLY_PROP_MANUFACTURER, | |
376 | }; | |
377 | ||
378 | static const struct power_supply_desc max77976_psy_desc = { | |
379 | .name = MAX77976_DRIVER_NAME, | |
380 | .type = POWER_SUPPLY_TYPE_USB, | |
381 | .properties = max77976_psy_props, | |
382 | .num_properties = ARRAY_SIZE(max77976_psy_props), | |
383 | .get_property = max77976_get_property, | |
384 | .set_property = max77976_set_property, | |
385 | .property_is_writeable = max77976_property_is_writeable, | |
386 | }; | |
387 | ||
388 | /* -------------------------------------------------------------------------- | |
389 | * Entry point | |
390 | */ | |
391 | ||
392 | static int max77976_detect(struct max77976 *chg) | |
393 | { | |
394 | struct device *dev = &chg->client->dev; | |
395 | unsigned int id, ver, rev; | |
396 | int err; | |
397 | ||
398 | err = regmap_read(chg->regmap, MAX77976_REG_CHIP_ID, &id); | |
399 | if (err) | |
400 | return dev_err_probe(dev, err, "cannot read chip ID\n"); | |
401 | ||
402 | if (id != MAX77976_CHIP_ID) | |
403 | return dev_err_probe(dev, -ENXIO, "unknown model ID 0x%02x\n", id); | |
404 | ||
405 | err = regmap_field_read(chg->rfield[VERSION], &ver); | |
406 | if (!err) | |
407 | err = regmap_field_read(chg->rfield[REVISION], &rev); | |
408 | if (err) | |
409 | return dev_err_probe(dev, -ENXIO, "cannot read version/revision\n"); | |
410 | ||
411 | dev_info(dev, "detected model MAX779%02x ver %u rev %u", id, ver, rev); | |
412 | ||
413 | return 0; | |
414 | } | |
415 | ||
416 | static int max77976_configure(struct max77976 *chg) | |
417 | { | |
418 | struct device *dev = &chg->client->dev; | |
419 | int err; | |
420 | ||
421 | /* Magic value to unlock writing to some registers */ | |
422 | err = regmap_field_write(chg->rfield[CHGPROT], 0x3); | |
423 | if (err) | |
424 | goto err; | |
425 | ||
426 | /* | |
427 | * Mode 5 = Charger ON, OTG OFF, buck ON, boost OFF. | |
428 | * Other modes are not implemented by this driver. | |
429 | */ | |
430 | err = regmap_field_write(chg->rfield[MODE], MAX77976_MODE_CHARGER_BUCK); | |
431 | if (err) | |
432 | goto err; | |
433 | ||
434 | return 0; | |
435 | ||
436 | err: | |
437 | return dev_err_probe(dev, err, "error while configuring"); | |
438 | } | |
439 | ||
440 | static int max77976_probe(struct i2c_client *client) | |
441 | { | |
442 | struct device *dev = &client->dev; | |
443 | struct power_supply_config psy_cfg = {}; | |
444 | struct power_supply *psy; | |
445 | struct max77976 *chg; | |
446 | int err; | |
447 | int i; | |
448 | ||
449 | chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); | |
450 | if (!chg) | |
451 | return -ENOMEM; | |
452 | ||
453 | i2c_set_clientdata(client, chg); | |
454 | psy_cfg.drv_data = chg; | |
455 | chg->client = client; | |
456 | ||
457 | chg->regmap = devm_regmap_init_i2c(client, &max77976_regmap_config); | |
458 | if (IS_ERR(chg->regmap)) | |
459 | return dev_err_probe(dev, PTR_ERR(chg->regmap), | |
460 | "cannot allocate regmap\n"); | |
461 | ||
462 | for (i = 0; i < MAX77976_N_REGMAP_FIELDS; i++) { | |
463 | chg->rfield[i] = devm_regmap_field_alloc(dev, chg->regmap, | |
464 | max77976_reg_field[i]); | |
465 | if (IS_ERR(chg->rfield[i])) | |
466 | return dev_err_probe(dev, PTR_ERR(chg->rfield[i]), | |
467 | "cannot allocate regmap field\n"); | |
468 | } | |
469 | ||
470 | err = max77976_detect(chg); | |
471 | if (err) | |
472 | return err; | |
473 | ||
474 | err = max77976_configure(chg); | |
475 | if (err) | |
476 | return err; | |
477 | ||
478 | psy = devm_power_supply_register_no_ws(dev, &max77976_psy_desc, &psy_cfg); | |
479 | if (IS_ERR(psy)) | |
480 | return dev_err_probe(dev, PTR_ERR(psy), "cannot register\n"); | |
481 | ||
482 | return 0; | |
483 | } | |
484 | ||
485 | static const struct i2c_device_id max77976_i2c_id[] = { | |
486 | { MAX77976_DRIVER_NAME, 0 }, | |
487 | { }, | |
488 | }; | |
489 | MODULE_DEVICE_TABLE(i2c, max77976_i2c_id); | |
490 | ||
491 | static const struct of_device_id max77976_of_id[] = { | |
492 | { .compatible = "maxim,max77976" }, | |
493 | { }, | |
494 | }; | |
495 | MODULE_DEVICE_TABLE(of, max77976_of_id); | |
496 | ||
497 | static struct i2c_driver max77976_driver = { | |
498 | .driver = { | |
499 | .name = MAX77976_DRIVER_NAME, | |
500 | .of_match_table = max77976_of_id, | |
501 | }, | |
fe20b1dc | 502 | .probe = max77976_probe, |
715ecbc1 LC |
503 | .id_table = max77976_i2c_id, |
504 | }; | |
505 | module_i2c_driver(max77976_driver); | |
506 | ||
a6c487cd | 507 | MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>"); |
715ecbc1 LC |
508 | MODULE_DESCRIPTION("Maxim MAX77976 charger driver"); |
509 | MODULE_LICENSE("GPL v2"); |