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