1 Index: linux-2.6.38-rc6/drivers/cbus/Kconfig
2 ===================================================================
3 --- linux-2.6.38-rc6.orig/drivers/cbus/Kconfig 2011-02-27 00:58:11.730599141 +0100
4 +++ linux-2.6.38-rc6/drivers/cbus/Kconfig 2011-02-27 01:05:20.242989951 +0100
6 to Retu/Vilma. Detection state and events are exposed through
10 + depends on CBUS_RETU && CBUS_TAHVO
11 + tristate "Nokia n810 battery management"
13 + Nokia n810 device battery management.
18 Index: linux-2.6.38-rc6/drivers/cbus/Makefile
19 ===================================================================
20 --- linux-2.6.38-rc6.orig/drivers/cbus/Makefile 2011-02-27 00:58:11.713598687 +0100
21 +++ linux-2.6.38-rc6/drivers/cbus/Makefile 2011-02-27 01:05:20.243989978 +0100
23 obj-$(CONFIG_CBUS_RETU_RTC) += retu-rtc.o
24 obj-$(CONFIG_CBUS_RETU_WDT) += retu-wdt.o
25 obj-$(CONFIG_CBUS_RETU_HEADSET) += retu-headset.o
26 +n810bm-y += n810bm_main.o
27 +n810bm-y += lipocharge.o
28 +obj-$(CONFIG_N810BM) += n810bm.o
29 Index: linux-2.6.38-rc6/drivers/cbus/n810bm_main.c
30 ===================================================================
31 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
32 +++ linux-2.6.38-rc6/drivers/cbus/n810bm_main.c 2011-02-27 01:05:20.245990032 +0100
35 + * Nokia n810 battery management
37 + * WARNING: This driver is based on unconfirmed documentation.
38 + * It is possibly dangerous to use this software.
39 + * Use this software at your own risk!
41 + * Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
43 + * This program is free software; you can redistribute it and/or
44 + * modify it under the terms of the GNU General Public License
45 + * as published by the Free Software Foundation; either version 2
46 + * of the License, or (at your option) any later version.
48 + * This program is distributed in the hope that it will be useful,
49 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
50 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 + * GNU General Public License for more details.
56 +#include <linux/module.h>
57 +#include <linux/device.h>
58 +#include <linux/platform_device.h>
59 +#include <linux/slab.h>
60 +#include <linux/mutex.h>
61 +#include <linux/timer.h>
62 +#include <linux/firmware.h>
63 +#include <linux/bitops.h>
64 +#include <linux/workqueue.h>
65 +#include <linux/delay.h>
70 +#include "lipocharge.h"
73 +#define N810BM_PMM_BLOCK_FILENAME "n810-cal-bme-pmm.fw"
74 +#define N810BM_PMM_BLOCK_SIZE 0x600
75 +#define N810BM_PMM_GROUP_SIZE 0x200
76 +#define N810BM_PMM_ELEM_SIZE 0x10
78 +#define N810BM_CHECK_INTERVAL (HZ * 2)
79 +#define N810BM_MIN_VOLTAGE_THRES 3200 /* Absolute minimum voltage threshold */
83 + * The battery size indicator ADC measures the resistance between
84 + * the battery BSI pin and ground. This is used to detect the battery
85 + * capacity, as the BSI resistor is related to capacity.
87 + * Manually measured lookup table.
88 + * Hard to measure, thus not very accurate.
90 + * Resistance | ADC value
91 + * ========================
99 + * Manually measured lookup table.
100 + * Hard to measure, thus not very accurate.
102 + * Voltage | ADC value
103 + * =====================
122 +/* PMM block ADC IDs */
123 +enum n810bm_pmm_adc_id {
124 + N810BM_PMM_ADC_BATVOLT = 0x01, /* Battery voltage */
125 + N810BM_PMM_ADC_CHGVOLT = 0x02, /* Charger voltage */
126 + N810BM_PMM_ADC_GND2 = 0x03, /* Ground 0V */
127 + N810BM_PMM_ADC_BSI = 0x04, /* Battery size indicator */
128 + N810BM_PMM_ADC_BATTEMP = 0x05, /* Battery temperature */
129 + N810BM_PMM_ADC_HEADSET = 0x06, /* Headset detection */
130 + N810BM_PMM_ADC_HOOKDET = 0x07, /* Hook detection */
131 + N810BM_PMM_ADC_LIGHTSENS = 0x08, /* Light sensor */
132 + N810BM_PMM_ADC_BATCURR = 0x0E, /* Battery current */
133 + N810BM_PMM_ADC_BKUPVOLT = 0x13, /* Backup battery voltage */
134 + N810BM_PMM_ADC_LIGHTTEMP = 0x14, /* Light sensor temperature */
135 + N810BM_PMM_ADC_RFGP = 0x15, /* RF GP */
136 + N810BM_PMM_ADC_WBTX = 0x16, /* Wideband TX detection */
137 + N810BM_PMM_ADC_RETUTEMP = 0x17, /* RETU chip temperature */
138 + N810BM_PMM_ADC_0xFE = 0xFE,
141 +struct n810bm_adc_calib {
142 + enum n810bm_pmm_adc_id id;
151 +struct n810bm_calib {
152 + struct n810bm_adc_calib adc[25];
155 +enum n810bm_capacity {
156 + N810BM_CAP_UNKNOWN = -1,
157 + N810BM_CAP_NONE = 0,
158 + N810BM_CAP_1500MAH = 1500, /* 1500 mAh battery */
161 +enum n810bm_notify_flags {
162 + N810BM_NOTIFY_battery_charging,
163 + N810BM_NOTIFY_charger_pwm,
167 + bool battery_present; /* A battery is inserted */
168 + bool charger_present; /* The charger is connected */
169 + enum n810bm_capacity capacity; /* The capacity of the inserted battery (if any) */
171 + bool charger_enabled; /* Want to charge? */
172 + struct lipocharge charger; /* Charger subsystem */
173 + unsigned int active_current_pwm; /* Active value of TAHVO_REG_CHGCURR */
174 + int current_measure_enabled; /* Current measure enable refcount */
176 + struct platform_device *pdev;
177 + struct n810bm_calib calib; /* Calibration data */
179 + bool verbose_charge_log; /* Verbose charge logging */
181 + unsigned long notify_flags;
182 + struct work_struct notify_work;
183 + struct work_struct currmeas_irq_work;
184 + struct delayed_work periodic_check_work;
186 + bool initialized; /* The hardware was initialized */
187 + struct mutex mutex;
190 +static void n810bm_notify_battery_charging(struct n810bm *bm);
191 +static void n810bm_notify_charger_pwm(struct n810bm *bm);
194 +static struct platform_device *n810bm_retu_device;
195 +static struct platform_device *n810bm_tahvo_device;
198 +static inline struct n810bm * device_to_n810bm(struct device *dev)
200 + struct platform_device *pdev = to_platform_device(dev);
201 + struct n810bm *bm = platform_get_drvdata(pdev);
206 +static inline bool n810bm_known_battery_present(struct n810bm *bm)
208 + return bm->battery_present &&
209 + bm->capacity != N810BM_CAP_UNKNOWN &&
210 + bm->capacity != N810BM_CAP_NONE;
213 +static NORET_TYPE void n810bm_emergency(struct n810bm *bm, const char *message) ATTRIB_NORET;
214 +static void n810bm_emergency(struct n810bm *bm, const char *message)
216 + printk(KERN_EMERG "n810 battery management fatal fault: %s\n", message);
220 +static u16 tahvo_read(struct n810bm *bm, unsigned int reg)
222 + return tahvo_read_reg(reg);
225 +static void tahvo_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
227 + tahvo_set_clear_reg_bits(reg, set, mask);
230 +static inline void tahvo_write(struct n810bm *bm, unsigned int reg, u16 value)
232 + unsigned long flags;
234 + spin_lock_irqsave(&tahvo_lock, flags);
235 + tahvo_write_reg(reg, value);
236 + spin_unlock_irqrestore(&tahvo_lock, flags);
239 +static inline void tahvo_set(struct n810bm *bm, unsigned int reg, u16 mask)
241 + tahvo_set_clear_reg_bits(reg, mask, mask);
244 +static inline void tahvo_clear(struct n810bm *bm, unsigned int reg, u16 mask)
246 + tahvo_set_clear_reg_bits(reg, 0, mask);
249 +static u16 retu_read(struct n810bm *bm, unsigned int reg)
251 + return retu_read_reg(&n810bm_retu_device->dev, reg);
254 +static void retu_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
256 + retu_set_clear_reg_bits(&n810bm_retu_device->dev, reg, set, mask);
259 +static inline void retu_write(struct n810bm *bm, unsigned int reg, u16 value)
261 + retu_write_reg(&n810bm_retu_device->dev, reg, value);
264 +static int retu_adc_average(struct n810bm *bm, unsigned int chan,
265 + unsigned int nr_passes)
267 + unsigned int i, value = 0;
270 + if (WARN_ON(!nr_passes))
272 + for (i = 0; i < nr_passes; i++) {
273 + ret = retu_read_adc(&n810bm_retu_device->dev, chan);
278 + value /= nr_passes;
283 +static struct n810bm_adc_calib * n810bm_get_adc_calib(struct n810bm *bm,
284 + enum n810bm_pmm_adc_id id)
286 + unsigned int index = 0;
287 + struct n810bm_adc_calib *cal;
289 + if (id != N810BM_PMM_ADC_0xFE)
290 + index = (unsigned int)id + 1;
291 + if (index >= ARRAY_SIZE(bm->calib.adc))
294 + cal = &bm->calib.adc[index];
295 + WARN_ON(cal->id && cal->id != id);
300 +static int pmm_record_get(struct n810bm *bm,
301 + const struct firmware *pmm_block,
302 + void *buffer, size_t length,
303 + unsigned int group, unsigned int element, unsigned int offset)
305 + const u8 *pmm_area = pmm_block->data;
306 + u8 active_group_mask;
308 + if (pmm_block->size != N810BM_PMM_BLOCK_SIZE)
310 + if (group >= N810BM_PMM_BLOCK_SIZE / N810BM_PMM_GROUP_SIZE)
312 + if (element >= N810BM_PMM_GROUP_SIZE / N810BM_PMM_ELEM_SIZE)
314 + if (offset >= N810BM_PMM_ELEM_SIZE || length > N810BM_PMM_ELEM_SIZE ||
315 + length + offset > N810BM_PMM_ELEM_SIZE)
318 + active_group_mask = pmm_area[16];
319 + if (!(active_group_mask & (1 << group))) {
320 + dev_dbg(&bm->pdev->dev, "pwm_record_get: Requested group %u, "
321 + "but group is not active", group);
326 + pmm_area + group * N810BM_PMM_GROUP_SIZE
327 + + element * N810BM_PMM_ELEM_SIZE
334 +/* PMM block group 1 element */
335 +struct group1_element {
344 +static int extract_group1_elem(struct n810bm *bm,
345 + const struct firmware *pmm_block,
346 + const enum n810bm_pmm_adc_id *pmm_adc_ids, size_t nr_pmm_adc_ids,
347 + u32 field1_mask, u32 field2_mask)
349 + struct group1_element elem;
351 + unsigned int i, element_nr;
352 + struct n810bm_adc_calib *adc_calib;
354 + for (i = 0; i < nr_pmm_adc_ids; i++) {
355 + element_nr = (unsigned int)(pmm_adc_ids[i]) + 3;
357 + err = pmm_record_get(bm, pmm_block, &elem, sizeof(elem),
361 + adc_calib = n810bm_get_adc_calib(bm, elem.id);
363 + dev_err(&bm->pdev->dev, "extract_group1_elem: "
364 + "Could not get calib element for 0x%02X",
369 + if (adc_calib->flags == elem.flags) {
370 + adc_calib->field1 = le32_to_cpu(elem.field1) & field1_mask;
371 + adc_calib->field2 = le32_to_cpu(elem.field2) & field2_mask;
373 + dev_dbg(&bm->pdev->dev, "extract_group1_elem: "
374 + "Not extracting fields due to flags mismatch: "
375 + "0x%02X vs 0x%02X",
376 + adc_calib->flags, elem.flags);
383 +static int n810bm_parse_pmm_group1(struct n810bm *bm,
384 + const struct firmware *pmm_block)
386 + struct n810bm_adc_calib *adc_calib;
387 + struct group1_element elem;
390 + static const enum n810bm_pmm_adc_id pmm_adc_ids_1[] = {
391 + N810BM_PMM_ADC_BATVOLT,
392 + N810BM_PMM_ADC_CHGVOLT,
393 + N810BM_PMM_ADC_BKUPVOLT,
394 + N810BM_PMM_ADC_BATCURR,
396 + static const enum n810bm_pmm_adc_id pmm_adc_ids_2[] = {
397 + N810BM_PMM_ADC_BSI,
399 + static const enum n810bm_pmm_adc_id pmm_adc_ids_3[] = {
400 + N810BM_PMM_ADC_BATTEMP,
403 + /* Parse element 2 */
404 + err = pmm_record_get(bm, pmm_block, &elem, sizeof(elem),
407 + dev_err(&bm->pdev->dev,
408 + "PMM: Failed to get group 1 / element 2");
411 + if (elem.id == N810BM_PMM_ADC_0xFE && elem.flags == 0x05) {
412 + adc_calib = n810bm_get_adc_calib(bm, elem.id);
414 + dev_err(&bm->pdev->dev,
415 + "calib extract: Failed to get 0xFE calib");
418 + adc_calib->id = elem.id;
419 + adc_calib->flags = elem.flags;
420 + adc_calib->field1 = le32_to_cpu(elem.field1);
421 + adc_calib->field2 = le32_to_cpu(elem.field2);
424 + err = extract_group1_elem(bm, pmm_block,
425 + pmm_adc_ids_1, ARRAY_SIZE(pmm_adc_ids_1),
426 + 0xFFFFFFFF, 0xFFFFFFFF);
429 + err = extract_group1_elem(bm, pmm_block,
430 + pmm_adc_ids_2, ARRAY_SIZE(pmm_adc_ids_2),
434 + err = extract_group1_elem(bm, pmm_block,
435 + pmm_adc_ids_3, ARRAY_SIZE(pmm_adc_ids_3),
436 + 0xFFFFFFFF, 0x0000FFFF);
443 +static int n810bm_parse_pmm_group2(struct n810bm *bm,
444 + const struct firmware *pmm_block)
446 + dev_err(&bm->pdev->dev, "TODO: CAL BME PMM group 2 parser not implemented, yet");
447 + return -EOPNOTSUPP;
450 +static void n810bm_adc_calib_set_defaults(struct n810bm *bm)
452 + struct n810bm_adc_calib *adc_calib;
455 + static const struct n810bm_adc_calib defaults[] = {
456 + /* ADC group-nr 0 */
458 + .id = N810BM_PMM_ADC_HEADSET,
462 + .id = N810BM_PMM_ADC_HOOKDET,
466 + .id = N810BM_PMM_ADC_RFGP,
470 + .id = N810BM_PMM_ADC_LIGHTSENS,
474 + .id = N810BM_PMM_ADC_WBTX,
478 + .id = N810BM_PMM_ADC_RETUTEMP,
482 + .id = N810BM_PMM_ADC_GND2,
486 + /* ADC group-nr 1 */
488 + .id = N810BM_PMM_ADC_0xFE,
494 + .id = N810BM_PMM_ADC_BATVOLT,
500 + .id = N810BM_PMM_ADC_CHGVOLT,
506 + .id = N810BM_PMM_ADC_BKUPVOLT,
512 + .id = N810BM_PMM_ADC_BATCURR,
518 + /* ADC group-nr 2 */
520 + .id = N810BM_PMM_ADC_BSI,
526 + /* ADC group-nr 3 */
528 + .id = N810BM_PMM_ADC_BATTEMP,
531 + .field1 = 265423000,
534 + /* ADC group-nr 4 */
536 + .id = N810BM_PMM_ADC_LIGHTTEMP,
539 + .field1 = 19533778,
540 + .field2 = 308019670,
546 + /* Clear the array */
547 + memset(&bm->calib.adc, 0, sizeof(bm->calib.adc));
548 + for (i = 0; i < ARRAY_SIZE(bm->calib.adc); i++)
549 + bm->calib.adc[i].flags = 0xFF;
551 + /* Copy the defaults */
552 + for (i = 0; i < ARRAY_SIZE(defaults); i++) {
553 + adc_calib = n810bm_get_adc_calib(bm, defaults[i].id);
554 + if (WARN_ON(!adc_calib))
556 + *adc_calib = defaults[i];
560 +static int n810bm_parse_pmm_block(struct n810bm *bm,
561 + const struct firmware *pmm_block)
565 + unsigned int i, count;
566 + struct n810bm_adc_calib *adc_calib;
568 + /* Initialize to defaults */
569 + n810bm_adc_calib_set_defaults(bm);
571 + /* Parse the PMM data */
572 + err = pmm_record_get(bm, pmm_block, &byte, sizeof(byte),
573 + 1, 0, 0); /* group 1 / element 0 */
574 + err |= (byte != 0x01);
575 + err |= pmm_record_get(bm, pmm_block, &byte, sizeof(byte),
576 + 1, 1, 0); /* group 1 / element 1 */
577 + err |= (byte != 0x01);
579 + err = n810bm_parse_pmm_group2(bm, pmm_block);
581 + err = n810bm_parse_pmm_group1(bm, pmm_block);
585 + /* Sanity checks */
586 + for (i = 0, count = 0; i < ARRAY_SIZE(bm->calib.adc); i++) {
587 + adc_calib = &bm->calib.adc[i];
588 + if (adc_calib->flags == 0xFF)
590 + switch (adc_calib->id) {
591 + case N810BM_PMM_ADC_BATVOLT:
592 + if (adc_calib->field1 < 2400 ||
593 + adc_calib->field1 > 2700)
594 + goto value_check_fail;
595 + if (adc_calib->field2 < 20000 ||
596 + adc_calib->field2 > 23000)
597 + goto value_check_fail;
600 + case N810BM_PMM_ADC_BSI:
601 + if (adc_calib->field1 < 1100 ||
602 + adc_calib->field1 > 1300)
603 + goto value_check_fail;
606 + case N810BM_PMM_ADC_BATCURR:
607 + if (adc_calib->field2 < 7000 ||
608 + adc_calib->field2 > 12000)
609 + goto value_check_fail;
612 + case N810BM_PMM_ADC_0xFE:
613 + if ((s32)adc_calib->field1 > 14 ||
614 + (s32)adc_calib->field1 < -14)
615 + goto value_check_fail;
616 + if (adc_calib->field2 < 13000 ||
617 + adc_calib->field2 > 13350)
618 + goto value_check_fail;
621 + case N810BM_PMM_ADC_CHGVOLT:
622 + case N810BM_PMM_ADC_BATTEMP:
623 + case N810BM_PMM_ADC_BKUPVOLT:
626 + case N810BM_PMM_ADC_GND2:
627 + case N810BM_PMM_ADC_HOOKDET:
628 + case N810BM_PMM_ADC_LIGHTSENS:
629 + case N810BM_PMM_ADC_HEADSET:
630 + case N810BM_PMM_ADC_LIGHTTEMP:
631 + case N810BM_PMM_ADC_RFGP:
632 + case N810BM_PMM_ADC_WBTX:
633 + case N810BM_PMM_ADC_RETUTEMP:
636 + dev_dbg(&bm->pdev->dev,
637 + "ADC 0x%02X calib: 0x%02X 0x%02X 0x%08X 0x%08X 0x%04X 0x%04X",
638 + adc_calib->id, adc_calib->flags, adc_calib->adc_groupnr,
639 + adc_calib->field1, adc_calib->field2,
640 + adc_calib->field3, adc_calib->field4);
643 + dev_err(&bm->pdev->dev, "PMM sanity check: Did not find "
644 + "all required values (count=%u)", count);
651 + dev_err(&bm->pdev->dev, "PMM image sanity check failed "
652 + "(id=%02X, field1=%08X, field2=%08X)",
653 + adc_calib->id, adc_calib->field1, adc_calib->field2);
658 +/* Set the current measure timer that triggers on Tahvo IRQ 7
659 + * An interval of zero disables the timer. */
660 +static void n810bm_set_current_measure_timer(struct n810bm *bm,
661 + u16 millisec_interval)
663 + u16 value = millisec_interval;
665 + if (value <= 0xF905) {
666 + value = ((u64)0x10624DD3 * (u64)(value + 0xF9)) >> 32;
671 + tahvo_write(bm, TAHVO_REG_BATCURRTIMER, value & 0xFF);
673 + tahvo_set(bm, TAHVO_REG_CHGCTL,
674 + TAHVO_REG_CHGCTL_CURTIMRST);
675 + tahvo_clear(bm, TAHVO_REG_CHGCTL,
676 + TAHVO_REG_CHGCTL_CURTIMRST);
678 + if (millisec_interval)
679 + tahvo_enable_irq(TAHVO_INT_BATCURR);
681 + tahvo_disable_irq(TAHVO_INT_BATCURR);
683 + //TODO also do a software timer for safety.
686 +static void n810bm_enable_current_measure(struct n810bm *bm)
688 + WARN_ON(bm->current_measure_enabled < 0);
689 + if (!bm->current_measure_enabled) {
690 + /* Enable the current measurement circuitry */
691 + tahvo_set(bm, TAHVO_REG_CHGCTL,
692 + TAHVO_REG_CHGCTL_CURMEAS);
693 + dev_dbg(&bm->pdev->dev,
694 + "Current measurement circuitry enabled");
696 + bm->current_measure_enabled++;
699 +static void n810bm_disable_current_measure(struct n810bm *bm)
701 + bm->current_measure_enabled--;
702 + WARN_ON(bm->current_measure_enabled < 0);
703 + if (!bm->current_measure_enabled) {
704 + /* Disable the current measurement circuitry */
705 + tahvo_clear(bm, TAHVO_REG_CHGCTL,
706 + TAHVO_REG_CHGCTL_CURMEAS);
707 + dev_dbg(&bm->pdev->dev,
708 + "Current measurement circuitry disabled");
712 +/* Measure the actual battery current. Returns a signed value in mA.
713 + * Does only work, if current measurement was enabled. */
714 +static int n810bm_measure_batt_current(struct n810bm *bm)
717 + int adc = 0, ma, i;
719 + if (WARN_ON(bm->current_measure_enabled <= 0))
721 + for (i = 0; i < 3; i++) {
722 + retval = tahvo_read(bm, TAHVO_REG_BATCURR);
723 + adc += (s16)retval; /* Value is signed */
727 + //TODO convert to mA
733 +/* Requires bm->mutex locked */
734 +static int n810bm_measure_batt_current_async(struct n810bm *bm)
737 + bool charging = lipocharge_is_charging(&bm->charger);
739 + n810bm_enable_current_measure(bm);
741 + WARN_ON(bm->active_current_pwm != 0);
742 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
743 + TAHVO_REG_CHGCTL_EN |
744 + TAHVO_REG_CHGCTL_PWMOVR |
745 + TAHVO_REG_CHGCTL_PWMOVRZERO,
746 + TAHVO_REG_CHGCTL_EN |
747 + TAHVO_REG_CHGCTL_PWMOVR |
748 + (charging ? 0 : TAHVO_REG_CHGCTL_PWMOVRZERO));
749 + ma = n810bm_measure_batt_current(bm);
750 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
751 + TAHVO_REG_CHGCTL_EN |
752 + TAHVO_REG_CHGCTL_PWMOVR |
753 + TAHVO_REG_CHGCTL_PWMOVRZERO,
754 + (charging ? TAHVO_REG_CHGCTL_EN : 0));
755 + n810bm_disable_current_measure(bm);
760 +static int adc_sanity_check(struct n810bm *bm, unsigned int channel)
764 + value = retu_read_adc(&n810bm_retu_device->dev, channel);
766 + dev_err(&bm->pdev->dev, "Failed to read GND ADC channel %u",
770 + dev_dbg(&bm->pdev->dev,
771 + "GND ADC channel %u sanity check got value: %d",
774 + n810bm_emergency(bm, "GND ADC sanity check failed");
781 +static int n810bm_check_adc_sanity(struct n810bm *bm)
785 + /* Discard one conversion */
786 + retu_write(bm, RETU_REG_ADCSCR, 0);
787 + retu_read_adc(&n810bm_retu_device->dev, RETU_ADC_GND2);
789 + err = adc_sanity_check(bm, RETU_ADC_GND2);
796 +/* Measure the battery voltage. Returns the value in mV (or negative value on error). */
797 +static int n810bm_measure_batt_voltage(struct n810bm *bm)
801 + const unsigned int scale = 1000;
803 + adc = retu_adc_average(bm, RETU_ADC_BATTVOLT, 5);
808 + mv = 2800 + ((adc - 0x37) * (((4200 - 2800) * scale) / (0x236 - 0x37))) / scale;
810 + //TODO compensate for power consumption
811 + //TODO honor calibration values
816 +/* Measure the charger voltage. Returns the value in mV (or negative value on error). */
817 +static int n810bm_measure_charger_voltage(struct n810bm *bm)
822 + adc = retu_adc_average(bm, RETU_ADC_CHGVOLT, 5);
825 + //TODO convert to mV
831 +/* Measure backup battery voltage. Returns the value in mV (or negative value on error). */
832 +static int n810bm_measure_backup_batt_voltage(struct n810bm *bm)
837 + adc = retu_adc_average(bm, RETU_ADC_BKUPVOLT, 3);
840 + //TODO convert to mV
846 +/* Measure the battery temperature. Returns the value in K (or negative value on error). */
847 +static int n810bm_measure_batt_temp(struct n810bm *bm)
852 + adc = retu_adc_average(bm, RETU_ADC_BATTEMP, 3);
855 + //TODO convert to K
861 +/* Read the battery capacity via BSI pin. */
862 +static enum n810bm_capacity n810bm_read_batt_capacity(struct n810bm *bm)
865 + const unsigned int hyst = 20;
867 + adc = retu_adc_average(bm, RETU_ADC_BSI, 5);
869 + dev_err(&bm->pdev->dev, "Failed to read BSI ADC");
870 + return N810BM_CAP_UNKNOWN;
873 + if (adc >= 0x3B5 - hyst && adc <= 0x3B5 + hyst)
874 + return N810BM_CAP_1500MAH;
876 + dev_err(&bm->pdev->dev, "Capacity indicator 0x%X unknown", adc);
878 + return N810BM_CAP_UNKNOWN;
881 +/* Convert a battery voltage (in mV) to percentage. */
882 +static unsigned int n810bm_mvolt2percent(unsigned int mv)
884 + const unsigned int minv = 3700;
885 + const unsigned int maxv = 4150;
886 + unsigned int percent;
888 + mv = clamp(mv, minv, maxv);
889 + percent = (mv - minv) * 100 / (maxv - minv);
894 +static void n810bm_start_charge(struct n810bm *bm)
898 + WARN_ON(!bm->battery_present);
899 + WARN_ON(!bm->charger_present);
901 + /* Set PWM to zero */
902 + bm->active_current_pwm = 0;
903 + tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
905 + /* Charge global enable */
906 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
907 + TAHVO_REG_CHGCTL_EN |
908 + TAHVO_REG_CHGCTL_PWMOVR |
909 + TAHVO_REG_CHGCTL_PWMOVRZERO,
910 + TAHVO_REG_CHGCTL_EN);
912 + WARN_ON((int)bm->capacity <= 0);
913 + bm->charger.capacity = bm->capacity;
914 + err = lipocharge_start(&bm->charger);
917 + /* Initialize current measurement circuitry */
918 + n810bm_enable_current_measure(bm);
919 + n810bm_set_current_measure_timer(bm, 250);
921 + dev_info(&bm->pdev->dev, "Charging battery");
922 + n810bm_notify_charger_pwm(bm);
923 + n810bm_notify_battery_charging(bm);
926 +static void n810bm_stop_charge(struct n810bm *bm)
928 + if (lipocharge_is_charging(&bm->charger)) {
929 + n810bm_set_current_measure_timer(bm, 0);
930 + n810bm_disable_current_measure(bm);
932 + lipocharge_stop(&bm->charger);
934 + /* Set PWM to zero */
935 + bm->active_current_pwm = 0;
936 + tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
938 + /* Charge global disable */
939 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
940 + TAHVO_REG_CHGCTL_EN |
941 + TAHVO_REG_CHGCTL_PWMOVR |
942 + TAHVO_REG_CHGCTL_PWMOVRZERO,
945 + dev_info(&bm->pdev->dev, "Not charging battery");
946 + n810bm_notify_charger_pwm(bm);
947 + n810bm_notify_battery_charging(bm);
950 +/* Periodic check */
951 +static void n810bm_periodic_check_work(struct work_struct *work)
953 + struct n810bm *bm = container_of(to_delayed_work(work),
954 + struct n810bm, periodic_check_work);
956 + bool battery_was_present, charger_was_present;
959 + mutex_lock(&bm->mutex);
961 + status = retu_read(bm, RETU_REG_STATUS);
962 + battery_was_present = bm->battery_present;
963 + charger_was_present = bm->charger_present;
964 + bm->battery_present = !!(status & RETU_REG_STATUS_BATAVAIL);
965 + bm->charger_present = !!(status & RETU_REG_STATUS_CHGPLUG);
967 + if (bm->battery_present != battery_was_present) {
968 + /* Battery state changed */
969 + if (bm->battery_present) {
970 + bm->capacity = n810bm_read_batt_capacity(bm);
971 + if (bm->capacity == N810BM_CAP_UNKNOWN) {
972 + dev_err(&bm->pdev->dev, "Unknown battery detected");
974 + dev_info(&bm->pdev->dev, "Detected %u mAh battery",
975 + (unsigned int)bm->capacity);
978 + bm->capacity = N810BM_CAP_NONE;
979 + dev_info(&bm->pdev->dev, "The main battery was removed");
980 + //TODO disable charging
984 + if (bm->charger_present != charger_was_present) {
985 + /* Charger state changed */
986 + dev_info(&bm->pdev->dev, "The charger was %s",
987 + bm->charger_present ? "plugged in" : "removed");
990 + if ((bm->battery_present && !bm->charger_present) ||
991 + !n810bm_known_battery_present(bm)){
992 + /* We're draining the battery */
993 + mv = n810bm_measure_batt_voltage(bm);
995 + n810bm_emergency(bm,
996 + "check: Failed to measure voltage");
998 + if (mv < N810BM_MIN_VOLTAGE_THRES) {
999 + n810bm_emergency(bm,
1000 + "check: Minimum voltage threshold reached");
1004 + if (bm->charger_present && n810bm_known_battery_present(bm)) {
1005 + /* Known battery and charger are connected */
1006 + if (bm->charger_enabled) {
1007 + /* Charger is enabled */
1008 + if (!lipocharge_is_charging(&bm->charger)) {
1009 + //TODO start charging, if battery is below some threshold
1010 + n810bm_start_charge(bm);
1015 + if (lipocharge_is_charging(&bm->charger) && !bm->charger_present) {
1016 + /* Charger was unplugged. */
1017 + n810bm_stop_charge(bm);
1020 + mutex_unlock(&bm->mutex);
1021 + schedule_delayed_work(&bm->periodic_check_work,
1022 + round_jiffies_relative(N810BM_CHECK_INTERVAL));
1026 +static void n810bm_adc_irq_handler(unsigned long data)
1028 + struct n810bm *bm = (struct n810bm *)data;
1030 + retu_ack_irq(RETU_INT_ADCS);
1032 +dev_info(&bm->pdev->dev, "ADC interrupt triggered\n");
1036 +static void n810bm_tahvo_current_measure_work(struct work_struct *work)
1038 + struct n810bm *bm = container_of(work, struct n810bm, currmeas_irq_work);
1039 + int res, ma, mv, temp;
1041 + mutex_lock(&bm->mutex);
1042 + if (!lipocharge_is_charging(&bm->charger))
1045 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1046 + TAHVO_REG_CHGCTL_PWMOVR |
1047 + TAHVO_REG_CHGCTL_PWMOVRZERO,
1048 + TAHVO_REG_CHGCTL_PWMOVR);
1049 + ma = n810bm_measure_batt_current(bm);
1050 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1051 + TAHVO_REG_CHGCTL_PWMOVR |
1052 + TAHVO_REG_CHGCTL_PWMOVRZERO,
1053 + TAHVO_REG_CHGCTL_PWMOVR |
1054 + TAHVO_REG_CHGCTL_PWMOVRZERO);
1056 + mv = n810bm_measure_batt_voltage(bm);
1057 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1058 + TAHVO_REG_CHGCTL_PWMOVR |
1059 + TAHVO_REG_CHGCTL_PWMOVRZERO,
1061 + temp = n810bm_measure_batt_temp(bm);
1062 + if (WARN_ON(mv < 0))
1064 + if (WARN_ON(temp < 0))
1067 + if (bm->verbose_charge_log) {
1068 + dev_info(&bm->pdev->dev,
1069 + "Battery charge state: %d mV, %d mA (%s)",
1071 + (ma <= 0) ? "discharging" : "charging");
1073 + res = lipocharge_update_state(&bm->charger, mv, ma, temp);
1076 + dev_info(&bm->pdev->dev, "Battery fully charged");
1077 + n810bm_stop_charge(bm);
1080 + mutex_unlock(&bm->mutex);
1083 +static void n810bm_tahvo_current_measure_irq_handler(unsigned long data)
1085 + struct n810bm *bm = (struct n810bm *)data;
1087 + tahvo_ack_irq(TAHVO_INT_BATCURR);
1088 + schedule_work(&bm->currmeas_irq_work);
1091 +#define DEFINE_ATTR_NOTIFY(attr_name) \
1092 + void n810bm_notify_##attr_name(struct n810bm *bm) \
1094 + set_bit(N810BM_NOTIFY_##attr_name, &bm->notify_flags); \
1096 + schedule_work(&bm->notify_work); \
1099 +#define DEFINE_SHOW_INT_FUNC(name, member) \
1100 + static ssize_t n810bm_attr_##name##_show(struct device *dev, \
1101 + struct device_attribute *attr, \
1104 + struct n810bm *bm = device_to_n810bm(dev); \
1107 + mutex_lock(&bm->mutex); \
1108 + count = snprintf(buf, PAGE_SIZE, "%d\n", (int)(bm->member)); \
1109 + mutex_unlock(&bm->mutex); \
1114 +#define DEFINE_STORE_INT_FUNC(name, member) \
1115 + static ssize_t n810bm_attr_##name##_store(struct device *dev, \
1116 + struct device_attribute *attr,\
1117 + const char *buf, size_t count)\
1119 + struct n810bm *bm = device_to_n810bm(dev); \
1123 + mutex_lock(&bm->mutex); \
1124 + err = strict_strtol(buf, 0, &val); \
1126 + bm->member = (typeof(bm->member))val; \
1127 + mutex_unlock(&bm->mutex); \
1129 + return err ? err : count; \
1132 +#define DEFINE_ATTR_SHOW_INT(name, member) \
1133 + DEFINE_SHOW_INT_FUNC(name, member) \
1134 + static DEVICE_ATTR(name, S_IRUGO, \
1135 + n810bm_attr_##name##_show, NULL);
1137 +#define DEFINE_ATTR_SHOW_STORE_INT(name, member) \
1138 + DEFINE_SHOW_INT_FUNC(name, member) \
1139 + DEFINE_STORE_INT_FUNC(name, member) \
1140 + static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
1141 + n810bm_attr_##name##_show, \
1142 + n810bm_attr_##name##_store);
1144 +DEFINE_ATTR_SHOW_INT(battery_present, battery_present);
1145 +DEFINE_ATTR_SHOW_INT(charger_present, charger_present);
1146 +DEFINE_ATTR_SHOW_INT(charger_pwm, active_current_pwm);
1147 +static DEFINE_ATTR_NOTIFY(charger_pwm);
1148 +DEFINE_ATTR_SHOW_STORE_INT(charger_enable, charger_enabled);
1149 +DEFINE_ATTR_SHOW_STORE_INT(charger_verbose, verbose_charge_log);
1151 +static ssize_t n810bm_attr_battery_charging(struct device *dev,
1152 + struct device_attribute *attr,
1155 + struct n810bm *bm = device_to_n810bm(dev);
1158 + mutex_lock(&bm->mutex);
1159 + count = snprintf(buf, PAGE_SIZE, "%d\n",
1160 + (int)lipocharge_is_charging(&bm->charger));
1161 + mutex_unlock(&bm->mutex);
1165 +static DEVICE_ATTR(battery_charging, S_IRUGO,
1166 + n810bm_attr_battery_charging, NULL);
1167 +static DEFINE_ATTR_NOTIFY(battery_charging);
1169 +static ssize_t n810bm_attr_battery_level_show(struct device *dev,
1170 + struct device_attribute *attr,
1173 + struct n810bm *bm = device_to_n810bm(dev);
1174 + ssize_t count = -ENODEV;
1177 + mutex_lock(&bm->mutex);
1178 + if (!bm->battery_present || lipocharge_is_charging(&bm->charger))
1181 + millivolt = n810bm_measure_batt_voltage(bm);
1182 + if (millivolt >= 0) {
1183 + count = snprintf(buf, PAGE_SIZE, "%u\n",
1184 + n810bm_mvolt2percent(millivolt));
1186 + mutex_unlock(&bm->mutex);
1190 +static DEVICE_ATTR(battery_level, S_IRUGO,
1191 + n810bm_attr_battery_level_show, NULL);
1193 +static ssize_t n810bm_attr_battery_capacity_show(struct device *dev,
1194 + struct device_attribute *attr,
1197 + struct n810bm *bm = device_to_n810bm(dev);
1201 + mutex_lock(&bm->mutex);
1202 + if (n810bm_known_battery_present(bm))
1203 + capacity = (int)bm->capacity;
1204 + count = snprintf(buf, PAGE_SIZE, "%d\n", capacity);
1205 + mutex_unlock(&bm->mutex);
1209 +static DEVICE_ATTR(battery_capacity, S_IRUGO,
1210 + n810bm_attr_battery_capacity_show, NULL);
1212 +static ssize_t n810bm_attr_battery_temp_show(struct device *dev,
1213 + struct device_attribute *attr,
1216 + struct n810bm *bm = device_to_n810bm(dev);
1217 + ssize_t count = -ENODEV;
1220 + mutex_lock(&bm->mutex);
1221 + k = n810bm_measure_batt_temp(bm);
1223 + count = snprintf(buf, PAGE_SIZE, "%d\n", k);
1224 + mutex_unlock(&bm->mutex);
1228 +static DEVICE_ATTR(battery_temp, S_IRUGO,
1229 + n810bm_attr_battery_temp_show, NULL);
1231 +static ssize_t n810bm_attr_charger_voltage_show(struct device *dev,
1232 + struct device_attribute *attr,
1235 + struct n810bm *bm = device_to_n810bm(dev);
1236 + ssize_t count = -ENODEV;
1239 + mutex_lock(&bm->mutex);
1240 + if (bm->charger_present)
1241 + mv = n810bm_measure_charger_voltage(bm);
1243 + count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
1244 + mutex_unlock(&bm->mutex);
1248 +static DEVICE_ATTR(charger_voltage, S_IRUGO,
1249 + n810bm_attr_charger_voltage_show, NULL);
1251 +static ssize_t n810bm_attr_backup_battery_voltage_show(struct device *dev,
1252 + struct device_attribute *attr,
1255 + struct n810bm *bm = device_to_n810bm(dev);
1256 + ssize_t count = -ENODEV;
1259 + mutex_lock(&bm->mutex);
1260 + mv = n810bm_measure_backup_batt_voltage(bm);
1262 + count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
1263 + mutex_unlock(&bm->mutex);
1267 +static DEVICE_ATTR(backup_battery_voltage, S_IRUGO,
1268 + n810bm_attr_backup_battery_voltage_show, NULL);
1270 +static ssize_t n810bm_attr_battery_current_show(struct device *dev,
1271 + struct device_attribute *attr,
1274 + struct n810bm *bm = device_to_n810bm(dev);
1275 + ssize_t count = -ENODEV;
1278 + mutex_lock(&bm->mutex);
1279 + if (bm->battery_present)
1280 + ma = n810bm_measure_batt_current_async(bm);
1281 + count = snprintf(buf, PAGE_SIZE, "%d\n", ma);
1282 + mutex_unlock(&bm->mutex);
1286 +static DEVICE_ATTR(battery_current, S_IRUGO,
1287 + n810bm_attr_battery_current_show, NULL);
1289 +static const struct device_attribute *n810bm_attrs[] = {
1290 + &dev_attr_battery_present,
1291 + &dev_attr_battery_level,
1292 + &dev_attr_battery_charging,
1293 + &dev_attr_battery_current,
1294 + &dev_attr_battery_capacity,
1295 + &dev_attr_battery_temp,
1296 + &dev_attr_backup_battery_voltage,
1297 + &dev_attr_charger_present,
1298 + &dev_attr_charger_verbose,
1299 + &dev_attr_charger_voltage,
1300 + &dev_attr_charger_enable,
1301 + &dev_attr_charger_pwm,
1304 +static void n810bm_notify_work(struct work_struct *work)
1306 + struct n810bm *bm = container_of(work, struct n810bm, notify_work);
1307 + unsigned long notify_flags;
1309 + notify_flags = xchg(&bm->notify_flags, 0);
1312 +#define do_notify(attr_name) \
1314 + if (notify_flags & (1 << N810BM_NOTIFY_##attr_name)) { \
1315 + sysfs_notify(&bm->pdev->dev.kobj, NULL, \
1316 + dev_attr_##attr_name.attr.name); \
1320 + do_notify(battery_charging);
1321 + do_notify(charger_pwm);
1324 +static int n810bm_charger_set_current_pwm(struct lipocharge *c,
1325 + unsigned int duty_cycle)
1327 + struct n810bm *bm = container_of(c, struct n810bm, charger);
1328 + int err = -EINVAL;
1330 + WARN_ON(!mutex_is_locked(&bm->mutex));
1331 + if (WARN_ON(duty_cycle > 0xFF))
1333 + if (WARN_ON(!bm->charger_enabled))
1335 + if (WARN_ON(!bm->battery_present || !bm->charger_present))
1338 + if (duty_cycle != bm->active_current_pwm) {
1339 + bm->active_current_pwm = duty_cycle;
1340 + tahvo_write(bm, TAHVO_REG_CHGCURR, duty_cycle);
1341 + n810bm_notify_charger_pwm(bm);
1350 +static void n810bm_charger_emergency(struct lipocharge *c)
1352 + struct n810bm *bm = container_of(c, struct n810bm, charger);
1354 + n810bm_emergency(bm, "Battery charger fault");
1357 +static void n810bm_hw_exit(struct n810bm *bm)
1359 + n810bm_stop_charge(bm);
1360 + retu_write(bm, RETU_REG_ADCSCR, 0);
1363 +static int n810bm_hw_init(struct n810bm *bm)
1367 + err = n810bm_check_adc_sanity(bm);
1371 + n810bm_stop_charge(bm);
1376 +static void n810bm_cancel_and_flush_work(struct n810bm *bm)
1378 + cancel_delayed_work_sync(&bm->periodic_check_work);
1379 + cancel_work_sync(&bm->notify_work);
1380 + cancel_work_sync(&bm->currmeas_irq_work);
1381 + flush_scheduled_work();
1384 +static int n810bm_device_init(struct n810bm *bm)
1389 + bm->charger.rate = LIPORATE_p6C;
1390 + bm->charger.top_voltage = 4100;
1391 + bm->charger.duty_cycle_max = 0xFF;
1392 + bm->charger.set_current_pwm = n810bm_charger_set_current_pwm;
1393 + bm->charger.emergency = n810bm_charger_emergency;
1394 + lipocharge_init(&bm->charger, &bm->pdev->dev);
1396 + err = n810bm_hw_init(bm);
1399 + for (attr_index = 0; attr_index < ARRAY_SIZE(n810bm_attrs); attr_index++) {
1400 + err = device_create_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1402 + goto err_unwind_attrs;
1405 + err = retu_request_irq(RETU_INT_ADCS,
1406 + n810bm_adc_irq_handler,
1407 + (unsigned long)bm, "n810bm");
1409 + goto err_unwind_attrs;
1411 + err = tahvo_request_irq(TAHVO_INT_BATCURR,
1412 + n810bm_tahvo_current_measure_irq_handler,
1413 + (unsigned long)bm, "n810bm");
1415 + goto err_free_retu_irq;
1416 + tahvo_disable_irq(TAHVO_INT_BATCURR);
1418 + schedule_delayed_work(&bm->periodic_check_work,
1419 + round_jiffies_relative(N810BM_CHECK_INTERVAL));
1421 + bm->initialized = 1;
1422 + dev_info(&bm->pdev->dev, "Battery management initialized");
1427 +//XXX retu_free_irq(RETU_INT_ADCS);
1429 + for (attr_index--; attr_index >= 0; attr_index--)
1430 + device_remove_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1432 + n810bm_hw_exit(bm);
1434 + n810bm_cancel_and_flush_work(bm);
1439 +static void n810bm_device_exit(struct n810bm *bm)
1443 + if (!bm->initialized)
1446 + lipocharge_exit(&bm->charger);
1447 + tahvo_free_irq(TAHVO_INT_BATCURR);
1448 +//XXX retu_free_irq(RETU_INT_ADCS);
1449 + for (i = 0; i < ARRAY_SIZE(n810bm_attrs); i++)
1450 + device_remove_file(&bm->pdev->dev, n810bm_attrs[i]);
1452 + n810bm_cancel_and_flush_work(bm);
1454 + n810bm_hw_exit(bm);
1456 + bm->initialized = 0;
1459 +static void n810bm_pmm_block_found(const struct firmware *fw, void *context)
1461 + struct n810bm *bm = context;
1465 + dev_err(&bm->pdev->dev,
1466 + "CAL PMM block image file not found");
1469 + if (fw->size != N810BM_PMM_BLOCK_SIZE ||
1470 + memcmp(fw->data, "BME-PMM-BLOCK01", 15) != 0) {
1471 + dev_err(&bm->pdev->dev,
1472 + "CAL PMM block image file has an invalid format");
1476 + err = n810bm_parse_pmm_block(bm, fw);
1479 + release_firmware(fw);
1481 + err = n810bm_device_init(bm);
1483 + dev_err(&bm->pdev->dev,
1484 + "Failed to initialized battery management (%d)", err);
1490 + release_firmware(fw);
1495 +static int __devinit n810bm_probe(void)
1497 + struct n810bm *bm;
1500 + if (!n810bm_retu_device || !n810bm_tahvo_device)
1503 + bm = kzalloc(sizeof(*bm), GFP_KERNEL);
1506 + bm->pdev = n810bm_retu_device;
1507 + platform_set_drvdata(n810bm_retu_device, bm);
1508 + platform_set_drvdata(n810bm_tahvo_device, bm);
1509 + mutex_init(&bm->mutex);
1510 + INIT_DELAYED_WORK(&bm->periodic_check_work, n810bm_periodic_check_work);
1511 + INIT_WORK(&bm->notify_work, n810bm_notify_work);
1512 + INIT_WORK(&bm->currmeas_irq_work, n810bm_tahvo_current_measure_work);
1514 + dev_info(&bm->pdev->dev, "Requesting CAL BME PMM block firmware file "
1515 + N810BM_PMM_BLOCK_FILENAME);
1516 + err = request_firmware_nowait(THIS_MODULE, 1,
1517 + N810BM_PMM_BLOCK_FILENAME,
1518 + &bm->pdev->dev, GFP_KERNEL,
1519 + bm, n810bm_pmm_block_found);
1521 + dev_err(&bm->pdev->dev,
1522 + "Failed to request CAL PMM block image file (%d)", err);
1534 +static void __devexit n810bm_remove(void)
1536 + struct n810bm *bm;
1538 + if (!n810bm_retu_device || !n810bm_tahvo_device)
1540 + bm = platform_get_drvdata(n810bm_retu_device);
1542 + n810bm_device_exit(bm);
1545 + platform_set_drvdata(n810bm_retu_device, NULL);
1546 + platform_set_drvdata(n810bm_tahvo_device, NULL);
1549 +static int __devinit n810bm_retu_probe(struct platform_device *pdev)
1551 + n810bm_retu_device = pdev;
1552 + return n810bm_probe();
1555 +static int __devexit n810bm_retu_remove(struct platform_device *pdev)
1558 + n810bm_retu_device = NULL;
1562 +static int __devinit n810bm_tahvo_probe(struct platform_device *pdev)
1564 + n810bm_tahvo_device = pdev;
1565 + return n810bm_probe();
1568 +static int __devexit n810bm_tahvo_remove(struct platform_device *pdev)
1571 + n810bm_tahvo_device = NULL;
1575 +static struct platform_driver n810bm_retu_driver = {
1576 + .remove = __devexit_p(n810bm_retu_remove),
1578 + .name = "retu-n810bm",
1582 +static struct platform_driver n810bm_tahvo_driver = {
1583 + .remove = __devexit_p(n810bm_tahvo_remove),
1585 + .name = "tahvo-n810bm",
1589 +/* FIXME: for now alloc the device here... */
1590 +static struct platform_device n810bm_tahvo_dev = {
1591 + .name = "tahvo-n810bm",
1595 +static int __init n810bm_modinit(void)
1600 + err = platform_device_register(&n810bm_tahvo_dev);
1604 + err = platform_driver_probe(&n810bm_retu_driver, n810bm_retu_probe);
1607 + err = platform_driver_probe(&n810bm_tahvo_driver, n810bm_tahvo_probe);
1609 + platform_driver_unregister(&n810bm_retu_driver);
1615 +module_init(n810bm_modinit);
1617 +static void __exit n810bm_modexit(void)
1620 + platform_device_unregister(&n810bm_tahvo_dev);
1622 + platform_driver_unregister(&n810bm_tahvo_driver);
1623 + platform_driver_unregister(&n810bm_retu_driver);
1625 +module_exit(n810bm_modexit);
1627 +MODULE_DESCRIPTION("Nokia n810 battery management");
1628 +MODULE_FIRMWARE(N810BM_PMM_BLOCK_FILENAME);
1629 +MODULE_LICENSE("GPL");
1630 +MODULE_AUTHOR("Michael Buesch");
1631 Index: linux-2.6.38-rc6/drivers/cbus/lipocharge.c
1632 ===================================================================
1633 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
1634 +++ linux-2.6.38-rc6/drivers/cbus/lipocharge.c 2011-02-27 01:05:20.246990058 +0100
1637 + * Generic LIPO battery charger
1639 + * Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
1641 + * This program is free software; you can redistribute it and/or
1642 + * modify it under the terms of the GNU General Public License
1643 + * as published by the Free Software Foundation; either version 2
1644 + * of the License, or (at your option) any later version.
1646 + * This program is distributed in the hope that it will be useful,
1647 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1648 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1649 + * GNU General Public License for more details.
1654 +#include "lipocharge.h"
1656 +#include <linux/slab.h>
1659 +/* Hysteresis constants */
1660 +#define CURRENT_HYST 30 /* mA */
1661 +#define VOLTAGE_HYST 10 /* mV */
1663 +/* Threshold constants */
1664 +#define FINISH_CURRENT_PERCENT 3
1667 +/* Returns the requested first-stage charge current in mA */
1668 +static inline unsigned int get_stage1_charge_current(struct lipocharge *c)
1670 + /* current = (capacity * C) */
1671 + return c->capacity * c->rate / 1000;
1674 +void lipocharge_init(struct lipocharge *c, struct device *dev)
1677 + c->state = LIPO_IDLE;
1680 +void lipocharge_exit(struct lipocharge *c)
1682 + c->state = LIPO_IDLE;
1685 +int lipocharge_start(struct lipocharge *c)
1689 + if (c->state != LIPO_IDLE)
1691 + if (!c->set_current_pwm || !c->emergency)
1693 + if (!c->top_voltage || c->top_voltage > 4200)
1696 + c->active_duty_cycle = 0;
1697 + err = c->set_current_pwm(c, c->active_duty_cycle);
1700 + c->state = LIPO_FIRST_STAGE;
1705 +void lipocharge_stop(struct lipocharge *c)
1707 + if (c->state == LIPO_IDLE)
1709 + c->state = LIPO_IDLE;
1712 +static int lipocharge_increase_current(struct lipocharge *c,
1713 + unsigned int inc_permille)
1715 + int old_pwm, new_pwm;
1717 + if (c->active_duty_cycle >= c->duty_cycle_max)
1720 + old_pwm = c->active_duty_cycle;
1721 + new_pwm = old_pwm + (c->duty_cycle_max * inc_permille / 1000);
1722 + new_pwm = min(new_pwm, (int)c->duty_cycle_max);
1723 + c->active_duty_cycle = new_pwm;
1725 + dev_dbg(c->dev, "lipo: Increasing duty_cycle by "
1726 + "%u permille (0x%02X -> 0x%02X)",
1727 + inc_permille, old_pwm, new_pwm);
1729 + return c->set_current_pwm(c, c->active_duty_cycle);
1732 +static int lipocharge_decrease_current(struct lipocharge *c,
1733 + unsigned int dec_permille)
1735 + int old_pwm, new_pwm;
1737 + if (c->active_duty_cycle <= 0)
1740 + old_pwm = c->active_duty_cycle;
1741 + new_pwm = old_pwm - (c->duty_cycle_max * dec_permille / 1000);
1742 + new_pwm = max(0, new_pwm);
1743 + c->active_duty_cycle = new_pwm;
1745 + dev_dbg(c->dev, "lipo: Decreasing duty_cycle by "
1746 + "%u permille (0x%02X -> 0x%02X)",
1747 + dec_permille, old_pwm, new_pwm);
1749 + return c->set_current_pwm(c, c->active_duty_cycle);
1752 +/** lipocharge_update_state - Update the charge state
1753 + * @c: The context.
1754 + * @voltage_mV: The measured battery voltage.
1755 + * @current_mA: The measured charge current.
1756 + * negative -> drain.
1757 + * positive -> charge.
1758 + * @temp_K: Battery temperature in K.
1760 + * Returns 0 on success, -1 on error.
1761 + * Returns 1, if the charging process is finished.
1763 +int lipocharge_update_state(struct lipocharge *c,
1764 + unsigned int voltage_mV,
1766 + unsigned int temp_K)
1768 + int requested_current, current_diff;
1770 + unsigned int permille;
1775 + switch (c->state) {
1777 + dev_err(c->dev, "%s: called while idle", __func__);
1779 + case LIPO_FIRST_STAGE: /* Constant current */
1780 +//printk("GOT %u %d %u\n", voltage_mV, current_mA, temp_K);
1781 + if (voltage_mV >= c->top_voltage) {
1782 + /* Float voltage reached.
1783 + * Switch charger mode to "constant current" */
1784 + c->state = LIPO_SECOND_STAGE;
1785 + dev_dbg(c->dev, "Switched to second charging stage.");
1788 + /* Float voltage not reached, yet.
1789 + * Try to get the requested constant current. */
1790 + requested_current = get_stage1_charge_current(c);
1791 + if (current_mA < 0)
1793 + current_diff = requested_current - current_mA;
1794 + if (abs(requested_current - current_mA) > CURRENT_HYST) {
1795 + if (current_diff > 0) {
1796 + /* Increase current */
1797 + permille = current_diff * 1000 / requested_current;
1799 + err = lipocharge_increase_current(c, permille);
1803 + /* Decrease current */
1804 + permille = (-current_diff) * 1000 / requested_current;
1806 + err = lipocharge_decrease_current(c, permille);
1812 + case LIPO_SECOND_STAGE: /* Constant voltage */
1819 Index: linux-2.6.38-rc6/drivers/cbus/lipocharge.h
1820 ===================================================================
1821 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
1822 +++ linux-2.6.38-rc6/drivers/cbus/lipocharge.h 2011-02-27 01:05:20.246990058 +0100
1824 +#ifndef LIPOCHARGE_H_
1825 +#define LIPOCHARGE_H_
1827 +#include <linux/types.h>
1828 +#include <linux/device.h>
1831 +#define LIPORATE(a,b) (((a) * 1000) + ((b) * 100))
1832 +#define LIPORATE_p6C LIPORATE(0,6) /* 0.6C */
1834 +enum lipocharge_state {
1835 + LIPO_IDLE, /* Not charging */
1836 + LIPO_FIRST_STAGE, /* Charging: constant current */
1837 + LIPO_SECOND_STAGE, /* Charging: constant voltage */
1840 +/** struct lipocharge - A generic LIPO charger
1842 + * @capacity: Battery capacity in mAh.
1843 + * @rate: Charge rate.
1844 + * @top_voltage: Fully charged voltage, in mV.
1845 + * @duty_cycle_max: Max value for duty_cycle.
1847 + * @set_charge_current: Set the charge current PWM duty cycle.
1848 + * @emergency: Something went wrong. Force shutdown.
1850 +struct lipocharge {
1851 + unsigned int capacity;
1852 + unsigned int rate;
1853 + unsigned int top_voltage;
1854 + unsigned int duty_cycle_max;
1856 + int (*set_current_pwm)(struct lipocharge *c, unsigned int duty_cycle);
1857 + void (*emergency)(struct lipocharge *c);
1860 + struct device *dev;
1861 + enum lipocharge_state state;
1862 + unsigned int active_duty_cycle;
1864 + //TODO implement timer to cut power after maximum charge time.
1867 +void lipocharge_init(struct lipocharge *c, struct device *dev);
1868 +void lipocharge_exit(struct lipocharge *c);
1870 +int lipocharge_start(struct lipocharge *c);
1871 +void lipocharge_stop(struct lipocharge *c);
1873 +int lipocharge_update_state(struct lipocharge *c,
1874 + unsigned int voltage_mV,
1876 + unsigned int temp_K);
1878 +static inline bool lipocharge_is_charging(struct lipocharge *c)
1880 + return (c->state != LIPO_IDLE);
1883 +#endif /* LIPOCHARGE_H_ */
1884 Index: linux-2.6.38-rc6/drivers/cbus/cbus.c
1885 ===================================================================
1886 --- linux-2.6.38-rc6.orig/drivers/cbus/cbus.c 2011-02-27 00:58:11.781600500 +0100
1887 +++ linux-2.6.38-rc6/drivers/cbus/cbus.c 2011-02-27 01:05:20.247990084 +0100
1889 #include <linux/platform_device.h>
1891 #include <plat/cbus.h>
1892 +#include <linux/reboot.h>
1896 @@ -323,6 +324,13 @@
1898 module_exit(cbus_bus_exit);
1900 +void cbus_emergency(void)
1902 + machine_power_off();
1903 + panic("cbus: Failed to halt machine in emergency state\n");
1905 +EXPORT_SYMBOL(cbus_emergency);
1907 MODULE_DESCRIPTION("CBUS serial protocol");
1908 MODULE_LICENSE("GPL");
1909 MODULE_AUTHOR("Juha Yrjölä");
1910 Index: linux-2.6.38-rc6/drivers/cbus/cbus.h
1911 ===================================================================
1912 --- linux-2.6.38-rc6.orig/drivers/cbus/cbus.h 2011-02-27 00:58:11.764600047 +0100
1913 +++ linux-2.6.38-rc6/drivers/cbus/cbus.h 2011-02-27 01:05:20.247990084 +0100
1915 extern int cbus_read_reg(unsigned dev, unsigned reg);
1916 extern int cbus_write_reg(unsigned dev, unsigned reg, unsigned val);
1918 +NORET_TYPE void cbus_emergency(void) ATTRIB_NORET;
1920 #endif /* __DRIVERS_CBUS_CBUS_H */
1921 Index: linux-2.6.38-rc6/drivers/cbus/retu.c
1922 ===================================================================
1923 --- linux-2.6.38-rc6.orig/drivers/cbus/retu.c 2011-02-27 00:58:11.748599621 +0100
1924 +++ linux-2.6.38-rc6/drivers/cbus/retu.c 2011-02-27 01:05:20.247990084 +0100
1925 @@ -423,6 +423,11 @@
1929 + child = retu_allocate_child("retu-n810bm", parent, irq_base,
1930 + RETU_INT_ADCS, -1, 1);
1937 Index: linux-2.6.38-rc6/drivers/cbus/tahvo.c
1938 ===================================================================
1939 --- linux-2.6.38-rc6.orig/drivers/cbus/tahvo.c 2011-02-27 00:58:11.686597968 +0100
1940 +++ linux-2.6.38-rc6/drivers/cbus/tahvo.c 2011-02-27 12:17:27.516124940 +0100
1943 static struct tasklet_struct tahvo_tasklet;
1944 spinlock_t tahvo_lock = SPIN_LOCK_UNLOCKED;
1945 +EXPORT_SYMBOL(tahvo_lock);
1947 struct tahvo_irq_handler_desc {
1948 int (*func)(unsigned long);
1950 tahvo_write_reg(reg, w);
1951 spin_unlock_irqrestore(&tahvo_lock, flags);
1953 +EXPORT_SYMBOL(tahvo_set_clear_reg_bits);
1956 * Disable given TAHVO interrupt