aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/power/z2_battery.c
diff options
context:
space:
mode:
authorMarek Vasut <marek.vasut@gmail.com>2010-02-28 06:47:49 -0500
committerAnton Vorontsov <cbouatmailru@gmail.com>2010-04-06 12:35:58 -0400
commite3e8d1c93f9e6b766424b05f23f2416f22a0329d (patch)
tree6ec71b13165e353d56be9272551706c878d47028 /drivers/power/z2_battery.c
parenta009d29ea104c1bd8805a20018469897c2c2263c (diff)
Driver for Zipit Z2 battery chip
This patch adds driver for Zipit Z2 battery chip called AER915. No details are known about the chip. The chip is available through I2C bus at address 0x55 and it's register 0x02 contains battery voltage. Signed-off-by: Marek Vasut <marek.vasut@gmail.com> Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com>
Diffstat (limited to 'drivers/power/z2_battery.c')
-rw-r--r--drivers/power/z2_battery.c328
1 files changed, 328 insertions, 0 deletions
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
29struct 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
38static 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
49static 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
92static 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
99static 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
123static 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
130static 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
137static 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
187static 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
245err4:
246 kfree(charger->batt_ps.properties);
247err3:
248 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
249 free_irq(gpio_to_irq(info->charge_gpio), charger);
250err2:
251 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
252 gpio_free(info->charge_gpio);
253err:
254 kfree(charger);
255 return ret;
256}
257
258static 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
278static int z2_batt_suspend(struct i2c_client *client, pm_message_t state)
279{
280 flush_scheduled_work();
281 return 0;
282}
283
284static 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
296static const struct i2c_device_id z2_batt_id[] = {
297 { "aer915", 0 },
298 { }
299};
300
301static 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
313static int __init z2_batt_init(void)
314{
315 return i2c_add_driver(&z2_batt_driver);
316}
317
318static void __exit z2_batt_exit(void)
319{
320 i2c_del_driver(&z2_batt_driver);
321}
322
323module_init(z2_batt_init);
324module_exit(z2_batt_exit);
325
326MODULE_LICENSE("GPL");
327MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
328MODULE_DESCRIPTION("Zipit Z2 battery driver");