brintos

brintos / linux-shallow public Read only

0
0
Text · 15.2 KiB · 51310f6 Raw
637 lines · c
1// SPDX-License-Identifier: GPL-2.02//3//  max17040_battery.c4//  fuel-gauge systems for lithium-ion (Li+) batteries5//6//  Copyright (C) 2009 Samsung Electronics7//  Minkyu Kang <mk7.kang@samsung.com>8 9#include <linux/module.h>10#include <linux/init.h>11#include <linux/platform_device.h>12#include <linux/mutex.h>13#include <linux/err.h>14#include <linux/i2c.h>15#include <linux/delay.h>16#include <linux/interrupt.h>17#include <linux/power_supply.h>18#include <linux/of.h>19#include <linux/regmap.h>20#include <linux/slab.h>21#include <linux/iio/consumer.h>22 23#define MAX17040_VCELL	0x0224#define MAX17040_SOC	0x0425#define MAX17040_MODE	0x0626#define MAX17040_VER	0x0827#define MAX17040_CONFIG	0x0C28#define MAX17040_STATUS	0x1A29#define MAX17040_CMD	0xFE30 31 32#define MAX17040_DELAY		100033#define MAX17040_BATTERY_FULL	9534#define MAX17040_RCOMP_DEFAULT  0x970035 36#define MAX17040_ATHD_MASK		0x3f37#define MAX17040_ALSC_MASK		0x4038#define MAX17040_ATHD_DEFAULT_POWER_UP	439#define MAX17040_STATUS_HD_MASK		0x100040#define MAX17040_STATUS_SC_MASK		0x200041#define MAX17040_CFG_RCOMP_MASK		0xff0042 43enum chip_id {44	ID_MAX17040,45	ID_MAX17041,46	ID_MAX17043,47	ID_MAX17044,48	ID_MAX17048,49	ID_MAX17049,50	ID_MAX17058,51	ID_MAX17059,52};53 54/* values that differ by chip_id */55struct chip_data {56	u16 reset_val;57	u16 vcell_shift;58	u16 vcell_mul;59	u16 vcell_div;60	u8  has_low_soc_alert;61	u8  rcomp_bytes;62	u8  has_soc_alert;63};64 65static struct chip_data max17040_family[] = {66	[ID_MAX17040] = {67		.reset_val = 0x0054,68		.vcell_shift = 4,69		.vcell_mul = 1250,70		.vcell_div = 1,71		.has_low_soc_alert = 0,72		.rcomp_bytes = 2,73		.has_soc_alert = 0,74	},75	[ID_MAX17041] = {76		.reset_val = 0x0054,77		.vcell_shift = 4,78		.vcell_mul = 2500,79		.vcell_div = 1,80		.has_low_soc_alert = 0,81		.rcomp_bytes = 2,82		.has_soc_alert = 0,83	},84	[ID_MAX17043] = {85		.reset_val = 0x0054,86		.vcell_shift = 4,87		.vcell_mul = 1250,88		.vcell_div = 1,89		.has_low_soc_alert = 1,90		.rcomp_bytes = 1,91		.has_soc_alert = 0,92	},93	[ID_MAX17044] = {94		.reset_val = 0x0054,95		.vcell_shift = 4,96		.vcell_mul = 2500,97		.vcell_div = 1,98		.has_low_soc_alert = 1,99		.rcomp_bytes = 1,100		.has_soc_alert = 0,101	},102	[ID_MAX17048] = {103		.reset_val = 0x5400,104		.vcell_shift = 0,105		.vcell_mul = 625,106		.vcell_div = 8,107		.has_low_soc_alert = 1,108		.rcomp_bytes = 1,109		.has_soc_alert = 1,110	},111	[ID_MAX17049] = {112		.reset_val = 0x5400,113		.vcell_shift = 0,114		.vcell_mul = 625,115		.vcell_div = 4,116		.has_low_soc_alert = 1,117		.rcomp_bytes = 1,118		.has_soc_alert = 1,119	},120	[ID_MAX17058] = {121		.reset_val = 0x5400,122		.vcell_shift = 0,123		.vcell_mul = 625,124		.vcell_div = 8,125		.has_low_soc_alert = 1,126		.rcomp_bytes = 1,127		.has_soc_alert = 0,128	},129	[ID_MAX17059] = {130		.reset_val = 0x5400,131		.vcell_shift = 0,132		.vcell_mul = 625,133		.vcell_div = 4,134		.has_low_soc_alert = 1,135		.rcomp_bytes = 1,136		.has_soc_alert = 0,137	},138};139 140struct max17040_chip {141	struct i2c_client		*client;142	struct regmap			*regmap;143	struct delayed_work		work;144	struct power_supply		*battery;145	struct chip_data		data;146	struct iio_channel		*channel_temp;147 148	/* battery capacity */149	int soc;150	/* Low alert threshold from 32% to 1% of the State of Charge */151	u32 low_soc_alert;152	/* some devices return twice the capacity */153	bool quirk_double_soc;154	/* higher 8 bits for 17043+, 16 bits for 17040,41 */155	u16 rcomp;156};157 158static int max17040_reset(struct max17040_chip *chip)159{160	return regmap_write(chip->regmap, MAX17040_CMD, chip->data.reset_val);161}162 163static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level)164{165	level = 32 - level * (chip->quirk_double_soc ? 2 : 1);166	return regmap_update_bits(chip->regmap, MAX17040_CONFIG,167			MAX17040_ATHD_MASK, level);168}169 170static int max17040_set_soc_alert(struct max17040_chip *chip, bool enable)171{172	return regmap_update_bits(chip->regmap, MAX17040_CONFIG,173			MAX17040_ALSC_MASK, enable ? MAX17040_ALSC_MASK : 0);174}175 176static int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp)177{178	u16 mask = chip->data.rcomp_bytes == 2 ?179		0xffff : MAX17040_CFG_RCOMP_MASK;180 181	return regmap_update_bits(chip->regmap, MAX17040_CONFIG, mask, rcomp);182}183 184static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell)185{186	struct chip_data *d = &chip->data;187 188	return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div;189}190 191 192static int max17040_get_vcell(struct max17040_chip *chip)193{194	u32 vcell;195 196	regmap_read(chip->regmap, MAX17040_VCELL, &vcell);197 198	return max17040_raw_vcell_to_uvolts(chip, vcell);199}200 201static int max17040_get_soc(struct max17040_chip *chip)202{203	u32 soc;204 205	regmap_read(chip->regmap, MAX17040_SOC, &soc);206 207	return soc >> (chip->quirk_double_soc ? 9 : 8);208}209 210static int max17040_get_version(struct max17040_chip *chip)211{212	int ret;213	u32 version;214 215	ret = regmap_read(chip->regmap, MAX17040_VER, &version);216 217	return ret ? ret : version;218}219 220static int max17040_get_online(struct max17040_chip *chip)221{222	return 1;223}224 225static int max17040_get_of_data(struct max17040_chip *chip)226{227	struct device *dev = &chip->client->dev;228	struct chip_data *data = &max17040_family[229		(uintptr_t) of_device_get_match_data(dev)];230	int rcomp_len;231	u8 rcomp[2];232 233	chip->quirk_double_soc = device_property_read_bool(dev,234							   "maxim,double-soc");235 236	chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP;237	device_property_read_u32(dev,238				 "maxim,alert-low-soc-level",239				 &chip->low_soc_alert);240 241	if (chip->low_soc_alert <= 0 ||242	    chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) {243		dev_err(dev, "maxim,alert-low-soc-level out of bounds\n");244		return -EINVAL;245	}246 247	rcomp_len = device_property_count_u8(dev, "maxim,rcomp");248	chip->rcomp = MAX17040_RCOMP_DEFAULT;249	if (rcomp_len == data->rcomp_bytes) {250		if (!device_property_read_u8_array(dev, "maxim,rcomp",251						   rcomp, rcomp_len))252			chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] :253				      rcomp[0] << 8;254	} else if (rcomp_len > 0) {255		dev_err(dev, "maxim,rcomp has incorrect length\n");256		return -EINVAL;257	}258 259	return 0;260}261 262static void max17040_check_changes(struct max17040_chip *chip)263{264	chip->soc = max17040_get_soc(chip);265}266 267static void max17040_queue_work(struct max17040_chip *chip)268{269	queue_delayed_work(system_power_efficient_wq, &chip->work,270			   MAX17040_DELAY);271}272 273static void max17040_stop_work(void *data)274{275	struct max17040_chip *chip = data;276 277	cancel_delayed_work_sync(&chip->work);278}279 280static void max17040_work(struct work_struct *work)281{282	struct max17040_chip *chip;283	int last_soc;284 285	chip = container_of(work, struct max17040_chip, work.work);286 287	/* store SOC to check changes */288	last_soc = chip->soc;289	max17040_check_changes(chip);290 291	/* check changes and send uevent */292	if (last_soc != chip->soc)293		power_supply_changed(chip->battery);294 295	max17040_queue_work(chip);296}297 298/* Returns true if alert cause was SOC change, not low SOC */299static bool max17040_handle_soc_alert(struct max17040_chip *chip)300{301	bool ret = true;302	u32 data;303 304	regmap_read(chip->regmap, MAX17040_STATUS, &data);305 306	if (data & MAX17040_STATUS_HD_MASK) {307		// this alert was caused by low soc308		ret = false;309	}310	if (data & MAX17040_STATUS_SC_MASK) {311		// soc change bit -- deassert to mark as handled312		regmap_write(chip->regmap, MAX17040_STATUS,313				data & ~MAX17040_STATUS_SC_MASK);314	}315 316	return ret;317}318 319static irqreturn_t max17040_thread_handler(int id, void *dev)320{321	struct max17040_chip *chip = dev;322 323	if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip)))324		dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n");325 326	/* read registers */327	max17040_check_changes(chip);328 329	/* send uevent */330	power_supply_changed(chip->battery);331 332	/* reset alert bit */333	max17040_set_low_soc_alert(chip, chip->low_soc_alert);334 335	return IRQ_HANDLED;336}337 338static int max17040_enable_alert_irq(struct max17040_chip *chip)339{340	struct i2c_client *client = chip->client;341	int ret;342 343	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,344					max17040_thread_handler, IRQF_ONESHOT,345					chip->battery->desc->name, chip);346 347	return ret;348}349 350static int max17040_prop_writeable(struct power_supply *psy,351				   enum power_supply_property psp)352{353	switch (psp) {354	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:355		return 1;356	default:357		return 0;358	}359}360 361static int max17040_set_property(struct power_supply *psy,362			    enum power_supply_property psp,363			    const union power_supply_propval *val)364{365	struct max17040_chip *chip = power_supply_get_drvdata(psy);366	int ret;367 368	switch (psp) {369	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:370		/* alert threshold can be programmed from 1% up to 16/32% */371		if ((val->intval < 1) ||372		    (val->intval > (chip->quirk_double_soc ? 16 : 32))) {373			ret = -EINVAL;374			break;375		}376		ret = max17040_set_low_soc_alert(chip, val->intval);377		chip->low_soc_alert = val->intval;378		break;379	default:380		ret = -EINVAL;381	}382 383	return ret;384}385 386static int max17040_get_property(struct power_supply *psy,387			    enum power_supply_property psp,388			    union power_supply_propval *val)389{390	struct max17040_chip *chip = power_supply_get_drvdata(psy);391 392	switch (psp) {393	case POWER_SUPPLY_PROP_ONLINE:394	case POWER_SUPPLY_PROP_PRESENT:395		val->intval = max17040_get_online(chip);396		break;397	case POWER_SUPPLY_PROP_VOLTAGE_NOW:398		val->intval = max17040_get_vcell(chip);399		break;400	case POWER_SUPPLY_PROP_CAPACITY:401		val->intval = max17040_get_soc(chip);402		break;403	case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN:404		val->intval = chip->low_soc_alert;405		break;406	case POWER_SUPPLY_PROP_STATUS:407		power_supply_get_property_from_supplier(psy, psp, val);408		break;409	case POWER_SUPPLY_PROP_TEMP:410		if (!chip->channel_temp)411			return -ENODATA;412 413		iio_read_channel_processed_scale(chip->channel_temp,414						 &val->intval, 10);415		break;416	default:417		return -EINVAL;418	}419	return 0;420}421 422static const struct regmap_config max17040_regmap = {423	.reg_bits	= 8,424	.reg_stride	= 2,425	.val_bits	= 16,426	.val_format_endian = REGMAP_ENDIAN_BIG,427};428 429static enum power_supply_property max17040_battery_props[] = {430	POWER_SUPPLY_PROP_ONLINE,431	POWER_SUPPLY_PROP_PRESENT,432	POWER_SUPPLY_PROP_VOLTAGE_NOW,433	POWER_SUPPLY_PROP_CAPACITY,434	POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN,435	POWER_SUPPLY_PROP_STATUS,436	POWER_SUPPLY_PROP_TEMP,437};438 439static const struct power_supply_desc max17040_battery_desc = {440	.name			= "battery",441	.type			= POWER_SUPPLY_TYPE_BATTERY,442	.get_property		= max17040_get_property,443	.set_property		= max17040_set_property,444	.property_is_writeable  = max17040_prop_writeable,445	.properties		= max17040_battery_props,446	.num_properties		= ARRAY_SIZE(max17040_battery_props),447};448 449static int max17040_probe(struct i2c_client *client)450{451	const struct i2c_device_id *id = i2c_client_get_device_id(client);452	struct i2c_adapter *adapter = client->adapter;453	struct power_supply_config psy_cfg = {};454	struct max17040_chip *chip;455	enum chip_id chip_id;456	bool enable_irq = false;457	int ret;458 459	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))460		return -EIO;461 462	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);463	if (!chip)464		return -ENOMEM;465 466	chip->client = client;467	chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap);468	if (IS_ERR(chip->regmap))469		return PTR_ERR(chip->regmap);470	chip_id = (enum chip_id) id->driver_data;471	if (client->dev.of_node) {472		ret = max17040_get_of_data(chip);473		if (ret)474			return ret;475		chip_id = (uintptr_t)of_device_get_match_data(&client->dev);476	}477	chip->data = max17040_family[chip_id];478 479	i2c_set_clientdata(client, chip);480	psy_cfg.drv_data = chip;481 482	/* Switch to devm_iio_channel_get_optional when available  */483	chip->channel_temp = devm_iio_channel_get(&client->dev, "temp");484	if (IS_ERR(chip->channel_temp)) {485		ret = PTR_ERR(chip->channel_temp);486		if (ret != -ENODEV)487			return dev_err_probe(&client->dev, PTR_ERR(chip->channel_temp),488					     "failed to get temp\n");489		else490			chip->channel_temp = NULL;491	}492 493	chip->battery = devm_power_supply_register(&client->dev,494				&max17040_battery_desc, &psy_cfg);495	if (IS_ERR(chip->battery)) {496		dev_err(&client->dev, "failed: power supply register\n");497		return PTR_ERR(chip->battery);498	}499 500	ret = max17040_get_version(chip);501	if (ret < 0)502		return ret;503	dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret);504 505	if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041)506		max17040_reset(chip);507 508	max17040_set_rcomp(chip, chip->rcomp);509 510	/* check interrupt */511	if (client->irq && chip->data.has_low_soc_alert) {512		ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert);513		if (ret) {514			dev_err(&client->dev,515				"Failed to set low SOC alert: err %d\n", ret);516			return ret;517		}518 519		enable_irq = true;520	}521 522	if (client->irq && chip->data.has_soc_alert) {523		ret = max17040_set_soc_alert(chip, 1);524		if (ret) {525			dev_err(&client->dev,526				"Failed to set SOC alert: err %d\n", ret);527			return ret;528		}529		enable_irq = true;530	} else {531		/* soc alerts negate the need for polling */532		INIT_DEFERRABLE_WORK(&chip->work, max17040_work);533		ret = devm_add_action(&client->dev, max17040_stop_work, chip);534		if (ret)535			return ret;536		max17040_queue_work(chip);537	}538 539	if (enable_irq) {540		ret = max17040_enable_alert_irq(chip);541		if (ret) {542			client->irq = 0;543			dev_warn(&client->dev,544				 "Failed to get IRQ err %d\n", ret);545		}546	}547 548	return 0;549}550 551#ifdef CONFIG_PM_SLEEP552 553static int max17040_suspend(struct device *dev)554{555	struct i2c_client *client = to_i2c_client(dev);556	struct max17040_chip *chip = i2c_get_clientdata(client);557 558	if (client->irq && chip->data.has_soc_alert)559		// disable soc alert to prevent wakeup560		max17040_set_soc_alert(chip, 0);561	else562		cancel_delayed_work(&chip->work);563 564	if (client->irq && device_may_wakeup(dev))565		enable_irq_wake(client->irq);566 567	return 0;568}569 570static int max17040_resume(struct device *dev)571{572	struct i2c_client *client = to_i2c_client(dev);573	struct max17040_chip *chip = i2c_get_clientdata(client);574 575	if (client->irq && device_may_wakeup(dev))576		disable_irq_wake(client->irq);577 578	if (client->irq && chip->data.has_soc_alert)579		max17040_set_soc_alert(chip, 1);580	else581		max17040_queue_work(chip);582 583	return 0;584}585 586static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);587#define MAX17040_PM_OPS (&max17040_pm_ops)588 589#else590 591#define MAX17040_PM_OPS NULL592 593#endif /* CONFIG_PM_SLEEP */594 595static const struct i2c_device_id max17040_id[] = {596	{ "max17040", ID_MAX17040 },597	{ "max17041", ID_MAX17041 },598	{ "max17043", ID_MAX17043 },599	{ "max77836-battery", ID_MAX17043 },600	{ "max17044", ID_MAX17044 },601	{ "max17048", ID_MAX17048 },602	{ "max17049", ID_MAX17049 },603	{ "max17058", ID_MAX17058 },604	{ "max17059", ID_MAX17059 },605	{ /* sentinel */ }606};607MODULE_DEVICE_TABLE(i2c, max17040_id);608 609static const struct of_device_id max17040_of_match[] = {610	{ .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 },611	{ .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 },612	{ .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 },613	{ .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 },614	{ .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 },615	{ .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 },616	{ .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 },617	{ .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 },618	{ .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 },619	{ /* sentinel */ },620};621MODULE_DEVICE_TABLE(of, max17040_of_match);622 623static struct i2c_driver max17040_i2c_driver = {624	.driver	= {625		.name	= "max17040",626		.of_match_table = max17040_of_match,627		.pm	= MAX17040_PM_OPS,628	},629	.probe		= max17040_probe,630	.id_table	= max17040_id,631};632module_i2c_driver(max17040_i2c_driver);633 634MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");635MODULE_DESCRIPTION("MAX17040 Fuel Gauge");636MODULE_LICENSE("GPL");637