]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/power/z2_battery.c
Merge remote-tracking branch 'input/next'
[karo-tx-linux.git] / drivers / power / z2_battery.c
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/module.h>
13 #include <linux/gpio.h>
14 #include <linux/i2c.h>
15 #include <linux/interrupt.h>
16 #include <linux/irq.h>
17 #include <linux/power_supply.h>
18 #include <linux/slab.h>
19 #include <linux/z2_battery.h>
20
21 #define Z2_DEFAULT_NAME "Z2"
22
23 struct z2_charger {
24         struct z2_battery_info          *info;
25         int                             bat_status;
26         struct i2c_client               *client;
27         struct power_supply             *batt_ps;
28         struct power_supply_desc        batt_ps_desc;
29         struct mutex                    work_lock;
30         struct work_struct              bat_work;
31 };
32
33 static unsigned long z2_read_bat(struct z2_charger *charger)
34 {
35         int data;
36         data = i2c_smbus_read_byte_data(charger->client,
37                                         charger->info->batt_I2C_reg);
38         if (data < 0)
39                 return 0;
40
41         return data * charger->info->batt_mult / charger->info->batt_div;
42 }
43
44 static int z2_batt_get_property(struct power_supply *batt_ps,
45                             enum power_supply_property psp,
46                             union power_supply_propval *val)
47 {
48         struct z2_charger *charger = power_supply_get_drvdata(batt_ps);
49         struct z2_battery_info *info = charger->info;
50
51         switch (psp) {
52         case POWER_SUPPLY_PROP_STATUS:
53                 val->intval = charger->bat_status;
54                 break;
55         case POWER_SUPPLY_PROP_TECHNOLOGY:
56                 val->intval = info->batt_tech;
57                 break;
58         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
59                 if (info->batt_I2C_reg >= 0)
60                         val->intval = z2_read_bat(charger);
61                 else
62                         return -EINVAL;
63                 break;
64         case POWER_SUPPLY_PROP_VOLTAGE_MAX:
65                 if (info->max_voltage >= 0)
66                         val->intval = info->max_voltage;
67                 else
68                         return -EINVAL;
69                 break;
70         case POWER_SUPPLY_PROP_VOLTAGE_MIN:
71                 if (info->min_voltage >= 0)
72                         val->intval = info->min_voltage;
73                 else
74                         return -EINVAL;
75                 break;
76         case POWER_SUPPLY_PROP_PRESENT:
77                 val->intval = 1;
78                 break;
79         default:
80                 return -EINVAL;
81         }
82
83         return 0;
84 }
85
86 static void z2_batt_ext_power_changed(struct power_supply *batt_ps)
87 {
88         struct z2_charger *charger = power_supply_get_drvdata(batt_ps);
89
90         schedule_work(&charger->bat_work);
91 }
92
93 static void z2_batt_update(struct z2_charger *charger)
94 {
95         int old_status = charger->bat_status;
96         struct z2_battery_info *info;
97
98         info = charger->info;
99
100         mutex_lock(&charger->work_lock);
101
102         charger->bat_status = (info->charge_gpio >= 0) ?
103                 (gpio_get_value(info->charge_gpio) ?
104                 POWER_SUPPLY_STATUS_CHARGING :
105                 POWER_SUPPLY_STATUS_DISCHARGING) :
106                 POWER_SUPPLY_STATUS_UNKNOWN;
107
108         if (old_status != charger->bat_status) {
109                 pr_debug("%s: %i -> %i\n", charger->batt_ps->desc->name,
110                                 old_status,
111                                 charger->bat_status);
112                 power_supply_changed(charger->batt_ps);
113         }
114
115         mutex_unlock(&charger->work_lock);
116 }
117
118 static void z2_batt_work(struct work_struct *work)
119 {
120         struct z2_charger *charger;
121         charger = container_of(work, struct z2_charger, bat_work);
122         z2_batt_update(charger);
123 }
124
125 static irqreturn_t z2_charge_switch_irq(int irq, void *devid)
126 {
127         struct z2_charger *charger = devid;
128         schedule_work(&charger->bat_work);
129         return IRQ_HANDLED;
130 }
131
132 static int z2_batt_ps_init(struct z2_charger *charger, int props)
133 {
134         int i = 0;
135         enum power_supply_property *prop;
136         struct z2_battery_info *info = charger->info;
137
138         if (info->charge_gpio >= 0)
139                 props++;        /* POWER_SUPPLY_PROP_STATUS */
140         if (info->batt_tech >= 0)
141                 props++;        /* POWER_SUPPLY_PROP_TECHNOLOGY */
142         if (info->batt_I2C_reg >= 0)
143                 props++;        /* POWER_SUPPLY_PROP_VOLTAGE_NOW */
144         if (info->max_voltage >= 0)
145                 props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MAX */
146         if (info->min_voltage >= 0)
147                 props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MIN */
148
149         prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
150         if (!prop)
151                 return -ENOMEM;
152
153         prop[i++] = POWER_SUPPLY_PROP_PRESENT;
154         if (info->charge_gpio >= 0)
155                 prop[i++] = POWER_SUPPLY_PROP_STATUS;
156         if (info->batt_tech >= 0)
157                 prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
158         if (info->batt_I2C_reg >= 0)
159                 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
160         if (info->max_voltage >= 0)
161                 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
162         if (info->min_voltage >= 0)
163                 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
164
165         if (!info->batt_name) {
166                 dev_info(&charger->client->dev,
167                                 "Please consider setting proper battery "
168                                 "name in platform definition file, falling "
169                                 "back to name \" Z2_DEFAULT_NAME \"\n");
170                 charger->batt_ps_desc.name = Z2_DEFAULT_NAME;
171         } else
172                 charger->batt_ps_desc.name = info->batt_name;
173
174         charger->batt_ps_desc.properties        = prop;
175         charger->batt_ps_desc.num_properties    = props;
176         charger->batt_ps_desc.type              = POWER_SUPPLY_TYPE_BATTERY;
177         charger->batt_ps_desc.get_property      = z2_batt_get_property;
178         charger->batt_ps_desc.external_power_changed =
179                                                 z2_batt_ext_power_changed;
180         charger->batt_ps_desc.use_for_apm       = 1;
181
182         return 0;
183 }
184
185 static int z2_batt_probe(struct i2c_client *client,
186                                 const struct i2c_device_id *id)
187 {
188         int ret = 0;
189         int props = 1;  /* POWER_SUPPLY_PROP_PRESENT */
190         struct z2_charger *charger;
191         struct z2_battery_info *info = client->dev.platform_data;
192         struct power_supply_config psy_cfg = {};
193
194         if (info == NULL) {
195                 dev_err(&client->dev,
196                         "Please set platform device platform_data"
197                         " to a valid z2_battery_info pointer!\n");
198                 return -EINVAL;
199         }
200
201         charger = kzalloc(sizeof(*charger), GFP_KERNEL);
202         if (charger == NULL)
203                 return -ENOMEM;
204
205         charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
206         charger->info = info;
207         charger->client = client;
208         i2c_set_clientdata(client, charger);
209         psy_cfg.drv_data = 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                 irq_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, 0,
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         charger->batt_ps = power_supply_register(&client->dev,
238                                                  &charger->batt_ps_desc,
239                                                  &psy_cfg);
240         if (IS_ERR(charger->batt_ps)) {
241                 ret = PTR_ERR(charger->batt_ps);
242                 goto err4;
243         }
244
245         schedule_work(&charger->bat_work);
246
247         return 0;
248
249 err4:
250         kfree(charger->batt_ps_desc.properties);
251 err3:
252         if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
253                 free_irq(gpio_to_irq(info->charge_gpio), charger);
254 err2:
255         if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
256                 gpio_free(info->charge_gpio);
257 err:
258         kfree(charger);
259         return ret;
260 }
261
262 static int z2_batt_remove(struct i2c_client *client)
263 {
264         struct z2_charger *charger = i2c_get_clientdata(client);
265         struct z2_battery_info *info = charger->info;
266
267         cancel_work_sync(&charger->bat_work);
268         power_supply_unregister(charger->batt_ps);
269
270         kfree(charger->batt_ps_desc.properties);
271         if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
272                 free_irq(gpio_to_irq(info->charge_gpio), charger);
273                 gpio_free(info->charge_gpio);
274         }
275
276         kfree(charger);
277
278         return 0;
279 }
280
281 #ifdef CONFIG_PM
282 static int z2_batt_suspend(struct device *dev)
283 {
284         struct i2c_client *client = to_i2c_client(dev);
285         struct z2_charger *charger = i2c_get_clientdata(client);
286
287         flush_work(&charger->bat_work);
288         return 0;
289 }
290
291 static int z2_batt_resume(struct device *dev)
292 {
293         struct i2c_client *client = to_i2c_client(dev);
294         struct z2_charger *charger = i2c_get_clientdata(client);
295
296         schedule_work(&charger->bat_work);
297         return 0;
298 }
299
300 static const struct dev_pm_ops z2_battery_pm_ops = {
301         .suspend        = z2_batt_suspend,
302         .resume         = z2_batt_resume,
303 };
304
305 #define Z2_BATTERY_PM_OPS       (&z2_battery_pm_ops)
306
307 #else
308 #define Z2_BATTERY_PM_OPS       (NULL)
309 #endif
310
311 static const struct i2c_device_id z2_batt_id[] = {
312         { "aer915", 0 },
313         { }
314 };
315 MODULE_DEVICE_TABLE(i2c, z2_batt_id);
316
317 static struct i2c_driver z2_batt_driver = {
318         .driver = {
319                 .name   = "z2-battery",
320                 .owner  = THIS_MODULE,
321                 .pm     = Z2_BATTERY_PM_OPS
322         },
323         .probe          = z2_batt_probe,
324         .remove         = z2_batt_remove,
325         .id_table       = z2_batt_id,
326 };
327 module_i2c_driver(z2_batt_driver);
328
329 MODULE_LICENSE("GPL");
330 MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
331 MODULE_DESCRIPTION("Zipit Z2 battery driver");