2 arch/arm/mach-omap2/board-n8x0.c | 13 +
3 drivers/cbus/Kconfig | 12 +
4 drivers/cbus/Makefile | 3
5 drivers/cbus/lipocharge.c | 63 ++++++
6 drivers/cbus/lipocharge.h | 50 ++++
7 drivers/cbus/n810bm_main.c | 397 +++++++++++++++++++++++++++++++++++++++
8 drivers/cbus/retu.c | 4
9 drivers/cbus/retu.h | 3
10 drivers/cbus/tahvo.h | 6
11 9 files changed, 548 insertions(+), 3 deletions(-)
13 Index: linux-2.6.37-rc1/drivers/cbus/Kconfig
14 ===================================================================
15 --- linux-2.6.37-rc1.orig/drivers/cbus/Kconfig 2010-11-06 17:28:21.821000000 +0100
16 +++ linux-2.6.37-rc1/drivers/cbus/Kconfig 2010-11-06 17:28:21.872000001 +0100
18 to Retu/Vilma. Detection state and events are exposed through
22 + depends on CBUS_RETU && CBUS_TAHVO
23 + tristate "Nokia n810 battery management"
25 + Nokia n810 device battery management.
27 + WARNING: This driver is based on reverse engineered information.
28 + It is possibly dangerous to use this software.
29 + Use this software at your own risk!
34 Index: linux-2.6.37-rc1/drivers/cbus/Makefile
35 ===================================================================
36 --- linux-2.6.37-rc1.orig/drivers/cbus/Makefile 2010-11-06 17:28:21.812000054 +0100
37 +++ linux-2.6.37-rc1/drivers/cbus/Makefile 2010-11-06 17:28:21.872000001 +0100
39 obj-$(CONFIG_CBUS_TAHVO_USER) += tahvo-user.o
40 obj-$(CONFIG_CBUS_RETU_USER) += retu-user.o
41 obj-$(CONFIG_CBUS_RETU_HEADSET) += retu-headset.o
42 +n810bm-y += n810bm_main.o
43 +n810bm-y += lipocharge.o
44 +obj-$(CONFIG_N810BM) += n810bm.o
45 Index: linux-2.6.37-rc1/drivers/cbus/n810bm_main.c
46 ===================================================================
47 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
48 +++ linux-2.6.37-rc1/drivers/cbus/n810bm_main.c 2010-11-06 17:28:21.872000001 +0100
51 + * Nokia n810 battery management
53 + * WARNING: This driver is based on reverse engineered information.
54 + * It is possibly dangerous to use this software.
55 + * Use this software at your own risk!
57 + * Copyright (c) 2010 Michael Buesch <mb@bu3sch.de>
59 + * This program is free software; you can redistribute it and/or
60 + * modify it under the terms of the GNU General Public License
61 + * as published by the Free Software Foundation; either version 2
62 + * of the License, or (at your option) any later version.
64 + * This program is distributed in the hope that it will be useful,
65 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
66 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
67 + * GNU General Public License for more details.
70 +#include <linux/module.h>
71 +#include <linux/device.h>
72 +#include <linux/platform_device.h>
73 +#include <linux/slab.h>
74 +#include <linux/spinlock.h>
75 +#include <linux/timer.h>
76 +#include <linux/reboot.h>
80 +#include "lipocharge.h"
83 +#define N810BM_CHECK_INTERVAL (HZ * 5)
84 +#define N810BM_MIN_VOLTAGE_THRES 3300 /* Absolute minimum voltage threshold */
87 +/* Battery related retu ADC channels */
88 +#define RETU_ADC_BSI 0x01 /* Battery Size Indicator */
89 +#define RETU_ADC_BATTVOLT 0x08 /* Battery voltage measurement */
92 + * The battery size indicator ADC measures the resistance between
93 + * the battery BSI pin and ground. This is used to detect the battery
94 + * capacity, as the BSI resistor is related to capacity.
96 + * Manually measured lookup table.
97 + * Hard to measure, thus not very accurate.
99 + * Resistance | ADC value
100 + * ========================
107 +/* RETU_ADC_BATTVOLT
108 + * Manually measured lookup table.
109 + * Hard to measure, thus not very accurate.
111 + * Voltage | ADC value
112 + * =====================
131 +enum n810bm_capacity {
132 + N810BM_CAP_UNKNOWN = 0,
133 + N810BM_CAP_1500MAH = 1500, /* 1500 mAh battery */
137 + struct platform_device *pdev;
139 + enum n810bm_capacity capacity;
140 + struct timer_list check_timer;
142 + struct lipocharge *charger;
148 +static NORET_TYPE void n810bm_emergency(struct n810bm *bm, const char *message) ATTRIB_NORET;
149 +static void n810bm_emergency(struct n810bm *bm, const char *message)
151 + printk(KERN_EMERG "n810 battery management fatal fault: %s\n", message);
152 + /* Force a hard shutdown. */
153 + machine_power_off();
154 + panic("n810bm: Failed to halt machine in emergency state\n");
158 +static u16 retu_read(struct n810bm *bm, unsigned int reg)
161 + unsigned long flags;
163 + spin_lock_irqsave(&retu_lock, flags);
164 + ret = retu_read_reg(reg);
165 + spin_unlock_irqrestore(&retu_lock, flags);
166 + if (ret < 0 || ret > 0xFFFF)
167 + n810bm_emergency(bm, "retu_read");
173 +static void retu_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
176 + unsigned long flags;
179 + spin_lock_irqsave(&retu_lock, flags);
181 + ret = retu_read_reg(reg);
182 + if (ret < 0 || ret > 0xFFFF)
189 + ret = retu_write_reg(reg, value);
192 + spin_unlock_irqrestore(&retu_lock, flags);
197 + spin_unlock_irqrestore(&retu_lock, flags);
198 + n810bm_emergency(bm, "retu_maskset");
201 +static inline void retu_write(struct n810bm *bm, unsigned int reg, u16 value)
203 + return retu_maskset(bm, reg, 0xFFFF, value);
206 +static int retu_adc_average(struct n810bm *bm, unsigned int chan,
207 + unsigned int nr_passes)
209 + unsigned int i, value = 0;
212 + if (WARN_ON(!nr_passes))
214 + for (i = 0; i < nr_passes; i++) {
215 + ret = retu_read_adc(chan);
220 + value /= nr_passes;
225 +/* Measure the battery voltage. Returns the value in mV (or negative value on error). */
226 +static int n810bm_measure_batt_voltage(struct n810bm *bm)
230 + const unsigned int scale = 1000;
232 + adc = retu_adc_average(bm, RETU_ADC_BATTVOLT, 5);
237 + mv = 2800 + ((adc - 0x37) * (((4200 - 2800) * scale) / (0x236 - 0x37))) / scale;
242 +/* Read the battery capacity via BSI pin. */
243 +static enum n810bm_capacity n810bm_read_batt_capacity(struct n810bm *bm)
246 + const unsigned int hyst = 20;
248 + adc = retu_adc_average(bm, RETU_ADC_BSI, 5);
250 + dev_err(&bm->pdev->dev, "Failed to read BSI ADC");
251 + return N810BM_CAP_UNKNOWN;
254 + if (adc >= 0x3B5 - hyst && adc <= 0x3B5 + hyst)
255 + return N810BM_CAP_1500MAH;
257 + dev_err(&bm->pdev->dev, "Capacity indicator 0x%X unknown", adc);
259 + return N810BM_CAP_UNKNOWN;
262 +/* Convert a battery voltage (in mV) to percentage. */
263 +static unsigned int n810bm_mvolt2percent(unsigned int mv)
265 + const unsigned int minv = 3700;
266 + const unsigned int maxv = 4150;
267 + unsigned int percent;
269 + mv = clamp(mv, minv, maxv);
270 + percent = (mv - minv) * 100 / (maxv - minv);
275 +static void n810bm_check_timer(unsigned long data)
277 + struct n810bm *bm = (struct n810bm *)data;
278 + unsigned long flags;
281 + spin_lock_irqsave(&bm->lock, flags);
283 + mv = n810bm_measure_batt_voltage(bm);
285 + n810bm_emergency(bm, "check timer: Failed to measure");
286 + if (mv < N810BM_MIN_VOLTAGE_THRES)
287 + n810bm_emergency(bm, "check timer: Minimum voltage threshold reached");
289 + mod_timer(&bm->check_timer, round_jiffies(jiffies + N810BM_CHECK_INTERVAL));
290 + spin_unlock_irqrestore(&bm->lock, flags);
295 +static void n810bm_adc_irq_handler(unsigned long data)
297 + struct n810bm *bm = (struct n810bm *)data;
299 + retu_ack_irq(RETU_INT_ADCS);
301 +printk("n810bm: ADC timer triggered\n");
304 +static ssize_t n810bm_attr_charge_show(struct device *dev,
305 + struct device_attribute *attr,
308 + struct platform_device *pdev = to_platform_device(dev);
309 + struct n810bm *bm = platform_get_drvdata(pdev);
314 + spin_lock_irq(&bm->lock);
315 + millivolt = n810bm_measure_batt_voltage(bm);
316 + if (millivolt >= 0) {
317 + count = snprintf(buf, PAGE_SIZE, "%u\n",
318 + n810bm_mvolt2percent(millivolt));
321 + spin_unlock_irq(&bm->lock);
323 + return err ? err : count;
325 +static DEVICE_ATTR(batt_charge, 0444, n810bm_attr_charge_show, NULL);
327 +static ssize_t n810bm_attr_capacity_show(struct device *dev,
328 + struct device_attribute *attr,
331 + struct platform_device *pdev = to_platform_device(dev);
332 + struct n810bm *bm = platform_get_drvdata(pdev);
335 + spin_lock_irq(&bm->lock);
336 + count = snprintf(buf, PAGE_SIZE, "%u\n",
337 + (unsigned int)bm->capacity);
338 + spin_unlock_irq(&bm->lock);
342 +static DEVICE_ATTR(batt_capacity, 0444, n810bm_attr_capacity_show, NULL);
344 +static void n810bm_hw_exit(struct n810bm *bm)
346 + retu_write(bm, RETU_REG_ADCSCR, 0);
349 +static int n810bm_hw_init(struct n810bm *bm)
351 + retu_write(bm, RETU_REG_ADCSCR, 0);
353 + bm->capacity = n810bm_read_batt_capacity(bm);
354 + if (bm->capacity == N810BM_CAP_UNKNOWN) {
355 + dev_err(&bm->pdev->dev, "Unknown battery detected");
358 + dev_info(&bm->pdev->dev, "Detected %u mAh battery\n",
359 + (unsigned int)bm->capacity);
364 +static int __devinit n810bm_probe(struct platform_device *pdev)
369 + bm = kzalloc(sizeof(*bm), GFP_KERNEL);
373 + platform_set_drvdata(pdev, bm);
374 + spin_lock_init(&bm->lock);
375 + setup_timer(&bm->check_timer, n810bm_check_timer, (unsigned long)bm);
377 + err = n810bm_hw_init(bm);
380 + err = device_create_file(&pdev->dev, &dev_attr_batt_charge);
383 + err = device_create_file(&pdev->dev, &dev_attr_batt_capacity);
385 + goto err_rem_charge;
386 + err = retu_request_irq(RETU_INT_ADCS, n810bm_adc_irq_handler,
387 + (unsigned long)bm, "n810bm");
391 + mod_timer(&bm->check_timer, round_jiffies(jiffies + N810BM_CHECK_INTERVAL));
393 + dev_info(&pdev->dev, "Battery management initialized");
398 + device_remove_file(&pdev->dev, &dev_attr_batt_capacity);
400 + device_remove_file(&pdev->dev, &dev_attr_batt_charge);
402 + n810bm_hw_exit(bm);
405 + platform_set_drvdata(pdev, NULL);
409 +static int __devexit n810bm_remove(struct platform_device *pdev)
411 + struct n810bm *bm = platform_get_drvdata(pdev);
413 + retu_free_irq(RETU_INT_ADCS);
414 + del_timer_sync(&bm->check_timer);
415 + device_remove_file(&pdev->dev, &dev_attr_batt_capacity);
416 + device_remove_file(&pdev->dev, &dev_attr_batt_charge);
417 + n810bm_hw_exit(bm);
420 + platform_set_drvdata(pdev, NULL);
425 +static struct platform_driver n810bm_driver = {
426 + .remove = __devexit_p(n810bm_remove),
432 +static int __init n810bm_modinit(void)
434 + return platform_driver_probe(&n810bm_driver, n810bm_probe);
436 +module_init(n810bm_modinit);
438 +static void __exit n810bm_modexit(void)
440 + platform_driver_unregister(&n810bm_driver);
442 +module_exit(n810bm_modexit);
444 +MODULE_DESCRIPTION("Nokia n810 battery management");
445 +MODULE_LICENSE("GPL");
446 +MODULE_AUTHOR("Michael Buesch");
447 Index: linux-2.6.37-rc1/drivers/cbus/retu.c
448 ===================================================================
449 --- linux-2.6.37-rc1.orig/drivers/cbus/retu.c 2010-11-06 17:28:21.812000054 +0100
450 +++ linux-2.6.37-rc1/drivers/cbus/retu.c 2010-11-06 17:28:21.872000001 +0100
453 * This function writes a value to the specified register
455 -void retu_write_reg(int reg, u16 val)
456 +int retu_write_reg(int reg, u16 val)
458 BUG_ON(!retu_initialized);
459 - cbus_write_reg(cbus_host, RETU_ID, reg, val);
460 + return cbus_write_reg(cbus_host, RETU_ID, reg, val);
463 void retu_set_clear_reg_bits(int reg, u16 set, u16 clear)
464 Index: linux-2.6.37-rc1/drivers/cbus/retu.h
465 ===================================================================
466 --- linux-2.6.37-rc1.orig/drivers/cbus/retu.h 2010-11-06 17:28:21.812000054 +0100
467 +++ linux-2.6.37-rc1/drivers/cbus/retu.h 2010-11-06 17:28:21.872000001 +0100
469 #define RETU_REG_CC2 0x0e /* Common control register 2 */
470 #define RETU_REG_CTRL_CLR 0x0f /* Regulator clear register */
471 #define RETU_REG_CTRL_SET 0x10 /* Regulator set register */
472 +#define RETU_REG_UNK1 0x14 /* 0x1000 is set when charger is plugged in */
473 #define RETU_REG_STATUS 0x16 /* Status register */
474 #define RETU_REG_WATCHDOG 0x17 /* Watchdog register */
475 #define RETU_REG_AUDTXR 0x18 /* Audio Codec Tx register */
477 #define MAX_RETU_IRQ_HANDLERS 16
479 int retu_read_reg(int reg);
480 -void retu_write_reg(int reg, u16 val);
481 +int retu_write_reg(int reg, u16 val);
482 void retu_set_clear_reg_bits(int reg, u16 set, u16 clear);
483 int retu_read_adc(int channel);
484 int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
485 Index: linux-2.6.37-rc1/arch/arm/mach-omap2/board-n8x0.c
486 ===================================================================
487 --- linux-2.6.37-rc1.orig/arch/arm/mach-omap2/board-n8x0.c 2010-11-06 17:28:21.796000362 +0100
488 +++ linux-2.6.37-rc1/arch/arm/mach-omap2/board-n8x0.c 2010-11-06 17:28:21.873000001 +0100
490 ARRAY_SIZE(n8x0_gpio_switches));
493 +static struct platform_device n810_bm_device = {
498 +static void __init n810_bm_init(void)
500 + if (platform_device_register(&n810_bm_device))
504 static void __init n8x0_init_machine(void)
506 omap2420_mux_init(board_mux, OMAP_PACKAGE_ZAC);
515 MACHINE_START(NOKIA_N800, "Nokia N800")
516 Index: linux-2.6.37-rc1/drivers/cbus/lipocharge.c
517 ===================================================================
518 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
519 +++ linux-2.6.37-rc1/drivers/cbus/lipocharge.c 2010-11-06 17:28:21.873000001 +0100
522 + * Generic LIPO battery charger
524 + * Copyright (c) 2010 Michael Buesch <mb@bu3sch.de>
526 + * This program is free software; you can redistribute it and/or
527 + * modify it under the terms of the GNU General Public License
528 + * as published by the Free Software Foundation; either version 2
529 + * of the License, or (at your option) any later version.
531 + * This program is distributed in the hope that it will be useful,
532 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
533 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
534 + * GNU General Public License for more details.
537 +#include "lipocharge.h"
539 +#include <linux/slab.h>
542 +static void lipocharge_timer(unsigned long data)
544 + struct lipocharge *c = (struct lipocharge *)data;
546 + spin_lock(&c->lock);
548 + spin_unlock(&c->lock);
551 +struct lipocharge * lipocharge_alloc(gfp_t gfp)
553 + struct lipocharge *c;
555 + c = kzalloc(sizeof(*c), gfp);
558 + spin_lock_init(&c->lock);
559 + setup_timer(&c->timer, lipocharge_timer, (unsigned long)c);
564 +void lipocharge_free(struct lipocharge *c)
569 +int lipocharge_start(struct lipocharge *c)
571 + if (!c->set_current || !c->get_voltage ||
572 + !c->finished || !c->emergency)
574 + if (!c->top_voltage || c->top_voltage > 4200)
579 +void lipocharge_stop(struct lipocharge *c)
581 + del_timer_sync(&c->timer);
584 Index: linux-2.6.37-rc1/drivers/cbus/lipocharge.h
585 ===================================================================
586 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
587 +++ linux-2.6.37-rc1/drivers/cbus/lipocharge.h 2010-11-06 17:28:21.873000001 +0100
589 +#ifndef LIPOCHARGE_H_
590 +#define LIPOCHARGE_H_
592 +#include <linux/timer.h>
593 +#include <linux/spinlock.h>
596 +#define LIPORATE(a,b) (((a) * 1000) + (b))
597 +#define LIPORATE_1C LIPORATE(1,0) /* 1C */
598 +#define LIPORATE_p8C LIPORATE(0,8) /* 0.8C */
600 +/** struct lipocharge - A generic LIPO charger
602 + * @capacity: Battery capacity in mAh.
603 + * @rate: Charge rate.
604 + * @top_voltage: Fully charged voltage, in mV.
606 + * @set_current: Set the charge current, in mA.
607 + * @get_voltage: Get the battery voltage, in mV.
609 + * @emergency: Something went wrong. Force shutdown.
611 + * @priv: opaque pointer.
615 + unsigned int capacity;
617 + unsigned int top_voltage;
619 + int (*set_current)(struct lipocharge *c, unsigned int ma);
620 + int (*get_voltage)(struct lipocharge *c, unsigned int *mv);
622 + void (*finished)(struct lipocharge *c);
623 + void (*emergency)(struct lipocharge *c);
629 + struct timer_list timer;
632 +struct lipocharge * lipocharge_alloc(gfp_t gfp);
633 +void lipocharge_free(struct lipocharge *c);
635 +int lipocharge_start(struct lipocharge *c);
636 +void lipocharge_stop(struct lipocharge *c);
638 +#endif /* LIPOCHARGE_H_ */
639 Index: linux-2.6.37-rc1/drivers/cbus/tahvo.h
640 ===================================================================
641 --- linux-2.6.37-rc1.orig/drivers/cbus/tahvo.h 2010-11-06 17:28:21.813000041 +0100
642 +++ linux-2.6.37-rc1/drivers/cbus/tahvo.h 2010-11-06 17:28:21.873000001 +0100
644 #define TAHVO_REG_IDR 0x01 /* Interrupt ID */
645 #define TAHVO_REG_IDSR 0x02 /* Interrupt status */
646 #define TAHVO_REG_IMR 0x03 /* Interrupt mask */
647 +#define TAHVO_REG_CHGCURR 0x04 /* Charge current control (8-bit) */
648 #define TAHVO_REG_LEDPWMR 0x05 /* LED PWM */
649 #define TAHVO_REG_USBR 0x06 /* USB control */
650 +#define TAHVO_REG_CHGCTL 0x08 /* Charge control register */
651 +#define TAHVO_REG_CHGCTL_EN 0x0001 /* Global charge enable */
652 +#define TAHVO_REG_CHGCTL2 0x0c /* Charge control register 2 */
653 +#define TAHVO_REG_BATCURR 0x0d /* Battery (dis)charge current (signed 16-bit) */
655 #define TAHVO_REG_MAX 0x0d
657 /* Interrupt sources */