Root/
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 | |
40 | struct 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 | |
56 | static 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 | |
82 | static 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 | |
94 | static 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 | |
106 | static 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 | |
112 | static 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 | |
124 | static 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 | |
136 | static 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 | |
147 | static void max17040_get_online(struct i2c_client *client) |
148 | { |
149 | struct max17040_chip *chip = i2c_get_clientdata(client); |
150 | |
151 | if (chip->pdata->battery_online) |
152 | chip->online = chip->pdata->battery_online(); |
153 | else |
154 | chip->online = 1; |
155 | } |
156 | |
157 | static void max17040_get_status(struct i2c_client *client) |
158 | { |
159 | struct max17040_chip *chip = i2c_get_clientdata(client); |
160 | |
161 | if (!chip->pdata->charger_online || !chip->pdata->charger_enable) { |
162 | chip->status = POWER_SUPPLY_STATUS_UNKNOWN; |
163 | return; |
164 | } |
165 | |
166 | if (chip->pdata->charger_online()) { |
167 | if (chip->pdata->charger_enable()) |
168 | chip->status = POWER_SUPPLY_STATUS_CHARGING; |
169 | else |
170 | chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
171 | } else { |
172 | chip->status = POWER_SUPPLY_STATUS_DISCHARGING; |
173 | } |
174 | |
175 | if (chip->soc > MAX17040_BATTERY_FULL) |
176 | chip->status = POWER_SUPPLY_STATUS_FULL; |
177 | } |
178 | |
179 | static void max17040_work(struct work_struct *work) |
180 | { |
181 | struct max17040_chip *chip; |
182 | |
183 | chip = container_of(work, struct max17040_chip, work.work); |
184 | |
185 | max17040_get_vcell(chip->client); |
186 | max17040_get_soc(chip->client); |
187 | max17040_get_online(chip->client); |
188 | max17040_get_status(chip->client); |
189 | |
190 | schedule_delayed_work(&chip->work, MAX17040_DELAY); |
191 | } |
192 | |
193 | static enum power_supply_property max17040_battery_props[] = { |
194 | POWER_SUPPLY_PROP_STATUS, |
195 | POWER_SUPPLY_PROP_ONLINE, |
196 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
197 | POWER_SUPPLY_PROP_CAPACITY, |
198 | }; |
199 | |
200 | static int __devinit max17040_probe(struct i2c_client *client, |
201 | const struct i2c_device_id *id) |
202 | { |
203 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); |
204 | struct max17040_chip *chip; |
205 | int ret; |
206 | |
207 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) |
208 | return -EIO; |
209 | |
210 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); |
211 | if (!chip) |
212 | return -ENOMEM; |
213 | |
214 | chip->client = client; |
215 | chip->pdata = client->dev.platform_data; |
216 | |
217 | i2c_set_clientdata(client, chip); |
218 | |
219 | chip->battery.name = "battery"; |
220 | chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; |
221 | chip->battery.get_property = max17040_get_property; |
222 | chip->battery.properties = max17040_battery_props; |
223 | chip->battery.num_properties = ARRAY_SIZE(max17040_battery_props); |
224 | |
225 | ret = power_supply_register(&client->dev, &chip->battery); |
226 | if (ret) { |
227 | dev_err(&client->dev, "failed: power supply register\n"); |
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 | kfree(chip); |
248 | return 0; |
249 | } |
250 | |
251 | #ifdef CONFIG_PM |
252 | |
253 | static int max17040_suspend(struct i2c_client *client, |
254 | pm_message_t state) |
255 | { |
256 | struct max17040_chip *chip = i2c_get_clientdata(client); |
257 | |
258 | cancel_delayed_work(&chip->work); |
259 | return 0; |
260 | } |
261 | |
262 | static int max17040_resume(struct i2c_client *client) |
263 | { |
264 | struct max17040_chip *chip = i2c_get_clientdata(client); |
265 | |
266 | schedule_delayed_work(&chip->work, MAX17040_DELAY); |
267 | return 0; |
268 | } |
269 | |
270 | #else |
271 | |
272 | #define max17040_suspend NULL |
273 | #define max17040_resume NULL |
274 | |
275 | #endif /* CONFIG_PM */ |
276 | |
277 | static const struct i2c_device_id max17040_id[] = { |
278 | { "max17040", 0 }, |
279 | { } |
280 | }; |
281 | MODULE_DEVICE_TABLE(i2c, max17040_id); |
282 | |
283 | static struct i2c_driver max17040_i2c_driver = { |
284 | .driver = { |
285 | .name = "max17040", |
286 | }, |
287 | .probe = max17040_probe, |
288 | .remove = __devexit_p(max17040_remove), |
289 | .suspend = max17040_suspend, |
290 | .resume = max17040_resume, |
291 | .id_table = max17040_id, |
292 | }; |
293 | module_i2c_driver(max17040_i2c_driver); |
294 | |
295 | MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>"); |
296 | MODULE_DESCRIPTION("MAX17040 Fuel Gauge"); |
297 | MODULE_LICENSE("GPL"); |
298 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9