Root/
1 | /* |
2 | * I2C client/driver for the Maxim/Dallas DS2782 Stand-Alone Fuel Gauge IC |
3 | * |
4 | * Copyright (C) 2009 Bluewater Systems Ltd |
5 | * |
6 | * Author: Ryan Mallon |
7 | * |
8 | * DS2786 added by Yulia Vilensky <vilensky@compulab.co.il> |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. |
13 | * |
14 | */ |
15 | |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/types.h> |
19 | #include <linux/errno.h> |
20 | #include <linux/swab.h> |
21 | #include <linux/i2c.h> |
22 | #include <linux/idr.h> |
23 | #include <linux/power_supply.h> |
24 | #include <linux/slab.h> |
25 | #include <linux/ds2782_battery.h> |
26 | |
27 | #define DS2782_REG_RARC 0x06 /* Remaining active relative capacity */ |
28 | |
29 | #define DS278x_REG_VOLT_MSB 0x0c |
30 | #define DS278x_REG_TEMP_MSB 0x0a |
31 | #define DS278x_REG_CURRENT_MSB 0x0e |
32 | |
33 | /* EEPROM Block */ |
34 | #define DS2782_REG_RSNSP 0x69 /* Sense resistor value */ |
35 | |
36 | /* Current unit measurement in uA for a 1 milli-ohm sense resistor */ |
37 | #define DS2782_CURRENT_UNITS 1563 |
38 | |
39 | #define DS2786_REG_RARC 0x02 /* Remaining active relative capacity */ |
40 | |
41 | #define DS2786_CURRENT_UNITS 25 |
42 | |
43 | struct ds278x_info; |
44 | |
45 | struct ds278x_battery_ops { |
46 | int (*get_battery_current)(struct ds278x_info *info, int *current_uA); |
47 | int (*get_battery_voltage)(struct ds278x_info *info, int *voltage_uV); |
48 | int (*get_battery_capacity)(struct ds278x_info *info, int *capacity); |
49 | }; |
50 | |
51 | #define to_ds278x_info(x) container_of(x, struct ds278x_info, battery) |
52 | |
53 | struct ds278x_info { |
54 | struct i2c_client *client; |
55 | struct power_supply battery; |
56 | struct ds278x_battery_ops *ops; |
57 | int id; |
58 | int rsns; |
59 | }; |
60 | |
61 | static DEFINE_IDR(battery_id); |
62 | static DEFINE_MUTEX(battery_lock); |
63 | |
64 | static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val) |
65 | { |
66 | int ret; |
67 | |
68 | ret = i2c_smbus_read_byte_data(info->client, reg); |
69 | if (ret < 0) { |
70 | dev_err(&info->client->dev, "register read failed\n"); |
71 | return ret; |
72 | } |
73 | |
74 | *val = ret; |
75 | return 0; |
76 | } |
77 | |
78 | static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb, |
79 | s16 *val) |
80 | { |
81 | int ret; |
82 | |
83 | ret = swab16(i2c_smbus_read_word_data(info->client, reg_msb)); |
84 | if (ret < 0) { |
85 | dev_err(&info->client->dev, "register read failed\n"); |
86 | return ret; |
87 | } |
88 | |
89 | *val = ret; |
90 | return 0; |
91 | } |
92 | |
93 | static int ds278x_get_temp(struct ds278x_info *info, int *temp) |
94 | { |
95 | s16 raw; |
96 | int err; |
97 | |
98 | /* |
99 | * Temperature is measured in units of 0.125 degrees celcius, the |
100 | * power_supply class measures temperature in tenths of degrees |
101 | * celsius. The temperature value is stored as a 10 bit number, plus |
102 | * sign in the upper bits of a 16 bit register. |
103 | */ |
104 | err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw); |
105 | if (err) |
106 | return err; |
107 | *temp = ((raw / 32) * 125) / 100; |
108 | return 0; |
109 | } |
110 | |
111 | static int ds2782_get_current(struct ds278x_info *info, int *current_uA) |
112 | { |
113 | int sense_res; |
114 | int err; |
115 | u8 sense_res_raw; |
116 | s16 raw; |
117 | |
118 | /* |
119 | * The units of measurement for current are dependent on the value of |
120 | * the sense resistor. |
121 | */ |
122 | err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw); |
123 | if (err) |
124 | return err; |
125 | if (sense_res_raw == 0) { |
126 | dev_err(&info->client->dev, "sense resistor value is 0\n"); |
127 | return -ENXIO; |
128 | } |
129 | sense_res = 1000 / sense_res_raw; |
130 | |
131 | dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n", |
132 | sense_res); |
133 | err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw); |
134 | if (err) |
135 | return err; |
136 | *current_uA = raw * (DS2782_CURRENT_UNITS / sense_res); |
137 | return 0; |
138 | } |
139 | |
140 | static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uV) |
141 | { |
142 | s16 raw; |
143 | int err; |
144 | |
145 | /* |
146 | * Voltage is measured in units of 4.88mV. The voltage is stored as |
147 | * a 10-bit number plus sign, in the upper bits of a 16-bit register |
148 | */ |
149 | err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); |
150 | if (err) |
151 | return err; |
152 | *voltage_uV = (raw / 32) * 4800; |
153 | return 0; |
154 | } |
155 | |
156 | static int ds2782_get_capacity(struct ds278x_info *info, int *capacity) |
157 | { |
158 | int err; |
159 | u8 raw; |
160 | |
161 | err = ds278x_read_reg(info, DS2782_REG_RARC, &raw); |
162 | if (err) |
163 | return err; |
164 | *capacity = raw; |
165 | return 0; |
166 | } |
167 | |
168 | static int ds2786_get_current(struct ds278x_info *info, int *current_uA) |
169 | { |
170 | int err; |
171 | s16 raw; |
172 | |
173 | err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw); |
174 | if (err) |
175 | return err; |
176 | *current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns); |
177 | return 0; |
178 | } |
179 | |
180 | static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uV) |
181 | { |
182 | s16 raw; |
183 | int err; |
184 | |
185 | /* |
186 | * Voltage is measured in units of 1.22mV. The voltage is stored as |
187 | * a 10-bit number plus sign, in the upper bits of a 16-bit register |
188 | */ |
189 | err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw); |
190 | if (err) |
191 | return err; |
192 | *voltage_uV = (raw / 8) * 1220; |
193 | return 0; |
194 | } |
195 | |
196 | static int ds2786_get_capacity(struct ds278x_info *info, int *capacity) |
197 | { |
198 | int err; |
199 | u8 raw; |
200 | |
201 | err = ds278x_read_reg(info, DS2786_REG_RARC, &raw); |
202 | if (err) |
203 | return err; |
204 | /* Relative capacity is displayed with resolution 0.5 % */ |
205 | *capacity = raw/2 ; |
206 | return 0; |
207 | } |
208 | |
209 | static int ds278x_get_status(struct ds278x_info *info, int *status) |
210 | { |
211 | int err; |
212 | int current_uA; |
213 | int capacity; |
214 | |
215 | err = info->ops->get_battery_current(info, ¤t_uA); |
216 | if (err) |
217 | return err; |
218 | |
219 | err = info->ops->get_battery_capacity(info, &capacity); |
220 | if (err) |
221 | return err; |
222 | |
223 | if (capacity == 100) |
224 | *status = POWER_SUPPLY_STATUS_FULL; |
225 | else if (current_uA == 0) |
226 | *status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
227 | else if (current_uA < 0) |
228 | *status = POWER_SUPPLY_STATUS_DISCHARGING; |
229 | else |
230 | *status = POWER_SUPPLY_STATUS_CHARGING; |
231 | |
232 | return 0; |
233 | } |
234 | |
235 | static int ds278x_battery_get_property(struct power_supply *psy, |
236 | enum power_supply_property prop, |
237 | union power_supply_propval *val) |
238 | { |
239 | struct ds278x_info *info = to_ds278x_info(psy); |
240 | int ret; |
241 | |
242 | switch (prop) { |
243 | case POWER_SUPPLY_PROP_STATUS: |
244 | ret = ds278x_get_status(info, &val->intval); |
245 | break; |
246 | |
247 | case POWER_SUPPLY_PROP_CAPACITY: |
248 | ret = info->ops->get_battery_capacity(info, &val->intval); |
249 | break; |
250 | |
251 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
252 | ret = info->ops->get_battery_voltage(info, &val->intval); |
253 | break; |
254 | |
255 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
256 | ret = info->ops->get_battery_current(info, &val->intval); |
257 | break; |
258 | |
259 | case POWER_SUPPLY_PROP_TEMP: |
260 | ret = ds278x_get_temp(info, &val->intval); |
261 | break; |
262 | |
263 | default: |
264 | ret = -EINVAL; |
265 | } |
266 | |
267 | return ret; |
268 | } |
269 | |
270 | static enum power_supply_property ds278x_battery_props[] = { |
271 | POWER_SUPPLY_PROP_STATUS, |
272 | POWER_SUPPLY_PROP_CAPACITY, |
273 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
274 | POWER_SUPPLY_PROP_CURRENT_NOW, |
275 | POWER_SUPPLY_PROP_TEMP, |
276 | }; |
277 | |
278 | static void ds278x_power_supply_init(struct power_supply *battery) |
279 | { |
280 | battery->type = POWER_SUPPLY_TYPE_BATTERY; |
281 | battery->properties = ds278x_battery_props; |
282 | battery->num_properties = ARRAY_SIZE(ds278x_battery_props); |
283 | battery->get_property = ds278x_battery_get_property; |
284 | battery->external_power_changed = NULL; |
285 | } |
286 | |
287 | static int ds278x_battery_remove(struct i2c_client *client) |
288 | { |
289 | struct ds278x_info *info = i2c_get_clientdata(client); |
290 | |
291 | power_supply_unregister(&info->battery); |
292 | kfree(info->battery.name); |
293 | |
294 | mutex_lock(&battery_lock); |
295 | idr_remove(&battery_id, info->id); |
296 | mutex_unlock(&battery_lock); |
297 | |
298 | kfree(info); |
299 | return 0; |
300 | } |
301 | |
302 | enum ds278x_num_id { |
303 | DS2782 = 0, |
304 | DS2786, |
305 | }; |
306 | |
307 | static struct ds278x_battery_ops ds278x_ops[] = { |
308 | [DS2782] = { |
309 | .get_battery_current = ds2782_get_current, |
310 | .get_battery_voltage = ds2782_get_voltage, |
311 | .get_battery_capacity = ds2782_get_capacity, |
312 | }, |
313 | [DS2786] = { |
314 | .get_battery_current = ds2786_get_current, |
315 | .get_battery_voltage = ds2786_get_voltage, |
316 | .get_battery_capacity = ds2786_get_capacity, |
317 | } |
318 | }; |
319 | |
320 | static int ds278x_battery_probe(struct i2c_client *client, |
321 | const struct i2c_device_id *id) |
322 | { |
323 | struct ds278x_platform_data *pdata = client->dev.platform_data; |
324 | struct ds278x_info *info; |
325 | int ret; |
326 | int num; |
327 | |
328 | /* |
329 | * ds2786 should have the sense resistor value set |
330 | * in the platform data |
331 | */ |
332 | if (id->driver_data == DS2786 && !pdata) { |
333 | dev_err(&client->dev, "missing platform data for ds2786\n"); |
334 | return -EINVAL; |
335 | } |
336 | |
337 | /* Get an ID for this battery */ |
338 | ret = idr_pre_get(&battery_id, GFP_KERNEL); |
339 | if (ret == 0) { |
340 | ret = -ENOMEM; |
341 | goto fail_id; |
342 | } |
343 | |
344 | mutex_lock(&battery_lock); |
345 | ret = idr_get_new(&battery_id, client, &num); |
346 | mutex_unlock(&battery_lock); |
347 | if (ret < 0) |
348 | goto fail_id; |
349 | |
350 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
351 | if (!info) { |
352 | ret = -ENOMEM; |
353 | goto fail_info; |
354 | } |
355 | |
356 | info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num); |
357 | if (!info->battery.name) { |
358 | ret = -ENOMEM; |
359 | goto fail_name; |
360 | } |
361 | |
362 | if (id->driver_data == DS2786) |
363 | info->rsns = pdata->rsns; |
364 | |
365 | i2c_set_clientdata(client, info); |
366 | info->client = client; |
367 | info->id = num; |
368 | info->ops = &ds278x_ops[id->driver_data]; |
369 | ds278x_power_supply_init(&info->battery); |
370 | |
371 | ret = power_supply_register(&client->dev, &info->battery); |
372 | if (ret) { |
373 | dev_err(&client->dev, "failed to register battery\n"); |
374 | goto fail_register; |
375 | } |
376 | |
377 | return 0; |
378 | |
379 | fail_register: |
380 | kfree(info->battery.name); |
381 | fail_name: |
382 | kfree(info); |
383 | fail_info: |
384 | mutex_lock(&battery_lock); |
385 | idr_remove(&battery_id, num); |
386 | mutex_unlock(&battery_lock); |
387 | fail_id: |
388 | return ret; |
389 | } |
390 | |
391 | static const struct i2c_device_id ds278x_id[] = { |
392 | {"ds2782", DS2782}, |
393 | {"ds2786", DS2786}, |
394 | {}, |
395 | }; |
396 | MODULE_DEVICE_TABLE(i2c, ds278x_id); |
397 | |
398 | static struct i2c_driver ds278x_battery_driver = { |
399 | .driver = { |
400 | .name = "ds2782-battery", |
401 | }, |
402 | .probe = ds278x_battery_probe, |
403 | .remove = ds278x_battery_remove, |
404 | .id_table = ds278x_id, |
405 | }; |
406 | module_i2c_driver(ds278x_battery_driver); |
407 | |
408 | MODULE_AUTHOR("Ryan Mallon"); |
409 | MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver"); |
410 | MODULE_LICENSE("GPL"); |
411 |
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