xref: /openbmc/linux/drivers/power/supply/da9150-fg.c (revision 0a94608f)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * DA9150 Fuel-Gauge Driver
4  *
5  * Copyright (c) 2015 Dialog Semiconductor
6  *
7  * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/of.h>
14 #include <linux/of_platform.h>
15 #include <linux/slab.h>
16 #include <linux/interrupt.h>
17 #include <linux/delay.h>
18 #include <linux/power_supply.h>
19 #include <linux/list.h>
20 #include <asm/div64.h>
21 #include <linux/mfd/da9150/core.h>
22 #include <linux/mfd/da9150/registers.h>
23 #include <linux/devm-helpers.h>
24 
25 /* Core2Wire */
26 #define DA9150_QIF_READ		(0x0 << 7)
27 #define DA9150_QIF_WRITE	(0x1 << 7)
28 #define DA9150_QIF_CODE_MASK	0x7F
29 
30 #define DA9150_QIF_BYTE_SIZE	8
31 #define DA9150_QIF_BYTE_MASK	0xFF
32 #define DA9150_QIF_SHORT_SIZE	2
33 #define DA9150_QIF_LONG_SIZE	4
34 
35 /* QIF Codes */
36 #define DA9150_QIF_UAVG			6
37 #define DA9150_QIF_UAVG_SIZE		DA9150_QIF_LONG_SIZE
38 #define DA9150_QIF_IAVG			8
39 #define DA9150_QIF_IAVG_SIZE		DA9150_QIF_LONG_SIZE
40 #define DA9150_QIF_NTCAVG		12
41 #define DA9150_QIF_NTCAVG_SIZE		DA9150_QIF_LONG_SIZE
42 #define DA9150_QIF_SHUNT_VAL		36
43 #define DA9150_QIF_SHUNT_VAL_SIZE	DA9150_QIF_SHORT_SIZE
44 #define DA9150_QIF_SD_GAIN		38
45 #define DA9150_QIF_SD_GAIN_SIZE		DA9150_QIF_LONG_SIZE
46 #define DA9150_QIF_FCC_MAH		40
47 #define DA9150_QIF_FCC_MAH_SIZE		DA9150_QIF_SHORT_SIZE
48 #define DA9150_QIF_SOC_PCT		43
49 #define DA9150_QIF_SOC_PCT_SIZE		DA9150_QIF_SHORT_SIZE
50 #define DA9150_QIF_CHARGE_LIMIT		44
51 #define DA9150_QIF_CHARGE_LIMIT_SIZE	DA9150_QIF_SHORT_SIZE
52 #define DA9150_QIF_DISCHARGE_LIMIT	45
53 #define DA9150_QIF_DISCHARGE_LIMIT_SIZE	DA9150_QIF_SHORT_SIZE
54 #define DA9150_QIF_FW_MAIN_VER		118
55 #define DA9150_QIF_FW_MAIN_VER_SIZE	DA9150_QIF_SHORT_SIZE
56 #define DA9150_QIF_E_FG_STATUS		126
57 #define DA9150_QIF_E_FG_STATUS_SIZE	DA9150_QIF_SHORT_SIZE
58 #define DA9150_QIF_SYNC			127
59 #define DA9150_QIF_SYNC_SIZE		DA9150_QIF_SHORT_SIZE
60 #define DA9150_QIF_MAX_CODES		128
61 
62 /* QIF Sync Timeout */
63 #define DA9150_QIF_SYNC_TIMEOUT		1000
64 #define DA9150_QIF_SYNC_RETRIES		10
65 
66 /* QIF E_FG_STATUS */
67 #define DA9150_FG_IRQ_LOW_SOC_MASK	(1 << 0)
68 #define DA9150_FG_IRQ_HIGH_SOC_MASK	(1 << 1)
69 #define DA9150_FG_IRQ_SOC_MASK	\
70 	(DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK)
71 
72 /* Private data */
73 struct da9150_fg {
74 	struct da9150 *da9150;
75 	struct device *dev;
76 
77 	struct mutex io_lock;
78 
79 	struct power_supply *battery;
80 	struct delayed_work work;
81 	u32 interval;
82 
83 	int warn_soc;
84 	int crit_soc;
85 	int soc;
86 };
87 
88 /* Battery Properties */
89 static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)
90 
91 {
92 	u8 buf[DA9150_QIF_LONG_SIZE];
93 	u8 read_addr;
94 	u32 res = 0;
95 	int i;
96 
97 	/* Set QIF code (READ mode) */
98 	read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ;
99 
100 	da9150_read_qif(fg->da9150, read_addr, size, buf);
101 	for (i = 0; i < size; ++i)
102 		res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE));
103 
104 	return res;
105 }
106 
107 static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
108 				 u32 val)
109 
110 {
111 	u8 buf[DA9150_QIF_LONG_SIZE];
112 	u8 write_addr;
113 	int i;
114 
115 	/* Set QIF code (WRITE mode) */
116 	write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE;
117 
118 	for (i = 0; i < size; ++i) {
119 		buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) &
120 			 DA9150_QIF_BYTE_MASK;
121 	}
122 	da9150_write_qif(fg->da9150, write_addr, size, buf);
123 }
124 
125 /* Trigger QIF Sync to update QIF readable data */
126 static void da9150_fg_read_sync_start(struct da9150_fg *fg)
127 {
128 	int i = 0;
129 	u32 res = 0;
130 
131 	mutex_lock(&fg->io_lock);
132 
133 	/* Check if QIF sync already requested, and write to sync if not */
134 	res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
135 				  DA9150_QIF_SYNC_SIZE);
136 	if (res > 0)
137 		da9150_fg_write_attr(fg, DA9150_QIF_SYNC,
138 				     DA9150_QIF_SYNC_SIZE, 0);
139 
140 	/* Wait for sync to complete */
141 	res = 0;
142 	while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
143 		usleep_range(DA9150_QIF_SYNC_TIMEOUT,
144 			     DA9150_QIF_SYNC_TIMEOUT * 2);
145 		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
146 					  DA9150_QIF_SYNC_SIZE);
147 	}
148 
149 	/* Check if sync completed */
150 	if (res == 0)
151 		dev_err(fg->dev, "Failed to perform QIF read sync!\n");
152 }
153 
154 /*
155  * Should always be called after QIF sync read has been performed, and all
156  * attributes required have been accessed.
157  */
158 static inline void da9150_fg_read_sync_end(struct da9150_fg *fg)
159 {
160 	mutex_unlock(&fg->io_lock);
161 }
162 
163 /* Sync read of single QIF attribute */
164 static u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size)
165 {
166 	u32 val;
167 
168 	da9150_fg_read_sync_start(fg);
169 	val = da9150_fg_read_attr(fg, code, size);
170 	da9150_fg_read_sync_end(fg);
171 
172 	return val;
173 }
174 
175 /* Wait for QIF Sync, write QIF data and wait for ack */
176 static void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size,
177 				      u32 val)
178 {
179 	int i = 0;
180 	u32 res = 0, sync_val;
181 
182 	mutex_lock(&fg->io_lock);
183 
184 	/* Check if QIF sync already requested */
185 	res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
186 				  DA9150_QIF_SYNC_SIZE);
187 
188 	/* Wait for an existing sync to complete */
189 	while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
190 		usleep_range(DA9150_QIF_SYNC_TIMEOUT,
191 			     DA9150_QIF_SYNC_TIMEOUT * 2);
192 		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
193 					  DA9150_QIF_SYNC_SIZE);
194 	}
195 
196 	if (res == 0) {
197 		dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n");
198 		mutex_unlock(&fg->io_lock);
199 		return;
200 	}
201 
202 	/* Write value for QIF code */
203 	da9150_fg_write_attr(fg, code, size, val);
204 
205 	/* Wait for write acknowledgment */
206 	i = 0;
207 	sync_val = res;
208 	while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
209 		usleep_range(DA9150_QIF_SYNC_TIMEOUT,
210 			     DA9150_QIF_SYNC_TIMEOUT * 2);
211 		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
212 					  DA9150_QIF_SYNC_SIZE);
213 	}
214 
215 	mutex_unlock(&fg->io_lock);
216 
217 	/* Check write was actually successful */
218 	if (res != (sync_val + 1))
219 		dev_err(fg->dev, "Error performing QIF sync write for code %d\n",
220 			code);
221 }
222 
223 /* Power Supply attributes */
224 static int da9150_fg_capacity(struct da9150_fg *fg,
225 			      union power_supply_propval *val)
226 {
227 	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
228 					       DA9150_QIF_SOC_PCT_SIZE);
229 
230 	if (val->intval > 100)
231 		val->intval = 100;
232 
233 	return 0;
234 }
235 
236 static int da9150_fg_current_avg(struct da9150_fg *fg,
237 				 union power_supply_propval *val)
238 {
239 	u32 iavg, sd_gain, shunt_val;
240 	u64 div, res;
241 
242 	da9150_fg_read_sync_start(fg);
243 	iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG,
244 				   DA9150_QIF_IAVG_SIZE);
245 	shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL,
246 					DA9150_QIF_SHUNT_VAL_SIZE);
247 	sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN,
248 				      DA9150_QIF_SD_GAIN_SIZE);
249 	da9150_fg_read_sync_end(fg);
250 
251 	div = (u64) (sd_gain * shunt_val * 65536ULL);
252 	do_div(div, 1000000);
253 	res = (u64) (iavg * 1000000ULL);
254 	do_div(res, div);
255 
256 	val->intval = (int) res;
257 
258 	return 0;
259 }
260 
261 static int da9150_fg_voltage_avg(struct da9150_fg *fg,
262 				 union power_supply_propval *val)
263 {
264 	u64 res;
265 
266 	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG,
267 					       DA9150_QIF_UAVG_SIZE);
268 
269 	res = (u64) (val->intval * 186ULL);
270 	do_div(res, 10000);
271 	val->intval = (int) res;
272 
273 	return 0;
274 }
275 
276 static int da9150_fg_charge_full(struct da9150_fg *fg,
277 				 union power_supply_propval *val)
278 {
279 	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH,
280 					       DA9150_QIF_FCC_MAH_SIZE);
281 
282 	val->intval = val->intval * 1000;
283 
284 	return 0;
285 }
286 
287 /*
288  * Temperature reading from device is only valid if battery/system provides
289  * valid NTC to associated pin of DA9150 chip.
290  */
291 static int da9150_fg_temp(struct da9150_fg *fg,
292 			  union power_supply_propval *val)
293 {
294 	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG,
295 					       DA9150_QIF_NTCAVG_SIZE);
296 
297 	val->intval = (val->intval * 10) / 1048576;
298 
299 	return 0;
300 }
301 
302 static enum power_supply_property da9150_fg_props[] = {
303 	POWER_SUPPLY_PROP_CAPACITY,
304 	POWER_SUPPLY_PROP_CURRENT_AVG,
305 	POWER_SUPPLY_PROP_VOLTAGE_AVG,
306 	POWER_SUPPLY_PROP_CHARGE_FULL,
307 	POWER_SUPPLY_PROP_TEMP,
308 };
309 
310 static int da9150_fg_get_prop(struct power_supply *psy,
311 			      enum power_supply_property psp,
312 			      union power_supply_propval *val)
313 {
314 	struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent);
315 	int ret;
316 
317 	switch (psp) {
318 	case POWER_SUPPLY_PROP_CAPACITY:
319 		ret = da9150_fg_capacity(fg, val);
320 		break;
321 	case POWER_SUPPLY_PROP_CURRENT_AVG:
322 		ret = da9150_fg_current_avg(fg, val);
323 		break;
324 	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
325 		ret = da9150_fg_voltage_avg(fg, val);
326 		break;
327 	case POWER_SUPPLY_PROP_CHARGE_FULL:
328 		ret = da9150_fg_charge_full(fg, val);
329 		break;
330 	case POWER_SUPPLY_PROP_TEMP:
331 		ret = da9150_fg_temp(fg, val);
332 		break;
333 	default:
334 		ret = -EINVAL;
335 		break;
336 	}
337 
338 	return ret;
339 }
340 
341 /* Repeated SOC check */
342 static bool da9150_fg_soc_changed(struct da9150_fg *fg)
343 {
344 	union power_supply_propval val;
345 
346 	da9150_fg_capacity(fg, &val);
347 	if (val.intval != fg->soc) {
348 		fg->soc = val.intval;
349 		return true;
350 	}
351 
352 	return false;
353 }
354 
355 static void da9150_fg_work(struct work_struct *work)
356 {
357 	struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work);
358 
359 	/* Report if SOC has changed */
360 	if (da9150_fg_soc_changed(fg))
361 		power_supply_changed(fg->battery);
362 
363 	schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval));
364 }
365 
366 /* SOC level event configuration */
367 static void da9150_fg_soc_event_config(struct da9150_fg *fg)
368 {
369 	int soc;
370 
371 	soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
372 				       DA9150_QIF_SOC_PCT_SIZE);
373 
374 	if (soc > fg->warn_soc) {
375 		/* If SOC > warn level, set discharge warn level event */
376 		da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
377 					  DA9150_QIF_DISCHARGE_LIMIT_SIZE,
378 					  fg->warn_soc + 1);
379 	} else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) {
380 		/*
381 		 * If SOC <= warn level, set discharge crit level event,
382 		 * and set charge warn level event.
383 		 */
384 		da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
385 					  DA9150_QIF_DISCHARGE_LIMIT_SIZE,
386 					  fg->crit_soc + 1);
387 
388 		da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
389 					  DA9150_QIF_CHARGE_LIMIT_SIZE,
390 					  fg->warn_soc);
391 	} else if (soc <= fg->crit_soc) {
392 		/* If SOC <= crit level, set charge crit level event */
393 		da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
394 					  DA9150_QIF_CHARGE_LIMIT_SIZE,
395 					  fg->crit_soc);
396 	}
397 }
398 
399 static irqreturn_t da9150_fg_irq(int irq, void *data)
400 {
401 	struct da9150_fg *fg = data;
402 	u32 e_fg_status;
403 
404 	/* Read FG IRQ status info */
405 	e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS,
406 					  DA9150_QIF_E_FG_STATUS_SIZE);
407 
408 	/* Handle warning/critical threhold events */
409 	if (e_fg_status & DA9150_FG_IRQ_SOC_MASK)
410 		da9150_fg_soc_event_config(fg);
411 
412 	/* Clear any FG IRQs */
413 	da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS,
414 			     DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status);
415 
416 	return IRQ_HANDLED;
417 }
418 
419 static struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev)
420 {
421 	struct device_node *fg_node = dev->of_node;
422 	struct da9150_fg_pdata *pdata;
423 
424 	pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL);
425 	if (!pdata)
426 		return NULL;
427 
428 	of_property_read_u32(fg_node, "dlg,update-interval",
429 			     &pdata->update_interval);
430 	of_property_read_u8(fg_node, "dlg,warn-soc-level",
431 			    &pdata->warn_soc_lvl);
432 	of_property_read_u8(fg_node, "dlg,crit-soc-level",
433 			    &pdata->crit_soc_lvl);
434 
435 	return pdata;
436 }
437 
438 static const struct power_supply_desc fg_desc = {
439 	.name		= "da9150-fg",
440 	.type		= POWER_SUPPLY_TYPE_BATTERY,
441 	.properties	= da9150_fg_props,
442 	.num_properties	= ARRAY_SIZE(da9150_fg_props),
443 	.get_property	= da9150_fg_get_prop,
444 };
445 
446 static int da9150_fg_probe(struct platform_device *pdev)
447 {
448 	struct device *dev = &pdev->dev;
449 	struct da9150 *da9150 = dev_get_drvdata(dev->parent);
450 	struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev);
451 	struct da9150_fg *fg;
452 	int ver, irq, ret = 0;
453 
454 	fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL);
455 	if (fg == NULL)
456 		return -ENOMEM;
457 
458 	platform_set_drvdata(pdev, fg);
459 	fg->da9150 = da9150;
460 	fg->dev = dev;
461 
462 	mutex_init(&fg->io_lock);
463 
464 	/* Enable QIF */
465 	da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK,
466 			DA9150_FG_QIF_EN_MASK);
467 
468 	fg->battery = devm_power_supply_register(dev, &fg_desc, NULL);
469 	if (IS_ERR(fg->battery)) {
470 		ret = PTR_ERR(fg->battery);
471 		return ret;
472 	}
473 
474 	ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER,
475 				  DA9150_QIF_FW_MAIN_VER_SIZE);
476 	dev_info(dev, "Version: 0x%x\n", ver);
477 
478 	/* Handle DT data if provided */
479 	if (dev->of_node) {
480 		fg_pdata = da9150_fg_dt_pdata(dev);
481 		dev->platform_data = fg_pdata;
482 	}
483 
484 	/* Handle any pdata provided */
485 	if (fg_pdata) {
486 		fg->interval = fg_pdata->update_interval;
487 
488 		if (fg_pdata->warn_soc_lvl > 100)
489 			dev_warn(dev, "Invalid SOC warning level provided, Ignoring");
490 		else
491 			fg->warn_soc = fg_pdata->warn_soc_lvl;
492 
493 		if ((fg_pdata->crit_soc_lvl > 100) ||
494 		    (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl))
495 			dev_warn(dev, "Invalid SOC critical level provided, Ignoring");
496 		else
497 			fg->crit_soc = fg_pdata->crit_soc_lvl;
498 
499 
500 	}
501 
502 	/* Configure initial SOC level events */
503 	da9150_fg_soc_event_config(fg);
504 
505 	/*
506 	 * If an interval period has been provided then setup repeating
507 	 * work for reporting data updates.
508 	 */
509 	if (fg->interval) {
510 		ret = devm_delayed_work_autocancel(dev, &fg->work,
511 						   da9150_fg_work);
512 		if (ret) {
513 			dev_err(dev, "Failed to init work\n");
514 			return ret;
515 		}
516 
517 		schedule_delayed_work(&fg->work,
518 				      msecs_to_jiffies(fg->interval));
519 	}
520 
521 	/* Register IRQ */
522 	irq = platform_get_irq_byname(pdev, "FG");
523 	if (irq < 0)
524 		return irq;
525 
526 	ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq,
527 					IRQF_ONESHOT, "FG", fg);
528 	if (ret) {
529 		dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
530 		return ret;
531 	}
532 
533 	return 0;
534 }
535 
536 static int da9150_fg_resume(struct platform_device *pdev)
537 {
538 	struct da9150_fg *fg = platform_get_drvdata(pdev);
539 
540 	/*
541 	 * Trigger SOC check to happen now so as to indicate any value change
542 	 * since last check before suspend.
543 	 */
544 	if (fg->interval)
545 		flush_delayed_work(&fg->work);
546 
547 	return 0;
548 }
549 
550 static struct platform_driver da9150_fg_driver = {
551 	.driver = {
552 		.name = "da9150-fuel-gauge",
553 	},
554 	.probe = da9150_fg_probe,
555 	.resume = da9150_fg_resume,
556 };
557 
558 module_platform_driver(da9150_fg_driver);
559 
560 MODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150");
561 MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
562 MODULE_LICENSE("GPL");
563