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