1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // max17040_battery.c |
4 | // fuel-gauge systems for lithium-ion (Li+) batteries |
5 | // |
6 | // Copyright (C) 2009 Samsung Electronics |
7 | // Minkyu Kang <mk7.kang@samsung.com> |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/init.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/mutex.h> |
13 | #include <linux/err.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/power_supply.h> |
18 | #include <linux/of.h> |
19 | #include <linux/regmap.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/iio/consumer.h> |
22 | |
23 | #define MAX17040_VCELL 0x02 |
24 | #define MAX17040_SOC 0x04 |
25 | #define MAX17040_MODE 0x06 |
26 | #define MAX17040_VER 0x08 |
27 | #define MAX17040_CONFIG 0x0C |
28 | #define MAX17040_STATUS 0x1A |
29 | #define MAX17040_CMD 0xFE |
30 | |
31 | |
32 | #define MAX17040_DELAY 1000 |
33 | #define MAX17040_BATTERY_FULL 95 |
34 | #define MAX17040_RCOMP_DEFAULT 0x9700 |
35 | |
36 | #define MAX17040_ATHD_MASK 0x3f |
37 | #define MAX17040_ALSC_MASK 0x40 |
38 | #define MAX17040_ATHD_DEFAULT_POWER_UP 4 |
39 | #define MAX17040_STATUS_HD_MASK 0x1000 |
40 | #define MAX17040_STATUS_SC_MASK 0x2000 |
41 | #define MAX17040_CFG_RCOMP_MASK 0xff00 |
42 | |
43 | enum chip_id { |
44 | ID_MAX17040, |
45 | ID_MAX17041, |
46 | ID_MAX17043, |
47 | ID_MAX17044, |
48 | ID_MAX17048, |
49 | ID_MAX17049, |
50 | ID_MAX17058, |
51 | ID_MAX17059, |
52 | }; |
53 | |
54 | /* values that differ by chip_id */ |
55 | struct chip_data { |
56 | u16 reset_val; |
57 | u16 vcell_shift; |
58 | u16 vcell_mul; |
59 | u16 vcell_div; |
60 | u8 has_low_soc_alert; |
61 | u8 rcomp_bytes; |
62 | u8 has_soc_alert; |
63 | }; |
64 | |
65 | static struct chip_data max17040_family[] = { |
66 | [ID_MAX17040] = { |
67 | .reset_val = 0x0054, |
68 | .vcell_shift = 4, |
69 | .vcell_mul = 1250, |
70 | .vcell_div = 1, |
71 | .has_low_soc_alert = 0, |
72 | .rcomp_bytes = 2, |
73 | .has_soc_alert = 0, |
74 | }, |
75 | [ID_MAX17041] = { |
76 | .reset_val = 0x0054, |
77 | .vcell_shift = 4, |
78 | .vcell_mul = 2500, |
79 | .vcell_div = 1, |
80 | .has_low_soc_alert = 0, |
81 | .rcomp_bytes = 2, |
82 | .has_soc_alert = 0, |
83 | }, |
84 | [ID_MAX17043] = { |
85 | .reset_val = 0x0054, |
86 | .vcell_shift = 4, |
87 | .vcell_mul = 1250, |
88 | .vcell_div = 1, |
89 | .has_low_soc_alert = 1, |
90 | .rcomp_bytes = 1, |
91 | .has_soc_alert = 0, |
92 | }, |
93 | [ID_MAX17044] = { |
94 | .reset_val = 0x0054, |
95 | .vcell_shift = 4, |
96 | .vcell_mul = 2500, |
97 | .vcell_div = 1, |
98 | .has_low_soc_alert = 1, |
99 | .rcomp_bytes = 1, |
100 | .has_soc_alert = 0, |
101 | }, |
102 | [ID_MAX17048] = { |
103 | .reset_val = 0x5400, |
104 | .vcell_shift = 0, |
105 | .vcell_mul = 625, |
106 | .vcell_div = 8, |
107 | .has_low_soc_alert = 1, |
108 | .rcomp_bytes = 1, |
109 | .has_soc_alert = 1, |
110 | }, |
111 | [ID_MAX17049] = { |
112 | .reset_val = 0x5400, |
113 | .vcell_shift = 0, |
114 | .vcell_mul = 625, |
115 | .vcell_div = 4, |
116 | .has_low_soc_alert = 1, |
117 | .rcomp_bytes = 1, |
118 | .has_soc_alert = 1, |
119 | }, |
120 | [ID_MAX17058] = { |
121 | .reset_val = 0x5400, |
122 | .vcell_shift = 0, |
123 | .vcell_mul = 625, |
124 | .vcell_div = 8, |
125 | .has_low_soc_alert = 1, |
126 | .rcomp_bytes = 1, |
127 | .has_soc_alert = 0, |
128 | }, |
129 | [ID_MAX17059] = { |
130 | .reset_val = 0x5400, |
131 | .vcell_shift = 0, |
132 | .vcell_mul = 625, |
133 | .vcell_div = 4, |
134 | .has_low_soc_alert = 1, |
135 | .rcomp_bytes = 1, |
136 | .has_soc_alert = 0, |
137 | }, |
138 | }; |
139 | |
140 | struct max17040_chip { |
141 | struct i2c_client *client; |
142 | struct regmap *regmap; |
143 | struct delayed_work work; |
144 | struct power_supply *battery; |
145 | struct chip_data data; |
146 | struct iio_channel *channel_temp; |
147 | |
148 | /* battery capacity */ |
149 | int soc; |
150 | /* Low alert threshold from 32% to 1% of the State of Charge */ |
151 | u32 low_soc_alert; |
152 | /* some devices return twice the capacity */ |
153 | bool quirk_double_soc; |
154 | /* higher 8 bits for 17043+, 16 bits for 17040,41 */ |
155 | u16 rcomp; |
156 | }; |
157 | |
158 | static int max17040_reset(struct max17040_chip *chip) |
159 | { |
160 | return regmap_write(map: chip->regmap, MAX17040_CMD, val: chip->data.reset_val); |
161 | } |
162 | |
163 | static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) |
164 | { |
165 | level = 32 - level * (chip->quirk_double_soc ? 2 : 1); |
166 | return regmap_update_bits(map: chip->regmap, MAX17040_CONFIG, |
167 | MAX17040_ATHD_MASK, val: level); |
168 | } |
169 | |
170 | static int max17040_set_soc_alert(struct max17040_chip *chip, bool enable) |
171 | { |
172 | return regmap_update_bits(map: chip->regmap, MAX17040_CONFIG, |
173 | MAX17040_ALSC_MASK, val: enable ? MAX17040_ALSC_MASK : 0); |
174 | } |
175 | |
176 | static int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp) |
177 | { |
178 | u16 mask = chip->data.rcomp_bytes == 2 ? |
179 | 0xffff : MAX17040_CFG_RCOMP_MASK; |
180 | |
181 | return regmap_update_bits(map: chip->regmap, MAX17040_CONFIG, mask, val: rcomp); |
182 | } |
183 | |
184 | static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell) |
185 | { |
186 | struct chip_data *d = &chip->data; |
187 | |
188 | return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div; |
189 | } |
190 | |
191 | |
192 | static int max17040_get_vcell(struct max17040_chip *chip) |
193 | { |
194 | u32 vcell; |
195 | |
196 | regmap_read(map: chip->regmap, MAX17040_VCELL, val: &vcell); |
197 | |
198 | return max17040_raw_vcell_to_uvolts(chip, vcell); |
199 | } |
200 | |
201 | static int max17040_get_soc(struct max17040_chip *chip) |
202 | { |
203 | u32 soc; |
204 | |
205 | regmap_read(map: chip->regmap, MAX17040_SOC, val: &soc); |
206 | |
207 | return soc >> (chip->quirk_double_soc ? 9 : 8); |
208 | } |
209 | |
210 | static int max17040_get_version(struct max17040_chip *chip) |
211 | { |
212 | int ret; |
213 | u32 version; |
214 | |
215 | ret = regmap_read(map: chip->regmap, MAX17040_VER, val: &version); |
216 | |
217 | return ret ? ret : version; |
218 | } |
219 | |
220 | static int max17040_get_online(struct max17040_chip *chip) |
221 | { |
222 | return 1; |
223 | } |
224 | |
225 | static int max17040_get_of_data(struct max17040_chip *chip) |
226 | { |
227 | struct device *dev = &chip->client->dev; |
228 | struct chip_data *data = &max17040_family[ |
229 | (uintptr_t) of_device_get_match_data(dev)]; |
230 | int rcomp_len; |
231 | u8 rcomp[2]; |
232 | |
233 | chip->quirk_double_soc = device_property_read_bool(dev, |
234 | propname: "maxim,double-soc" ); |
235 | |
236 | chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP; |
237 | device_property_read_u32(dev, |
238 | propname: "maxim,alert-low-soc-level" , |
239 | val: &chip->low_soc_alert); |
240 | |
241 | if (chip->low_soc_alert <= 0 || |
242 | chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) { |
243 | dev_err(dev, "maxim,alert-low-soc-level out of bounds\n" ); |
244 | return -EINVAL; |
245 | } |
246 | |
247 | rcomp_len = device_property_count_u8(dev, propname: "maxim,rcomp" ); |
248 | chip->rcomp = MAX17040_RCOMP_DEFAULT; |
249 | if (rcomp_len == data->rcomp_bytes) { |
250 | if (!device_property_read_u8_array(dev, propname: "maxim,rcomp" , |
251 | val: rcomp, nval: rcomp_len)) |
252 | chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] : |
253 | rcomp[0] << 8; |
254 | } else if (rcomp_len > 0) { |
255 | dev_err(dev, "maxim,rcomp has incorrect length\n" ); |
256 | return -EINVAL; |
257 | } |
258 | |
259 | return 0; |
260 | } |
261 | |
262 | static void max17040_check_changes(struct max17040_chip *chip) |
263 | { |
264 | chip->soc = max17040_get_soc(chip); |
265 | } |
266 | |
267 | static void max17040_queue_work(struct max17040_chip *chip) |
268 | { |
269 | queue_delayed_work(wq: system_power_efficient_wq, dwork: &chip->work, |
270 | MAX17040_DELAY); |
271 | } |
272 | |
273 | static void max17040_stop_work(void *data) |
274 | { |
275 | struct max17040_chip *chip = data; |
276 | |
277 | cancel_delayed_work_sync(dwork: &chip->work); |
278 | } |
279 | |
280 | static void max17040_work(struct work_struct *work) |
281 | { |
282 | struct max17040_chip *chip; |
283 | int last_soc; |
284 | |
285 | chip = container_of(work, struct max17040_chip, work.work); |
286 | |
287 | /* store SOC to check changes */ |
288 | last_soc = chip->soc; |
289 | max17040_check_changes(chip); |
290 | |
291 | /* check changes and send uevent */ |
292 | if (last_soc != chip->soc) |
293 | power_supply_changed(psy: chip->battery); |
294 | |
295 | max17040_queue_work(chip); |
296 | } |
297 | |
298 | /* Returns true if alert cause was SOC change, not low SOC */ |
299 | static bool max17040_handle_soc_alert(struct max17040_chip *chip) |
300 | { |
301 | bool ret = true; |
302 | u32 data; |
303 | |
304 | regmap_read(map: chip->regmap, MAX17040_STATUS, val: &data); |
305 | |
306 | if (data & MAX17040_STATUS_HD_MASK) { |
307 | // this alert was caused by low soc |
308 | ret = false; |
309 | } |
310 | if (data & MAX17040_STATUS_SC_MASK) { |
311 | // soc change bit -- deassert to mark as handled |
312 | regmap_write(map: chip->regmap, MAX17040_STATUS, |
313 | val: data & ~MAX17040_STATUS_SC_MASK); |
314 | } |
315 | |
316 | return ret; |
317 | } |
318 | |
319 | static irqreturn_t max17040_thread_handler(int id, void *dev) |
320 | { |
321 | struct max17040_chip *chip = dev; |
322 | |
323 | if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip))) |
324 | dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n" ); |
325 | |
326 | /* read registers */ |
327 | max17040_check_changes(chip); |
328 | |
329 | /* send uevent */ |
330 | power_supply_changed(psy: chip->battery); |
331 | |
332 | /* reset alert bit */ |
333 | max17040_set_low_soc_alert(chip, level: chip->low_soc_alert); |
334 | |
335 | return IRQ_HANDLED; |
336 | } |
337 | |
338 | static int max17040_enable_alert_irq(struct max17040_chip *chip) |
339 | { |
340 | struct i2c_client *client = chip->client; |
341 | int ret; |
342 | |
343 | ret = devm_request_threaded_irq(dev: &client->dev, irq: client->irq, NULL, |
344 | thread_fn: max17040_thread_handler, IRQF_ONESHOT, |
345 | devname: chip->battery->desc->name, dev_id: chip); |
346 | |
347 | return ret; |
348 | } |
349 | |
350 | static int max17040_prop_writeable(struct power_supply *psy, |
351 | enum power_supply_property psp) |
352 | { |
353 | switch (psp) { |
354 | case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: |
355 | return 1; |
356 | default: |
357 | return 0; |
358 | } |
359 | } |
360 | |
361 | static int max17040_set_property(struct power_supply *psy, |
362 | enum power_supply_property psp, |
363 | const union power_supply_propval *val) |
364 | { |
365 | struct max17040_chip *chip = power_supply_get_drvdata(psy); |
366 | int ret; |
367 | |
368 | switch (psp) { |
369 | case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: |
370 | /* alert threshold can be programmed from 1% up to 16/32% */ |
371 | if ((val->intval < 1) || |
372 | (val->intval > (chip->quirk_double_soc ? 16 : 32))) { |
373 | ret = -EINVAL; |
374 | break; |
375 | } |
376 | ret = max17040_set_low_soc_alert(chip, level: val->intval); |
377 | chip->low_soc_alert = val->intval; |
378 | break; |
379 | default: |
380 | ret = -EINVAL; |
381 | } |
382 | |
383 | return ret; |
384 | } |
385 | |
386 | static int max17040_get_property(struct power_supply *psy, |
387 | enum power_supply_property psp, |
388 | union power_supply_propval *val) |
389 | { |
390 | struct max17040_chip *chip = power_supply_get_drvdata(psy); |
391 | |
392 | switch (psp) { |
393 | case POWER_SUPPLY_PROP_ONLINE: |
394 | case POWER_SUPPLY_PROP_PRESENT: |
395 | val->intval = max17040_get_online(chip); |
396 | break; |
397 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
398 | val->intval = max17040_get_vcell(chip); |
399 | break; |
400 | case POWER_SUPPLY_PROP_CAPACITY: |
401 | val->intval = max17040_get_soc(chip); |
402 | break; |
403 | case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: |
404 | val->intval = chip->low_soc_alert; |
405 | break; |
406 | case POWER_SUPPLY_PROP_STATUS: |
407 | power_supply_get_property_from_supplier(psy, psp, val); |
408 | break; |
409 | case POWER_SUPPLY_PROP_TEMP: |
410 | if (!chip->channel_temp) |
411 | return -ENODATA; |
412 | |
413 | iio_read_channel_processed_scale(chan: chip->channel_temp, |
414 | val: &val->intval, scale: 10); |
415 | break; |
416 | default: |
417 | return -EINVAL; |
418 | } |
419 | return 0; |
420 | } |
421 | |
422 | static const struct regmap_config max17040_regmap = { |
423 | .reg_bits = 8, |
424 | .reg_stride = 2, |
425 | .val_bits = 16, |
426 | .val_format_endian = REGMAP_ENDIAN_BIG, |
427 | }; |
428 | |
429 | static enum power_supply_property max17040_battery_props[] = { |
430 | POWER_SUPPLY_PROP_ONLINE, |
431 | POWER_SUPPLY_PROP_PRESENT, |
432 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
433 | POWER_SUPPLY_PROP_CAPACITY, |
434 | POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, |
435 | POWER_SUPPLY_PROP_STATUS, |
436 | POWER_SUPPLY_PROP_TEMP, |
437 | }; |
438 | |
439 | static const struct power_supply_desc max17040_battery_desc = { |
440 | .name = "battery" , |
441 | .type = POWER_SUPPLY_TYPE_BATTERY, |
442 | .get_property = max17040_get_property, |
443 | .set_property = max17040_set_property, |
444 | .property_is_writeable = max17040_prop_writeable, |
445 | .properties = max17040_battery_props, |
446 | .num_properties = ARRAY_SIZE(max17040_battery_props), |
447 | }; |
448 | |
449 | static int max17040_probe(struct i2c_client *client) |
450 | { |
451 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
452 | struct i2c_adapter *adapter = client->adapter; |
453 | struct power_supply_config psy_cfg = {}; |
454 | struct max17040_chip *chip; |
455 | enum chip_id chip_id; |
456 | bool enable_irq = false; |
457 | int ret; |
458 | |
459 | if (!i2c_check_functionality(adap: adapter, I2C_FUNC_SMBUS_BYTE)) |
460 | return -EIO; |
461 | |
462 | chip = devm_kzalloc(dev: &client->dev, size: sizeof(*chip), GFP_KERNEL); |
463 | if (!chip) |
464 | return -ENOMEM; |
465 | |
466 | chip->client = client; |
467 | chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); |
468 | if (IS_ERR(ptr: chip->regmap)) |
469 | return PTR_ERR(ptr: chip->regmap); |
470 | chip_id = (enum chip_id) id->driver_data; |
471 | if (client->dev.of_node) { |
472 | ret = max17040_get_of_data(chip); |
473 | if (ret) |
474 | return ret; |
475 | chip_id = (uintptr_t)of_device_get_match_data(dev: &client->dev); |
476 | } |
477 | chip->data = max17040_family[chip_id]; |
478 | |
479 | i2c_set_clientdata(client, data: chip); |
480 | psy_cfg.drv_data = chip; |
481 | |
482 | /* Switch to devm_iio_channel_get_optional when available */ |
483 | chip->channel_temp = devm_iio_channel_get(dev: &client->dev, consumer_channel: "temp" ); |
484 | if (IS_ERR(ptr: chip->channel_temp)) { |
485 | ret = PTR_ERR(ptr: chip->channel_temp); |
486 | if (ret != -ENODEV) |
487 | return dev_err_probe(dev: &client->dev, err: PTR_ERR(ptr: chip->channel_temp), |
488 | fmt: "failed to get temp\n" ); |
489 | else |
490 | chip->channel_temp = NULL; |
491 | } |
492 | |
493 | chip->battery = devm_power_supply_register(parent: &client->dev, |
494 | desc: &max17040_battery_desc, cfg: &psy_cfg); |
495 | if (IS_ERR(ptr: chip->battery)) { |
496 | dev_err(&client->dev, "failed: power supply register\n" ); |
497 | return PTR_ERR(ptr: chip->battery); |
498 | } |
499 | |
500 | ret = max17040_get_version(chip); |
501 | if (ret < 0) |
502 | return ret; |
503 | dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n" , ret); |
504 | |
505 | if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) |
506 | max17040_reset(chip); |
507 | |
508 | max17040_set_rcomp(chip, rcomp: chip->rcomp); |
509 | |
510 | /* check interrupt */ |
511 | if (client->irq && chip->data.has_low_soc_alert) { |
512 | ret = max17040_set_low_soc_alert(chip, level: chip->low_soc_alert); |
513 | if (ret) { |
514 | dev_err(&client->dev, |
515 | "Failed to set low SOC alert: err %d\n" , ret); |
516 | return ret; |
517 | } |
518 | |
519 | enable_irq = true; |
520 | } |
521 | |
522 | if (client->irq && chip->data.has_soc_alert) { |
523 | ret = max17040_set_soc_alert(chip, enable: 1); |
524 | if (ret) { |
525 | dev_err(&client->dev, |
526 | "Failed to set SOC alert: err %d\n" , ret); |
527 | return ret; |
528 | } |
529 | enable_irq = true; |
530 | } else { |
531 | /* soc alerts negate the need for polling */ |
532 | INIT_DEFERRABLE_WORK(&chip->work, max17040_work); |
533 | ret = devm_add_action(&client->dev, max17040_stop_work, chip); |
534 | if (ret) |
535 | return ret; |
536 | max17040_queue_work(chip); |
537 | } |
538 | |
539 | if (enable_irq) { |
540 | ret = max17040_enable_alert_irq(chip); |
541 | if (ret) { |
542 | client->irq = 0; |
543 | dev_warn(&client->dev, |
544 | "Failed to get IRQ err %d\n" , ret); |
545 | } |
546 | } |
547 | |
548 | return 0; |
549 | } |
550 | |
551 | #ifdef CONFIG_PM_SLEEP |
552 | |
553 | static int max17040_suspend(struct device *dev) |
554 | { |
555 | struct i2c_client *client = to_i2c_client(dev); |
556 | struct max17040_chip *chip = i2c_get_clientdata(client); |
557 | |
558 | if (client->irq && chip->data.has_soc_alert) |
559 | // disable soc alert to prevent wakeup |
560 | max17040_set_soc_alert(chip, enable: 0); |
561 | else |
562 | cancel_delayed_work(dwork: &chip->work); |
563 | |
564 | if (client->irq && device_may_wakeup(dev)) |
565 | enable_irq_wake(irq: client->irq); |
566 | |
567 | return 0; |
568 | } |
569 | |
570 | static int max17040_resume(struct device *dev) |
571 | { |
572 | struct i2c_client *client = to_i2c_client(dev); |
573 | struct max17040_chip *chip = i2c_get_clientdata(client); |
574 | |
575 | if (client->irq && device_may_wakeup(dev)) |
576 | disable_irq_wake(irq: client->irq); |
577 | |
578 | if (client->irq && chip->data.has_soc_alert) |
579 | max17040_set_soc_alert(chip, enable: 1); |
580 | else |
581 | max17040_queue_work(chip); |
582 | |
583 | return 0; |
584 | } |
585 | |
586 | static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); |
587 | #define MAX17040_PM_OPS (&max17040_pm_ops) |
588 | |
589 | #else |
590 | |
591 | #define MAX17040_PM_OPS NULL |
592 | |
593 | #endif /* CONFIG_PM_SLEEP */ |
594 | |
595 | static const struct i2c_device_id max17040_id[] = { |
596 | { "max17040" , ID_MAX17040 }, |
597 | { "max17041" , ID_MAX17041 }, |
598 | { "max17043" , ID_MAX17043 }, |
599 | { "max77836-battery" , ID_MAX17043 }, |
600 | { "max17044" , ID_MAX17044 }, |
601 | { "max17048" , ID_MAX17048 }, |
602 | { "max17049" , ID_MAX17049 }, |
603 | { "max17058" , ID_MAX17058 }, |
604 | { "max17059" , ID_MAX17059 }, |
605 | { /* sentinel */ } |
606 | }; |
607 | MODULE_DEVICE_TABLE(i2c, max17040_id); |
608 | |
609 | static const struct of_device_id max17040_of_match[] = { |
610 | { .compatible = "maxim,max17040" , .data = (void *) ID_MAX17040 }, |
611 | { .compatible = "maxim,max17041" , .data = (void *) ID_MAX17041 }, |
612 | { .compatible = "maxim,max17043" , .data = (void *) ID_MAX17043 }, |
613 | { .compatible = "maxim,max77836-battery" , .data = (void *) ID_MAX17043 }, |
614 | { .compatible = "maxim,max17044" , .data = (void *) ID_MAX17044 }, |
615 | { .compatible = "maxim,max17048" , .data = (void *) ID_MAX17048 }, |
616 | { .compatible = "maxim,max17049" , .data = (void *) ID_MAX17049 }, |
617 | { .compatible = "maxim,max17058" , .data = (void *) ID_MAX17058 }, |
618 | { .compatible = "maxim,max17059" , .data = (void *) ID_MAX17059 }, |
619 | { /* sentinel */ }, |
620 | }; |
621 | MODULE_DEVICE_TABLE(of, max17040_of_match); |
622 | |
623 | static struct i2c_driver max17040_i2c_driver = { |
624 | .driver = { |
625 | .name = "max17040" , |
626 | .of_match_table = max17040_of_match, |
627 | .pm = MAX17040_PM_OPS, |
628 | }, |
629 | .probe = max17040_probe, |
630 | .id_table = max17040_id, |
631 | }; |
632 | module_i2c_driver(max17040_i2c_driver); |
633 | |
634 | MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>" ); |
635 | MODULE_DESCRIPTION("MAX17040 Fuel Gauge" ); |
636 | MODULE_LICENSE("GPL" ); |
637 | |