Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  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", 0 },
281	{ }
282};
283MODULE_DEVICE_TABLE(i2c, max17040_id);
284
285static struct i2c_driver max17040_i2c_driver = {
286	.driver	= {
287		.name	= "max17040",
288		.pm	= MAX17040_PM_OPS,
289	},
290	.probe		= max17040_probe,
291	.remove		= max17040_remove,
292	.id_table	= max17040_id,
293};
294module_i2c_driver(max17040_i2c_driver);
295
296MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
297MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
298MODULE_LICENSE("GPL");