File Index
Files and directories
Sign in
FILE

mm8013.c

File preview with syntax highlighting.

linux-6.19.14/drivers/power/supply/mm8013.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
  4 * Copyright (c) 2023, Linaro Limited
  5 */
  6#include <linux/delay.h>
  7#include <linux/i2c.h>
  8#include <linux/power_supply.h>
  9#include <linux/regmap.h>
 10
 11#define REG_BATID			0x00 /* This one is very unclear */
 12 #define BATID_101			0x0101 /* 107kOhm */
 13 #define BATID_102			0x0102 /* 10kOhm */
 14#define REG_TEMPERATURE			0x06
 15#define REG_VOLTAGE			0x08
 16#define REG_FLAGS			0x0a
 17 #define MM8013_FLAG_OTC		BIT(15)
 18 #define MM8013_FLAG_OTD		BIT(14)
 19 #define MM8013_FLAG_BATHI		BIT(13)
 20 #define MM8013_FLAG_BATLOW		BIT(12)
 21 #define MM8013_FLAG_CHG_INH		BIT(11)
 22 #define MM8013_FLAG_FC			BIT(9)
 23 #define MM8013_FLAG_CHG		BIT(8)
 24 #define MM8013_FLAG_OCC		BIT(6)
 25 #define MM8013_FLAG_ODC		BIT(5)
 26 #define MM8013_FLAG_OT			BIT(4)
 27 #define MM8013_FLAG_UT			BIT(3)
 28 #define MM8013_FLAG_DSG		BIT(0)
 29#define REG_FULL_CHARGE_CAPACITY	0x0e
 30#define REG_NOMINAL_CHARGE_CAPACITY	0x0c
 31#define REG_AVERAGE_CURRENT		0x14
 32#define REG_AVERAGE_TIME_TO_EMPTY	0x16
 33#define REG_AVERAGE_TIME_TO_FULL	0x18
 34#define REG_MAX_LOAD_CURRENT		0x1e
 35#define REG_CYCLE_COUNT			0x2a
 36#define REG_STATE_OF_CHARGE		0x2c
 37#define REG_DESIGN_CAPACITY		0x3c
 38/* TODO: 0x62-0x68 seem to contain 'MM8013C' in a length-prefixed, non-terminated string */
 39
 40#define DECIKELVIN_TO_DECIDEGC(t)	(t - 2731)
 41
 42struct mm8013_chip {
 43	struct i2c_client *client;
 44	struct regmap *regmap;
 45};
 46
 47static int mm8013_checkdevice(struct mm8013_chip *chip)
 48{
 49	int battery_id, ret;
 50	u32 val;
 51
 52	ret = regmap_write(chip->regmap, REG_BATID, 0x0008);
 53	if (ret < 0)
 54		return ret;
 55
 56	ret = regmap_read(chip->regmap, REG_BATID, &val);
 57	if (ret < 0)
 58		return ret;
 59
 60	if (val == BATID_102)
 61		battery_id = 2;
 62	else if (val == BATID_101)
 63		battery_id = 1;
 64	else
 65		return -EINVAL;
 66
 67	dev_dbg(&chip->client->dev, "battery_id: %d\n", battery_id);
 68
 69	return 0;
 70}
 71
 72static enum power_supply_property mm8013_battery_props[] = {
 73	POWER_SUPPLY_PROP_CAPACITY,
 74	POWER_SUPPLY_PROP_CHARGE_FULL,
 75	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 76	POWER_SUPPLY_PROP_CHARGE_NOW,
 77	POWER_SUPPLY_PROP_CURRENT_MAX,
 78	POWER_SUPPLY_PROP_CURRENT_NOW,
 79	POWER_SUPPLY_PROP_CYCLE_COUNT,
 80	POWER_SUPPLY_PROP_HEALTH,
 81	POWER_SUPPLY_PROP_PRESENT,
 82	POWER_SUPPLY_PROP_STATUS,
 83	POWER_SUPPLY_PROP_TEMP,
 84	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
 85	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
 86	POWER_SUPPLY_PROP_VOLTAGE_NOW,
 87};
 88
 89static int mm8013_get_property(struct power_supply *psy,
 90			       enum power_supply_property psp,
 91			       union power_supply_propval *val)
 92{
 93	struct mm8013_chip *chip = power_supply_get_drvdata(psy);
 94	int ret = 0;
 95	u32 regval;
 96
 97	switch (psp) {
 98	case POWER_SUPPLY_PROP_CAPACITY:
 99		ret = regmap_read(chip->regmap, REG_STATE_OF_CHARGE, &regval);
100		if (ret < 0)
101			return ret;
102
103		val->intval = regval;
104		break;
105	case POWER_SUPPLY_PROP_CHARGE_FULL:
106		ret = regmap_read(chip->regmap, REG_FULL_CHARGE_CAPACITY, &regval);
107		if (ret < 0)
108			return ret;
109
110		val->intval = 1000 * regval;
111		break;
112	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
113		ret = regmap_read(chip->regmap, REG_DESIGN_CAPACITY, &regval);
114		if (ret < 0)
115			return ret;
116
117		val->intval = 1000 * regval;
118		break;
119	case POWER_SUPPLY_PROP_CHARGE_NOW:
120		ret = regmap_read(chip->regmap, REG_NOMINAL_CHARGE_CAPACITY, &regval);
121		if (ret < 0)
122			return ret;
123
124		val->intval = 1000 * regval;
125		break;
126	case POWER_SUPPLY_PROP_CURRENT_MAX:
127		ret = regmap_read(chip->regmap, REG_MAX_LOAD_CURRENT, &regval);
128		if (ret < 0)
129			return ret;
130
131		val->intval = -1000 * (s16)regval;
132		break;
133	case POWER_SUPPLY_PROP_CURRENT_NOW:
134		ret = regmap_read(chip->regmap, REG_AVERAGE_CURRENT, &regval);
135		if (ret < 0)
136			return ret;
137
138		val->intval = -1000 * (s16)regval;
139		break;
140	case POWER_SUPPLY_PROP_CYCLE_COUNT:
141		ret = regmap_read(chip->regmap, REG_CYCLE_COUNT, &regval);
142		if (ret < 0)
143			return ret;
144
145		val->intval = regval;
146		break;
147	case POWER_SUPPLY_PROP_HEALTH:
148		ret = regmap_read(chip->regmap, REG_FLAGS, &regval);
149		if (ret < 0)
150			return ret;
151
152		if (regval & MM8013_FLAG_UT)
153			val->intval = POWER_SUPPLY_HEALTH_COLD;
154		else if (regval & (MM8013_FLAG_ODC | MM8013_FLAG_OCC))
155			val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT;
156		else if (regval & (MM8013_FLAG_BATLOW))
157			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
158		else if (regval & MM8013_FLAG_BATHI)
159			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
160		else if (regval & (MM8013_FLAG_OT | MM8013_FLAG_OTD | MM8013_FLAG_OTC))
161			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
162		else
163			val->intval = POWER_SUPPLY_HEALTH_GOOD;
164		break;
165	case POWER_SUPPLY_PROP_PRESENT:
166		ret = regmap_read(chip->regmap, REG_TEMPERATURE, &regval);
167		if (ret < 0)
168			return ret;
169
170		val->intval = ((s16)regval > 0);
171		break;
172	case POWER_SUPPLY_PROP_STATUS:
173		ret = regmap_read(chip->regmap, REG_FLAGS, &regval);
174		if (ret < 0)
175			return ret;
176
177		if (regval & MM8013_FLAG_DSG)
178			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
179		else if (regval & MM8013_FLAG_CHG_INH)
180			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
181		else if (regval & MM8013_FLAG_CHG)
182			val->intval = POWER_SUPPLY_STATUS_CHARGING;
183		else if (regval & MM8013_FLAG_FC)
184			val->intval = POWER_SUPPLY_STATUS_FULL;
185		else
186			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
187		break;
188	case POWER_SUPPLY_PROP_TEMP:
189		ret = regmap_read(chip->regmap, REG_TEMPERATURE, &regval);
190		if (ret < 0)
191			return ret;
192
193		val->intval = DECIKELVIN_TO_DECIDEGC(regval);
194		break;
195	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
196		ret = regmap_read(chip->regmap, REG_AVERAGE_TIME_TO_EMPTY, &regval);
197		if (ret < 0)
198			return ret;
199
200		/* The estimation is not yet ready */
201		if (regval == U16_MAX)
202			return -ENODATA;
203
204		val->intval = regval;
205		break;
206	case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
207		ret = regmap_read(chip->regmap, REG_AVERAGE_TIME_TO_FULL, &regval);
208		if (ret < 0)
209			return ret;
210
211		/* The estimation is not yet ready */
212		if (regval == U16_MAX)
213			return -ENODATA;
214
215		val->intval = regval;
216		break;
217	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
218		ret = regmap_read(chip->regmap, REG_VOLTAGE, &regval);
219		if (ret < 0)
220			return ret;
221
222		val->intval = 1000 * regval;
223		break;
224	default:
225		return -EINVAL;
226	}
227
228	return 0;
229}
230
231static const struct power_supply_desc mm8013_desc = {
232	.name			= "mm8013",
233	.type			= POWER_SUPPLY_TYPE_BATTERY,
234	.properties		= mm8013_battery_props,
235	.num_properties		= ARRAY_SIZE(mm8013_battery_props),
236	.get_property		= mm8013_get_property,
237};
238
239static const struct regmap_config mm8013_regmap_config = {
240	.reg_bits = 8,
241	.val_bits = 16,
242	.max_register = 0x68,
243	.use_single_read = true,
244	.use_single_write = true,
245	.val_format_endian = REGMAP_ENDIAN_LITTLE,
246};
247
248static int mm8013_probe(struct i2c_client *client)
249{
250	struct power_supply_config psy_cfg = {};
251	struct device *dev = &client->dev;
252	struct power_supply *psy;
253	struct mm8013_chip *chip;
254	int ret = 0;
255
256	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
257		return dev_err_probe(dev, -EIO,
258				     "I2C_FUNC_SMBUS_WORD_DATA not supported\n");
259
260	chip = devm_kzalloc(dev, sizeof(struct mm8013_chip), GFP_KERNEL);
261	if (!chip)
262		return -ENOMEM;
263
264	chip->client = client;
265
266	chip->regmap = devm_regmap_init_i2c(client, &mm8013_regmap_config);
267	if (IS_ERR(chip->regmap)) {
268		ret = PTR_ERR(chip->regmap);
269		return dev_err_probe(dev, ret, "Couldn't initialize regmap\n");
270	}
271
272	ret = mm8013_checkdevice(chip);
273	if (ret)
274		return dev_err_probe(dev, ret, "MM8013 not found\n");
275
276	psy_cfg.drv_data = chip;
277	psy_cfg.fwnode = dev_fwnode(dev);
278
279	psy = devm_power_supply_register(dev, &mm8013_desc, &psy_cfg);
280	if (IS_ERR(psy))
281		return PTR_ERR(psy);
282
283	return 0;
284}
285
286static const struct i2c_device_id mm8013_id_table[] = {
287	{ "mm8013" },
288	{}
289};
290MODULE_DEVICE_TABLE(i2c, mm8013_id_table);
291
292static const struct of_device_id mm8013_match_table[] = {
293	{ .compatible = "mitsumi,mm8013" },
294	{}
295};
296
297static struct i2c_driver mm8013_i2c_driver = {
298	.probe = mm8013_probe,
299	.id_table = mm8013_id_table,
300	.driver = {
301		.name = "mm8013",
302		.of_match_table = mm8013_match_table,
303	},
304};
305module_i2c_driver(mm8013_i2c_driver);
306
307MODULE_DESCRIPTION("MM8013 fuel gauge driver");
308MODULE_LICENSE("GPL");
~/ Read-only file preview.