162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * DA9150 Fuel-Gauge Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2015 Dialog Semiconductor
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/power_supply.h>
1862306a36Sopenharmony_ci#include <linux/list.h>
1962306a36Sopenharmony_ci#include <asm/div64.h>
2062306a36Sopenharmony_ci#include <linux/mfd/da9150/core.h>
2162306a36Sopenharmony_ci#include <linux/mfd/da9150/registers.h>
2262306a36Sopenharmony_ci#include <linux/devm-helpers.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Core2Wire */
2562306a36Sopenharmony_ci#define DA9150_QIF_READ		(0x0 << 7)
2662306a36Sopenharmony_ci#define DA9150_QIF_WRITE	(0x1 << 7)
2762306a36Sopenharmony_ci#define DA9150_QIF_CODE_MASK	0x7F
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define DA9150_QIF_BYTE_SIZE	8
3062306a36Sopenharmony_ci#define DA9150_QIF_BYTE_MASK	0xFF
3162306a36Sopenharmony_ci#define DA9150_QIF_SHORT_SIZE	2
3262306a36Sopenharmony_ci#define DA9150_QIF_LONG_SIZE	4
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* QIF Codes */
3562306a36Sopenharmony_ci#define DA9150_QIF_UAVG			6
3662306a36Sopenharmony_ci#define DA9150_QIF_UAVG_SIZE		DA9150_QIF_LONG_SIZE
3762306a36Sopenharmony_ci#define DA9150_QIF_IAVG			8
3862306a36Sopenharmony_ci#define DA9150_QIF_IAVG_SIZE		DA9150_QIF_LONG_SIZE
3962306a36Sopenharmony_ci#define DA9150_QIF_NTCAVG		12
4062306a36Sopenharmony_ci#define DA9150_QIF_NTCAVG_SIZE		DA9150_QIF_LONG_SIZE
4162306a36Sopenharmony_ci#define DA9150_QIF_SHUNT_VAL		36
4262306a36Sopenharmony_ci#define DA9150_QIF_SHUNT_VAL_SIZE	DA9150_QIF_SHORT_SIZE
4362306a36Sopenharmony_ci#define DA9150_QIF_SD_GAIN		38
4462306a36Sopenharmony_ci#define DA9150_QIF_SD_GAIN_SIZE		DA9150_QIF_LONG_SIZE
4562306a36Sopenharmony_ci#define DA9150_QIF_FCC_MAH		40
4662306a36Sopenharmony_ci#define DA9150_QIF_FCC_MAH_SIZE		DA9150_QIF_SHORT_SIZE
4762306a36Sopenharmony_ci#define DA9150_QIF_SOC_PCT		43
4862306a36Sopenharmony_ci#define DA9150_QIF_SOC_PCT_SIZE		DA9150_QIF_SHORT_SIZE
4962306a36Sopenharmony_ci#define DA9150_QIF_CHARGE_LIMIT		44
5062306a36Sopenharmony_ci#define DA9150_QIF_CHARGE_LIMIT_SIZE	DA9150_QIF_SHORT_SIZE
5162306a36Sopenharmony_ci#define DA9150_QIF_DISCHARGE_LIMIT	45
5262306a36Sopenharmony_ci#define DA9150_QIF_DISCHARGE_LIMIT_SIZE	DA9150_QIF_SHORT_SIZE
5362306a36Sopenharmony_ci#define DA9150_QIF_FW_MAIN_VER		118
5462306a36Sopenharmony_ci#define DA9150_QIF_FW_MAIN_VER_SIZE	DA9150_QIF_SHORT_SIZE
5562306a36Sopenharmony_ci#define DA9150_QIF_E_FG_STATUS		126
5662306a36Sopenharmony_ci#define DA9150_QIF_E_FG_STATUS_SIZE	DA9150_QIF_SHORT_SIZE
5762306a36Sopenharmony_ci#define DA9150_QIF_SYNC			127
5862306a36Sopenharmony_ci#define DA9150_QIF_SYNC_SIZE		DA9150_QIF_SHORT_SIZE
5962306a36Sopenharmony_ci#define DA9150_QIF_MAX_CODES		128
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* QIF Sync Timeout */
6262306a36Sopenharmony_ci#define DA9150_QIF_SYNC_TIMEOUT		1000
6362306a36Sopenharmony_ci#define DA9150_QIF_SYNC_RETRIES		10
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* QIF E_FG_STATUS */
6662306a36Sopenharmony_ci#define DA9150_FG_IRQ_LOW_SOC_MASK	(1 << 0)
6762306a36Sopenharmony_ci#define DA9150_FG_IRQ_HIGH_SOC_MASK	(1 << 1)
6862306a36Sopenharmony_ci#define DA9150_FG_IRQ_SOC_MASK	\
6962306a36Sopenharmony_ci	(DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK)
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* Private data */
7262306a36Sopenharmony_cistruct da9150_fg {
7362306a36Sopenharmony_ci	struct da9150 *da9150;
7462306a36Sopenharmony_ci	struct device *dev;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	struct mutex io_lock;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	struct power_supply *battery;
7962306a36Sopenharmony_ci	struct delayed_work work;
8062306a36Sopenharmony_ci	u32 interval;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	int warn_soc;
8362306a36Sopenharmony_ci	int crit_soc;
8462306a36Sopenharmony_ci	int soc;
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* Battery Properties */
8862306a36Sopenharmony_cistatic u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	u8 buf[DA9150_QIF_LONG_SIZE];
9262306a36Sopenharmony_ci	u8 read_addr;
9362306a36Sopenharmony_ci	u32 res = 0;
9462306a36Sopenharmony_ci	int i;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* Set QIF code (READ mode) */
9762306a36Sopenharmony_ci	read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	da9150_read_qif(fg->da9150, read_addr, size, buf);
10062306a36Sopenharmony_ci	for (i = 0; i < size; ++i)
10162306a36Sopenharmony_ci		res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE));
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return res;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
10762306a36Sopenharmony_ci				 u32 val)
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	u8 buf[DA9150_QIF_LONG_SIZE];
11162306a36Sopenharmony_ci	u8 write_addr;
11262306a36Sopenharmony_ci	int i;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* Set QIF code (WRITE mode) */
11562306a36Sopenharmony_ci	write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	for (i = 0; i < size; ++i) {
11862306a36Sopenharmony_ci		buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) &
11962306a36Sopenharmony_ci			 DA9150_QIF_BYTE_MASK;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	da9150_write_qif(fg->da9150, write_addr, size, buf);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* Trigger QIF Sync to update QIF readable data */
12562306a36Sopenharmony_cistatic void da9150_fg_read_sync_start(struct da9150_fg *fg)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	int i = 0;
12862306a36Sopenharmony_ci	u32 res = 0;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	mutex_lock(&fg->io_lock);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* Check if QIF sync already requested, and write to sync if not */
13362306a36Sopenharmony_ci	res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
13462306a36Sopenharmony_ci				  DA9150_QIF_SYNC_SIZE);
13562306a36Sopenharmony_ci	if (res > 0)
13662306a36Sopenharmony_ci		da9150_fg_write_attr(fg, DA9150_QIF_SYNC,
13762306a36Sopenharmony_ci				     DA9150_QIF_SYNC_SIZE, 0);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* Wait for sync to complete */
14062306a36Sopenharmony_ci	res = 0;
14162306a36Sopenharmony_ci	while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
14262306a36Sopenharmony_ci		usleep_range(DA9150_QIF_SYNC_TIMEOUT,
14362306a36Sopenharmony_ci			     DA9150_QIF_SYNC_TIMEOUT * 2);
14462306a36Sopenharmony_ci		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
14562306a36Sopenharmony_ci					  DA9150_QIF_SYNC_SIZE);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* Check if sync completed */
14962306a36Sopenharmony_ci	if (res == 0)
15062306a36Sopenharmony_ci		dev_err(fg->dev, "Failed to perform QIF read sync!\n");
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/*
15462306a36Sopenharmony_ci * Should always be called after QIF sync read has been performed, and all
15562306a36Sopenharmony_ci * attributes required have been accessed.
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistatic inline void da9150_fg_read_sync_end(struct da9150_fg *fg)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	mutex_unlock(&fg->io_lock);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* Sync read of single QIF attribute */
16362306a36Sopenharmony_cistatic u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	u32 val;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	da9150_fg_read_sync_start(fg);
16862306a36Sopenharmony_ci	val = da9150_fg_read_attr(fg, code, size);
16962306a36Sopenharmony_ci	da9150_fg_read_sync_end(fg);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return val;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/* Wait for QIF Sync, write QIF data and wait for ack */
17562306a36Sopenharmony_cistatic void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size,
17662306a36Sopenharmony_ci				      u32 val)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	int i = 0;
17962306a36Sopenharmony_ci	u32 res = 0, sync_val;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	mutex_lock(&fg->io_lock);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Check if QIF sync already requested */
18462306a36Sopenharmony_ci	res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
18562306a36Sopenharmony_ci				  DA9150_QIF_SYNC_SIZE);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* Wait for an existing sync to complete */
18862306a36Sopenharmony_ci	while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
18962306a36Sopenharmony_ci		usleep_range(DA9150_QIF_SYNC_TIMEOUT,
19062306a36Sopenharmony_ci			     DA9150_QIF_SYNC_TIMEOUT * 2);
19162306a36Sopenharmony_ci		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
19262306a36Sopenharmony_ci					  DA9150_QIF_SYNC_SIZE);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (res == 0) {
19662306a36Sopenharmony_ci		dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n");
19762306a36Sopenharmony_ci		mutex_unlock(&fg->io_lock);
19862306a36Sopenharmony_ci		return;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* Write value for QIF code */
20262306a36Sopenharmony_ci	da9150_fg_write_attr(fg, code, size, val);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* Wait for write acknowledgment */
20562306a36Sopenharmony_ci	i = 0;
20662306a36Sopenharmony_ci	sync_val = res;
20762306a36Sopenharmony_ci	while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) {
20862306a36Sopenharmony_ci		usleep_range(DA9150_QIF_SYNC_TIMEOUT,
20962306a36Sopenharmony_ci			     DA9150_QIF_SYNC_TIMEOUT * 2);
21062306a36Sopenharmony_ci		res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,
21162306a36Sopenharmony_ci					  DA9150_QIF_SYNC_SIZE);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	mutex_unlock(&fg->io_lock);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* Check write was actually successful */
21762306a36Sopenharmony_ci	if (res != (sync_val + 1))
21862306a36Sopenharmony_ci		dev_err(fg->dev, "Error performing QIF sync write for code %d\n",
21962306a36Sopenharmony_ci			code);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/* Power Supply attributes */
22362306a36Sopenharmony_cistatic int da9150_fg_capacity(struct da9150_fg *fg,
22462306a36Sopenharmony_ci			      union power_supply_propval *val)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
22762306a36Sopenharmony_ci					       DA9150_QIF_SOC_PCT_SIZE);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (val->intval > 100)
23062306a36Sopenharmony_ci		val->intval = 100;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return 0;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int da9150_fg_current_avg(struct da9150_fg *fg,
23662306a36Sopenharmony_ci				 union power_supply_propval *val)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	u32 iavg, sd_gain, shunt_val;
23962306a36Sopenharmony_ci	u64 div, res;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	da9150_fg_read_sync_start(fg);
24262306a36Sopenharmony_ci	iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG,
24362306a36Sopenharmony_ci				   DA9150_QIF_IAVG_SIZE);
24462306a36Sopenharmony_ci	shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL,
24562306a36Sopenharmony_ci					DA9150_QIF_SHUNT_VAL_SIZE);
24662306a36Sopenharmony_ci	sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN,
24762306a36Sopenharmony_ci				      DA9150_QIF_SD_GAIN_SIZE);
24862306a36Sopenharmony_ci	da9150_fg_read_sync_end(fg);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	div = (u64) (sd_gain * shunt_val * 65536ULL);
25162306a36Sopenharmony_ci	do_div(div, 1000000);
25262306a36Sopenharmony_ci	res = (u64) (iavg * 1000000ULL);
25362306a36Sopenharmony_ci	do_div(res, div);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	val->intval = (int) res;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int da9150_fg_voltage_avg(struct da9150_fg *fg,
26162306a36Sopenharmony_ci				 union power_supply_propval *val)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	u64 res;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG,
26662306a36Sopenharmony_ci					       DA9150_QIF_UAVG_SIZE);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	res = (u64) (val->intval * 186ULL);
26962306a36Sopenharmony_ci	do_div(res, 10000);
27062306a36Sopenharmony_ci	val->intval = (int) res;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return 0;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int da9150_fg_charge_full(struct da9150_fg *fg,
27662306a36Sopenharmony_ci				 union power_supply_propval *val)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH,
27962306a36Sopenharmony_ci					       DA9150_QIF_FCC_MAH_SIZE);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	val->intval = val->intval * 1000;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	return 0;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/*
28762306a36Sopenharmony_ci * Temperature reading from device is only valid if battery/system provides
28862306a36Sopenharmony_ci * valid NTC to associated pin of DA9150 chip.
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cistatic int da9150_fg_temp(struct da9150_fg *fg,
29162306a36Sopenharmony_ci			  union power_supply_propval *val)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG,
29462306a36Sopenharmony_ci					       DA9150_QIF_NTCAVG_SIZE);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	val->intval = (val->intval * 10) / 1048576;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return 0;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic enum power_supply_property da9150_fg_props[] = {
30262306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
30362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_AVG,
30462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_AVG,
30562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_FULL,
30662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_TEMP,
30762306a36Sopenharmony_ci};
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int da9150_fg_get_prop(struct power_supply *psy,
31062306a36Sopenharmony_ci			      enum power_supply_property psp,
31162306a36Sopenharmony_ci			      union power_supply_propval *val)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent);
31462306a36Sopenharmony_ci	int ret;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	switch (psp) {
31762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
31862306a36Sopenharmony_ci		ret = da9150_fg_capacity(fg, val);
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_AVG:
32162306a36Sopenharmony_ci		ret = da9150_fg_current_avg(fg, val);
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
32462306a36Sopenharmony_ci		ret = da9150_fg_voltage_avg(fg, val);
32562306a36Sopenharmony_ci		break;
32662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_FULL:
32762306a36Sopenharmony_ci		ret = da9150_fg_charge_full(fg, val);
32862306a36Sopenharmony_ci		break;
32962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_TEMP:
33062306a36Sopenharmony_ci		ret = da9150_fg_temp(fg, val);
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	default:
33362306a36Sopenharmony_ci		ret = -EINVAL;
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return ret;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/* Repeated SOC check */
34162306a36Sopenharmony_cistatic bool da9150_fg_soc_changed(struct da9150_fg *fg)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	union power_supply_propval val;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	da9150_fg_capacity(fg, &val);
34662306a36Sopenharmony_ci	if (val.intval != fg->soc) {
34762306a36Sopenharmony_ci		fg->soc = val.intval;
34862306a36Sopenharmony_ci		return true;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	return false;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void da9150_fg_work(struct work_struct *work)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* Report if SOC has changed */
35962306a36Sopenharmony_ci	if (da9150_fg_soc_changed(fg))
36062306a36Sopenharmony_ci		power_supply_changed(fg->battery);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval));
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci/* SOC level event configuration */
36662306a36Sopenharmony_cistatic void da9150_fg_soc_event_config(struct da9150_fg *fg)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	int soc;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,
37162306a36Sopenharmony_ci				       DA9150_QIF_SOC_PCT_SIZE);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (soc > fg->warn_soc) {
37462306a36Sopenharmony_ci		/* If SOC > warn level, set discharge warn level event */
37562306a36Sopenharmony_ci		da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
37662306a36Sopenharmony_ci					  DA9150_QIF_DISCHARGE_LIMIT_SIZE,
37762306a36Sopenharmony_ci					  fg->warn_soc + 1);
37862306a36Sopenharmony_ci	} else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) {
37962306a36Sopenharmony_ci		/*
38062306a36Sopenharmony_ci		 * If SOC <= warn level, set discharge crit level event,
38162306a36Sopenharmony_ci		 * and set charge warn level event.
38262306a36Sopenharmony_ci		 */
38362306a36Sopenharmony_ci		da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,
38462306a36Sopenharmony_ci					  DA9150_QIF_DISCHARGE_LIMIT_SIZE,
38562306a36Sopenharmony_ci					  fg->crit_soc + 1);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
38862306a36Sopenharmony_ci					  DA9150_QIF_CHARGE_LIMIT_SIZE,
38962306a36Sopenharmony_ci					  fg->warn_soc);
39062306a36Sopenharmony_ci	} else if (soc <= fg->crit_soc) {
39162306a36Sopenharmony_ci		/* If SOC <= crit level, set charge crit level event */
39262306a36Sopenharmony_ci		da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,
39362306a36Sopenharmony_ci					  DA9150_QIF_CHARGE_LIMIT_SIZE,
39462306a36Sopenharmony_ci					  fg->crit_soc);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic irqreturn_t da9150_fg_irq(int irq, void *data)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct da9150_fg *fg = data;
40162306a36Sopenharmony_ci	u32 e_fg_status;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* Read FG IRQ status info */
40462306a36Sopenharmony_ci	e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS,
40562306a36Sopenharmony_ci					  DA9150_QIF_E_FG_STATUS_SIZE);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* Handle warning/critical threhold events */
40862306a36Sopenharmony_ci	if (e_fg_status & DA9150_FG_IRQ_SOC_MASK)
40962306a36Sopenharmony_ci		da9150_fg_soc_event_config(fg);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/* Clear any FG IRQs */
41262306a36Sopenharmony_ci	da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS,
41362306a36Sopenharmony_ci			     DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return IRQ_HANDLED;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct device_node *fg_node = dev->of_node;
42162306a36Sopenharmony_ci	struct da9150_fg_pdata *pdata;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL);
42462306a36Sopenharmony_ci	if (!pdata)
42562306a36Sopenharmony_ci		return NULL;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	of_property_read_u32(fg_node, "dlg,update-interval",
42862306a36Sopenharmony_ci			     &pdata->update_interval);
42962306a36Sopenharmony_ci	of_property_read_u8(fg_node, "dlg,warn-soc-level",
43062306a36Sopenharmony_ci			    &pdata->warn_soc_lvl);
43162306a36Sopenharmony_ci	of_property_read_u8(fg_node, "dlg,crit-soc-level",
43262306a36Sopenharmony_ci			    &pdata->crit_soc_lvl);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return pdata;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic const struct power_supply_desc fg_desc = {
43862306a36Sopenharmony_ci	.name		= "da9150-fg",
43962306a36Sopenharmony_ci	.type		= POWER_SUPPLY_TYPE_BATTERY,
44062306a36Sopenharmony_ci	.properties	= da9150_fg_props,
44162306a36Sopenharmony_ci	.num_properties	= ARRAY_SIZE(da9150_fg_props),
44262306a36Sopenharmony_ci	.get_property	= da9150_fg_get_prop,
44362306a36Sopenharmony_ci};
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic int da9150_fg_probe(struct platform_device *pdev)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
44862306a36Sopenharmony_ci	struct da9150 *da9150 = dev_get_drvdata(dev->parent);
44962306a36Sopenharmony_ci	struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev);
45062306a36Sopenharmony_ci	struct da9150_fg *fg;
45162306a36Sopenharmony_ci	int ver, irq, ret = 0;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL);
45462306a36Sopenharmony_ci	if (fg == NULL)
45562306a36Sopenharmony_ci		return -ENOMEM;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	platform_set_drvdata(pdev, fg);
45862306a36Sopenharmony_ci	fg->da9150 = da9150;
45962306a36Sopenharmony_ci	fg->dev = dev;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	mutex_init(&fg->io_lock);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* Enable QIF */
46462306a36Sopenharmony_ci	da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK,
46562306a36Sopenharmony_ci			DA9150_FG_QIF_EN_MASK);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	fg->battery = devm_power_supply_register(dev, &fg_desc, NULL);
46862306a36Sopenharmony_ci	if (IS_ERR(fg->battery)) {
46962306a36Sopenharmony_ci		ret = PTR_ERR(fg->battery);
47062306a36Sopenharmony_ci		return ret;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER,
47462306a36Sopenharmony_ci				  DA9150_QIF_FW_MAIN_VER_SIZE);
47562306a36Sopenharmony_ci	dev_info(dev, "Version: 0x%x\n", ver);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* Handle DT data if provided */
47862306a36Sopenharmony_ci	if (dev->of_node) {
47962306a36Sopenharmony_ci		fg_pdata = da9150_fg_dt_pdata(dev);
48062306a36Sopenharmony_ci		dev->platform_data = fg_pdata;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* Handle any pdata provided */
48462306a36Sopenharmony_ci	if (fg_pdata) {
48562306a36Sopenharmony_ci		fg->interval = fg_pdata->update_interval;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		if (fg_pdata->warn_soc_lvl > 100)
48862306a36Sopenharmony_ci			dev_warn(dev, "Invalid SOC warning level provided, Ignoring");
48962306a36Sopenharmony_ci		else
49062306a36Sopenharmony_ci			fg->warn_soc = fg_pdata->warn_soc_lvl;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		if ((fg_pdata->crit_soc_lvl > 100) ||
49362306a36Sopenharmony_ci		    (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl))
49462306a36Sopenharmony_ci			dev_warn(dev, "Invalid SOC critical level provided, Ignoring");
49562306a36Sopenharmony_ci		else
49662306a36Sopenharmony_ci			fg->crit_soc = fg_pdata->crit_soc_lvl;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	/* Configure initial SOC level events */
50262306a36Sopenharmony_ci	da9150_fg_soc_event_config(fg);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/*
50562306a36Sopenharmony_ci	 * If an interval period has been provided then setup repeating
50662306a36Sopenharmony_ci	 * work for reporting data updates.
50762306a36Sopenharmony_ci	 */
50862306a36Sopenharmony_ci	if (fg->interval) {
50962306a36Sopenharmony_ci		ret = devm_delayed_work_autocancel(dev, &fg->work,
51062306a36Sopenharmony_ci						   da9150_fg_work);
51162306a36Sopenharmony_ci		if (ret) {
51262306a36Sopenharmony_ci			dev_err(dev, "Failed to init work\n");
51362306a36Sopenharmony_ci			return ret;
51462306a36Sopenharmony_ci		}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		schedule_delayed_work(&fg->work,
51762306a36Sopenharmony_ci				      msecs_to_jiffies(fg->interval));
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* Register IRQ */
52162306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, "FG");
52262306a36Sopenharmony_ci	if (irq < 0)
52362306a36Sopenharmony_ci		return irq;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq,
52662306a36Sopenharmony_ci					IRQF_ONESHOT, "FG", fg);
52762306a36Sopenharmony_ci	if (ret) {
52862306a36Sopenharmony_ci		dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
52962306a36Sopenharmony_ci		return ret;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	return 0;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int da9150_fg_resume(struct platform_device *pdev)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct da9150_fg *fg = platform_get_drvdata(pdev);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	/*
54062306a36Sopenharmony_ci	 * Trigger SOC check to happen now so as to indicate any value change
54162306a36Sopenharmony_ci	 * since last check before suspend.
54262306a36Sopenharmony_ci	 */
54362306a36Sopenharmony_ci	if (fg->interval)
54462306a36Sopenharmony_ci		flush_delayed_work(&fg->work);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return 0;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic struct platform_driver da9150_fg_driver = {
55062306a36Sopenharmony_ci	.driver = {
55162306a36Sopenharmony_ci		.name = "da9150-fuel-gauge",
55262306a36Sopenharmony_ci	},
55362306a36Sopenharmony_ci	.probe = da9150_fg_probe,
55462306a36Sopenharmony_ci	.resume = da9150_fg_resume,
55562306a36Sopenharmony_ci};
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cimodule_platform_driver(da9150_fg_driver);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ciMODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150");
56062306a36Sopenharmony_ciMODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
56162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
562