xref: /openbmc/linux/drivers/power/supply/da9150-fg.c (revision 2ce8284c)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * DA9150 Fuel-Gauge Driver
48c0984e5SSebastian Reichel  *
58c0984e5SSebastian Reichel  * Copyright (c) 2015 Dialog Semiconductor
68c0984e5SSebastian Reichel  *
78c0984e5SSebastian Reichel  * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
88c0984e5SSebastian Reichel  */
98c0984e5SSebastian Reichel 
108c0984e5SSebastian Reichel #include <linux/kernel.h>
118c0984e5SSebastian Reichel #include <linux/module.h>
128c0984e5SSebastian Reichel #include <linux/platform_device.h>
138c0984e5SSebastian Reichel #include <linux/of.h>
148c0984e5SSebastian Reichel #include <linux/slab.h>
158c0984e5SSebastian Reichel #include <linux/interrupt.h>
168c0984e5SSebastian Reichel #include <linux/delay.h>
178c0984e5SSebastian Reichel #include <linux/power_supply.h>
188c0984e5SSebastian Reichel #include <linux/list.h>
198c0984e5SSebastian Reichel #include <asm/div64.h>
208c0984e5SSebastian Reichel #include <linux/mfd/da9150/core.h>
218c0984e5SSebastian Reichel #include <linux/mfd/da9150/registers.h>
22419c0e9dSChristophe JAILLET #include <linux/devm-helpers.h>
238c0984e5SSebastian Reichel 
248c0984e5SSebastian Reichel /* Core2Wire */
258c0984e5SSebastian Reichel #define DA9150_QIF_READ		(0x0 << 7)
268c0984e5SSebastian Reichel #define DA9150_QIF_WRITE	(0x1 << 7)
278c0984e5SSebastian Reichel #define DA9150_QIF_CODE_MASK	0x7F
288c0984e5SSebastian Reichel 
298c0984e5SSebastian Reichel #define DA9150_QIF_BYTE_SIZE	8
308c0984e5SSebastian Reichel #define DA9150_QIF_BYTE_MASK	0xFF
318c0984e5SSebastian Reichel #define DA9150_QIF_SHORT_SIZE	2
328c0984e5SSebastian Reichel #define DA9150_QIF_LONG_SIZE	4
338c0984e5SSebastian Reichel 
348c0984e5SSebastian Reichel /* QIF Codes */
358c0984e5SSebastian Reichel #define DA9150_QIF_UAVG			6
368c0984e5SSebastian Reichel #define DA9150_QIF_UAVG_SIZE		DA9150_QIF_LONG_SIZE
378c0984e5SSebastian Reichel #define DA9150_QIF_IAVG			8
388c0984e5SSebastian Reichel #define DA9150_QIF_IAVG_SIZE		DA9150_QIF_LONG_SIZE
398c0984e5SSebastian Reichel #define DA9150_QIF_NTCAVG		12
408c0984e5SSebastian Reichel #define DA9150_QIF_NTCAVG_SIZE		DA9150_QIF_LONG_SIZE
418c0984e5SSebastian Reichel #define DA9150_QIF_SHUNT_VAL		36
428c0984e5SSebastian Reichel #define DA9150_QIF_SHUNT_VAL_SIZE	DA9150_QIF_SHORT_SIZE
438c0984e5SSebastian Reichel #define DA9150_QIF_SD_GAIN		38
448c0984e5SSebastian Reichel #define DA9150_QIF_SD_GAIN_SIZE		DA9150_QIF_LONG_SIZE
458c0984e5SSebastian Reichel #define DA9150_QIF_FCC_MAH		40
468c0984e5SSebastian Reichel #define DA9150_QIF_FCC_MAH_SIZE		DA9150_QIF_SHORT_SIZE
478c0984e5SSebastian Reichel #define DA9150_QIF_SOC_PCT		43
488c0984e5SSebastian Reichel #define DA9150_QIF_SOC_PCT_SIZE		DA9150_QIF_SHORT_SIZE
498c0984e5SSebastian Reichel #define DA9150_QIF_CHARGE_LIMIT		44
508c0984e5SSebastian Reichel #define DA9150_QIF_CHARGE_LIMIT_SIZE	DA9150_QIF_SHORT_SIZE
518c0984e5SSebastian Reichel #define DA9150_QIF_DISCHARGE_LIMIT	45
528c0984e5SSebastian Reichel #define DA9150_QIF_DISCHARGE_LIMIT_SIZE	DA9150_QIF_SHORT_SIZE
538c0984e5SSebastian Reichel #define DA9150_QIF_FW_MAIN_VER		118
548c0984e5SSebastian Reichel #define DA9150_QIF_FW_MAIN_VER_SIZE	DA9150_QIF_SHORT_SIZE
558c0984e5SSebastian Reichel #define DA9150_QIF_E_FG_STATUS		126
568c0984e5SSebastian Reichel #define DA9150_QIF_E_FG_STATUS_SIZE	DA9150_QIF_SHORT_SIZE
578c0984e5SSebastian Reichel #define DA9150_QIF_SYNC			127
588c0984e5SSebastian Reichel #define DA9150_QIF_SYNC_SIZE		DA9150_QIF_SHORT_SIZE
598c0984e5SSebastian Reichel #define DA9150_QIF_MAX_CODES		128
608c0984e5SSebastian Reichel 
618c0984e5SSebastian Reichel /* QIF Sync Timeout */
628c0984e5SSebastian Reichel #define DA9150_QIF_SYNC_TIMEOUT		1000
638c0984e5SSebastian Reichel #define DA9150_QIF_SYNC_RETRIES		10
648c0984e5SSebastian Reichel 
658c0984e5SSebastian Reichel /* QIF E_FG_STATUS */
668c0984e5SSebastian Reichel #define DA9150_FG_IRQ_LOW_SOC_MASK	(1 << 0)
678c0984e5SSebastian Reichel #define DA9150_FG_IRQ_HIGH_SOC_MASK	(1 << 1)
688c0984e5SSebastian Reichel #define DA9150_FG_IRQ_SOC_MASK	\
698c0984e5SSebastian Reichel 	(DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK)
708c0984e5SSebastian Reichel 
718c0984e5SSebastian Reichel /* Private data */
728c0984e5SSebastian Reichel struct da9150_fg {
738c0984e5SSebastian Reichel 	struct da9150 *da9150;
748c0984e5SSebastian Reichel 	struct device *dev;
758c0984e5SSebastian Reichel 
768c0984e5SSebastian Reichel 	struct mutex io_lock;
778c0984e5SSebastian Reichel 
788c0984e5SSebastian Reichel 	struct power_supply *battery;
798c0984e5SSebastian Reichel 	struct delayed_work work;
808c0984e5SSebastian Reichel 	u32 interval;
818c0984e5SSebastian Reichel 
828c0984e5SSebastian Reichel 	int warn_soc;
838c0984e5SSebastian Reichel 	int crit_soc;
848c0984e5SSebastian Reichel 	int soc;
858c0984e5SSebastian Reichel };
868c0984e5SSebastian Reichel 
878c0984e5SSebastian Reichel /* Battery Properties */
da9150_fg_read_attr(struct da9150_fg * fg,u8 code,u8 size)888c0984e5SSebastian Reichel static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)
898c0984e5SSebastian Reichel 
908c0984e5SSebastian Reichel {
91fc5a7f03SGustavo A. R. Silva 	u8 buf[DA9150_QIF_LONG_SIZE];
928c0984e5SSebastian Reichel 	u8 read_addr;
938c0984e5SSebastian Reichel 	u32 res = 0;
948c0984e5SSebastian Reichel 	int i;
958c0984e5SSebastian Reichel 
968c0984e5SSebastian Reichel 	/* Set QIF code (READ mode) */
978c0984e5SSebastian Reichel 	read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ;
988c0984e5SSebastian Reichel 
998c0984e5SSebastian Reichel 	da9150_read_qif(fg->da9150, read_addr, size, buf);
1008c0984e5SSebastian Reichel 	for (i = 0; i < size; ++i)
1018c0984e5SSebastian Reichel 		res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE));
1028c0984e5SSebastian Reichel 
1038c0984e5SSebastian Reichel 	return res;
1048c0984e5SSebastian Reichel }
1058c0984e5SSebastian Reichel 
da9150_fg_write_attr(struct da9150_fg * fg,u8 code,u8 size,u32 val)1068c0984e5SSebastian Reichel static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
1078c0984e5SSebastian Reichel 				 u32 val)
1088c0984e5SSebastian Reichel 
1098c0984e5SSebastian Reichel {
110fc5a7f03SGustavo A. R. Silva 	u8 buf[DA9150_QIF_LONG_SIZE];
1118c0984e5SSebastian Reichel 	u8 write_addr;
1128c0984e5SSebastian Reichel 	int i;
1138c0984e5SSebastian Reichel 
1148c0984e5SSebastian Reichel 	/* Set QIF code (WRITE mode) */
1158c0984e5SSebastian Reichel 	write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE;
1168c0984e5SSebastian Reichel 
1178c0984e5SSebastian Reichel 	for (i = 0; i < size; ++i) {
1188c0984e5SSebastian Reichel 		buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) &
1198c0984e5SSebastian Reichel 			 DA9150_QIF_BYTE_MASK;
1208c0984e5SSebastian Reichel 	}
1218c0984e5SSebastian Reichel 	da9150_write_qif(fg->da9150, write_addr, size, buf);
1228c0984e5SSebastian Reichel }
1238c0984e5SSebastian Reichel 
1248c0984e5SSebastian Reichel /* Trigger QIF Sync to update QIF readable data */
da9150_fg_read_sync_start(struct da9150_fg * fg)1258c0984e5SSebastian Reichel static void da9150_fg_read_sync_start(struct da9150_fg *fg)
1268c0984e5SSebastian Reichel {
1278c0984e5SSebastian Reichel 	int i = 0;
1288c0984e5SSebastian Reichel 	u32 res = 0;
1298c0984e5SSebastian Reichel 
1308c0984e5SSebastian Reichel 	mutex_lock(&fg->io_lock);
1318c0984e5SSebastian Reichel 
1328c0984e5SSebastian Reichel 	/* Check if QIF sync already requested, and write to sync if not */
1338c0984e5SSebastian Reichel 	res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
1348c0984e5SSebastian Reichel 				  DA9150_QIF_SYNC_SIZE);
1358c0984e5SSebastian Reichel 	if (res > 0)
1368c0984e5SSebastian Reichel 		da9150_fg_write_attr(fg, DA9150_QIF_SYNC,
1378c0984e5SSebastian Reichel 				     DA9150_QIF_SYNC_SIZE, 0);
1388c0984e5SSebastian Reichel 
1398c0984e5SSebastian Reichel 	/* Wait for sync to complete */
1408c0984e5SSebastian Reichel 	res = 0;
1418c0984e5SSebastian Reichel 	while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
1428c0984e5SSebastian Reichel 		usleep_range(DA9150_QIF_SYNC_TIMEOUT,
1438c0984e5SSebastian Reichel 			     DA9150_QIF_SYNC_TIMEOUT * 2);
1448c0984e5SSebastian Reichel 		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
1458c0984e5SSebastian Reichel 					  DA9150_QIF_SYNC_SIZE);
1468c0984e5SSebastian Reichel 	}
1478c0984e5SSebastian Reichel 
1488c0984e5SSebastian Reichel 	/* Check if sync completed */
1498c0984e5SSebastian Reichel 	if (res == 0)
1508c0984e5SSebastian Reichel 		dev_err(fg->dev, "Failed to perform QIF read sync!\n");
1518c0984e5SSebastian Reichel }
1528c0984e5SSebastian Reichel 
1538c0984e5SSebastian Reichel /*
1548c0984e5SSebastian Reichel  * Should always be called after QIF sync read has been performed, and all
1558c0984e5SSebastian Reichel  * attributes required have been accessed.
1568c0984e5SSebastian Reichel  */
da9150_fg_read_sync_end(struct da9150_fg * fg)1578c0984e5SSebastian Reichel static inline void da9150_fg_read_sync_end(struct da9150_fg *fg)
1588c0984e5SSebastian Reichel {
1598c0984e5SSebastian Reichel 	mutex_unlock(&fg->io_lock);
1608c0984e5SSebastian Reichel }
1618c0984e5SSebastian Reichel 
1628c0984e5SSebastian Reichel /* Sync read of single QIF attribute */
da9150_fg_read_attr_sync(struct da9150_fg * fg,u8 code,u8 size)1638c0984e5SSebastian Reichel static u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size)
1648c0984e5SSebastian Reichel {
1658c0984e5SSebastian Reichel 	u32 val;
1668c0984e5SSebastian Reichel 
1678c0984e5SSebastian Reichel 	da9150_fg_read_sync_start(fg);
1688c0984e5SSebastian Reichel 	val = da9150_fg_read_attr(fg, code, size);
1698c0984e5SSebastian Reichel 	da9150_fg_read_sync_end(fg);
1708c0984e5SSebastian Reichel 
1718c0984e5SSebastian Reichel 	return val;
1728c0984e5SSebastian Reichel }
1738c0984e5SSebastian Reichel 
1748c0984e5SSebastian Reichel /* Wait for QIF Sync, write QIF data and wait for ack */
da9150_fg_write_attr_sync(struct da9150_fg * fg,u8 code,u8 size,u32 val)1758c0984e5SSebastian Reichel static void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size,
1768c0984e5SSebastian Reichel 				      u32 val)
1778c0984e5SSebastian Reichel {
1788c0984e5SSebastian Reichel 	int i = 0;
1798c0984e5SSebastian Reichel 	u32 res = 0, sync_val;
1808c0984e5SSebastian Reichel 
1818c0984e5SSebastian Reichel 	mutex_lock(&fg->io_lock);
1828c0984e5SSebastian Reichel 
1838c0984e5SSebastian Reichel 	/* Check if QIF sync already requested */
1848c0984e5SSebastian Reichel 	res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
1858c0984e5SSebastian Reichel 				  DA9150_QIF_SYNC_SIZE);
1868c0984e5SSebastian Reichel 
1878c0984e5SSebastian Reichel 	/* Wait for an existing sync to complete */
1888c0984e5SSebastian Reichel 	while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
1898c0984e5SSebastian Reichel 		usleep_range(DA9150_QIF_SYNC_TIMEOUT,
1908c0984e5SSebastian Reichel 			     DA9150_QIF_SYNC_TIMEOUT * 2);
1918c0984e5SSebastian Reichel 		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
1928c0984e5SSebastian Reichel 					  DA9150_QIF_SYNC_SIZE);
1938c0984e5SSebastian Reichel 	}
1948c0984e5SSebastian Reichel 
1958c0984e5SSebastian Reichel 	if (res == 0) {
1968c0984e5SSebastian Reichel 		dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n");
1978c0984e5SSebastian Reichel 		mutex_unlock(&fg->io_lock);
1988c0984e5SSebastian Reichel 		return;
1998c0984e5SSebastian Reichel 	}
2008c0984e5SSebastian Reichel 
2018c0984e5SSebastian Reichel 	/* Write value for QIF code */
2028c0984e5SSebastian Reichel 	da9150_fg_write_attr(fg, code, size, val);
2038c0984e5SSebastian Reichel 
2048c0984e5SSebastian Reichel 	/* Wait for write acknowledgment */
2058c0984e5SSebastian Reichel 	i = 0;
2068c0984e5SSebastian Reichel 	sync_val = res;
2078c0984e5SSebastian Reichel 	while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
2088c0984e5SSebastian Reichel 		usleep_range(DA9150_QIF_SYNC_TIMEOUT,
2098c0984e5SSebastian Reichel 			     DA9150_QIF_SYNC_TIMEOUT * 2);
2108c0984e5SSebastian Reichel 		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
2118c0984e5SSebastian Reichel 					  DA9150_QIF_SYNC_SIZE);
2128c0984e5SSebastian Reichel 	}
2138c0984e5SSebastian Reichel 
2148c0984e5SSebastian Reichel 	mutex_unlock(&fg->io_lock);
2158c0984e5SSebastian Reichel 
2168c0984e5SSebastian Reichel 	/* Check write was actually successful */
2178c0984e5SSebastian Reichel 	if (res != (sync_val + 1))
2188c0984e5SSebastian Reichel 		dev_err(fg->dev, "Error performing QIF sync write for code %d\n",
2198c0984e5SSebastian Reichel 			code);
2208c0984e5SSebastian Reichel }
2218c0984e5SSebastian Reichel 
2228c0984e5SSebastian Reichel /* Power Supply attributes */
da9150_fg_capacity(struct da9150_fg * fg,union power_supply_propval * val)2238c0984e5SSebastian Reichel static int da9150_fg_capacity(struct da9150_fg *fg,
2248c0984e5SSebastian Reichel 			      union power_supply_propval *val)
2258c0984e5SSebastian Reichel {
2268c0984e5SSebastian Reichel 	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
2278c0984e5SSebastian Reichel 					       DA9150_QIF_SOC_PCT_SIZE);
2288c0984e5SSebastian Reichel 
2298c0984e5SSebastian Reichel 	if (val->intval > 100)
2308c0984e5SSebastian Reichel 		val->intval = 100;
2318c0984e5SSebastian Reichel 
2328c0984e5SSebastian Reichel 	return 0;
2338c0984e5SSebastian Reichel }
2348c0984e5SSebastian Reichel 
da9150_fg_current_avg(struct da9150_fg * fg,union power_supply_propval * val)2358c0984e5SSebastian Reichel static int da9150_fg_current_avg(struct da9150_fg *fg,
2368c0984e5SSebastian Reichel 				 union power_supply_propval *val)
2378c0984e5SSebastian Reichel {
2388c0984e5SSebastian Reichel 	u32 iavg, sd_gain, shunt_val;
2398c0984e5SSebastian Reichel 	u64 div, res;
2408c0984e5SSebastian Reichel 
2418c0984e5SSebastian Reichel 	da9150_fg_read_sync_start(fg);
2428c0984e5SSebastian Reichel 	iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG,
2438c0984e5SSebastian Reichel 				   DA9150_QIF_IAVG_SIZE);
2448c0984e5SSebastian Reichel 	shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL,
2458c0984e5SSebastian Reichel 					DA9150_QIF_SHUNT_VAL_SIZE);
2468c0984e5SSebastian Reichel 	sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN,
2478c0984e5SSebastian Reichel 				      DA9150_QIF_SD_GAIN_SIZE);
2488c0984e5SSebastian Reichel 	da9150_fg_read_sync_end(fg);
2498c0984e5SSebastian Reichel 
2508c0984e5SSebastian Reichel 	div = (u64) (sd_gain * shunt_val * 65536ULL);
2518c0984e5SSebastian Reichel 	do_div(div, 1000000);
2528c0984e5SSebastian Reichel 	res = (u64) (iavg * 1000000ULL);
2538c0984e5SSebastian Reichel 	do_div(res, div);
2548c0984e5SSebastian Reichel 
2558c0984e5SSebastian Reichel 	val->intval = (int) res;
2568c0984e5SSebastian Reichel 
2578c0984e5SSebastian Reichel 	return 0;
2588c0984e5SSebastian Reichel }
2598c0984e5SSebastian Reichel 
da9150_fg_voltage_avg(struct da9150_fg * fg,union power_supply_propval * val)2608c0984e5SSebastian Reichel static int da9150_fg_voltage_avg(struct da9150_fg *fg,
2618c0984e5SSebastian Reichel 				 union power_supply_propval *val)
2628c0984e5SSebastian Reichel {
2638c0984e5SSebastian Reichel 	u64 res;
2648c0984e5SSebastian Reichel 
2658c0984e5SSebastian Reichel 	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG,
2668c0984e5SSebastian Reichel 					       DA9150_QIF_UAVG_SIZE);
2678c0984e5SSebastian Reichel 
2688c0984e5SSebastian Reichel 	res = (u64) (val->intval * 186ULL);
2698c0984e5SSebastian Reichel 	do_div(res, 10000);
2708c0984e5SSebastian Reichel 	val->intval = (int) res;
2718c0984e5SSebastian Reichel 
2728c0984e5SSebastian Reichel 	return 0;
2738c0984e5SSebastian Reichel }
2748c0984e5SSebastian Reichel 
da9150_fg_charge_full(struct da9150_fg * fg,union power_supply_propval * val)2758c0984e5SSebastian Reichel static int da9150_fg_charge_full(struct da9150_fg *fg,
2768c0984e5SSebastian Reichel 				 union power_supply_propval *val)
2778c0984e5SSebastian Reichel {
2788c0984e5SSebastian Reichel 	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH,
2798c0984e5SSebastian Reichel 					       DA9150_QIF_FCC_MAH_SIZE);
2808c0984e5SSebastian Reichel 
2818c0984e5SSebastian Reichel 	val->intval = val->intval * 1000;
2828c0984e5SSebastian Reichel 
2838c0984e5SSebastian Reichel 	return 0;
2848c0984e5SSebastian Reichel }
2858c0984e5SSebastian Reichel 
2868c0984e5SSebastian Reichel /*
2878c0984e5SSebastian Reichel  * Temperature reading from device is only valid if battery/system provides
2888c0984e5SSebastian Reichel  * valid NTC to associated pin of DA9150 chip.
2898c0984e5SSebastian Reichel  */
da9150_fg_temp(struct da9150_fg * fg,union power_supply_propval * val)2908c0984e5SSebastian Reichel static int da9150_fg_temp(struct da9150_fg *fg,
2918c0984e5SSebastian Reichel 			  union power_supply_propval *val)
2928c0984e5SSebastian Reichel {
2938c0984e5SSebastian Reichel 	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG,
2948c0984e5SSebastian Reichel 					       DA9150_QIF_NTCAVG_SIZE);
2958c0984e5SSebastian Reichel 
2968c0984e5SSebastian Reichel 	val->intval = (val->intval * 10) / 1048576;
2978c0984e5SSebastian Reichel 
2988c0984e5SSebastian Reichel 	return 0;
2998c0984e5SSebastian Reichel }
3008c0984e5SSebastian Reichel 
3018c0984e5SSebastian Reichel static enum power_supply_property da9150_fg_props[] = {
3028c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CAPACITY,
3038c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CURRENT_AVG,
3048c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_AVG,
3058c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CHARGE_FULL,
3068c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_TEMP,
3078c0984e5SSebastian Reichel };
3088c0984e5SSebastian Reichel 
da9150_fg_get_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)3098c0984e5SSebastian Reichel static int da9150_fg_get_prop(struct power_supply *psy,
3108c0984e5SSebastian Reichel 			      enum power_supply_property psp,
3118c0984e5SSebastian Reichel 			      union power_supply_propval *val)
3128c0984e5SSebastian Reichel {
3138c0984e5SSebastian Reichel 	struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent);
3148c0984e5SSebastian Reichel 	int ret;
3158c0984e5SSebastian Reichel 
3168c0984e5SSebastian Reichel 	switch (psp) {
3178c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CAPACITY:
3188c0984e5SSebastian Reichel 		ret = da9150_fg_capacity(fg, val);
3198c0984e5SSebastian Reichel 		break;
3208c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CURRENT_AVG:
3218c0984e5SSebastian Reichel 		ret = da9150_fg_current_avg(fg, val);
3228c0984e5SSebastian Reichel 		break;
3238c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
3248c0984e5SSebastian Reichel 		ret = da9150_fg_voltage_avg(fg, val);
3258c0984e5SSebastian Reichel 		break;
3268c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CHARGE_FULL:
3278c0984e5SSebastian Reichel 		ret = da9150_fg_charge_full(fg, val);
3288c0984e5SSebastian Reichel 		break;
3298c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_TEMP:
3308c0984e5SSebastian Reichel 		ret = da9150_fg_temp(fg, val);
3318c0984e5SSebastian Reichel 		break;
3328c0984e5SSebastian Reichel 	default:
3338c0984e5SSebastian Reichel 		ret = -EINVAL;
3348c0984e5SSebastian Reichel 		break;
3358c0984e5SSebastian Reichel 	}
3368c0984e5SSebastian Reichel 
3378c0984e5SSebastian Reichel 	return ret;
3388c0984e5SSebastian Reichel }
3398c0984e5SSebastian Reichel 
3408c0984e5SSebastian Reichel /* Repeated SOC check */
da9150_fg_soc_changed(struct da9150_fg * fg)3418c0984e5SSebastian Reichel static bool da9150_fg_soc_changed(struct da9150_fg *fg)
3428c0984e5SSebastian Reichel {
3438c0984e5SSebastian Reichel 	union power_supply_propval val;
3448c0984e5SSebastian Reichel 
3458c0984e5SSebastian Reichel 	da9150_fg_capacity(fg, &val);
3468c0984e5SSebastian Reichel 	if (val.intval != fg->soc) {
3478c0984e5SSebastian Reichel 		fg->soc = val.intval;
3488c0984e5SSebastian Reichel 		return true;
3498c0984e5SSebastian Reichel 	}
3508c0984e5SSebastian Reichel 
3518c0984e5SSebastian Reichel 	return false;
3528c0984e5SSebastian Reichel }
3538c0984e5SSebastian Reichel 
da9150_fg_work(struct work_struct * work)3548c0984e5SSebastian Reichel static void da9150_fg_work(struct work_struct *work)
3558c0984e5SSebastian Reichel {
3568c0984e5SSebastian Reichel 	struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work);
3578c0984e5SSebastian Reichel 
3588c0984e5SSebastian Reichel 	/* Report if SOC has changed */
3598c0984e5SSebastian Reichel 	if (da9150_fg_soc_changed(fg))
3608c0984e5SSebastian Reichel 		power_supply_changed(fg->battery);
3618c0984e5SSebastian Reichel 
3628c0984e5SSebastian Reichel 	schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval));
3638c0984e5SSebastian Reichel }
3648c0984e5SSebastian Reichel 
3658c0984e5SSebastian Reichel /* SOC level event configuration */
da9150_fg_soc_event_config(struct da9150_fg * fg)3668c0984e5SSebastian Reichel static void da9150_fg_soc_event_config(struct da9150_fg *fg)
3678c0984e5SSebastian Reichel {
3688c0984e5SSebastian Reichel 	int soc;
3698c0984e5SSebastian Reichel 
3708c0984e5SSebastian Reichel 	soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
3718c0984e5SSebastian Reichel 				       DA9150_QIF_SOC_PCT_SIZE);
3728c0984e5SSebastian Reichel 
3738c0984e5SSebastian Reichel 	if (soc > fg->warn_soc) {
3748c0984e5SSebastian Reichel 		/* If SOC > warn level, set discharge warn level event */
3758c0984e5SSebastian Reichel 		da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
3768c0984e5SSebastian Reichel 					  DA9150_QIF_DISCHARGE_LIMIT_SIZE,
3778c0984e5SSebastian Reichel 					  fg->warn_soc + 1);
3788c0984e5SSebastian Reichel 	} else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) {
3798c0984e5SSebastian Reichel 		/*
3808c0984e5SSebastian Reichel 		 * If SOC <= warn level, set discharge crit level event,
3818c0984e5SSebastian Reichel 		 * and set charge warn level event.
3828c0984e5SSebastian Reichel 		 */
3838c0984e5SSebastian Reichel 		da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
3848c0984e5SSebastian Reichel 					  DA9150_QIF_DISCHARGE_LIMIT_SIZE,
3858c0984e5SSebastian Reichel 					  fg->crit_soc + 1);
3868c0984e5SSebastian Reichel 
3878c0984e5SSebastian Reichel 		da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
3888c0984e5SSebastian Reichel 					  DA9150_QIF_CHARGE_LIMIT_SIZE,
3898c0984e5SSebastian Reichel 					  fg->warn_soc);
3908c0984e5SSebastian Reichel 	} else if (soc <= fg->crit_soc) {
3918c0984e5SSebastian Reichel 		/* If SOC <= crit level, set charge crit level event */
3928c0984e5SSebastian Reichel 		da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
3938c0984e5SSebastian Reichel 					  DA9150_QIF_CHARGE_LIMIT_SIZE,
3948c0984e5SSebastian Reichel 					  fg->crit_soc);
3958c0984e5SSebastian Reichel 	}
3968c0984e5SSebastian Reichel }
3978c0984e5SSebastian Reichel 
da9150_fg_irq(int irq,void * data)3988c0984e5SSebastian Reichel static irqreturn_t da9150_fg_irq(int irq, void *data)
3998c0984e5SSebastian Reichel {
4008c0984e5SSebastian Reichel 	struct da9150_fg *fg = data;
4018c0984e5SSebastian Reichel 	u32 e_fg_status;
4028c0984e5SSebastian Reichel 
4038c0984e5SSebastian Reichel 	/* Read FG IRQ status info */
4048c0984e5SSebastian Reichel 	e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS,
4058c0984e5SSebastian Reichel 					  DA9150_QIF_E_FG_STATUS_SIZE);
4068c0984e5SSebastian Reichel 
4078c0984e5SSebastian Reichel 	/* Handle warning/critical threhold events */
4088c0984e5SSebastian Reichel 	if (e_fg_status & DA9150_FG_IRQ_SOC_MASK)
4098c0984e5SSebastian Reichel 		da9150_fg_soc_event_config(fg);
4108c0984e5SSebastian Reichel 
4118c0984e5SSebastian Reichel 	/* Clear any FG IRQs */
4128c0984e5SSebastian Reichel 	da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS,
4138c0984e5SSebastian Reichel 			     DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status);
4148c0984e5SSebastian Reichel 
4158c0984e5SSebastian Reichel 	return IRQ_HANDLED;
4168c0984e5SSebastian Reichel }
4178c0984e5SSebastian Reichel 
da9150_fg_dt_pdata(struct device * dev)4188c0984e5SSebastian Reichel static struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev)
4198c0984e5SSebastian Reichel {
4208c0984e5SSebastian Reichel 	struct device_node *fg_node = dev->of_node;
4218c0984e5SSebastian Reichel 	struct da9150_fg_pdata *pdata;
4228c0984e5SSebastian Reichel 
4238c0984e5SSebastian Reichel 	pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL);
4248c0984e5SSebastian Reichel 	if (!pdata)
4258c0984e5SSebastian Reichel 		return NULL;
4268c0984e5SSebastian Reichel 
4278c0984e5SSebastian Reichel 	of_property_read_u32(fg_node, "dlg,update-interval",
4288c0984e5SSebastian Reichel 			     &pdata->update_interval);
4298c0984e5SSebastian Reichel 	of_property_read_u8(fg_node, "dlg,warn-soc-level",
4308c0984e5SSebastian Reichel 			    &pdata->warn_soc_lvl);
4318c0984e5SSebastian Reichel 	of_property_read_u8(fg_node, "dlg,crit-soc-level",
4328c0984e5SSebastian Reichel 			    &pdata->crit_soc_lvl);
4338c0984e5SSebastian Reichel 
4348c0984e5SSebastian Reichel 	return pdata;
4358c0984e5SSebastian Reichel }
4368c0984e5SSebastian Reichel 
4378c0984e5SSebastian Reichel static const struct power_supply_desc fg_desc = {
4388c0984e5SSebastian Reichel 	.name		= "da9150-fg",
4398c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_BATTERY,
4408c0984e5SSebastian Reichel 	.properties	= da9150_fg_props,
4418c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(da9150_fg_props),
4428c0984e5SSebastian Reichel 	.get_property	= da9150_fg_get_prop,
4438c0984e5SSebastian Reichel };
4448c0984e5SSebastian Reichel 
da9150_fg_probe(struct platform_device * pdev)4458c0984e5SSebastian Reichel static int da9150_fg_probe(struct platform_device *pdev)
4468c0984e5SSebastian Reichel {
4478c0984e5SSebastian Reichel 	struct device *dev = &pdev->dev;
4488c0984e5SSebastian Reichel 	struct da9150 *da9150 = dev_get_drvdata(dev->parent);
4498c0984e5SSebastian Reichel 	struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev);
4508c0984e5SSebastian Reichel 	struct da9150_fg *fg;
4518c0984e5SSebastian Reichel 	int ver, irq, ret = 0;
4528c0984e5SSebastian Reichel 
4538c0984e5SSebastian Reichel 	fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL);
4548c0984e5SSebastian Reichel 	if (fg == NULL)
4558c0984e5SSebastian Reichel 		return -ENOMEM;
4568c0984e5SSebastian Reichel 
4578c0984e5SSebastian Reichel 	platform_set_drvdata(pdev, fg);
4588c0984e5SSebastian Reichel 	fg->da9150 = da9150;
4598c0984e5SSebastian Reichel 	fg->dev = dev;
4608c0984e5SSebastian Reichel 
4618c0984e5SSebastian Reichel 	mutex_init(&fg->io_lock);
4628c0984e5SSebastian Reichel 
4638c0984e5SSebastian Reichel 	/* Enable QIF */
4648c0984e5SSebastian Reichel 	da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK,
4658c0984e5SSebastian Reichel 			DA9150_FG_QIF_EN_MASK);
4668c0984e5SSebastian Reichel 
4678c0984e5SSebastian Reichel 	fg->battery = devm_power_supply_register(dev, &fg_desc, NULL);
4688c0984e5SSebastian Reichel 	if (IS_ERR(fg->battery)) {
4698c0984e5SSebastian Reichel 		ret = PTR_ERR(fg->battery);
4708c0984e5SSebastian Reichel 		return ret;
4718c0984e5SSebastian Reichel 	}
4728c0984e5SSebastian Reichel 
4738c0984e5SSebastian Reichel 	ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER,
4748c0984e5SSebastian Reichel 				  DA9150_QIF_FW_MAIN_VER_SIZE);
4758c0984e5SSebastian Reichel 	dev_info(dev, "Version: 0x%x\n", ver);
4768c0984e5SSebastian Reichel 
4778c0984e5SSebastian Reichel 	/* Handle DT data if provided */
4788c0984e5SSebastian Reichel 	if (dev->of_node) {
4798c0984e5SSebastian Reichel 		fg_pdata = da9150_fg_dt_pdata(dev);
4808c0984e5SSebastian Reichel 		dev->platform_data = fg_pdata;
4818c0984e5SSebastian Reichel 	}
4828c0984e5SSebastian Reichel 
4838c0984e5SSebastian Reichel 	/* Handle any pdata provided */
4848c0984e5SSebastian Reichel 	if (fg_pdata) {
4858c0984e5SSebastian Reichel 		fg->interval = fg_pdata->update_interval;
4868c0984e5SSebastian Reichel 
4878c0984e5SSebastian Reichel 		if (fg_pdata->warn_soc_lvl > 100)
4888c0984e5SSebastian Reichel 			dev_warn(dev, "Invalid SOC warning level provided, Ignoring");
4898c0984e5SSebastian Reichel 		else
4908c0984e5SSebastian Reichel 			fg->warn_soc = fg_pdata->warn_soc_lvl;
4918c0984e5SSebastian Reichel 
4928c0984e5SSebastian Reichel 		if ((fg_pdata->crit_soc_lvl > 100) ||
4938c0984e5SSebastian Reichel 		    (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl))
4948c0984e5SSebastian Reichel 			dev_warn(dev, "Invalid SOC critical level provided, Ignoring");
4958c0984e5SSebastian Reichel 		else
4968c0984e5SSebastian Reichel 			fg->crit_soc = fg_pdata->crit_soc_lvl;
4978c0984e5SSebastian Reichel 
4988c0984e5SSebastian Reichel 
4998c0984e5SSebastian Reichel 	}
5008c0984e5SSebastian Reichel 
5018c0984e5SSebastian Reichel 	/* Configure initial SOC level events */
5028c0984e5SSebastian Reichel 	da9150_fg_soc_event_config(fg);
5038c0984e5SSebastian Reichel 
5048c0984e5SSebastian Reichel 	/*
5058c0984e5SSebastian Reichel 	 * If an interval period has been provided then setup repeating
5068c0984e5SSebastian Reichel 	 * work for reporting data updates.
5078c0984e5SSebastian Reichel 	 */
5088c0984e5SSebastian Reichel 	if (fg->interval) {
509419c0e9dSChristophe JAILLET 		ret = devm_delayed_work_autocancel(dev, &fg->work,
510419c0e9dSChristophe JAILLET 						   da9150_fg_work);
511419c0e9dSChristophe JAILLET 		if (ret) {
512419c0e9dSChristophe JAILLET 			dev_err(dev, "Failed to init work\n");
513419c0e9dSChristophe JAILLET 			return ret;
514419c0e9dSChristophe JAILLET 		}
515419c0e9dSChristophe JAILLET 
5168c0984e5SSebastian Reichel 		schedule_delayed_work(&fg->work,
5178c0984e5SSebastian Reichel 				      msecs_to_jiffies(fg->interval));
5188c0984e5SSebastian Reichel 	}
5198c0984e5SSebastian Reichel 
5208c0984e5SSebastian Reichel 	/* Register IRQ */
5218c0984e5SSebastian Reichel 	irq = platform_get_irq_byname(pdev, "FG");
522*e6824196SYang Li 	if (irq < 0)
523419c0e9dSChristophe JAILLET 		return irq;
5248c0984e5SSebastian Reichel 
5258c0984e5SSebastian Reichel 	ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq,
5268c0984e5SSebastian Reichel 					IRQF_ONESHOT, "FG", fg);
5278c0984e5SSebastian Reichel 	if (ret) {
5288c0984e5SSebastian Reichel 		dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
5298c0984e5SSebastian Reichel 		return ret;
5308c0984e5SSebastian Reichel 	}
5318c0984e5SSebastian Reichel 
5328c0984e5SSebastian Reichel 	return 0;
5338c0984e5SSebastian Reichel }
5348c0984e5SSebastian Reichel 
da9150_fg_resume(struct platform_device * pdev)5358c0984e5SSebastian Reichel static int da9150_fg_resume(struct platform_device *pdev)
5368c0984e5SSebastian Reichel {
5378c0984e5SSebastian Reichel 	struct da9150_fg *fg = platform_get_drvdata(pdev);
5388c0984e5SSebastian Reichel 
5398c0984e5SSebastian Reichel 	/*
5408c0984e5SSebastian Reichel 	 * Trigger SOC check to happen now so as to indicate any value change
5418c0984e5SSebastian Reichel 	 * since last check before suspend.
5428c0984e5SSebastian Reichel 	 */
5438c0984e5SSebastian Reichel 	if (fg->interval)
5448c0984e5SSebastian Reichel 		flush_delayed_work(&fg->work);
5458c0984e5SSebastian Reichel 
5468c0984e5SSebastian Reichel 	return 0;
5478c0984e5SSebastian Reichel }
5488c0984e5SSebastian Reichel 
5498c0984e5SSebastian Reichel static struct platform_driver da9150_fg_driver = {
5508c0984e5SSebastian Reichel 	.driver = {
5518c0984e5SSebastian Reichel 		.name = "da9150-fuel-gauge",
5528c0984e5SSebastian Reichel 	},
5538c0984e5SSebastian Reichel 	.probe = da9150_fg_probe,
5548c0984e5SSebastian Reichel 	.resume = da9150_fg_resume,
5558c0984e5SSebastian Reichel };
5568c0984e5SSebastian Reichel 
5578c0984e5SSebastian Reichel module_platform_driver(da9150_fg_driver);
5588c0984e5SSebastian Reichel 
5598c0984e5SSebastian Reichel MODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150");
5608c0984e5SSebastian Reichel MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
5618c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
562