Skip to content

Commit c6f4a42

Browse files
MinkyuKangenomsg
authored andcommitted
Add MAX17040 Fuel Gauge driver
The MAX17040 is a I2C interfaced Fuel Gauge systems for lithium-ion batteries This patch adds support the MAX17040 Fuel Gauge Signed-off-by: Minkyu Kang <mk7.kang@samsung.com> Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com>
1 parent cef437e commit c6f4a42

File tree

4 files changed

+338
-1
lines changed

4 files changed

+338
-1
lines changed

drivers/power/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ config BATTERY_DA9030
8282
Say Y here to enable support for batteries charger integrated into
8383
DA9030 PMIC.
8484

85+
config BATTERY_MAX17040
86+
tristate "Maxim MAX17040 Fuel Gauge"
87+
depends on I2C
88+
help
89+
MAX17040 is fuel-gauge systems for lithium-ion (Li+) batteries
90+
in handheld and portable equipment. The MAX17040 is configured
91+
to operate with a single lithium cell
92+
8593
config CHARGER_PCF50633
8694
tristate "NXP PCF50633 MBC"
8795
depends on MFD_PCF50633

drivers/power/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
2525
obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
2626
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
2727
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
28-
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
28+
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
29+
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o

drivers/power/max17040_battery.c

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
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+
23+
#define MAX17040_VCELL_MSB 0x02
24+
#define MAX17040_VCELL_LSB 0x03
25+
#define MAX17040_SOC_MSB 0x04
26+
#define MAX17040_SOC_LSB 0x05
27+
#define MAX17040_MODE_MSB 0x06
28+
#define MAX17040_MODE_LSB 0x07
29+
#define MAX17040_VER_MSB 0x08
30+
#define MAX17040_VER_LSB 0x09
31+
#define MAX17040_RCOMP_MSB 0x0C
32+
#define MAX17040_RCOMP_LSB 0x0D
33+
#define MAX17040_CMD_MSB 0xFE
34+
#define MAX17040_CMD_LSB 0xFF
35+
36+
#define MAX17040_DELAY 1000
37+
#define MAX17040_BATTERY_FULL 95
38+
39+
struct max17040_chip {
40+
struct i2c_client *client;
41+
struct delayed_work work;
42+
struct power_supply battery;
43+
struct max17040_platform_data *pdata;
44+
45+
/* State Of Connect */
46+
int online;
47+
/* battery voltage */
48+
int vcell;
49+
/* battery capacity */
50+
int soc;
51+
/* State Of Charge */
52+
int status;
53+
};
54+
55+
static int max17040_get_property(struct power_supply *psy,
56+
enum power_supply_property psp,
57+
union power_supply_propval *val)
58+
{
59+
struct max17040_chip *chip = container_of(psy,
60+
struct max17040_chip, battery);
61+
62+
switch (psp) {
63+
case POWER_SUPPLY_PROP_STATUS:
64+
val->intval = chip->status;
65+
break;
66+
case POWER_SUPPLY_PROP_ONLINE:
67+
val->intval = chip->online;
68+
break;
69+
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
70+
val->intval = chip->vcell;
71+
break;
72+
case POWER_SUPPLY_PROP_CAPACITY:
73+
val->intval = chip->soc;
74+
break;
75+
default:
76+
return -EINVAL;
77+
}
78+
return 0;
79+
}
80+
81+
static int max17040_write_reg(struct i2c_client *client, int reg, u8 value)
82+
{
83+
int ret;
84+
85+
ret = i2c_smbus_write_byte_data(client, reg, value);
86+
87+
if (ret < 0)
88+
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
89+
90+
return ret;
91+
}
92+
93+
static int max17040_read_reg(struct i2c_client *client, int reg)
94+
{
95+
int ret;
96+
97+
ret = i2c_smbus_read_byte_data(client, reg);
98+
99+
if (ret < 0)
100+
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
101+
102+
return ret;
103+
}
104+
105+
static void max17040_reset(struct i2c_client *client)
106+
{
107+
max17040_write_reg(client, MAX17040_CMD_MSB, 0x54);
108+
max17040_write_reg(client, MAX17040_CMD_LSB, 0x00);
109+
}
110+
111+
static void max17040_get_vcell(struct i2c_client *client)
112+
{
113+
struct max17040_chip *chip = i2c_get_clientdata(client);
114+
u8 msb;
115+
u8 lsb;
116+
117+
msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
118+
lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);
119+
120+
chip->vcell = (msb << 4) + (lsb >> 4);
121+
}
122+
123+
static void max17040_get_soc(struct i2c_client *client)
124+
{
125+
struct max17040_chip *chip = i2c_get_clientdata(client);
126+
u8 msb;
127+
u8 lsb;
128+
129+
msb = max17040_read_reg(client, MAX17040_SOC_MSB);
130+
lsb = max17040_read_reg(client, MAX17040_SOC_LSB);
131+
132+
chip->soc = msb;
133+
}
134+
135+
static void max17040_get_version(struct i2c_client *client)
136+
{
137+
u8 msb;
138+
u8 lsb;
139+
140+
msb = max17040_read_reg(client, MAX17040_VER_MSB);
141+
lsb = max17040_read_reg(client, MAX17040_VER_LSB);
142+
143+
dev_info(&client->dev, "MAX17040 Fuel-Gauge Ver %d%d\n", msb, lsb);
144+
}
145+
146+
static void max17040_get_online(struct i2c_client *client)
147+
{
148+
struct max17040_chip *chip = i2c_get_clientdata(client);
149+
150+
if (chip->pdata->battery_online)
151+
chip->online = chip->pdata->battery_online();
152+
else
153+
chip->online = 1;
154+
}
155+
156+
static void max17040_get_status(struct i2c_client *client)
157+
{
158+
struct max17040_chip *chip = i2c_get_clientdata(client);
159+
160+
if (!chip->pdata->charger_online || !chip->pdata->charger_enable) {
161+
chip->status = POWER_SUPPLY_STATUS_UNKNOWN;
162+
return;
163+
}
164+
165+
if (chip->pdata->charger_online()) {
166+
if (chip->pdata->charger_enable())
167+
chip->status = POWER_SUPPLY_STATUS_CHARGING;
168+
else
169+
chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
170+
} else {
171+
chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
172+
}
173+
174+
if (chip->soc > MAX17040_BATTERY_FULL)
175+
chip->status = POWER_SUPPLY_STATUS_FULL;
176+
}
177+
178+
static void max17040_work(struct work_struct *work)
179+
{
180+
struct max17040_chip *chip;
181+
182+
chip = container_of(work, struct max17040_chip, work.work);
183+
184+
max17040_get_vcell(chip->client);
185+
max17040_get_soc(chip->client);
186+
max17040_get_online(chip->client);
187+
max17040_get_status(chip->client);
188+
189+
schedule_delayed_work(&chip->work, MAX17040_DELAY);
190+
}
191+
192+
static enum power_supply_property max17040_battery_props[] = {
193+
POWER_SUPPLY_PROP_STATUS,
194+
POWER_SUPPLY_PROP_ONLINE,
195+
POWER_SUPPLY_PROP_VOLTAGE_NOW,
196+
POWER_SUPPLY_PROP_CAPACITY,
197+
};
198+
199+
static int __devinit max17040_probe(struct i2c_client *client,
200+
const struct i2c_device_id *id)
201+
{
202+
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
203+
struct max17040_chip *chip;
204+
int ret;
205+
206+
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
207+
return -EIO;
208+
209+
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
210+
if (!chip)
211+
return -ENOMEM;
212+
213+
chip->client = client;
214+
chip->pdata = client->dev.platform_data;
215+
216+
i2c_set_clientdata(client, chip);
217+
218+
chip->battery.name = "battery";
219+
chip->battery.type = POWER_SUPPLY_TYPE_BATTERY;
220+
chip->battery.get_property = max17040_get_property;
221+
chip->battery.properties = max17040_battery_props;
222+
chip->battery.num_properties = ARRAY_SIZE(max17040_battery_props);
223+
224+
ret = power_supply_register(&client->dev, &chip->battery);
225+
if (ret) {
226+
dev_err(&client->dev, "failed: power supply register\n");
227+
i2c_set_clientdata(client, NULL);
228+
kfree(chip);
229+
return ret;
230+
}
231+
232+
max17040_reset(client);
233+
max17040_get_version(client);
234+
235+
INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);
236+
schedule_delayed_work(&chip->work, MAX17040_DELAY);
237+
238+
return 0;
239+
}
240+
241+
static int __devexit 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+
i2c_set_clientdata(client, NULL);
248+
kfree(chip);
249+
return 0;
250+
}
251+
252+
#ifdef CONFIG_PM
253+
254+
static int max17040_suspend(struct i2c_client *client,
255+
pm_message_t state)
256+
{
257+
struct max17040_chip *chip = i2c_get_clientdata(client);
258+
259+
cancel_delayed_work(&chip->work);
260+
return 0;
261+
}
262+
263+
static int max17040_resume(struct i2c_client *client)
264+
{
265+
struct max17040_chip *chip = i2c_get_clientdata(client);
266+
267+
schedule_delayed_work(&chip->work, MAX17040_DELAY);
268+
return 0;
269+
}
270+
271+
#else
272+
273+
#define max17040_suspend NULL
274+
#define max17040_resume NULL
275+
276+
#endif /* CONFIG_PM */
277+
278+
static const struct i2c_device_id max17040_id[] = {
279+
{ "max17040", 0 },
280+
{ }
281+
};
282+
MODULE_DEVICE_TABLE(i2c, max17040_id);
283+
284+
static struct i2c_driver max17040_i2c_driver = {
285+
.driver = {
286+
.name = "max17040",
287+
},
288+
.probe = max17040_probe,
289+
.remove = __devexit_p(max17040_remove),
290+
.suspend = max17040_suspend,
291+
.resume = max17040_resume,
292+
.id_table = max17040_id,
293+
};
294+
295+
static int __init max17040_init(void)
296+
{
297+
return i2c_add_driver(&max17040_i2c_driver);
298+
}
299+
module_init(max17040_init);
300+
301+
static void __exit max17040_exit(void)
302+
{
303+
i2c_del_driver(&max17040_i2c_driver);
304+
}
305+
module_exit(max17040_exit);
306+
307+
MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
308+
MODULE_DESCRIPTION("MAX17040 Fuel Gauge");
309+
MODULE_LICENSE("GPL");

include/linux/max17040_battery.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright (C) 2009 Samsung Electronics
3+
* Minkyu Kang <mk7.kang@samsung.com>
4+
*
5+
* This program is free software; you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License version 2 as
7+
* published by the Free Software Foundation.
8+
*/
9+
10+
#ifndef __MAX17040_BATTERY_H_
11+
#define __MAX17040_BATTERY_H_
12+
13+
struct max17040_platform_data {
14+
int (*battery_online)(void);
15+
int (*charger_online)(void);
16+
int (*charger_enable)(void);
17+
};
18+
19+
#endif

0 commit comments

Comments
 (0)