Merge branch 'org.openembedded.dev' of git@git.openembedded.net:openembedded into...
[vuplus_openembedded] / packages / linux / linux-2.6.28 / collie / 0018-collie-add-battery-driver.patch
1 From a2cf77eaf64b201a00b9682c25596ef0bcda8dc4 Mon Sep 17 00:00:00 2001
2 From: Thomas Kunze <thommycheck@gmx.de>
3 Date: Tue, 10 Feb 2009 18:16:03 +0100
4 Subject: [PATCH 18/23] collie: add battery driver
5
6 This driver is based on tosa_battery.c.
7
8 Conflicts:
9
10         drivers/power/Makefile
11 ---
12  drivers/power/Kconfig          |    7 +
13  drivers/power/Makefile         |    1 +
14  drivers/power/collie_battery.c |  418 ++++++++++++++++++++++++++++++++++++++++
15  3 files changed, 426 insertions(+), 0 deletions(-)
16  create mode 100644 drivers/power/collie_battery.c
17
18 diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
19 index 8e0c2b4..a945046 100644
20 --- a/drivers/power/Kconfig
21 +++ b/drivers/power/Kconfig
22 @@ -56,6 +56,13 @@ config BATTERY_TOSA
23           Say Y to enable support for the battery on the Sharp Zaurus
24           SL-6000 (tosa) models.
25  
26 +config BATTERY_COLLIE
27 +       tristate "Sharp SL-5500 (collie) battery"
28 +       depends on SA1100_COLLIE && MCP_UCB1200
29 +       help
30 +         Say Y to enable support for the battery on the Sharp Zaurus
31 +         SL-5500 (collie) models.
32 +
33  config BATTERY_WM97XX
34         bool "WM97xx generic battery driver"
35         depends on TOUCHSCREEN_WM97XX=y
36 diff --git a/drivers/power/Makefile b/drivers/power/Makefile
37 index e8f1ece..51a7263 100644
38 --- a/drivers/power/Makefile
39 +++ b/drivers/power/Makefile
40 @@ -21,5 +21,6 @@ obj-$(CONFIG_BATTERY_DS2760)  += ds2760_battery.o
41  obj-$(CONFIG_BATTERY_PMU)      += pmu_battery.o
42  obj-$(CONFIG_BATTERY_OLPC)     += olpc_battery.o
43  obj-$(CONFIG_BATTERY_TOSA)     += tosa_battery.o
44 +obj-$(CONFIG_BATTERY_COLLIE)   += collie_battery.o
45  obj-$(CONFIG_BATTERY_WM97XX)   += wm97xx_battery.o
46  obj-$(CONFIG_BATTERY_BQ27x00)  += bq27x00_battery.o
47 diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c
48 new file mode 100644
49 index 0000000..039f41a
50 --- /dev/null
51 +++ b/drivers/power/collie_battery.c
52 @@ -0,0 +1,418 @@
53 +/*
54 + * Battery and Power Management code for the Sharp SL-5x00
55 + *
56 + * Copyright (C) 2009 Thomas Kunze
57 + *
58 + * based on tosa_battery.c
59 + *
60 + * This program is free software; you can redistribute it and/or modify
61 + * it under the terms of the GNU General Public License version 2 as
62 + * published by the Free Software Foundation.
63 + *
64 + */
65 +#include <linux/kernel.h>
66 +#include <linux/module.h>
67 +#include <linux/power_supply.h>
68 +#include <linux/delay.h>
69 +#include <linux/spinlock.h>
70 +#include <linux/interrupt.h>
71 +#include <linux/gpio.h>
72 +#include <linux/mfd/ucb1x00.h>
73 +
74 +#include <asm/mach/sharpsl_param.h>
75 +#include <asm/mach-types.h>
76 +#include <mach/collie.h>
77 +
78 +static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
79 +static struct work_struct bat_work;
80 +static struct ucb1x00 *ucb;
81 +
82 +struct collie_bat {
83 +       int status;
84 +       struct power_supply psy;
85 +       int full_chrg;
86 +
87 +       struct mutex work_lock; /* protects data */
88 +
89 +       bool (*is_present)(struct collie_bat *bat);
90 +       int gpio_full;
91 +       int gpio_charge_on;
92 +
93 +       int technology;
94 +
95 +       int gpio_bat;
96 +       int adc_bat;
97 +       int adc_bat_divider;
98 +       int bat_max;
99 +       int bat_min;
100 +
101 +       int gpio_temp;
102 +       int adc_temp;
103 +       int adc_temp_divider;
104 +};
105 +
106 +static struct collie_bat collie_bat_main;
107 +
108 +static unsigned long collie_read_bat(struct collie_bat *bat)
109 +{
110 +       unsigned long value = 0;
111 +
112 +       if (bat->gpio_bat < 0 || bat->adc_bat < 0)
113 +               return 0;
114 +       mutex_lock(&bat_lock);
115 +       gpio_set_value(bat->gpio_bat, 1);
116 +       msleep(5);
117 +       ucb1x00_adc_enable(ucb);
118 +       value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
119 +       ucb1x00_adc_disable(ucb);
120 +       gpio_set_value(bat->gpio_bat, 0);
121 +       mutex_unlock(&bat_lock);
122 +       value = value * 1000000 / bat->adc_bat_divider;
123 +
124 +       return value;
125 +}
126 +
127 +static unsigned long collie_read_temp(struct collie_bat *bat)
128 +{
129 +       unsigned long value = 0;
130 +       if (bat->gpio_temp < 0 || bat->adc_temp < 0)
131 +               return 0;
132 +
133 +       mutex_lock(&bat_lock);
134 +       gpio_set_value(bat->gpio_temp, 1);
135 +       msleep(5);
136 +       ucb1x00_adc_enable(ucb);
137 +       value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
138 +       ucb1x00_adc_disable(ucb);
139 +       gpio_set_value(bat->gpio_temp, 0);
140 +       mutex_unlock(&bat_lock);
141 +
142 +       value = value * 10000 / bat->adc_temp_divider;
143 +
144 +       return value;
145 +}
146 +
147 +static int collie_bat_get_property(struct power_supply *psy,
148 +                           enum power_supply_property psp,
149 +                           union power_supply_propval *val)
150 +{
151 +       int ret = 0;
152 +       struct collie_bat *bat = container_of(psy, struct collie_bat, psy);
153 +
154 +       if (bat->is_present && !bat->is_present(bat)
155 +                       && psp != POWER_SUPPLY_PROP_PRESENT) {
156 +               return -ENODEV;
157 +       }
158 +
159 +       switch (psp) {
160 +       case POWER_SUPPLY_PROP_STATUS:
161 +               val->intval = bat->status;
162 +               break;
163 +       case POWER_SUPPLY_PROP_TECHNOLOGY:
164 +               val->intval = bat->technology;
165 +               break;
166 +       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
167 +               val->intval = collie_read_bat(bat);
168 +               break;
169 +       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
170 +               if (bat->full_chrg == -1)
171 +                       val->intval = bat->bat_max;
172 +               else
173 +                       val->intval = bat->full_chrg;
174 +               break;
175 +       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
176 +               val->intval = bat->bat_max;
177 +               break;
178 +       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
179 +               val->intval = bat->bat_min;
180 +               break;
181 +       case POWER_SUPPLY_PROP_TEMP:
182 +               val->intval = collie_read_temp(bat);
183 +               break;
184 +       case POWER_SUPPLY_PROP_PRESENT:
185 +               val->intval = bat->is_present ? bat->is_present(bat) : 1;
186 +               break;
187 +       default:
188 +               ret = -EINVAL;
189 +               break;
190 +       }
191 +       return ret;
192 +}
193 +
194 +static void collie_bat_external_power_changed(struct power_supply *psy)
195 +{
196 +       schedule_work(&bat_work);
197 +}
198 +
199 +static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
200 +{
201 +       pr_info("collie_bat_gpio irq: %d\n", gpio_get_value(irq_to_gpio(irq)));
202 +       schedule_work(&bat_work);
203 +       return IRQ_HANDLED;
204 +}
205 +
206 +static void collie_bat_update(struct collie_bat *bat)
207 +{
208 +       int old;
209 +       struct power_supply *psy = &bat->psy;
210 +
211 +       mutex_lock(&bat->work_lock);
212 +
213 +       old = bat->status;
214 +
215 +       if (bat->is_present && !bat->is_present(bat)) {
216 +               printk(KERN_NOTICE "%s not present\n", psy->name);
217 +               bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
218 +               bat->full_chrg = -1;
219 +       } else if (power_supply_am_i_supplied(psy)) {
220 +               if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
221 +                       gpio_set_value(bat->gpio_charge_on, 1);
222 +                       mdelay(15);
223 +               }
224 +
225 +               if (gpio_get_value(bat->gpio_full)) {
226 +                       if (old == POWER_SUPPLY_STATUS_CHARGING ||
227 +                                       bat->full_chrg == -1)
228 +                               bat->full_chrg = collie_read_bat(bat);
229 +
230 +                       gpio_set_value(bat->gpio_charge_on, 0);
231 +                       bat->status = POWER_SUPPLY_STATUS_FULL;
232 +               } else {
233 +                       gpio_set_value(bat->gpio_charge_on, 1);
234 +                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
235 +               }
236 +       } else {
237 +               gpio_set_value(bat->gpio_charge_on, 0);
238 +               bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
239 +       }
240 +
241 +       if (old != bat->status)
242 +               power_supply_changed(psy);
243 +
244 +       mutex_unlock(&bat->work_lock);
245 +}
246 +
247 +static void collie_bat_work(struct work_struct *work)
248 +{
249 +       collie_bat_update(&collie_bat_main);
250 +}
251 +
252 +
253 +static enum power_supply_property collie_bat_main_props[] = {
254 +       POWER_SUPPLY_PROP_STATUS,
255 +       POWER_SUPPLY_PROP_TECHNOLOGY,
256 +       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
257 +       POWER_SUPPLY_PROP_VOLTAGE_NOW,
258 +       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
259 +       POWER_SUPPLY_PROP_VOLTAGE_MAX,
260 +       POWER_SUPPLY_PROP_PRESENT,
261 +       POWER_SUPPLY_PROP_TEMP,
262 +};
263 +
264 +static enum power_supply_property collie_bat_bu_props[] = {
265 +       POWER_SUPPLY_PROP_STATUS,
266 +       POWER_SUPPLY_PROP_TECHNOLOGY,
267 +       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
268 +       POWER_SUPPLY_PROP_VOLTAGE_NOW,
269 +       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
270 +       POWER_SUPPLY_PROP_VOLTAGE_MAX,
271 +       POWER_SUPPLY_PROP_PRESENT,
272 +};
273 +
274 +static struct collie_bat collie_bat_main = {
275 +       .status = POWER_SUPPLY_STATUS_DISCHARGING,
276 +       .full_chrg = -1,
277 +       .psy = {
278 +               .name           = "main-battery",
279 +               .type           = POWER_SUPPLY_TYPE_BATTERY,
280 +               .properties     = collie_bat_main_props,
281 +               .num_properties = ARRAY_SIZE(collie_bat_main_props),
282 +               .get_property   = collie_bat_get_property,
283 +               .external_power_changed = collie_bat_external_power_changed,
284 +               .use_for_apm    = 1,
285 +       },
286 +
287 +       .gpio_full = COLLIE_GPIO_CO,
288 +       .gpio_charge_on = COLLIE_GPIO_CHARGE_ON,
289 +
290 +       .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
291 +
292 +       .gpio_bat = COLLIE_GPIO_MBAT_ON,
293 +       .adc_bat = UCB_ADC_INP_AD1,
294 +       .adc_bat_divider = 155,
295 +       .bat_max = 4310000,
296 +       .bat_min = 1551 * 1000000 / 414,
297 +
298 +       .gpio_temp = COLLIE_GPIO_TMP_ON,
299 +       .adc_temp = UCB_ADC_INP_AD0,
300 +       .adc_temp_divider = 10000,
301 +};
302 +
303 +static struct collie_bat collie_bat_bu = {
304 +       .status = POWER_SUPPLY_STATUS_UNKNOWN,
305 +       .full_chrg = -1,
306 +
307 +       .psy = {
308 +               .name           = "backup-battery",
309 +               .type           = POWER_SUPPLY_TYPE_BATTERY,
310 +               .properties     = collie_bat_bu_props,
311 +               .num_properties = ARRAY_SIZE(collie_bat_bu_props),
312 +               .get_property   = collie_bat_get_property,
313 +               .external_power_changed = collie_bat_external_power_changed,
314 +       },
315 +
316 +       .gpio_full = -1,
317 +       .gpio_charge_on = -1,
318 +
319 +       .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
320 +
321 +       .gpio_bat = COLLIE_GPIO_BBAT_ON,
322 +       .adc_bat = UCB_ADC_INP_AD1,
323 +       .adc_bat_divider = 155,
324 +       .bat_max = 3000000,
325 +       .bat_min = 1900000,
326 +
327 +       .gpio_temp = -1,
328 +       .adc_temp = -1,
329 +       .adc_temp_divider = -1,
330 +};
331 +
332 +static struct {
333 +       int gpio;
334 +       char *name;
335 +       bool output;
336 +       int value;
337 +} gpios[] = {
338 +       { COLLIE_GPIO_CO,               "main battery full",    0, 0 },
339 +       { COLLIE_GPIO_MAIN_BAT_LOW,     "main battery low",     0, 0 },
340 +       { COLLIE_GPIO_CHARGE_ON,        "main charge on",       1, 0 },
341 +       { COLLIE_GPIO_MBAT_ON,          "main battery",         1, 0 },
342 +       { COLLIE_GPIO_TMP_ON,           "main battery temp",    1, 0 },
343 +       { COLLIE_GPIO_BBAT_ON,          "backup battery",       1, 0 },
344 +};
345 +
346 +#ifdef CONFIG_PM
347 +static int collie_bat_suspend(struct ucb1x00_dev *dev, pm_message_t state)
348 +{
349 +       /* flush all pending status updates */
350 +       flush_scheduled_work();
351 +       return 0;
352 +}
353 +
354 +static int collie_bat_resume(struct ucb1x00_dev *dev)
355 +{
356 +       /* things may have changed while we were away */
357 +       schedule_work(&bat_work);
358 +       return 0;
359 +}
360 +#else
361 +#define collie_bat_suspend NULL
362 +#define collie_bat_resume NULL
363 +#endif
364 +
365 +static int __devinit collie_bat_probe(struct ucb1x00_dev *dev)
366 +{
367 +       int ret;
368 +       int i;
369 +
370 +       if (!machine_is_collie())
371 +               return -ENODEV;
372 +
373 +       ucb = dev->ucb;
374 +
375 +       for (i = 0; i < ARRAY_SIZE(gpios); i++) {
376 +               ret = gpio_request(gpios[i].gpio, gpios[i].name);
377 +               if (ret) {
378 +                       i--;
379 +                       goto err_gpio;
380 +               }
381 +
382 +               if (gpios[i].output)
383 +                       ret = gpio_direction_output(gpios[i].gpio,
384 +                                       gpios[i].value);
385 +               else
386 +                       ret = gpio_direction_input(gpios[i].gpio);
387 +
388 +               if (ret)
389 +                       goto err_gpio;
390 +       }
391 +
392 +       mutex_init(&collie_bat_main.work_lock);
393 +
394 +       INIT_WORK(&bat_work, collie_bat_work);
395 +
396 +       ret = power_supply_register(&dev->ucb->dev, &collie_bat_main.psy);
397 +       if (ret)
398 +               goto err_psy_reg_main;
399 +       ret = power_supply_register(&dev->ucb->dev, &collie_bat_bu.psy);
400 +       if (ret)
401 +               goto err_psy_reg_bu;
402 +
403 +       ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO),
404 +                               collie_bat_gpio_isr,
405 +                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
406 +                               "main full", &collie_bat_main);
407 +       if (!ret) {
408 +               schedule_work(&bat_work);
409 +               return 0;
410 +       }
411 +       power_supply_unregister(&collie_bat_bu.psy);
412 +err_psy_reg_bu:
413 +       power_supply_unregister(&collie_bat_main.psy);
414 +err_psy_reg_main:
415 +
416 +       /* see comment in collie_bat_remove */
417 +       flush_scheduled_work();
418 +
419 +       i--;
420 +err_gpio:
421 +       for (; i >= 0; i--)
422 +               gpio_free(gpios[i].gpio);
423 +
424 +       return ret;
425 +}
426 +
427 +static void __devexit collie_bat_remove(struct ucb1x00_dev *dev)
428 +{
429 +       int i;
430 +
431 +       free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
432 +
433 +       power_supply_unregister(&collie_bat_bu.psy);
434 +       power_supply_unregister(&collie_bat_main.psy);
435 +
436 +       /*
437 +        * now flush all pending work.
438 +        * we won't get any more schedules, since all
439 +        * sources (isr and external_power_changed)
440 +        * are unregistered now.
441 +        */
442 +       flush_scheduled_work();
443 +
444 +       for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--)
445 +               gpio_free(gpios[i].gpio);
446 +}
447 +
448 +static struct ucb1x00_driver collie_bat_driver = {
449 +       .add            = collie_bat_probe,
450 +       .remove         = __devexit_p(collie_bat_remove),
451 +       .suspend        = collie_bat_suspend,
452 +       .resume         = collie_bat_resume,
453 +};
454 +
455 +static int __init collie_bat_init(void)
456 +{
457 +       return ucb1x00_register_driver(&collie_bat_driver);
458 +}
459 +
460 +static void __exit collie_bat_exit(void)
461 +{
462 +       ucb1x00_unregister_driver(&collie_bat_driver);
463 +}
464 +
465 +module_init(collie_bat_init);
466 +module_exit(collie_bat_exit);
467 +
468 +MODULE_LICENSE("GPL");
469 +MODULE_AUTHOR("Thomas Kunze");
470 +MODULE_DESCRIPTION("Collie battery driver");
471 -- 
472 1.5.6.5
473