1 // SPDX-License-Identifier: GPL-2.0+
3 * Battery monitor driver for the uPI uG3105 battery monitor
5 * Note the uG3105 is not a full-featured autonomous fuel-gauge. Instead it is
6 * expected to be use in combination with some always on microcontroller reading
7 * its coulomb-counter before it can wrap (must be read every 400 seconds!).
9 * Since Linux does not monitor coulomb-counter changes while the device
10 * is off or suspended, the coulomb counter is not used atm.
12 * Possible improvements:
13 * 1. Activate commented out total_coulomb_count code
14 * 2. Reset total_coulomb_count val to 0 when the battery is as good as empty
15 * and remember that we did this (and clear the flag for this on susp/resume)
16 * 3. When the battery is full check if the flag that we set total_coulomb_count
17 * to when the battery was empty is set. If so we now know the capacity,
18 * not the design, but actual capacity, of the battery
19 * 4. Add some mechanism (needs userspace help, or maybe use efivar?) to remember
20 * the actual capacity of the battery over reboots
21 * 5. When we know the actual capacity at probe time, add energy_now and
22 * energy_full attributes. Guess boot + resume energy_now value based on ocv
23 * and then use total_coulomb_count to report energy_now over time, resetting
24 * things to adjust for drift when empty/full. This should give more accurate
25 * readings, esp. in the 30-70% range and allow userspace to estimate time
26 * remaining till empty/full
27 * 6. Maybe unregister + reregister the psy device when we learn the actual
28 * capacity during run-time ?
30 * The above will also require some sort of mwh_per_unit calculation. Testing
31 * has shown that an estimated 7404mWh increase of the battery's energy results
32 * in a total_coulomb_count increase of 3277 units with a 5 milli-ohm sense R.
34 * Copyright (C) 2021 Hans de Goede <hdegoede@redhat.com>
37 #include <linux/devm-helpers.h>
38 #include <linux/module.h>
39 #include <linux/mutex.h>
40 #include <linux/slab.h>
41 #include <linux/i2c.h>
42 #include <linux/mod_devicetable.h>
43 #include <linux/power_supply.h>
44 #include <linux/workqueue.h>
46 #define UG3105_MOV_AVG_WINDOW 8
47 #define UG3105_INIT_POLL_TIME (5 * HZ)
48 #define UG3105_POLL_TIME (30 * HZ)
49 #define UG3105_SETTLE_TIME (1 * HZ)
51 #define UG3105_INIT_POLL_COUNT 30
53 #define UG3105_REG_MODE 0x00
54 #define UG3105_REG_CTRL1 0x01
55 #define UG3105_REG_COULOMB_CNT 0x02
56 #define UG3105_REG_BAT_VOLT 0x08
57 #define UG3105_REG_BAT_CURR 0x0c
59 #define UG3105_MODE_STANDBY 0x00
60 #define UG3105_MODE_RUN 0x10
62 #define UG3105_CTRL1_RESET_COULOMB_CNT 0x03
64 #define UG3105_CURR_HYST_UA 65000
66 #define UG3105_LOW_BAT_UV 3700000
67 #define UG3105_FULL_BAT_HYST_UV 38000
70 struct i2c_client
*client
;
71 struct power_supply
*psy
;
72 struct power_supply_battery_info
*info
;
73 struct delayed_work work
;
75 int ocv
[UG3105_MOV_AVG_WINDOW
]; /* micro-volt */
76 int intern_res
[UG3105_MOV_AVG_WINDOW
]; /* milli-ohm */
79 int ocv_avg
; /* micro-volt */
80 int intern_res_poll_count
;
81 int intern_res_avg_index
;
82 int intern_res_avg
; /* milli-ohm */
83 int volt
; /* micro-volt */
84 int curr
; /* micro-ampere */
85 int total_coulomb_count
;
93 static int ug3105_read_word(struct i2c_client
*client
, u8 reg
)
97 val
= i2c_smbus_read_word_data(client
, reg
);
99 dev_err(&client
->dev
, "Error reading reg 0x%02x\n", reg
);
104 static int ug3105_get_status(struct ug3105_chip
*chip
)
106 int full
= chip
->info
->constant_charge_voltage_max_uv
- UG3105_FULL_BAT_HYST_UV
;
108 if (chip
->curr
> UG3105_CURR_HYST_UA
)
109 return POWER_SUPPLY_STATUS_CHARGING
;
111 if (chip
->curr
< -UG3105_CURR_HYST_UA
)
112 return POWER_SUPPLY_STATUS_DISCHARGING
;
114 if (chip
->supplied
&& chip
->ocv_avg
> full
)
115 return POWER_SUPPLY_STATUS_FULL
;
117 return POWER_SUPPLY_STATUS_NOT_CHARGING
;
120 static int ug3105_get_capacity(struct ug3105_chip
*chip
)
123 * OCV voltages in uV for 0-110% in 5% increments, the 100-110% is
124 * for LiPo HV (High-Voltage) bateries which can go up to 4.35V
125 * instead of the usual 4.2V.
127 static const int ocv_capacity_tbl
[23] = {
152 int i
, ocv_diff
, ocv_step
;
154 if (chip
->ocv_avg
< ocv_capacity_tbl
[0])
157 if (chip
->status
== POWER_SUPPLY_STATUS_FULL
)
160 for (i
= 1; i
< ARRAY_SIZE(ocv_capacity_tbl
); i
++) {
161 if (chip
->ocv_avg
> ocv_capacity_tbl
[i
])
164 ocv_diff
= ocv_capacity_tbl
[i
] - chip
->ocv_avg
;
165 ocv_step
= ocv_capacity_tbl
[i
] - ocv_capacity_tbl
[i
- 1];
166 /* scale 0-110% down to 0-100% for LiPo HV */
167 if (chip
->info
->constant_charge_voltage_max_uv
>= 4300000)
168 return (i
* 500 - ocv_diff
* 500 / ocv_step
) / 110;
170 return i
* 5 - ocv_diff
* 5 / ocv_step
;
176 static void ug3105_work(struct work_struct
*work
)
178 struct ug3105_chip
*chip
= container_of(work
, struct ug3105_chip
,
180 int i
, val
, curr_diff
, volt_diff
, res
, win_size
;
181 bool prev_supplied
= chip
->supplied
;
182 int prev_status
= chip
->status
;
183 int prev_volt
= chip
->volt
;
184 int prev_curr
= chip
->curr
;
185 struct power_supply
*psy
;
187 mutex_lock(&chip
->lock
);
193 val
= ug3105_read_word(chip
->client
, UG3105_REG_BAT_VOLT
);
196 chip
->volt
= val
* chip
->uv_per_unit
;
198 val
= ug3105_read_word(chip
->client
, UG3105_REG_BAT_CURR
);
201 chip
->curr
= (s16
)val
* chip
->ua_per_unit
;
203 chip
->ocv
[chip
->ocv_avg_index
] =
204 chip
->volt
- chip
->curr
* chip
->intern_res_avg
/ 1000;
205 chip
->ocv_avg_index
= (chip
->ocv_avg_index
+ 1) % UG3105_MOV_AVG_WINDOW
;
209 * See possible improvements comment above.
211 * Read + reset coulomb counter every 10 polls (every 300 seconds)
212 * if ((chip->poll_count % 10) == 0) {
213 * val = ug3105_read_word(chip->client, UG3105_REG_COULOMB_CNT);
217 * i2c_smbus_write_byte_data(chip->client, UG3105_REG_CTRL1,
218 * UG3105_CTRL1_RESET_COULOMB_CNT);
220 * chip->total_coulomb_count += (s16)val;
221 * dev_dbg(&chip->client->dev, "coulomb count %d total %d\n",
222 * (s16)val, chip->total_coulomb_count);
227 win_size
= min(chip
->poll_count
, UG3105_MOV_AVG_WINDOW
);
228 for (i
= 0; i
< win_size
; i
++)
229 chip
->ocv_avg
+= chip
->ocv
[i
];
230 chip
->ocv_avg
/= win_size
;
232 chip
->supplied
= power_supply_am_i_supplied(psy
);
233 chip
->status
= ug3105_get_status(chip
);
234 chip
->capacity
= ug3105_get_capacity(chip
);
237 * Skip internal resistance calc on charger [un]plug and
238 * when the battery is almost empty (voltage low).
240 if (chip
->supplied
!= prev_supplied
||
241 chip
->volt
< UG3105_LOW_BAT_UV
||
242 chip
->poll_count
< 2)
246 * Assuming that the OCV voltage does not change significantly
247 * between 2 polls, then we can calculate the internal resistance
248 * on a significant current change by attributing all voltage
249 * change between the 2 readings to the internal resistance.
251 curr_diff
= abs(chip
->curr
- prev_curr
);
252 if (curr_diff
< UG3105_CURR_HYST_UA
)
255 volt_diff
= abs(chip
->volt
- prev_volt
);
256 res
= volt_diff
* 1000 / curr_diff
;
258 if ((res
< (chip
->intern_res_avg
* 2 / 3)) ||
259 (res
> (chip
->intern_res_avg
* 4 / 3))) {
260 dev_dbg(&chip
->client
->dev
, "Ignoring outlier internal resistance %d mOhm\n", res
);
264 dev_dbg(&chip
->client
->dev
, "Internal resistance %d mOhm\n", res
);
266 chip
->intern_res
[chip
->intern_res_avg_index
] = res
;
267 chip
->intern_res_avg_index
= (chip
->intern_res_avg_index
+ 1) % UG3105_MOV_AVG_WINDOW
;
268 chip
->intern_res_poll_count
++;
270 chip
->intern_res_avg
= 0;
271 win_size
= min(chip
->intern_res_poll_count
, UG3105_MOV_AVG_WINDOW
);
272 for (i
= 0; i
< win_size
; i
++)
273 chip
->intern_res_avg
+= chip
->intern_res
[i
];
274 chip
->intern_res_avg
/= win_size
;
277 mutex_unlock(&chip
->lock
);
279 queue_delayed_work(system_wq
, &chip
->work
,
280 (chip
->poll_count
<= UG3105_INIT_POLL_COUNT
) ?
281 UG3105_INIT_POLL_TIME
: UG3105_POLL_TIME
);
283 if (chip
->status
!= prev_status
&& psy
)
284 power_supply_changed(psy
);
287 static enum power_supply_property ug3105_battery_props
[] = {
288 POWER_SUPPLY_PROP_STATUS
,
289 POWER_SUPPLY_PROP_PRESENT
,
290 POWER_SUPPLY_PROP_TECHNOLOGY
,
291 POWER_SUPPLY_PROP_SCOPE
,
292 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
293 POWER_SUPPLY_PROP_VOLTAGE_OCV
,
294 POWER_SUPPLY_PROP_CURRENT_NOW
,
295 POWER_SUPPLY_PROP_CAPACITY
,
298 static int ug3105_get_property(struct power_supply
*psy
,
299 enum power_supply_property psp
,
300 union power_supply_propval
*val
)
302 struct ug3105_chip
*chip
= power_supply_get_drvdata(psy
);
305 mutex_lock(&chip
->lock
);
313 case POWER_SUPPLY_PROP_STATUS
:
314 val
->intval
= chip
->status
;
316 case POWER_SUPPLY_PROP_PRESENT
:
319 case POWER_SUPPLY_PROP_TECHNOLOGY
:
320 val
->intval
= chip
->info
->technology
;
322 case POWER_SUPPLY_PROP_SCOPE
:
323 val
->intval
= POWER_SUPPLY_SCOPE_SYSTEM
;
325 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
326 ret
= ug3105_read_word(chip
->client
, UG3105_REG_BAT_VOLT
);
329 val
->intval
= ret
* chip
->uv_per_unit
;
332 case POWER_SUPPLY_PROP_VOLTAGE_OCV
:
333 val
->intval
= chip
->ocv_avg
;
335 case POWER_SUPPLY_PROP_CURRENT_NOW
:
336 ret
= ug3105_read_word(chip
->client
, UG3105_REG_BAT_CURR
);
339 val
->intval
= (s16
)ret
* chip
->ua_per_unit
;
342 case POWER_SUPPLY_PROP_CAPACITY
:
343 val
->intval
= chip
->capacity
;
350 mutex_unlock(&chip
->lock
);
354 static void ug3105_external_power_changed(struct power_supply
*psy
)
356 struct ug3105_chip
*chip
= power_supply_get_drvdata(psy
);
358 dev_dbg(&chip
->client
->dev
, "external power changed\n");
359 mod_delayed_work(system_wq
, &chip
->work
, UG3105_SETTLE_TIME
);
362 static const struct power_supply_desc ug3105_psy_desc
= {
363 .name
= "ug3105_battery",
364 .type
= POWER_SUPPLY_TYPE_BATTERY
,
365 .get_property
= ug3105_get_property
,
366 .external_power_changed
= ug3105_external_power_changed
,
367 .properties
= ug3105_battery_props
,
368 .num_properties
= ARRAY_SIZE(ug3105_battery_props
),
371 static void ug3105_init(struct ug3105_chip
*chip
)
373 chip
->poll_count
= 0;
374 chip
->ocv_avg_index
= 0;
375 chip
->total_coulomb_count
= 0;
376 i2c_smbus_write_byte_data(chip
->client
, UG3105_REG_MODE
,
378 i2c_smbus_write_byte_data(chip
->client
, UG3105_REG_CTRL1
,
379 UG3105_CTRL1_RESET_COULOMB_CNT
);
380 queue_delayed_work(system_wq
, &chip
->work
, 0);
381 flush_delayed_work(&chip
->work
);
384 static int ug3105_probe(struct i2c_client
*client
)
386 struct power_supply_config psy_cfg
= {};
387 struct device
*dev
= &client
->dev
;
388 u32 curr_sense_res_uohm
= 10000;
389 struct power_supply
*psy
;
390 struct ug3105_chip
*chip
;
393 chip
= devm_kzalloc(dev
, sizeof(*chip
), GFP_KERNEL
);
397 chip
->client
= client
;
398 mutex_init(&chip
->lock
);
399 ret
= devm_delayed_work_autocancel(dev
, &chip
->work
, ug3105_work
);
403 psy_cfg
.drv_data
= chip
;
404 psy
= devm_power_supply_register(dev
, &ug3105_psy_desc
, &psy_cfg
);
408 ret
= power_supply_get_battery_info(psy
, &chip
->info
);
412 if (chip
->info
->factory_internal_resistance_uohm
== -EINVAL
||
413 chip
->info
->constant_charge_voltage_max_uv
== -EINVAL
) {
414 dev_err(dev
, "error required properties are missing\n");
418 device_property_read_u32(dev
, "upisemi,rsns-microohm", &curr_sense_res_uohm
);
421 * DAC maximum is 4.5V divided by 65536 steps + an unknown factor of 10
422 * coming from somewhere for some reason (verified with a volt-meter).
424 chip
->uv_per_unit
= 45000000/65536;
425 /* Datasheet says 8.1 uV per unit for the current ADC */
426 chip
->ua_per_unit
= 8100000 / curr_sense_res_uohm
;
428 /* Use provided internal resistance as start point (in milli-ohm) */
429 chip
->intern_res_avg
= chip
->info
->factory_internal_resistance_uohm
/ 1000;
430 /* Also add it to the internal resistance moving average window */
431 chip
->intern_res
[0] = chip
->intern_res_avg
;
432 chip
->intern_res_avg_index
= 1;
433 chip
->intern_res_poll_count
= 1;
435 mutex_lock(&chip
->lock
);
437 mutex_unlock(&chip
->lock
);
441 i2c_set_clientdata(client
, chip
);
445 static int __maybe_unused
ug3105_suspend(struct device
*dev
)
447 struct ug3105_chip
*chip
= dev_get_drvdata(dev
);
449 cancel_delayed_work_sync(&chip
->work
);
450 i2c_smbus_write_byte_data(chip
->client
, UG3105_REG_MODE
,
451 UG3105_MODE_STANDBY
);
456 static int __maybe_unused
ug3105_resume(struct device
*dev
)
458 struct ug3105_chip
*chip
= dev_get_drvdata(dev
);
465 static SIMPLE_DEV_PM_OPS(ug3105_pm_ops
, ug3105_suspend
,
468 static const struct i2c_device_id ug3105_id
[] = {
472 MODULE_DEVICE_TABLE(i2c
, ug3105_id
);
474 static struct i2c_driver ug3105_i2c_driver
= {
477 .pm
= &ug3105_pm_ops
,
479 .probe
= ug3105_probe
,
480 .id_table
= ug3105_id
,
482 module_i2c_driver(ug3105_i2c_driver
);
484 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com");
485 MODULE_DESCRIPTION("uPI uG3105 battery monitor driver");
486 MODULE_LICENSE("GPL");