xref: /linux/drivers/power/supply/ug3105_battery.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Battery monitor driver for the uPI uG3105 battery monitor
4  *
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!).
8  *
9  * Since Linux does not monitor coulomb-counter changes while the device
10  * is off or suspended, the coulomb counter is not used atm.
11  *
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 ?
29  *
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.
33  *
34  * Copyright (C) 2021 Hans de Goede <hdegoede@redhat.com>
35  */
36 
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>
45 
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)
50 
51 #define UG3105_INIT_POLL_COUNT					30
52 
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
58 
59 #define UG3105_MODE_STANDBY					0x00
60 #define UG3105_MODE_RUN						0x10
61 
62 #define UG3105_CTRL1_RESET_COULOMB_CNT				0x03
63 
64 #define UG3105_CURR_HYST_UA					65000
65 
66 #define UG3105_LOW_BAT_UV					3700000
67 #define UG3105_FULL_BAT_HYST_UV					38000
68 
69 struct ug3105_chip {
70 	struct i2c_client *client;
71 	struct power_supply *psy;
72 	struct power_supply_battery_info *info;
73 	struct delayed_work work;
74 	struct mutex lock;
75 	int ocv[UG3105_MOV_AVG_WINDOW];		/* micro-volt */
76 	int intern_res[UG3105_MOV_AVG_WINDOW];	/* milli-ohm */
77 	int poll_count;
78 	int ocv_avg_index;
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;
86 	int uv_per_unit;
87 	int ua_per_unit;
88 	int status;
89 	int capacity;
90 	bool supplied;
91 };
92 
ug3105_read_word(struct i2c_client * client,u8 reg)93 static int ug3105_read_word(struct i2c_client *client, u8 reg)
94 {
95 	int val;
96 
97 	val = i2c_smbus_read_word_data(client, reg);
98 	if (val < 0)
99 		dev_err(&client->dev, "Error reading reg 0x%02x\n", reg);
100 
101 	return val;
102 }
103 
ug3105_get_status(struct ug3105_chip * chip)104 static int ug3105_get_status(struct ug3105_chip *chip)
105 {
106 	int full = chip->info->constant_charge_voltage_max_uv - UG3105_FULL_BAT_HYST_UV;
107 
108 	if (chip->curr > UG3105_CURR_HYST_UA)
109 		return POWER_SUPPLY_STATUS_CHARGING;
110 
111 	if (chip->curr < -UG3105_CURR_HYST_UA)
112 		return POWER_SUPPLY_STATUS_DISCHARGING;
113 
114 	if (chip->supplied && chip->ocv_avg > full)
115 		return POWER_SUPPLY_STATUS_FULL;
116 
117 	return POWER_SUPPLY_STATUS_NOT_CHARGING;
118 }
119 
ug3105_get_capacity(struct ug3105_chip * chip)120 static int ug3105_get_capacity(struct ug3105_chip *chip)
121 {
122 	/*
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.
126 	 */
127 	static const int ocv_capacity_tbl[23] = {
128 		3350000,
129 		3610000,
130 		3690000,
131 		3710000,
132 		3730000,
133 		3750000,
134 		3770000,
135 		3786667,
136 		3803333,
137 		3820000,
138 		3836667,
139 		3853333,
140 		3870000,
141 		3907500,
142 		3945000,
143 		3982500,
144 		4020000,
145 		4075000,
146 		4110000,
147 		4150000,
148 		4200000,
149 		4250000,
150 		4300000,
151 	};
152 	int i, ocv_diff, ocv_step;
153 
154 	if (chip->ocv_avg < ocv_capacity_tbl[0])
155 		return 0;
156 
157 	if (chip->status == POWER_SUPPLY_STATUS_FULL)
158 		return 100;
159 
160 	for (i = 1; i < ARRAY_SIZE(ocv_capacity_tbl); i++) {
161 		if (chip->ocv_avg > ocv_capacity_tbl[i])
162 			continue;
163 
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;
169 		else
170 			return i * 5 - ocv_diff * 5 / ocv_step;
171 	}
172 
173 	return 100;
174 }
175 
ug3105_work(struct work_struct * work)176 static void ug3105_work(struct work_struct *work)
177 {
178 	struct ug3105_chip *chip = container_of(work, struct ug3105_chip,
179 						work.work);
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;
186 
187 	mutex_lock(&chip->lock);
188 
189 	psy = chip->psy;
190 	if (!psy)
191 		goto out;
192 
193 	val = ug3105_read_word(chip->client, UG3105_REG_BAT_VOLT);
194 	if (val < 0)
195 		goto out;
196 	chip->volt = val * chip->uv_per_unit;
197 
198 	val = ug3105_read_word(chip->client, UG3105_REG_BAT_CURR);
199 	if (val < 0)
200 		goto out;
201 	chip->curr = (s16)val * chip->ua_per_unit;
202 
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;
206 	chip->poll_count++;
207 
208 	/*
209 	 * See possible improvements comment above.
210 	 *
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);
214 	 *	if (val < 0)
215 	 *		goto out;
216 	 *
217 	 *	i2c_smbus_write_byte_data(chip->client, UG3105_REG_CTRL1,
218 	 *				  UG3105_CTRL1_RESET_COULOMB_CNT);
219 	 *
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);
223 	 * }
224 	 */
225 
226 	chip->ocv_avg = 0;
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;
231 
232 	chip->supplied = power_supply_am_i_supplied(psy);
233 	chip->status = ug3105_get_status(chip);
234 	chip->capacity = ug3105_get_capacity(chip);
235 
236 	/*
237 	 * Skip internal resistance calc on charger [un]plug and
238 	 * when the battery is almost empty (voltage low).
239 	 */
240 	if (chip->supplied != prev_supplied ||
241 	    chip->volt < UG3105_LOW_BAT_UV ||
242 	    chip->poll_count < 2)
243 		goto out;
244 
245 	/*
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.
250 	 */
251 	curr_diff = abs(chip->curr - prev_curr);
252 	if (curr_diff < UG3105_CURR_HYST_UA)
253 		goto out;
254 
255 	volt_diff = abs(chip->volt - prev_volt);
256 	res = volt_diff * 1000 / curr_diff;
257 
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);
261 		goto out;
262 	}
263 
264 	dev_dbg(&chip->client->dev, "Internal resistance %d mOhm\n", res);
265 
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++;
269 
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;
275 
276 out:
277 	mutex_unlock(&chip->lock);
278 
279 	queue_delayed_work(system_wq, &chip->work,
280 			   (chip->poll_count <= UG3105_INIT_POLL_COUNT) ?
281 					UG3105_INIT_POLL_TIME : UG3105_POLL_TIME);
282 
283 	if (chip->status != prev_status && psy)
284 		power_supply_changed(psy);
285 }
286 
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,
296 };
297 
ug3105_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)298 static int ug3105_get_property(struct power_supply *psy,
299 			       enum power_supply_property psp,
300 			       union power_supply_propval *val)
301 {
302 	struct ug3105_chip *chip = power_supply_get_drvdata(psy);
303 	int ret = 0;
304 
305 	mutex_lock(&chip->lock);
306 
307 	if (!chip->psy) {
308 		ret = -EAGAIN;
309 		goto out;
310 	}
311 
312 	switch (psp) {
313 	case POWER_SUPPLY_PROP_STATUS:
314 		val->intval = chip->status;
315 		break;
316 	case POWER_SUPPLY_PROP_PRESENT:
317 		val->intval = 1;
318 		break;
319 	case POWER_SUPPLY_PROP_TECHNOLOGY:
320 		val->intval = chip->info->technology;
321 		break;
322 	case POWER_SUPPLY_PROP_SCOPE:
323 		val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
324 		break;
325 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
326 		ret = ug3105_read_word(chip->client, UG3105_REG_BAT_VOLT);
327 		if (ret < 0)
328 			break;
329 		val->intval = ret * chip->uv_per_unit;
330 		ret = 0;
331 		break;
332 	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
333 		val->intval = chip->ocv_avg;
334 		break;
335 	case POWER_SUPPLY_PROP_CURRENT_NOW:
336 		ret = ug3105_read_word(chip->client, UG3105_REG_BAT_CURR);
337 		if (ret < 0)
338 			break;
339 		val->intval = (s16)ret * chip->ua_per_unit;
340 		ret = 0;
341 		break;
342 	case POWER_SUPPLY_PROP_CAPACITY:
343 		val->intval = chip->capacity;
344 		break;
345 	default:
346 		ret = -EINVAL;
347 	}
348 
349 out:
350 	mutex_unlock(&chip->lock);
351 	return ret;
352 }
353 
ug3105_external_power_changed(struct power_supply * psy)354 static void ug3105_external_power_changed(struct power_supply *psy)
355 {
356 	struct ug3105_chip *chip = power_supply_get_drvdata(psy);
357 
358 	dev_dbg(&chip->client->dev, "external power changed\n");
359 	mod_delayed_work(system_wq, &chip->work, UG3105_SETTLE_TIME);
360 }
361 
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),
369 };
370 
ug3105_init(struct ug3105_chip * chip)371 static void ug3105_init(struct ug3105_chip *chip)
372 {
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,
377 				  UG3105_MODE_RUN);
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);
382 }
383 
ug3105_probe(struct i2c_client * client)384 static int ug3105_probe(struct i2c_client *client)
385 {
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;
391 	int ret;
392 
393 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
394 	if (!chip)
395 		return -ENOMEM;
396 
397 	chip->client = client;
398 	mutex_init(&chip->lock);
399 	ret = devm_delayed_work_autocancel(dev, &chip->work, ug3105_work);
400 	if (ret)
401 		return ret;
402 
403 	psy_cfg.drv_data = chip;
404 	psy = devm_power_supply_register(dev, &ug3105_psy_desc, &psy_cfg);
405 	if (IS_ERR(psy))
406 		return PTR_ERR(psy);
407 
408 	ret = power_supply_get_battery_info(psy, &chip->info);
409 	if (ret)
410 		return ret;
411 
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");
415 		return -ENODEV;
416 	}
417 
418 	device_property_read_u32(dev, "upisemi,rsns-microohm", &curr_sense_res_uohm);
419 
420 	/*
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).
423 	 */
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;
427 
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;
434 
435 	mutex_lock(&chip->lock);
436 	chip->psy = psy;
437 	mutex_unlock(&chip->lock);
438 
439 	ug3105_init(chip);
440 
441 	i2c_set_clientdata(client, chip);
442 	return 0;
443 }
444 
ug3105_suspend(struct device * dev)445 static int __maybe_unused ug3105_suspend(struct device *dev)
446 {
447 	struct ug3105_chip *chip = dev_get_drvdata(dev);
448 
449 	cancel_delayed_work_sync(&chip->work);
450 	i2c_smbus_write_byte_data(chip->client, UG3105_REG_MODE,
451 				  UG3105_MODE_STANDBY);
452 
453 	return 0;
454 }
455 
ug3105_resume(struct device * dev)456 static int __maybe_unused ug3105_resume(struct device *dev)
457 {
458 	struct ug3105_chip *chip = dev_get_drvdata(dev);
459 
460 	ug3105_init(chip);
461 
462 	return 0;
463 }
464 
465 static SIMPLE_DEV_PM_OPS(ug3105_pm_ops, ug3105_suspend,
466 			ug3105_resume);
467 
468 static const struct i2c_device_id ug3105_id[] = {
469 	{ "ug3105" },
470 	{ }
471 };
472 MODULE_DEVICE_TABLE(i2c, ug3105_id);
473 
474 static struct i2c_driver ug3105_i2c_driver = {
475 	.driver	= {
476 		.name = "ug3105",
477 		.pm = &ug3105_pm_ops,
478 	},
479 	.probe = ug3105_probe,
480 	.id_table = ug3105_id,
481 };
482 module_i2c_driver(ug3105_i2c_driver);
483 
484 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com");
485 MODULE_DESCRIPTION("uPI uG3105 battery monitor driver");
486 MODULE_LICENSE("GPL");
487