diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/power/Kconfig | 8 | ||||
-rw-r--r-- | drivers/power/Makefile | 3 | ||||
-rw-r--r-- | drivers/power/bq27x00_battery.c | 381 | ||||
-rw-r--r-- | drivers/power/pda_power.c | 11 | ||||
-rw-r--r-- | drivers/power/power_supply_core.c | 25 |
5 files changed, 422 insertions, 6 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 63bb57910445..8e0c2b47803c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -51,7 +51,7 @@ config BATTERY_OLPC | |||
51 | 51 | ||
52 | config BATTERY_TOSA | 52 | config BATTERY_TOSA |
53 | tristate "Sharp SL-6000 (tosa) battery" | 53 | tristate "Sharp SL-6000 (tosa) battery" |
54 | depends on MACH_TOSA && MFD_TC6393XB | 54 | depends on MACH_TOSA && MFD_TC6393XB && TOUCHSCREEN_WM97XX |
55 | help | 55 | help |
56 | Say Y to enable support for the battery on the Sharp Zaurus | 56 | Say Y to enable support for the battery on the Sharp Zaurus |
57 | SL-6000 (tosa) models. | 57 | SL-6000 (tosa) models. |
@@ -62,4 +62,10 @@ config BATTERY_WM97XX | |||
62 | help | 62 | help |
63 | Say Y to enable support for battery measured by WM97xx aux port. | 63 | Say Y to enable support for battery measured by WM97xx aux port. |
64 | 64 | ||
65 | config BATTERY_BQ27x00 | ||
66 | tristate "BQ27200 battery driver" | ||
67 | depends on I2C | ||
68 | help | ||
69 | Say Y here to enable support for batteries with BQ27200(I2C) chip. | ||
70 | |||
65 | endif # POWER_SUPPLY | 71 | endif # POWER_SUPPLY |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 4e20026cc45a..e8f1ecec5d8f 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -21,4 +21,5 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o | |||
21 | obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o | 21 | obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o |
22 | obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o | 22 | obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o |
23 | obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o | 23 | obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o |
24 | obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o \ No newline at end of file | 24 | obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o |
25 | obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o | ||
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c new file mode 100644 index 000000000000..0c056fcc01ce --- /dev/null +++ b/drivers/power/bq27x00_battery.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | * BQ27x00 battery driver | ||
3 | * | ||
4 | * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> | ||
5 | * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> | ||
6 | * | ||
7 | * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. | ||
8 | * | ||
9 | * This package is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | ||
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | ||
15 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
16 | * | ||
17 | */ | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/param.h> | ||
20 | #include <linux/jiffies.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/power_supply.h> | ||
25 | #include <linux/idr.h> | ||
26 | #include <linux/i2c.h> | ||
27 | #include <asm/unaligned.h> | ||
28 | |||
29 | #define DRIVER_VERSION "1.0.0" | ||
30 | |||
31 | #define BQ27x00_REG_TEMP 0x06 | ||
32 | #define BQ27x00_REG_VOLT 0x08 | ||
33 | #define BQ27x00_REG_RSOC 0x0B /* Relative State-of-Charge */ | ||
34 | #define BQ27x00_REG_AI 0x14 | ||
35 | #define BQ27x00_REG_FLAGS 0x0A | ||
36 | |||
37 | /* If the system has several batteries we need a different name for each | ||
38 | * of them... | ||
39 | */ | ||
40 | static DEFINE_IDR(battery_id); | ||
41 | static DEFINE_MUTEX(battery_mutex); | ||
42 | |||
43 | struct bq27x00_device_info; | ||
44 | struct bq27x00_access_methods { | ||
45 | int (*read)(u8 reg, int *rt_value, int b_single, | ||
46 | struct bq27x00_device_info *di); | ||
47 | }; | ||
48 | |||
49 | struct bq27x00_device_info { | ||
50 | struct device *dev; | ||
51 | int id; | ||
52 | int voltage_uV; | ||
53 | int current_uA; | ||
54 | int temp_C; | ||
55 | int charge_rsoc; | ||
56 | struct bq27x00_access_methods *bus; | ||
57 | struct power_supply bat; | ||
58 | |||
59 | struct i2c_client *client; | ||
60 | }; | ||
61 | |||
62 | static enum power_supply_property bq27x00_battery_props[] = { | ||
63 | POWER_SUPPLY_PROP_PRESENT, | ||
64 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
65 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
66 | POWER_SUPPLY_PROP_CAPACITY, | ||
67 | POWER_SUPPLY_PROP_TEMP, | ||
68 | }; | ||
69 | |||
70 | /* | ||
71 | * Common code for BQ27x00 devices | ||
72 | */ | ||
73 | |||
74 | static int bq27x00_read(u8 reg, int *rt_value, int b_single, | ||
75 | struct bq27x00_device_info *di) | ||
76 | { | ||
77 | int ret; | ||
78 | |||
79 | ret = di->bus->read(reg, rt_value, b_single, di); | ||
80 | *rt_value = be16_to_cpu(*rt_value); | ||
81 | |||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * Return the battery temperature in Celcius degrees | ||
87 | * Or < 0 if something fails. | ||
88 | */ | ||
89 | static int bq27x00_battery_temperature(struct bq27x00_device_info *di) | ||
90 | { | ||
91 | int ret; | ||
92 | int temp = 0; | ||
93 | |||
94 | ret = bq27x00_read(BQ27x00_REG_TEMP, &temp, 0, di); | ||
95 | if (ret) { | ||
96 | dev_err(di->dev, "error reading temperature\n"); | ||
97 | return ret; | ||
98 | } | ||
99 | |||
100 | return (temp >> 2) - 273; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * Return the battery Voltage in milivolts | ||
105 | * Or < 0 if something fails. | ||
106 | */ | ||
107 | static int bq27x00_battery_voltage(struct bq27x00_device_info *di) | ||
108 | { | ||
109 | int ret; | ||
110 | int volt = 0; | ||
111 | |||
112 | ret = bq27x00_read(BQ27x00_REG_VOLT, &volt, 0, di); | ||
113 | if (ret) { | ||
114 | dev_err(di->dev, "error reading voltage\n"); | ||
115 | return ret; | ||
116 | } | ||
117 | |||
118 | return volt; | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * Return the battery average current | ||
123 | * Note that current can be negative signed as well | ||
124 | * Or 0 if something fails. | ||
125 | */ | ||
126 | static int bq27x00_battery_current(struct bq27x00_device_info *di) | ||
127 | { | ||
128 | int ret; | ||
129 | int curr = 0; | ||
130 | int flags = 0; | ||
131 | |||
132 | ret = bq27x00_read(BQ27x00_REG_AI, &curr, 0, di); | ||
133 | if (ret) { | ||
134 | dev_err(di->dev, "error reading current\n"); | ||
135 | return 0; | ||
136 | } | ||
137 | ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); | ||
138 | if (ret < 0) { | ||
139 | dev_err(di->dev, "error reading flags\n"); | ||
140 | return 0; | ||
141 | } | ||
142 | if ((flags & (1 << 7)) != 0) { | ||
143 | dev_dbg(di->dev, "negative current!\n"); | ||
144 | return -curr; | ||
145 | } | ||
146 | return curr; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Return the battery Relative State-of-Charge | ||
151 | * Or < 0 if something fails. | ||
152 | */ | ||
153 | static int bq27x00_battery_rsoc(struct bq27x00_device_info *di) | ||
154 | { | ||
155 | int ret; | ||
156 | int rsoc = 0; | ||
157 | |||
158 | ret = bq27x00_read(BQ27x00_REG_RSOC, &rsoc, 1, di); | ||
159 | if (ret) { | ||
160 | dev_err(di->dev, "error reading relative State-of-Charge\n"); | ||
161 | return ret; | ||
162 | } | ||
163 | |||
164 | return rsoc >> 8; | ||
165 | } | ||
166 | |||
167 | #define to_bq27x00_device_info(x) container_of((x), \ | ||
168 | struct bq27x00_device_info, bat); | ||
169 | |||
170 | static int bq27x00_battery_get_property(struct power_supply *psy, | ||
171 | enum power_supply_property psp, | ||
172 | union power_supply_propval *val) | ||
173 | { | ||
174 | struct bq27x00_device_info *di = to_bq27x00_device_info(psy); | ||
175 | |||
176 | switch (psp) { | ||
177 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
178 | case POWER_SUPPLY_PROP_PRESENT: | ||
179 | val->intval = bq27x00_battery_voltage(di); | ||
180 | if (psp == POWER_SUPPLY_PROP_PRESENT) | ||
181 | val->intval = val->intval <= 0 ? 0 : 1; | ||
182 | break; | ||
183 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
184 | val->intval = bq27x00_battery_current(di); | ||
185 | break; | ||
186 | case POWER_SUPPLY_PROP_CAPACITY: | ||
187 | val->intval = bq27x00_battery_rsoc(di); | ||
188 | break; | ||
189 | case POWER_SUPPLY_PROP_TEMP: | ||
190 | val->intval = bq27x00_battery_temperature(di); | ||
191 | break; | ||
192 | default: | ||
193 | return -EINVAL; | ||
194 | } | ||
195 | |||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | static void bq27x00_powersupply_init(struct bq27x00_device_info *di) | ||
200 | { | ||
201 | di->bat.type = POWER_SUPPLY_TYPE_BATTERY; | ||
202 | di->bat.properties = bq27x00_battery_props; | ||
203 | di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); | ||
204 | di->bat.get_property = bq27x00_battery_get_property; | ||
205 | di->bat.external_power_changed = NULL; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * BQ27200 specific code | ||
210 | */ | ||
211 | |||
212 | static int bq27200_read(u8 reg, int *rt_value, int b_single, | ||
213 | struct bq27x00_device_info *di) | ||
214 | { | ||
215 | struct i2c_client *client = di->client; | ||
216 | struct i2c_msg msg[1]; | ||
217 | unsigned char data[2]; | ||
218 | int err; | ||
219 | |||
220 | if (!client->adapter) | ||
221 | return -ENODEV; | ||
222 | |||
223 | msg->addr = client->addr; | ||
224 | msg->flags = 0; | ||
225 | msg->len = 1; | ||
226 | msg->buf = data; | ||
227 | |||
228 | data[0] = reg; | ||
229 | err = i2c_transfer(client->adapter, msg, 1); | ||
230 | |||
231 | if (err >= 0) { | ||
232 | if (!b_single) | ||
233 | msg->len = 2; | ||
234 | else | ||
235 | msg->len = 1; | ||
236 | |||
237 | msg->flags = I2C_M_RD; | ||
238 | err = i2c_transfer(client->adapter, msg, 1); | ||
239 | if (err >= 0) { | ||
240 | if (!b_single) | ||
241 | *rt_value = get_unaligned_be16(data); | ||
242 | else | ||
243 | *rt_value = data[0]; | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | } | ||
248 | return err; | ||
249 | } | ||
250 | |||
251 | static int bq27200_battery_probe(struct i2c_client *client, | ||
252 | const struct i2c_device_id *id) | ||
253 | { | ||
254 | char *name; | ||
255 | struct bq27x00_device_info *di; | ||
256 | struct bq27x00_access_methods *bus; | ||
257 | int num; | ||
258 | int retval = 0; | ||
259 | |||
260 | /* Get new ID for the new battery device */ | ||
261 | retval = idr_pre_get(&battery_id, GFP_KERNEL); | ||
262 | if (retval == 0) | ||
263 | return -ENOMEM; | ||
264 | mutex_lock(&battery_mutex); | ||
265 | retval = idr_get_new(&battery_id, client, &num); | ||
266 | mutex_unlock(&battery_mutex); | ||
267 | if (retval < 0) | ||
268 | return retval; | ||
269 | |||
270 | name = kasprintf(GFP_KERNEL, "bq27200-%d", num); | ||
271 | if (!name) { | ||
272 | dev_err(&client->dev, "failed to allocate device name\n"); | ||
273 | retval = -ENOMEM; | ||
274 | goto batt_failed_1; | ||
275 | } | ||
276 | |||
277 | di = kzalloc(sizeof(*di), GFP_KERNEL); | ||
278 | if (!di) { | ||
279 | dev_err(&client->dev, "failed to allocate device info data\n"); | ||
280 | retval = -ENOMEM; | ||
281 | goto batt_failed_2; | ||
282 | } | ||
283 | di->id = num; | ||
284 | |||
285 | bus = kzalloc(sizeof(*bus), GFP_KERNEL); | ||
286 | if (!bus) { | ||
287 | dev_err(&client->dev, "failed to allocate access method " | ||
288 | "data\n"); | ||
289 | retval = -ENOMEM; | ||
290 | goto batt_failed_3; | ||
291 | } | ||
292 | |||
293 | i2c_set_clientdata(client, di); | ||
294 | di->dev = &client->dev; | ||
295 | di->bat.name = name; | ||
296 | bus->read = &bq27200_read; | ||
297 | di->bus = bus; | ||
298 | di->client = client; | ||
299 | |||
300 | bq27x00_powersupply_init(di); | ||
301 | |||
302 | retval = power_supply_register(&client->dev, &di->bat); | ||
303 | if (retval) { | ||
304 | dev_err(&client->dev, "failed to register battery\n"); | ||
305 | goto batt_failed_4; | ||
306 | } | ||
307 | |||
308 | dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION); | ||
309 | |||
310 | return 0; | ||
311 | |||
312 | batt_failed_4: | ||
313 | kfree(bus); | ||
314 | batt_failed_3: | ||
315 | kfree(di); | ||
316 | batt_failed_2: | ||
317 | kfree(name); | ||
318 | batt_failed_1: | ||
319 | mutex_lock(&battery_mutex); | ||
320 | idr_remove(&battery_id, num); | ||
321 | mutex_unlock(&battery_mutex); | ||
322 | |||
323 | return retval; | ||
324 | } | ||
325 | |||
326 | static int bq27200_battery_remove(struct i2c_client *client) | ||
327 | { | ||
328 | struct bq27x00_device_info *di = i2c_get_clientdata(client); | ||
329 | |||
330 | power_supply_unregister(&di->bat); | ||
331 | |||
332 | kfree(di->bat.name); | ||
333 | |||
334 | mutex_lock(&battery_mutex); | ||
335 | idr_remove(&battery_id, di->id); | ||
336 | mutex_unlock(&battery_mutex); | ||
337 | |||
338 | kfree(di); | ||
339 | |||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | /* | ||
344 | * Module stuff | ||
345 | */ | ||
346 | |||
347 | static const struct i2c_device_id bq27200_id[] = { | ||
348 | { "bq27200", 0 }, | ||
349 | {}, | ||
350 | }; | ||
351 | |||
352 | static struct i2c_driver bq27200_battery_driver = { | ||
353 | .driver = { | ||
354 | .name = "bq27200-battery", | ||
355 | }, | ||
356 | .probe = bq27200_battery_probe, | ||
357 | .remove = bq27200_battery_remove, | ||
358 | .id_table = bq27200_id, | ||
359 | }; | ||
360 | |||
361 | static int __init bq27x00_battery_init(void) | ||
362 | { | ||
363 | int ret; | ||
364 | |||
365 | ret = i2c_add_driver(&bq27200_battery_driver); | ||
366 | if (ret) | ||
367 | printk(KERN_ERR "Unable to register BQ27200 driver\n"); | ||
368 | |||
369 | return ret; | ||
370 | } | ||
371 | module_init(bq27x00_battery_init); | ||
372 | |||
373 | static void __exit bq27x00_battery_exit(void) | ||
374 | { | ||
375 | i2c_del_driver(&bq27200_battery_driver); | ||
376 | } | ||
377 | module_exit(bq27x00_battery_exit); | ||
378 | |||
379 | MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); | ||
380 | MODULE_DESCRIPTION("BQ27x00 battery monitor driver"); | ||
381 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 0471ec743ab9..d30bb766fcef 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c | |||
@@ -334,13 +334,16 @@ static int pda_power_remove(struct platform_device *pdev) | |||
334 | } | 334 | } |
335 | 335 | ||
336 | #ifdef CONFIG_PM | 336 | #ifdef CONFIG_PM |
337 | static int ac_wakeup_enabled; | ||
338 | static int usb_wakeup_enabled; | ||
339 | |||
337 | static int pda_power_suspend(struct platform_device *pdev, pm_message_t state) | 340 | static int pda_power_suspend(struct platform_device *pdev, pm_message_t state) |
338 | { | 341 | { |
339 | if (device_may_wakeup(&pdev->dev)) { | 342 | if (device_may_wakeup(&pdev->dev)) { |
340 | if (ac_irq) | 343 | if (ac_irq) |
341 | enable_irq_wake(ac_irq->start); | 344 | ac_wakeup_enabled = !enable_irq_wake(ac_irq->start); |
342 | if (usb_irq) | 345 | if (usb_irq) |
343 | enable_irq_wake(usb_irq->start); | 346 | usb_wakeup_enabled = !enable_irq_wake(usb_irq->start); |
344 | } | 347 | } |
345 | 348 | ||
346 | return 0; | 349 | return 0; |
@@ -349,9 +352,9 @@ static int pda_power_suspend(struct platform_device *pdev, pm_message_t state) | |||
349 | static int pda_power_resume(struct platform_device *pdev) | 352 | static int pda_power_resume(struct platform_device *pdev) |
350 | { | 353 | { |
351 | if (device_may_wakeup(&pdev->dev)) { | 354 | if (device_may_wakeup(&pdev->dev)) { |
352 | if (usb_irq) | 355 | if (usb_irq && usb_wakeup_enabled) |
353 | disable_irq_wake(usb_irq->start); | 356 | disable_irq_wake(usb_irq->start); |
354 | if (ac_irq) | 357 | if (ac_irq && ac_wakeup_enabled) |
355 | disable_irq_wake(ac_irq->start); | 358 | disable_irq_wake(ac_irq->start); |
356 | } | 359 | } |
357 | 360 | ||
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 3007695f90c8..5520040449c4 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c | |||
@@ -87,6 +87,30 @@ int power_supply_am_i_supplied(struct power_supply *psy) | |||
87 | return error; | 87 | return error; |
88 | } | 88 | } |
89 | 89 | ||
90 | static int __power_supply_is_system_supplied(struct device *dev, void *data) | ||
91 | { | ||
92 | union power_supply_propval ret = {0,}; | ||
93 | struct power_supply *psy = dev_get_drvdata(dev); | ||
94 | |||
95 | if (psy->type != POWER_SUPPLY_TYPE_BATTERY) { | ||
96 | if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret)) | ||
97 | return 0; | ||
98 | if (ret.intval) | ||
99 | return ret.intval; | ||
100 | } | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | int power_supply_is_system_supplied(void) | ||
105 | { | ||
106 | int error; | ||
107 | |||
108 | error = class_for_each_device(power_supply_class, NULL, NULL, | ||
109 | __power_supply_is_system_supplied); | ||
110 | |||
111 | return error; | ||
112 | } | ||
113 | |||
90 | int power_supply_register(struct device *parent, struct power_supply *psy) | 114 | int power_supply_register(struct device *parent, struct power_supply *psy) |
91 | { | 115 | { |
92 | int rc = 0; | 116 | int rc = 0; |
@@ -148,6 +172,7 @@ static void __exit power_supply_class_exit(void) | |||
148 | 172 | ||
149 | EXPORT_SYMBOL_GPL(power_supply_changed); | 173 | EXPORT_SYMBOL_GPL(power_supply_changed); |
150 | EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); | 174 | EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); |
175 | EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); | ||
151 | EXPORT_SYMBOL_GPL(power_supply_register); | 176 | EXPORT_SYMBOL_GPL(power_supply_register); |
152 | EXPORT_SYMBOL_GPL(power_supply_unregister); | 177 | EXPORT_SYMBOL_GPL(power_supply_unregister); |
153 | 178 | ||