1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Fuel gauge driver for CellWise 2013 / 2015 |
4 | * |
5 | * Copyright (C) 2012, RockChip |
6 | * Copyright (C) 2020, Tobias Schramm |
7 | * |
8 | * Authors: xuhuicong <xhc@rock-chips.com> |
9 | * Authors: Tobias Schramm <t.schramm@manjaro.org> |
10 | */ |
11 | |
12 | #include <linux/bits.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/gfp.h> |
16 | #include <linux/gpio/consumer.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/module.h> |
19 | #include <linux/power_supply.h> |
20 | #include <linux/property.h> |
21 | #include <linux/regmap.h> |
22 | #include <linux/time.h> |
23 | #include <linux/workqueue.h> |
24 | #include <linux/devm-helpers.h> |
25 | |
26 | #define CW2015_SIZE_BATINFO 64 |
27 | |
28 | #define CW2015_RESET_TRIES 5 |
29 | |
30 | #define CW2015_REG_VERSION 0x00 |
31 | #define CW2015_REG_VCELL 0x02 |
32 | #define CW2015_REG_SOC 0x04 |
33 | #define CW2015_REG_RRT_ALERT 0x06 |
34 | #define CW2015_REG_CONFIG 0x08 |
35 | #define CW2015_REG_MODE 0x0A |
36 | #define CW2015_REG_BATINFO 0x10 |
37 | |
38 | #define CW2015_MODE_SLEEP_MASK GENMASK(7, 6) |
39 | #define CW2015_MODE_SLEEP (0x03 << 6) |
40 | #define CW2015_MODE_NORMAL (0x00 << 6) |
41 | #define CW2015_MODE_QUICK_START (0x03 << 4) |
42 | #define CW2015_MODE_RESTART (0x0f << 0) |
43 | |
44 | #define CW2015_CONFIG_UPDATE_FLG (0x01 << 1) |
45 | #define CW2015_ATHD(x) ((x) << 3) |
46 | #define CW2015_MASK_ATHD GENMASK(7, 3) |
47 | #define CW2015_MASK_SOC GENMASK(12, 0) |
48 | |
49 | /* reset gauge of no valid state of charge could be polled for 40s */ |
50 | #define CW2015_BAT_SOC_ERROR_MS (40 * MSEC_PER_SEC) |
51 | /* reset gauge if state of charge stuck for half an hour during charging */ |
52 | #define CW2015_BAT_CHARGING_STUCK_MS (1800 * MSEC_PER_SEC) |
53 | |
54 | /* poll interval from CellWise GPL Android driver example */ |
55 | #define CW2015_DEFAULT_POLL_INTERVAL_MS 8000 |
56 | |
57 | #define CW2015_AVERAGING_SAMPLES 3 |
58 | |
59 | struct cw_battery { |
60 | struct device *dev; |
61 | struct workqueue_struct *battery_workqueue; |
62 | struct delayed_work battery_delay_work; |
63 | struct regmap *regmap; |
64 | struct power_supply *rk_bat; |
65 | struct power_supply_battery_info *battery; |
66 | u8 *bat_profile; |
67 | |
68 | bool charger_attached; |
69 | bool battery_changed; |
70 | |
71 | int soc; |
72 | int voltage_mv; |
73 | int status; |
74 | int time_to_empty; |
75 | int charge_count; |
76 | |
77 | u32 poll_interval_ms; |
78 | u8 alert_level; |
79 | |
80 | unsigned int read_errors; |
81 | unsigned int charge_stuck_cnt; |
82 | }; |
83 | |
84 | static int cw_read_word(struct cw_battery *cw_bat, u8 reg, u16 *val) |
85 | { |
86 | __be16 value; |
87 | int ret; |
88 | |
89 | ret = regmap_bulk_read(map: cw_bat->regmap, reg, val: &value, val_count: sizeof(value)); |
90 | if (ret) |
91 | return ret; |
92 | |
93 | *val = be16_to_cpu(value); |
94 | return 0; |
95 | } |
96 | |
97 | static int cw_update_profile(struct cw_battery *cw_bat) |
98 | { |
99 | int ret; |
100 | unsigned int reg_val; |
101 | u8 reset_val; |
102 | |
103 | /* make sure gauge is not in sleep mode */ |
104 | ret = regmap_read(map: cw_bat->regmap, CW2015_REG_MODE, val: ®_val); |
105 | if (ret) |
106 | return ret; |
107 | |
108 | reset_val = reg_val; |
109 | if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) { |
110 | dev_err(cw_bat->dev, |
111 | "Gauge is in sleep mode, can't update battery info\n" ); |
112 | return -EINVAL; |
113 | } |
114 | |
115 | /* write new battery info */ |
116 | ret = regmap_raw_write(map: cw_bat->regmap, CW2015_REG_BATINFO, |
117 | val: cw_bat->bat_profile, |
118 | CW2015_SIZE_BATINFO); |
119 | if (ret) |
120 | return ret; |
121 | |
122 | /* set config update flag */ |
123 | reg_val |= CW2015_CONFIG_UPDATE_FLG; |
124 | reg_val &= ~CW2015_MASK_ATHD; |
125 | reg_val |= CW2015_ATHD(cw_bat->alert_level); |
126 | ret = regmap_write(map: cw_bat->regmap, CW2015_REG_CONFIG, val: reg_val); |
127 | if (ret) |
128 | return ret; |
129 | |
130 | /* reset gauge to apply new battery profile */ |
131 | reset_val &= ~CW2015_MODE_RESTART; |
132 | reg_val = reset_val | CW2015_MODE_RESTART; |
133 | ret = regmap_write(map: cw_bat->regmap, CW2015_REG_MODE, val: reg_val); |
134 | if (ret) |
135 | return ret; |
136 | |
137 | /* wait for gauge to reset */ |
138 | msleep(msecs: 20); |
139 | |
140 | /* clear reset flag */ |
141 | ret = regmap_write(map: cw_bat->regmap, CW2015_REG_MODE, val: reset_val); |
142 | if (ret) |
143 | return ret; |
144 | |
145 | /* wait for gauge to become ready */ |
146 | ret = regmap_read_poll_timeout(cw_bat->regmap, CW2015_REG_SOC, |
147 | reg_val, reg_val <= 100, |
148 | 10 * USEC_PER_MSEC, 10 * USEC_PER_SEC); |
149 | if (ret) |
150 | dev_err(cw_bat->dev, |
151 | "Gauge did not become ready after profile upload\n" ); |
152 | else |
153 | dev_dbg(cw_bat->dev, "Battery profile updated\n" ); |
154 | |
155 | return ret; |
156 | } |
157 | |
158 | static int cw_init(struct cw_battery *cw_bat) |
159 | { |
160 | int ret; |
161 | unsigned int reg_val = CW2015_MODE_SLEEP; |
162 | |
163 | if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) { |
164 | reg_val = CW2015_MODE_NORMAL; |
165 | ret = regmap_write(map: cw_bat->regmap, CW2015_REG_MODE, val: reg_val); |
166 | if (ret) |
167 | return ret; |
168 | } |
169 | |
170 | ret = regmap_read(map: cw_bat->regmap, CW2015_REG_CONFIG, val: ®_val); |
171 | if (ret) |
172 | return ret; |
173 | |
174 | if ((reg_val & CW2015_MASK_ATHD) != CW2015_ATHD(cw_bat->alert_level)) { |
175 | dev_dbg(cw_bat->dev, "Setting new alert level\n" ); |
176 | reg_val &= ~CW2015_MASK_ATHD; |
177 | reg_val |= ~CW2015_ATHD(cw_bat->alert_level); |
178 | ret = regmap_write(map: cw_bat->regmap, CW2015_REG_CONFIG, val: reg_val); |
179 | if (ret) |
180 | return ret; |
181 | } |
182 | |
183 | ret = regmap_read(map: cw_bat->regmap, CW2015_REG_CONFIG, val: ®_val); |
184 | if (ret) |
185 | return ret; |
186 | |
187 | if (!(reg_val & CW2015_CONFIG_UPDATE_FLG)) { |
188 | dev_dbg(cw_bat->dev, |
189 | "Battery profile not present, uploading battery profile\n" ); |
190 | if (cw_bat->bat_profile) { |
191 | ret = cw_update_profile(cw_bat); |
192 | if (ret) { |
193 | dev_err(cw_bat->dev, |
194 | "Failed to upload battery profile\n" ); |
195 | return ret; |
196 | } |
197 | } else { |
198 | dev_warn(cw_bat->dev, |
199 | "No profile specified, continuing without profile\n" ); |
200 | } |
201 | } else if (cw_bat->bat_profile) { |
202 | u8 bat_info[CW2015_SIZE_BATINFO]; |
203 | |
204 | ret = regmap_raw_read(map: cw_bat->regmap, CW2015_REG_BATINFO, |
205 | val: bat_info, CW2015_SIZE_BATINFO); |
206 | if (ret) { |
207 | dev_err(cw_bat->dev, |
208 | "Failed to read stored battery profile\n" ); |
209 | return ret; |
210 | } |
211 | |
212 | if (memcmp(p: bat_info, q: cw_bat->bat_profile, CW2015_SIZE_BATINFO)) { |
213 | dev_warn(cw_bat->dev, "Replacing stored battery profile\n" ); |
214 | ret = cw_update_profile(cw_bat); |
215 | if (ret) |
216 | return ret; |
217 | } |
218 | } else { |
219 | dev_warn(cw_bat->dev, |
220 | "Can't check current battery profile, no profile provided\n" ); |
221 | } |
222 | |
223 | dev_dbg(cw_bat->dev, "Battery profile configured\n" ); |
224 | return 0; |
225 | } |
226 | |
227 | static int cw_power_on_reset(struct cw_battery *cw_bat) |
228 | { |
229 | int ret; |
230 | unsigned char reset_val; |
231 | |
232 | reset_val = CW2015_MODE_SLEEP; |
233 | ret = regmap_write(map: cw_bat->regmap, CW2015_REG_MODE, val: reset_val); |
234 | if (ret) |
235 | return ret; |
236 | |
237 | /* wait for gauge to enter sleep */ |
238 | msleep(msecs: 20); |
239 | |
240 | reset_val = CW2015_MODE_NORMAL; |
241 | ret = regmap_write(map: cw_bat->regmap, CW2015_REG_MODE, val: reset_val); |
242 | if (ret) |
243 | return ret; |
244 | |
245 | ret = cw_init(cw_bat); |
246 | if (ret) |
247 | return ret; |
248 | return 0; |
249 | } |
250 | |
251 | #define HYSTERESIS(current, previous, up, down) \ |
252 | (((current) < (previous) + (up)) && ((current) > (previous) - (down))) |
253 | |
254 | static int cw_get_soc(struct cw_battery *cw_bat) |
255 | { |
256 | unsigned int soc; |
257 | int ret; |
258 | |
259 | ret = regmap_read(map: cw_bat->regmap, CW2015_REG_SOC, val: &soc); |
260 | if (ret) |
261 | return ret; |
262 | |
263 | if (soc > 100) { |
264 | int max_error_cycles = |
265 | CW2015_BAT_SOC_ERROR_MS / cw_bat->poll_interval_ms; |
266 | |
267 | dev_err(cw_bat->dev, "Invalid SoC %d%%\n" , soc); |
268 | cw_bat->read_errors++; |
269 | if (cw_bat->read_errors > max_error_cycles) { |
270 | dev_warn(cw_bat->dev, |
271 | "Too many invalid SoC reports, resetting gauge\n" ); |
272 | cw_power_on_reset(cw_bat); |
273 | cw_bat->read_errors = 0; |
274 | } |
275 | return cw_bat->soc; |
276 | } |
277 | cw_bat->read_errors = 0; |
278 | |
279 | /* Reset gauge if stuck while charging */ |
280 | if (cw_bat->status == POWER_SUPPLY_STATUS_CHARGING && soc == cw_bat->soc) { |
281 | int max_stuck_cycles = |
282 | CW2015_BAT_CHARGING_STUCK_MS / cw_bat->poll_interval_ms; |
283 | |
284 | cw_bat->charge_stuck_cnt++; |
285 | if (cw_bat->charge_stuck_cnt > max_stuck_cycles) { |
286 | dev_warn(cw_bat->dev, |
287 | "SoC stuck @%u%%, resetting gauge\n" , soc); |
288 | cw_power_on_reset(cw_bat); |
289 | cw_bat->charge_stuck_cnt = 0; |
290 | } |
291 | } else { |
292 | cw_bat->charge_stuck_cnt = 0; |
293 | } |
294 | |
295 | /* Ignore voltage dips during charge */ |
296 | if (cw_bat->charger_attached && HYSTERESIS(soc, cw_bat->soc, 0, 3)) |
297 | soc = cw_bat->soc; |
298 | |
299 | /* Ignore voltage spikes during discharge */ |
300 | if (!cw_bat->charger_attached && HYSTERESIS(soc, cw_bat->soc, 3, 0)) |
301 | soc = cw_bat->soc; |
302 | |
303 | return soc; |
304 | } |
305 | |
306 | static int cw_get_voltage(struct cw_battery *cw_bat) |
307 | { |
308 | int ret, i, voltage_mv; |
309 | u16 reg_val; |
310 | u32 avg = 0; |
311 | |
312 | for (i = 0; i < CW2015_AVERAGING_SAMPLES; i++) { |
313 | ret = cw_read_word(cw_bat, CW2015_REG_VCELL, val: ®_val); |
314 | if (ret) |
315 | return ret; |
316 | |
317 | avg += reg_val; |
318 | } |
319 | avg /= CW2015_AVERAGING_SAMPLES; |
320 | |
321 | /* |
322 | * 305 uV per ADC step |
323 | * Use 312 / 1024 as efficient approximation of 305 / 1000 |
324 | * Negligible error of 0.1% |
325 | */ |
326 | voltage_mv = avg * 312 / 1024; |
327 | |
328 | dev_dbg(cw_bat->dev, "Read voltage: %d mV, raw=0x%04x\n" , |
329 | voltage_mv, reg_val); |
330 | return voltage_mv; |
331 | } |
332 | |
333 | static int cw_get_time_to_empty(struct cw_battery *cw_bat) |
334 | { |
335 | int ret; |
336 | u16 value16; |
337 | |
338 | ret = cw_read_word(cw_bat, CW2015_REG_RRT_ALERT, val: &value16); |
339 | if (ret) |
340 | return ret; |
341 | |
342 | return value16 & CW2015_MASK_SOC; |
343 | } |
344 | |
345 | static void cw_update_charge_status(struct cw_battery *cw_bat) |
346 | { |
347 | int ret; |
348 | |
349 | ret = power_supply_am_i_supplied(psy: cw_bat->rk_bat); |
350 | if (ret < 0) { |
351 | dev_warn(cw_bat->dev, "Failed to get supply state: %d\n" , ret); |
352 | } else { |
353 | bool charger_attached; |
354 | |
355 | charger_attached = !!ret; |
356 | if (cw_bat->charger_attached != charger_attached) { |
357 | cw_bat->battery_changed = true; |
358 | if (charger_attached) |
359 | cw_bat->charge_count++; |
360 | } |
361 | cw_bat->charger_attached = charger_attached; |
362 | } |
363 | } |
364 | |
365 | static void cw_update_soc(struct cw_battery *cw_bat) |
366 | { |
367 | int soc; |
368 | |
369 | soc = cw_get_soc(cw_bat); |
370 | if (soc < 0) |
371 | dev_err(cw_bat->dev, "Failed to get SoC from gauge: %d\n" , soc); |
372 | else if (cw_bat->soc != soc) { |
373 | cw_bat->soc = soc; |
374 | cw_bat->battery_changed = true; |
375 | } |
376 | } |
377 | |
378 | static void cw_update_voltage(struct cw_battery *cw_bat) |
379 | { |
380 | int voltage_mv; |
381 | |
382 | voltage_mv = cw_get_voltage(cw_bat); |
383 | if (voltage_mv < 0) |
384 | dev_err(cw_bat->dev, "Failed to get voltage from gauge: %d\n" , |
385 | voltage_mv); |
386 | else |
387 | cw_bat->voltage_mv = voltage_mv; |
388 | } |
389 | |
390 | static void cw_update_status(struct cw_battery *cw_bat) |
391 | { |
392 | int status = POWER_SUPPLY_STATUS_DISCHARGING; |
393 | |
394 | if (cw_bat->charger_attached) { |
395 | if (cw_bat->soc >= 100) |
396 | status = POWER_SUPPLY_STATUS_FULL; |
397 | else |
398 | status = POWER_SUPPLY_STATUS_CHARGING; |
399 | } |
400 | |
401 | if (cw_bat->status != status) |
402 | cw_bat->battery_changed = true; |
403 | cw_bat->status = status; |
404 | } |
405 | |
406 | static void cw_update_time_to_empty(struct cw_battery *cw_bat) |
407 | { |
408 | int time_to_empty; |
409 | |
410 | time_to_empty = cw_get_time_to_empty(cw_bat); |
411 | if (time_to_empty < 0) |
412 | dev_err(cw_bat->dev, "Failed to get time to empty from gauge: %d\n" , |
413 | time_to_empty); |
414 | else if (cw_bat->time_to_empty != time_to_empty) { |
415 | cw_bat->time_to_empty = time_to_empty; |
416 | cw_bat->battery_changed = true; |
417 | } |
418 | } |
419 | |
420 | static void cw_bat_work(struct work_struct *work) |
421 | { |
422 | struct delayed_work *delay_work; |
423 | struct cw_battery *cw_bat; |
424 | int ret; |
425 | unsigned int reg_val; |
426 | |
427 | delay_work = to_delayed_work(work); |
428 | cw_bat = container_of(delay_work, struct cw_battery, battery_delay_work); |
429 | ret = regmap_read(map: cw_bat->regmap, CW2015_REG_MODE, val: ®_val); |
430 | if (ret) { |
431 | dev_err(cw_bat->dev, "Failed to read mode from gauge: %d\n" , ret); |
432 | } else { |
433 | if ((reg_val & CW2015_MODE_SLEEP_MASK) == CW2015_MODE_SLEEP) { |
434 | int i; |
435 | |
436 | for (i = 0; i < CW2015_RESET_TRIES; i++) { |
437 | if (!cw_power_on_reset(cw_bat)) |
438 | break; |
439 | } |
440 | } |
441 | cw_update_soc(cw_bat); |
442 | cw_update_voltage(cw_bat); |
443 | cw_update_charge_status(cw_bat); |
444 | cw_update_status(cw_bat); |
445 | cw_update_time_to_empty(cw_bat); |
446 | } |
447 | dev_dbg(cw_bat->dev, "charger_attached = %d\n" , cw_bat->charger_attached); |
448 | dev_dbg(cw_bat->dev, "status = %d\n" , cw_bat->status); |
449 | dev_dbg(cw_bat->dev, "soc = %d%%\n" , cw_bat->soc); |
450 | dev_dbg(cw_bat->dev, "voltage = %dmV\n" , cw_bat->voltage_mv); |
451 | |
452 | if (cw_bat->battery_changed) |
453 | power_supply_changed(psy: cw_bat->rk_bat); |
454 | cw_bat->battery_changed = false; |
455 | |
456 | queue_delayed_work(wq: cw_bat->battery_workqueue, |
457 | dwork: &cw_bat->battery_delay_work, |
458 | delay: msecs_to_jiffies(m: cw_bat->poll_interval_ms)); |
459 | } |
460 | |
461 | static bool cw_battery_valid_time_to_empty(struct cw_battery *cw_bat) |
462 | { |
463 | return cw_bat->time_to_empty > 0 && |
464 | cw_bat->time_to_empty < CW2015_MASK_SOC && |
465 | cw_bat->status == POWER_SUPPLY_STATUS_DISCHARGING; |
466 | } |
467 | |
468 | static int cw_battery_get_property(struct power_supply *psy, |
469 | enum power_supply_property psp, |
470 | union power_supply_propval *val) |
471 | { |
472 | struct cw_battery *cw_bat; |
473 | |
474 | cw_bat = power_supply_get_drvdata(psy); |
475 | switch (psp) { |
476 | case POWER_SUPPLY_PROP_CAPACITY: |
477 | val->intval = cw_bat->soc; |
478 | break; |
479 | |
480 | case POWER_SUPPLY_PROP_STATUS: |
481 | val->intval = cw_bat->status; |
482 | break; |
483 | |
484 | case POWER_SUPPLY_PROP_PRESENT: |
485 | val->intval = !!cw_bat->voltage_mv; |
486 | break; |
487 | |
488 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
489 | val->intval = cw_bat->voltage_mv * 1000; |
490 | break; |
491 | |
492 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: |
493 | if (cw_battery_valid_time_to_empty(cw_bat)) |
494 | val->intval = cw_bat->time_to_empty * 60; |
495 | else |
496 | val->intval = 0; |
497 | break; |
498 | |
499 | case POWER_SUPPLY_PROP_TECHNOLOGY: |
500 | val->intval = POWER_SUPPLY_TECHNOLOGY_LION; |
501 | break; |
502 | |
503 | case POWER_SUPPLY_PROP_CHARGE_COUNTER: |
504 | val->intval = cw_bat->charge_count; |
505 | break; |
506 | |
507 | case POWER_SUPPLY_PROP_CHARGE_FULL: |
508 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: |
509 | if (cw_bat->battery->charge_full_design_uah > 0) |
510 | val->intval = cw_bat->battery->charge_full_design_uah; |
511 | else |
512 | val->intval = 0; |
513 | break; |
514 | |
515 | case POWER_SUPPLY_PROP_CHARGE_NOW: |
516 | val->intval = cw_bat->battery->charge_full_design_uah; |
517 | val->intval = val->intval * cw_bat->soc / 100; |
518 | break; |
519 | |
520 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
521 | if (cw_battery_valid_time_to_empty(cw_bat) && |
522 | cw_bat->battery->charge_full_design_uah > 0) { |
523 | /* calculate remaining capacity */ |
524 | val->intval = cw_bat->battery->charge_full_design_uah; |
525 | val->intval = val->intval * cw_bat->soc / 100; |
526 | |
527 | /* estimate current based on time to empty */ |
528 | val->intval = 60 * val->intval / cw_bat->time_to_empty; |
529 | } else { |
530 | val->intval = 0; |
531 | } |
532 | |
533 | break; |
534 | |
535 | default: |
536 | break; |
537 | } |
538 | return 0; |
539 | } |
540 | |
541 | static enum power_supply_property cw_battery_properties[] = { |
542 | POWER_SUPPLY_PROP_CAPACITY, |
543 | POWER_SUPPLY_PROP_STATUS, |
544 | POWER_SUPPLY_PROP_PRESENT, |
545 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
546 | POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, |
547 | POWER_SUPPLY_PROP_TECHNOLOGY, |
548 | POWER_SUPPLY_PROP_CHARGE_COUNTER, |
549 | POWER_SUPPLY_PROP_CHARGE_FULL, |
550 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, |
551 | POWER_SUPPLY_PROP_CHARGE_NOW, |
552 | POWER_SUPPLY_PROP_CURRENT_NOW, |
553 | }; |
554 | |
555 | static const struct power_supply_desc cw2015_bat_desc = { |
556 | .name = "cw2015-battery" , |
557 | .type = POWER_SUPPLY_TYPE_BATTERY, |
558 | .properties = cw_battery_properties, |
559 | .num_properties = ARRAY_SIZE(cw_battery_properties), |
560 | .get_property = cw_battery_get_property, |
561 | }; |
562 | |
563 | static int cw2015_parse_properties(struct cw_battery *cw_bat) |
564 | { |
565 | struct device *dev = cw_bat->dev; |
566 | int length; |
567 | int ret; |
568 | |
569 | length = device_property_count_u8(dev, propname: "cellwise,battery-profile" ); |
570 | if (length < 0) { |
571 | dev_warn(cw_bat->dev, |
572 | "No battery-profile found, using current flash contents\n" ); |
573 | } else if (length != CW2015_SIZE_BATINFO) { |
574 | dev_err(cw_bat->dev, "battery-profile must be %d bytes\n" , |
575 | CW2015_SIZE_BATINFO); |
576 | return -EINVAL; |
577 | } else { |
578 | cw_bat->bat_profile = devm_kzalloc(dev, size: length, GFP_KERNEL); |
579 | if (!cw_bat->bat_profile) |
580 | return -ENOMEM; |
581 | |
582 | ret = device_property_read_u8_array(dev, |
583 | propname: "cellwise,battery-profile" , |
584 | val: cw_bat->bat_profile, |
585 | nval: length); |
586 | if (ret) |
587 | return ret; |
588 | } |
589 | |
590 | ret = device_property_read_u32(dev, propname: "cellwise,monitor-interval-ms" , |
591 | val: &cw_bat->poll_interval_ms); |
592 | if (ret) { |
593 | dev_dbg(cw_bat->dev, "Using default poll interval\n" ); |
594 | cw_bat->poll_interval_ms = CW2015_DEFAULT_POLL_INTERVAL_MS; |
595 | } |
596 | |
597 | return 0; |
598 | } |
599 | |
600 | static const struct regmap_range regmap_ranges_rd_yes[] = { |
601 | regmap_reg_range(CW2015_REG_VERSION, CW2015_REG_VERSION), |
602 | regmap_reg_range(CW2015_REG_VCELL, CW2015_REG_CONFIG), |
603 | regmap_reg_range(CW2015_REG_MODE, CW2015_REG_MODE), |
604 | regmap_reg_range(CW2015_REG_BATINFO, |
605 | CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1), |
606 | }; |
607 | |
608 | static const struct regmap_access_table regmap_rd_table = { |
609 | .yes_ranges = regmap_ranges_rd_yes, |
610 | .n_yes_ranges = 4, |
611 | }; |
612 | |
613 | static const struct regmap_range regmap_ranges_wr_yes[] = { |
614 | regmap_reg_range(CW2015_REG_RRT_ALERT, CW2015_REG_CONFIG), |
615 | regmap_reg_range(CW2015_REG_MODE, CW2015_REG_MODE), |
616 | regmap_reg_range(CW2015_REG_BATINFO, |
617 | CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1), |
618 | }; |
619 | |
620 | static const struct regmap_access_table regmap_wr_table = { |
621 | .yes_ranges = regmap_ranges_wr_yes, |
622 | .n_yes_ranges = 3, |
623 | }; |
624 | |
625 | static const struct regmap_range regmap_ranges_vol_yes[] = { |
626 | regmap_reg_range(CW2015_REG_VCELL, CW2015_REG_SOC + 1), |
627 | }; |
628 | |
629 | static const struct regmap_access_table regmap_vol_table = { |
630 | .yes_ranges = regmap_ranges_vol_yes, |
631 | .n_yes_ranges = 1, |
632 | }; |
633 | |
634 | static const struct regmap_config cw2015_regmap_config = { |
635 | .reg_bits = 8, |
636 | .val_bits = 8, |
637 | .rd_table = ®map_rd_table, |
638 | .wr_table = ®map_wr_table, |
639 | .volatile_table = ®map_vol_table, |
640 | .max_register = CW2015_REG_BATINFO + CW2015_SIZE_BATINFO - 1, |
641 | }; |
642 | |
643 | static int cw_bat_probe(struct i2c_client *client) |
644 | { |
645 | int ret; |
646 | struct cw_battery *cw_bat; |
647 | struct power_supply_config psy_cfg = { 0 }; |
648 | |
649 | cw_bat = devm_kzalloc(dev: &client->dev, size: sizeof(*cw_bat), GFP_KERNEL); |
650 | if (!cw_bat) |
651 | return -ENOMEM; |
652 | |
653 | i2c_set_clientdata(client, data: cw_bat); |
654 | cw_bat->dev = &client->dev; |
655 | cw_bat->soc = 1; |
656 | |
657 | ret = cw2015_parse_properties(cw_bat); |
658 | if (ret) { |
659 | dev_err(cw_bat->dev, "Failed to parse cw2015 properties\n" ); |
660 | return ret; |
661 | } |
662 | |
663 | cw_bat->regmap = devm_regmap_init_i2c(client, &cw2015_regmap_config); |
664 | if (IS_ERR(ptr: cw_bat->regmap)) { |
665 | dev_err(cw_bat->dev, "Failed to allocate regmap: %ld\n" , |
666 | PTR_ERR(cw_bat->regmap)); |
667 | return PTR_ERR(ptr: cw_bat->regmap); |
668 | } |
669 | |
670 | ret = cw_init(cw_bat); |
671 | if (ret) { |
672 | dev_err(cw_bat->dev, "Init failed: %d\n" , ret); |
673 | return ret; |
674 | } |
675 | |
676 | psy_cfg.drv_data = cw_bat; |
677 | psy_cfg.fwnode = dev_fwnode(cw_bat->dev); |
678 | |
679 | cw_bat->rk_bat = devm_power_supply_register(parent: &client->dev, |
680 | desc: &cw2015_bat_desc, |
681 | cfg: &psy_cfg); |
682 | if (IS_ERR(ptr: cw_bat->rk_bat)) { |
683 | /* try again if this happens */ |
684 | dev_err_probe(dev: &client->dev, err: PTR_ERR(ptr: cw_bat->rk_bat), |
685 | fmt: "Failed to register power supply\n" ); |
686 | return PTR_ERR(ptr: cw_bat->rk_bat); |
687 | } |
688 | |
689 | ret = power_supply_get_battery_info(psy: cw_bat->rk_bat, info_out: &cw_bat->battery); |
690 | if (ret) { |
691 | /* Allocate an empty battery */ |
692 | cw_bat->battery = devm_kzalloc(dev: &client->dev, |
693 | size: sizeof(*cw_bat->battery), |
694 | GFP_KERNEL); |
695 | if (!cw_bat->battery) |
696 | return -ENOMEM; |
697 | dev_warn(cw_bat->dev, |
698 | "No monitored battery, some properties will be missing\n" ); |
699 | } |
700 | |
701 | cw_bat->battery_workqueue = create_singlethread_workqueue("rk_battery" ); |
702 | if (!cw_bat->battery_workqueue) |
703 | return -ENOMEM; |
704 | |
705 | devm_delayed_work_autocancel(dev: &client->dev, |
706 | w: &cw_bat->battery_delay_work, worker: cw_bat_work); |
707 | queue_delayed_work(wq: cw_bat->battery_workqueue, |
708 | dwork: &cw_bat->battery_delay_work, delay: msecs_to_jiffies(m: 10)); |
709 | return 0; |
710 | } |
711 | |
712 | static int __maybe_unused cw_bat_suspend(struct device *dev) |
713 | { |
714 | struct i2c_client *client = to_i2c_client(dev); |
715 | struct cw_battery *cw_bat = i2c_get_clientdata(client); |
716 | |
717 | cancel_delayed_work_sync(dwork: &cw_bat->battery_delay_work); |
718 | return 0; |
719 | } |
720 | |
721 | static int __maybe_unused cw_bat_resume(struct device *dev) |
722 | { |
723 | struct i2c_client *client = to_i2c_client(dev); |
724 | struct cw_battery *cw_bat = i2c_get_clientdata(client); |
725 | |
726 | queue_delayed_work(wq: cw_bat->battery_workqueue, |
727 | dwork: &cw_bat->battery_delay_work, delay: 0); |
728 | return 0; |
729 | } |
730 | |
731 | static SIMPLE_DEV_PM_OPS(cw_bat_pm_ops, cw_bat_suspend, cw_bat_resume); |
732 | |
733 | static const struct i2c_device_id cw_bat_id_table[] = { |
734 | { "cw2015" , 0 }, |
735 | { } |
736 | }; |
737 | |
738 | static const struct of_device_id cw2015_of_match[] = { |
739 | { .compatible = "cellwise,cw2015" }, |
740 | { } |
741 | }; |
742 | MODULE_DEVICE_TABLE(of, cw2015_of_match); |
743 | |
744 | static struct i2c_driver cw_bat_driver = { |
745 | .driver = { |
746 | .name = "cw2015" , |
747 | .of_match_table = cw2015_of_match, |
748 | .pm = &cw_bat_pm_ops, |
749 | }, |
750 | .probe = cw_bat_probe, |
751 | .id_table = cw_bat_id_table, |
752 | }; |
753 | |
754 | module_i2c_driver(cw_bat_driver); |
755 | |
756 | MODULE_AUTHOR("xhc<xhc@rock-chips.com>" ); |
757 | MODULE_AUTHOR("Tobias Schramm <t.schramm@manjaro.org>" ); |
758 | MODULE_DESCRIPTION("cw2015/cw2013 battery driver" ); |
759 | MODULE_LICENSE("GPL" ); |
760 | |