diff options
-rw-r--r-- | drivers/power/Kconfig | 6 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/z2_battery.c | 328 | ||||
-rw-r--r-- | include/linux/z2_battery.h | 17 |
4 files changed, 352 insertions, 0 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index faaa9b4d0d07..22f2fa912127 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig | |||
@@ -125,6 +125,12 @@ config BATTERY_MAX17040 | |||
125 | in handheld and portable equipment. The MAX17040 is configured | 125 | in handheld and portable equipment. The MAX17040 is configured |
126 | to operate with a single lithium cell | 126 | to operate with a single lithium cell |
127 | 127 | ||
128 | config BATTERY_Z2 | ||
129 | tristate "Z2 battery driver" | ||
130 | depends on I2C && MACH_ZIPIT2 | ||
131 | help | ||
132 | Say Y to include support for the battery on the Zipit Z2. | ||
133 | |||
128 | config CHARGER_PCF50633 | 134 | config CHARGER_PCF50633 |
129 | tristate "NXP PCF50633 MBC" | 135 | tristate "NXP PCF50633 MBC" |
130 | depends on MFD_PCF50633 | 136 | depends on MFD_PCF50633 |
diff --git a/drivers/power/Makefile b/drivers/power/Makefile index a2ba7c85c97a..a82f292e5c94 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile | |||
@@ -31,4 +31,5 @@ obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o | |||
31 | obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o | 31 | obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o |
32 | obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o | 32 | obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o |
33 | obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o | 33 | obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o |
34 | obj-$(CONFIG_BATTERY_Z2) += z2_battery.o | ||
34 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o | 35 | obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o |
diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c new file mode 100644 index 000000000000..9cca465436e3 --- /dev/null +++ b/drivers/power/z2_battery.c | |||
@@ -0,0 +1,328 @@ | |||
1 | /* | ||
2 | * Battery measurement code for Zipit Z2 | ||
3 | * | ||
4 | * Copyright (C) 2009 Peter Edwards <sweetlilmre@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/power_supply.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/spinlock.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/gpio.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/irq.h> | ||
23 | #include <asm/irq.h> | ||
24 | #include <asm/mach/irq.h> | ||
25 | #include <linux/z2_battery.h> | ||
26 | |||
27 | #define Z2_DEFAULT_NAME "Z2" | ||
28 | |||
29 | struct z2_charger { | ||
30 | struct z2_battery_info *info; | ||
31 | int bat_status; | ||
32 | struct i2c_client *client; | ||
33 | struct power_supply batt_ps; | ||
34 | struct mutex work_lock; | ||
35 | struct work_struct bat_work; | ||
36 | }; | ||
37 | |||
38 | static unsigned long z2_read_bat(struct z2_charger *charger) | ||
39 | { | ||
40 | int data; | ||
41 | data = i2c_smbus_read_byte_data(charger->client, | ||
42 | charger->info->batt_I2C_reg); | ||
43 | if (data < 0) | ||
44 | return 0; | ||
45 | |||
46 | return data * charger->info->batt_mult / charger->info->batt_div; | ||
47 | } | ||
48 | |||
49 | static int z2_batt_get_property(struct power_supply *batt_ps, | ||
50 | enum power_supply_property psp, | ||
51 | union power_supply_propval *val) | ||
52 | { | ||
53 | struct z2_charger *charger = container_of(batt_ps, struct z2_charger, | ||
54 | batt_ps); | ||
55 | struct z2_battery_info *info = charger->info; | ||
56 | |||
57 | switch (psp) { | ||
58 | case POWER_SUPPLY_PROP_STATUS: | ||
59 | val->intval = charger->bat_status; | ||
60 | break; | ||
61 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
62 | val->intval = info->batt_tech; | ||
63 | break; | ||
64 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
65 | if (info->batt_I2C_reg >= 0) | ||
66 | val->intval = z2_read_bat(charger); | ||
67 | else | ||
68 | return -EINVAL; | ||
69 | break; | ||
70 | case POWER_SUPPLY_PROP_VOLTAGE_MAX: | ||
71 | if (info->max_voltage >= 0) | ||
72 | val->intval = info->max_voltage; | ||
73 | else | ||
74 | return -EINVAL; | ||
75 | break; | ||
76 | case POWER_SUPPLY_PROP_VOLTAGE_MIN: | ||
77 | if (info->min_voltage >= 0) | ||
78 | val->intval = info->min_voltage; | ||
79 | else | ||
80 | return -EINVAL; | ||
81 | break; | ||
82 | case POWER_SUPPLY_PROP_PRESENT: | ||
83 | val->intval = 1; | ||
84 | break; | ||
85 | default: | ||
86 | return -EINVAL; | ||
87 | } | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static void z2_batt_ext_power_changed(struct power_supply *batt_ps) | ||
93 | { | ||
94 | struct z2_charger *charger = container_of(batt_ps, struct z2_charger, | ||
95 | batt_ps); | ||
96 | schedule_work(&charger->bat_work); | ||
97 | } | ||
98 | |||
99 | static void z2_batt_update(struct z2_charger *charger) | ||
100 | { | ||
101 | int old_status = charger->bat_status; | ||
102 | struct z2_battery_info *info; | ||
103 | |||
104 | info = charger->info; | ||
105 | |||
106 | mutex_lock(&charger->work_lock); | ||
107 | |||
108 | charger->bat_status = (info->charge_gpio >= 0) ? | ||
109 | (gpio_get_value(info->charge_gpio) ? | ||
110 | POWER_SUPPLY_STATUS_CHARGING : | ||
111 | POWER_SUPPLY_STATUS_DISCHARGING) : | ||
112 | POWER_SUPPLY_STATUS_UNKNOWN; | ||
113 | |||
114 | if (old_status != charger->bat_status) { | ||
115 | pr_debug("%s: %i -> %i\n", charger->batt_ps.name, old_status, | ||
116 | charger->bat_status); | ||
117 | power_supply_changed(&charger->batt_ps); | ||
118 | } | ||
119 | |||
120 | mutex_unlock(&charger->work_lock); | ||
121 | } | ||
122 | |||
123 | static void z2_batt_work(struct work_struct *work) | ||
124 | { | ||
125 | struct z2_charger *charger; | ||
126 | charger = container_of(work, struct z2_charger, bat_work); | ||
127 | z2_batt_update(charger); | ||
128 | } | ||
129 | |||
130 | static irqreturn_t z2_charge_switch_irq(int irq, void *devid) | ||
131 | { | ||
132 | struct z2_charger *charger = devid; | ||
133 | schedule_work(&charger->bat_work); | ||
134 | return IRQ_HANDLED; | ||
135 | } | ||
136 | |||
137 | static int z2_batt_ps_init(struct z2_charger *charger, int props) | ||
138 | { | ||
139 | int i = 0; | ||
140 | enum power_supply_property *prop; | ||
141 | struct z2_battery_info *info = charger->info; | ||
142 | |||
143 | if (info->batt_tech >= 0) | ||
144 | props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ | ||
145 | if (info->batt_I2C_reg >= 0) | ||
146 | props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ | ||
147 | if (info->max_voltage >= 0) | ||
148 | props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ | ||
149 | if (info->min_voltage >= 0) | ||
150 | props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ | ||
151 | |||
152 | prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); | ||
153 | if (!prop) | ||
154 | return -ENOMEM; | ||
155 | |||
156 | prop[i++] = POWER_SUPPLY_PROP_PRESENT; | ||
157 | if (info->charge_gpio >= 0) | ||
158 | prop[i++] = POWER_SUPPLY_PROP_STATUS; | ||
159 | if (info->batt_tech >= 0) | ||
160 | prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; | ||
161 | if (info->batt_I2C_reg >= 0) | ||
162 | prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; | ||
163 | if (info->max_voltage >= 0) | ||
164 | prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; | ||
165 | if (info->min_voltage >= 0) | ||
166 | prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; | ||
167 | |||
168 | if (!info->batt_name) { | ||
169 | dev_info(&charger->client->dev, | ||
170 | "Please consider setting proper battery " | ||
171 | "name in platform definition file, falling " | ||
172 | "back to name \" Z2_DEFAULT_NAME \"\n"); | ||
173 | charger->batt_ps.name = Z2_DEFAULT_NAME; | ||
174 | } else | ||
175 | charger->batt_ps.name = info->batt_name; | ||
176 | |||
177 | charger->batt_ps.properties = prop; | ||
178 | charger->batt_ps.num_properties = props; | ||
179 | charger->batt_ps.type = POWER_SUPPLY_TYPE_BATTERY; | ||
180 | charger->batt_ps.get_property = z2_batt_get_property; | ||
181 | charger->batt_ps.external_power_changed = z2_batt_ext_power_changed; | ||
182 | charger->batt_ps.use_for_apm = 1; | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int __devinit z2_batt_probe(struct i2c_client *client, | ||
188 | const struct i2c_device_id *id) | ||
189 | { | ||
190 | int ret = 0; | ||
191 | int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ | ||
192 | struct z2_charger *charger; | ||
193 | struct z2_battery_info *info = client->dev.platform_data; | ||
194 | |||
195 | if (info == NULL) { | ||
196 | dev_err(&client->dev, | ||
197 | "Please set platform device platform_data" | ||
198 | " to a valid z2_battery_info pointer!\n"); | ||
199 | return -EINVAL; | ||
200 | } | ||
201 | |||
202 | charger = kzalloc(sizeof(*charger), GFP_KERNEL); | ||
203 | if (charger == NULL) | ||
204 | return -ENOMEM; | ||
205 | |||
206 | charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN; | ||
207 | charger->info = info; | ||
208 | charger->client = client; | ||
209 | i2c_set_clientdata(client, charger); | ||
210 | |||
211 | mutex_init(&charger->work_lock); | ||
212 | |||
213 | if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) { | ||
214 | ret = gpio_request(info->charge_gpio, "BATT CHRG"); | ||
215 | if (ret) | ||
216 | goto err; | ||
217 | |||
218 | ret = gpio_direction_input(info->charge_gpio); | ||
219 | if (ret) | ||
220 | goto err2; | ||
221 | |||
222 | set_irq_type(gpio_to_irq(info->charge_gpio), | ||
223 | IRQ_TYPE_EDGE_BOTH); | ||
224 | ret = request_irq(gpio_to_irq(info->charge_gpio), | ||
225 | z2_charge_switch_irq, IRQF_DISABLED, | ||
226 | "AC Detect", charger); | ||
227 | if (ret) | ||
228 | goto err3; | ||
229 | } | ||
230 | |||
231 | ret = z2_batt_ps_init(charger, props); | ||
232 | if (ret) | ||
233 | goto err3; | ||
234 | |||
235 | INIT_WORK(&charger->bat_work, z2_batt_work); | ||
236 | |||
237 | ret = power_supply_register(&client->dev, &charger->batt_ps); | ||
238 | if (ret) | ||
239 | goto err4; | ||
240 | |||
241 | schedule_work(&charger->bat_work); | ||
242 | |||
243 | return 0; | ||
244 | |||
245 | err4: | ||
246 | kfree(charger->batt_ps.properties); | ||
247 | err3: | ||
248 | if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) | ||
249 | free_irq(gpio_to_irq(info->charge_gpio), charger); | ||
250 | err2: | ||
251 | if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) | ||
252 | gpio_free(info->charge_gpio); | ||
253 | err: | ||
254 | kfree(charger); | ||
255 | return ret; | ||
256 | } | ||
257 | |||
258 | static int __devexit z2_batt_remove(struct i2c_client *client) | ||
259 | { | ||
260 | struct z2_charger *charger = i2c_get_clientdata(client); | ||
261 | struct z2_battery_info *info = charger->info; | ||
262 | |||
263 | flush_scheduled_work(); | ||
264 | power_supply_unregister(&charger->batt_ps); | ||
265 | |||
266 | kfree(charger->batt_ps.properties); | ||
267 | if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) { | ||
268 | free_irq(gpio_to_irq(info->charge_gpio), charger); | ||
269 | gpio_free(info->charge_gpio); | ||
270 | } | ||
271 | |||
272 | kfree(charger); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | #ifdef CONFIG_PM | ||
278 | static int z2_batt_suspend(struct i2c_client *client, pm_message_t state) | ||
279 | { | ||
280 | flush_scheduled_work(); | ||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static int z2_batt_resume(struct i2c_client *client) | ||
285 | { | ||
286 | struct z2_charger *charger = i2c_get_clientdata(client); | ||
287 | |||
288 | schedule_work(&charger->bat_work); | ||
289 | return 0; | ||
290 | } | ||
291 | #else | ||
292 | #define z2_batt_suspend NULL | ||
293 | #define z2_batt_resume NULL | ||
294 | #endif | ||
295 | |||
296 | static const struct i2c_device_id z2_batt_id[] = { | ||
297 | { "aer915", 0 }, | ||
298 | { } | ||
299 | }; | ||
300 | |||
301 | static struct i2c_driver z2_batt_driver = { | ||
302 | .driver = { | ||
303 | .name = "z2-battery", | ||
304 | .owner = THIS_MODULE, | ||
305 | }, | ||
306 | .probe = z2_batt_probe, | ||
307 | .remove = z2_batt_remove, | ||
308 | .suspend = z2_batt_suspend, | ||
309 | .resume = z2_batt_resume, | ||
310 | .id_table = z2_batt_id, | ||
311 | }; | ||
312 | |||
313 | static int __init z2_batt_init(void) | ||
314 | { | ||
315 | return i2c_add_driver(&z2_batt_driver); | ||
316 | } | ||
317 | |||
318 | static void __exit z2_batt_exit(void) | ||
319 | { | ||
320 | i2c_del_driver(&z2_batt_driver); | ||
321 | } | ||
322 | |||
323 | module_init(z2_batt_init); | ||
324 | module_exit(z2_batt_exit); | ||
325 | |||
326 | MODULE_LICENSE("GPL"); | ||
327 | MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>"); | ||
328 | MODULE_DESCRIPTION("Zipit Z2 battery driver"); | ||
diff --git a/include/linux/z2_battery.h b/include/linux/z2_battery.h new file mode 100644 index 000000000000..7b9750404d22 --- /dev/null +++ b/include/linux/z2_battery.h | |||
@@ -0,0 +1,17 @@ | |||
1 | #ifndef _LINUX_Z2_BATTERY_H | ||
2 | #define _LINUX_Z2_BATTERY_H | ||
3 | |||
4 | struct z2_battery_info { | ||
5 | int batt_I2C_bus; | ||
6 | int batt_I2C_addr; | ||
7 | int batt_I2C_reg; | ||
8 | int charge_gpio; | ||
9 | int min_voltage; | ||
10 | int max_voltage; | ||
11 | int batt_div; | ||
12 | int batt_mult; | ||
13 | int batt_tech; | ||
14 | char *batt_name; | ||
15 | }; | ||
16 | |||
17 | #endif | ||