Commit | Line | Data |
---|---|---|
359ab9f5 MH |
1 | /* |
2 | * Fuel gauge driver for Maxim 17042 / 8966 / 8997 | |
3 | * Note that Maxim 8966 and 8997 are mfd and this is its subdevice. | |
4 | * | |
5 | * Copyright (C) 2011 Samsung Electronics | |
6 | * MyungJoo Ham <myungjoo.ham@samsung.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | * | |
22 | * This driver is based on max17040_battery.c | |
23 | */ | |
24 | ||
25 | #include <linux/init.h> | |
7e6d62db | 26 | #include <linux/module.h> |
359ab9f5 MH |
27 | #include <linux/slab.h> |
28 | #include <linux/i2c.h> | |
f3a71a6e | 29 | #include <linux/delay.h> |
e5f3872d | 30 | #include <linux/interrupt.h> |
48bc1774 | 31 | #include <linux/pm.h> |
359ab9f5 MH |
32 | #include <linux/mod_devicetable.h> |
33 | #include <linux/power_supply.h> | |
34 | #include <linux/power/max17042_battery.h> | |
3832246d | 35 | #include <linux/of.h> |
39e7213e | 36 | #include <linux/regmap.h> |
359ab9f5 | 37 | |
f3a71a6e RP |
38 | /* Status register bits */ |
39 | #define STATUS_POR_BIT (1 << 1) | |
40 | #define STATUS_BST_BIT (1 << 3) | |
41 | #define STATUS_VMN_BIT (1 << 8) | |
42 | #define STATUS_TMN_BIT (1 << 9) | |
43 | #define STATUS_SMN_BIT (1 << 10) | |
44 | #define STATUS_BI_BIT (1 << 11) | |
45 | #define STATUS_VMX_BIT (1 << 12) | |
46 | #define STATUS_TMX_BIT (1 << 13) | |
47 | #define STATUS_SMX_BIT (1 << 14) | |
48 | #define STATUS_BR_BIT (1 << 15) | |
49 | ||
e5f3872d RP |
50 | /* Interrupt mask bits */ |
51 | #define CONFIG_ALRT_BIT_ENBL (1 << 2) | |
5cdd4d7f RP |
52 | #define STATUS_INTR_SOCMIN_BIT (1 << 10) |
53 | #define STATUS_INTR_SOCMAX_BIT (1 << 14) | |
e5f3872d | 54 | |
f3a71a6e RP |
55 | #define VFSOC0_LOCK 0x0000 |
56 | #define VFSOC0_UNLOCK 0x0080 | |
57 | #define MODEL_UNLOCK1 0X0059 | |
58 | #define MODEL_UNLOCK2 0X00C4 | |
59 | #define MODEL_LOCK1 0X0000 | |
60 | #define MODEL_LOCK2 0X0000 | |
61 | ||
62 | #define dQ_ACC_DIV 0x4 | |
63 | #define dP_ACC_100 0x1900 | |
64 | #define dP_ACC_200 0x3200 | |
65 | ||
edd4ab05 RP |
66 | #define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */ |
67 | ||
359ab9f5 MH |
68 | struct max17042_chip { |
69 | struct i2c_client *client; | |
39e7213e | 70 | struct regmap *regmap; |
297d716f | 71 | struct power_supply *battery; |
9a8422d2 | 72 | enum max170xx_chip_type chip_type; |
359ab9f5 | 73 | struct max17042_platform_data *pdata; |
f3a71a6e RP |
74 | struct work_struct work; |
75 | int init_complete; | |
359ab9f5 MH |
76 | }; |
77 | ||
359ab9f5 | 78 | static enum power_supply_property max17042_battery_props[] = { |
086ef502 DK |
79 | POWER_SUPPLY_PROP_PRESENT, |
80 | POWER_SUPPLY_PROP_CYCLE_COUNT, | |
81 | POWER_SUPPLY_PROP_VOLTAGE_MAX, | |
82 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, | |
359ab9f5 MH |
83 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
84 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | |
a2ebfe2f | 85 | POWER_SUPPLY_PROP_VOLTAGE_OCV, |
359ab9f5 | 86 | POWER_SUPPLY_PROP_CAPACITY, |
086ef502 | 87 | POWER_SUPPLY_PROP_CHARGE_FULL, |
5fc55bc8 | 88 | POWER_SUPPLY_PROP_CHARGE_COUNTER, |
086ef502 | 89 | POWER_SUPPLY_PROP_TEMP, |
edd4ab05 RP |
90 | POWER_SUPPLY_PROP_TEMP_ALERT_MIN, |
91 | POWER_SUPPLY_PROP_TEMP_ALERT_MAX, | |
92 | POWER_SUPPLY_PROP_TEMP_MIN, | |
93 | POWER_SUPPLY_PROP_TEMP_MAX, | |
94 | POWER_SUPPLY_PROP_HEALTH, | |
086ef502 DK |
95 | POWER_SUPPLY_PROP_CURRENT_NOW, |
96 | POWER_SUPPLY_PROP_CURRENT_AVG, | |
359ab9f5 MH |
97 | }; |
98 | ||
edd4ab05 RP |
99 | static int max17042_get_temperature(struct max17042_chip *chip, int *temp) |
100 | { | |
101 | int ret; | |
102 | u32 data; | |
103 | struct regmap *map = chip->regmap; | |
104 | ||
105 | ret = regmap_read(map, MAX17042_TEMP, &data); | |
106 | if (ret < 0) | |
107 | return ret; | |
108 | ||
109 | *temp = data; | |
110 | /* The value is signed. */ | |
111 | if (*temp & 0x8000) { | |
112 | *temp = (0x7fff & ~*temp) + 1; | |
113 | *temp *= -1; | |
114 | } | |
115 | ||
116 | /* The value is converted into deci-centigrade scale */ | |
117 | /* Units of LSB = 1 / 256 degree Celsius */ | |
118 | *temp = *temp * 10 / 256; | |
119 | return 0; | |
120 | } | |
121 | ||
122 | static int max17042_get_battery_health(struct max17042_chip *chip, int *health) | |
123 | { | |
124 | int temp, vavg, vbatt, ret; | |
125 | u32 val; | |
126 | ||
127 | ret = regmap_read(chip->regmap, MAX17042_AvgVCELL, &val); | |
128 | if (ret < 0) | |
129 | goto health_error; | |
130 | ||
131 | /* bits [0-3] unused */ | |
132 | vavg = val * 625 / 8; | |
133 | /* Convert to millivolts */ | |
134 | vavg /= 1000; | |
135 | ||
136 | ret = regmap_read(chip->regmap, MAX17042_VCELL, &val); | |
137 | if (ret < 0) | |
138 | goto health_error; | |
139 | ||
140 | /* bits [0-3] unused */ | |
141 | vbatt = val * 625 / 8; | |
142 | /* Convert to millivolts */ | |
143 | vbatt /= 1000; | |
144 | ||
145 | if (vavg < chip->pdata->vmin) { | |
146 | *health = POWER_SUPPLY_HEALTH_DEAD; | |
147 | goto out; | |
148 | } | |
149 | ||
150 | if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) { | |
151 | *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | |
152 | goto out; | |
153 | } | |
154 | ||
155 | ret = max17042_get_temperature(chip, &temp); | |
156 | if (ret < 0) | |
157 | goto health_error; | |
158 | ||
159 | if (temp <= chip->pdata->temp_min) { | |
160 | *health = POWER_SUPPLY_HEALTH_COLD; | |
161 | goto out; | |
162 | } | |
163 | ||
164 | if (temp >= chip->pdata->temp_max) { | |
165 | *health = POWER_SUPPLY_HEALTH_OVERHEAT; | |
166 | goto out; | |
167 | } | |
168 | ||
169 | *health = POWER_SUPPLY_HEALTH_GOOD; | |
170 | ||
171 | out: | |
172 | return 0; | |
173 | ||
174 | health_error: | |
175 | return ret; | |
176 | } | |
177 | ||
359ab9f5 MH |
178 | static int max17042_get_property(struct power_supply *psy, |
179 | enum power_supply_property psp, | |
180 | union power_supply_propval *val) | |
181 | { | |
297d716f | 182 | struct max17042_chip *chip = power_supply_get_drvdata(psy); |
39e7213e | 183 | struct regmap *map = chip->regmap; |
60a1f6e4 | 184 | int ret; |
39e7213e | 185 | u32 data; |
359ab9f5 | 186 | |
f3a71a6e RP |
187 | if (!chip->init_complete) |
188 | return -EAGAIN; | |
189 | ||
359ab9f5 | 190 | switch (psp) { |
086ef502 | 191 | case POWER_SUPPLY_PROP_PRESENT: |
39e7213e | 192 | ret = regmap_read(map, MAX17042_STATUS, &data); |
60a1f6e4 RP |
193 | if (ret < 0) |
194 | return ret; | |
195 | ||
39e7213e | 196 | if (data & MAX17042_STATUS_BattAbsent) |
086ef502 DK |
197 | val->intval = 0; |
198 | else | |
199 | val->intval = 1; | |
200 | break; | |
201 | case POWER_SUPPLY_PROP_CYCLE_COUNT: | |
39e7213e | 202 | ret = regmap_read(map, MAX17042_Cycles, &data); |
60a1f6e4 RP |
203 | if (ret < 0) |
204 | return ret; | |
205 | ||
39e7213e | 206 | val->intval = data; |
086ef502 DK |
207 | break; |
208 | case POWER_SUPPLY_PROP_VOLTAGE_MAX: | |
39e7213e | 209 | ret = regmap_read(map, MAX17042_MinMaxVolt, &data); |
60a1f6e4 RP |
210 | if (ret < 0) |
211 | return ret; | |
212 | ||
39e7213e | 213 | val->intval = data >> 8; |
086ef502 DK |
214 | val->intval *= 20000; /* Units of LSB = 20mV */ |
215 | break; | |
216 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: | |
709c2c70 | 217 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) |
39e7213e | 218 | ret = regmap_read(map, MAX17042_V_empty, &data); |
9a8422d2 | 219 | else |
39e7213e | 220 | ret = regmap_read(map, MAX17047_V_empty, &data); |
60a1f6e4 RP |
221 | if (ret < 0) |
222 | return ret; | |
223 | ||
39e7213e | 224 | val->intval = data >> 7; |
086ef502 DK |
225 | val->intval *= 10000; /* Units of LSB = 10mV */ |
226 | break; | |
359ab9f5 | 227 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
39e7213e | 228 | ret = regmap_read(map, MAX17042_VCELL, &data); |
60a1f6e4 RP |
229 | if (ret < 0) |
230 | return ret; | |
231 | ||
39e7213e | 232 | val->intval = data * 625 / 8; |
359ab9f5 MH |
233 | break; |
234 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | |
39e7213e | 235 | ret = regmap_read(map, MAX17042_AvgVCELL, &data); |
60a1f6e4 RP |
236 | if (ret < 0) |
237 | return ret; | |
238 | ||
39e7213e | 239 | val->intval = data * 625 / 8; |
a2ebfe2f RP |
240 | break; |
241 | case POWER_SUPPLY_PROP_VOLTAGE_OCV: | |
39e7213e | 242 | ret = regmap_read(map, MAX17042_OCVInternal, &data); |
a2ebfe2f RP |
243 | if (ret < 0) |
244 | return ret; | |
245 | ||
39e7213e | 246 | val->intval = data * 625 / 8; |
359ab9f5 MH |
247 | break; |
248 | case POWER_SUPPLY_PROP_CAPACITY: | |
39e7213e | 249 | ret = regmap_read(map, MAX17042_RepSOC, &data); |
60a1f6e4 RP |
250 | if (ret < 0) |
251 | return ret; | |
252 | ||
39e7213e | 253 | val->intval = data >> 8; |
359ab9f5 | 254 | break; |
086ef502 | 255 | case POWER_SUPPLY_PROP_CHARGE_FULL: |
39e7213e | 256 | ret = regmap_read(map, MAX17042_FullCAP, &data); |
60a1f6e4 RP |
257 | if (ret < 0) |
258 | return ret; | |
259 | ||
39e7213e | 260 | val->intval = data * 1000 / 2; |
5fc55bc8 RP |
261 | break; |
262 | case POWER_SUPPLY_PROP_CHARGE_COUNTER: | |
39e7213e | 263 | ret = regmap_read(map, MAX17042_QH, &data); |
5fc55bc8 RP |
264 | if (ret < 0) |
265 | return ret; | |
266 | ||
39e7213e | 267 | val->intval = data * 1000 / 2; |
086ef502 DK |
268 | break; |
269 | case POWER_SUPPLY_PROP_TEMP: | |
edd4ab05 RP |
270 | ret = max17042_get_temperature(chip, &val->intval); |
271 | if (ret < 0) | |
272 | return ret; | |
273 | break; | |
274 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: | |
275 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | |
276 | if (ret < 0) | |
277 | return ret; | |
278 | /* LSB is Alert Minimum. In deci-centigrade */ | |
279 | val->intval = (data & 0xff) * 10; | |
280 | break; | |
281 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
282 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | |
283 | if (ret < 0) | |
284 | return ret; | |
285 | /* MSB is Alert Maximum. In deci-centigrade */ | |
286 | val->intval = (data >> 8) * 10; | |
287 | break; | |
288 | case POWER_SUPPLY_PROP_TEMP_MIN: | |
289 | val->intval = chip->pdata->temp_min; | |
290 | break; | |
291 | case POWER_SUPPLY_PROP_TEMP_MAX: | |
292 | val->intval = chip->pdata->temp_max; | |
293 | break; | |
294 | case POWER_SUPPLY_PROP_HEALTH: | |
295 | ret = max17042_get_battery_health(chip, &val->intval); | |
60a1f6e4 RP |
296 | if (ret < 0) |
297 | return ret; | |
086ef502 DK |
298 | break; |
299 | case POWER_SUPPLY_PROP_CURRENT_NOW: | |
300 | if (chip->pdata->enable_current_sense) { | |
39e7213e | 301 | ret = regmap_read(map, MAX17042_Current, &data); |
60a1f6e4 RP |
302 | if (ret < 0) |
303 | return ret; | |
304 | ||
39e7213e | 305 | val->intval = data; |
086ef502 DK |
306 | if (val->intval & 0x8000) { |
307 | /* Negative */ | |
308 | val->intval = ~val->intval & 0x7fff; | |
309 | val->intval++; | |
310 | val->intval *= -1; | |
311 | } | |
91d8b0d6 | 312 | val->intval *= 1562500 / chip->pdata->r_sns; |
086ef502 DK |
313 | } else { |
314 | return -EINVAL; | |
315 | } | |
316 | break; | |
317 | case POWER_SUPPLY_PROP_CURRENT_AVG: | |
318 | if (chip->pdata->enable_current_sense) { | |
39e7213e | 319 | ret = regmap_read(map, MAX17042_AvgCurrent, &data); |
60a1f6e4 RP |
320 | if (ret < 0) |
321 | return ret; | |
322 | ||
39e7213e | 323 | val->intval = data; |
086ef502 DK |
324 | if (val->intval & 0x8000) { |
325 | /* Negative */ | |
326 | val->intval = ~val->intval & 0x7fff; | |
327 | val->intval++; | |
328 | val->intval *= -1; | |
329 | } | |
330 | val->intval *= 1562500 / chip->pdata->r_sns; | |
331 | } else { | |
332 | return -EINVAL; | |
333 | } | |
334 | break; | |
359ab9f5 MH |
335 | default: |
336 | return -EINVAL; | |
337 | } | |
338 | return 0; | |
339 | } | |
340 | ||
edd4ab05 RP |
341 | static int max17042_set_property(struct power_supply *psy, |
342 | enum power_supply_property psp, | |
343 | const union power_supply_propval *val) | |
344 | { | |
345 | struct max17042_chip *chip = power_supply_get_drvdata(psy); | |
346 | struct regmap *map = chip->regmap; | |
347 | int ret = 0; | |
348 | u32 data; | |
349 | int8_t temp; | |
350 | ||
351 | switch (psp) { | |
352 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: | |
353 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | |
354 | if (ret < 0) | |
355 | return ret; | |
356 | ||
357 | /* Input in deci-centigrade, convert to centigrade */ | |
358 | temp = val->intval / 10; | |
359 | /* force min < max */ | |
360 | if (temp >= (int8_t)(data >> 8)) | |
361 | temp = (int8_t)(data >> 8) - 1; | |
362 | /* Write both MAX and MIN ALERT */ | |
363 | data = (data & 0xff00) + temp; | |
364 | ret = regmap_write(map, MAX17042_TALRT_Th, data); | |
365 | break; | |
366 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
367 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | |
368 | if (ret < 0) | |
369 | return ret; | |
370 | ||
371 | /* Input in Deci-Centigrade, convert to centigrade */ | |
372 | temp = val->intval / 10; | |
373 | /* force max > min */ | |
374 | if (temp <= (int8_t)(data & 0xff)) | |
375 | temp = (int8_t)(data & 0xff) + 1; | |
376 | /* Write both MAX and MIN ALERT */ | |
377 | data = (data & 0xff) + (temp << 8); | |
378 | ret = regmap_write(map, MAX17042_TALRT_Th, data); | |
379 | break; | |
380 | default: | |
381 | ret = -EINVAL; | |
382 | } | |
383 | ||
384 | return ret; | |
385 | } | |
386 | ||
387 | static int max17042_property_is_writeable(struct power_supply *psy, | |
388 | enum power_supply_property psp) | |
389 | { | |
390 | int ret; | |
391 | ||
392 | switch (psp) { | |
393 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: | |
394 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
395 | ret = 1; | |
396 | break; | |
397 | default: | |
398 | ret = 0; | |
399 | } | |
400 | ||
401 | return ret; | |
402 | } | |
403 | ||
39e7213e | 404 | static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value) |
f3a71a6e RP |
405 | { |
406 | int retries = 8; | |
407 | int ret; | |
39e7213e | 408 | u32 read_value; |
f3a71a6e RP |
409 | |
410 | do { | |
39e7213e JL |
411 | ret = regmap_write(map, reg, value); |
412 | regmap_read(map, reg, &read_value); | |
f3a71a6e RP |
413 | if (read_value != value) { |
414 | ret = -EIO; | |
415 | retries--; | |
416 | } | |
417 | } while (retries && read_value != value); | |
418 | ||
419 | if (ret < 0) | |
39e7213e | 420 | pr_err("%s: err %d\n", __func__, ret); |
f3a71a6e RP |
421 | |
422 | return ret; | |
423 | } | |
424 | ||
39e7213e JL |
425 | static inline void max17042_override_por(struct regmap *map, |
426 | u8 reg, u16 value) | |
f3a71a6e RP |
427 | { |
428 | if (value) | |
39e7213e | 429 | regmap_write(map, reg, value); |
f3a71a6e RP |
430 | } |
431 | ||
432 | static inline void max10742_unlock_model(struct max17042_chip *chip) | |
433 | { | |
39e7213e | 434 | struct regmap *map = chip->regmap; |
bbaeeaaf | 435 | |
39e7213e JL |
436 | regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1); |
437 | regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2); | |
f3a71a6e RP |
438 | } |
439 | ||
440 | static inline void max10742_lock_model(struct max17042_chip *chip) | |
441 | { | |
39e7213e JL |
442 | struct regmap *map = chip->regmap; |
443 | ||
444 | regmap_write(map, MAX17042_MLOCKReg1, MODEL_LOCK1); | |
445 | regmap_write(map, MAX17042_MLOCKReg2, MODEL_LOCK2); | |
f3a71a6e RP |
446 | } |
447 | ||
448 | static inline void max17042_write_model_data(struct max17042_chip *chip, | |
449 | u8 addr, int size) | |
450 | { | |
39e7213e | 451 | struct regmap *map = chip->regmap; |
f3a71a6e | 452 | int i; |
bbaeeaaf | 453 | |
f3a71a6e | 454 | for (i = 0; i < size; i++) |
39e7213e JL |
455 | regmap_write(map, addr + i, |
456 | chip->pdata->config_data->cell_char_tbl[i]); | |
f3a71a6e RP |
457 | } |
458 | ||
459 | static inline void max17042_read_model_data(struct max17042_chip *chip, | |
39e7213e | 460 | u8 addr, u32 *data, int size) |
f3a71a6e | 461 | { |
39e7213e | 462 | struct regmap *map = chip->regmap; |
f3a71a6e RP |
463 | int i; |
464 | ||
465 | for (i = 0; i < size; i++) | |
39e7213e | 466 | regmap_read(map, addr + i, &data[i]); |
f3a71a6e RP |
467 | } |
468 | ||
469 | static inline int max17042_model_data_compare(struct max17042_chip *chip, | |
470 | u16 *data1, u16 *data2, int size) | |
471 | { | |
472 | int i; | |
473 | ||
474 | if (memcmp(data1, data2, size)) { | |
475 | dev_err(&chip->client->dev, "%s compare failed\n", __func__); | |
476 | for (i = 0; i < size; i++) | |
477 | dev_info(&chip->client->dev, "0x%x, 0x%x", | |
478 | data1[i], data2[i]); | |
479 | dev_info(&chip->client->dev, "\n"); | |
480 | return -EINVAL; | |
481 | } | |
482 | return 0; | |
483 | } | |
484 | ||
485 | static int max17042_init_model(struct max17042_chip *chip) | |
486 | { | |
487 | int ret; | |
1ef3d8fb | 488 | int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); |
39e7213e | 489 | u32 *temp_data; |
f3a71a6e | 490 | |
1ef3d8fb | 491 | temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); |
f3a71a6e RP |
492 | if (!temp_data) |
493 | return -ENOMEM; | |
494 | ||
495 | max10742_unlock_model(chip); | |
496 | max17042_write_model_data(chip, MAX17042_MODELChrTbl, | |
497 | table_size); | |
498 | max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data, | |
499 | table_size); | |
500 | ||
501 | ret = max17042_model_data_compare( | |
502 | chip, | |
503 | chip->pdata->config_data->cell_char_tbl, | |
39e7213e | 504 | (u16 *)temp_data, |
f3a71a6e RP |
505 | table_size); |
506 | ||
507 | max10742_lock_model(chip); | |
508 | kfree(temp_data); | |
509 | ||
510 | return ret; | |
511 | } | |
512 | ||
513 | static int max17042_verify_model_lock(struct max17042_chip *chip) | |
514 | { | |
515 | int i; | |
1ef3d8fb | 516 | int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); |
39e7213e | 517 | u32 *temp_data; |
f3a71a6e RP |
518 | int ret = 0; |
519 | ||
1ef3d8fb | 520 | temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); |
f3a71a6e RP |
521 | if (!temp_data) |
522 | return -ENOMEM; | |
523 | ||
524 | max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data, | |
525 | table_size); | |
526 | for (i = 0; i < table_size; i++) | |
527 | if (temp_data[i]) | |
528 | ret = -EINVAL; | |
529 | ||
530 | kfree(temp_data); | |
531 | return ret; | |
532 | } | |
533 | ||
534 | static void max17042_write_config_regs(struct max17042_chip *chip) | |
535 | { | |
536 | struct max17042_config_data *config = chip->pdata->config_data; | |
39e7213e | 537 | struct regmap *map = chip->regmap; |
f3a71a6e | 538 | |
39e7213e JL |
539 | regmap_write(map, MAX17042_CONFIG, config->config); |
540 | regmap_write(map, MAX17042_LearnCFG, config->learn_cfg); | |
541 | regmap_write(map, MAX17042_FilterCFG, | |
f3a71a6e | 542 | config->filter_cfg); |
39e7213e | 543 | regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); |
709c2c70 BS |
544 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || |
545 | chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) | |
39e7213e | 546 | regmap_write(map, MAX17047_FullSOCThr, |
9a8422d2 | 547 | config->full_soc_thresh); |
f3a71a6e RP |
548 | } |
549 | ||
550 | static void max17042_write_custom_regs(struct max17042_chip *chip) | |
551 | { | |
552 | struct max17042_config_data *config = chip->pdata->config_data; | |
39e7213e | 553 | struct regmap *map = chip->regmap; |
f3a71a6e | 554 | |
39e7213e JL |
555 | max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0); |
556 | max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0); | |
557 | max17042_write_verify_reg(map, MAX17042_ICHGTerm, config->ichgt_term); | |
709c2c70 | 558 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) { |
39e7213e JL |
559 | regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco); |
560 | max17042_write_verify_reg(map, MAX17042_K_empty0, | |
9a8422d2 RP |
561 | config->kempty0); |
562 | } else { | |
39e7213e | 563 | max17042_write_verify_reg(map, MAX17047_QRTbl00, |
9a8422d2 | 564 | config->qrtbl00); |
39e7213e | 565 | max17042_write_verify_reg(map, MAX17047_QRTbl10, |
9a8422d2 | 566 | config->qrtbl10); |
39e7213e | 567 | max17042_write_verify_reg(map, MAX17047_QRTbl20, |
9a8422d2 | 568 | config->qrtbl20); |
39e7213e | 569 | max17042_write_verify_reg(map, MAX17047_QRTbl30, |
9a8422d2 RP |
570 | config->qrtbl30); |
571 | } | |
f3a71a6e RP |
572 | } |
573 | ||
574 | static void max17042_update_capacity_regs(struct max17042_chip *chip) | |
575 | { | |
576 | struct max17042_config_data *config = chip->pdata->config_data; | |
39e7213e | 577 | struct regmap *map = chip->regmap; |
f3a71a6e | 578 | |
39e7213e | 579 | max17042_write_verify_reg(map, MAX17042_FullCAP, |
f3a71a6e | 580 | config->fullcap); |
39e7213e JL |
581 | regmap_write(map, MAX17042_DesignCap, config->design_cap); |
582 | max17042_write_verify_reg(map, MAX17042_FullCAPNom, | |
f3a71a6e RP |
583 | config->fullcapnom); |
584 | } | |
585 | ||
586 | static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip) | |
587 | { | |
39e7213e JL |
588 | unsigned int vfSoc; |
589 | struct regmap *map = chip->regmap; | |
f3a71a6e | 590 | |
39e7213e JL |
591 | regmap_read(map, MAX17042_VFSOC, &vfSoc); |
592 | regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK); | |
593 | max17042_write_verify_reg(map, MAX17042_VFSOC0, vfSoc); | |
594 | regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_LOCK); | |
f3a71a6e RP |
595 | } |
596 | ||
597 | static void max17042_load_new_capacity_params(struct max17042_chip *chip) | |
598 | { | |
39e7213e | 599 | u32 full_cap0, rep_cap, dq_acc, vfSoc; |
f3a71a6e RP |
600 | u32 rem_cap; |
601 | ||
602 | struct max17042_config_data *config = chip->pdata->config_data; | |
39e7213e | 603 | struct regmap *map = chip->regmap; |
f3a71a6e | 604 | |
39e7213e JL |
605 | regmap_read(map, MAX17042_FullCAP0, &full_cap0); |
606 | regmap_read(map, MAX17042_VFSOC, &vfSoc); | |
f3a71a6e RP |
607 | |
608 | /* fg_vfSoc needs to shifted by 8 bits to get the | |
609 | * perc in 1% accuracy, to get the right rem_cap multiply | |
610 | * full_cap0, fg_vfSoc and devide by 100 | |
611 | */ | |
612 | rem_cap = ((vfSoc >> 8) * full_cap0) / 100; | |
39e7213e | 613 | max17042_write_verify_reg(map, MAX17042_RemCap, rem_cap); |
f3a71a6e | 614 | |
39e7213e JL |
615 | rep_cap = rem_cap; |
616 | max17042_write_verify_reg(map, MAX17042_RepCap, rep_cap); | |
f3a71a6e RP |
617 | |
618 | /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */ | |
619 | dq_acc = config->fullcap / dQ_ACC_DIV; | |
39e7213e JL |
620 | max17042_write_verify_reg(map, MAX17042_dQacc, dq_acc); |
621 | max17042_write_verify_reg(map, MAX17042_dPacc, dP_ACC_200); | |
f3a71a6e | 622 | |
39e7213e | 623 | max17042_write_verify_reg(map, MAX17042_FullCAP, |
f3a71a6e | 624 | config->fullcap); |
39e7213e | 625 | regmap_write(map, MAX17042_DesignCap, |
f3a71a6e | 626 | config->design_cap); |
39e7213e | 627 | max17042_write_verify_reg(map, MAX17042_FullCAPNom, |
f3a71a6e | 628 | config->fullcapnom); |
9a8422d2 | 629 | /* Update SOC register with new SOC */ |
39e7213e | 630 | regmap_write(map, MAX17042_RepSOC, vfSoc); |
f3a71a6e RP |
631 | } |
632 | ||
633 | /* | |
634 | * Block write all the override values coming from platform data. | |
635 | * This function MUST be called before the POR initialization proceedure | |
636 | * specified by maxim. | |
637 | */ | |
638 | static inline void max17042_override_por_values(struct max17042_chip *chip) | |
639 | { | |
39e7213e | 640 | struct regmap *map = chip->regmap; |
f3a71a6e RP |
641 | struct max17042_config_data *config = chip->pdata->config_data; |
642 | ||
39e7213e JL |
643 | max17042_override_por(map, MAX17042_TGAIN, config->tgain); |
644 | max17042_override_por(map, MAx17042_TOFF, config->toff); | |
645 | max17042_override_por(map, MAX17042_CGAIN, config->cgain); | |
646 | max17042_override_por(map, MAX17042_COFF, config->coff); | |
647 | ||
648 | max17042_override_por(map, MAX17042_VALRT_Th, config->valrt_thresh); | |
649 | max17042_override_por(map, MAX17042_TALRT_Th, config->talrt_thresh); | |
650 | max17042_override_por(map, MAX17042_SALRT_Th, | |
651 | config->soc_alrt_thresh); | |
652 | max17042_override_por(map, MAX17042_CONFIG, config->config); | |
653 | max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer); | |
654 | ||
655 | max17042_override_por(map, MAX17042_DesignCap, config->design_cap); | |
656 | max17042_override_por(map, MAX17042_ICHGTerm, config->ichgt_term); | |
657 | ||
658 | max17042_override_por(map, MAX17042_AtRate, config->at_rate); | |
659 | max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg); | |
660 | max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg); | |
661 | max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg); | |
662 | max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg); | |
663 | max17042_override_por(map, MAX17042_MaskSOC, config->masksoc); | |
664 | ||
665 | max17042_override_por(map, MAX17042_FullCAP, config->fullcap); | |
666 | max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom); | |
709c2c70 | 667 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) |
39e7213e | 668 | max17042_override_por(map, MAX17042_SOC_empty, |
9a8422d2 | 669 | config->socempty); |
39e7213e JL |
670 | max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty); |
671 | max17042_override_por(map, MAX17042_dQacc, config->dqacc); | |
672 | max17042_override_por(map, MAX17042_dPacc, config->dpacc); | |
f3a71a6e | 673 | |
709c2c70 | 674 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) |
39e7213e | 675 | max17042_override_por(map, MAX17042_V_empty, config->vempty); |
9a8422d2 | 676 | else |
39e7213e JL |
677 | max17042_override_por(map, MAX17047_V_empty, config->vempty); |
678 | max17042_override_por(map, MAX17042_TempNom, config->temp_nom); | |
679 | max17042_override_por(map, MAX17042_TempLim, config->temp_lim); | |
680 | max17042_override_por(map, MAX17042_FCTC, config->fctc); | |
681 | max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0); | |
682 | max17042_override_por(map, MAX17042_TempCo, config->tcompc0); | |
9a8422d2 | 683 | if (chip->chip_type) { |
39e7213e JL |
684 | max17042_override_por(map, MAX17042_EmptyTempCo, |
685 | config->empty_tempco); | |
686 | max17042_override_por(map, MAX17042_K_empty0, | |
687 | config->kempty0); | |
9a8422d2 | 688 | } |
f3a71a6e RP |
689 | } |
690 | ||
691 | static int max17042_init_chip(struct max17042_chip *chip) | |
692 | { | |
39e7213e | 693 | struct regmap *map = chip->regmap; |
f3a71a6e | 694 | int ret; |
f3a71a6e RP |
695 | |
696 | max17042_override_por_values(chip); | |
697 | /* After Power up, the MAX17042 requires 500mS in order | |
698 | * to perform signal debouncing and initial SOC reporting | |
699 | */ | |
700 | msleep(500); | |
701 | ||
702 | /* Initialize configaration */ | |
703 | max17042_write_config_regs(chip); | |
704 | ||
705 | /* write cell characterization data */ | |
706 | ret = max17042_init_model(chip); | |
707 | if (ret) { | |
708 | dev_err(&chip->client->dev, "%s init failed\n", | |
709 | __func__); | |
710 | return -EIO; | |
711 | } | |
a879f19f AC |
712 | |
713 | ret = max17042_verify_model_lock(chip); | |
f3a71a6e RP |
714 | if (ret) { |
715 | dev_err(&chip->client->dev, "%s lock verify failed\n", | |
716 | __func__); | |
717 | return -EIO; | |
718 | } | |
719 | /* write custom parameters */ | |
720 | max17042_write_custom_regs(chip); | |
721 | ||
722 | /* update capacity params */ | |
723 | max17042_update_capacity_regs(chip); | |
724 | ||
725 | /* delay must be atleast 350mS to allow VFSOC | |
726 | * to be calculated from the new configuration | |
727 | */ | |
728 | msleep(350); | |
729 | ||
730 | /* reset vfsoc0 reg */ | |
731 | max17042_reset_vfsoc0_reg(chip); | |
732 | ||
733 | /* load new capacity params */ | |
734 | max17042_load_new_capacity_params(chip); | |
735 | ||
736 | /* Init complete, Clear the POR bit */ | |
bc352686 | 737 | regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0); |
f3a71a6e RP |
738 | return 0; |
739 | } | |
740 | ||
e5f3872d RP |
741 | static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off) |
742 | { | |
39e7213e JL |
743 | struct regmap *map = chip->regmap; |
744 | u32 soc, soc_tr; | |
e5f3872d RP |
745 | |
746 | /* program interrupt thesholds such that we should | |
747 | * get interrupt for every 'off' perc change in the soc | |
748 | */ | |
39e7213e JL |
749 | regmap_read(map, MAX17042_RepSOC, &soc); |
750 | soc >>= 8; | |
e5f3872d RP |
751 | soc_tr = (soc + off) << 8; |
752 | soc_tr |= (soc - off); | |
39e7213e | 753 | regmap_write(map, MAX17042_SALRT_Th, soc_tr); |
e5f3872d RP |
754 | } |
755 | ||
e5f3872d RP |
756 | static irqreturn_t max17042_thread_handler(int id, void *dev) |
757 | { | |
758 | struct max17042_chip *chip = dev; | |
39e7213e | 759 | u32 val; |
e5f3872d | 760 | |
39e7213e | 761 | regmap_read(chip->regmap, MAX17042_STATUS, &val); |
5cdd4d7f RP |
762 | if ((val & STATUS_INTR_SOCMIN_BIT) || |
763 | (val & STATUS_INTR_SOCMAX_BIT)) { | |
e5f3872d RP |
764 | dev_info(&chip->client->dev, "SOC threshold INTR\n"); |
765 | max17042_set_soc_threshold(chip, 1); | |
766 | } | |
767 | ||
297d716f | 768 | power_supply_changed(chip->battery); |
e5f3872d RP |
769 | return IRQ_HANDLED; |
770 | } | |
f3a71a6e RP |
771 | |
772 | static void max17042_init_worker(struct work_struct *work) | |
773 | { | |
774 | struct max17042_chip *chip = container_of(work, | |
775 | struct max17042_chip, work); | |
776 | int ret; | |
777 | ||
778 | /* Initialize registers according to values from the platform data */ | |
779 | if (chip->pdata->enable_por_init && chip->pdata->config_data) { | |
780 | ret = max17042_init_chip(chip); | |
781 | if (ret) | |
782 | return; | |
783 | } | |
784 | ||
785 | chip->init_complete = 1; | |
786 | } | |
787 | ||
3832246d KL |
788 | #ifdef CONFIG_OF |
789 | static struct max17042_platform_data * | |
790 | max17042_get_pdata(struct device *dev) | |
791 | { | |
792 | struct device_node *np = dev->of_node; | |
793 | u32 prop; | |
794 | struct max17042_platform_data *pdata; | |
795 | ||
796 | if (!np) | |
797 | return dev->platform_data; | |
798 | ||
799 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | |
800 | if (!pdata) | |
801 | return NULL; | |
802 | ||
803 | /* | |
804 | * Require current sense resistor value to be specified for | |
805 | * current-sense functionality to be enabled at all. | |
806 | */ | |
807 | if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) { | |
808 | pdata->r_sns = prop; | |
809 | pdata->enable_current_sense = true; | |
810 | } | |
811 | ||
a6e6b63e KK |
812 | if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min)) |
813 | pdata->temp_min = INT_MIN; | |
814 | if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max)) | |
815 | pdata->temp_max = INT_MAX; | |
816 | if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin)) | |
817 | pdata->vmin = INT_MIN; | |
818 | if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax)) | |
819 | pdata->vmax = INT_MAX; | |
820 | ||
3832246d KL |
821 | return pdata; |
822 | } | |
823 | #else | |
824 | static struct max17042_platform_data * | |
825 | max17042_get_pdata(struct device *dev) | |
826 | { | |
827 | return dev->platform_data; | |
828 | } | |
829 | #endif | |
830 | ||
e0291285 | 831 | static const struct regmap_config max17042_regmap_config = { |
39e7213e JL |
832 | .reg_bits = 8, |
833 | .val_bits = 16, | |
834 | .val_format_endian = REGMAP_ENDIAN_NATIVE, | |
835 | }; | |
836 | ||
297d716f KK |
837 | static const struct power_supply_desc max17042_psy_desc = { |
838 | .name = "max170xx_battery", | |
839 | .type = POWER_SUPPLY_TYPE_BATTERY, | |
840 | .get_property = max17042_get_property, | |
edd4ab05 RP |
841 | .set_property = max17042_set_property, |
842 | .property_is_writeable = max17042_property_is_writeable, | |
297d716f KK |
843 | .properties = max17042_battery_props, |
844 | .num_properties = ARRAY_SIZE(max17042_battery_props), | |
845 | }; | |
846 | ||
847 | static const struct power_supply_desc max17042_no_current_sense_psy_desc = { | |
848 | .name = "max170xx_battery", | |
849 | .type = POWER_SUPPLY_TYPE_BATTERY, | |
850 | .get_property = max17042_get_property, | |
edd4ab05 RP |
851 | .set_property = max17042_set_property, |
852 | .property_is_writeable = max17042_property_is_writeable, | |
297d716f KK |
853 | .properties = max17042_battery_props, |
854 | .num_properties = ARRAY_SIZE(max17042_battery_props) - 2, | |
855 | }; | |
856 | ||
c8afa640 | 857 | static int max17042_probe(struct i2c_client *client, |
359ab9f5 MH |
858 | const struct i2c_device_id *id) |
859 | { | |
860 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | |
297d716f KK |
861 | const struct power_supply_desc *max17042_desc = &max17042_psy_desc; |
862 | struct power_supply_config psy_cfg = {}; | |
359ab9f5 MH |
863 | struct max17042_chip *chip; |
864 | int ret; | |
39e7213e JL |
865 | int i; |
866 | u32 val; | |
359ab9f5 MH |
867 | |
868 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) | |
869 | return -EIO; | |
870 | ||
2f3b4342 | 871 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); |
359ab9f5 MH |
872 | if (!chip) |
873 | return -ENOMEM; | |
874 | ||
875 | chip->client = client; | |
39e7213e JL |
876 | chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); |
877 | if (IS_ERR(chip->regmap)) { | |
878 | dev_err(&client->dev, "Failed to initialize regmap\n"); | |
879 | return -EINVAL; | |
880 | } | |
881 | ||
3832246d KL |
882 | chip->pdata = max17042_get_pdata(&client->dev); |
883 | if (!chip->pdata) { | |
884 | dev_err(&client->dev, "no platform data provided\n"); | |
885 | return -EINVAL; | |
886 | } | |
359ab9f5 MH |
887 | |
888 | i2c_set_clientdata(client, chip); | |
709c2c70 | 889 | chip->chip_type = id->driver_data; |
297d716f | 890 | psy_cfg.drv_data = chip; |
359ab9f5 | 891 | |
086ef502 DK |
892 | /* When current is not measured, |
893 | * CURRENT_NOW and CURRENT_AVG properties should be invisible. */ | |
894 | if (!chip->pdata->enable_current_sense) | |
297d716f | 895 | max17042_desc = &max17042_no_current_sense_psy_desc; |
086ef502 | 896 | |
4cfa892c PR |
897 | if (chip->pdata->r_sns == 0) |
898 | chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; | |
899 | ||
086ef502 | 900 | if (chip->pdata->init_data) |
39e7213e JL |
901 | for (i = 0; i < chip->pdata->num_init_data; i++) |
902 | regmap_write(chip->regmap, | |
903 | chip->pdata->init_data[i].addr, | |
904 | chip->pdata->init_data[i].data); | |
086ef502 | 905 | |
359ab9f5 | 906 | if (!chip->pdata->enable_current_sense) { |
39e7213e JL |
907 | regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000); |
908 | regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003); | |
909 | regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007); | |
359ab9f5 MH |
910 | } |
911 | ||
50bddb99 VT |
912 | chip->battery = devm_power_supply_register(&client->dev, max17042_desc, |
913 | &psy_cfg); | |
297d716f | 914 | if (IS_ERR(chip->battery)) { |
243e3527 | 915 | dev_err(&client->dev, "failed: power supply register\n"); |
297d716f | 916 | return PTR_ERR(chip->battery); |
243e3527 RP |
917 | } |
918 | ||
e5f3872d | 919 | if (client->irq) { |
50bddb99 VT |
920 | ret = devm_request_threaded_irq(&client->dev, client->irq, |
921 | NULL, | |
922 | max17042_thread_handler, | |
923 | IRQF_TRIGGER_FALLING | | |
924 | IRQF_ONESHOT, | |
925 | chip->battery->desc->name, | |
926 | chip); | |
e5f3872d | 927 | if (!ret) { |
bc352686 KK |
928 | regmap_update_bits(chip->regmap, MAX17042_CONFIG, |
929 | CONFIG_ALRT_BIT_ENBL, | |
930 | CONFIG_ALRT_BIT_ENBL); | |
e5f3872d | 931 | max17042_set_soc_threshold(chip, 1); |
e5ba50bc RP |
932 | } else { |
933 | client->irq = 0; | |
e5f3872d RP |
934 | dev_err(&client->dev, "%s(): cannot get IRQ\n", |
935 | __func__); | |
e5ba50bc | 936 | } |
e5f3872d RP |
937 | } |
938 | ||
39e7213e JL |
939 | regmap_read(chip->regmap, MAX17042_STATUS, &val); |
940 | if (val & STATUS_POR_BIT) { | |
f3a71a6e RP |
941 | INIT_WORK(&chip->work, max17042_init_worker); |
942 | schedule_work(&chip->work); | |
943 | } else { | |
944 | chip->init_complete = 1; | |
945 | } | |
946 | ||
243e3527 | 947 | return 0; |
359ab9f5 MH |
948 | } |
949 | ||
3d23c7f4 | 950 | #ifdef CONFIG_PM_SLEEP |
48bc1774 RP |
951 | static int max17042_suspend(struct device *dev) |
952 | { | |
953 | struct max17042_chip *chip = dev_get_drvdata(dev); | |
954 | ||
48e41c70 AV |
955 | /* |
956 | * disable the irq and enable irq_wake | |
48bc1774 RP |
957 | * capability to the interrupt line. |
958 | */ | |
959 | if (chip->client->irq) { | |
960 | disable_irq(chip->client->irq); | |
961 | enable_irq_wake(chip->client->irq); | |
962 | } | |
963 | ||
964 | return 0; | |
965 | } | |
966 | ||
967 | static int max17042_resume(struct device *dev) | |
968 | { | |
969 | struct max17042_chip *chip = dev_get_drvdata(dev); | |
970 | ||
971 | if (chip->client->irq) { | |
972 | disable_irq_wake(chip->client->irq); | |
973 | enable_irq(chip->client->irq); | |
974 | /* re-program the SOC thresholds to 1% change */ | |
975 | max17042_set_soc_threshold(chip, 1); | |
976 | } | |
977 | ||
978 | return 0; | |
979 | } | |
48bc1774 RP |
980 | #endif |
981 | ||
3d23c7f4 MB |
982 | static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend, |
983 | max17042_resume); | |
984 | ||
3832246d KL |
985 | #ifdef CONFIG_OF |
986 | static const struct of_device_id max17042_dt_match[] = { | |
987 | { .compatible = "maxim,max17042" }, | |
9a8422d2 RP |
988 | { .compatible = "maxim,max17047" }, |
989 | { .compatible = "maxim,max17050" }, | |
3832246d KL |
990 | { }, |
991 | }; | |
992 | MODULE_DEVICE_TABLE(of, max17042_dt_match); | |
993 | #endif | |
994 | ||
359ab9f5 | 995 | static const struct i2c_device_id max17042_id[] = { |
709c2c70 BS |
996 | { "max17042", MAXIM_DEVICE_TYPE_MAX17042 }, |
997 | { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, | |
998 | { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, | |
359ab9f5 MH |
999 | { } |
1000 | }; | |
1001 | MODULE_DEVICE_TABLE(i2c, max17042_id); | |
1002 | ||
1003 | static struct i2c_driver max17042_i2c_driver = { | |
1004 | .driver = { | |
1005 | .name = "max17042", | |
3832246d | 1006 | .of_match_table = of_match_ptr(max17042_dt_match), |
3d23c7f4 | 1007 | .pm = &max17042_pm_ops, |
359ab9f5 MH |
1008 | }, |
1009 | .probe = max17042_probe, | |
359ab9f5 MH |
1010 | .id_table = max17042_id, |
1011 | }; | |
5ff92e7a | 1012 | module_i2c_driver(max17042_i2c_driver); |
359ab9f5 MH |
1013 | |
1014 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | |
1015 | MODULE_DESCRIPTION("MAX17042 Fuel Gauge"); | |
1016 | MODULE_LICENSE("GPL"); |