0c2e8c2c2e1ffa1087d6a20b63689ac9330b0e7c
[openwrt.git] / target / linux / omap24xx / patches-2.6.37 / 900-n810-battery-management.patch
1 ---
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(-)
12
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
17 @@ -94,4 +94,12 @@
18           to Retu/Vilma. Detection state and events are exposed through
19           sysfs.
20  
21 +config N810BM
22 +       depends on CBUS_RETU && CBUS_TAHVO
23 +       tristate "Nokia n810 battery management"
24 +       ---help---
25 +         Nokia n810 device battery management.
26 +
27 +         If unsure, say N.
28 +
29  endmenu
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
34 @@ -12,3 +12,6 @@
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
45 @@ -0,0 +1,1583 @@
46 +/*
47 + *   Nokia n810 battery management
48 + *
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!
52 + *
53 + *   Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
54 + *
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.
59 + *
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.
64 + */
65 +
66 +#define DEBUG
67 +
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>
78 +
79 +#include "cbus.h"
80 +#include "retu.h"
81 +#include "tahvo.h"
82 +#include "lipocharge.h"
83 +
84 +
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
89 +
90 +#define N810BM_CHECK_INTERVAL          (HZ * 2)
91 +#define N810BM_MIN_VOLTAGE_THRES       3200 /* Absolute minimum voltage threshold */
92 +
93 +
94 +/* RETU_ADC_BSI
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.
98 + *
99 + * Manually measured lookup table.
100 + * Hard to measure, thus not very accurate.
101 + *
102 + * Resistance  |  ADC value
103 + * ========================
104 + * 120k        |  0x3AC
105 + * 110k        |  0x37C
106 + * 100k        |  0x351
107 + *  90k        |  0x329
108 + */
109 +
110 +/* RETU_ADC_BATTVOLT
111 + * Manually measured lookup table.
112 + * Hard to measure, thus not very accurate.
113 + *
114 + * Voltage  |  ADC value
115 + * =====================
116 + * 2.80V    |  0x037
117 + * 2.90V    |  0x05E
118 + * 3.00V    |  0x090
119 + * 3.10V    |  0x0A4
120 + * 3.20V    |  0x0CC
121 + * 3.30V    |  0x0EF
122 + * 3.40V    |  0x115
123 + * 3.50V    |  0x136
124 + * 3.60V    |  0x15C
125 + * 3.70V    |  0x187
126 + * 3.80V    |  0x1A5
127 + * 3.90V    |  0x1C9
128 + * 4.00V    |  0x1ED
129 + * 4.10V    |  0x212
130 + * 4.20V    |  0x236
131 + */
132 +
133 +
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,
151 +};
152 +
153 +struct n810bm_adc_calib {
154 +       enum n810bm_pmm_adc_id id;
155 +       u8 flags;
156 +       u8 adc_groupnr;
157 +       u32 field1;
158 +       u32 field2;
159 +       u16 field3;
160 +       u16 field4;
161 +};
162 +
163 +struct n810bm_calib {
164 +       struct n810bm_adc_calib adc[25];
165 +};
166 +
167 +enum n810bm_capacity {
168 +       N810BM_CAP_UNKNOWN      = -1,
169 +       N810BM_CAP_NONE         = 0,
170 +       N810BM_CAP_1500MAH      = 1500, /* 1500 mAh battery */
171 +};
172 +
173 +enum n810bm_notify_flags {
174 +       N810BM_NOTIFY_battery_charging,
175 +       N810BM_NOTIFY_charger_pwm,
176 +};
177 +
178 +struct n810bm {
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) */
182 +
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 */
187 +
188 +       struct platform_device *pdev;
189 +       struct n810bm_calib calib;              /* Calibration data */
190 +
191 +       bool verbose_charge_log;                /* Verbose charge logging */
192 +
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;
197 +
198 +       bool initialized;                       /* The hardware was initialized */
199 +       struct mutex mutex;
200 +};
201 +
202 +static void n810bm_notify_battery_charging(struct n810bm *bm);
203 +static void n810bm_notify_charger_pwm(struct n810bm *bm);
204 +
205 +
206 +static inline struct n810bm * device_to_n810bm(struct device *dev)
207 +{
208 +       struct platform_device *pdev = to_platform_device(dev);
209 +       struct n810bm *bm = platform_get_drvdata(pdev);
210 +
211 +       return bm;
212 +}
213 +
214 +static inline bool n810bm_known_battery_present(struct n810bm *bm)
215 +{
216 +       return bm->battery_present &&
217 +              bm->capacity != N810BM_CAP_UNKNOWN &&
218 +              bm->capacity != N810BM_CAP_NONE;
219 +}
220 +
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)
223 +{
224 +       printk(KERN_EMERG "n810 battery management fatal fault: %s\n", message);
225 +       cbus_emergency();
226 +}
227 +
228 +static u16 tahvo_read(struct n810bm *bm, unsigned int reg)
229 +{
230 +       int ret;
231 +       unsigned long flags;
232 +
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");
238 +
239 +       return ret;
240 +}
241 +
242 +static void tahvo_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
243 +{
244 +       int ret;
245 +       unsigned long flags;
246 +       u16 value;
247 +
248 +       spin_lock_irqsave(&tahvo_lock, flags);
249 +       if (~mask) {
250 +               ret = tahvo_read_reg(reg);
251 +               if (ret < 0 || ret > 0xFFFF)
252 +                       goto fatal_unlock;
253 +               value = ret;
254 +       } else
255 +               value = 0;
256 +       value &= ~mask;
257 +       value |= set;
258 +       ret = tahvo_write_reg(reg, value);
259 +       if (ret)
260 +               goto fatal_unlock;
261 +       spin_unlock_irqrestore(&tahvo_lock, flags);
262 +
263 +       return;
264 +
265 +fatal_unlock:
266 +       spin_unlock_irqrestore(&tahvo_lock, flags);
267 +       n810bm_emergency(bm, "tahvo_maskset");
268 +}
269 +
270 +static inline void tahvo_write(struct n810bm *bm, unsigned int reg, u16 value)
271 +{
272 +       tahvo_maskset(bm, reg, 0xFFFF, value);
273 +}
274 +
275 +static inline void tahvo_set(struct n810bm *bm, unsigned int reg, u16 mask)
276 +{
277 +       tahvo_maskset(bm, reg, mask, mask);
278 +}
279 +
280 +static inline void tahvo_clear(struct n810bm *bm, unsigned int reg, u16 mask)
281 +{
282 +       tahvo_maskset(bm, reg, mask, 0);
283 +}
284 +
285 +static u16 retu_read(struct n810bm *bm, unsigned int reg)
286 +{
287 +       int ret;
288 +       unsigned long flags;
289 +
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");
295 +
296 +       return ret;
297 +}
298 +
299 +static void retu_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
300 +{
301 +       int ret;
302 +       unsigned long flags;
303 +       u16 value;
304 +
305 +       spin_lock_irqsave(&retu_lock, flags);
306 +       if (~mask) {
307 +               ret = retu_read_reg(reg);
308 +               if (ret < 0 || ret > 0xFFFF)
309 +                       goto fatal_unlock;
310 +               value = ret;
311 +       } else
312 +               value = 0;
313 +       value &= ~mask;
314 +       value |= set;
315 +       ret = retu_write_reg(reg, value);
316 +       if (ret)
317 +               goto fatal_unlock;
318 +       spin_unlock_irqrestore(&retu_lock, flags);
319 +
320 +       return;
321 +
322 +fatal_unlock:
323 +       spin_unlock_irqrestore(&retu_lock, flags);
324 +       n810bm_emergency(bm, "retu_maskset");
325 +}
326 +
327 +static inline void retu_write(struct n810bm *bm, unsigned int reg, u16 value)
328 +{
329 +       retu_maskset(bm, reg, 0xFFFF, value);
330 +}
331 +
332 +static int retu_adc_average(struct n810bm *bm, unsigned int chan,
333 +                           unsigned int nr_passes)
334 +{
335 +       unsigned int i, value = 0;
336 +       int ret;
337 +
338 +       if (WARN_ON(!nr_passes))
339 +               return 0;
340 +       for (i = 0; i < nr_passes; i++) {
341 +               ret = retu_read_adc(chan);
342 +               if (ret < 0)
343 +                       return ret;
344 +               value += ret;
345 +       }
346 +       value /= nr_passes;
347 +
348 +       return value;
349 +}
350 +
351 +static struct n810bm_adc_calib * n810bm_get_adc_calib(struct n810bm *bm,
352 +                                               enum n810bm_pmm_adc_id id)
353 +{
354 +       unsigned int index = 0;
355 +       struct n810bm_adc_calib *cal;
356 +
357 +       if (id != N810BM_PMM_ADC_0xFE)
358 +               index = (unsigned int)id + 1;
359 +       if (index >= ARRAY_SIZE(bm->calib.adc))
360 +               return NULL;
361 +
362 +       cal = &bm->calib.adc[index];
363 +       WARN_ON(cal->id && cal->id != id);
364 +
365 +       return cal;
366 +}
367 +
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)
372 +{
373 +       const u8 *pmm_area = pmm_block->data;
374 +       u8 active_group_mask;
375 +
376 +       if (pmm_block->size != N810BM_PMM_BLOCK_SIZE)
377 +               return -EINVAL;
378 +       if (group >= N810BM_PMM_BLOCK_SIZE / N810BM_PMM_GROUP_SIZE)
379 +               return -EINVAL;
380 +       if (element >= N810BM_PMM_GROUP_SIZE / N810BM_PMM_ELEM_SIZE)
381 +               return -EINVAL;
382 +       if (offset >= N810BM_PMM_ELEM_SIZE || length > N810BM_PMM_ELEM_SIZE ||
383 +           length + offset > N810BM_PMM_ELEM_SIZE)
384 +               return -EINVAL;
385 +
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);
390 +               return -ENOENT;
391 +       }
392 +
393 +       memcpy(buffer,
394 +              pmm_area + group * N810BM_PMM_GROUP_SIZE
395 +                       + element * N810BM_PMM_ELEM_SIZE
396 +                       + offset,
397 +              length);
398 +
399 +       return 0;
400 +}
401 +
402 +/* PMM block group 1 element */
403 +struct group1_element {
404 +       u8 id;
405 +       u8 flags;
406 +       u8 adc_groupnr;
407 +       u8 _padding;
408 +       __le32 field1;
409 +       __le32 field2;
410 +} __packed;
411 +
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)
416 +{
417 +       struct group1_element elem;
418 +       int err;
419 +       unsigned int i, element_nr;
420 +       struct n810bm_adc_calib *adc_calib;
421 +
422 +       for (i = 0; i < nr_pmm_adc_ids; i++) {
423 +               element_nr = (unsigned int)(pmm_adc_ids[i]) + 3;
424 +
425 +               err = pmm_record_get(bm, pmm_block, &elem, sizeof(elem),
426 +                                    1, element_nr, 0);
427 +               if (err)
428 +                       continue;
429 +               adc_calib = n810bm_get_adc_calib(bm, elem.id);
430 +               if (!adc_calib) {
431 +                       dev_err(&bm->pdev->dev, "extract_group1_elem: "
432 +                               "Could not get calib element for 0x%02X",
433 +                               elem.id);
434 +                       return -EINVAL;
435 +               }
436 +
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;
440 +               } else {
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);
445 +               }
446 +       }
447 +
448 +       return 0;
449 +}
450 +
451 +static int n810bm_parse_pmm_group1(struct n810bm *bm,
452 +                                  const struct firmware *pmm_block)
453 +{
454 +       struct n810bm_adc_calib *adc_calib;
455 +       struct group1_element elem;
456 +       int err;
457 +
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,
463 +       };
464 +       static const enum n810bm_pmm_adc_id pmm_adc_ids_2[] = {
465 +               N810BM_PMM_ADC_BSI,
466 +       };
467 +       static const enum n810bm_pmm_adc_id pmm_adc_ids_3[] = {
468 +               N810BM_PMM_ADC_BATTEMP,
469 +       };
470 +
471 +       /* Parse element 2 */
472 +       err = pmm_record_get(bm, pmm_block, &elem, sizeof(elem),
473 +                            1, 2, 0);
474 +       if (err) {
475 +               dev_err(&bm->pdev->dev,
476 +                       "PMM: Failed to get group 1 / element 2");
477 +               return err;
478 +       }
479 +       if (elem.id == N810BM_PMM_ADC_0xFE && elem.flags == 0x05) {
480 +               adc_calib = n810bm_get_adc_calib(bm, elem.id);
481 +               if (!adc_calib) {
482 +                       dev_err(&bm->pdev->dev,
483 +                               "calib extract: Failed to get 0xFE calib");
484 +                       return -EINVAL;
485 +               }
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);
490 +       }
491 +
492 +       err = extract_group1_elem(bm, pmm_block,
493 +                                 pmm_adc_ids_1, ARRAY_SIZE(pmm_adc_ids_1),
494 +                                 0xFFFFFFFF, 0xFFFFFFFF);
495 +       if (err)
496 +               return err;
497 +       err = extract_group1_elem(bm, pmm_block,
498 +                                 pmm_adc_ids_2, ARRAY_SIZE(pmm_adc_ids_2),
499 +                                 0xFFFFFFFF, 0);
500 +       if (err)
501 +               return err;
502 +       err = extract_group1_elem(bm, pmm_block,
503 +                                 pmm_adc_ids_3, ARRAY_SIZE(pmm_adc_ids_3),
504 +                                 0xFFFFFFFF, 0x0000FFFF);
505 +       if (err)
506 +               return err;
507 +
508 +       return 0;
509 +}
510 +
511 +static int n810bm_parse_pmm_group2(struct n810bm *bm,
512 +                                  const struct firmware *pmm_block)
513 +{
514 +       dev_err(&bm->pdev->dev, "TODO: CAL BME PMM group 2 parser not implemented, yet");
515 +       return -EOPNOTSUPP;
516 +}
517 +
518 +static void n810bm_adc_calib_set_defaults(struct n810bm *bm)
519 +{
520 +       struct n810bm_adc_calib *adc_calib;
521 +       unsigned int i;
522 +
523 +       static const struct n810bm_adc_calib defaults[] = {
524 +               /* ADC group-nr 0 */
525 +               {
526 +                       .id             = N810BM_PMM_ADC_HEADSET,
527 +                       .flags          = 0x00,
528 +                       .adc_groupnr    = 0,
529 +               }, {
530 +                       .id             = N810BM_PMM_ADC_HOOKDET,
531 +                       .flags          = 0x00,
532 +                       .adc_groupnr    = 0,
533 +               }, {
534 +                       .id             = N810BM_PMM_ADC_RFGP,
535 +                       .flags          = 0x00,
536 +                       .adc_groupnr    = 0,
537 +               }, {
538 +                       .id             = N810BM_PMM_ADC_LIGHTSENS,
539 +                       .flags          = 0x00,
540 +                       .adc_groupnr    = 0,
541 +               }, {
542 +                       .id             = N810BM_PMM_ADC_WBTX,
543 +                       .flags          = 0x00,
544 +                       .adc_groupnr    = 0,
545 +               }, {
546 +                       .id             = N810BM_PMM_ADC_RETUTEMP,
547 +                       .flags          = 0x00,
548 +                       .adc_groupnr    = 0,
549 +               }, {
550 +                       .id             = N810BM_PMM_ADC_GND2,
551 +                       .flags          = 0x00,
552 +                       .adc_groupnr    = 0,
553 +               },
554 +               /* ADC group-nr 1 */
555 +               {
556 +                       .id             = N810BM_PMM_ADC_0xFE,
557 +                       .flags          = 0x05,
558 +                       .adc_groupnr    = 1,
559 +                       .field1         = (u32)-2,
560 +                       .field2         = 13189,
561 +               }, {
562 +                       .id             = N810BM_PMM_ADC_BATVOLT,
563 +                       .flags          = 0x01,
564 +                       .adc_groupnr    = 1,
565 +                       .field1         = 2527,
566 +                       .field2         = 21373,
567 +               }, {
568 +                       .id             = N810BM_PMM_ADC_CHGVOLT,
569 +                       .flags          = 0x01,
570 +                       .adc_groupnr    = 1,
571 +                       .field1         = 0,
572 +                       .field2         = 129848,
573 +               }, {
574 +                       .id             = N810BM_PMM_ADC_BKUPVOLT,
575 +                       .flags          = 0x01,
576 +                       .adc_groupnr    = 1,
577 +                       .field1         = 0,
578 +                       .field2         = 20000,
579 +               }, {
580 +                       .id             = N810BM_PMM_ADC_BATCURR,
581 +                       .flags          = 0x06,
582 +                       .adc_groupnr    = 1,
583 +                       .field1         = 0,
584 +                       .field2         = 9660,
585 +               },
586 +               /* ADC group-nr 2 */
587 +               {
588 +                       .id             = N810BM_PMM_ADC_BSI,
589 +                       .flags          = 0x02,
590 +                       .adc_groupnr    = 2,
591 +                       .field1         = 1169,
592 +                       .field2         = 0,
593 +               },
594 +               /* ADC group-nr 3 */
595 +               {
596 +                       .id             = N810BM_PMM_ADC_BATTEMP,
597 +                       .flags          = 0x03,
598 +                       .adc_groupnr    = 3,
599 +                       .field1         = 265423000,
600 +                       .field2         = 298,
601 +               },
602 +               /* ADC group-nr 4 */
603 +               {
604 +                       .id             = N810BM_PMM_ADC_LIGHTTEMP,
605 +                       .flags          = 0x04,
606 +                       .adc_groupnr    = 4,
607 +                       .field1         = 19533778,
608 +                       .field2         = 308019670,
609 +                       .field3         = 4700,
610 +                       .field4         = 2500,
611 +               },
612 +       };
613 +
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;
618 +
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))
623 +                       continue;
624 +               *adc_calib = defaults[i];
625 +       }
626 +}
627 +
628 +static int n810bm_parse_pmm_block(struct n810bm *bm,
629 +                                 const struct firmware *pmm_block)
630 +{
631 +       u8 byte;
632 +       int err;
633 +       unsigned int i, count;
634 +       struct n810bm_adc_calib *adc_calib;
635 +
636 +       /* Initialize to defaults */
637 +       n810bm_adc_calib_set_defaults(bm);
638 +
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);
646 +       if (err)
647 +               err = n810bm_parse_pmm_group2(bm, pmm_block);
648 +       else
649 +               err = n810bm_parse_pmm_group1(bm, pmm_block);
650 +       if (err)
651 +               return err;
652 +
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)
657 +                       continue;
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;
666 +                       count++;
667 +                       break;
668 +               case N810BM_PMM_ADC_BSI:
669 +                       if (adc_calib->field1 < 1100 ||
670 +                           adc_calib->field1 > 1300)
671 +                               goto value_check_fail;
672 +                       count++;
673 +                       break;
674 +               case N810BM_PMM_ADC_BATCURR:
675 +                       if (adc_calib->field2 < 7000 ||
676 +                           adc_calib->field2 > 12000)
677 +                               goto value_check_fail;
678 +                       count++;
679 +                       break;
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;
687 +                       count++;
688 +                       break;
689 +               case N810BM_PMM_ADC_CHGVOLT:
690 +               case N810BM_PMM_ADC_BATTEMP:
691 +               case N810BM_PMM_ADC_BKUPVOLT:
692 +                       count++;
693 +                       break;
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:
702 +                       break;
703 +               }
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);
709 +       }
710 +       if (count != 7) {
711 +               dev_err(&bm->pdev->dev, "PMM sanity check: Did not find "
712 +                       "all required values (count=%u)", count);
713 +               goto check_fail;
714 +       }
715 +
716 +       return 0;
717 +
718 +value_check_fail:
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);
722 +check_fail:
723 +       return -EILSEQ;
724 +}
725 +
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)
730 +{
731 +       u16 value = millisec_interval;
732 +
733 +       if (value <= 0xF905) {
734 +               value = ((u64)0x10624DD3 * (u64)(value + 0xF9)) >> 32;
735 +               value /= 16;
736 +       } else
737 +               value = 0xFF;
738 +
739 +       tahvo_write(bm, TAHVO_REG_BATCURRTIMER, value & 0xFF);
740 +
741 +       tahvo_set(bm, TAHVO_REG_CHGCTL,
742 +                 TAHVO_REG_CHGCTL_CURTIMRST);
743 +       tahvo_clear(bm, TAHVO_REG_CHGCTL,
744 +                   TAHVO_REG_CHGCTL_CURTIMRST);
745 +
746 +       if (millisec_interval)
747 +               tahvo_enable_irq(TAHVO_INT_BATCURR);
748 +       else
749 +               tahvo_disable_irq(TAHVO_INT_BATCURR);
750 +
751 +       //TODO also do a software timer for safety.
752 +}
753 +
754 +static void n810bm_enable_current_measure(struct n810bm *bm)
755 +{
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");
763 +       }
764 +       bm->current_measure_enabled++;
765 +}
766 +
767 +static void n810bm_disable_current_measure(struct n810bm *bm)
768 +{
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");
777 +       }
778 +}
779 +
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)
783 +{
784 +       u16 retval;
785 +       int adc = 0, ma, i;
786 +
787 +       if (WARN_ON(bm->current_measure_enabled <= 0))
788 +               return 0;
789 +       for (i = 0; i < 3; i++) {
790 +               retval = tahvo_read(bm, TAHVO_REG_BATCURR);
791 +               adc += (s16)retval; /* Value is signed */
792 +       }
793 +       adc /= 3;
794 +
795 +       //TODO convert to mA
796 +       ma = adc;
797 +
798 +       return ma;
799 +}
800 +
801 +/* Requires bm->mutex locked */
802 +static int n810bm_measure_batt_current_async(struct n810bm *bm)
803 +{
804 +       int ma;
805 +       bool charging = lipocharge_is_charging(&bm->charger);
806 +
807 +       n810bm_enable_current_measure(bm);
808 +       if (!charging)
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);
824 +
825 +       return ma;
826 +}
827 +
828 +static int adc_sanity_check(struct n810bm *bm, unsigned int channel)
829 +{
830 +       int value;
831 +
832 +       value = retu_read_adc(channel);
833 +       if (value < 0) {
834 +               dev_err(&bm->pdev->dev, "Failed to read GND ADC channel %u",
835 +                       channel);
836 +               return -EIO;
837 +       }
838 +       dev_dbg(&bm->pdev->dev,
839 +               "GND ADC channel %u sanity check got value: %d",
840 +               channel, value);
841 +       if (value > 5) {
842 +               n810bm_emergency(bm, "GND ADC sanity check failed");
843 +               return -EIO;
844 +       }
845 +
846 +       return 0;
847 +}
848 +
849 +static int n810bm_check_adc_sanity(struct n810bm *bm)
850 +{
851 +       int err;
852 +
853 +       /* Discard one conversion */
854 +       retu_write(bm, RETU_REG_ADCSCR, 0);
855 +       retu_read_adc(RETU_ADC_GND2);
856 +
857 +       err = adc_sanity_check(bm, RETU_ADC_GND2);
858 +       if (err)
859 +               return err;
860 +
861 +       return 0;
862 +}
863 +
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)
866 +{
867 +       int adc;
868 +       unsigned int mv;
869 +       const unsigned int scale = 1000;
870 +
871 +       adc = retu_adc_average(bm, RETU_ADC_BATTVOLT, 5);
872 +       if (adc < 0)
873 +               return adc;
874 +       if (adc <= 0x37)
875 +               return 2800;
876 +       mv = 2800 + ((adc - 0x37) * (((4200 - 2800) * scale) / (0x236 - 0x37))) / scale;
877 +
878 +       //TODO compensate for power consumption
879 +       //TODO honor calibration values
880 +
881 +       return mv;
882 +}
883 +
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)
886 +{
887 +       int adc;
888 +       unsigned int mv;
889 +
890 +       adc = retu_adc_average(bm, RETU_ADC_CHGVOLT, 5);
891 +       if (adc < 0)
892 +               return adc;
893 +       //TODO convert to mV
894 +       mv = adc;
895 +
896 +       return mv;
897 +}
898 +
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)
901 +{
902 +       int adc;
903 +       unsigned int mv;
904 +
905 +       adc = retu_adc_average(bm, RETU_ADC_BKUPVOLT, 3);
906 +       if (adc < 0)
907 +               return adc;
908 +       //TODO convert to mV
909 +       mv = adc;
910 +
911 +       return mv;
912 +}
913 +
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)
916 +{
917 +       int adc;
918 +       unsigned int k;
919 +
920 +       adc = retu_adc_average(bm, RETU_ADC_BATTEMP, 3);
921 +       if (adc < 0)
922 +               return adc;
923 +       //TODO convert to K
924 +       k = adc;
925 +
926 +       return k;
927 +}
928 +
929 +/* Read the battery capacity via BSI pin. */
930 +static enum n810bm_capacity n810bm_read_batt_capacity(struct n810bm *bm)
931 +{
932 +       int adc;
933 +       const unsigned int hyst = 20;
934 +
935 +       adc = retu_adc_average(bm, RETU_ADC_BSI, 5);
936 +       if (adc < 0) {
937 +               dev_err(&bm->pdev->dev, "Failed to read BSI ADC");
938 +               return N810BM_CAP_UNKNOWN;
939 +       }
940 +
941 +       if (adc >= 0x3B5 - hyst && adc <= 0x3B5 + hyst)
942 +               return N810BM_CAP_1500MAH;
943 +
944 +       dev_err(&bm->pdev->dev, "Capacity indicator 0x%X unknown", adc);
945 +
946 +       return N810BM_CAP_UNKNOWN;
947 +}
948 +
949 +/* Convert a battery voltage (in mV) to percentage. */
950 +static unsigned int n810bm_mvolt2percent(unsigned int mv)
951 +{
952 +       const unsigned int minv = 3700;
953 +       const unsigned int maxv = 4150;
954 +       unsigned int percent;
955 +
956 +       mv = clamp(mv, minv, maxv);
957 +       percent = (mv - minv) * 100 / (maxv - minv);
958 +
959 +       return percent;
960 +}
961 +
962 +static void n810bm_start_charge(struct n810bm *bm)
963 +{
964 +       int err;
965 +
966 +       WARN_ON(!bm->battery_present);
967 +       WARN_ON(!bm->charger_present);
968 +
969 +       /* Set PWM to zero */
970 +       bm->active_current_pwm = 0;
971 +       tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
972 +
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);
979 +
980 +       WARN_ON((int)bm->capacity <= 0);
981 +       bm->charger.capacity = bm->capacity;
982 +       err = lipocharge_start(&bm->charger);
983 +       WARN_ON(err);
984 +
985 +       /* Initialize current measurement circuitry */
986 +       n810bm_enable_current_measure(bm);
987 +       n810bm_set_current_measure_timer(bm, 250);
988 +
989 +       dev_info(&bm->pdev->dev, "Charging battery");
990 +       n810bm_notify_charger_pwm(bm);
991 +       n810bm_notify_battery_charging(bm);
992 +}
993 +
994 +static void n810bm_stop_charge(struct n810bm *bm)
995 +{
996 +       if (lipocharge_is_charging(&bm->charger)) {
997 +               n810bm_set_current_measure_timer(bm, 0);
998 +               n810bm_disable_current_measure(bm);
999 +       }
1000 +       lipocharge_stop(&bm->charger);
1001 +
1002 +       /* Set PWM to zero */
1003 +       bm->active_current_pwm = 0;
1004 +       tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
1005 +
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,
1011 +                     0);
1012 +
1013 +       dev_info(&bm->pdev->dev, "Not charging battery");
1014 +       n810bm_notify_charger_pwm(bm);
1015 +       n810bm_notify_battery_charging(bm);
1016 +}
1017 +
1018 +/* Periodic check */
1019 +static void n810bm_periodic_check_work(struct work_struct *work)
1020 +{
1021 +       struct n810bm *bm = container_of(to_delayed_work(work),
1022 +                                        struct n810bm, periodic_check_work);
1023 +       u16 status;
1024 +       bool battery_was_present, charger_was_present;
1025 +       int mv;
1026 +
1027 +       mutex_lock(&bm->mutex);
1028 +
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);
1034 +
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");
1041 +                       } else {
1042 +                               dev_info(&bm->pdev->dev, "Detected %u mAh battery",
1043 +                                        (unsigned int)bm->capacity);
1044 +                       }
1045 +               } else {
1046 +                       bm->capacity = N810BM_CAP_NONE;
1047 +                       dev_info(&bm->pdev->dev, "The main battery was removed");
1048 +                       //TODO disable charging
1049 +               }
1050 +       }
1051 +
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");
1056 +       }
1057 +
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);
1062 +               if (mv < 0) {
1063 +                       n810bm_emergency(bm,
1064 +                               "check: Failed to measure voltage");
1065 +               }
1066 +               if (mv < N810BM_MIN_VOLTAGE_THRES) {
1067 +                       n810bm_emergency(bm,
1068 +                               "check: Minimum voltage threshold reached");
1069 +               }
1070 +       }
1071 +
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);
1079 +                       }
1080 +               }
1081 +       }
1082 +
1083 +       if (lipocharge_is_charging(&bm->charger) && !bm->charger_present) {
1084 +               /* Charger was unplugged. */
1085 +               n810bm_stop_charge(bm);
1086 +       }
1087 +
1088 +       mutex_unlock(&bm->mutex);
1089 +       schedule_delayed_work(&bm->periodic_check_work,
1090 +                             round_jiffies_relative(N810BM_CHECK_INTERVAL));
1091 +}
1092 +
1093 +static void n810bm_adc_irq_handler(unsigned long data)
1094 +{
1095 +       struct n810bm *bm = (struct n810bm *)data;
1096 +
1097 +       retu_ack_irq(RETU_INT_ADCS);
1098 +       //TODO
1099 +dev_info(&bm->pdev->dev, "ADC interrupt triggered\n");
1100 +}
1101 +
1102 +static void n810bm_tahvo_current_measure_work(struct work_struct *work)
1103 +{
1104 +       struct n810bm *bm = container_of(work, struct n810bm, currmeas_irq_work);
1105 +       int res, ma, mv, temp;
1106 +
1107 +       mutex_lock(&bm->mutex);
1108 +       if (!lipocharge_is_charging(&bm->charger))
1109 +               goto out_unlock;
1110 +
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);
1121 +       msleep(10);
1122 +       mv = n810bm_measure_batt_voltage(bm);
1123 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1124 +                     TAHVO_REG_CHGCTL_PWMOVR |
1125 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
1126 +                     0);
1127 +       temp = n810bm_measure_batt_temp(bm);
1128 +       if (WARN_ON(mv < 0))
1129 +               goto out_unlock;
1130 +       if (WARN_ON(temp < 0))
1131 +               goto out_unlock;
1132 +
1133 +       if (bm->verbose_charge_log) {
1134 +               dev_info(&bm->pdev->dev,
1135 +                        "Battery charge state: %d mV, %d mA (%s)",
1136 +                        mv, ma,
1137 +                        (ma <= 0) ? "discharging" : "charging");
1138 +       }
1139 +       res = lipocharge_update_state(&bm->charger, mv, ma, temp);
1140 +       if (res) {
1141 +               if (res > 0)
1142 +                       dev_info(&bm->pdev->dev, "Battery fully charged");
1143 +               n810bm_stop_charge(bm);
1144 +       }
1145 +out_unlock:
1146 +       mutex_unlock(&bm->mutex);
1147 +}
1148 +
1149 +static void n810bm_tahvo_current_measure_irq_handler(unsigned long data)
1150 +{
1151 +       struct n810bm *bm = (struct n810bm *)data;
1152 +
1153 +       tahvo_ack_irq(TAHVO_INT_BATCURR);
1154 +       schedule_work(&bm->currmeas_irq_work);
1155 +}
1156 +
1157 +#define DEFINE_ATTR_NOTIFY(attr_name)                                          \
1158 +       void n810bm_notify_##attr_name(struct n810bm *bm)                       \
1159 +       {                                                                       \
1160 +               set_bit(N810BM_NOTIFY_##attr_name, &bm->notify_flags);          \
1161 +               wmb();                                                          \
1162 +               schedule_work(&bm->notify_work);                                \
1163 +       }
1164 +
1165 +#define DEFINE_SHOW_INT_FUNC(name, member)                                     \
1166 +       static ssize_t n810bm_attr_##name##_show(struct device *dev,            \
1167 +                                                struct device_attribute *attr, \
1168 +                                                char *buf)                     \
1169 +       {                                                                       \
1170 +               struct n810bm *bm = device_to_n810bm(dev);                      \
1171 +               ssize_t count;                                                  \
1172 +                                                                               \
1173 +               mutex_lock(&bm->mutex);                                         \
1174 +               count = snprintf(buf, PAGE_SIZE, "%d\n", (int)(bm->member));    \
1175 +               mutex_unlock(&bm->mutex);                                       \
1176 +                                                                               \
1177 +               return count;                                                   \
1178 +       }
1179 +
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)\
1184 +       {                                                                       \
1185 +               struct n810bm *bm = device_to_n810bm(dev);                      \
1186 +               long val;                                                       \
1187 +               int err;                                                        \
1188 +                                                                               \
1189 +               mutex_lock(&bm->mutex);                                         \
1190 +               err = strict_strtol(buf, 0, &val);                              \
1191 +               if (!err)                                                       \
1192 +                       bm->member = (typeof(bm->member))val;                   \
1193 +               mutex_unlock(&bm->mutex);                                       \
1194 +                                                                               \
1195 +               return err ? err : count;                                       \
1196 +       }
1197 +
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);
1202 +
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);
1209 +
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);
1216 +
1217 +static ssize_t n810bm_attr_battery_charging(struct device *dev,
1218 +                                           struct device_attribute *attr,
1219 +                                           char *buf)
1220 +{
1221 +       struct n810bm *bm = device_to_n810bm(dev);
1222 +       ssize_t count;
1223 +
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);
1228 +
1229 +       return count;
1230 +}
1231 +static DEVICE_ATTR(battery_charging, S_IRUGO,
1232 +                  n810bm_attr_battery_charging, NULL);
1233 +static DEFINE_ATTR_NOTIFY(battery_charging);
1234 +
1235 +static ssize_t n810bm_attr_battery_level_show(struct device *dev,
1236 +                                             struct device_attribute *attr,
1237 +                                             char *buf)
1238 +{
1239 +       struct n810bm *bm = device_to_n810bm(dev);
1240 +       ssize_t count = -ENODEV;
1241 +       int millivolt;
1242 +
1243 +       mutex_lock(&bm->mutex);
1244 +       if (!bm->battery_present || lipocharge_is_charging(&bm->charger))
1245 +               millivolt = 0;
1246 +       else
1247 +               millivolt = n810bm_measure_batt_voltage(bm);
1248 +       if (millivolt >= 0) {
1249 +               count = snprintf(buf, PAGE_SIZE, "%u\n",
1250 +                                n810bm_mvolt2percent(millivolt));
1251 +       }
1252 +       mutex_unlock(&bm->mutex);
1253 +
1254 +       return count;
1255 +}
1256 +static DEVICE_ATTR(battery_level, S_IRUGO,
1257 +                  n810bm_attr_battery_level_show, NULL);
1258 +
1259 +static ssize_t n810bm_attr_battery_capacity_show(struct device *dev,
1260 +                                                struct device_attribute *attr,
1261 +                                                char *buf)
1262 +{
1263 +       struct n810bm *bm = device_to_n810bm(dev);
1264 +       ssize_t count;
1265 +       int capacity = 0;
1266 +
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);
1272 +
1273 +       return count;
1274 +}
1275 +static DEVICE_ATTR(battery_capacity, S_IRUGO,
1276 +                  n810bm_attr_battery_capacity_show, NULL);
1277 +
1278 +static ssize_t n810bm_attr_battery_temp_show(struct device *dev,
1279 +                                            struct device_attribute *attr,
1280 +                                            char *buf)
1281 +{
1282 +       struct n810bm *bm = device_to_n810bm(dev);
1283 +       ssize_t count = -ENODEV;
1284 +       int k;
1285 +
1286 +       mutex_lock(&bm->mutex);
1287 +       k = n810bm_measure_batt_temp(bm);
1288 +       if (k >= 0)
1289 +               count = snprintf(buf, PAGE_SIZE, "%d\n", k);
1290 +       mutex_unlock(&bm->mutex);
1291 +
1292 +       return count;
1293 +}
1294 +static DEVICE_ATTR(battery_temp, S_IRUGO,
1295 +                  n810bm_attr_battery_temp_show, NULL);
1296 +
1297 +static ssize_t n810bm_attr_charger_voltage_show(struct device *dev,
1298 +                                               struct device_attribute *attr,
1299 +                                               char *buf)
1300 +{
1301 +       struct n810bm *bm = device_to_n810bm(dev);
1302 +       ssize_t count = -ENODEV;
1303 +       int mv = 0;
1304 +
1305 +       mutex_lock(&bm->mutex);
1306 +       if (bm->charger_present)
1307 +               mv = n810bm_measure_charger_voltage(bm);
1308 +       if (mv >= 0)
1309 +               count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
1310 +       mutex_unlock(&bm->mutex);
1311 +
1312 +       return count;
1313 +}
1314 +static DEVICE_ATTR(charger_voltage, S_IRUGO,
1315 +                  n810bm_attr_charger_voltage_show, NULL);
1316 +
1317 +static ssize_t n810bm_attr_backup_battery_voltage_show(struct device *dev,
1318 +                                                      struct device_attribute *attr,
1319 +                                                      char *buf)
1320 +{
1321 +       struct n810bm *bm = device_to_n810bm(dev);
1322 +       ssize_t count = -ENODEV;
1323 +       int mv;
1324 +
1325 +       mutex_lock(&bm->mutex);
1326 +       mv = n810bm_measure_backup_batt_voltage(bm);
1327 +       if (mv >= 0)
1328 +               count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
1329 +       mutex_unlock(&bm->mutex);
1330 +
1331 +       return count;
1332 +}
1333 +static DEVICE_ATTR(backup_battery_voltage, S_IRUGO,
1334 +                  n810bm_attr_backup_battery_voltage_show, NULL);
1335 +
1336 +static ssize_t n810bm_attr_battery_current_show(struct device *dev,
1337 +                                               struct device_attribute *attr,
1338 +                                               char *buf)
1339 +{
1340 +       struct n810bm *bm = device_to_n810bm(dev);
1341 +       ssize_t count = -ENODEV;
1342 +       int ma = 0;
1343 +
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);
1349 +
1350 +       return count;
1351 +}
1352 +static DEVICE_ATTR(battery_current, S_IRUGO,
1353 +                  n810bm_attr_battery_current_show, NULL);
1354 +
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,
1368 +};
1369 +
1370 +static void n810bm_notify_work(struct work_struct *work)
1371 +{
1372 +       struct n810bm *bm = container_of(work, struct n810bm, notify_work);
1373 +       unsigned long notify_flags;
1374 +
1375 +       notify_flags = xchg(&bm->notify_flags, 0);
1376 +       mb();
1377 +
1378 +#define do_notify(attr_name)                                           \
1379 +       do {                                                            \
1380 +               if (notify_flags & (1 << N810BM_NOTIFY_##attr_name)) {  \
1381 +                       sysfs_notify(&bm->pdev->dev.kobj, NULL,         \
1382 +                                    dev_attr_##attr_name.attr.name);   \
1383 +               }                                                       \
1384 +       } while (0)
1385 +
1386 +       do_notify(battery_charging);
1387 +       do_notify(charger_pwm);
1388 +}
1389 +
1390 +static int n810bm_charger_set_current_pwm(struct lipocharge *c,
1391 +                                         unsigned int duty_cycle)
1392 +{
1393 +       struct n810bm *bm = container_of(c, struct n810bm, charger);
1394 +       int err = -EINVAL;
1395 +
1396 +       WARN_ON(!mutex_is_locked(&bm->mutex));
1397 +       if (WARN_ON(duty_cycle > 0xFF))
1398 +               goto out;
1399 +       if (WARN_ON(!bm->charger_enabled))
1400 +               goto out;
1401 +       if (WARN_ON(!bm->battery_present || !bm->charger_present))
1402 +               goto out;
1403 +
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);
1408 +       }
1409 +
1410 +       err = 0;
1411 +out:
1412 +
1413 +       return err;
1414 +}
1415 +
1416 +static void n810bm_charger_emergency(struct lipocharge *c)
1417 +{
1418 +       struct n810bm *bm = container_of(c, struct n810bm, charger);
1419 +
1420 +       n810bm_emergency(bm, "Battery charger fault");
1421 +}
1422 +
1423 +static void n810bm_hw_exit(struct n810bm *bm)
1424 +{
1425 +       n810bm_stop_charge(bm);
1426 +       retu_write(bm, RETU_REG_ADCSCR, 0);
1427 +}
1428 +
1429 +static int n810bm_hw_init(struct n810bm *bm)
1430 +{
1431 +       int err;
1432 +
1433 +       err = n810bm_check_adc_sanity(bm);
1434 +       if (err)
1435 +               return err;
1436 +
1437 +       n810bm_stop_charge(bm);
1438 +
1439 +       return 0;
1440 +}
1441 +
1442 +static void n810bm_cancel_and_flush_work(struct n810bm *bm)
1443 +{
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();
1448 +}
1449 +
1450 +static int n810bm_device_init(struct n810bm *bm)
1451 +{
1452 +       int attr_index;
1453 +       int err;
1454 +
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);
1461 +
1462 +       err = n810bm_hw_init(bm);
1463 +       if (err)
1464 +               goto error;
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]);
1467 +               if (err)
1468 +                       goto err_unwind_attrs;
1469 +       }
1470 +       err = retu_request_irq(RETU_INT_ADCS,
1471 +                              n810bm_adc_irq_handler,
1472 +                              (unsigned long)bm, "n810bm");
1473 +       if (err)
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");
1478 +       if (err)
1479 +               goto err_free_retu_irq;
1480 +       tahvo_disable_irq(TAHVO_INT_BATCURR);
1481 +
1482 +       schedule_delayed_work(&bm->periodic_check_work,
1483 +                             round_jiffies_relative(N810BM_CHECK_INTERVAL));
1484 +
1485 +       bm->initialized = 1;
1486 +       dev_info(&bm->pdev->dev, "Battery management initialized");
1487 +
1488 +       return 0;
1489 +
1490 +err_free_retu_irq:
1491 +       retu_free_irq(RETU_INT_ADCS);
1492 +err_unwind_attrs:
1493 +       for (attr_index--; attr_index >= 0; attr_index--)
1494 +               device_remove_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1495 +/*err_exit:*/
1496 +       n810bm_hw_exit(bm);
1497 +error:
1498 +       n810bm_cancel_and_flush_work(bm);
1499 +
1500 +       return err;
1501 +}
1502 +
1503 +static void n810bm_device_exit(struct n810bm *bm)
1504 +{
1505 +       int i;
1506 +
1507 +       if (!bm->initialized)
1508 +               return;
1509 +
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]);
1515 +
1516 +       n810bm_cancel_and_flush_work(bm);
1517 +
1518 +       n810bm_hw_exit(bm);
1519 +
1520 +       bm->initialized = 0;
1521 +}
1522 +
1523 +static void n810bm_pmm_block_found(const struct firmware *fw, void *context)
1524 +{
1525 +       struct n810bm *bm = context;
1526 +       int err;
1527 +
1528 +       if (!fw) {
1529 +               dev_err(&bm->pdev->dev,
1530 +                       "CAL PMM block image file not found");
1531 +               goto err_release;
1532 +       }
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");
1537 +               goto err_release;
1538 +       }
1539 +
1540 +       err = n810bm_parse_pmm_block(bm, fw);
1541 +       if (err)
1542 +               goto err_release;
1543 +       release_firmware(fw);
1544 +
1545 +       err = n810bm_device_init(bm);
1546 +       if (err) {
1547 +               dev_err(&bm->pdev->dev,
1548 +                       "Failed to initialized battery management (%d)", err);
1549 +               goto error;
1550 +       }
1551 +
1552 +       return;
1553 +err_release:
1554 +       release_firmware(fw);
1555 +error:
1556 +       return;
1557 +}
1558 +
1559 +static int __devinit n810bm_probe(struct platform_device *pdev)
1560 +{
1561 +       struct n810bm *bm;
1562 +       int err;
1563 +
1564 +       bm = kzalloc(sizeof(*bm), GFP_KERNEL);
1565 +       if (!bm)
1566 +               return -ENOMEM;
1567 +       bm->pdev = pdev;
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);
1573 +
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);
1580 +       if (err) {
1581 +               dev_err(&bm->pdev->dev,
1582 +                       "Failed to request CAL PMM block image file (%d)", err);
1583 +               goto err_free;
1584 +       }
1585 +
1586 +       return 0;
1587 +
1588 +err_free:
1589 +       kfree(bm);
1590 +
1591 +       return err;
1592 +}
1593 +
1594 +static int __devexit n810bm_remove(struct platform_device *pdev)
1595 +{
1596 +       struct n810bm *bm = platform_get_drvdata(pdev);
1597 +
1598 +       n810bm_device_exit(bm);
1599 +
1600 +       kfree(bm);
1601 +       platform_set_drvdata(pdev, NULL);
1602 +
1603 +       return 0;
1604 +}
1605 +
1606 +static struct platform_driver n810bm_driver = {
1607 +       .remove         = __devexit_p(n810bm_remove),
1608 +       .driver         = {
1609 +               .name   = "n810bm",
1610 +       }
1611 +};
1612 +
1613 +static int __init n810bm_modinit(void)
1614 +{
1615 +       return platform_driver_probe(&n810bm_driver, n810bm_probe);
1616 +}
1617 +module_init(n810bm_modinit);
1618 +
1619 +static void __exit n810bm_modexit(void)
1620 +{
1621 +       platform_driver_unregister(&n810bm_driver);
1622 +}
1623 +module_exit(n810bm_modexit);
1624 +
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
1633 @@ -85,10 +85,10 @@
1634   *
1635   * This function writes a value to the specified register
1636   */
1637 -void retu_write_reg(int reg, u16 val)
1638 +int retu_write_reg(int reg, u16 val)
1639  {
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);
1643  }
1644  
1645  void retu_set_clear_reg_bits(int reg, u16 set, u16 clear)
1646 @@ -459,6 +459,7 @@
1647  EXPORT_SYMBOL(retu_ack_irq);
1648  EXPORT_SYMBOL(retu_read_reg);
1649  EXPORT_SYMBOL(retu_write_reg);
1650 +EXPORT_SYMBOL(retu_read_adc);
1651  
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
1658 @@ -40,6 +40,8 @@
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
1667 @@ -57,8 +59,25 @@
1668  
1669  #define        MAX_RETU_IRQ_HANDLERS   16
1670  
1671 +/* ADC channels */
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 */
1686 +
1687 +
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));
1700  }
1701  
1702 +static struct platform_device n810_bm_device = {
1703 +       .name           = "n810bm",
1704 +       .id             = -1,
1705 +};
1706 +
1707 +static void __init n810_bm_init(void)
1708 +{
1709 +       if (platform_device_register(&n810_bm_device))
1710 +               BUG();
1711 +}
1712 +
1713  static void __init n8x0_init_machine(void)
1714  {
1715         omap2420_mux_init(board_mux, OMAP_PACKAGE_ZAC);
1716 @@ -933,6 +944,8 @@
1717         n8x0_onenand_init();
1718         n8x0_mmc_init();
1719         n8x0_usb_init();
1720 +
1721 +       n810_bm_init();
1722  }
1723  
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
1729 @@ -0,0 +1,183 @@
1730 +/*
1731 + *   Generic LIPO battery charger
1732 + *
1733 + *   Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
1734 + *
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.
1739 + *
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.
1744 + */
1745 +
1746 +#define DEBUG
1747 +
1748 +#include "lipocharge.h"
1749 +
1750 +#include <linux/slab.h>
1751 +
1752 +
1753 +/* Hysteresis constants */
1754 +#define CURRENT_HYST           30 /* mA */
1755 +#define VOLTAGE_HYST           10 /* mV */
1756 +
1757 +/* Threshold constants */
1758 +#define FINISH_CURRENT_PERCENT 3
1759 +
1760 +
1761 +/* Returns the requested first-stage charge current in mA */
1762 +static inline unsigned int get_stage1_charge_current(struct lipocharge *c)
1763 +{
1764 +       /* current = (capacity * C) */
1765 +       return c->capacity * c->rate / 1000;
1766 +}
1767 +
1768 +void lipocharge_init(struct lipocharge *c, struct device *dev)
1769 +{
1770 +       c->dev = dev;
1771 +       c->state = LIPO_IDLE;
1772 +}
1773 +
1774 +void lipocharge_exit(struct lipocharge *c)
1775 +{
1776 +       c->state = LIPO_IDLE;
1777 +}
1778 +
1779 +int lipocharge_start(struct lipocharge *c)
1780 +{
1781 +       int err;
1782 +
1783 +       if (c->state != LIPO_IDLE)
1784 +               return -EBUSY;
1785 +       if (!c->set_current_pwm || !c->emergency)
1786 +               return -EINVAL;
1787 +       if (!c->top_voltage || c->top_voltage > 4200)
1788 +               return -EINVAL;
1789 +
1790 +       c->active_duty_cycle = 0;
1791 +       err = c->set_current_pwm(c, c->active_duty_cycle);
1792 +       if (err)
1793 +               return err;
1794 +       c->state = LIPO_FIRST_STAGE;
1795 +
1796 +       return 0;
1797 +}
1798 +
1799 +void lipocharge_stop(struct lipocharge *c)
1800 +{
1801 +       if (c->state == LIPO_IDLE)
1802 +               return;
1803 +       c->state = LIPO_IDLE;
1804 +}
1805 +
1806 +static int lipocharge_increase_current(struct lipocharge *c,
1807 +                                      unsigned int inc_permille)
1808 +{
1809 +       int old_pwm, new_pwm;
1810 +
1811 +       if (c->active_duty_cycle >= c->duty_cycle_max)
1812 +               return 0;
1813 +
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;
1818 +
1819 +       dev_dbg(c->dev, "lipo: Increasing duty_cycle by "
1820 +               "%u permille (0x%02X -> 0x%02X)",
1821 +               inc_permille, old_pwm, new_pwm);
1822 +
1823 +       return c->set_current_pwm(c, c->active_duty_cycle);
1824 +}
1825 +
1826 +static int lipocharge_decrease_current(struct lipocharge *c,
1827 +                                      unsigned int dec_permille)
1828 +{
1829 +       int old_pwm, new_pwm;
1830 +
1831 +       if (c->active_duty_cycle <= 0)
1832 +               return 0;
1833 +
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;
1838 +
1839 +       dev_dbg(c->dev, "lipo: Decreasing duty_cycle by "
1840 +               "%u permille (0x%02X -> 0x%02X)",
1841 +               dec_permille, old_pwm, new_pwm);
1842 +
1843 +       return c->set_current_pwm(c, c->active_duty_cycle);
1844 +}
1845 +
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.
1853 + *
1854 + * Returns 0 on success, -1 on error.
1855 + * Returns 1, if the charging process is finished.
1856 + */
1857 +int lipocharge_update_state(struct lipocharge *c,
1858 +                           unsigned int voltage_mV,
1859 +                           int current_mA,
1860 +                           unsigned int temp_K)
1861 +{
1862 +       int requested_current, current_diff;
1863 +       int err;
1864 +       unsigned int permille;
1865 +
1866 +       //TODO temp
1867 +
1868 +restart:
1869 +       switch (c->state) {
1870 +       case LIPO_IDLE:
1871 +               dev_err(c->dev, "%s: called while idle", __func__);
1872 +               return -EINVAL;
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.");
1880 +                       goto restart;
1881 +               }
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)
1886 +                       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;
1892 +                               permille /= 2;
1893 +                               err = lipocharge_increase_current(c, permille);
1894 +                               if (err)
1895 +                                       return err;
1896 +                       } else {
1897 +                               /* Decrease current */
1898 +                               permille = (-current_diff) * 1000 / requested_current;
1899 +                               permille /= 2;
1900 +                               err = lipocharge_decrease_current(c, permille);
1901 +                               if (err)
1902 +                                       return err;
1903 +                       }
1904 +               }
1905 +               break;
1906 +       case LIPO_SECOND_STAGE: /* Constant voltage */
1907 +               //TODO
1908 +               break;
1909 +       }
1910 +
1911 +       return 0;
1912 +}
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
1917 @@ -0,0 +1,60 @@
1918 +#ifndef LIPOCHARGE_H_
1919 +#define LIPOCHARGE_H_
1920 +
1921 +#include <linux/types.h>
1922 +#include <linux/device.h>
1923 +
1924 +
1925 +#define LIPORATE(a,b)  (((a) * 1000) + ((b) * 100))
1926 +#define LIPORATE_p6C   LIPORATE(0,6)   /* 0.6C */
1927 +
1928 +enum lipocharge_state {
1929 +       LIPO_IDLE,              /* Not charging */
1930 +       LIPO_FIRST_STAGE,       /* Charging: constant current */
1931 +       LIPO_SECOND_STAGE,      /* Charging: constant voltage */
1932 +};
1933 +
1934 +/** struct lipocharge - A generic LIPO charger
1935 + *
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.
1940 + *
1941 + * @set_charge_current: Set the charge current PWM duty cycle.
1942 + * @emergency: Something went wrong. Force shutdown.
1943 + */
1944 +struct lipocharge {
1945 +       unsigned int capacity;
1946 +       unsigned int rate;
1947 +       unsigned int top_voltage;
1948 +       unsigned int duty_cycle_max;
1949 +
1950 +       int (*set_current_pwm)(struct lipocharge *c, unsigned int duty_cycle);
1951 +       void (*emergency)(struct lipocharge *c);
1952 +
1953 +       /* internal */
1954 +       struct device *dev;
1955 +       enum lipocharge_state state;
1956 +       unsigned int active_duty_cycle;
1957 +
1958 +       //TODO implement timer to cut power after maximum charge time.
1959 +};
1960 +
1961 +void lipocharge_init(struct lipocharge *c, struct device *dev);
1962 +void lipocharge_exit(struct lipocharge *c);
1963 +
1964 +int lipocharge_start(struct lipocharge *c);
1965 +void lipocharge_stop(struct lipocharge *c);
1966 +
1967 +int lipocharge_update_state(struct lipocharge *c,
1968 +                           unsigned int voltage_mV,
1969 +                           int current_mA,
1970 +                           unsigned int temp_K);
1971 +
1972 +static inline bool lipocharge_is_charging(struct lipocharge *c)
1973 +{
1974 +       return (c->state != LIPO_IDLE);
1975 +}
1976 +
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
1982 @@ -30,17 +30,28 @@
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) */
1997 +
1998  #define TAHVO_REG_MAX          0x0d
1999  
2000  /* Interrupt sources */
2001  #define TAHVO_INT_VBUSON       0
2002 +#define TAHVO_INT_BATCURR      7 /* Battery current measure timer */
2003  
2004  #define MAX_TAHVO_IRQ_HANDLERS 8
2005  
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
2016 @@ -85,10 +85,10 @@
2017   *
2018   * This function writes a value to the specified register
2019   */
2020 -void tahvo_write_reg(int reg, u16 val)
2021 +int tahvo_write_reg(int reg, u16 val)
2022  {
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);
2026  }
2027  
2028  /**
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
2033 @@ -31,6 +31,7 @@
2034  #include <linux/gpio.h>
2035  #include <linux/platform_device.h>
2036  #include <linux/slab.h>
2037 +#include <linux/reboot.h>
2038  
2039  #include <asm/io.h>
2040  #include <asm/mach-types.h>
2041 @@ -301,6 +302,13 @@
2042  }
2043  module_exit(cbus_bus_exit);
2044  
2045 +void cbus_emergency(void)
2046 +{
2047 +       machine_power_off();
2048 +       panic("cbus: Failed to halt machine in emergency state\n");
2049 +}
2050 +EXPORT_SYMBOL(cbus_emergency);
2051 +
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
2059 @@ -33,4 +33,6 @@
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);
2062  
2063 +NORET_TYPE void cbus_emergency(void) ATTRIB_NORET;
2064 +
2065  #endif /* __DRIVERS_CBUS_CBUS_H */