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 | ||
e2116202 | 25 | #include <linux/acpi.h> |
359ab9f5 | 26 | #include <linux/init.h> |
7e6d62db | 27 | #include <linux/module.h> |
359ab9f5 MH |
28 | #include <linux/slab.h> |
29 | #include <linux/i2c.h> | |
f3a71a6e | 30 | #include <linux/delay.h> |
e5f3872d | 31 | #include <linux/interrupt.h> |
48bc1774 | 32 | #include <linux/pm.h> |
359ab9f5 MH |
33 | #include <linux/mod_devicetable.h> |
34 | #include <linux/power_supply.h> | |
35 | #include <linux/power/max17042_battery.h> | |
3832246d | 36 | #include <linux/of.h> |
39e7213e | 37 | #include <linux/regmap.h> |
359ab9f5 | 38 | |
f3a71a6e RP |
39 | /* Status register bits */ |
40 | #define STATUS_POR_BIT (1 << 1) | |
41 | #define STATUS_BST_BIT (1 << 3) | |
42 | #define STATUS_VMN_BIT (1 << 8) | |
43 | #define STATUS_TMN_BIT (1 << 9) | |
44 | #define STATUS_SMN_BIT (1 << 10) | |
45 | #define STATUS_BI_BIT (1 << 11) | |
46 | #define STATUS_VMX_BIT (1 << 12) | |
47 | #define STATUS_TMX_BIT (1 << 13) | |
48 | #define STATUS_SMX_BIT (1 << 14) | |
49 | #define STATUS_BR_BIT (1 << 15) | |
50 | ||
e5f3872d RP |
51 | /* Interrupt mask bits */ |
52 | #define CONFIG_ALRT_BIT_ENBL (1 << 2) | |
5cdd4d7f RP |
53 | #define STATUS_INTR_SOCMIN_BIT (1 << 10) |
54 | #define STATUS_INTR_SOCMAX_BIT (1 << 14) | |
e5f3872d | 55 | |
f3a71a6e RP |
56 | #define VFSOC0_LOCK 0x0000 |
57 | #define VFSOC0_UNLOCK 0x0080 | |
58 | #define MODEL_UNLOCK1 0X0059 | |
59 | #define MODEL_UNLOCK2 0X00C4 | |
60 | #define MODEL_LOCK1 0X0000 | |
61 | #define MODEL_LOCK2 0X0000 | |
62 | ||
63 | #define dQ_ACC_DIV 0x4 | |
64 | #define dP_ACC_100 0x1900 | |
65 | #define dP_ACC_200 0x3200 | |
66 | ||
edd4ab05 RP |
67 | #define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */ |
68 | ||
359ab9f5 MH |
69 | struct max17042_chip { |
70 | struct i2c_client *client; | |
39e7213e | 71 | struct regmap *regmap; |
297d716f | 72 | struct power_supply *battery; |
9a8422d2 | 73 | enum max170xx_chip_type chip_type; |
359ab9f5 | 74 | struct max17042_platform_data *pdata; |
f3a71a6e RP |
75 | struct work_struct work; |
76 | int init_complete; | |
359ab9f5 MH |
77 | }; |
78 | ||
359ab9f5 | 79 | static enum power_supply_property max17042_battery_props[] = { |
a9df22c0 | 80 | POWER_SUPPLY_PROP_STATUS, |
086ef502 | 81 | POWER_SUPPLY_PROP_PRESENT, |
ef7fcdae | 82 | POWER_SUPPLY_PROP_TECHNOLOGY, |
086ef502 DK |
83 | POWER_SUPPLY_PROP_CYCLE_COUNT, |
84 | POWER_SUPPLY_PROP_VOLTAGE_MAX, | |
7bfc9397 | 85 | POWER_SUPPLY_PROP_VOLTAGE_MIN, |
086ef502 | 86 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, |
359ab9f5 MH |
87 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
88 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | |
a2ebfe2f | 89 | POWER_SUPPLY_PROP_VOLTAGE_OCV, |
359ab9f5 | 90 | POWER_SUPPLY_PROP_CAPACITY, |
2e015412 | 91 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, |
086ef502 | 92 | POWER_SUPPLY_PROP_CHARGE_FULL, |
6d6b61ea | 93 | POWER_SUPPLY_PROP_CHARGE_NOW, |
5fc55bc8 | 94 | POWER_SUPPLY_PROP_CHARGE_COUNTER, |
086ef502 | 95 | POWER_SUPPLY_PROP_TEMP, |
edd4ab05 RP |
96 | POWER_SUPPLY_PROP_TEMP_ALERT_MIN, |
97 | POWER_SUPPLY_PROP_TEMP_ALERT_MAX, | |
98 | POWER_SUPPLY_PROP_TEMP_MIN, | |
99 | POWER_SUPPLY_PROP_TEMP_MAX, | |
100 | POWER_SUPPLY_PROP_HEALTH, | |
adb69a3c | 101 | POWER_SUPPLY_PROP_SCOPE, |
086ef502 DK |
102 | POWER_SUPPLY_PROP_CURRENT_NOW, |
103 | POWER_SUPPLY_PROP_CURRENT_AVG, | |
359ab9f5 MH |
104 | }; |
105 | ||
edd4ab05 RP |
106 | static int max17042_get_temperature(struct max17042_chip *chip, int *temp) |
107 | { | |
108 | int ret; | |
109 | u32 data; | |
110 | struct regmap *map = chip->regmap; | |
111 | ||
112 | ret = regmap_read(map, MAX17042_TEMP, &data); | |
113 | if (ret < 0) | |
114 | return ret; | |
115 | ||
c67c0693 | 116 | *temp = sign_extend32(data, 15); |
edd4ab05 RP |
117 | /* The value is converted into deci-centigrade scale */ |
118 | /* Units of LSB = 1 / 256 degree Celsius */ | |
119 | *temp = *temp * 10 / 256; | |
120 | return 0; | |
121 | } | |
122 | ||
a9df22c0 HG |
123 | static int max17042_get_status(struct max17042_chip *chip, int *status) |
124 | { | |
125 | int ret, charge_full, charge_now; | |
6e5ab19d HG |
126 | int avg_current; |
127 | u32 data; | |
a9df22c0 HG |
128 | |
129 | ret = power_supply_am_i_supplied(chip->battery); | |
130 | if (ret < 0) { | |
131 | *status = POWER_SUPPLY_STATUS_UNKNOWN; | |
132 | return 0; | |
133 | } | |
134 | if (ret == 0) { | |
135 | *status = POWER_SUPPLY_STATUS_DISCHARGING; | |
136 | return 0; | |
137 | } | |
138 | ||
139 | /* | |
140 | * The MAX170xx has builtin end-of-charge detection and will update | |
141 | * FullCAP to match RepCap when it detects end of charging. | |
142 | * | |
143 | * When this cycle the battery gets charged to a higher (calculated) | |
144 | * capacity then the previous cycle then FullCAP will get updated | |
145 | * contineously once end-of-charge detection kicks in, so allow the | |
146 | * 2 to differ a bit. | |
147 | */ | |
148 | ||
149 | ret = regmap_read(chip->regmap, MAX17042_FullCAP, &charge_full); | |
150 | if (ret < 0) | |
151 | return ret; | |
152 | ||
153 | ret = regmap_read(chip->regmap, MAX17042_RepCap, &charge_now); | |
154 | if (ret < 0) | |
155 | return ret; | |
156 | ||
6e5ab19d | 157 | if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD) { |
a9df22c0 | 158 | *status = POWER_SUPPLY_STATUS_FULL; |
6e5ab19d HG |
159 | return 0; |
160 | } | |
161 | ||
162 | /* | |
163 | * Even though we are supplied, we may still be discharging if the | |
164 | * supply is e.g. only delivering 5V 0.5A. Check current if available. | |
165 | */ | |
166 | if (!chip->pdata->enable_current_sense) { | |
a9df22c0 | 167 | *status = POWER_SUPPLY_STATUS_CHARGING; |
6e5ab19d HG |
168 | return 0; |
169 | } | |
170 | ||
171 | ret = regmap_read(chip->regmap, MAX17042_AvgCurrent, &data); | |
172 | if (ret < 0) | |
173 | return ret; | |
174 | ||
175 | avg_current = sign_extend32(data, 15); | |
176 | avg_current *= 1562500 / chip->pdata->r_sns; | |
177 | ||
178 | if (avg_current > 0) | |
179 | *status = POWER_SUPPLY_STATUS_CHARGING; | |
180 | else | |
181 | *status = POWER_SUPPLY_STATUS_DISCHARGING; | |
a9df22c0 HG |
182 | |
183 | return 0; | |
184 | } | |
185 | ||
edd4ab05 RP |
186 | static int max17042_get_battery_health(struct max17042_chip *chip, int *health) |
187 | { | |
188 | int temp, vavg, vbatt, ret; | |
189 | u32 val; | |
190 | ||
191 | ret = regmap_read(chip->regmap, MAX17042_AvgVCELL, &val); | |
192 | if (ret < 0) | |
193 | goto health_error; | |
194 | ||
195 | /* bits [0-3] unused */ | |
196 | vavg = val * 625 / 8; | |
197 | /* Convert to millivolts */ | |
198 | vavg /= 1000; | |
199 | ||
200 | ret = regmap_read(chip->regmap, MAX17042_VCELL, &val); | |
201 | if (ret < 0) | |
202 | goto health_error; | |
203 | ||
204 | /* bits [0-3] unused */ | |
205 | vbatt = val * 625 / 8; | |
206 | /* Convert to millivolts */ | |
207 | vbatt /= 1000; | |
208 | ||
209 | if (vavg < chip->pdata->vmin) { | |
210 | *health = POWER_SUPPLY_HEALTH_DEAD; | |
211 | goto out; | |
212 | } | |
213 | ||
214 | if (vbatt > chip->pdata->vmax + MAX17042_VMAX_TOLERANCE) { | |
215 | *health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | |
216 | goto out; | |
217 | } | |
218 | ||
219 | ret = max17042_get_temperature(chip, &temp); | |
220 | if (ret < 0) | |
221 | goto health_error; | |
222 | ||
91736213 | 223 | if (temp < chip->pdata->temp_min) { |
edd4ab05 RP |
224 | *health = POWER_SUPPLY_HEALTH_COLD; |
225 | goto out; | |
226 | } | |
227 | ||
91736213 | 228 | if (temp > chip->pdata->temp_max) { |
edd4ab05 RP |
229 | *health = POWER_SUPPLY_HEALTH_OVERHEAT; |
230 | goto out; | |
231 | } | |
232 | ||
233 | *health = POWER_SUPPLY_HEALTH_GOOD; | |
234 | ||
235 | out: | |
236 | return 0; | |
237 | ||
238 | health_error: | |
239 | return ret; | |
240 | } | |
241 | ||
359ab9f5 MH |
242 | static int max17042_get_property(struct power_supply *psy, |
243 | enum power_supply_property psp, | |
244 | union power_supply_propval *val) | |
245 | { | |
297d716f | 246 | struct max17042_chip *chip = power_supply_get_drvdata(psy); |
39e7213e | 247 | struct regmap *map = chip->regmap; |
60a1f6e4 | 248 | int ret; |
39e7213e | 249 | u32 data; |
d7d15fc6 | 250 | u64 data64; |
359ab9f5 | 251 | |
f3a71a6e RP |
252 | if (!chip->init_complete) |
253 | return -EAGAIN; | |
254 | ||
359ab9f5 | 255 | switch (psp) { |
a9df22c0 HG |
256 | case POWER_SUPPLY_PROP_STATUS: |
257 | ret = max17042_get_status(chip, &val->intval); | |
258 | if (ret < 0) | |
259 | return ret; | |
260 | break; | |
086ef502 | 261 | case POWER_SUPPLY_PROP_PRESENT: |
39e7213e | 262 | ret = regmap_read(map, MAX17042_STATUS, &data); |
60a1f6e4 RP |
263 | if (ret < 0) |
264 | return ret; | |
265 | ||
39e7213e | 266 | if (data & MAX17042_STATUS_BattAbsent) |
086ef502 DK |
267 | val->intval = 0; |
268 | else | |
269 | val->intval = 1; | |
270 | break; | |
ef7fcdae HG |
271 | case POWER_SUPPLY_PROP_TECHNOLOGY: |
272 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; | |
273 | break; | |
086ef502 | 274 | case POWER_SUPPLY_PROP_CYCLE_COUNT: |
39e7213e | 275 | ret = regmap_read(map, MAX17042_Cycles, &data); |
60a1f6e4 RP |
276 | if (ret < 0) |
277 | return ret; | |
278 | ||
39e7213e | 279 | val->intval = data; |
086ef502 DK |
280 | break; |
281 | case POWER_SUPPLY_PROP_VOLTAGE_MAX: | |
39e7213e | 282 | ret = regmap_read(map, MAX17042_MinMaxVolt, &data); |
60a1f6e4 RP |
283 | if (ret < 0) |
284 | return ret; | |
285 | ||
39e7213e | 286 | val->intval = data >> 8; |
086ef502 DK |
287 | val->intval *= 20000; /* Units of LSB = 20mV */ |
288 | break; | |
7bfc9397 HG |
289 | case POWER_SUPPLY_PROP_VOLTAGE_MIN: |
290 | ret = regmap_read(map, MAX17042_MinMaxVolt, &data); | |
291 | if (ret < 0) | |
292 | return ret; | |
293 | ||
294 | val->intval = (data & 0xff) * 20000; /* Units of 20mV */ | |
295 | break; | |
086ef502 | 296 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: |
709c2c70 | 297 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) |
39e7213e | 298 | ret = regmap_read(map, MAX17042_V_empty, &data); |
9a8422d2 | 299 | else |
39e7213e | 300 | ret = regmap_read(map, MAX17047_V_empty, &data); |
60a1f6e4 RP |
301 | if (ret < 0) |
302 | return ret; | |
303 | ||
39e7213e | 304 | val->intval = data >> 7; |
086ef502 DK |
305 | val->intval *= 10000; /* Units of LSB = 10mV */ |
306 | break; | |
359ab9f5 | 307 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
39e7213e | 308 | ret = regmap_read(map, MAX17042_VCELL, &data); |
60a1f6e4 RP |
309 | if (ret < 0) |
310 | return ret; | |
311 | ||
39e7213e | 312 | val->intval = data * 625 / 8; |
359ab9f5 MH |
313 | break; |
314 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | |
39e7213e | 315 | ret = regmap_read(map, MAX17042_AvgVCELL, &data); |
60a1f6e4 RP |
316 | if (ret < 0) |
317 | return ret; | |
318 | ||
39e7213e | 319 | val->intval = data * 625 / 8; |
a2ebfe2f RP |
320 | break; |
321 | case POWER_SUPPLY_PROP_VOLTAGE_OCV: | |
39e7213e | 322 | ret = regmap_read(map, MAX17042_OCVInternal, &data); |
a2ebfe2f RP |
323 | if (ret < 0) |
324 | return ret; | |
325 | ||
39e7213e | 326 | val->intval = data * 625 / 8; |
359ab9f5 MH |
327 | break; |
328 | case POWER_SUPPLY_PROP_CAPACITY: | |
39e7213e | 329 | ret = regmap_read(map, MAX17042_RepSOC, &data); |
60a1f6e4 RP |
330 | if (ret < 0) |
331 | return ret; | |
332 | ||
39e7213e | 333 | val->intval = data >> 8; |
359ab9f5 | 334 | break; |
2e015412 HG |
335 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: |
336 | ret = regmap_read(map, MAX17042_DesignCap, &data); | |
337 | if (ret < 0) | |
338 | return ret; | |
339 | ||
340 | data64 = data * 5000000ll; | |
341 | do_div(data64, chip->pdata->r_sns); | |
342 | val->intval = data64; | |
343 | break; | |
086ef502 | 344 | case POWER_SUPPLY_PROP_CHARGE_FULL: |
39e7213e | 345 | ret = regmap_read(map, MAX17042_FullCAP, &data); |
60a1f6e4 RP |
346 | if (ret < 0) |
347 | return ret; | |
348 | ||
6d6b61ea HG |
349 | data64 = data * 5000000ll; |
350 | do_div(data64, chip->pdata->r_sns); | |
351 | val->intval = data64; | |
352 | break; | |
353 | case POWER_SUPPLY_PROP_CHARGE_NOW: | |
354 | ret = regmap_read(map, MAX17042_RepCap, &data); | |
355 | if (ret < 0) | |
356 | return ret; | |
357 | ||
d7d15fc6 HG |
358 | data64 = data * 5000000ll; |
359 | do_div(data64, chip->pdata->r_sns); | |
360 | val->intval = data64; | |
5fc55bc8 RP |
361 | break; |
362 | case POWER_SUPPLY_PROP_CHARGE_COUNTER: | |
39e7213e | 363 | ret = regmap_read(map, MAX17042_QH, &data); |
5fc55bc8 RP |
364 | if (ret < 0) |
365 | return ret; | |
366 | ||
39e7213e | 367 | val->intval = data * 1000 / 2; |
086ef502 DK |
368 | break; |
369 | case POWER_SUPPLY_PROP_TEMP: | |
edd4ab05 RP |
370 | ret = max17042_get_temperature(chip, &val->intval); |
371 | if (ret < 0) | |
372 | return ret; | |
373 | break; | |
374 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: | |
375 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | |
376 | if (ret < 0) | |
377 | return ret; | |
378 | /* LSB is Alert Minimum. In deci-centigrade */ | |
2814913c | 379 | val->intval = sign_extend32(data & 0xff, 7) * 10; |
edd4ab05 RP |
380 | break; |
381 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
382 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | |
383 | if (ret < 0) | |
384 | return ret; | |
385 | /* MSB is Alert Maximum. In deci-centigrade */ | |
2814913c | 386 | val->intval = sign_extend32(data >> 8, 7) * 10; |
edd4ab05 RP |
387 | break; |
388 | case POWER_SUPPLY_PROP_TEMP_MIN: | |
389 | val->intval = chip->pdata->temp_min; | |
390 | break; | |
391 | case POWER_SUPPLY_PROP_TEMP_MAX: | |
392 | val->intval = chip->pdata->temp_max; | |
393 | break; | |
394 | case POWER_SUPPLY_PROP_HEALTH: | |
395 | ret = max17042_get_battery_health(chip, &val->intval); | |
60a1f6e4 RP |
396 | if (ret < 0) |
397 | return ret; | |
086ef502 | 398 | break; |
adb69a3c HG |
399 | case POWER_SUPPLY_PROP_SCOPE: |
400 | val->intval = POWER_SUPPLY_SCOPE_SYSTEM; | |
401 | break; | |
086ef502 DK |
402 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
403 | if (chip->pdata->enable_current_sense) { | |
39e7213e | 404 | ret = regmap_read(map, MAX17042_Current, &data); |
60a1f6e4 RP |
405 | if (ret < 0) |
406 | return ret; | |
407 | ||
c67c0693 | 408 | val->intval = sign_extend32(data, 15); |
91d8b0d6 | 409 | val->intval *= 1562500 / chip->pdata->r_sns; |
086ef502 DK |
410 | } else { |
411 | return -EINVAL; | |
412 | } | |
413 | break; | |
414 | case POWER_SUPPLY_PROP_CURRENT_AVG: | |
415 | if (chip->pdata->enable_current_sense) { | |
39e7213e | 416 | ret = regmap_read(map, MAX17042_AvgCurrent, &data); |
60a1f6e4 RP |
417 | if (ret < 0) |
418 | return ret; | |
419 | ||
c67c0693 | 420 | val->intval = sign_extend32(data, 15); |
086ef502 DK |
421 | val->intval *= 1562500 / chip->pdata->r_sns; |
422 | } else { | |
423 | return -EINVAL; | |
424 | } | |
425 | break; | |
359ab9f5 MH |
426 | default: |
427 | return -EINVAL; | |
428 | } | |
429 | return 0; | |
430 | } | |
431 | ||
edd4ab05 RP |
432 | static int max17042_set_property(struct power_supply *psy, |
433 | enum power_supply_property psp, | |
434 | const union power_supply_propval *val) | |
435 | { | |
436 | struct max17042_chip *chip = power_supply_get_drvdata(psy); | |
437 | struct regmap *map = chip->regmap; | |
438 | int ret = 0; | |
439 | u32 data; | |
440 | int8_t temp; | |
441 | ||
442 | switch (psp) { | |
443 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: | |
444 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | |
445 | if (ret < 0) | |
446 | return ret; | |
447 | ||
448 | /* Input in deci-centigrade, convert to centigrade */ | |
449 | temp = val->intval / 10; | |
450 | /* force min < max */ | |
451 | if (temp >= (int8_t)(data >> 8)) | |
452 | temp = (int8_t)(data >> 8) - 1; | |
453 | /* Write both MAX and MIN ALERT */ | |
454 | data = (data & 0xff00) + temp; | |
455 | ret = regmap_write(map, MAX17042_TALRT_Th, data); | |
456 | break; | |
457 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
458 | ret = regmap_read(map, MAX17042_TALRT_Th, &data); | |
459 | if (ret < 0) | |
460 | return ret; | |
461 | ||
462 | /* Input in Deci-Centigrade, convert to centigrade */ | |
463 | temp = val->intval / 10; | |
464 | /* force max > min */ | |
465 | if (temp <= (int8_t)(data & 0xff)) | |
466 | temp = (int8_t)(data & 0xff) + 1; | |
467 | /* Write both MAX and MIN ALERT */ | |
468 | data = (data & 0xff) + (temp << 8); | |
469 | ret = regmap_write(map, MAX17042_TALRT_Th, data); | |
470 | break; | |
471 | default: | |
472 | ret = -EINVAL; | |
473 | } | |
474 | ||
475 | return ret; | |
476 | } | |
477 | ||
478 | static int max17042_property_is_writeable(struct power_supply *psy, | |
479 | enum power_supply_property psp) | |
480 | { | |
481 | int ret; | |
482 | ||
483 | switch (psp) { | |
484 | case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: | |
485 | case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: | |
486 | ret = 1; | |
487 | break; | |
488 | default: | |
489 | ret = 0; | |
490 | } | |
491 | ||
492 | return ret; | |
493 | } | |
494 | ||
dcdddda8 HG |
495 | static void max17042_external_power_changed(struct power_supply *psy) |
496 | { | |
497 | power_supply_changed(psy); | |
498 | } | |
499 | ||
39e7213e | 500 | static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value) |
f3a71a6e RP |
501 | { |
502 | int retries = 8; | |
503 | int ret; | |
39e7213e | 504 | u32 read_value; |
f3a71a6e RP |
505 | |
506 | do { | |
39e7213e JL |
507 | ret = regmap_write(map, reg, value); |
508 | regmap_read(map, reg, &read_value); | |
f3a71a6e RP |
509 | if (read_value != value) { |
510 | ret = -EIO; | |
511 | retries--; | |
512 | } | |
513 | } while (retries && read_value != value); | |
514 | ||
515 | if (ret < 0) | |
39e7213e | 516 | pr_err("%s: err %d\n", __func__, ret); |
f3a71a6e RP |
517 | |
518 | return ret; | |
519 | } | |
520 | ||
39e7213e JL |
521 | static inline void max17042_override_por(struct regmap *map, |
522 | u8 reg, u16 value) | |
f3a71a6e RP |
523 | { |
524 | if (value) | |
39e7213e | 525 | regmap_write(map, reg, value); |
f3a71a6e RP |
526 | } |
527 | ||
528 | static inline void max10742_unlock_model(struct max17042_chip *chip) | |
529 | { | |
39e7213e | 530 | struct regmap *map = chip->regmap; |
bbaeeaaf | 531 | |
39e7213e JL |
532 | regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1); |
533 | regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2); | |
f3a71a6e RP |
534 | } |
535 | ||
536 | static inline void max10742_lock_model(struct max17042_chip *chip) | |
537 | { | |
39e7213e JL |
538 | struct regmap *map = chip->regmap; |
539 | ||
540 | regmap_write(map, MAX17042_MLOCKReg1, MODEL_LOCK1); | |
541 | regmap_write(map, MAX17042_MLOCKReg2, MODEL_LOCK2); | |
f3a71a6e RP |
542 | } |
543 | ||
544 | static inline void max17042_write_model_data(struct max17042_chip *chip, | |
545 | u8 addr, int size) | |
546 | { | |
39e7213e | 547 | struct regmap *map = chip->regmap; |
f3a71a6e | 548 | int i; |
bbaeeaaf | 549 | |
f3a71a6e | 550 | for (i = 0; i < size; i++) |
39e7213e JL |
551 | regmap_write(map, addr + i, |
552 | chip->pdata->config_data->cell_char_tbl[i]); | |
f3a71a6e RP |
553 | } |
554 | ||
555 | static inline void max17042_read_model_data(struct max17042_chip *chip, | |
5381cfb6 | 556 | u8 addr, u16 *data, int size) |
f3a71a6e | 557 | { |
39e7213e | 558 | struct regmap *map = chip->regmap; |
f3a71a6e | 559 | int i; |
5381cfb6 | 560 | u32 tmp; |
f3a71a6e | 561 | |
5381cfb6 SVA |
562 | for (i = 0; i < size; i++) { |
563 | regmap_read(map, addr + i, &tmp); | |
564 | data[i] = (u16)tmp; | |
565 | } | |
f3a71a6e RP |
566 | } |
567 | ||
568 | static inline int max17042_model_data_compare(struct max17042_chip *chip, | |
569 | u16 *data1, u16 *data2, int size) | |
570 | { | |
571 | int i; | |
572 | ||
573 | if (memcmp(data1, data2, size)) { | |
574 | dev_err(&chip->client->dev, "%s compare failed\n", __func__); | |
575 | for (i = 0; i < size; i++) | |
576 | dev_info(&chip->client->dev, "0x%x, 0x%x", | |
577 | data1[i], data2[i]); | |
578 | dev_info(&chip->client->dev, "\n"); | |
579 | return -EINVAL; | |
580 | } | |
581 | return 0; | |
582 | } | |
583 | ||
584 | static int max17042_init_model(struct max17042_chip *chip) | |
585 | { | |
586 | int ret; | |
1ef3d8fb | 587 | int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); |
5381cfb6 | 588 | u16 *temp_data; |
f3a71a6e | 589 | |
1ef3d8fb | 590 | temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); |
f3a71a6e RP |
591 | if (!temp_data) |
592 | return -ENOMEM; | |
593 | ||
594 | max10742_unlock_model(chip); | |
595 | max17042_write_model_data(chip, MAX17042_MODELChrTbl, | |
596 | table_size); | |
597 | max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data, | |
598 | table_size); | |
599 | ||
600 | ret = max17042_model_data_compare( | |
601 | chip, | |
602 | chip->pdata->config_data->cell_char_tbl, | |
5381cfb6 | 603 | temp_data, |
f3a71a6e RP |
604 | table_size); |
605 | ||
606 | max10742_lock_model(chip); | |
607 | kfree(temp_data); | |
608 | ||
609 | return ret; | |
610 | } | |
611 | ||
612 | static int max17042_verify_model_lock(struct max17042_chip *chip) | |
613 | { | |
614 | int i; | |
1ef3d8fb | 615 | int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); |
5381cfb6 | 616 | u16 *temp_data; |
f3a71a6e RP |
617 | int ret = 0; |
618 | ||
1ef3d8fb | 619 | temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); |
f3a71a6e RP |
620 | if (!temp_data) |
621 | return -ENOMEM; | |
622 | ||
623 | max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data, | |
624 | table_size); | |
625 | for (i = 0; i < table_size; i++) | |
626 | if (temp_data[i]) | |
627 | ret = -EINVAL; | |
628 | ||
629 | kfree(temp_data); | |
630 | return ret; | |
631 | } | |
632 | ||
633 | static void max17042_write_config_regs(struct max17042_chip *chip) | |
634 | { | |
635 | struct max17042_config_data *config = chip->pdata->config_data; | |
39e7213e | 636 | struct regmap *map = chip->regmap; |
f3a71a6e | 637 | |
39e7213e JL |
638 | regmap_write(map, MAX17042_CONFIG, config->config); |
639 | regmap_write(map, MAX17042_LearnCFG, config->learn_cfg); | |
640 | regmap_write(map, MAX17042_FilterCFG, | |
f3a71a6e | 641 | config->filter_cfg); |
39e7213e | 642 | regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); |
709c2c70 BS |
643 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || |
644 | chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) | |
39e7213e | 645 | regmap_write(map, MAX17047_FullSOCThr, |
9a8422d2 | 646 | config->full_soc_thresh); |
f3a71a6e RP |
647 | } |
648 | ||
649 | static void max17042_write_custom_regs(struct max17042_chip *chip) | |
650 | { | |
651 | struct max17042_config_data *config = chip->pdata->config_data; | |
39e7213e | 652 | struct regmap *map = chip->regmap; |
f3a71a6e | 653 | |
39e7213e JL |
654 | max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0); |
655 | max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0); | |
656 | max17042_write_verify_reg(map, MAX17042_ICHGTerm, config->ichgt_term); | |
709c2c70 | 657 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) { |
39e7213e JL |
658 | regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco); |
659 | max17042_write_verify_reg(map, MAX17042_K_empty0, | |
9a8422d2 RP |
660 | config->kempty0); |
661 | } else { | |
39e7213e | 662 | max17042_write_verify_reg(map, MAX17047_QRTbl00, |
9a8422d2 | 663 | config->qrtbl00); |
39e7213e | 664 | max17042_write_verify_reg(map, MAX17047_QRTbl10, |
9a8422d2 | 665 | config->qrtbl10); |
39e7213e | 666 | max17042_write_verify_reg(map, MAX17047_QRTbl20, |
9a8422d2 | 667 | config->qrtbl20); |
39e7213e | 668 | max17042_write_verify_reg(map, MAX17047_QRTbl30, |
9a8422d2 RP |
669 | config->qrtbl30); |
670 | } | |
f3a71a6e RP |
671 | } |
672 | ||
673 | static void max17042_update_capacity_regs(struct max17042_chip *chip) | |
674 | { | |
675 | struct max17042_config_data *config = chip->pdata->config_data; | |
39e7213e | 676 | struct regmap *map = chip->regmap; |
f3a71a6e | 677 | |
39e7213e | 678 | max17042_write_verify_reg(map, MAX17042_FullCAP, |
f3a71a6e | 679 | config->fullcap); |
39e7213e JL |
680 | regmap_write(map, MAX17042_DesignCap, config->design_cap); |
681 | max17042_write_verify_reg(map, MAX17042_FullCAPNom, | |
f3a71a6e RP |
682 | config->fullcapnom); |
683 | } | |
684 | ||
685 | static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip) | |
686 | { | |
39e7213e JL |
687 | unsigned int vfSoc; |
688 | struct regmap *map = chip->regmap; | |
f3a71a6e | 689 | |
39e7213e JL |
690 | regmap_read(map, MAX17042_VFSOC, &vfSoc); |
691 | regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK); | |
692 | max17042_write_verify_reg(map, MAX17042_VFSOC0, vfSoc); | |
693 | regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_LOCK); | |
f3a71a6e RP |
694 | } |
695 | ||
696 | static void max17042_load_new_capacity_params(struct max17042_chip *chip) | |
697 | { | |
39e7213e | 698 | u32 full_cap0, rep_cap, dq_acc, vfSoc; |
f3a71a6e RP |
699 | u32 rem_cap; |
700 | ||
701 | struct max17042_config_data *config = chip->pdata->config_data; | |
39e7213e | 702 | struct regmap *map = chip->regmap; |
f3a71a6e | 703 | |
39e7213e JL |
704 | regmap_read(map, MAX17042_FullCAP0, &full_cap0); |
705 | regmap_read(map, MAX17042_VFSOC, &vfSoc); | |
f3a71a6e RP |
706 | |
707 | /* fg_vfSoc needs to shifted by 8 bits to get the | |
708 | * perc in 1% accuracy, to get the right rem_cap multiply | |
709 | * full_cap0, fg_vfSoc and devide by 100 | |
710 | */ | |
711 | rem_cap = ((vfSoc >> 8) * full_cap0) / 100; | |
39e7213e | 712 | max17042_write_verify_reg(map, MAX17042_RemCap, rem_cap); |
f3a71a6e | 713 | |
39e7213e JL |
714 | rep_cap = rem_cap; |
715 | max17042_write_verify_reg(map, MAX17042_RepCap, rep_cap); | |
f3a71a6e RP |
716 | |
717 | /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */ | |
718 | dq_acc = config->fullcap / dQ_ACC_DIV; | |
39e7213e JL |
719 | max17042_write_verify_reg(map, MAX17042_dQacc, dq_acc); |
720 | max17042_write_verify_reg(map, MAX17042_dPacc, dP_ACC_200); | |
f3a71a6e | 721 | |
39e7213e | 722 | max17042_write_verify_reg(map, MAX17042_FullCAP, |
f3a71a6e | 723 | config->fullcap); |
39e7213e | 724 | regmap_write(map, MAX17042_DesignCap, |
f3a71a6e | 725 | config->design_cap); |
39e7213e | 726 | max17042_write_verify_reg(map, MAX17042_FullCAPNom, |
f3a71a6e | 727 | config->fullcapnom); |
9a8422d2 | 728 | /* Update SOC register with new SOC */ |
39e7213e | 729 | regmap_write(map, MAX17042_RepSOC, vfSoc); |
f3a71a6e RP |
730 | } |
731 | ||
732 | /* | |
733 | * Block write all the override values coming from platform data. | |
734 | * This function MUST be called before the POR initialization proceedure | |
735 | * specified by maxim. | |
736 | */ | |
737 | static inline void max17042_override_por_values(struct max17042_chip *chip) | |
738 | { | |
39e7213e | 739 | struct regmap *map = chip->regmap; |
f3a71a6e RP |
740 | struct max17042_config_data *config = chip->pdata->config_data; |
741 | ||
39e7213e JL |
742 | max17042_override_por(map, MAX17042_TGAIN, config->tgain); |
743 | max17042_override_por(map, MAx17042_TOFF, config->toff); | |
744 | max17042_override_por(map, MAX17042_CGAIN, config->cgain); | |
745 | max17042_override_por(map, MAX17042_COFF, config->coff); | |
746 | ||
747 | max17042_override_por(map, MAX17042_VALRT_Th, config->valrt_thresh); | |
748 | max17042_override_por(map, MAX17042_TALRT_Th, config->talrt_thresh); | |
749 | max17042_override_por(map, MAX17042_SALRT_Th, | |
750 | config->soc_alrt_thresh); | |
751 | max17042_override_por(map, MAX17042_CONFIG, config->config); | |
752 | max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer); | |
753 | ||
754 | max17042_override_por(map, MAX17042_DesignCap, config->design_cap); | |
755 | max17042_override_por(map, MAX17042_ICHGTerm, config->ichgt_term); | |
756 | ||
757 | max17042_override_por(map, MAX17042_AtRate, config->at_rate); | |
758 | max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg); | |
759 | max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg); | |
760 | max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg); | |
761 | max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg); | |
762 | max17042_override_por(map, MAX17042_MaskSOC, config->masksoc); | |
763 | ||
764 | max17042_override_por(map, MAX17042_FullCAP, config->fullcap); | |
765 | max17042_override_por(map, MAX17042_FullCAPNom, config->fullcapnom); | |
709c2c70 | 766 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) |
39e7213e | 767 | max17042_override_por(map, MAX17042_SOC_empty, |
9a8422d2 | 768 | config->socempty); |
39e7213e JL |
769 | max17042_override_por(map, MAX17042_LAvg_empty, config->lavg_empty); |
770 | max17042_override_por(map, MAX17042_dQacc, config->dqacc); | |
771 | max17042_override_por(map, MAX17042_dPacc, config->dpacc); | |
f3a71a6e | 772 | |
709c2c70 | 773 | if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) |
39e7213e | 774 | max17042_override_por(map, MAX17042_V_empty, config->vempty); |
9a8422d2 | 775 | else |
39e7213e JL |
776 | max17042_override_por(map, MAX17047_V_empty, config->vempty); |
777 | max17042_override_por(map, MAX17042_TempNom, config->temp_nom); | |
778 | max17042_override_por(map, MAX17042_TempLim, config->temp_lim); | |
779 | max17042_override_por(map, MAX17042_FCTC, config->fctc); | |
780 | max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0); | |
781 | max17042_override_por(map, MAX17042_TempCo, config->tcompc0); | |
9a8422d2 | 782 | if (chip->chip_type) { |
39e7213e JL |
783 | max17042_override_por(map, MAX17042_EmptyTempCo, |
784 | config->empty_tempco); | |
785 | max17042_override_por(map, MAX17042_K_empty0, | |
786 | config->kempty0); | |
9a8422d2 | 787 | } |
f3a71a6e RP |
788 | } |
789 | ||
790 | static int max17042_init_chip(struct max17042_chip *chip) | |
791 | { | |
39e7213e | 792 | struct regmap *map = chip->regmap; |
f3a71a6e | 793 | int ret; |
f3a71a6e RP |
794 | |
795 | max17042_override_por_values(chip); | |
796 | /* After Power up, the MAX17042 requires 500mS in order | |
797 | * to perform signal debouncing and initial SOC reporting | |
798 | */ | |
799 | msleep(500); | |
800 | ||
801 | /* Initialize configaration */ | |
802 | max17042_write_config_regs(chip); | |
803 | ||
804 | /* write cell characterization data */ | |
805 | ret = max17042_init_model(chip); | |
806 | if (ret) { | |
807 | dev_err(&chip->client->dev, "%s init failed\n", | |
808 | __func__); | |
809 | return -EIO; | |
810 | } | |
a879f19f AC |
811 | |
812 | ret = max17042_verify_model_lock(chip); | |
f3a71a6e RP |
813 | if (ret) { |
814 | dev_err(&chip->client->dev, "%s lock verify failed\n", | |
815 | __func__); | |
816 | return -EIO; | |
817 | } | |
818 | /* write custom parameters */ | |
819 | max17042_write_custom_regs(chip); | |
820 | ||
821 | /* update capacity params */ | |
822 | max17042_update_capacity_regs(chip); | |
823 | ||
824 | /* delay must be atleast 350mS to allow VFSOC | |
825 | * to be calculated from the new configuration | |
826 | */ | |
827 | msleep(350); | |
828 | ||
829 | /* reset vfsoc0 reg */ | |
830 | max17042_reset_vfsoc0_reg(chip); | |
831 | ||
832 | /* load new capacity params */ | |
833 | max17042_load_new_capacity_params(chip); | |
834 | ||
835 | /* Init complete, Clear the POR bit */ | |
bc352686 | 836 | regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0); |
f3a71a6e RP |
837 | return 0; |
838 | } | |
839 | ||
e5f3872d RP |
840 | static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off) |
841 | { | |
39e7213e JL |
842 | struct regmap *map = chip->regmap; |
843 | u32 soc, soc_tr; | |
e5f3872d RP |
844 | |
845 | /* program interrupt thesholds such that we should | |
846 | * get interrupt for every 'off' perc change in the soc | |
847 | */ | |
39e7213e JL |
848 | regmap_read(map, MAX17042_RepSOC, &soc); |
849 | soc >>= 8; | |
e5f3872d RP |
850 | soc_tr = (soc + off) << 8; |
851 | soc_tr |= (soc - off); | |
39e7213e | 852 | regmap_write(map, MAX17042_SALRT_Th, soc_tr); |
e5f3872d RP |
853 | } |
854 | ||
e5f3872d RP |
855 | static irqreturn_t max17042_thread_handler(int id, void *dev) |
856 | { | |
857 | struct max17042_chip *chip = dev; | |
39e7213e | 858 | u32 val; |
e5f3872d | 859 | |
39e7213e | 860 | regmap_read(chip->regmap, MAX17042_STATUS, &val); |
5cdd4d7f RP |
861 | if ((val & STATUS_INTR_SOCMIN_BIT) || |
862 | (val & STATUS_INTR_SOCMAX_BIT)) { | |
e5f3872d RP |
863 | dev_info(&chip->client->dev, "SOC threshold INTR\n"); |
864 | max17042_set_soc_threshold(chip, 1); | |
865 | } | |
866 | ||
297d716f | 867 | power_supply_changed(chip->battery); |
e5f3872d RP |
868 | return IRQ_HANDLED; |
869 | } | |
f3a71a6e RP |
870 | |
871 | static void max17042_init_worker(struct work_struct *work) | |
872 | { | |
873 | struct max17042_chip *chip = container_of(work, | |
874 | struct max17042_chip, work); | |
875 | int ret; | |
876 | ||
877 | /* Initialize registers according to values from the platform data */ | |
878 | if (chip->pdata->enable_por_init && chip->pdata->config_data) { | |
879 | ret = max17042_init_chip(chip); | |
880 | if (ret) | |
881 | return; | |
882 | } | |
883 | ||
884 | chip->init_complete = 1; | |
885 | } | |
886 | ||
3832246d KL |
887 | #ifdef CONFIG_OF |
888 | static struct max17042_platform_data * | |
2d7e6a83 | 889 | max17042_get_of_pdata(struct max17042_chip *chip) |
3832246d | 890 | { |
91736213 | 891 | struct device *dev = &chip->client->dev; |
3832246d KL |
892 | struct device_node *np = dev->of_node; |
893 | u32 prop; | |
894 | struct max17042_platform_data *pdata; | |
895 | ||
3832246d KL |
896 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); |
897 | if (!pdata) | |
898 | return NULL; | |
899 | ||
900 | /* | |
901 | * Require current sense resistor value to be specified for | |
902 | * current-sense functionality to be enabled at all. | |
903 | */ | |
904 | if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) { | |
905 | pdata->r_sns = prop; | |
906 | pdata->enable_current_sense = true; | |
907 | } | |
908 | ||
a6e6b63e KK |
909 | if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min)) |
910 | pdata->temp_min = INT_MIN; | |
911 | if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max)) | |
912 | pdata->temp_max = INT_MAX; | |
913 | if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin)) | |
914 | pdata->vmin = INT_MIN; | |
915 | if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax)) | |
916 | pdata->vmax = INT_MAX; | |
917 | ||
3832246d KL |
918 | return pdata; |
919 | } | |
2d7e6a83 HG |
920 | #endif |
921 | ||
91736213 HG |
922 | static struct max17042_reg_data max17047_default_pdata_init_regs[] = { |
923 | /* | |
924 | * Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection | |
925 | * when the voltage FG reports 95%, as recommended in the datasheet. | |
926 | */ | |
927 | { MAX17047_FullSOCThr, MAX17042_BATTERY_FULL << 8 }, | |
928 | }; | |
929 | ||
3832246d | 930 | static struct max17042_platform_data * |
2d7e6a83 | 931 | max17042_get_default_pdata(struct max17042_chip *chip) |
3832246d | 932 | { |
91736213 HG |
933 | struct device *dev = &chip->client->dev; |
934 | struct max17042_platform_data *pdata; | |
935 | int ret, misc_cfg; | |
936 | ||
91736213 HG |
937 | /* |
938 | * The MAX17047 gets used on x86 where we might not have pdata, assume | |
939 | * the firmware will already have initialized the fuel-gauge and provide | |
940 | * default values for the non init bits to make things work. | |
941 | */ | |
942 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | |
943 | if (!pdata) | |
944 | return pdata; | |
945 | ||
946 | if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17042) { | |
947 | pdata->init_data = max17047_default_pdata_init_regs; | |
948 | pdata->num_init_data = | |
949 | ARRAY_SIZE(max17047_default_pdata_init_regs); | |
950 | } | |
951 | ||
952 | ret = regmap_read(chip->regmap, MAX17042_MiscCFG, &misc_cfg); | |
953 | if (ret < 0) | |
954 | return NULL; | |
955 | ||
956 | /* If bits 0-1 are set to 3 then only Voltage readings are used */ | |
957 | if ((misc_cfg & 0x3) == 0x3) | |
958 | pdata->enable_current_sense = false; | |
959 | else | |
960 | pdata->enable_current_sense = true; | |
961 | ||
962 | pdata->vmin = MAX17042_DEFAULT_VMIN; | |
963 | pdata->vmax = MAX17042_DEFAULT_VMAX; | |
964 | pdata->temp_min = MAX17042_DEFAULT_TEMP_MIN; | |
965 | pdata->temp_max = MAX17042_DEFAULT_TEMP_MAX; | |
966 | ||
967 | return pdata; | |
3832246d | 968 | } |
2d7e6a83 HG |
969 | |
970 | static struct max17042_platform_data * | |
971 | max17042_get_pdata(struct max17042_chip *chip) | |
972 | { | |
973 | struct device *dev = &chip->client->dev; | |
974 | ||
975 | #ifdef CONFIG_OF | |
976 | if (dev->of_node) | |
977 | return max17042_get_of_pdata(chip); | |
3832246d | 978 | #endif |
2d7e6a83 HG |
979 | if (dev->platform_data) |
980 | return dev->platform_data; | |
981 | ||
982 | return max17042_get_default_pdata(chip); | |
983 | } | |
3832246d | 984 | |
e0291285 | 985 | static const struct regmap_config max17042_regmap_config = { |
39e7213e JL |
986 | .reg_bits = 8, |
987 | .val_bits = 16, | |
988 | .val_format_endian = REGMAP_ENDIAN_NATIVE, | |
989 | }; | |
990 | ||
297d716f KK |
991 | static const struct power_supply_desc max17042_psy_desc = { |
992 | .name = "max170xx_battery", | |
993 | .type = POWER_SUPPLY_TYPE_BATTERY, | |
994 | .get_property = max17042_get_property, | |
edd4ab05 RP |
995 | .set_property = max17042_set_property, |
996 | .property_is_writeable = max17042_property_is_writeable, | |
dcdddda8 | 997 | .external_power_changed = max17042_external_power_changed, |
297d716f KK |
998 | .properties = max17042_battery_props, |
999 | .num_properties = ARRAY_SIZE(max17042_battery_props), | |
1000 | }; | |
1001 | ||
1002 | static const struct power_supply_desc max17042_no_current_sense_psy_desc = { | |
1003 | .name = "max170xx_battery", | |
1004 | .type = POWER_SUPPLY_TYPE_BATTERY, | |
1005 | .get_property = max17042_get_property, | |
edd4ab05 RP |
1006 | .set_property = max17042_set_property, |
1007 | .property_is_writeable = max17042_property_is_writeable, | |
297d716f KK |
1008 | .properties = max17042_battery_props, |
1009 | .num_properties = ARRAY_SIZE(max17042_battery_props) - 2, | |
1010 | }; | |
1011 | ||
c8afa640 | 1012 | static int max17042_probe(struct i2c_client *client, |
359ab9f5 MH |
1013 | const struct i2c_device_id *id) |
1014 | { | |
1015 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | |
297d716f KK |
1016 | const struct power_supply_desc *max17042_desc = &max17042_psy_desc; |
1017 | struct power_supply_config psy_cfg = {}; | |
4f1e0cb7 | 1018 | const struct acpi_device_id *acpi_id = NULL; |
e2116202 | 1019 | struct device *dev = &client->dev; |
359ab9f5 MH |
1020 | struct max17042_chip *chip; |
1021 | int ret; | |
39e7213e JL |
1022 | int i; |
1023 | u32 val; | |
359ab9f5 MH |
1024 | |
1025 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) | |
1026 | return -EIO; | |
1027 | ||
2f3b4342 | 1028 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); |
359ab9f5 MH |
1029 | if (!chip) |
1030 | return -ENOMEM; | |
1031 | ||
1032 | chip->client = client; | |
e2116202 HG |
1033 | if (id) { |
1034 | chip->chip_type = id->driver_data; | |
1035 | } else { | |
1036 | acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); | |
1037 | if (!acpi_id) | |
1038 | return -ENODEV; | |
1039 | ||
1040 | chip->chip_type = acpi_id->driver_data; | |
1041 | } | |
39e7213e JL |
1042 | chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); |
1043 | if (IS_ERR(chip->regmap)) { | |
1044 | dev_err(&client->dev, "Failed to initialize regmap\n"); | |
1045 | return -EINVAL; | |
1046 | } | |
1047 | ||
91736213 | 1048 | chip->pdata = max17042_get_pdata(chip); |
3832246d KL |
1049 | if (!chip->pdata) { |
1050 | dev_err(&client->dev, "no platform data provided\n"); | |
1051 | return -EINVAL; | |
1052 | } | |
359ab9f5 MH |
1053 | |
1054 | i2c_set_clientdata(client, chip); | |
297d716f | 1055 | psy_cfg.drv_data = chip; |
66ec32fc | 1056 | psy_cfg.of_node = dev->of_node; |
359ab9f5 | 1057 | |
086ef502 DK |
1058 | /* When current is not measured, |
1059 | * CURRENT_NOW and CURRENT_AVG properties should be invisible. */ | |
1060 | if (!chip->pdata->enable_current_sense) | |
297d716f | 1061 | max17042_desc = &max17042_no_current_sense_psy_desc; |
086ef502 | 1062 | |
4cfa892c PR |
1063 | if (chip->pdata->r_sns == 0) |
1064 | chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; | |
1065 | ||
086ef502 | 1066 | if (chip->pdata->init_data) |
39e7213e JL |
1067 | for (i = 0; i < chip->pdata->num_init_data; i++) |
1068 | regmap_write(chip->regmap, | |
1069 | chip->pdata->init_data[i].addr, | |
1070 | chip->pdata->init_data[i].data); | |
086ef502 | 1071 | |
359ab9f5 | 1072 | if (!chip->pdata->enable_current_sense) { |
39e7213e JL |
1073 | regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000); |
1074 | regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003); | |
1075 | regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007); | |
359ab9f5 MH |
1076 | } |
1077 | ||
50bddb99 VT |
1078 | chip->battery = devm_power_supply_register(&client->dev, max17042_desc, |
1079 | &psy_cfg); | |
297d716f | 1080 | if (IS_ERR(chip->battery)) { |
243e3527 | 1081 | dev_err(&client->dev, "failed: power supply register\n"); |
297d716f | 1082 | return PTR_ERR(chip->battery); |
243e3527 RP |
1083 | } |
1084 | ||
e5f3872d | 1085 | if (client->irq) { |
a865a155 HG |
1086 | unsigned int flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; |
1087 | ||
1088 | /* | |
1089 | * On ACPI systems the IRQ may be handled by ACPI-event code, | |
1090 | * so we need to share (if the ACPI code is willing to share). | |
1091 | */ | |
1092 | if (acpi_id) | |
1093 | flags |= IRQF_SHARED | IRQF_PROBE_SHARED; | |
1094 | ||
50bddb99 VT |
1095 | ret = devm_request_threaded_irq(&client->dev, client->irq, |
1096 | NULL, | |
a865a155 | 1097 | max17042_thread_handler, flags, |
50bddb99 VT |
1098 | chip->battery->desc->name, |
1099 | chip); | |
e5f3872d | 1100 | if (!ret) { |
bc352686 KK |
1101 | regmap_update_bits(chip->regmap, MAX17042_CONFIG, |
1102 | CONFIG_ALRT_BIT_ENBL, | |
1103 | CONFIG_ALRT_BIT_ENBL); | |
e5f3872d | 1104 | max17042_set_soc_threshold(chip, 1); |
e5ba50bc RP |
1105 | } else { |
1106 | client->irq = 0; | |
a865a155 HG |
1107 | if (ret != -EBUSY) |
1108 | dev_err(&client->dev, "Failed to get IRQ\n"); | |
e5ba50bc | 1109 | } |
e5f3872d | 1110 | } |
a865a155 HG |
1111 | /* Not able to update the charge threshold when exceeded? -> disable */ |
1112 | if (!client->irq) | |
1113 | regmap_write(chip->regmap, MAX17042_SALRT_Th, 0xff00); | |
e5f3872d | 1114 | |
39e7213e JL |
1115 | regmap_read(chip->regmap, MAX17042_STATUS, &val); |
1116 | if (val & STATUS_POR_BIT) { | |
f3a71a6e RP |
1117 | INIT_WORK(&chip->work, max17042_init_worker); |
1118 | schedule_work(&chip->work); | |
1119 | } else { | |
1120 | chip->init_complete = 1; | |
1121 | } | |
1122 | ||
243e3527 | 1123 | return 0; |
359ab9f5 MH |
1124 | } |
1125 | ||
3d23c7f4 | 1126 | #ifdef CONFIG_PM_SLEEP |
48bc1774 RP |
1127 | static int max17042_suspend(struct device *dev) |
1128 | { | |
1129 | struct max17042_chip *chip = dev_get_drvdata(dev); | |
1130 | ||
48e41c70 AV |
1131 | /* |
1132 | * disable the irq and enable irq_wake | |
48bc1774 RP |
1133 | * capability to the interrupt line. |
1134 | */ | |
1135 | if (chip->client->irq) { | |
1136 | disable_irq(chip->client->irq); | |
1137 | enable_irq_wake(chip->client->irq); | |
1138 | } | |
1139 | ||
1140 | return 0; | |
1141 | } | |
1142 | ||
1143 | static int max17042_resume(struct device *dev) | |
1144 | { | |
1145 | struct max17042_chip *chip = dev_get_drvdata(dev); | |
1146 | ||
1147 | if (chip->client->irq) { | |
1148 | disable_irq_wake(chip->client->irq); | |
1149 | enable_irq(chip->client->irq); | |
1150 | /* re-program the SOC thresholds to 1% change */ | |
1151 | max17042_set_soc_threshold(chip, 1); | |
1152 | } | |
1153 | ||
1154 | return 0; | |
1155 | } | |
48bc1774 RP |
1156 | #endif |
1157 | ||
3d23c7f4 MB |
1158 | static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend, |
1159 | max17042_resume); | |
1160 | ||
e2116202 HG |
1161 | #ifdef CONFIG_ACPI |
1162 | static const struct acpi_device_id max17042_acpi_match[] = { | |
1163 | { "MAX17047", MAXIM_DEVICE_TYPE_MAX17047 }, | |
1164 | { } | |
1165 | }; | |
1166 | MODULE_DEVICE_TABLE(acpi, max17042_acpi_match); | |
1167 | #endif | |
1168 | ||
3832246d KL |
1169 | #ifdef CONFIG_OF |
1170 | static const struct of_device_id max17042_dt_match[] = { | |
1171 | { .compatible = "maxim,max17042" }, | |
9a8422d2 RP |
1172 | { .compatible = "maxim,max17047" }, |
1173 | { .compatible = "maxim,max17050" }, | |
3832246d KL |
1174 | { }, |
1175 | }; | |
1176 | MODULE_DEVICE_TABLE(of, max17042_dt_match); | |
1177 | #endif | |
1178 | ||
359ab9f5 | 1179 | static const struct i2c_device_id max17042_id[] = { |
709c2c70 BS |
1180 | { "max17042", MAXIM_DEVICE_TYPE_MAX17042 }, |
1181 | { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, | |
1182 | { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, | |
359ab9f5 MH |
1183 | { } |
1184 | }; | |
1185 | MODULE_DEVICE_TABLE(i2c, max17042_id); | |
1186 | ||
1187 | static struct i2c_driver max17042_i2c_driver = { | |
1188 | .driver = { | |
1189 | .name = "max17042", | |
e2116202 | 1190 | .acpi_match_table = ACPI_PTR(max17042_acpi_match), |
3832246d | 1191 | .of_match_table = of_match_ptr(max17042_dt_match), |
3d23c7f4 | 1192 | .pm = &max17042_pm_ops, |
359ab9f5 MH |
1193 | }, |
1194 | .probe = max17042_probe, | |
359ab9f5 MH |
1195 | .id_table = max17042_id, |
1196 | }; | |
5ff92e7a | 1197 | module_i2c_driver(max17042_i2c_driver); |
359ab9f5 MH |
1198 | |
1199 | MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); | |
1200 | MODULE_DESCRIPTION("MAX17042 Fuel Gauge"); | |
1201 | MODULE_LICENSE("GPL"); |