xref: /openbmc/linux/drivers/power/supply/max17040_battery.c (revision 8c0984e5a75337df513047ec92a6c09d78e3e5cd)
1*8c0984e5SSebastian Reichel /*
2*8c0984e5SSebastian Reichel  *  max17040_battery.c
3*8c0984e5SSebastian Reichel  *  fuel-gauge systems for lithium-ion (Li+) batteries
4*8c0984e5SSebastian Reichel  *
5*8c0984e5SSebastian Reichel  *  Copyright (C) 2009 Samsung Electronics
6*8c0984e5SSebastian Reichel  *  Minkyu Kang <mk7.kang@samsung.com>
7*8c0984e5SSebastian Reichel  *
8*8c0984e5SSebastian Reichel  * This program is free software; you can redistribute it and/or modify
9*8c0984e5SSebastian Reichel  * it under the terms of the GNU General Public License version 2 as
10*8c0984e5SSebastian Reichel  * published by the Free Software Foundation.
11*8c0984e5SSebastian Reichel  */
12*8c0984e5SSebastian Reichel 
13*8c0984e5SSebastian Reichel #include <linux/module.h>
14*8c0984e5SSebastian Reichel #include <linux/init.h>
15*8c0984e5SSebastian Reichel #include <linux/platform_device.h>
16*8c0984e5SSebastian Reichel #include <linux/mutex.h>
17*8c0984e5SSebastian Reichel #include <linux/err.h>
18*8c0984e5SSebastian Reichel #include <linux/i2c.h>
19*8c0984e5SSebastian Reichel #include <linux/delay.h>
20*8c0984e5SSebastian Reichel #include <linux/power_supply.h>
21*8c0984e5SSebastian Reichel #include <linux/max17040_battery.h>
22*8c0984e5SSebastian Reichel #include <linux/slab.h>
23*8c0984e5SSebastian Reichel 
24*8c0984e5SSebastian Reichel #define MAX17040_VCELL_MSB	0x02
25*8c0984e5SSebastian Reichel #define MAX17040_VCELL_LSB	0x03
26*8c0984e5SSebastian Reichel #define MAX17040_SOC_MSB	0x04
27*8c0984e5SSebastian Reichel #define MAX17040_SOC_LSB	0x05
28*8c0984e5SSebastian Reichel #define MAX17040_MODE_MSB	0x06
29*8c0984e5SSebastian Reichel #define MAX17040_MODE_LSB	0x07
30*8c0984e5SSebastian Reichel #define MAX17040_VER_MSB	0x08
31*8c0984e5SSebastian Reichel #define MAX17040_VER_LSB	0x09
32*8c0984e5SSebastian Reichel #define MAX17040_RCOMP_MSB	0x0C
33*8c0984e5SSebastian Reichel #define MAX17040_RCOMP_LSB	0x0D
34*8c0984e5SSebastian Reichel #define MAX17040_CMD_MSB	0xFE
35*8c0984e5SSebastian Reichel #define MAX17040_CMD_LSB	0xFF
36*8c0984e5SSebastian Reichel 
37*8c0984e5SSebastian Reichel #define MAX17040_DELAY		1000
38*8c0984e5SSebastian Reichel #define MAX17040_BATTERY_FULL	95
39*8c0984e5SSebastian Reichel 
40*8c0984e5SSebastian Reichel struct max17040_chip {
41*8c0984e5SSebastian Reichel 	struct i2c_client		*client;
42*8c0984e5SSebastian Reichel 	struct delayed_work		work;
43*8c0984e5SSebastian Reichel 	struct power_supply		*battery;
44*8c0984e5SSebastian Reichel 	struct max17040_platform_data	*pdata;
45*8c0984e5SSebastian Reichel 
46*8c0984e5SSebastian Reichel 	/* State Of Connect */
47*8c0984e5SSebastian Reichel 	int online;
48*8c0984e5SSebastian Reichel 	/* battery voltage */
49*8c0984e5SSebastian Reichel 	int vcell;
50*8c0984e5SSebastian Reichel 	/* battery capacity */
51*8c0984e5SSebastian Reichel 	int soc;
52*8c0984e5SSebastian Reichel 	/* State Of Charge */
53*8c0984e5SSebastian Reichel 	int status;
54*8c0984e5SSebastian Reichel };
55*8c0984e5SSebastian Reichel 
56*8c0984e5SSebastian Reichel static int max17040_get_property(struct power_supply *psy,
57*8c0984e5SSebastian Reichel 			    enum power_supply_property psp,
58*8c0984e5SSebastian Reichel 			    union power_supply_propval *val)
59*8c0984e5SSebastian Reichel {
60*8c0984e5SSebastian Reichel 	struct max17040_chip *chip = power_supply_get_drvdata(psy);
61*8c0984e5SSebastian Reichel 
62*8c0984e5SSebastian Reichel 	switch (psp) {
63*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
64*8c0984e5SSebastian Reichel 		val->intval = chip->status;
65*8c0984e5SSebastian Reichel 		break;
66*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
67*8c0984e5SSebastian Reichel 		val->intval = chip->online;
68*8c0984e5SSebastian Reichel 		break;
69*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
70*8c0984e5SSebastian Reichel 		val->intval = chip->vcell;
71*8c0984e5SSebastian Reichel 		break;
72*8c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_CAPACITY:
73*8c0984e5SSebastian Reichel 		val->intval = chip->soc;
74*8c0984e5SSebastian Reichel 		break;
75*8c0984e5SSebastian Reichel 	default:
76*8c0984e5SSebastian Reichel 		return -EINVAL;
77*8c0984e5SSebastian Reichel 	}
78*8c0984e5SSebastian Reichel 	return 0;
79*8c0984e5SSebastian Reichel }
80*8c0984e5SSebastian Reichel 
81*8c0984e5SSebastian Reichel static int max17040_write_reg(struct i2c_client *client, int reg, u8 value)
82*8c0984e5SSebastian Reichel {
83*8c0984e5SSebastian Reichel 	int ret;
84*8c0984e5SSebastian Reichel 
85*8c0984e5SSebastian Reichel 	ret = i2c_smbus_write_byte_data(client, reg, value);
86*8c0984e5SSebastian Reichel 
87*8c0984e5SSebastian Reichel 	if (ret < 0)
88*8c0984e5SSebastian Reichel 		dev_err(&client->dev, "%s: err %d\n", __func__, ret);
89*8c0984e5SSebastian Reichel 
90*8c0984e5SSebastian Reichel 	return ret;
91*8c0984e5SSebastian Reichel }
92*8c0984e5SSebastian Reichel 
93*8c0984e5SSebastian Reichel static int max17040_read_reg(struct i2c_client *client, int reg)
94*8c0984e5SSebastian Reichel {
95*8c0984e5SSebastian Reichel 	int ret;
96*8c0984e5SSebastian Reichel 
97*8c0984e5SSebastian Reichel 	ret = i2c_smbus_read_byte_data(client, reg);
98*8c0984e5SSebastian Reichel 
99*8c0984e5SSebastian Reichel 	if (ret < 0)
100*8c0984e5SSebastian Reichel 		dev_err(&client->dev, "%s: err %d\n", __func__, ret);
101*8c0984e5SSebastian Reichel 
102*8c0984e5SSebastian Reichel 	return ret;
103*8c0984e5SSebastian Reichel }
104*8c0984e5SSebastian Reichel 
105*8c0984e5SSebastian Reichel static void max17040_reset(struct i2c_client *client)
106*8c0984e5SSebastian Reichel {
107*8c0984e5SSebastian Reichel 	max17040_write_reg(client, MAX17040_CMD_MSB, 0x54);
108*8c0984e5SSebastian Reichel 	max17040_write_reg(client, MAX17040_CMD_LSB, 0x00);
109*8c0984e5SSebastian Reichel }
110*8c0984e5SSebastian Reichel 
111*8c0984e5SSebastian Reichel static void max17040_get_vcell(struct i2c_client *client)
112*8c0984e5SSebastian Reichel {
113*8c0984e5SSebastian Reichel 	struct max17040_chip *chip = i2c_get_clientdata(client);
114*8c0984e5SSebastian Reichel 	u8 msb;
115*8c0984e5SSebastian Reichel 	u8 lsb;
116*8c0984e5SSebastian Reichel 
117*8c0984e5SSebastian Reichel 	msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
118*8c0984e5SSebastian Reichel 	lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);
119*8c0984e5SSebastian Reichel 
120*8c0984e5SSebastian Reichel 	chip->vcell = (msb << 4) + (lsb >> 4);
121*8c0984e5SSebastian Reichel }
122*8c0984e5SSebastian Reichel 
123*8c0984e5SSebastian Reichel static void max17040_get_soc(struct i2c_client *client)
124*8c0984e5SSebastian Reichel {
125*8c0984e5SSebastian Reichel 	struct max17040_chip *chip = i2c_get_clientdata(client);
126*8c0984e5SSebastian Reichel 	u8 msb;
127*8c0984e5SSebastian Reichel 	u8 lsb;
128*8c0984e5SSebastian Reichel 
129*8c0984e5SSebastian Reichel 	msb = max17040_read_reg(client, MAX17040_SOC_MSB);
130*8c0984e5SSebastian Reichel 	lsb = max17040_read_reg(client, MAX17040_SOC_LSB);
131*8c0984e5SSebastian Reichel 
132*8c0984e5SSebastian Reichel 	chip->soc = msb;
133*8c0984e5SSebastian Reichel }
134*8c0984e5SSebastian Reichel 
135*8c0984e5SSebastian Reichel static void max17040_get_version(struct i2c_client *client)
136*8c0984e5SSebastian Reichel {
137*8c0984e5SSebastian Reichel 	u8 msb;
138*8c0984e5SSebastian Reichel 	u8 lsb;
139*8c0984e5SSebastian Reichel 
140*8c0984e5SSebastian Reichel 	msb = max17040_read_reg(client, MAX17040_VER_MSB);
141*8c0984e5SSebastian Reichel 	lsb = max17040_read_reg(client, MAX17040_VER_LSB);
142*8c0984e5SSebastian Reichel 
143*8c0984e5SSebastian Reichel 	dev_info(&client->dev, "MAX17040 Fuel-Gauge Ver %d%d\n", msb, lsb);
144*8c0984e5SSebastian Reichel }
145*8c0984e5SSebastian Reichel 
146*8c0984e5SSebastian Reichel static void max17040_get_online(struct i2c_client *client)
147*8c0984e5SSebastian Reichel {
148*8c0984e5SSebastian Reichel 	struct max17040_chip *chip = i2c_get_clientdata(client);
149*8c0984e5SSebastian Reichel 
150*8c0984e5SSebastian Reichel 	if (chip->pdata && chip->pdata->battery_online)
151*8c0984e5SSebastian Reichel 		chip->online = chip->pdata->battery_online();
152*8c0984e5SSebastian Reichel 	else
153*8c0984e5SSebastian Reichel 		chip->online = 1;
154*8c0984e5SSebastian Reichel }
155*8c0984e5SSebastian Reichel 
156*8c0984e5SSebastian Reichel static void max17040_get_status(struct i2c_client *client)
157*8c0984e5SSebastian Reichel {
158*8c0984e5SSebastian Reichel 	struct max17040_chip *chip = i2c_get_clientdata(client);
159*8c0984e5SSebastian Reichel 
160*8c0984e5SSebastian Reichel 	if (!chip->pdata || !chip->pdata->charger_online
161*8c0984e5SSebastian Reichel 			|| !chip->pdata->charger_enable) {
162*8c0984e5SSebastian Reichel 		chip->status = POWER_SUPPLY_STATUS_UNKNOWN;
163*8c0984e5SSebastian Reichel 		return;
164*8c0984e5SSebastian Reichel 	}
165*8c0984e5SSebastian Reichel 
166*8c0984e5SSebastian Reichel 	if (chip->pdata->charger_online()) {
167*8c0984e5SSebastian Reichel 		if (chip->pdata->charger_enable())
168*8c0984e5SSebastian Reichel 			chip->status = POWER_SUPPLY_STATUS_CHARGING;
169*8c0984e5SSebastian Reichel 		else
170*8c0984e5SSebastian Reichel 			chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
171*8c0984e5SSebastian Reichel 	} else {
172*8c0984e5SSebastian Reichel 		chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
173*8c0984e5SSebastian Reichel 	}
174*8c0984e5SSebastian Reichel 
175*8c0984e5SSebastian Reichel 	if (chip->soc > MAX17040_BATTERY_FULL)
176*8c0984e5SSebastian Reichel 		chip->status = POWER_SUPPLY_STATUS_FULL;
177*8c0984e5SSebastian Reichel }
178*8c0984e5SSebastian Reichel 
179*8c0984e5SSebastian Reichel static void max17040_work(struct work_struct *work)
180*8c0984e5SSebastian Reichel {
181*8c0984e5SSebastian Reichel 	struct max17040_chip *chip;
182*8c0984e5SSebastian Reichel 
183*8c0984e5SSebastian Reichel 	chip = container_of(work, struct max17040_chip, work.work);
184*8c0984e5SSebastian Reichel 
185*8c0984e5SSebastian Reichel 	max17040_get_vcell(chip->client);
186*8c0984e5SSebastian Reichel 	max17040_get_soc(chip->client);
187*8c0984e5SSebastian Reichel 	max17040_get_online(chip->client);
188*8c0984e5SSebastian Reichel 	max17040_get_status(chip->client);
189*8c0984e5SSebastian Reichel 
190*8c0984e5SSebastian Reichel 	queue_delayed_work(system_power_efficient_wq, &chip->work,
191*8c0984e5SSebastian Reichel 			   MAX17040_DELAY);
192*8c0984e5SSebastian Reichel }
193*8c0984e5SSebastian Reichel 
194*8c0984e5SSebastian Reichel static enum power_supply_property max17040_battery_props[] = {
195*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS,
196*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ONLINE,
197*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
198*8c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_CAPACITY,
199*8c0984e5SSebastian Reichel };
200*8c0984e5SSebastian Reichel 
201*8c0984e5SSebastian Reichel static const struct power_supply_desc max17040_battery_desc = {
202*8c0984e5SSebastian Reichel 	.name		= "battery",
203*8c0984e5SSebastian Reichel 	.type		= POWER_SUPPLY_TYPE_BATTERY,
204*8c0984e5SSebastian Reichel 	.get_property	= max17040_get_property,
205*8c0984e5SSebastian Reichel 	.properties	= max17040_battery_props,
206*8c0984e5SSebastian Reichel 	.num_properties	= ARRAY_SIZE(max17040_battery_props),
207*8c0984e5SSebastian Reichel };
208*8c0984e5SSebastian Reichel 
209*8c0984e5SSebastian Reichel static int max17040_probe(struct i2c_client *client,
210*8c0984e5SSebastian Reichel 			const struct i2c_device_id *id)
211*8c0984e5SSebastian Reichel {
212*8c0984e5SSebastian Reichel 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
213*8c0984e5SSebastian Reichel 	struct power_supply_config psy_cfg = {};
214*8c0984e5SSebastian Reichel 	struct max17040_chip *chip;
215*8c0984e5SSebastian Reichel 
216*8c0984e5SSebastian Reichel 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
217*8c0984e5SSebastian Reichel 		return -EIO;
218*8c0984e5SSebastian Reichel 
219*8c0984e5SSebastian Reichel 	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
220*8c0984e5SSebastian Reichel 	if (!chip)
221*8c0984e5SSebastian Reichel 		return -ENOMEM;
222*8c0984e5SSebastian Reichel 
223*8c0984e5SSebastian Reichel 	chip->client = client;
224*8c0984e5SSebastian Reichel 	chip->pdata = client->dev.platform_data;
225*8c0984e5SSebastian Reichel 
226*8c0984e5SSebastian Reichel 	i2c_set_clientdata(client, chip);
227*8c0984e5SSebastian Reichel 	psy_cfg.drv_data = chip;
228*8c0984e5SSebastian Reichel 
229*8c0984e5SSebastian Reichel 	chip->battery = power_supply_register(&client->dev,
230*8c0984e5SSebastian Reichel 				&max17040_battery_desc, &psy_cfg);
231*8c0984e5SSebastian Reichel 	if (IS_ERR(chip->battery)) {
232*8c0984e5SSebastian Reichel 		dev_err(&client->dev, "failed: power supply register\n");
233*8c0984e5SSebastian Reichel 		return PTR_ERR(chip->battery);
234*8c0984e5SSebastian Reichel 	}
235*8c0984e5SSebastian Reichel 
236*8c0984e5SSebastian Reichel 	max17040_reset(client);
237*8c0984e5SSebastian Reichel 	max17040_get_version(client);
238*8c0984e5SSebastian Reichel 
239*8c0984e5SSebastian Reichel 	INIT_DEFERRABLE_WORK(&chip->work, max17040_work);
240*8c0984e5SSebastian Reichel 	queue_delayed_work(system_power_efficient_wq, &chip->work,
241*8c0984e5SSebastian Reichel 			   MAX17040_DELAY);
242*8c0984e5SSebastian Reichel 
243*8c0984e5SSebastian Reichel 	return 0;
244*8c0984e5SSebastian Reichel }
245*8c0984e5SSebastian Reichel 
246*8c0984e5SSebastian Reichel static int max17040_remove(struct i2c_client *client)
247*8c0984e5SSebastian Reichel {
248*8c0984e5SSebastian Reichel 	struct max17040_chip *chip = i2c_get_clientdata(client);
249*8c0984e5SSebastian Reichel 
250*8c0984e5SSebastian Reichel 	power_supply_unregister(chip->battery);
251*8c0984e5SSebastian Reichel 	cancel_delayed_work(&chip->work);
252*8c0984e5SSebastian Reichel 	return 0;
253*8c0984e5SSebastian Reichel }
254*8c0984e5SSebastian Reichel 
255*8c0984e5SSebastian Reichel #ifdef CONFIG_PM_SLEEP
256*8c0984e5SSebastian Reichel 
257*8c0984e5SSebastian Reichel static int max17040_suspend(struct device *dev)
258*8c0984e5SSebastian Reichel {
259*8c0984e5SSebastian Reichel 	struct i2c_client *client = to_i2c_client(dev);
260*8c0984e5SSebastian Reichel 	struct max17040_chip *chip = i2c_get_clientdata(client);
261*8c0984e5SSebastian Reichel 
262*8c0984e5SSebastian Reichel 	cancel_delayed_work(&chip->work);
263*8c0984e5SSebastian Reichel 	return 0;
264*8c0984e5SSebastian Reichel }
265*8c0984e5SSebastian Reichel 
266*8c0984e5SSebastian Reichel static int max17040_resume(struct device *dev)
267*8c0984e5SSebastian Reichel {
268*8c0984e5SSebastian Reichel 	struct i2c_client *client = to_i2c_client(dev);
269*8c0984e5SSebastian Reichel 	struct max17040_chip *chip = i2c_get_clientdata(client);
270*8c0984e5SSebastian Reichel 
271*8c0984e5SSebastian Reichel 	queue_delayed_work(system_power_efficient_wq, &chip->work,
272*8c0984e5SSebastian Reichel 			   MAX17040_DELAY);
273*8c0984e5SSebastian Reichel 	return 0;
274*8c0984e5SSebastian Reichel }
275*8c0984e5SSebastian Reichel 
276*8c0984e5SSebastian Reichel static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume);
277*8c0984e5SSebastian Reichel #define MAX17040_PM_OPS (&max17040_pm_ops)
278*8c0984e5SSebastian Reichel 
279*8c0984e5SSebastian Reichel #else
280*8c0984e5SSebastian Reichel 
281*8c0984e5SSebastian Reichel #define MAX17040_PM_OPS NULL
282*8c0984e5SSebastian Reichel 
283*8c0984e5SSebastian Reichel #endif /* CONFIG_PM_SLEEP */
284*8c0984e5SSebastian Reichel 
285*8c0984e5SSebastian Reichel static const struct i2c_device_id max17040_id[] = {
286*8c0984e5SSebastian Reichel 	{ "max17040" },
287*8c0984e5SSebastian Reichel 	{ "max77836-battery" },
288*8c0984e5SSebastian Reichel 	{ }
289*8c0984e5SSebastian Reichel };
290*8c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(i2c, max17040_id);
291*8c0984e5SSebastian Reichel 
292*8c0984e5SSebastian Reichel static struct i2c_driver max17040_i2c_driver = {
293*8c0984e5SSebastian Reichel 	.driver	= {
294*8c0984e5SSebastian Reichel 		.name	= "max17040",
295*8c0984e5SSebastian Reichel 		.pm	= MAX17040_PM_OPS,
296*8c0984e5SSebastian Reichel 	},
297*8c0984e5SSebastian Reichel 	.probe		= max17040_probe,
298*8c0984e5SSebastian Reichel 	.remove		= max17040_remove,
299*8c0984e5SSebastian Reichel 	.id_table	= max17040_id,
300*8c0984e5SSebastian Reichel };
301*8c0984e5SSebastian Reichel module_i2c_driver(max17040_i2c_driver);
302*8c0984e5SSebastian Reichel 
303*8c0984e5SSebastian Reichel MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
304*8c0984e5SSebastian Reichel MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
305*8c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
306