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.1/drivers/cbus/Kconfig
14 ===================================================================
15 --- linux-2.6.37.1.orig/drivers/cbus/Kconfig 2011-02-19 20:26:01.282338290 +0100
16 +++ linux-2.6.37.1/drivers/cbus/Kconfig 2011-02-19 20:26:01.526318768 +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.
30 Index: linux-2.6.37.1/drivers/cbus/Makefile
31 ===================================================================
32 --- linux-2.6.37.1.orig/drivers/cbus/Makefile 2011-02-19 20:26:01.250340850 +0100
33 +++ linux-2.6.37.1/drivers/cbus/Makefile 2011-02-19 20:26:01.526318768 +0100
35 obj-$(CONFIG_CBUS_TAHVO_USER) += tahvo-user.o
36 obj-$(CONFIG_CBUS_RETU_USER) += retu-user.o
37 obj-$(CONFIG_CBUS_RETU_HEADSET) += retu-headset.o
38 +n810bm-y += n810bm_main.o
39 +n810bm-y += lipocharge.o
40 +obj-$(CONFIG_N810BM) += n810bm.o
41 Index: linux-2.6.37.1/drivers/cbus/n810bm_main.c
42 ===================================================================
43 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
44 +++ linux-2.6.37.1/drivers/cbus/n810bm_main.c 2011-02-20 17:50:58.807801112 +0100
47 + * Nokia n810 battery management
49 + * WARNING: This driver is based on unconfirmed documentation.
50 + * It is possibly dangerous to use this software.
51 + * Use this software at your own risk!
53 + * Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
55 + * This program is free software; you can redistribute it and/or
56 + * modify it under the terms of the GNU General Public License
57 + * as published by the Free Software Foundation; either version 2
58 + * of the License, or (at your option) any later version.
60 + * This program is distributed in the hope that it will be useful,
61 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
62 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63 + * GNU General Public License for more details.
68 +#include <linux/module.h>
69 +#include <linux/device.h>
70 +#include <linux/platform_device.h>
71 +#include <linux/slab.h>
72 +#include <linux/mutex.h>
73 +#include <linux/timer.h>
74 +#include <linux/firmware.h>
75 +#include <linux/bitops.h>
76 +#include <linux/workqueue.h>
77 +#include <linux/delay.h>
82 +#include "lipocharge.h"
85 +#define N810BM_PMM_BLOCK_FILENAME "n810-cal-bme-pmm.fw"
86 +#define N810BM_PMM_BLOCK_SIZE 0x600
87 +#define N810BM_PMM_GROUP_SIZE 0x200
88 +#define N810BM_PMM_ELEM_SIZE 0x10
90 +#define N810BM_CHECK_INTERVAL (HZ * 2)
91 +#define N810BM_MIN_VOLTAGE_THRES 3200 /* Absolute minimum voltage threshold */
95 + * The battery size indicator ADC measures the resistance between
96 + * the battery BSI pin and ground. This is used to detect the battery
97 + * capacity, as the BSI resistor is related to capacity.
99 + * Manually measured lookup table.
100 + * Hard to measure, thus not very accurate.
102 + * Resistance | ADC value
103 + * ========================
110 +/* RETU_ADC_BATTVOLT
111 + * Manually measured lookup table.
112 + * Hard to measure, thus not very accurate.
114 + * Voltage | ADC value
115 + * =====================
134 +/* PMM block ADC IDs */
135 +enum n810bm_pmm_adc_id {
136 + N810BM_PMM_ADC_BATVOLT = 0x01, /* Battery voltage */
137 + N810BM_PMM_ADC_CHGVOLT = 0x02, /* Charger voltage */
138 + N810BM_PMM_ADC_GND2 = 0x03, /* Ground 0V */
139 + N810BM_PMM_ADC_BSI = 0x04, /* Battery size indicator */
140 + N810BM_PMM_ADC_BATTEMP = 0x05, /* Battery temperature */
141 + N810BM_PMM_ADC_HEADSET = 0x06, /* Headset detection */
142 + N810BM_PMM_ADC_HOOKDET = 0x07, /* Hook detection */
143 + N810BM_PMM_ADC_LIGHTSENS = 0x08, /* Light sensor */
144 + N810BM_PMM_ADC_BATCURR = 0x0E, /* Battery current */
145 + N810BM_PMM_ADC_BKUPVOLT = 0x13, /* Backup battery voltage */
146 + N810BM_PMM_ADC_LIGHTTEMP = 0x14, /* Light sensor temperature */
147 + N810BM_PMM_ADC_RFGP = 0x15, /* RF GP */
148 + N810BM_PMM_ADC_WBTX = 0x16, /* Wideband TX detection */
149 + N810BM_PMM_ADC_RETUTEMP = 0x17, /* RETU chip temperature */
150 + N810BM_PMM_ADC_0xFE = 0xFE,
153 +struct n810bm_adc_calib {
154 + enum n810bm_pmm_adc_id id;
163 +struct n810bm_calib {
164 + struct n810bm_adc_calib adc[25];
167 +enum n810bm_capacity {
168 + N810BM_CAP_UNKNOWN = -1,
169 + N810BM_CAP_NONE = 0,
170 + N810BM_CAP_1500MAH = 1500, /* 1500 mAh battery */
173 +enum n810bm_notify_flags {
174 + N810BM_NOTIFY_battery_charging,
175 + N810BM_NOTIFY_charger_pwm,
179 + bool battery_present; /* A battery is inserted */
180 + bool charger_present; /* The charger is connected */
181 + enum n810bm_capacity capacity; /* The capacity of the inserted battery (if any) */
183 + bool charger_enabled; /* Want to charge? */
184 + struct lipocharge charger; /* Charger subsystem */
185 + unsigned int active_current_pwm; /* Active value of TAHVO_REG_CHGCURR */
186 + int current_measure_enabled; /* Current measure enable refcount */
188 + struct platform_device *pdev;
189 + struct n810bm_calib calib; /* Calibration data */
191 + bool verbose_charge_log; /* Verbose charge logging */
193 + unsigned long notify_flags;
194 + struct work_struct notify_work;
195 + struct work_struct currmeas_irq_work;
196 + struct delayed_work periodic_check_work;
198 + bool initialized; /* The hardware was initialized */
199 + struct mutex mutex;
202 +static void n810bm_notify_battery_charging(struct n810bm *bm);
203 +static void n810bm_notify_charger_pwm(struct n810bm *bm);
206 +static inline struct n810bm * device_to_n810bm(struct device *dev)
208 + struct platform_device *pdev = to_platform_device(dev);
209 + struct n810bm *bm = platform_get_drvdata(pdev);
214 +static inline bool n810bm_known_battery_present(struct n810bm *bm)
216 + return bm->battery_present &&
217 + bm->capacity != N810BM_CAP_UNKNOWN &&
218 + bm->capacity != N810BM_CAP_NONE;
221 +static NORET_TYPE void n810bm_emergency(struct n810bm *bm, const char *message) ATTRIB_NORET;
222 +static void n810bm_emergency(struct n810bm *bm, const char *message)
224 + printk(KERN_EMERG "n810 battery management fatal fault: %s\n", message);
228 +static u16 tahvo_read(struct n810bm *bm, unsigned int reg)
231 + unsigned long flags;
233 + spin_lock_irqsave(&tahvo_lock, flags);
234 + ret = tahvo_read_reg(reg);
235 + spin_unlock_irqrestore(&tahvo_lock, flags);
236 + if (ret < 0 || ret > 0xFFFF)
237 + n810bm_emergency(bm, "tahvo_read");
242 +static void tahvo_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
245 + unsigned long flags;
248 + spin_lock_irqsave(&tahvo_lock, flags);
250 + ret = tahvo_read_reg(reg);
251 + if (ret < 0 || ret > 0xFFFF)
258 + ret = tahvo_write_reg(reg, value);
261 + spin_unlock_irqrestore(&tahvo_lock, flags);
266 + spin_unlock_irqrestore(&tahvo_lock, flags);
267 + n810bm_emergency(bm, "tahvo_maskset");
270 +static inline void tahvo_write(struct n810bm *bm, unsigned int reg, u16 value)
272 + tahvo_maskset(bm, reg, 0xFFFF, value);
275 +static inline void tahvo_set(struct n810bm *bm, unsigned int reg, u16 mask)
277 + tahvo_maskset(bm, reg, mask, mask);
280 +static inline void tahvo_clear(struct n810bm *bm, unsigned int reg, u16 mask)
282 + tahvo_maskset(bm, reg, mask, 0);
285 +static u16 retu_read(struct n810bm *bm, unsigned int reg)
288 + unsigned long flags;
290 + spin_lock_irqsave(&retu_lock, flags);
291 + ret = retu_read_reg(reg);
292 + spin_unlock_irqrestore(&retu_lock, flags);
293 + if (ret < 0 || ret > 0xFFFF)
294 + n810bm_emergency(bm, "retu_read");
299 +static void retu_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
302 + unsigned long flags;
305 + spin_lock_irqsave(&retu_lock, flags);
307 + ret = retu_read_reg(reg);
308 + if (ret < 0 || ret > 0xFFFF)
315 + ret = retu_write_reg(reg, value);
318 + spin_unlock_irqrestore(&retu_lock, flags);
323 + spin_unlock_irqrestore(&retu_lock, flags);
324 + n810bm_emergency(bm, "retu_maskset");
327 +static inline void retu_write(struct n810bm *bm, unsigned int reg, u16 value)
329 + retu_maskset(bm, reg, 0xFFFF, value);
332 +static int retu_adc_average(struct n810bm *bm, unsigned int chan,
333 + unsigned int nr_passes)
335 + unsigned int i, value = 0;
338 + if (WARN_ON(!nr_passes))
340 + for (i = 0; i < nr_passes; i++) {
341 + ret = retu_read_adc(chan);
346 + value /= nr_passes;
351 +static struct n810bm_adc_calib * n810bm_get_adc_calib(struct n810bm *bm,
352 + enum n810bm_pmm_adc_id id)
354 + unsigned int index = 0;
355 + struct n810bm_adc_calib *cal;
357 + if (id != N810BM_PMM_ADC_0xFE)
358 + index = (unsigned int)id + 1;
359 + if (index >= ARRAY_SIZE(bm->calib.adc))
362 + cal = &bm->calib.adc[index];
363 + WARN_ON(cal->id && cal->id != id);
368 +static int pmm_record_get(struct n810bm *bm,
369 + const struct firmware *pmm_block,
370 + void *buffer, size_t length,
371 + unsigned int group, unsigned int element, unsigned int offset)
373 + const u8 *pmm_area = pmm_block->data;
374 + u8 active_group_mask;
376 + if (pmm_block->size != N810BM_PMM_BLOCK_SIZE)
378 + if (group >= N810BM_PMM_BLOCK_SIZE / N810BM_PMM_GROUP_SIZE)
380 + if (element >= N810BM_PMM_GROUP_SIZE / N810BM_PMM_ELEM_SIZE)
382 + if (offset >= N810BM_PMM_ELEM_SIZE || length > N810BM_PMM_ELEM_SIZE ||
383 + length + offset > N810BM_PMM_ELEM_SIZE)
386 + active_group_mask = pmm_area[16];
387 + if (!(active_group_mask & (1 << group))) {
388 + dev_dbg(&bm->pdev->dev, "pwm_record_get: Requested group %u, "
389 + "but group is not active", group);
394 + pmm_area + group * N810BM_PMM_GROUP_SIZE
395 + + element * N810BM_PMM_ELEM_SIZE
402 +/* PMM block group 1 element */
403 +struct group1_element {
412 +static int extract_group1_elem(struct n810bm *bm,
413 + const struct firmware *pmm_block,
414 + const enum n810bm_pmm_adc_id *pmm_adc_ids, size_t nr_pmm_adc_ids,
415 + u32 field1_mask, u32 field2_mask)
417 + struct group1_element elem;
419 + unsigned int i, element_nr;
420 + struct n810bm_adc_calib *adc_calib;
422 + for (i = 0; i < nr_pmm_adc_ids; i++) {
423 + element_nr = (unsigned int)(pmm_adc_ids[i]) + 3;
425 + err = pmm_record_get(bm, pmm_block, &elem, sizeof(elem),
429 + adc_calib = n810bm_get_adc_calib(bm, elem.id);
431 + dev_err(&bm->pdev->dev, "extract_group1_elem: "
432 + "Could not get calib element for 0x%02X",
437 + if (adc_calib->flags == elem.flags) {
438 + adc_calib->field1 = le32_to_cpu(elem.field1) & field1_mask;
439 + adc_calib->field2 = le32_to_cpu(elem.field2) & field2_mask;
441 + dev_dbg(&bm->pdev->dev, "extract_group1_elem: "
442 + "Not extracting fields due to flags mismatch: "
443 + "0x%02X vs 0x%02X",
444 + adc_calib->flags, elem.flags);
451 +static int n810bm_parse_pmm_group1(struct n810bm *bm,
452 + const struct firmware *pmm_block)
454 + struct n810bm_adc_calib *adc_calib;
455 + struct group1_element elem;
458 + static const enum n810bm_pmm_adc_id pmm_adc_ids_1[] = {
459 + N810BM_PMM_ADC_BATVOLT,
460 + N810BM_PMM_ADC_CHGVOLT,
461 + N810BM_PMM_ADC_BKUPVOLT,
462 + N810BM_PMM_ADC_BATCURR,
464 + static const enum n810bm_pmm_adc_id pmm_adc_ids_2[] = {
465 + N810BM_PMM_ADC_BSI,
467 + static const enum n810bm_pmm_adc_id pmm_adc_ids_3[] = {
468 + N810BM_PMM_ADC_BATTEMP,
471 + /* Parse element 2 */
472 + err = pmm_record_get(bm, pmm_block, &elem, sizeof(elem),
475 + dev_err(&bm->pdev->dev,
476 + "PMM: Failed to get group 1 / element 2");
479 + if (elem.id == N810BM_PMM_ADC_0xFE && elem.flags == 0x05) {
480 + adc_calib = n810bm_get_adc_calib(bm, elem.id);
482 + dev_err(&bm->pdev->dev,
483 + "calib extract: Failed to get 0xFE calib");
486 + adc_calib->id = elem.id;
487 + adc_calib->flags = elem.flags;
488 + adc_calib->field1 = le32_to_cpu(elem.field1);
489 + adc_calib->field2 = le32_to_cpu(elem.field2);
492 + err = extract_group1_elem(bm, pmm_block,
493 + pmm_adc_ids_1, ARRAY_SIZE(pmm_adc_ids_1),
494 + 0xFFFFFFFF, 0xFFFFFFFF);
497 + err = extract_group1_elem(bm, pmm_block,
498 + pmm_adc_ids_2, ARRAY_SIZE(pmm_adc_ids_2),
502 + err = extract_group1_elem(bm, pmm_block,
503 + pmm_adc_ids_3, ARRAY_SIZE(pmm_adc_ids_3),
504 + 0xFFFFFFFF, 0x0000FFFF);
511 +static int n810bm_parse_pmm_group2(struct n810bm *bm,
512 + const struct firmware *pmm_block)
514 + dev_err(&bm->pdev->dev, "TODO: CAL BME PMM group 2 parser not implemented, yet");
515 + return -EOPNOTSUPP;
518 +static void n810bm_adc_calib_set_defaults(struct n810bm *bm)
520 + struct n810bm_adc_calib *adc_calib;
523 + static const struct n810bm_adc_calib defaults[] = {
524 + /* ADC group-nr 0 */
526 + .id = N810BM_PMM_ADC_HEADSET,
530 + .id = N810BM_PMM_ADC_HOOKDET,
534 + .id = N810BM_PMM_ADC_RFGP,
538 + .id = N810BM_PMM_ADC_LIGHTSENS,
542 + .id = N810BM_PMM_ADC_WBTX,
546 + .id = N810BM_PMM_ADC_RETUTEMP,
550 + .id = N810BM_PMM_ADC_GND2,
554 + /* ADC group-nr 1 */
556 + .id = N810BM_PMM_ADC_0xFE,
562 + .id = N810BM_PMM_ADC_BATVOLT,
568 + .id = N810BM_PMM_ADC_CHGVOLT,
574 + .id = N810BM_PMM_ADC_BKUPVOLT,
580 + .id = N810BM_PMM_ADC_BATCURR,
586 + /* ADC group-nr 2 */
588 + .id = N810BM_PMM_ADC_BSI,
594 + /* ADC group-nr 3 */
596 + .id = N810BM_PMM_ADC_BATTEMP,
599 + .field1 = 265423000,
602 + /* ADC group-nr 4 */
604 + .id = N810BM_PMM_ADC_LIGHTTEMP,
607 + .field1 = 19533778,
608 + .field2 = 308019670,
614 + /* Clear the array */
615 + memset(&bm->calib.adc, 0, sizeof(bm->calib.adc));
616 + for (i = 0; i < ARRAY_SIZE(bm->calib.adc); i++)
617 + bm->calib.adc[i].flags = 0xFF;
619 + /* Copy the defaults */
620 + for (i = 0; i < ARRAY_SIZE(defaults); i++) {
621 + adc_calib = n810bm_get_adc_calib(bm, defaults[i].id);
622 + if (WARN_ON(!adc_calib))
624 + *adc_calib = defaults[i];
628 +static int n810bm_parse_pmm_block(struct n810bm *bm,
629 + const struct firmware *pmm_block)
633 + unsigned int i, count;
634 + struct n810bm_adc_calib *adc_calib;
636 + /* Initialize to defaults */
637 + n810bm_adc_calib_set_defaults(bm);
639 + /* Parse the PMM data */
640 + err = pmm_record_get(bm, pmm_block, &byte, sizeof(byte),
641 + 1, 0, 0); /* group 1 / element 0 */
642 + err |= (byte != 0x01);
643 + err |= pmm_record_get(bm, pmm_block, &byte, sizeof(byte),
644 + 1, 1, 0); /* group 1 / element 1 */
645 + err |= (byte != 0x01);
647 + err = n810bm_parse_pmm_group2(bm, pmm_block);
649 + err = n810bm_parse_pmm_group1(bm, pmm_block);
653 + /* Sanity checks */
654 + for (i = 0, count = 0; i < ARRAY_SIZE(bm->calib.adc); i++) {
655 + adc_calib = &bm->calib.adc[i];
656 + if (adc_calib->flags == 0xFF)
658 + switch (adc_calib->id) {
659 + case N810BM_PMM_ADC_BATVOLT:
660 + if (adc_calib->field1 < 2400 ||
661 + adc_calib->field1 > 2700)
662 + goto value_check_fail;
663 + if (adc_calib->field2 < 20000 ||
664 + adc_calib->field2 > 23000)
665 + goto value_check_fail;
668 + case N810BM_PMM_ADC_BSI:
669 + if (adc_calib->field1 < 1100 ||
670 + adc_calib->field1 > 1300)
671 + goto value_check_fail;
674 + case N810BM_PMM_ADC_BATCURR:
675 + if (adc_calib->field2 < 7000 ||
676 + adc_calib->field2 > 12000)
677 + goto value_check_fail;
680 + case N810BM_PMM_ADC_0xFE:
681 + if ((s32)adc_calib->field1 > 14 ||
682 + (s32)adc_calib->field1 < -14)
683 + goto value_check_fail;
684 + if (adc_calib->field2 < 13000 ||
685 + adc_calib->field2 > 13350)
686 + goto value_check_fail;
689 + case N810BM_PMM_ADC_CHGVOLT:
690 + case N810BM_PMM_ADC_BATTEMP:
691 + case N810BM_PMM_ADC_BKUPVOLT:
694 + case N810BM_PMM_ADC_GND2:
695 + case N810BM_PMM_ADC_HOOKDET:
696 + case N810BM_PMM_ADC_LIGHTSENS:
697 + case N810BM_PMM_ADC_HEADSET:
698 + case N810BM_PMM_ADC_LIGHTTEMP:
699 + case N810BM_PMM_ADC_RFGP:
700 + case N810BM_PMM_ADC_WBTX:
701 + case N810BM_PMM_ADC_RETUTEMP:
704 + dev_dbg(&bm->pdev->dev,
705 + "ADC 0x%02X calib: 0x%02X 0x%02X 0x%08X 0x%08X 0x%04X 0x%04X",
706 + adc_calib->id, adc_calib->flags, adc_calib->adc_groupnr,
707 + adc_calib->field1, adc_calib->field2,
708 + adc_calib->field3, adc_calib->field4);
711 + dev_err(&bm->pdev->dev, "PMM sanity check: Did not find "
712 + "all required values (count=%u)", count);
719 + dev_err(&bm->pdev->dev, "PMM image sanity check failed "
720 + "(id=%02X, field1=%08X, field2=%08X)",
721 + adc_calib->id, adc_calib->field1, adc_calib->field2);
726 +/* Set the current measure timer that triggers on Tahvo IRQ 7
727 + * An interval of zero disables the timer. */
728 +static void n810bm_set_current_measure_timer(struct n810bm *bm,
729 + u16 millisec_interval)
731 + u16 value = millisec_interval;
733 + if (value <= 0xF905) {
734 + value = ((u64)0x10624DD3 * (u64)(value + 0xF9)) >> 32;
739 + tahvo_write(bm, TAHVO_REG_BATCURRTIMER, value & 0xFF);
741 + tahvo_set(bm, TAHVO_REG_CHGCTL,
742 + TAHVO_REG_CHGCTL_CURTIMRST);
743 + tahvo_clear(bm, TAHVO_REG_CHGCTL,
744 + TAHVO_REG_CHGCTL_CURTIMRST);
746 + if (millisec_interval)
747 + tahvo_enable_irq(TAHVO_INT_BATCURR);
749 + tahvo_disable_irq(TAHVO_INT_BATCURR);
751 + //TODO also do a software timer for safety.
754 +static void n810bm_enable_current_measure(struct n810bm *bm)
756 + WARN_ON(bm->current_measure_enabled < 0);
757 + if (!bm->current_measure_enabled) {
758 + /* Enable the current measurement circuitry */
759 + tahvo_set(bm, TAHVO_REG_CHGCTL,
760 + TAHVO_REG_CHGCTL_CURMEAS);
761 + dev_dbg(&bm->pdev->dev,
762 + "Current measurement circuitry enabled");
764 + bm->current_measure_enabled++;
767 +static void n810bm_disable_current_measure(struct n810bm *bm)
769 + bm->current_measure_enabled--;
770 + WARN_ON(bm->current_measure_enabled < 0);
771 + if (!bm->current_measure_enabled) {
772 + /* Disable the current measurement circuitry */
773 + tahvo_clear(bm, TAHVO_REG_CHGCTL,
774 + TAHVO_REG_CHGCTL_CURMEAS);
775 + dev_dbg(&bm->pdev->dev,
776 + "Current measurement circuitry disabled");
780 +/* Measure the actual battery current. Returns a signed value in mA.
781 + * Does only work, if current measurement was enabled. */
782 +static int n810bm_measure_batt_current(struct n810bm *bm)
785 + int adc = 0, ma, i;
787 + if (WARN_ON(bm->current_measure_enabled <= 0))
789 + for (i = 0; i < 3; i++) {
790 + retval = tahvo_read(bm, TAHVO_REG_BATCURR);
791 + adc += (s16)retval; /* Value is signed */
795 + //TODO convert to mA
801 +/* Requires bm->mutex locked */
802 +static int n810bm_measure_batt_current_async(struct n810bm *bm)
805 + bool charging = lipocharge_is_charging(&bm->charger);
807 + n810bm_enable_current_measure(bm);
809 + WARN_ON(bm->active_current_pwm != 0);
810 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
811 + TAHVO_REG_CHGCTL_EN |
812 + TAHVO_REG_CHGCTL_PWMOVR |
813 + TAHVO_REG_CHGCTL_PWMOVRZERO,
814 + TAHVO_REG_CHGCTL_EN |
815 + TAHVO_REG_CHGCTL_PWMOVR |
816 + (charging ? 0 : TAHVO_REG_CHGCTL_PWMOVRZERO));
817 + ma = n810bm_measure_batt_current(bm);
818 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
819 + TAHVO_REG_CHGCTL_EN |
820 + TAHVO_REG_CHGCTL_PWMOVR |
821 + TAHVO_REG_CHGCTL_PWMOVRZERO,
822 + (charging ? TAHVO_REG_CHGCTL_EN : 0));
823 + n810bm_disable_current_measure(bm);
828 +static int adc_sanity_check(struct n810bm *bm, unsigned int channel)
832 + value = retu_read_adc(channel);
834 + dev_err(&bm->pdev->dev, "Failed to read GND ADC channel %u",
838 + dev_dbg(&bm->pdev->dev,
839 + "GND ADC channel %u sanity check got value: %d",
842 + n810bm_emergency(bm, "GND ADC sanity check failed");
849 +static int n810bm_check_adc_sanity(struct n810bm *bm)
853 + /* Discard one conversion */
854 + retu_write(bm, RETU_REG_ADCSCR, 0);
855 + retu_read_adc(RETU_ADC_GND2);
857 + err = adc_sanity_check(bm, RETU_ADC_GND2);
864 +/* Measure the battery voltage. Returns the value in mV (or negative value on error). */
865 +static int n810bm_measure_batt_voltage(struct n810bm *bm)
869 + const unsigned int scale = 1000;
871 + adc = retu_adc_average(bm, RETU_ADC_BATTVOLT, 5);
876 + mv = 2800 + ((adc - 0x37) * (((4200 - 2800) * scale) / (0x236 - 0x37))) / scale;
878 + //TODO compensate for power consumption
879 + //TODO honor calibration values
884 +/* Measure the charger voltage. Returns the value in mV (or negative value on error). */
885 +static int n810bm_measure_charger_voltage(struct n810bm *bm)
890 + adc = retu_adc_average(bm, RETU_ADC_CHGVOLT, 5);
893 + //TODO convert to mV
899 +/* Measure backup battery voltage. Returns the value in mV (or negative value on error). */
900 +static int n810bm_measure_backup_batt_voltage(struct n810bm *bm)
905 + adc = retu_adc_average(bm, RETU_ADC_BKUPVOLT, 3);
908 + //TODO convert to mV
914 +/* Measure the battery temperature. Returns the value in K (or negative value on error). */
915 +static int n810bm_measure_batt_temp(struct n810bm *bm)
920 + adc = retu_adc_average(bm, RETU_ADC_BATTEMP, 3);
923 + //TODO convert to K
929 +/* Read the battery capacity via BSI pin. */
930 +static enum n810bm_capacity n810bm_read_batt_capacity(struct n810bm *bm)
933 + const unsigned int hyst = 20;
935 + adc = retu_adc_average(bm, RETU_ADC_BSI, 5);
937 + dev_err(&bm->pdev->dev, "Failed to read BSI ADC");
938 + return N810BM_CAP_UNKNOWN;
941 + if (adc >= 0x3B5 - hyst && adc <= 0x3B5 + hyst)
942 + return N810BM_CAP_1500MAH;
944 + dev_err(&bm->pdev->dev, "Capacity indicator 0x%X unknown", adc);
946 + return N810BM_CAP_UNKNOWN;
949 +/* Convert a battery voltage (in mV) to percentage. */
950 +static unsigned int n810bm_mvolt2percent(unsigned int mv)
952 + const unsigned int minv = 3700;
953 + const unsigned int maxv = 4150;
954 + unsigned int percent;
956 + mv = clamp(mv, minv, maxv);
957 + percent = (mv - minv) * 100 / (maxv - minv);
962 +static void n810bm_start_charge(struct n810bm *bm)
966 + WARN_ON(!bm->battery_present);
967 + WARN_ON(!bm->charger_present);
969 + /* Set PWM to zero */
970 + bm->active_current_pwm = 0;
971 + tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
973 + /* Charge global enable */
974 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
975 + TAHVO_REG_CHGCTL_EN |
976 + TAHVO_REG_CHGCTL_PWMOVR |
977 + TAHVO_REG_CHGCTL_PWMOVRZERO,
978 + TAHVO_REG_CHGCTL_EN);
980 + WARN_ON((int)bm->capacity <= 0);
981 + bm->charger.capacity = bm->capacity;
982 + err = lipocharge_start(&bm->charger);
985 + /* Initialize current measurement circuitry */
986 + n810bm_enable_current_measure(bm);
987 + n810bm_set_current_measure_timer(bm, 250);
989 + dev_info(&bm->pdev->dev, "Charging battery");
990 + n810bm_notify_charger_pwm(bm);
991 + n810bm_notify_battery_charging(bm);
994 +static void n810bm_stop_charge(struct n810bm *bm)
996 + if (lipocharge_is_charging(&bm->charger)) {
997 + n810bm_set_current_measure_timer(bm, 0);
998 + n810bm_disable_current_measure(bm);
1000 + lipocharge_stop(&bm->charger);
1002 + /* Set PWM to zero */
1003 + bm->active_current_pwm = 0;
1004 + tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
1006 + /* Charge global disable */
1007 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1008 + TAHVO_REG_CHGCTL_EN |
1009 + TAHVO_REG_CHGCTL_PWMOVR |
1010 + TAHVO_REG_CHGCTL_PWMOVRZERO,
1013 + dev_info(&bm->pdev->dev, "Not charging battery");
1014 + n810bm_notify_charger_pwm(bm);
1015 + n810bm_notify_battery_charging(bm);
1018 +/* Periodic check */
1019 +static void n810bm_periodic_check_work(struct work_struct *work)
1021 + struct n810bm *bm = container_of(to_delayed_work(work),
1022 + struct n810bm, periodic_check_work);
1024 + bool battery_was_present, charger_was_present;
1027 + mutex_lock(&bm->mutex);
1029 + status = retu_read(bm, RETU_REG_STATUS);
1030 + battery_was_present = bm->battery_present;
1031 + charger_was_present = bm->charger_present;
1032 + bm->battery_present = !!(status & RETU_REG_STATUS_BATAVAIL);
1033 + bm->charger_present = !!(status & RETU_REG_STATUS_CHGPLUG);
1035 + if (bm->battery_present != battery_was_present) {
1036 + /* Battery state changed */
1037 + if (bm->battery_present) {
1038 + bm->capacity = n810bm_read_batt_capacity(bm);
1039 + if (bm->capacity == N810BM_CAP_UNKNOWN) {
1040 + dev_err(&bm->pdev->dev, "Unknown battery detected");
1042 + dev_info(&bm->pdev->dev, "Detected %u mAh battery",
1043 + (unsigned int)bm->capacity);
1046 + bm->capacity = N810BM_CAP_NONE;
1047 + dev_info(&bm->pdev->dev, "The main battery was removed");
1048 + //TODO disable charging
1052 + if (bm->charger_present != charger_was_present) {
1053 + /* Charger state changed */
1054 + dev_info(&bm->pdev->dev, "The charger was %s",
1055 + bm->charger_present ? "plugged in" : "removed");
1058 + if ((bm->battery_present && !bm->charger_present) ||
1059 + !n810bm_known_battery_present(bm)){
1060 + /* We're draining the battery */
1061 + mv = n810bm_measure_batt_voltage(bm);
1063 + n810bm_emergency(bm,
1064 + "check: Failed to measure voltage");
1066 + if (mv < N810BM_MIN_VOLTAGE_THRES) {
1067 + n810bm_emergency(bm,
1068 + "check: Minimum voltage threshold reached");
1072 + if (bm->charger_present && n810bm_known_battery_present(bm)) {
1073 + /* Known battery and charger are connected */
1074 + if (bm->charger_enabled) {
1075 + /* Charger is enabled */
1076 + if (!lipocharge_is_charging(&bm->charger)) {
1077 + //TODO start charging, if battery is below some threshold
1078 + n810bm_start_charge(bm);
1083 + if (lipocharge_is_charging(&bm->charger) && !bm->charger_present) {
1084 + /* Charger was unplugged. */
1085 + n810bm_stop_charge(bm);
1088 + mutex_unlock(&bm->mutex);
1089 + schedule_delayed_work(&bm->periodic_check_work,
1090 + round_jiffies_relative(N810BM_CHECK_INTERVAL));
1093 +static void n810bm_adc_irq_handler(unsigned long data)
1095 + struct n810bm *bm = (struct n810bm *)data;
1097 + retu_ack_irq(RETU_INT_ADCS);
1099 +dev_info(&bm->pdev->dev, "ADC interrupt triggered\n");
1102 +static void n810bm_tahvo_current_measure_work(struct work_struct *work)
1104 + struct n810bm *bm = container_of(work, struct n810bm, currmeas_irq_work);
1105 + int res, ma, mv, temp;
1107 + mutex_lock(&bm->mutex);
1108 + if (!lipocharge_is_charging(&bm->charger))
1111 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1112 + TAHVO_REG_CHGCTL_PWMOVR |
1113 + TAHVO_REG_CHGCTL_PWMOVRZERO,
1114 + TAHVO_REG_CHGCTL_PWMOVR);
1115 + ma = n810bm_measure_batt_current(bm);
1116 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1117 + TAHVO_REG_CHGCTL_PWMOVR |
1118 + TAHVO_REG_CHGCTL_PWMOVRZERO,
1119 + TAHVO_REG_CHGCTL_PWMOVR |
1120 + TAHVO_REG_CHGCTL_PWMOVRZERO);
1122 + mv = n810bm_measure_batt_voltage(bm);
1123 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1124 + TAHVO_REG_CHGCTL_PWMOVR |
1125 + TAHVO_REG_CHGCTL_PWMOVRZERO,
1127 + temp = n810bm_measure_batt_temp(bm);
1128 + if (WARN_ON(mv < 0))
1130 + if (WARN_ON(temp < 0))
1133 + if (bm->verbose_charge_log) {
1134 + dev_info(&bm->pdev->dev,
1135 + "Battery charge state: %d mV, %d mA (%s)",
1137 + (ma <= 0) ? "discharging" : "charging");
1139 + res = lipocharge_update_state(&bm->charger, mv, ma, temp);
1142 + dev_info(&bm->pdev->dev, "Battery fully charged");
1143 + n810bm_stop_charge(bm);
1146 + mutex_unlock(&bm->mutex);
1149 +static void n810bm_tahvo_current_measure_irq_handler(unsigned long data)
1151 + struct n810bm *bm = (struct n810bm *)data;
1153 + tahvo_ack_irq(TAHVO_INT_BATCURR);
1154 + schedule_work(&bm->currmeas_irq_work);
1157 +#define DEFINE_ATTR_NOTIFY(attr_name) \
1158 + void n810bm_notify_##attr_name(struct n810bm *bm) \
1160 + set_bit(N810BM_NOTIFY_##attr_name, &bm->notify_flags); \
1162 + schedule_work(&bm->notify_work); \
1165 +#define DEFINE_SHOW_INT_FUNC(name, member) \
1166 + static ssize_t n810bm_attr_##name##_show(struct device *dev, \
1167 + struct device_attribute *attr, \
1170 + struct n810bm *bm = device_to_n810bm(dev); \
1173 + mutex_lock(&bm->mutex); \
1174 + count = snprintf(buf, PAGE_SIZE, "%d\n", (int)(bm->member)); \
1175 + mutex_unlock(&bm->mutex); \
1180 +#define DEFINE_STORE_INT_FUNC(name, member) \
1181 + static ssize_t n810bm_attr_##name##_store(struct device *dev, \
1182 + struct device_attribute *attr,\
1183 + const char *buf, size_t count)\
1185 + struct n810bm *bm = device_to_n810bm(dev); \
1189 + mutex_lock(&bm->mutex); \
1190 + err = strict_strtol(buf, 0, &val); \
1192 + bm->member = (typeof(bm->member))val; \
1193 + mutex_unlock(&bm->mutex); \
1195 + return err ? err : count; \
1198 +#define DEFINE_ATTR_SHOW_INT(name, member) \
1199 + DEFINE_SHOW_INT_FUNC(name, member) \
1200 + static DEVICE_ATTR(name, S_IRUGO, \
1201 + n810bm_attr_##name##_show, NULL);
1203 +#define DEFINE_ATTR_SHOW_STORE_INT(name, member) \
1204 + DEFINE_SHOW_INT_FUNC(name, member) \
1205 + DEFINE_STORE_INT_FUNC(name, member) \
1206 + static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
1207 + n810bm_attr_##name##_show, \
1208 + n810bm_attr_##name##_store);
1210 +DEFINE_ATTR_SHOW_INT(battery_present, battery_present);
1211 +DEFINE_ATTR_SHOW_INT(charger_present, charger_present);
1212 +DEFINE_ATTR_SHOW_INT(charger_pwm, active_current_pwm);
1213 +static DEFINE_ATTR_NOTIFY(charger_pwm);
1214 +DEFINE_ATTR_SHOW_STORE_INT(charger_enable, charger_enabled);
1215 +DEFINE_ATTR_SHOW_STORE_INT(charger_verbose, verbose_charge_log);
1217 +static ssize_t n810bm_attr_battery_charging(struct device *dev,
1218 + struct device_attribute *attr,
1221 + struct n810bm *bm = device_to_n810bm(dev);
1224 + mutex_lock(&bm->mutex);
1225 + count = snprintf(buf, PAGE_SIZE, "%d\n",
1226 + (int)lipocharge_is_charging(&bm->charger));
1227 + mutex_unlock(&bm->mutex);
1231 +static DEVICE_ATTR(battery_charging, S_IRUGO,
1232 + n810bm_attr_battery_charging, NULL);
1233 +static DEFINE_ATTR_NOTIFY(battery_charging);
1235 +static ssize_t n810bm_attr_battery_level_show(struct device *dev,
1236 + struct device_attribute *attr,
1239 + struct n810bm *bm = device_to_n810bm(dev);
1240 + ssize_t count = -ENODEV;
1243 + mutex_lock(&bm->mutex);
1244 + if (!bm->battery_present || lipocharge_is_charging(&bm->charger))
1247 + millivolt = n810bm_measure_batt_voltage(bm);
1248 + if (millivolt >= 0) {
1249 + count = snprintf(buf, PAGE_SIZE, "%u\n",
1250 + n810bm_mvolt2percent(millivolt));
1252 + mutex_unlock(&bm->mutex);
1256 +static DEVICE_ATTR(battery_level, S_IRUGO,
1257 + n810bm_attr_battery_level_show, NULL);
1259 +static ssize_t n810bm_attr_battery_capacity_show(struct device *dev,
1260 + struct device_attribute *attr,
1263 + struct n810bm *bm = device_to_n810bm(dev);
1267 + mutex_lock(&bm->mutex);
1268 + if (n810bm_known_battery_present(bm))
1269 + capacity = (int)bm->capacity;
1270 + count = snprintf(buf, PAGE_SIZE, "%d\n", capacity);
1271 + mutex_unlock(&bm->mutex);
1275 +static DEVICE_ATTR(battery_capacity, S_IRUGO,
1276 + n810bm_attr_battery_capacity_show, NULL);
1278 +static ssize_t n810bm_attr_battery_temp_show(struct device *dev,
1279 + struct device_attribute *attr,
1282 + struct n810bm *bm = device_to_n810bm(dev);
1283 + ssize_t count = -ENODEV;
1286 + mutex_lock(&bm->mutex);
1287 + k = n810bm_measure_batt_temp(bm);
1289 + count = snprintf(buf, PAGE_SIZE, "%d\n", k);
1290 + mutex_unlock(&bm->mutex);
1294 +static DEVICE_ATTR(battery_temp, S_IRUGO,
1295 + n810bm_attr_battery_temp_show, NULL);
1297 +static ssize_t n810bm_attr_charger_voltage_show(struct device *dev,
1298 + struct device_attribute *attr,
1301 + struct n810bm *bm = device_to_n810bm(dev);
1302 + ssize_t count = -ENODEV;
1305 + mutex_lock(&bm->mutex);
1306 + if (bm->charger_present)
1307 + mv = n810bm_measure_charger_voltage(bm);
1309 + count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
1310 + mutex_unlock(&bm->mutex);
1314 +static DEVICE_ATTR(charger_voltage, S_IRUGO,
1315 + n810bm_attr_charger_voltage_show, NULL);
1317 +static ssize_t n810bm_attr_backup_battery_voltage_show(struct device *dev,
1318 + struct device_attribute *attr,
1321 + struct n810bm *bm = device_to_n810bm(dev);
1322 + ssize_t count = -ENODEV;
1325 + mutex_lock(&bm->mutex);
1326 + mv = n810bm_measure_backup_batt_voltage(bm);
1328 + count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
1329 + mutex_unlock(&bm->mutex);
1333 +static DEVICE_ATTR(backup_battery_voltage, S_IRUGO,
1334 + n810bm_attr_backup_battery_voltage_show, NULL);
1336 +static ssize_t n810bm_attr_battery_current_show(struct device *dev,
1337 + struct device_attribute *attr,
1340 + struct n810bm *bm = device_to_n810bm(dev);
1341 + ssize_t count = -ENODEV;
1344 + mutex_lock(&bm->mutex);
1345 + if (bm->battery_present)
1346 + ma = n810bm_measure_batt_current_async(bm);
1347 + count = snprintf(buf, PAGE_SIZE, "%d\n", ma);
1348 + mutex_unlock(&bm->mutex);
1352 +static DEVICE_ATTR(battery_current, S_IRUGO,
1353 + n810bm_attr_battery_current_show, NULL);
1355 +static const struct device_attribute *n810bm_attrs[] = {
1356 + &dev_attr_battery_present,
1357 + &dev_attr_battery_level,
1358 + &dev_attr_battery_charging,
1359 + &dev_attr_battery_current,
1360 + &dev_attr_battery_capacity,
1361 + &dev_attr_battery_temp,
1362 + &dev_attr_backup_battery_voltage,
1363 + &dev_attr_charger_present,
1364 + &dev_attr_charger_verbose,
1365 + &dev_attr_charger_voltage,
1366 + &dev_attr_charger_enable,
1367 + &dev_attr_charger_pwm,
1370 +static void n810bm_notify_work(struct work_struct *work)
1372 + struct n810bm *bm = container_of(work, struct n810bm, notify_work);
1373 + unsigned long notify_flags;
1375 + notify_flags = xchg(&bm->notify_flags, 0);
1378 +#define do_notify(attr_name) \
1380 + if (notify_flags & (1 << N810BM_NOTIFY_##attr_name)) { \
1381 + sysfs_notify(&bm->pdev->dev.kobj, NULL, \
1382 + dev_attr_##attr_name.attr.name); \
1386 + do_notify(battery_charging);
1387 + do_notify(charger_pwm);
1390 +static int n810bm_charger_set_current_pwm(struct lipocharge *c,
1391 + unsigned int duty_cycle)
1393 + struct n810bm *bm = container_of(c, struct n810bm, charger);
1394 + int err = -EINVAL;
1396 + WARN_ON(!mutex_is_locked(&bm->mutex));
1397 + if (WARN_ON(duty_cycle > 0xFF))
1399 + if (WARN_ON(!bm->charger_enabled))
1401 + if (WARN_ON(!bm->battery_present || !bm->charger_present))
1404 + if (duty_cycle != bm->active_current_pwm) {
1405 + bm->active_current_pwm = duty_cycle;
1406 + tahvo_write(bm, TAHVO_REG_CHGCURR, duty_cycle);
1407 + n810bm_notify_charger_pwm(bm);
1416 +static void n810bm_charger_emergency(struct lipocharge *c)
1418 + struct n810bm *bm = container_of(c, struct n810bm, charger);
1420 + n810bm_emergency(bm, "Battery charger fault");
1423 +static void n810bm_hw_exit(struct n810bm *bm)
1425 + n810bm_stop_charge(bm);
1426 + retu_write(bm, RETU_REG_ADCSCR, 0);
1429 +static int n810bm_hw_init(struct n810bm *bm)
1433 + err = n810bm_check_adc_sanity(bm);
1437 + n810bm_stop_charge(bm);
1442 +static void n810bm_cancel_and_flush_work(struct n810bm *bm)
1444 + cancel_delayed_work_sync(&bm->periodic_check_work);
1445 + cancel_work_sync(&bm->notify_work);
1446 + cancel_work_sync(&bm->currmeas_irq_work);
1447 + flush_scheduled_work();
1450 +static int n810bm_device_init(struct n810bm *bm)
1455 + bm->charger.rate = LIPORATE_p6C;
1456 + bm->charger.top_voltage = 4100;
1457 + bm->charger.duty_cycle_max = 0xFF;
1458 + bm->charger.set_current_pwm = n810bm_charger_set_current_pwm;
1459 + bm->charger.emergency = n810bm_charger_emergency;
1460 + lipocharge_init(&bm->charger, &bm->pdev->dev);
1462 + err = n810bm_hw_init(bm);
1465 + for (attr_index = 0; attr_index < ARRAY_SIZE(n810bm_attrs); attr_index++) {
1466 + err = device_create_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1468 + goto err_unwind_attrs;
1470 + err = retu_request_irq(RETU_INT_ADCS,
1471 + n810bm_adc_irq_handler,
1472 + (unsigned long)bm, "n810bm");
1474 + goto err_unwind_attrs;
1475 + err = tahvo_request_irq(TAHVO_INT_BATCURR,
1476 + n810bm_tahvo_current_measure_irq_handler,
1477 + (unsigned long)bm, "n810bm");
1479 + goto err_free_retu_irq;
1480 + tahvo_disable_irq(TAHVO_INT_BATCURR);
1482 + schedule_delayed_work(&bm->periodic_check_work,
1483 + round_jiffies_relative(N810BM_CHECK_INTERVAL));
1485 + bm->initialized = 1;
1486 + dev_info(&bm->pdev->dev, "Battery management initialized");
1491 + retu_free_irq(RETU_INT_ADCS);
1493 + for (attr_index--; attr_index >= 0; attr_index--)
1494 + device_remove_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1496 + n810bm_hw_exit(bm);
1498 + n810bm_cancel_and_flush_work(bm);
1503 +static void n810bm_device_exit(struct n810bm *bm)
1507 + if (!bm->initialized)
1510 + lipocharge_exit(&bm->charger);
1511 + tahvo_free_irq(TAHVO_INT_BATCURR);
1512 + retu_free_irq(RETU_INT_ADCS);
1513 + for (i = 0; i < ARRAY_SIZE(n810bm_attrs); i++)
1514 + device_remove_file(&bm->pdev->dev, n810bm_attrs[i]);
1516 + n810bm_cancel_and_flush_work(bm);
1518 + n810bm_hw_exit(bm);
1520 + bm->initialized = 0;
1523 +static void n810bm_pmm_block_found(const struct firmware *fw, void *context)
1525 + struct n810bm *bm = context;
1529 + dev_err(&bm->pdev->dev,
1530 + "CAL PMM block image file not found");
1533 + if (fw->size != N810BM_PMM_BLOCK_SIZE ||
1534 + memcmp(fw->data, "BME-PMM-BLOCK01", 15) != 0) {
1535 + dev_err(&bm->pdev->dev,
1536 + "CAL PMM block image file has an invalid format");
1540 + err = n810bm_parse_pmm_block(bm, fw);
1543 + release_firmware(fw);
1545 + err = n810bm_device_init(bm);
1547 + dev_err(&bm->pdev->dev,
1548 + "Failed to initialized battery management (%d)", err);
1554 + release_firmware(fw);
1559 +static int __devinit n810bm_probe(struct platform_device *pdev)
1561 + struct n810bm *bm;
1564 + bm = kzalloc(sizeof(*bm), GFP_KERNEL);
1568 + platform_set_drvdata(pdev, bm);
1569 + mutex_init(&bm->mutex);
1570 + INIT_DELAYED_WORK(&bm->periodic_check_work, n810bm_periodic_check_work);
1571 + INIT_WORK(&bm->notify_work, n810bm_notify_work);
1572 + INIT_WORK(&bm->currmeas_irq_work, n810bm_tahvo_current_measure_work);
1574 + dev_info(&bm->pdev->dev, "Requesting CAL BME PMM block firmware file "
1575 + N810BM_PMM_BLOCK_FILENAME);
1576 + err = request_firmware_nowait(THIS_MODULE, 1,
1577 + N810BM_PMM_BLOCK_FILENAME,
1578 + &bm->pdev->dev, GFP_KERNEL,
1579 + bm, n810bm_pmm_block_found);
1581 + dev_err(&bm->pdev->dev,
1582 + "Failed to request CAL PMM block image file (%d)", err);
1594 +static int __devexit n810bm_remove(struct platform_device *pdev)
1596 + struct n810bm *bm = platform_get_drvdata(pdev);
1598 + n810bm_device_exit(bm);
1601 + platform_set_drvdata(pdev, NULL);
1606 +static struct platform_driver n810bm_driver = {
1607 + .remove = __devexit_p(n810bm_remove),
1613 +static int __init n810bm_modinit(void)
1615 + return platform_driver_probe(&n810bm_driver, n810bm_probe);
1617 +module_init(n810bm_modinit);
1619 +static void __exit n810bm_modexit(void)
1621 + platform_driver_unregister(&n810bm_driver);
1623 +module_exit(n810bm_modexit);
1625 +MODULE_DESCRIPTION("Nokia n810 battery management");
1626 +MODULE_FIRMWARE(N810BM_PMM_BLOCK_FILENAME);
1627 +MODULE_LICENSE("GPL");
1628 +MODULE_AUTHOR("Michael Buesch");
1629 Index: linux-2.6.37.1/drivers/cbus/retu.c
1630 ===================================================================
1631 --- linux-2.6.37.1.orig/drivers/cbus/retu.c 2011-02-19 20:26:01.251340770 +0100
1632 +++ linux-2.6.37.1/drivers/cbus/retu.c 2011-02-19 20:26:01.529318528 +0100
1635 * This function writes a value to the specified register
1637 -void retu_write_reg(int reg, u16 val)
1638 +int retu_write_reg(int reg, u16 val)
1640 BUG_ON(!retu_initialized);
1641 - cbus_write_reg(cbus_host, RETU_ID, reg, val);
1642 + return cbus_write_reg(cbus_host, RETU_ID, reg, val);
1645 void retu_set_clear_reg_bits(int reg, u16 set, u16 clear)
1647 EXPORT_SYMBOL(retu_ack_irq);
1648 EXPORT_SYMBOL(retu_read_reg);
1649 EXPORT_SYMBOL(retu_write_reg);
1650 +EXPORT_SYMBOL(retu_read_adc);
1652 subsys_initcall(retu_init);
1653 module_exit(retu_exit);
1654 Index: linux-2.6.37.1/drivers/cbus/retu.h
1655 ===================================================================
1656 --- linux-2.6.37.1.orig/drivers/cbus/retu.h 2011-02-19 20:26:01.252340690 +0100
1657 +++ linux-2.6.37.1/drivers/cbus/retu.h 2011-02-19 20:26:01.530318448 +0100
1659 #define RETU_REG_CTRL_CLR 0x0f /* Regulator clear register */
1660 #define RETU_REG_CTRL_SET 0x10 /* Regulator set register */
1661 #define RETU_REG_STATUS 0x16 /* Status register */
1662 +#define RETU_REG_STATUS_BATAVAIL 0x0100 /* Battery available */
1663 +#define RETU_REG_STATUS_CHGPLUG 0x1000 /* Charger is plugged in */
1664 #define RETU_REG_WATCHDOG 0x17 /* Watchdog register */
1665 #define RETU_REG_AUDTXR 0x18 /* Audio Codec Tx register */
1666 #define RETU_REG_MAX 0x1f
1669 #define MAX_RETU_IRQ_HANDLERS 16
1672 +#define RETU_ADC_GND 0x00 /* Ground */
1673 +#define RETU_ADC_BSI 0x01 /* Battery Size Indicator */
1674 +#define RETU_ADC_BATTEMP 0x02 /* Battery temperature */
1675 +#define RETU_ADC_CHGVOLT 0x03 /* Charger voltage */
1676 +#define RETU_ADC_HEADSET 0x04 /* Headset detection */
1677 +#define RETU_ADC_HOOKDET 0x05 /* Hook detection */
1678 +#define RETU_ADC_RFGP 0x06 /* RF GP */
1679 +#define RETU_ADC_WBTX 0x07 /* Wideband Tx detection */
1680 +#define RETU_ADC_BATTVOLT 0x08 /* Battery voltage measurement */
1681 +#define RETU_ADC_GND2 0x09 /* Ground */
1682 +#define RETU_ADC_LIGHTSENS 0x0A /* Light sensor */
1683 +#define RETU_ADC_LIGHTTEMP 0x0B /* Light sensor temperature */
1684 +#define RETU_ADC_BKUPVOLT 0x0C /* Backup battery voltage */
1685 +#define RETU_ADC_TEMP 0x0D /* RETU temperature */
1688 int retu_read_reg(int reg);
1689 -void retu_write_reg(int reg, u16 val);
1690 +int retu_write_reg(int reg, u16 val);
1691 void retu_set_clear_reg_bits(int reg, u16 set, u16 clear);
1692 int retu_read_adc(int channel);
1693 int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
1694 Index: linux-2.6.37.1/arch/arm/mach-omap2/board-n8x0.c
1695 ===================================================================
1696 --- linux-2.6.37.1.orig/arch/arm/mach-omap2/board-n8x0.c 2011-02-19 20:26:01.201344770 +0100
1697 +++ linux-2.6.37.1/arch/arm/mach-omap2/board-n8x0.c 2011-02-19 20:26:01.531318368 +0100
1698 @@ -907,6 +907,17 @@
1699 ARRAY_SIZE(n8x0_gpio_switches));
1702 +static struct platform_device n810_bm_device = {
1707 +static void __init n810_bm_init(void)
1709 + if (platform_device_register(&n810_bm_device))
1713 static void __init n8x0_init_machine(void)
1715 omap2420_mux_init(board_mux, OMAP_PACKAGE_ZAC);
1717 n8x0_onenand_init();
1724 MACHINE_START(NOKIA_N800, "Nokia N800")
1725 Index: linux-2.6.37.1/drivers/cbus/lipocharge.c
1726 ===================================================================
1727 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
1728 +++ linux-2.6.37.1/drivers/cbus/lipocharge.c 2011-02-19 20:26:01.531318368 +0100
1731 + * Generic LIPO battery charger
1733 + * Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
1735 + * This program is free software; you can redistribute it and/or
1736 + * modify it under the terms of the GNU General Public License
1737 + * as published by the Free Software Foundation; either version 2
1738 + * of the License, or (at your option) any later version.
1740 + * This program is distributed in the hope that it will be useful,
1741 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1742 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1743 + * GNU General Public License for more details.
1748 +#include "lipocharge.h"
1750 +#include <linux/slab.h>
1753 +/* Hysteresis constants */
1754 +#define CURRENT_HYST 30 /* mA */
1755 +#define VOLTAGE_HYST 10 /* mV */
1757 +/* Threshold constants */
1758 +#define FINISH_CURRENT_PERCENT 3
1761 +/* Returns the requested first-stage charge current in mA */
1762 +static inline unsigned int get_stage1_charge_current(struct lipocharge *c)
1764 + /* current = (capacity * C) */
1765 + return c->capacity * c->rate / 1000;
1768 +void lipocharge_init(struct lipocharge *c, struct device *dev)
1771 + c->state = LIPO_IDLE;
1774 +void lipocharge_exit(struct lipocharge *c)
1776 + c->state = LIPO_IDLE;
1779 +int lipocharge_start(struct lipocharge *c)
1783 + if (c->state != LIPO_IDLE)
1785 + if (!c->set_current_pwm || !c->emergency)
1787 + if (!c->top_voltage || c->top_voltage > 4200)
1790 + c->active_duty_cycle = 0;
1791 + err = c->set_current_pwm(c, c->active_duty_cycle);
1794 + c->state = LIPO_FIRST_STAGE;
1799 +void lipocharge_stop(struct lipocharge *c)
1801 + if (c->state == LIPO_IDLE)
1803 + c->state = LIPO_IDLE;
1806 +static int lipocharge_increase_current(struct lipocharge *c,
1807 + unsigned int inc_permille)
1809 + int old_pwm, new_pwm;
1811 + if (c->active_duty_cycle >= c->duty_cycle_max)
1814 + old_pwm = c->active_duty_cycle;
1815 + new_pwm = old_pwm + (c->duty_cycle_max * inc_permille / 1000);
1816 + new_pwm = min(new_pwm, (int)c->duty_cycle_max);
1817 + c->active_duty_cycle = new_pwm;
1819 + dev_dbg(c->dev, "lipo: Increasing duty_cycle by "
1820 + "%u permille (0x%02X -> 0x%02X)",
1821 + inc_permille, old_pwm, new_pwm);
1823 + return c->set_current_pwm(c, c->active_duty_cycle);
1826 +static int lipocharge_decrease_current(struct lipocharge *c,
1827 + unsigned int dec_permille)
1829 + int old_pwm, new_pwm;
1831 + if (c->active_duty_cycle <= 0)
1834 + old_pwm = c->active_duty_cycle;
1835 + new_pwm = old_pwm - (c->duty_cycle_max * dec_permille / 1000);
1836 + new_pwm = max(0, new_pwm);
1837 + c->active_duty_cycle = new_pwm;
1839 + dev_dbg(c->dev, "lipo: Decreasing duty_cycle by "
1840 + "%u permille (0x%02X -> 0x%02X)",
1841 + dec_permille, old_pwm, new_pwm);
1843 + return c->set_current_pwm(c, c->active_duty_cycle);
1846 +/** lipocharge_update_state - Update the charge state
1847 + * @c: The context.
1848 + * @voltage_mV: The measured battery voltage.
1849 + * @current_mA: The measured charge current.
1850 + * negative -> drain.
1851 + * positive -> charge.
1852 + * @temp_K: Battery temperature in K.
1854 + * Returns 0 on success, -1 on error.
1855 + * Returns 1, if the charging process is finished.
1857 +int lipocharge_update_state(struct lipocharge *c,
1858 + unsigned int voltage_mV,
1860 + unsigned int temp_K)
1862 + int requested_current, current_diff;
1864 + unsigned int permille;
1869 + switch (c->state) {
1871 + dev_err(c->dev, "%s: called while idle", __func__);
1873 + case LIPO_FIRST_STAGE: /* Constant current */
1874 +//printk("GOT %u %d %u\n", voltage_mV, current_mA, temp_K);
1875 + if (voltage_mV >= c->top_voltage) {
1876 + /* Float voltage reached.
1877 + * Switch charger mode to "constant current" */
1878 + c->state = LIPO_SECOND_STAGE;
1879 + dev_dbg(c->dev, "Switched to second charging stage.");
1882 + /* Float voltage not reached, yet.
1883 + * Try to get the requested constant current. */
1884 + requested_current = get_stage1_charge_current(c);
1885 + if (current_mA < 0)
1887 + current_diff = requested_current - current_mA;
1888 + if (abs(requested_current - current_mA) > CURRENT_HYST) {
1889 + if (current_diff > 0) {
1890 + /* Increase current */
1891 + permille = current_diff * 1000 / requested_current;
1893 + err = lipocharge_increase_current(c, permille);
1897 + /* Decrease current */
1898 + permille = (-current_diff) * 1000 / requested_current;
1900 + err = lipocharge_decrease_current(c, permille);
1906 + case LIPO_SECOND_STAGE: /* Constant voltage */
1913 Index: linux-2.6.37.1/drivers/cbus/lipocharge.h
1914 ===================================================================
1915 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
1916 +++ linux-2.6.37.1/drivers/cbus/lipocharge.h 2011-02-19 20:26:01.531318368 +0100
1918 +#ifndef LIPOCHARGE_H_
1919 +#define LIPOCHARGE_H_
1921 +#include <linux/types.h>
1922 +#include <linux/device.h>
1925 +#define LIPORATE(a,b) (((a) * 1000) + ((b) * 100))
1926 +#define LIPORATE_p6C LIPORATE(0,6) /* 0.6C */
1928 +enum lipocharge_state {
1929 + LIPO_IDLE, /* Not charging */
1930 + LIPO_FIRST_STAGE, /* Charging: constant current */
1931 + LIPO_SECOND_STAGE, /* Charging: constant voltage */
1934 +/** struct lipocharge - A generic LIPO charger
1936 + * @capacity: Battery capacity in mAh.
1937 + * @rate: Charge rate.
1938 + * @top_voltage: Fully charged voltage, in mV.
1939 + * @duty_cycle_max: Max value for duty_cycle.
1941 + * @set_charge_current: Set the charge current PWM duty cycle.
1942 + * @emergency: Something went wrong. Force shutdown.
1944 +struct lipocharge {
1945 + unsigned int capacity;
1946 + unsigned int rate;
1947 + unsigned int top_voltage;
1948 + unsigned int duty_cycle_max;
1950 + int (*set_current_pwm)(struct lipocharge *c, unsigned int duty_cycle);
1951 + void (*emergency)(struct lipocharge *c);
1954 + struct device *dev;
1955 + enum lipocharge_state state;
1956 + unsigned int active_duty_cycle;
1958 + //TODO implement timer to cut power after maximum charge time.
1961 +void lipocharge_init(struct lipocharge *c, struct device *dev);
1962 +void lipocharge_exit(struct lipocharge *c);
1964 +int lipocharge_start(struct lipocharge *c);
1965 +void lipocharge_stop(struct lipocharge *c);
1967 +int lipocharge_update_state(struct lipocharge *c,
1968 + unsigned int voltage_mV,
1970 + unsigned int temp_K);
1972 +static inline bool lipocharge_is_charging(struct lipocharge *c)
1974 + return (c->state != LIPO_IDLE);
1977 +#endif /* LIPOCHARGE_H_ */
1978 Index: linux-2.6.37.1/drivers/cbus/tahvo.h
1979 ===================================================================
1980 --- linux-2.6.37.1.orig/drivers/cbus/tahvo.h 2011-02-19 20:26:01.256340370 +0100
1981 +++ linux-2.6.37.1/drivers/cbus/tahvo.h 2011-02-19 20:26:01.532318288 +0100
1983 #define TAHVO_REG_IDR 0x01 /* Interrupt ID */
1984 #define TAHVO_REG_IDSR 0x02 /* Interrupt status */
1985 #define TAHVO_REG_IMR 0x03 /* Interrupt mask */
1986 +#define TAHVO_REG_CHGCURR 0x04 /* Charge current control PWM (8-bit) */
1987 #define TAHVO_REG_LEDPWMR 0x05 /* LED PWM */
1988 #define TAHVO_REG_USBR 0x06 /* USB control */
1989 +#define TAHVO_REG_CHGCTL 0x08 /* Charge control register */
1990 +#define TAHVO_REG_CHGCTL_EN 0x0001 /* Global charge enable */
1991 +#define TAHVO_REG_CHGCTL_PWMOVR 0x0004 /* PWM override. Force charge PWM to 0%/100% duty cycle. */
1992 +#define TAHVO_REG_CHGCTL_PWMOVRZERO 0x0008 /* If set, PWM override is 0% (If unset -> 100%) */
1993 +#define TAHVO_REG_CHGCTL_CURMEAS 0x0040 /* Enable battery current measurement. */
1994 +#define TAHVO_REG_CHGCTL_CURTIMRST 0x0080 /* Current measure timer reset. */
1995 +#define TAHVO_REG_BATCURRTIMER 0x0c /* Battery current measure timer (8-bit) */
1996 +#define TAHVO_REG_BATCURR 0x0d /* Battery (dis)charge current (signed 16-bit) */
1998 #define TAHVO_REG_MAX 0x0d
2000 /* Interrupt sources */
2001 #define TAHVO_INT_VBUSON 0
2002 +#define TAHVO_INT_BATCURR 7 /* Battery current measure timer */
2004 #define MAX_TAHVO_IRQ_HANDLERS 8
2006 int tahvo_read_reg(int reg);
2007 -void tahvo_write_reg(int reg, u16 val);
2008 +int tahvo_write_reg(int reg, u16 val);
2009 void tahvo_set_clear_reg_bits(int reg, u16 set, u16 clear);
2010 int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
2011 void tahvo_free_irq(int id);
2012 Index: linux-2.6.37.1/drivers/cbus/tahvo.c
2013 ===================================================================
2014 --- linux-2.6.37.1.orig/drivers/cbus/tahvo.c 2011-02-19 20:26:01.256340370 +0100
2015 +++ linux-2.6.37.1/drivers/cbus/tahvo.c 2011-02-19 20:26:01.532318288 +0100
2018 * This function writes a value to the specified register
2020 -void tahvo_write_reg(int reg, u16 val)
2021 +int tahvo_write_reg(int reg, u16 val)
2023 BUG_ON(!tahvo_initialized);
2024 - cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
2025 + return cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
2029 Index: linux-2.6.37.1/drivers/cbus/cbus.c
2030 ===================================================================
2031 --- linux-2.6.37.1.orig/drivers/cbus/cbus.c 2011-02-19 20:26:01.249340930 +0100
2032 +++ linux-2.6.37.1/drivers/cbus/cbus.c 2011-02-19 20:26:01.533318208 +0100
2034 #include <linux/gpio.h>
2035 #include <linux/platform_device.h>
2036 #include <linux/slab.h>
2037 +#include <linux/reboot.h>
2040 #include <asm/mach-types.h>
2041 @@ -301,6 +302,13 @@
2043 module_exit(cbus_bus_exit);
2045 +void cbus_emergency(void)
2047 + machine_power_off();
2048 + panic("cbus: Failed to halt machine in emergency state\n");
2050 +EXPORT_SYMBOL(cbus_emergency);
2052 MODULE_DESCRIPTION("CBUS serial protocol");
2053 MODULE_LICENSE("GPL");
2054 MODULE_AUTHOR("Juha Yrjölä");
2055 Index: linux-2.6.37.1/drivers/cbus/cbus.h
2056 ===================================================================
2057 --- linux-2.6.37.1.orig/drivers/cbus/cbus.h 2011-02-19 20:26:01.250340850 +0100
2058 +++ linux-2.6.37.1/drivers/cbus/cbus.h 2011-02-19 20:26:01.533318208 +0100
2060 extern int cbus_read_reg(struct cbus_host *host, int dev, int reg);
2061 extern int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val);
2063 +NORET_TYPE void cbus_emergency(void) ATTRIB_NORET;
2065 #endif /* __DRIVERS_CBUS_CBUS_H */