18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci//  max17040_battery.c
48c2ecf20Sopenharmony_ci//  fuel-gauge systems for lithium-ion (Li+) batteries
58c2ecf20Sopenharmony_ci//
68c2ecf20Sopenharmony_ci//  Copyright (C) 2009 Samsung Electronics
78c2ecf20Sopenharmony_ci//  Minkyu Kang <mk7.kang@samsung.com>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/mutex.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/i2c.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
188c2ecf20Sopenharmony_ci#include <linux/of_device.h>
198c2ecf20Sopenharmony_ci#include <linux/max17040_battery.h>
208c2ecf20Sopenharmony_ci#include <linux/regmap.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define MAX17040_VCELL	0x02
248c2ecf20Sopenharmony_ci#define MAX17040_SOC	0x04
258c2ecf20Sopenharmony_ci#define MAX17040_MODE	0x06
268c2ecf20Sopenharmony_ci#define MAX17040_VER	0x08
278c2ecf20Sopenharmony_ci#define MAX17040_CONFIG	0x0C
288c2ecf20Sopenharmony_ci#define MAX17040_STATUS	0x1A
298c2ecf20Sopenharmony_ci#define MAX17040_CMD	0xFE
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define MAX17040_DELAY		1000
338c2ecf20Sopenharmony_ci#define MAX17040_BATTERY_FULL	95
348c2ecf20Sopenharmony_ci#define MAX17040_RCOMP_DEFAULT  0x9700
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define MAX17040_ATHD_MASK		0x3f
378c2ecf20Sopenharmony_ci#define MAX17040_ALSC_MASK		0x40
388c2ecf20Sopenharmony_ci#define MAX17040_ATHD_DEFAULT_POWER_UP	4
398c2ecf20Sopenharmony_ci#define MAX17040_STATUS_HD_MASK		0x1000
408c2ecf20Sopenharmony_ci#define MAX17040_STATUS_SC_MASK		0x2000
418c2ecf20Sopenharmony_ci#define MAX17040_CFG_RCOMP_MASK		0xff00
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cienum chip_id {
448c2ecf20Sopenharmony_ci	ID_MAX17040,
458c2ecf20Sopenharmony_ci	ID_MAX17041,
468c2ecf20Sopenharmony_ci	ID_MAX17043,
478c2ecf20Sopenharmony_ci	ID_MAX17044,
488c2ecf20Sopenharmony_ci	ID_MAX17048,
498c2ecf20Sopenharmony_ci	ID_MAX17049,
508c2ecf20Sopenharmony_ci	ID_MAX17058,
518c2ecf20Sopenharmony_ci	ID_MAX17059,
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* values that differ by chip_id */
558c2ecf20Sopenharmony_cistruct chip_data {
568c2ecf20Sopenharmony_ci	u16 reset_val;
578c2ecf20Sopenharmony_ci	u16 vcell_shift;
588c2ecf20Sopenharmony_ci	u16 vcell_mul;
598c2ecf20Sopenharmony_ci	u16 vcell_div;
608c2ecf20Sopenharmony_ci	u8  has_low_soc_alert;
618c2ecf20Sopenharmony_ci	u8  rcomp_bytes;
628c2ecf20Sopenharmony_ci	u8  has_soc_alert;
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic struct chip_data max17040_family[] = {
668c2ecf20Sopenharmony_ci	[ID_MAX17040] = {
678c2ecf20Sopenharmony_ci		.reset_val = 0x0054,
688c2ecf20Sopenharmony_ci		.vcell_shift = 4,
698c2ecf20Sopenharmony_ci		.vcell_mul = 1250,
708c2ecf20Sopenharmony_ci		.vcell_div = 1,
718c2ecf20Sopenharmony_ci		.has_low_soc_alert = 0,
728c2ecf20Sopenharmony_ci		.rcomp_bytes = 2,
738c2ecf20Sopenharmony_ci		.has_soc_alert = 0,
748c2ecf20Sopenharmony_ci	},
758c2ecf20Sopenharmony_ci	[ID_MAX17041] = {
768c2ecf20Sopenharmony_ci		.reset_val = 0x0054,
778c2ecf20Sopenharmony_ci		.vcell_shift = 4,
788c2ecf20Sopenharmony_ci		.vcell_mul = 2500,
798c2ecf20Sopenharmony_ci		.vcell_div = 1,
808c2ecf20Sopenharmony_ci		.has_low_soc_alert = 0,
818c2ecf20Sopenharmony_ci		.rcomp_bytes = 2,
828c2ecf20Sopenharmony_ci		.has_soc_alert = 0,
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	[ID_MAX17043] = {
858c2ecf20Sopenharmony_ci		.reset_val = 0x0054,
868c2ecf20Sopenharmony_ci		.vcell_shift = 4,
878c2ecf20Sopenharmony_ci		.vcell_mul = 1250,
888c2ecf20Sopenharmony_ci		.vcell_div = 1,
898c2ecf20Sopenharmony_ci		.has_low_soc_alert = 1,
908c2ecf20Sopenharmony_ci		.rcomp_bytes = 1,
918c2ecf20Sopenharmony_ci		.has_soc_alert = 0,
928c2ecf20Sopenharmony_ci	},
938c2ecf20Sopenharmony_ci	[ID_MAX17044] = {
948c2ecf20Sopenharmony_ci		.reset_val = 0x0054,
958c2ecf20Sopenharmony_ci		.vcell_shift = 4,
968c2ecf20Sopenharmony_ci		.vcell_mul = 2500,
978c2ecf20Sopenharmony_ci		.vcell_div = 1,
988c2ecf20Sopenharmony_ci		.has_low_soc_alert = 1,
998c2ecf20Sopenharmony_ci		.rcomp_bytes = 1,
1008c2ecf20Sopenharmony_ci		.has_soc_alert = 0,
1018c2ecf20Sopenharmony_ci	},
1028c2ecf20Sopenharmony_ci	[ID_MAX17048] = {
1038c2ecf20Sopenharmony_ci		.reset_val = 0x5400,
1048c2ecf20Sopenharmony_ci		.vcell_shift = 0,
1058c2ecf20Sopenharmony_ci		.vcell_mul = 625,
1068c2ecf20Sopenharmony_ci		.vcell_div = 8,
1078c2ecf20Sopenharmony_ci		.has_low_soc_alert = 1,
1088c2ecf20Sopenharmony_ci		.rcomp_bytes = 1,
1098c2ecf20Sopenharmony_ci		.has_soc_alert = 1,
1108c2ecf20Sopenharmony_ci	},
1118c2ecf20Sopenharmony_ci	[ID_MAX17049] = {
1128c2ecf20Sopenharmony_ci		.reset_val = 0x5400,
1138c2ecf20Sopenharmony_ci		.vcell_shift = 0,
1148c2ecf20Sopenharmony_ci		.vcell_mul = 625,
1158c2ecf20Sopenharmony_ci		.vcell_div = 4,
1168c2ecf20Sopenharmony_ci		.has_low_soc_alert = 1,
1178c2ecf20Sopenharmony_ci		.rcomp_bytes = 1,
1188c2ecf20Sopenharmony_ci		.has_soc_alert = 1,
1198c2ecf20Sopenharmony_ci	},
1208c2ecf20Sopenharmony_ci	[ID_MAX17058] = {
1218c2ecf20Sopenharmony_ci		.reset_val = 0x5400,
1228c2ecf20Sopenharmony_ci		.vcell_shift = 0,
1238c2ecf20Sopenharmony_ci		.vcell_mul = 625,
1248c2ecf20Sopenharmony_ci		.vcell_div = 8,
1258c2ecf20Sopenharmony_ci		.has_low_soc_alert = 1,
1268c2ecf20Sopenharmony_ci		.rcomp_bytes = 1,
1278c2ecf20Sopenharmony_ci		.has_soc_alert = 0,
1288c2ecf20Sopenharmony_ci	},
1298c2ecf20Sopenharmony_ci	[ID_MAX17059] = {
1308c2ecf20Sopenharmony_ci		.reset_val = 0x5400,
1318c2ecf20Sopenharmony_ci		.vcell_shift = 0,
1328c2ecf20Sopenharmony_ci		.vcell_mul = 625,
1338c2ecf20Sopenharmony_ci		.vcell_div = 4,
1348c2ecf20Sopenharmony_ci		.has_low_soc_alert = 1,
1358c2ecf20Sopenharmony_ci		.rcomp_bytes = 1,
1368c2ecf20Sopenharmony_ci		.has_soc_alert = 0,
1378c2ecf20Sopenharmony_ci	},
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistruct max17040_chip {
1418c2ecf20Sopenharmony_ci	struct i2c_client		*client;
1428c2ecf20Sopenharmony_ci	struct regmap			*regmap;
1438c2ecf20Sopenharmony_ci	struct delayed_work		work;
1448c2ecf20Sopenharmony_ci	struct power_supply		*battery;
1458c2ecf20Sopenharmony_ci	struct max17040_platform_data	*pdata;
1468c2ecf20Sopenharmony_ci	struct chip_data		data;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* battery capacity */
1498c2ecf20Sopenharmony_ci	int soc;
1508c2ecf20Sopenharmony_ci	/* State Of Charge */
1518c2ecf20Sopenharmony_ci	int status;
1528c2ecf20Sopenharmony_ci	/* Low alert threshold from 32% to 1% of the State of Charge */
1538c2ecf20Sopenharmony_ci	u32 low_soc_alert;
1548c2ecf20Sopenharmony_ci	/* some devices return twice the capacity */
1558c2ecf20Sopenharmony_ci	bool quirk_double_soc;
1568c2ecf20Sopenharmony_ci	/* higher 8 bits for 17043+, 16 bits for 17040,41 */
1578c2ecf20Sopenharmony_ci	u16 rcomp;
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic int max17040_reset(struct max17040_chip *chip)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	return regmap_write(chip->regmap, MAX17040_CMD, chip->data.reset_val);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	level = 32 - level * (chip->quirk_double_soc ? 2 : 1);
1688c2ecf20Sopenharmony_ci	return regmap_update_bits(chip->regmap, MAX17040_CONFIG,
1698c2ecf20Sopenharmony_ci			MAX17040_ATHD_MASK, level);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic int max17040_set_soc_alert(struct max17040_chip *chip, bool enable)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	return regmap_update_bits(chip->regmap, MAX17040_CONFIG,
1758c2ecf20Sopenharmony_ci			MAX17040_ALSC_MASK, enable ? MAX17040_ALSC_MASK : 0);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	u16 mask = chip->data.rcomp_bytes == 2 ?
1818c2ecf20Sopenharmony_ci		0xffff : MAX17040_CFG_RCOMP_MASK;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return regmap_update_bits(chip->regmap, MAX17040_CONFIG, mask, rcomp);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct chip_data *d = &chip->data;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int max17040_get_vcell(struct max17040_chip *chip)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	u32 vcell;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	regmap_read(chip->regmap, MAX17040_VCELL, &vcell);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return max17040_raw_vcell_to_uvolts(chip, vcell);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int max17040_get_soc(struct max17040_chip *chip)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	u32 soc;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	regmap_read(chip->regmap, MAX17040_SOC, &soc);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	return soc >> (chip->quirk_double_soc ? 9 : 8);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int max17040_get_version(struct max17040_chip *chip)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	int ret;
2158c2ecf20Sopenharmony_ci	u32 version;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	ret = regmap_read(chip->regmap, MAX17040_VER, &version);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return ret ? ret : version;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int max17040_get_online(struct max17040_chip *chip)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	return chip->pdata && chip->pdata->battery_online ?
2258c2ecf20Sopenharmony_ci		chip->pdata->battery_online() : 1;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int max17040_get_status(struct max17040_chip *chip)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	if (!chip->pdata || !chip->pdata->charger_online
2318c2ecf20Sopenharmony_ci			|| !chip->pdata->charger_enable)
2328c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_UNKNOWN;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (max17040_get_soc(chip) > MAX17040_BATTERY_FULL)
2358c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_FULL;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (chip->pdata->charger_online())
2388c2ecf20Sopenharmony_ci		if (chip->pdata->charger_enable())
2398c2ecf20Sopenharmony_ci			return POWER_SUPPLY_STATUS_CHARGING;
2408c2ecf20Sopenharmony_ci		else
2418c2ecf20Sopenharmony_ci			return POWER_SUPPLY_STATUS_NOT_CHARGING;
2428c2ecf20Sopenharmony_ci	else
2438c2ecf20Sopenharmony_ci		return POWER_SUPPLY_STATUS_DISCHARGING;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int max17040_get_of_data(struct max17040_chip *chip)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct device *dev = &chip->client->dev;
2498c2ecf20Sopenharmony_ci	struct chip_data *data = &max17040_family[
2508c2ecf20Sopenharmony_ci		(uintptr_t) of_device_get_match_data(dev)];
2518c2ecf20Sopenharmony_ci	int rcomp_len;
2528c2ecf20Sopenharmony_ci	u8 rcomp[2];
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	chip->quirk_double_soc = device_property_read_bool(dev,
2558c2ecf20Sopenharmony_ci							   "maxim,double-soc");
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP;
2588c2ecf20Sopenharmony_ci	device_property_read_u32(dev,
2598c2ecf20Sopenharmony_ci				 "maxim,alert-low-soc-level",
2608c2ecf20Sopenharmony_ci				 &chip->low_soc_alert);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (chip->low_soc_alert <= 0 ||
2638c2ecf20Sopenharmony_ci	    chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) {
2648c2ecf20Sopenharmony_ci		dev_err(dev, "maxim,alert-low-soc-level out of bounds\n");
2658c2ecf20Sopenharmony_ci		return -EINVAL;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	rcomp_len = device_property_count_u8(dev, "maxim,rcomp");
2698c2ecf20Sopenharmony_ci	chip->rcomp = MAX17040_RCOMP_DEFAULT;
2708c2ecf20Sopenharmony_ci	if (rcomp_len == data->rcomp_bytes) {
2718c2ecf20Sopenharmony_ci		device_property_read_u8_array(dev, "maxim,rcomp",
2728c2ecf20Sopenharmony_ci					      rcomp, rcomp_len);
2738c2ecf20Sopenharmony_ci		chip->rcomp = rcomp_len == 2 ?
2748c2ecf20Sopenharmony_ci			rcomp[0] << 8 | rcomp[1] :
2758c2ecf20Sopenharmony_ci			rcomp[0] << 8;
2768c2ecf20Sopenharmony_ci	} else if (rcomp_len > 0) {
2778c2ecf20Sopenharmony_ci		dev_err(dev, "maxim,rcomp has incorrect length\n");
2788c2ecf20Sopenharmony_ci		return -EINVAL;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return 0;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic void max17040_check_changes(struct max17040_chip *chip)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	chip->soc = max17040_get_soc(chip);
2878c2ecf20Sopenharmony_ci	chip->status = max17040_get_status(chip);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic void max17040_queue_work(struct max17040_chip *chip)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &chip->work,
2938c2ecf20Sopenharmony_ci			   MAX17040_DELAY);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic void max17040_stop_work(void *data)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct max17040_chip *chip = data;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&chip->work);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic void max17040_work(struct work_struct *work)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct max17040_chip *chip;
3068c2ecf20Sopenharmony_ci	int last_soc, last_status;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	chip = container_of(work, struct max17040_chip, work.work);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* store SOC and status to check changes */
3118c2ecf20Sopenharmony_ci	last_soc = chip->soc;
3128c2ecf20Sopenharmony_ci	last_status = chip->status;
3138c2ecf20Sopenharmony_ci	max17040_check_changes(chip);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/* check changes and send uevent */
3168c2ecf20Sopenharmony_ci	if (last_soc != chip->soc || last_status != chip->status)
3178c2ecf20Sopenharmony_ci		power_supply_changed(chip->battery);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	max17040_queue_work(chip);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci/* Returns true if alert cause was SOC change, not low SOC */
3238c2ecf20Sopenharmony_cistatic bool max17040_handle_soc_alert(struct max17040_chip *chip)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	bool ret = true;
3268c2ecf20Sopenharmony_ci	u32 data;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	regmap_read(chip->regmap, MAX17040_STATUS, &data);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (data & MAX17040_STATUS_HD_MASK) {
3318c2ecf20Sopenharmony_ci		// this alert was caused by low soc
3328c2ecf20Sopenharmony_ci		ret = false;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci	if (data & MAX17040_STATUS_SC_MASK) {
3358c2ecf20Sopenharmony_ci		// soc change bit -- deassert to mark as handled
3368c2ecf20Sopenharmony_ci		regmap_write(chip->regmap, MAX17040_STATUS,
3378c2ecf20Sopenharmony_ci				data & ~MAX17040_STATUS_SC_MASK);
3388c2ecf20Sopenharmony_ci	}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	return ret;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic irqreturn_t max17040_thread_handler(int id, void *dev)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct max17040_chip *chip = dev;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip)))
3488c2ecf20Sopenharmony_ci		dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n");
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	/* read registers */
3518c2ecf20Sopenharmony_ci	max17040_check_changes(chip);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* send uevent */
3548c2ecf20Sopenharmony_ci	power_supply_changed(chip->battery);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	/* reset alert bit */
3578c2ecf20Sopenharmony_ci	max17040_set_low_soc_alert(chip, chip->low_soc_alert);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic int max17040_enable_alert_irq(struct max17040_chip *chip)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct i2c_client *client = chip->client;
3658c2ecf20Sopenharmony_ci	unsigned int flags;
3668c2ecf20Sopenharmony_ci	int ret;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
3698c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
3708c2ecf20Sopenharmony_ci					max17040_thread_handler, flags,
3718c2ecf20Sopenharmony_ci					chip->battery->desc->name, chip);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return ret;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic int max17040_prop_writeable(struct power_supply *psy,
3778c2ecf20Sopenharmony_ci				   enum power_supply_property psp)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	switch (psp) {
3808c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
3818c2ecf20Sopenharmony_ci		return 1;
3828c2ecf20Sopenharmony_ci	default:
3838c2ecf20Sopenharmony_ci		return 0;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic int max17040_set_property(struct power_supply *psy,
3888c2ecf20Sopenharmony_ci			    enum power_supply_property psp,
3898c2ecf20Sopenharmony_ci			    const union power_supply_propval *val)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct max17040_chip *chip = power_supply_get_drvdata(psy);
3928c2ecf20Sopenharmony_ci	int ret;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	switch (psp) {
3958c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
3968c2ecf20Sopenharmony_ci		/* alert threshold can be programmed from 1% up to 16/32% */
3978c2ecf20Sopenharmony_ci		if ((val->intval < 1) ||
3988c2ecf20Sopenharmony_ci		    (val->intval > (chip->quirk_double_soc ? 16 : 32))) {
3998c2ecf20Sopenharmony_ci			ret = -EINVAL;
4008c2ecf20Sopenharmony_ci			break;
4018c2ecf20Sopenharmony_ci		}
4028c2ecf20Sopenharmony_ci		ret = max17040_set_low_soc_alert(chip, val->intval);
4038c2ecf20Sopenharmony_ci		chip->low_soc_alert = val->intval;
4048c2ecf20Sopenharmony_ci		break;
4058c2ecf20Sopenharmony_ci	default:
4068c2ecf20Sopenharmony_ci		ret = -EINVAL;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return ret;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int max17040_get_property(struct power_supply *psy,
4138c2ecf20Sopenharmony_ci			    enum power_supply_property psp,
4148c2ecf20Sopenharmony_ci			    union power_supply_propval *val)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct max17040_chip *chip = power_supply_get_drvdata(psy);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	switch (psp) {
4198c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
4208c2ecf20Sopenharmony_ci		val->intval = max17040_get_status(chip);
4218c2ecf20Sopenharmony_ci		break;
4228c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
4238c2ecf20Sopenharmony_ci		val->intval = max17040_get_online(chip);
4248c2ecf20Sopenharmony_ci		break;
4258c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
4268c2ecf20Sopenharmony_ci		val->intval = max17040_get_vcell(chip);
4278c2ecf20Sopenharmony_ci		break;
4288c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
4298c2ecf20Sopenharmony_ci		val->intval = max17040_get_soc(chip);
4308c2ecf20Sopenharmony_ci		break;
4318c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
4328c2ecf20Sopenharmony_ci		val->intval = chip->low_soc_alert;
4338c2ecf20Sopenharmony_ci		break;
4348c2ecf20Sopenharmony_ci	default:
4358c2ecf20Sopenharmony_ci		return -EINVAL;
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci	return 0;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic const struct regmap_config max17040_regmap = {
4418c2ecf20Sopenharmony_ci	.reg_bits	= 8,
4428c2ecf20Sopenharmony_ci	.reg_stride	= 2,
4438c2ecf20Sopenharmony_ci	.val_bits	= 16,
4448c2ecf20Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_BIG,
4458c2ecf20Sopenharmony_ci};
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic enum power_supply_property max17040_battery_props[] = {
4488c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
4498c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
4508c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
4518c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
4528c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
4538c2ecf20Sopenharmony_ci};
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic const struct power_supply_desc max17040_battery_desc = {
4568c2ecf20Sopenharmony_ci	.name			= "battery",
4578c2ecf20Sopenharmony_ci	.type			= POWER_SUPPLY_TYPE_BATTERY,
4588c2ecf20Sopenharmony_ci	.get_property		= max17040_get_property,
4598c2ecf20Sopenharmony_ci	.set_property		= max17040_set_property,
4608c2ecf20Sopenharmony_ci	.property_is_writeable  = max17040_prop_writeable,
4618c2ecf20Sopenharmony_ci	.properties		= max17040_battery_props,
4628c2ecf20Sopenharmony_ci	.num_properties		= ARRAY_SIZE(max17040_battery_props),
4638c2ecf20Sopenharmony_ci};
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic int max17040_probe(struct i2c_client *client,
4668c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
4698c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = {};
4708c2ecf20Sopenharmony_ci	struct max17040_chip *chip;
4718c2ecf20Sopenharmony_ci	enum chip_id chip_id;
4728c2ecf20Sopenharmony_ci	bool enable_irq = false;
4738c2ecf20Sopenharmony_ci	int ret;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
4768c2ecf20Sopenharmony_ci		return -EIO;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
4798c2ecf20Sopenharmony_ci	if (!chip)
4808c2ecf20Sopenharmony_ci		return -ENOMEM;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	chip->client = client;
4838c2ecf20Sopenharmony_ci	chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap);
4848c2ecf20Sopenharmony_ci	chip->pdata = client->dev.platform_data;
4858c2ecf20Sopenharmony_ci	if (IS_ERR(chip->regmap))
4868c2ecf20Sopenharmony_ci		return PTR_ERR(chip->regmap);
4878c2ecf20Sopenharmony_ci	chip_id = (enum chip_id) id->driver_data;
4888c2ecf20Sopenharmony_ci	if (client->dev.of_node) {
4898c2ecf20Sopenharmony_ci		ret = max17040_get_of_data(chip);
4908c2ecf20Sopenharmony_ci		if (ret)
4918c2ecf20Sopenharmony_ci			return ret;
4928c2ecf20Sopenharmony_ci		chip_id = (enum chip_id) (uintptr_t)
4938c2ecf20Sopenharmony_ci			of_device_get_match_data(&client->dev);
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci	chip->data = max17040_family[chip_id];
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, chip);
4988c2ecf20Sopenharmony_ci	psy_cfg.drv_data = chip;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	chip->battery = devm_power_supply_register(&client->dev,
5018c2ecf20Sopenharmony_ci				&max17040_battery_desc, &psy_cfg);
5028c2ecf20Sopenharmony_ci	if (IS_ERR(chip->battery)) {
5038c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed: power supply register\n");
5048c2ecf20Sopenharmony_ci		return PTR_ERR(chip->battery);
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	ret = max17040_get_version(chip);
5088c2ecf20Sopenharmony_ci	if (ret < 0)
5098c2ecf20Sopenharmony_ci		return ret;
5108c2ecf20Sopenharmony_ci	dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041)
5138c2ecf20Sopenharmony_ci		max17040_reset(chip);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	max17040_set_rcomp(chip, chip->rcomp);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* check interrupt */
5188c2ecf20Sopenharmony_ci	if (client->irq && chip->data.has_low_soc_alert) {
5198c2ecf20Sopenharmony_ci		ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert);
5208c2ecf20Sopenharmony_ci		if (ret) {
5218c2ecf20Sopenharmony_ci			dev_err(&client->dev,
5228c2ecf20Sopenharmony_ci				"Failed to set low SOC alert: err %d\n", ret);
5238c2ecf20Sopenharmony_ci			return ret;
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		enable_irq = true;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (client->irq && chip->data.has_soc_alert) {
5308c2ecf20Sopenharmony_ci		ret = max17040_set_soc_alert(chip, 1);
5318c2ecf20Sopenharmony_ci		if (ret) {
5328c2ecf20Sopenharmony_ci			dev_err(&client->dev,
5338c2ecf20Sopenharmony_ci				"Failed to set SOC alert: err %d\n", ret);
5348c2ecf20Sopenharmony_ci			return ret;
5358c2ecf20Sopenharmony_ci		}
5368c2ecf20Sopenharmony_ci		enable_irq = true;
5378c2ecf20Sopenharmony_ci	} else {
5388c2ecf20Sopenharmony_ci		/* soc alerts negate the need for polling */
5398c2ecf20Sopenharmony_ci		INIT_DEFERRABLE_WORK(&chip->work, max17040_work);
5408c2ecf20Sopenharmony_ci		ret = devm_add_action(&client->dev, max17040_stop_work, chip);
5418c2ecf20Sopenharmony_ci		if (ret)
5428c2ecf20Sopenharmony_ci			return ret;
5438c2ecf20Sopenharmony_ci		max17040_queue_work(chip);
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if (enable_irq) {
5478c2ecf20Sopenharmony_ci		ret = max17040_enable_alert_irq(chip);
5488c2ecf20Sopenharmony_ci		if (ret) {
5498c2ecf20Sopenharmony_ci			client->irq = 0;
5508c2ecf20Sopenharmony_ci			dev_warn(&client->dev,
5518c2ecf20Sopenharmony_ci				 "Failed to get IRQ err %d\n", ret);
5528c2ecf20Sopenharmony_ci		}
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	return 0;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_cistatic int max17040_suspend(struct device *dev)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
5638c2ecf20Sopenharmony_ci	struct max17040_chip *chip = i2c_get_clientdata(client);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (client->irq && chip->data.has_soc_alert)
5668c2ecf20Sopenharmony_ci		// disable soc alert to prevent wakeup
5678c2ecf20Sopenharmony_ci		max17040_set_soc_alert(chip, 0);
5688c2ecf20Sopenharmony_ci	else
5698c2ecf20Sopenharmony_ci		cancel_delayed_work(&chip->work);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (client->irq && device_may_wakeup(dev))
5728c2ecf20Sopenharmony_ci		enable_irq_wake(client->irq);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	return 0;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic int max17040_resume(struct device *dev)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
5808c2ecf20Sopenharmony_ci	struct max17040_chip *chip = i2c_get_clientdata(client);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	if (client->irq && device_may_wakeup(dev))
5838c2ecf20Sopenharmony_ci		disable_irq_wake(client->irq);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (client->irq && chip->data.has_soc_alert)
5868c2ecf20Sopenharmony_ci		max17040_set_soc_alert(chip, 1);
5878c2ecf20Sopenharmony_ci	else
5888c2ecf20Sopenharmony_ci		max17040_queue_work(chip);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	return 0;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
5948c2ecf20Sopenharmony_ci#define MAX17040_PM_OPS (&max17040_pm_ops)
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci#else
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci#define MAX17040_PM_OPS NULL
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic const struct i2c_device_id max17040_id[] = {
6038c2ecf20Sopenharmony_ci	{ "max17040", ID_MAX17040 },
6048c2ecf20Sopenharmony_ci	{ "max17041", ID_MAX17041 },
6058c2ecf20Sopenharmony_ci	{ "max17043", ID_MAX17043 },
6068c2ecf20Sopenharmony_ci	{ "max77836-battery", ID_MAX17043 },
6078c2ecf20Sopenharmony_ci	{ "max17044", ID_MAX17044 },
6088c2ecf20Sopenharmony_ci	{ "max17048", ID_MAX17048 },
6098c2ecf20Sopenharmony_ci	{ "max17049", ID_MAX17049 },
6108c2ecf20Sopenharmony_ci	{ "max17058", ID_MAX17058 },
6118c2ecf20Sopenharmony_ci	{ "max17059", ID_MAX17059 },
6128c2ecf20Sopenharmony_ci	{ /* sentinel */ }
6138c2ecf20Sopenharmony_ci};
6148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max17040_id);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic const struct of_device_id max17040_of_match[] = {
6178c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 },
6188c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 },
6198c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 },
6208c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 },
6218c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 },
6228c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 },
6238c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 },
6248c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 },
6258c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 },
6268c2ecf20Sopenharmony_ci	{ /* sentinel */ },
6278c2ecf20Sopenharmony_ci};
6288c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, max17040_of_match);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic struct i2c_driver max17040_i2c_driver = {
6318c2ecf20Sopenharmony_ci	.driver	= {
6328c2ecf20Sopenharmony_ci		.name	= "max17040",
6338c2ecf20Sopenharmony_ci		.of_match_table = max17040_of_match,
6348c2ecf20Sopenharmony_ci		.pm	= MAX17040_PM_OPS,
6358c2ecf20Sopenharmony_ci	},
6368c2ecf20Sopenharmony_ci	.probe		= max17040_probe,
6378c2ecf20Sopenharmony_ci	.id_table	= max17040_id,
6388c2ecf20Sopenharmony_ci};
6398c2ecf20Sopenharmony_cimodule_i2c_driver(max17040_i2c_driver);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
6428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAX17040 Fuel Gauge");
6438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
644