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
6 This driver is based on tosa_battery.c.
10 drivers/power/Makefile
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
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.
26 +config BATTERY_COLLIE
27 + tristate "Sharp SL-5500 (collie) battery"
28 + depends on SA1100_COLLIE && MCP_UCB1200
30 + Say Y to enable support for the battery on the Sharp Zaurus
31 + SL-5500 (collie) models.
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
49 index 0000000..039f41a
51 +++ b/drivers/power/collie_battery.c
54 + * Battery and Power Management code for the Sharp SL-5x00
56 + * Copyright (C) 2009 Thomas Kunze
58 + * based on tosa_battery.c
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.
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>
74 +#include <asm/mach/sharpsl_param.h>
75 +#include <asm/mach-types.h>
76 +#include <mach/collie.h>
78 +static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
79 +static struct work_struct bat_work;
80 +static struct ucb1x00 *ucb;
84 + struct power_supply psy;
87 + struct mutex work_lock; /* protects data */
89 + bool (*is_present)(struct collie_bat *bat);
97 + int adc_bat_divider;
103 + int adc_temp_divider;
106 +static struct collie_bat collie_bat_main;
108 +static unsigned long collie_read_bat(struct collie_bat *bat)
110 + unsigned long value = 0;
112 + if (bat->gpio_bat < 0 || bat->adc_bat < 0)
114 + mutex_lock(&bat_lock);
115 + gpio_set_value(bat->gpio_bat, 1);
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;
127 +static unsigned long collie_read_temp(struct collie_bat *bat)
129 + unsigned long value = 0;
130 + if (bat->gpio_temp < 0 || bat->adc_temp < 0)
133 + mutex_lock(&bat_lock);
134 + gpio_set_value(bat->gpio_temp, 1);
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);
142 + value = value * 10000 / bat->adc_temp_divider;
147 +static int collie_bat_get_property(struct power_supply *psy,
148 + enum power_supply_property psp,
149 + union power_supply_propval *val)
152 + struct collie_bat *bat = container_of(psy, struct collie_bat, psy);
154 + if (bat->is_present && !bat->is_present(bat)
155 + && psp != POWER_SUPPLY_PROP_PRESENT) {
160 + case POWER_SUPPLY_PROP_STATUS:
161 + val->intval = bat->status;
163 + case POWER_SUPPLY_PROP_TECHNOLOGY:
164 + val->intval = bat->technology;
166 + case POWER_SUPPLY_PROP_VOLTAGE_NOW:
167 + val->intval = collie_read_bat(bat);
169 + case POWER_SUPPLY_PROP_VOLTAGE_MAX:
170 + if (bat->full_chrg == -1)
171 + val->intval = bat->bat_max;
173 + val->intval = bat->full_chrg;
175 + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
176 + val->intval = bat->bat_max;
178 + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
179 + val->intval = bat->bat_min;
181 + case POWER_SUPPLY_PROP_TEMP:
182 + val->intval = collie_read_temp(bat);
184 + case POWER_SUPPLY_PROP_PRESENT:
185 + val->intval = bat->is_present ? bat->is_present(bat) : 1;
194 +static void collie_bat_external_power_changed(struct power_supply *psy)
196 + schedule_work(&bat_work);
199 +static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
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;
206 +static void collie_bat_update(struct collie_bat *bat)
209 + struct power_supply *psy = &bat->psy;
211 + mutex_lock(&bat->work_lock);
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);
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);
230 + gpio_set_value(bat->gpio_charge_on, 0);
231 + bat->status = POWER_SUPPLY_STATUS_FULL;
233 + gpio_set_value(bat->gpio_charge_on, 1);
234 + bat->status = POWER_SUPPLY_STATUS_CHARGING;
237 + gpio_set_value(bat->gpio_charge_on, 0);
238 + bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
241 + if (old != bat->status)
242 + power_supply_changed(psy);
244 + mutex_unlock(&bat->work_lock);
247 +static void collie_bat_work(struct work_struct *work)
249 + collie_bat_update(&collie_bat_main);
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,
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,
274 +static struct collie_bat collie_bat_main = {
275 + .status = POWER_SUPPLY_STATUS_DISCHARGING,
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,
287 + .gpio_full = COLLIE_GPIO_CO,
288 + .gpio_charge_on = COLLIE_GPIO_CHARGE_ON,
290 + .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
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,
298 + .gpio_temp = COLLIE_GPIO_TMP_ON,
299 + .adc_temp = UCB_ADC_INP_AD0,
300 + .adc_temp_divider = 10000,
303 +static struct collie_bat collie_bat_bu = {
304 + .status = POWER_SUPPLY_STATUS_UNKNOWN,
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,
317 + .gpio_charge_on = -1,
319 + .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
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,
329 + .adc_temp_divider = -1,
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 },
347 +static int collie_bat_suspend(struct ucb1x00_dev *dev, pm_message_t state)
349 + /* flush all pending status updates */
350 + flush_scheduled_work();
354 +static int collie_bat_resume(struct ucb1x00_dev *dev)
356 + /* things may have changed while we were away */
357 + schedule_work(&bat_work);
361 +#define collie_bat_suspend NULL
362 +#define collie_bat_resume NULL
365 +static int __devinit collie_bat_probe(struct ucb1x00_dev *dev)
370 + if (!machine_is_collie())
375 + for (i = 0; i < ARRAY_SIZE(gpios); i++) {
376 + ret = gpio_request(gpios[i].gpio, gpios[i].name);
382 + if (gpios[i].output)
383 + ret = gpio_direction_output(gpios[i].gpio,
386 + ret = gpio_direction_input(gpios[i].gpio);
392 + mutex_init(&collie_bat_main.work_lock);
394 + INIT_WORK(&bat_work, collie_bat_work);
396 + ret = power_supply_register(&dev->ucb->dev, &collie_bat_main.psy);
398 + goto err_psy_reg_main;
399 + ret = power_supply_register(&dev->ucb->dev, &collie_bat_bu.psy);
401 + goto err_psy_reg_bu;
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);
408 + schedule_work(&bat_work);
411 + power_supply_unregister(&collie_bat_bu.psy);
413 + power_supply_unregister(&collie_bat_main.psy);
416 + /* see comment in collie_bat_remove */
417 + flush_scheduled_work();
421 + for (; i >= 0; i--)
422 + gpio_free(gpios[i].gpio);
427 +static void __devexit collie_bat_remove(struct ucb1x00_dev *dev)
431 + free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
433 + power_supply_unregister(&collie_bat_bu.psy);
434 + power_supply_unregister(&collie_bat_main.psy);
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.
442 + flush_scheduled_work();
444 + for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--)
445 + gpio_free(gpios[i].gpio);
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,
455 +static int __init collie_bat_init(void)
457 + return ucb1x00_register_driver(&collie_bat_driver);
460 +static void __exit collie_bat_exit(void)
462 + ucb1x00_unregister_driver(&collie_bat_driver);
465 +module_init(collie_bat_init);
466 +module_exit(collie_bat_exit);
468 +MODULE_LICENSE("GPL");
469 +MODULE_AUTHOR("Thomas Kunze");
470 +MODULE_DESCRIPTION("Collie battery driver");