162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci//  max17040_battery.c
462306a36Sopenharmony_ci//  fuel-gauge systems for lithium-ion (Li+) batteries
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci//  Copyright (C) 2009 Samsung Electronics
762306a36Sopenharmony_ci//  Minkyu Kang <mk7.kang@samsung.com>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/mutex.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/i2c.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/power_supply.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define MAX17040_VCELL	0x02
2362306a36Sopenharmony_ci#define MAX17040_SOC	0x04
2462306a36Sopenharmony_ci#define MAX17040_MODE	0x06
2562306a36Sopenharmony_ci#define MAX17040_VER	0x08
2662306a36Sopenharmony_ci#define MAX17040_CONFIG	0x0C
2762306a36Sopenharmony_ci#define MAX17040_STATUS	0x1A
2862306a36Sopenharmony_ci#define MAX17040_CMD	0xFE
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MAX17040_DELAY		1000
3262306a36Sopenharmony_ci#define MAX17040_BATTERY_FULL	95
3362306a36Sopenharmony_ci#define MAX17040_RCOMP_DEFAULT  0x9700
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define MAX17040_ATHD_MASK		0x3f
3662306a36Sopenharmony_ci#define MAX17040_ALSC_MASK		0x40
3762306a36Sopenharmony_ci#define MAX17040_ATHD_DEFAULT_POWER_UP	4
3862306a36Sopenharmony_ci#define MAX17040_STATUS_HD_MASK		0x1000
3962306a36Sopenharmony_ci#define MAX17040_STATUS_SC_MASK		0x2000
4062306a36Sopenharmony_ci#define MAX17040_CFG_RCOMP_MASK		0xff00
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cienum chip_id {
4362306a36Sopenharmony_ci	ID_MAX17040,
4462306a36Sopenharmony_ci	ID_MAX17041,
4562306a36Sopenharmony_ci	ID_MAX17043,
4662306a36Sopenharmony_ci	ID_MAX17044,
4762306a36Sopenharmony_ci	ID_MAX17048,
4862306a36Sopenharmony_ci	ID_MAX17049,
4962306a36Sopenharmony_ci	ID_MAX17058,
5062306a36Sopenharmony_ci	ID_MAX17059,
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* values that differ by chip_id */
5462306a36Sopenharmony_cistruct chip_data {
5562306a36Sopenharmony_ci	u16 reset_val;
5662306a36Sopenharmony_ci	u16 vcell_shift;
5762306a36Sopenharmony_ci	u16 vcell_mul;
5862306a36Sopenharmony_ci	u16 vcell_div;
5962306a36Sopenharmony_ci	u8  has_low_soc_alert;
6062306a36Sopenharmony_ci	u8  rcomp_bytes;
6162306a36Sopenharmony_ci	u8  has_soc_alert;
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct chip_data max17040_family[] = {
6562306a36Sopenharmony_ci	[ID_MAX17040] = {
6662306a36Sopenharmony_ci		.reset_val = 0x0054,
6762306a36Sopenharmony_ci		.vcell_shift = 4,
6862306a36Sopenharmony_ci		.vcell_mul = 1250,
6962306a36Sopenharmony_ci		.vcell_div = 1,
7062306a36Sopenharmony_ci		.has_low_soc_alert = 0,
7162306a36Sopenharmony_ci		.rcomp_bytes = 2,
7262306a36Sopenharmony_ci		.has_soc_alert = 0,
7362306a36Sopenharmony_ci	},
7462306a36Sopenharmony_ci	[ID_MAX17041] = {
7562306a36Sopenharmony_ci		.reset_val = 0x0054,
7662306a36Sopenharmony_ci		.vcell_shift = 4,
7762306a36Sopenharmony_ci		.vcell_mul = 2500,
7862306a36Sopenharmony_ci		.vcell_div = 1,
7962306a36Sopenharmony_ci		.has_low_soc_alert = 0,
8062306a36Sopenharmony_ci		.rcomp_bytes = 2,
8162306a36Sopenharmony_ci		.has_soc_alert = 0,
8262306a36Sopenharmony_ci	},
8362306a36Sopenharmony_ci	[ID_MAX17043] = {
8462306a36Sopenharmony_ci		.reset_val = 0x0054,
8562306a36Sopenharmony_ci		.vcell_shift = 4,
8662306a36Sopenharmony_ci		.vcell_mul = 1250,
8762306a36Sopenharmony_ci		.vcell_div = 1,
8862306a36Sopenharmony_ci		.has_low_soc_alert = 1,
8962306a36Sopenharmony_ci		.rcomp_bytes = 1,
9062306a36Sopenharmony_ci		.has_soc_alert = 0,
9162306a36Sopenharmony_ci	},
9262306a36Sopenharmony_ci	[ID_MAX17044] = {
9362306a36Sopenharmony_ci		.reset_val = 0x0054,
9462306a36Sopenharmony_ci		.vcell_shift = 4,
9562306a36Sopenharmony_ci		.vcell_mul = 2500,
9662306a36Sopenharmony_ci		.vcell_div = 1,
9762306a36Sopenharmony_ci		.has_low_soc_alert = 1,
9862306a36Sopenharmony_ci		.rcomp_bytes = 1,
9962306a36Sopenharmony_ci		.has_soc_alert = 0,
10062306a36Sopenharmony_ci	},
10162306a36Sopenharmony_ci	[ID_MAX17048] = {
10262306a36Sopenharmony_ci		.reset_val = 0x5400,
10362306a36Sopenharmony_ci		.vcell_shift = 0,
10462306a36Sopenharmony_ci		.vcell_mul = 625,
10562306a36Sopenharmony_ci		.vcell_div = 8,
10662306a36Sopenharmony_ci		.has_low_soc_alert = 1,
10762306a36Sopenharmony_ci		.rcomp_bytes = 1,
10862306a36Sopenharmony_ci		.has_soc_alert = 1,
10962306a36Sopenharmony_ci	},
11062306a36Sopenharmony_ci	[ID_MAX17049] = {
11162306a36Sopenharmony_ci		.reset_val = 0x5400,
11262306a36Sopenharmony_ci		.vcell_shift = 0,
11362306a36Sopenharmony_ci		.vcell_mul = 625,
11462306a36Sopenharmony_ci		.vcell_div = 4,
11562306a36Sopenharmony_ci		.has_low_soc_alert = 1,
11662306a36Sopenharmony_ci		.rcomp_bytes = 1,
11762306a36Sopenharmony_ci		.has_soc_alert = 1,
11862306a36Sopenharmony_ci	},
11962306a36Sopenharmony_ci	[ID_MAX17058] = {
12062306a36Sopenharmony_ci		.reset_val = 0x5400,
12162306a36Sopenharmony_ci		.vcell_shift = 0,
12262306a36Sopenharmony_ci		.vcell_mul = 625,
12362306a36Sopenharmony_ci		.vcell_div = 8,
12462306a36Sopenharmony_ci		.has_low_soc_alert = 1,
12562306a36Sopenharmony_ci		.rcomp_bytes = 1,
12662306a36Sopenharmony_ci		.has_soc_alert = 0,
12762306a36Sopenharmony_ci	},
12862306a36Sopenharmony_ci	[ID_MAX17059] = {
12962306a36Sopenharmony_ci		.reset_val = 0x5400,
13062306a36Sopenharmony_ci		.vcell_shift = 0,
13162306a36Sopenharmony_ci		.vcell_mul = 625,
13262306a36Sopenharmony_ci		.vcell_div = 4,
13362306a36Sopenharmony_ci		.has_low_soc_alert = 1,
13462306a36Sopenharmony_ci		.rcomp_bytes = 1,
13562306a36Sopenharmony_ci		.has_soc_alert = 0,
13662306a36Sopenharmony_ci	},
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistruct max17040_chip {
14062306a36Sopenharmony_ci	struct i2c_client		*client;
14162306a36Sopenharmony_ci	struct regmap			*regmap;
14262306a36Sopenharmony_ci	struct delayed_work		work;
14362306a36Sopenharmony_ci	struct power_supply		*battery;
14462306a36Sopenharmony_ci	struct chip_data		data;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	/* battery capacity */
14762306a36Sopenharmony_ci	int soc;
14862306a36Sopenharmony_ci	/* Low alert threshold from 32% to 1% of the State of Charge */
14962306a36Sopenharmony_ci	u32 low_soc_alert;
15062306a36Sopenharmony_ci	/* some devices return twice the capacity */
15162306a36Sopenharmony_ci	bool quirk_double_soc;
15262306a36Sopenharmony_ci	/* higher 8 bits for 17043+, 16 bits for 17040,41 */
15362306a36Sopenharmony_ci	u16 rcomp;
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int max17040_reset(struct max17040_chip *chip)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	return regmap_write(chip->regmap, MAX17040_CMD, chip->data.reset_val);
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	level = 32 - level * (chip->quirk_double_soc ? 2 : 1);
16462306a36Sopenharmony_ci	return regmap_update_bits(chip->regmap, MAX17040_CONFIG,
16562306a36Sopenharmony_ci			MAX17040_ATHD_MASK, level);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int max17040_set_soc_alert(struct max17040_chip *chip, bool enable)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	return regmap_update_bits(chip->regmap, MAX17040_CONFIG,
17162306a36Sopenharmony_ci			MAX17040_ALSC_MASK, enable ? MAX17040_ALSC_MASK : 0);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	u16 mask = chip->data.rcomp_bytes == 2 ?
17762306a36Sopenharmony_ci		0xffff : MAX17040_CFG_RCOMP_MASK;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return regmap_update_bits(chip->regmap, MAX17040_CONFIG, mask, rcomp);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct chip_data *d = &chip->data;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int max17040_get_vcell(struct max17040_chip *chip)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	u32 vcell;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	regmap_read(chip->regmap, MAX17040_VCELL, &vcell);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return max17040_raw_vcell_to_uvolts(chip, vcell);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int max17040_get_soc(struct max17040_chip *chip)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	u32 soc;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	regmap_read(chip->regmap, MAX17040_SOC, &soc);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return soc >> (chip->quirk_double_soc ? 9 : 8);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic int max17040_get_version(struct max17040_chip *chip)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	int ret;
21162306a36Sopenharmony_ci	u32 version;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	ret = regmap_read(chip->regmap, MAX17040_VER, &version);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return ret ? ret : version;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int max17040_get_online(struct max17040_chip *chip)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	return 1;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int max17040_get_of_data(struct max17040_chip *chip)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct device *dev = &chip->client->dev;
22662306a36Sopenharmony_ci	struct chip_data *data = &max17040_family[
22762306a36Sopenharmony_ci		(uintptr_t) of_device_get_match_data(dev)];
22862306a36Sopenharmony_ci	int rcomp_len;
22962306a36Sopenharmony_ci	u8 rcomp[2];
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	chip->quirk_double_soc = device_property_read_bool(dev,
23262306a36Sopenharmony_ci							   "maxim,double-soc");
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP;
23562306a36Sopenharmony_ci	device_property_read_u32(dev,
23662306a36Sopenharmony_ci				 "maxim,alert-low-soc-level",
23762306a36Sopenharmony_ci				 &chip->low_soc_alert);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (chip->low_soc_alert <= 0 ||
24062306a36Sopenharmony_ci	    chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) {
24162306a36Sopenharmony_ci		dev_err(dev, "maxim,alert-low-soc-level out of bounds\n");
24262306a36Sopenharmony_ci		return -EINVAL;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	rcomp_len = device_property_count_u8(dev, "maxim,rcomp");
24662306a36Sopenharmony_ci	chip->rcomp = MAX17040_RCOMP_DEFAULT;
24762306a36Sopenharmony_ci	if (rcomp_len == data->rcomp_bytes) {
24862306a36Sopenharmony_ci		if (!device_property_read_u8_array(dev, "maxim,rcomp",
24962306a36Sopenharmony_ci						   rcomp, rcomp_len))
25062306a36Sopenharmony_ci			chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] :
25162306a36Sopenharmony_ci				      rcomp[0] << 8;
25262306a36Sopenharmony_ci	} else if (rcomp_len > 0) {
25362306a36Sopenharmony_ci		dev_err(dev, "maxim,rcomp has incorrect length\n");
25462306a36Sopenharmony_ci		return -EINVAL;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic void max17040_check_changes(struct max17040_chip *chip)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	chip->soc = max17040_get_soc(chip);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void max17040_queue_work(struct max17040_chip *chip)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &chip->work,
26862306a36Sopenharmony_ci			   MAX17040_DELAY);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void max17040_stop_work(void *data)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct max17040_chip *chip = data;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	cancel_delayed_work_sync(&chip->work);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void max17040_work(struct work_struct *work)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct max17040_chip *chip;
28162306a36Sopenharmony_ci	int last_soc;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	chip = container_of(work, struct max17040_chip, work.work);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* store SOC to check changes */
28662306a36Sopenharmony_ci	last_soc = chip->soc;
28762306a36Sopenharmony_ci	max17040_check_changes(chip);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* check changes and send uevent */
29062306a36Sopenharmony_ci	if (last_soc != chip->soc)
29162306a36Sopenharmony_ci		power_supply_changed(chip->battery);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	max17040_queue_work(chip);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/* Returns true if alert cause was SOC change, not low SOC */
29762306a36Sopenharmony_cistatic bool max17040_handle_soc_alert(struct max17040_chip *chip)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	bool ret = true;
30062306a36Sopenharmony_ci	u32 data;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	regmap_read(chip->regmap, MAX17040_STATUS, &data);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (data & MAX17040_STATUS_HD_MASK) {
30562306a36Sopenharmony_ci		// this alert was caused by low soc
30662306a36Sopenharmony_ci		ret = false;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci	if (data & MAX17040_STATUS_SC_MASK) {
30962306a36Sopenharmony_ci		// soc change bit -- deassert to mark as handled
31062306a36Sopenharmony_ci		regmap_write(chip->regmap, MAX17040_STATUS,
31162306a36Sopenharmony_ci				data & ~MAX17040_STATUS_SC_MASK);
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return ret;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic irqreturn_t max17040_thread_handler(int id, void *dev)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct max17040_chip *chip = dev;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip)))
32262306a36Sopenharmony_ci		dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n");
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* read registers */
32562306a36Sopenharmony_ci	max17040_check_changes(chip);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* send uevent */
32862306a36Sopenharmony_ci	power_supply_changed(chip->battery);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* reset alert bit */
33162306a36Sopenharmony_ci	max17040_set_low_soc_alert(chip, chip->low_soc_alert);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return IRQ_HANDLED;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic int max17040_enable_alert_irq(struct max17040_chip *chip)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct i2c_client *client = chip->client;
33962306a36Sopenharmony_ci	int ret;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
34262306a36Sopenharmony_ci					max17040_thread_handler, IRQF_ONESHOT,
34362306a36Sopenharmony_ci					chip->battery->desc->name, chip);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return ret;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int max17040_prop_writeable(struct power_supply *psy,
34962306a36Sopenharmony_ci				   enum power_supply_property psp)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	switch (psp) {
35262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
35362306a36Sopenharmony_ci		return 1;
35462306a36Sopenharmony_ci	default:
35562306a36Sopenharmony_ci		return 0;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic int max17040_set_property(struct power_supply *psy,
36062306a36Sopenharmony_ci			    enum power_supply_property psp,
36162306a36Sopenharmony_ci			    const union power_supply_propval *val)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct max17040_chip *chip = power_supply_get_drvdata(psy);
36462306a36Sopenharmony_ci	int ret;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	switch (psp) {
36762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
36862306a36Sopenharmony_ci		/* alert threshold can be programmed from 1% up to 16/32% */
36962306a36Sopenharmony_ci		if ((val->intval < 1) ||
37062306a36Sopenharmony_ci		    (val->intval > (chip->quirk_double_soc ? 16 : 32))) {
37162306a36Sopenharmony_ci			ret = -EINVAL;
37262306a36Sopenharmony_ci			break;
37362306a36Sopenharmony_ci		}
37462306a36Sopenharmony_ci		ret = max17040_set_low_soc_alert(chip, val->intval);
37562306a36Sopenharmony_ci		chip->low_soc_alert = val->intval;
37662306a36Sopenharmony_ci		break;
37762306a36Sopenharmony_ci	default:
37862306a36Sopenharmony_ci		ret = -EINVAL;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return ret;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int max17040_get_property(struct power_supply *psy,
38562306a36Sopenharmony_ci			    enum power_supply_property psp,
38662306a36Sopenharmony_ci			    union power_supply_propval *val)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct max17040_chip *chip = power_supply_get_drvdata(psy);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	switch (psp) {
39162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
39262306a36Sopenharmony_ci		val->intval = max17040_get_online(chip);
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
39562306a36Sopenharmony_ci		val->intval = max17040_get_vcell(chip);
39662306a36Sopenharmony_ci		break;
39762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
39862306a36Sopenharmony_ci		val->intval = max17040_get_soc(chip);
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:
40162306a36Sopenharmony_ci		val->intval = chip->low_soc_alert;
40262306a36Sopenharmony_ci		break;
40362306a36Sopenharmony_ci	default:
40462306a36Sopenharmony_ci		return -EINVAL;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic const struct regmap_config max17040_regmap = {
41062306a36Sopenharmony_ci	.reg_bits	= 8,
41162306a36Sopenharmony_ci	.reg_stride	= 2,
41262306a36Sopenharmony_ci	.val_bits	= 16,
41362306a36Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_BIG,
41462306a36Sopenharmony_ci};
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic enum power_supply_property max17040_battery_props[] = {
41762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE,
41862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
41962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
42062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,
42162306a36Sopenharmony_ci};
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic const struct power_supply_desc max17040_battery_desc = {
42462306a36Sopenharmony_ci	.name			= "battery",
42562306a36Sopenharmony_ci	.type			= POWER_SUPPLY_TYPE_BATTERY,
42662306a36Sopenharmony_ci	.get_property		= max17040_get_property,
42762306a36Sopenharmony_ci	.set_property		= max17040_set_property,
42862306a36Sopenharmony_ci	.property_is_writeable  = max17040_prop_writeable,
42962306a36Sopenharmony_ci	.properties		= max17040_battery_props,
43062306a36Sopenharmony_ci	.num_properties		= ARRAY_SIZE(max17040_battery_props),
43162306a36Sopenharmony_ci};
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int max17040_probe(struct i2c_client *client)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(client);
43662306a36Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
43762306a36Sopenharmony_ci	struct power_supply_config psy_cfg = {};
43862306a36Sopenharmony_ci	struct max17040_chip *chip;
43962306a36Sopenharmony_ci	enum chip_id chip_id;
44062306a36Sopenharmony_ci	bool enable_irq = false;
44162306a36Sopenharmony_ci	int ret;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
44462306a36Sopenharmony_ci		return -EIO;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
44762306a36Sopenharmony_ci	if (!chip)
44862306a36Sopenharmony_ci		return -ENOMEM;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	chip->client = client;
45162306a36Sopenharmony_ci	chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap);
45262306a36Sopenharmony_ci	if (IS_ERR(chip->regmap))
45362306a36Sopenharmony_ci		return PTR_ERR(chip->regmap);
45462306a36Sopenharmony_ci	chip_id = (enum chip_id) id->driver_data;
45562306a36Sopenharmony_ci	if (client->dev.of_node) {
45662306a36Sopenharmony_ci		ret = max17040_get_of_data(chip);
45762306a36Sopenharmony_ci		if (ret)
45862306a36Sopenharmony_ci			return ret;
45962306a36Sopenharmony_ci		chip_id = (uintptr_t)of_device_get_match_data(&client->dev);
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci	chip->data = max17040_family[chip_id];
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	i2c_set_clientdata(client, chip);
46462306a36Sopenharmony_ci	psy_cfg.drv_data = chip;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	chip->battery = devm_power_supply_register(&client->dev,
46762306a36Sopenharmony_ci				&max17040_battery_desc, &psy_cfg);
46862306a36Sopenharmony_ci	if (IS_ERR(chip->battery)) {
46962306a36Sopenharmony_ci		dev_err(&client->dev, "failed: power supply register\n");
47062306a36Sopenharmony_ci		return PTR_ERR(chip->battery);
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	ret = max17040_get_version(chip);
47462306a36Sopenharmony_ci	if (ret < 0)
47562306a36Sopenharmony_ci		return ret;
47662306a36Sopenharmony_ci	dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041)
47962306a36Sopenharmony_ci		max17040_reset(chip);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	max17040_set_rcomp(chip, chip->rcomp);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* check interrupt */
48462306a36Sopenharmony_ci	if (client->irq && chip->data.has_low_soc_alert) {
48562306a36Sopenharmony_ci		ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert);
48662306a36Sopenharmony_ci		if (ret) {
48762306a36Sopenharmony_ci			dev_err(&client->dev,
48862306a36Sopenharmony_ci				"Failed to set low SOC alert: err %d\n", ret);
48962306a36Sopenharmony_ci			return ret;
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		enable_irq = true;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (client->irq && chip->data.has_soc_alert) {
49662306a36Sopenharmony_ci		ret = max17040_set_soc_alert(chip, 1);
49762306a36Sopenharmony_ci		if (ret) {
49862306a36Sopenharmony_ci			dev_err(&client->dev,
49962306a36Sopenharmony_ci				"Failed to set SOC alert: err %d\n", ret);
50062306a36Sopenharmony_ci			return ret;
50162306a36Sopenharmony_ci		}
50262306a36Sopenharmony_ci		enable_irq = true;
50362306a36Sopenharmony_ci	} else {
50462306a36Sopenharmony_ci		/* soc alerts negate the need for polling */
50562306a36Sopenharmony_ci		INIT_DEFERRABLE_WORK(&chip->work, max17040_work);
50662306a36Sopenharmony_ci		ret = devm_add_action(&client->dev, max17040_stop_work, chip);
50762306a36Sopenharmony_ci		if (ret)
50862306a36Sopenharmony_ci			return ret;
50962306a36Sopenharmony_ci		max17040_queue_work(chip);
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (enable_irq) {
51362306a36Sopenharmony_ci		ret = max17040_enable_alert_irq(chip);
51462306a36Sopenharmony_ci		if (ret) {
51562306a36Sopenharmony_ci			client->irq = 0;
51662306a36Sopenharmony_ci			dev_warn(&client->dev,
51762306a36Sopenharmony_ci				 "Failed to get IRQ err %d\n", ret);
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic int max17040_suspend(struct device *dev)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
52962306a36Sopenharmony_ci	struct max17040_chip *chip = i2c_get_clientdata(client);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (client->irq && chip->data.has_soc_alert)
53262306a36Sopenharmony_ci		// disable soc alert to prevent wakeup
53362306a36Sopenharmony_ci		max17040_set_soc_alert(chip, 0);
53462306a36Sopenharmony_ci	else
53562306a36Sopenharmony_ci		cancel_delayed_work(&chip->work);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (client->irq && device_may_wakeup(dev))
53862306a36Sopenharmony_ci		enable_irq_wake(client->irq);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int max17040_resume(struct device *dev)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
54662306a36Sopenharmony_ci	struct max17040_chip *chip = i2c_get_clientdata(client);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (client->irq && device_may_wakeup(dev))
54962306a36Sopenharmony_ci		disable_irq_wake(client->irq);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (client->irq && chip->data.has_soc_alert)
55262306a36Sopenharmony_ci		max17040_set_soc_alert(chip, 1);
55362306a36Sopenharmony_ci	else
55462306a36Sopenharmony_ci		max17040_queue_work(chip);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
56062306a36Sopenharmony_ci#define MAX17040_PM_OPS (&max17040_pm_ops)
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci#else
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci#define MAX17040_PM_OPS NULL
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic const struct i2c_device_id max17040_id[] = {
56962306a36Sopenharmony_ci	{ "max17040", ID_MAX17040 },
57062306a36Sopenharmony_ci	{ "max17041", ID_MAX17041 },
57162306a36Sopenharmony_ci	{ "max17043", ID_MAX17043 },
57262306a36Sopenharmony_ci	{ "max77836-battery", ID_MAX17043 },
57362306a36Sopenharmony_ci	{ "max17044", ID_MAX17044 },
57462306a36Sopenharmony_ci	{ "max17048", ID_MAX17048 },
57562306a36Sopenharmony_ci	{ "max17049", ID_MAX17049 },
57662306a36Sopenharmony_ci	{ "max17058", ID_MAX17058 },
57762306a36Sopenharmony_ci	{ "max17059", ID_MAX17059 },
57862306a36Sopenharmony_ci	{ /* sentinel */ }
57962306a36Sopenharmony_ci};
58062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, max17040_id);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic const struct of_device_id max17040_of_match[] = {
58362306a36Sopenharmony_ci	{ .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 },
58462306a36Sopenharmony_ci	{ .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 },
58562306a36Sopenharmony_ci	{ .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 },
58662306a36Sopenharmony_ci	{ .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 },
58762306a36Sopenharmony_ci	{ .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 },
58862306a36Sopenharmony_ci	{ .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 },
58962306a36Sopenharmony_ci	{ .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 },
59062306a36Sopenharmony_ci	{ .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 },
59162306a36Sopenharmony_ci	{ .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 },
59262306a36Sopenharmony_ci	{ /* sentinel */ },
59362306a36Sopenharmony_ci};
59462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, max17040_of_match);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic struct i2c_driver max17040_i2c_driver = {
59762306a36Sopenharmony_ci	.driver	= {
59862306a36Sopenharmony_ci		.name	= "max17040",
59962306a36Sopenharmony_ci		.of_match_table = max17040_of_match,
60062306a36Sopenharmony_ci		.pm	= MAX17040_PM_OPS,
60162306a36Sopenharmony_ci	},
60262306a36Sopenharmony_ci	.probe		= max17040_probe,
60362306a36Sopenharmony_ci	.id_table	= max17040_id,
60462306a36Sopenharmony_ci};
60562306a36Sopenharmony_cimodule_i2c_driver(max17040_i2c_driver);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ciMODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
60862306a36Sopenharmony_ciMODULE_DESCRIPTION("MAX17040 Fuel Gauge");
60962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
610