omap24xx: Add cbus tahvo locking fix.
[openwrt.git] / target / linux / omap24xx / patches-3.1 / 900-n810-battery-management.patch
1 Index: linux-3.1/drivers/cbus/Kconfig
2 ===================================================================
3 --- linux-3.1.orig/drivers/cbus/Kconfig 2011-11-05 17:03:39.578846146 +0100
4 +++ linux-3.1/drivers/cbus/Kconfig      2011-11-05 17:06:51.589348749 +0100
5 @@ -83,4 +83,12 @@ config CBUS_RETU_HEADSET
6  
7  endif # CBUS_RETU
8  
9 +config N810BM
10 +       depends on CBUS_RETU && CBUS_TAHVO
11 +       tristate "Nokia n810 battery management"
12 +       ---help---
13 +         Nokia n810 device battery management.
14 +
15 +         If unsure, say N.
16 +
17  endmenu
18 Index: linux-3.1/drivers/cbus/Makefile
19 ===================================================================
20 --- linux-3.1.orig/drivers/cbus/Makefile        2011-11-05 17:03:39.618846091 +0100
21 +++ linux-3.1/drivers/cbus/Makefile     2011-11-05 17:06:51.589348749 +0100
22 @@ -11,3 +11,6 @@ obj-$(CONFIG_CBUS_RETU_POWERBUTTON) += r
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-3.1/drivers/cbus/n810bm_main.c
30 ===================================================================
31 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
32 +++ linux-3.1/drivers/cbus/n810bm_main.c        2011-11-05 17:06:51.589348749 +0100
33 @@ -0,0 +1,1572 @@
34 +/*
35 + *   Nokia n810 battery management
36 + *
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!
40 + *
41 + *   Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
42 + *
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.
47 + *
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.
52 + */
53 +
54 +#define DEBUG
55 +
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>
66 +#include <linux/interrupt.h>
67 +
68 +#include "cbus.h"
69 +#include "retu.h"
70 +#include "tahvo.h"
71 +#include "lipocharge.h"
72 +
73 +
74 +#define N810BM_PMM_BLOCK_FILENAME      "n810-cal-bme-pmm.fw"
75 +#define N810BM_PMM_BLOCK_SIZE          0x600
76 +#define N810BM_PMM_GROUP_SIZE          0x200
77 +#define N810BM_PMM_ELEM_SIZE           0x10
78 +
79 +#define N810BM_CHECK_INTERVAL          (HZ * 2)
80 +#define N810BM_MIN_VOLTAGE_THRES       3200 /* Absolute minimum voltage threshold */
81 +
82 +
83 +/* RETU_ADC_BSI
84 + * The battery size indicator ADC measures the resistance between
85 + * the battery BSI pin and ground. This is used to detect the battery
86 + * capacity, as the BSI resistor is related to capacity.
87 + *
88 + * Manually measured lookup table.
89 + * Hard to measure, thus not very accurate.
90 + *
91 + * Resistance  |  ADC value
92 + * ========================
93 + * 120k        |  0x3AC
94 + * 110k        |  0x37C
95 + * 100k        |  0x351
96 + *  90k        |  0x329
97 + */
98 +
99 +/* RETU_ADC_BATTVOLT
100 + * Manually measured lookup table.
101 + * Hard to measure, thus not very accurate.
102 + *
103 + * Voltage  |  ADC value
104 + * =====================
105 + * 2.80V    |  0x037
106 + * 2.90V    |  0x05E
107 + * 3.00V    |  0x090
108 + * 3.10V    |  0x0A4
109 + * 3.20V    |  0x0CC
110 + * 3.30V    |  0x0EF
111 + * 3.40V    |  0x115
112 + * 3.50V    |  0x136
113 + * 3.60V    |  0x15C
114 + * 3.70V    |  0x187
115 + * 3.80V    |  0x1A5
116 + * 3.90V    |  0x1C9
117 + * 4.00V    |  0x1ED
118 + * 4.10V    |  0x212
119 + * 4.20V    |  0x236
120 + */
121 +
122 +
123 +/* PMM block ADC IDs */
124 +enum n810bm_pmm_adc_id {
125 +       N810BM_PMM_ADC_BATVOLT          = 0x01, /* Battery voltage */
126 +       N810BM_PMM_ADC_CHGVOLT          = 0x02, /* Charger voltage */
127 +       N810BM_PMM_ADC_GND2             = 0x03, /* Ground 0V */
128 +       N810BM_PMM_ADC_BSI              = 0x04, /* Battery size indicator */
129 +       N810BM_PMM_ADC_BATTEMP          = 0x05, /* Battery temperature */
130 +       N810BM_PMM_ADC_HEADSET          = 0x06, /* Headset detection */
131 +       N810BM_PMM_ADC_HOOKDET          = 0x07, /* Hook detection */
132 +       N810BM_PMM_ADC_LIGHTSENS        = 0x08, /* Light sensor */
133 +       N810BM_PMM_ADC_BATCURR          = 0x0E, /* Battery current */
134 +       N810BM_PMM_ADC_BKUPVOLT         = 0x13, /* Backup battery voltage */
135 +       N810BM_PMM_ADC_LIGHTTEMP        = 0x14, /* Light sensor temperature */
136 +       N810BM_PMM_ADC_RFGP             = 0x15, /* RF GP */
137 +       N810BM_PMM_ADC_WBTX             = 0x16, /* Wideband TX detection */
138 +       N810BM_PMM_ADC_RETUTEMP         = 0x17, /* RETU chip temperature */
139 +       N810BM_PMM_ADC_0xFE             = 0xFE,
140 +};
141 +
142 +struct n810bm_adc_calib {
143 +       enum n810bm_pmm_adc_id id;
144 +       u8 flags;
145 +       u8 adc_groupnr;
146 +       u32 field1;
147 +       u32 field2;
148 +       u16 field3;
149 +       u16 field4;
150 +};
151 +
152 +struct n810bm_calib {
153 +       struct n810bm_adc_calib adc[25];
154 +};
155 +
156 +enum n810bm_capacity {
157 +       N810BM_CAP_UNKNOWN      = -1,
158 +       N810BM_CAP_NONE         = 0,
159 +       N810BM_CAP_1500MAH      = 1500, /* 1500 mAh battery */
160 +};
161 +
162 +enum n810bm_notify_flags {
163 +       N810BM_NOTIFY_charger_present,
164 +       N810BM_NOTIFY_charger_state,
165 +       N810BM_NOTIFY_charger_pwm,
166 +};
167 +
168 +struct n810bm {
169 +       int tahvo_irq;
170 +       bool tahvo_irq_enabled;
171 +
172 +       bool battery_present;                   /* A battery is inserted */
173 +       bool charger_present;                   /* The charger is connected */
174 +       enum n810bm_capacity capacity;          /* The capacity of the inserted battery (if any) */
175 +
176 +       bool charger_enabled;                   /* Want to charge? */
177 +       struct lipocharge charger;              /* Charger subsystem */
178 +       unsigned int active_current_pwm;        /* Active value of TAHVO_REG_CHGCURR */
179 +       int current_measure_enabled;            /* Current measure enable refcount */
180 +
181 +       struct platform_device *pdev;
182 +       struct n810bm_calib calib;              /* Calibration data */
183 +
184 +       bool verbose_charge_log;                /* Verbose charge logging */
185 +
186 +       unsigned long notify_flags;
187 +       struct work_struct notify_work;
188 +       struct delayed_work periodic_check_work;
189 +
190 +       bool initialized;                       /* The hardware was initialized */
191 +       struct mutex mutex;
192 +};
193 +
194 +static void n810bm_notify_charger_present(struct n810bm *bm);
195 +static void n810bm_notify_charger_state(struct n810bm *bm);
196 +static void n810bm_notify_charger_pwm(struct n810bm *bm);
197 +
198 +
199 +static struct platform_device *n810bm_retu_device;
200 +static struct platform_device *n810bm_tahvo_device;
201 +
202 +
203 +static inline struct n810bm * device_to_n810bm(struct device *dev)
204 +{
205 +       struct platform_device *pdev = to_platform_device(dev);
206 +       struct n810bm *bm = platform_get_drvdata(pdev);
207 +
208 +       return bm;
209 +}
210 +
211 +static inline bool n810bm_known_battery_present(struct n810bm *bm)
212 +{
213 +       return bm->battery_present &&
214 +              bm->capacity != N810BM_CAP_UNKNOWN &&
215 +              bm->capacity != N810BM_CAP_NONE;
216 +}
217 +
218 +static NORET_TYPE void n810bm_emergency(struct n810bm *bm, const char *message) ATTRIB_NORET;
219 +static void n810bm_emergency(struct n810bm *bm, const char *message)
220 +{
221 +       printk(KERN_EMERG "n810 battery management fatal fault: %s\n", message);
222 +       cbus_emergency();
223 +}
224 +
225 +static u16 tahvo_read(struct n810bm *bm, unsigned int reg)
226 +{
227 +       return tahvo_read_reg(&n810bm_tahvo_device->dev, reg);
228 +}
229 +
230 +static void tahvo_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
231 +{
232 +       tahvo_set_clear_reg_bits(&n810bm_tahvo_device->dev, reg, set, mask);
233 +}
234 +
235 +static inline void tahvo_write(struct n810bm *bm, unsigned int reg, u16 value)
236 +{
237 +       tahvo_write_reg(&n810bm_tahvo_device->dev, reg, value);
238 +}
239 +
240 +static inline void tahvo_set(struct n810bm *bm, unsigned int reg, u16 mask)
241 +{
242 +       tahvo_set_clear_reg_bits(&n810bm_tahvo_device->dev, reg, mask, mask);
243 +}
244 +
245 +static inline void tahvo_clear(struct n810bm *bm, unsigned int reg, u16 mask)
246 +{
247 +       tahvo_set_clear_reg_bits(&n810bm_tahvo_device->dev, reg, 0, mask);
248 +}
249 +
250 +static u16 retu_read(struct n810bm *bm, unsigned int reg)
251 +{
252 +       return retu_read_reg(&n810bm_retu_device->dev, reg);
253 +}
254 +
255 +static void retu_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
256 +{
257 +       retu_set_clear_reg_bits(&n810bm_retu_device->dev, reg, set, mask);
258 +}
259 +
260 +static inline void retu_write(struct n810bm *bm, unsigned int reg, u16 value)
261 +{
262 +       retu_write_reg(&n810bm_retu_device->dev, reg, value);
263 +}
264 +
265 +static int retu_adc_average(struct n810bm *bm, unsigned int chan,
266 +                           unsigned int nr_passes)
267 +{
268 +       unsigned int i, value = 0;
269 +       int ret;
270 +
271 +       if (WARN_ON(!nr_passes))
272 +               return 0;
273 +       for (i = 0; i < nr_passes; i++) {
274 +               ret = retu_read_adc(&n810bm_retu_device->dev, chan);
275 +               if (ret < 0)
276 +                       return ret;
277 +               value += ret;
278 +       }
279 +       value /= nr_passes;
280 +
281 +       return value;
282 +}
283 +
284 +static struct n810bm_adc_calib * n810bm_get_adc_calib(struct n810bm *bm,
285 +                                               enum n810bm_pmm_adc_id id)
286 +{
287 +       unsigned int index = 0;
288 +       struct n810bm_adc_calib *cal;
289 +
290 +       if (id != N810BM_PMM_ADC_0xFE)
291 +               index = (unsigned int)id + 1;
292 +       if (index >= ARRAY_SIZE(bm->calib.adc))
293 +               return NULL;
294 +
295 +       cal = &bm->calib.adc[index];
296 +       WARN_ON(cal->id && cal->id != id);
297 +
298 +       return cal;
299 +}
300 +
301 +static int pmm_record_get(struct n810bm *bm,
302 +                         const struct firmware *pmm_block,
303 +                         void *buffer, size_t length,
304 +                         unsigned int group, unsigned int element, unsigned int offset)
305 +{
306 +       const u8 *pmm_area = pmm_block->data;
307 +       u8 active_group_mask;
308 +
309 +       if (pmm_block->size != N810BM_PMM_BLOCK_SIZE)
310 +               return -EINVAL;
311 +       if (group >= N810BM_PMM_BLOCK_SIZE / N810BM_PMM_GROUP_SIZE)
312 +               return -EINVAL;
313 +       if (element >= N810BM_PMM_GROUP_SIZE / N810BM_PMM_ELEM_SIZE)
314 +               return -EINVAL;
315 +       if (offset >= N810BM_PMM_ELEM_SIZE || length > N810BM_PMM_ELEM_SIZE ||
316 +           length + offset > N810BM_PMM_ELEM_SIZE)
317 +               return -EINVAL;
318 +
319 +       active_group_mask = pmm_area[16];
320 +       if (!(active_group_mask & (1 << group))) {
321 +               dev_dbg(&bm->pdev->dev, "pwm_record_get: Requested group %u, "
322 +                       "but group is not active", group);
323 +               return -ENOENT;
324 +       }
325 +
326 +       memcpy(buffer,
327 +              pmm_area + group * N810BM_PMM_GROUP_SIZE
328 +                       + element * N810BM_PMM_ELEM_SIZE
329 +                       + offset,
330 +              length);
331 +
332 +       return 0;
333 +}
334 +
335 +/* PMM block group 1 element */
336 +struct group1_element {
337 +       u8 id;
338 +       u8 flags;
339 +       u8 adc_groupnr;
340 +       u8 _padding;
341 +       __le32 field1;
342 +       __le32 field2;
343 +} __packed;
344 +
345 +static int extract_group1_elem(struct n810bm *bm,
346 +                              const struct firmware *pmm_block,
347 +                              const enum n810bm_pmm_adc_id *pmm_adc_ids, size_t nr_pmm_adc_ids,
348 +                              u32 field1_mask, u32 field2_mask)
349 +{
350 +       struct group1_element elem;
351 +       int err;
352 +       unsigned int i, element_nr;
353 +       struct n810bm_adc_calib *adc_calib;
354 +
355 +       for (i = 0; i < nr_pmm_adc_ids; i++) {
356 +               element_nr = (unsigned int)(pmm_adc_ids[i]) + 3;
357 +
358 +               err = pmm_record_get(bm, pmm_block, &elem, sizeof(elem),
359 +                                    1, element_nr, 0);
360 +               if (err)
361 +                       continue;
362 +               adc_calib = n810bm_get_adc_calib(bm, elem.id);
363 +               if (!adc_calib) {
364 +                       dev_err(&bm->pdev->dev, "extract_group1_elem: "
365 +                               "Could not get calib element for 0x%02X",
366 +                               elem.id);
367 +                       return -EINVAL;
368 +               }
369 +
370 +               if (adc_calib->flags == elem.flags) {
371 +                       adc_calib->field1 = le32_to_cpu(elem.field1) & field1_mask;
372 +                       adc_calib->field2 = le32_to_cpu(elem.field2) & field2_mask;
373 +               } else {
374 +                       dev_dbg(&bm->pdev->dev, "extract_group1_elem: "
375 +                               "Not extracting fields due to flags mismatch: "
376 +                               "0x%02X vs 0x%02X",
377 +                               adc_calib->flags, elem.flags);
378 +               }
379 +       }
380 +
381 +       return 0;
382 +}
383 +
384 +static int n810bm_parse_pmm_group1(struct n810bm *bm,
385 +                                  const struct firmware *pmm_block)
386 +{
387 +       struct n810bm_adc_calib *adc_calib;
388 +       struct group1_element elem;
389 +       int err;
390 +
391 +       static const enum n810bm_pmm_adc_id pmm_adc_ids_1[] = {
392 +               N810BM_PMM_ADC_BATVOLT,
393 +               N810BM_PMM_ADC_CHGVOLT,
394 +               N810BM_PMM_ADC_BKUPVOLT,
395 +               N810BM_PMM_ADC_BATCURR,
396 +       };
397 +       static const enum n810bm_pmm_adc_id pmm_adc_ids_2[] = {
398 +               N810BM_PMM_ADC_BSI,
399 +       };
400 +       static const enum n810bm_pmm_adc_id pmm_adc_ids_3[] = {
401 +               N810BM_PMM_ADC_BATTEMP,
402 +       };
403 +
404 +       /* Parse element 2 */
405 +       err = pmm_record_get(bm, pmm_block, &elem, sizeof(elem),
406 +                            1, 2, 0);
407 +       if (err) {
408 +               dev_err(&bm->pdev->dev,
409 +                       "PMM: Failed to get group 1 / element 2");
410 +               return err;
411 +       }
412 +       if (elem.id == N810BM_PMM_ADC_0xFE && elem.flags == 0x05) {
413 +               adc_calib = n810bm_get_adc_calib(bm, elem.id);
414 +               if (!adc_calib) {
415 +                       dev_err(&bm->pdev->dev,
416 +                               "calib extract: Failed to get 0xFE calib");
417 +                       return -EINVAL;
418 +               }
419 +               adc_calib->id = elem.id;
420 +               adc_calib->flags = elem.flags;
421 +               adc_calib->field1 = le32_to_cpu(elem.field1);
422 +               adc_calib->field2 = le32_to_cpu(elem.field2);
423 +       }
424 +
425 +       err = extract_group1_elem(bm, pmm_block,
426 +                                 pmm_adc_ids_1, ARRAY_SIZE(pmm_adc_ids_1),
427 +                                 0xFFFFFFFF, 0xFFFFFFFF);
428 +       if (err)
429 +               return err;
430 +       err = extract_group1_elem(bm, pmm_block,
431 +                                 pmm_adc_ids_2, ARRAY_SIZE(pmm_adc_ids_2),
432 +                                 0xFFFFFFFF, 0);
433 +       if (err)
434 +               return err;
435 +       err = extract_group1_elem(bm, pmm_block,
436 +                                 pmm_adc_ids_3, ARRAY_SIZE(pmm_adc_ids_3),
437 +                                 0xFFFFFFFF, 0x0000FFFF);
438 +       if (err)
439 +               return err;
440 +
441 +       return 0;
442 +}
443 +
444 +static int n810bm_parse_pmm_group2(struct n810bm *bm,
445 +                                  const struct firmware *pmm_block)
446 +{
447 +       dev_err(&bm->pdev->dev, "TODO: CAL BME PMM group 2 parser not implemented, yet");
448 +       return -EOPNOTSUPP;
449 +}
450 +
451 +static void n810bm_adc_calib_set_defaults(struct n810bm *bm)
452 +{
453 +       struct n810bm_adc_calib *adc_calib;
454 +       unsigned int i;
455 +
456 +       static const struct n810bm_adc_calib defaults[] = {
457 +               /* ADC group-nr 0 */
458 +               {
459 +                       .id             = N810BM_PMM_ADC_HEADSET,
460 +                       .flags          = 0x00,
461 +                       .adc_groupnr    = 0,
462 +               }, {
463 +                       .id             = N810BM_PMM_ADC_HOOKDET,
464 +                       .flags          = 0x00,
465 +                       .adc_groupnr    = 0,
466 +               }, {
467 +                       .id             = N810BM_PMM_ADC_RFGP,
468 +                       .flags          = 0x00,
469 +                       .adc_groupnr    = 0,
470 +               }, {
471 +                       .id             = N810BM_PMM_ADC_LIGHTSENS,
472 +                       .flags          = 0x00,
473 +                       .adc_groupnr    = 0,
474 +               }, {
475 +                       .id             = N810BM_PMM_ADC_WBTX,
476 +                       .flags          = 0x00,
477 +                       .adc_groupnr    = 0,
478 +               }, {
479 +                       .id             = N810BM_PMM_ADC_RETUTEMP,
480 +                       .flags          = 0x00,
481 +                       .adc_groupnr    = 0,
482 +               }, {
483 +                       .id             = N810BM_PMM_ADC_GND2,
484 +                       .flags          = 0x00,
485 +                       .adc_groupnr    = 0,
486 +               },
487 +               /* ADC group-nr 1 */
488 +               {
489 +                       .id             = N810BM_PMM_ADC_0xFE,
490 +                       .flags          = 0x05,
491 +                       .adc_groupnr    = 1,
492 +                       .field1         = (u32)-2,
493 +                       .field2         = 13189,
494 +               }, {
495 +                       .id             = N810BM_PMM_ADC_BATVOLT,
496 +                       .flags          = 0x01,
497 +                       .adc_groupnr    = 1,
498 +                       .field1         = 2527,
499 +                       .field2         = 21373,
500 +               }, {
501 +                       .id             = N810BM_PMM_ADC_CHGVOLT,
502 +                       .flags          = 0x01,
503 +                       .adc_groupnr    = 1,
504 +                       .field1         = 0,
505 +                       .field2         = 129848,
506 +               }, {
507 +                       .id             = N810BM_PMM_ADC_BKUPVOLT,
508 +                       .flags          = 0x01,
509 +                       .adc_groupnr    = 1,
510 +                       .field1         = 0,
511 +                       .field2         = 20000,
512 +               }, {
513 +                       .id             = N810BM_PMM_ADC_BATCURR,
514 +                       .flags          = 0x06,
515 +                       .adc_groupnr    = 1,
516 +                       .field1         = 0,
517 +                       .field2         = 9660,
518 +               },
519 +               /* ADC group-nr 2 */
520 +               {
521 +                       .id             = N810BM_PMM_ADC_BSI,
522 +                       .flags          = 0x02,
523 +                       .adc_groupnr    = 2,
524 +                       .field1         = 1169,
525 +                       .field2         = 0,
526 +               },
527 +               /* ADC group-nr 3 */
528 +               {
529 +                       .id             = N810BM_PMM_ADC_BATTEMP,
530 +                       .flags          = 0x03,
531 +                       .adc_groupnr    = 3,
532 +                       .field1         = 265423000,
533 +                       .field2         = 298,
534 +               },
535 +               /* ADC group-nr 4 */
536 +               {
537 +                       .id             = N810BM_PMM_ADC_LIGHTTEMP,
538 +                       .flags          = 0x04,
539 +                       .adc_groupnr    = 4,
540 +                       .field1         = 19533778,
541 +                       .field2         = 308019670,
542 +                       .field3         = 4700,
543 +                       .field4         = 2500,
544 +               },
545 +       };
546 +
547 +       /* Clear the array */
548 +       memset(&bm->calib.adc, 0, sizeof(bm->calib.adc));
549 +       for (i = 0; i < ARRAY_SIZE(bm->calib.adc); i++)
550 +               bm->calib.adc[i].flags = 0xFF;
551 +
552 +       /* Copy the defaults */
553 +       for (i = 0; i < ARRAY_SIZE(defaults); i++) {
554 +               adc_calib = n810bm_get_adc_calib(bm, defaults[i].id);
555 +               if (WARN_ON(!adc_calib))
556 +                       continue;
557 +               *adc_calib = defaults[i];
558 +       }
559 +}
560 +
561 +static int n810bm_parse_pmm_block(struct n810bm *bm,
562 +                                 const struct firmware *pmm_block)
563 +{
564 +       u8 byte;
565 +       int err;
566 +       unsigned int i, count;
567 +       struct n810bm_adc_calib *adc_calib;
568 +
569 +       /* Initialize to defaults */
570 +       n810bm_adc_calib_set_defaults(bm);
571 +
572 +       /* Parse the PMM data */
573 +       err = pmm_record_get(bm, pmm_block, &byte, sizeof(byte),
574 +                            1, 0, 0); /* group 1 / element 0 */
575 +       err |= (byte != 0x01);
576 +       err |= pmm_record_get(bm, pmm_block, &byte, sizeof(byte),
577 +                             1, 1, 0); /* group 1 / element 1 */
578 +       err |= (byte != 0x01);
579 +       if (err)
580 +               err = n810bm_parse_pmm_group2(bm, pmm_block);
581 +       else
582 +               err = n810bm_parse_pmm_group1(bm, pmm_block);
583 +       if (err)
584 +               return err;
585 +
586 +       /* Sanity checks */
587 +       for (i = 0, count = 0; i < ARRAY_SIZE(bm->calib.adc); i++) {
588 +               adc_calib = &bm->calib.adc[i];
589 +               if (adc_calib->flags == 0xFF)
590 +                       continue;
591 +               switch (adc_calib->id) {
592 +               case N810BM_PMM_ADC_BATVOLT:
593 +                       if (adc_calib->field1 < 2400 ||
594 +                           adc_calib->field1 > 2700)
595 +                               goto value_check_fail;
596 +                       if (adc_calib->field2 < 20000 ||
597 +                           adc_calib->field2 > 23000)
598 +                               goto value_check_fail;
599 +                       count++;
600 +                       break;
601 +               case N810BM_PMM_ADC_BSI:
602 +                       if (adc_calib->field1 < 1100 ||
603 +                           adc_calib->field1 > 1300)
604 +                               goto value_check_fail;
605 +                       count++;
606 +                       break;
607 +               case N810BM_PMM_ADC_BATCURR:
608 +                       if (adc_calib->field2 < 7000 ||
609 +                           adc_calib->field2 > 12000)
610 +                               goto value_check_fail;
611 +                       count++;
612 +                       break;
613 +               case N810BM_PMM_ADC_0xFE:
614 +                       if ((s32)adc_calib->field1 > 14 ||
615 +                           (s32)adc_calib->field1 < -14)
616 +                               goto value_check_fail;
617 +                       if (adc_calib->field2 < 13000 ||
618 +                           adc_calib->field2 > 13350)
619 +                               goto value_check_fail;
620 +                       count++;
621 +                       break;
622 +               case N810BM_PMM_ADC_CHGVOLT:
623 +               case N810BM_PMM_ADC_BATTEMP:
624 +               case N810BM_PMM_ADC_BKUPVOLT:
625 +                       count++;
626 +                       break;
627 +               case N810BM_PMM_ADC_GND2:
628 +               case N810BM_PMM_ADC_HOOKDET:
629 +               case N810BM_PMM_ADC_LIGHTSENS:
630 +               case N810BM_PMM_ADC_HEADSET:
631 +               case N810BM_PMM_ADC_LIGHTTEMP:
632 +               case N810BM_PMM_ADC_RFGP:
633 +               case N810BM_PMM_ADC_WBTX:
634 +               case N810BM_PMM_ADC_RETUTEMP:
635 +                       break;
636 +               }
637 +               dev_dbg(&bm->pdev->dev,
638 +                       "ADC 0x%02X calib: 0x%02X 0x%02X 0x%08X 0x%08X 0x%04X 0x%04X",
639 +                       adc_calib->id, adc_calib->flags, adc_calib->adc_groupnr,
640 +                       adc_calib->field1, adc_calib->field2,
641 +                       adc_calib->field3, adc_calib->field4);
642 +       }
643 +       if (count != 7) {
644 +               dev_err(&bm->pdev->dev, "PMM sanity check: Did not find "
645 +                       "all required values (count=%u)", count);
646 +               goto check_fail;
647 +       }
648 +
649 +       return 0;
650 +
651 +value_check_fail:
652 +       dev_err(&bm->pdev->dev, "PMM image sanity check failed "
653 +               "(id=%02X, field1=%08X, field2=%08X)",
654 +               adc_calib->id, adc_calib->field1, adc_calib->field2);
655 +check_fail:
656 +       return -EILSEQ;
657 +}
658 +
659 +/* Set the current measure timer that triggers on Tahvo IRQ 7
660 + * An interval of zero disables the timer. */
661 +static void n810bm_set_current_measure_timer(struct n810bm *bm,
662 +                                            u16 millisec_interval)
663 +{
664 +       u16 value = millisec_interval;
665 +
666 +       if (value <= 0xF905) {
667 +               value = ((u64)0x10624DD3 * (u64)(value + 0xF9)) >> 32;
668 +               value /= 16;
669 +       } else
670 +               value = 0xFF;
671 +
672 +       tahvo_write(bm, TAHVO_REG_BATCURRTIMER, value & 0xFF);
673 +
674 +       tahvo_set(bm, TAHVO_REG_CHGCTL,
675 +                 TAHVO_REG_CHGCTL_CURTIMRST);
676 +       tahvo_clear(bm, TAHVO_REG_CHGCTL,
677 +                   TAHVO_REG_CHGCTL_CURTIMRST);
678 +
679 +       if (millisec_interval) {
680 +               if (!bm->tahvo_irq_enabled) {
681 +                       bm->tahvo_irq_enabled = 1;
682 +                       enable_irq(bm->tahvo_irq);
683 +               }
684 +       } else {
685 +               if (bm->tahvo_irq_enabled) {
686 +                       bm->tahvo_irq_enabled = 0;
687 +                       disable_irq_nosync(bm->tahvo_irq);
688 +               }
689 +       }
690 +
691 +       //TODO also do a software timer for safety.
692 +}
693 +
694 +static void n810bm_enable_current_measure(struct n810bm *bm)
695 +{
696 +       WARN_ON(bm->current_measure_enabled < 0);
697 +       if (!bm->current_measure_enabled) {
698 +               /* Enable the current measurement circuitry */
699 +               tahvo_set(bm, TAHVO_REG_CHGCTL,
700 +                         TAHVO_REG_CHGCTL_CURMEAS);
701 +               dev_dbg(&bm->pdev->dev,
702 +                       "Current measurement circuitry enabled");
703 +       }
704 +       bm->current_measure_enabled++;
705 +}
706 +
707 +static void n810bm_disable_current_measure(struct n810bm *bm)
708 +{
709 +       bm->current_measure_enabled--;
710 +       WARN_ON(bm->current_measure_enabled < 0);
711 +       if (!bm->current_measure_enabled) {
712 +               /* Disable the current measurement circuitry */
713 +               tahvo_clear(bm, TAHVO_REG_CHGCTL,
714 +                           TAHVO_REG_CHGCTL_CURMEAS);
715 +               dev_dbg(&bm->pdev->dev,
716 +                       "Current measurement circuitry disabled");
717 +       }
718 +}
719 +
720 +/* Measure the actual battery current. Returns a signed value in mA.
721 + * Does only work, if current measurement was enabled. */
722 +static int n810bm_measure_batt_current(struct n810bm *bm)
723 +{
724 +       u16 retval;
725 +       int adc = 0, ma, i;
726 +
727 +       if (WARN_ON(bm->current_measure_enabled <= 0))
728 +               return 0;
729 +       for (i = 0; i < 3; i++) {
730 +               retval = tahvo_read(bm, TAHVO_REG_BATCURR);
731 +               adc += (s16)retval; /* Value is signed */
732 +       }
733 +       adc /= 3;
734 +
735 +       //TODO convert to mA
736 +       ma = adc;
737 +
738 +       return ma;
739 +}
740 +
741 +/* Requires bm->mutex locked */
742 +static int n810bm_measure_batt_current_async(struct n810bm *bm)
743 +{
744 +       int ma;
745 +       bool charging = lipocharge_is_charging(&bm->charger);
746 +
747 +       n810bm_enable_current_measure(bm);
748 +       if (!charging)
749 +               WARN_ON(bm->active_current_pwm != 0);
750 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
751 +                     TAHVO_REG_CHGCTL_EN |
752 +                     TAHVO_REG_CHGCTL_PWMOVR |
753 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
754 +                     TAHVO_REG_CHGCTL_EN |
755 +                     TAHVO_REG_CHGCTL_PWMOVR |
756 +                     (charging ? 0 : TAHVO_REG_CHGCTL_PWMOVRZERO));
757 +       ma = n810bm_measure_batt_current(bm);
758 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
759 +                     TAHVO_REG_CHGCTL_EN |
760 +                     TAHVO_REG_CHGCTL_PWMOVR |
761 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
762 +                     (charging ? TAHVO_REG_CHGCTL_EN : 0));
763 +       n810bm_disable_current_measure(bm);
764 +
765 +       return ma;
766 +}
767 +
768 +static int adc_sanity_check(struct n810bm *bm, unsigned int channel)
769 +{
770 +       int value;
771 +
772 +       value = retu_read_adc(&n810bm_retu_device->dev, channel);
773 +       if (value < 0) {
774 +               dev_err(&bm->pdev->dev, "Failed to read GND ADC channel %u",
775 +                       channel);
776 +               return -EIO;
777 +       }
778 +       dev_dbg(&bm->pdev->dev,
779 +               "GND ADC channel %u sanity check got value: %d",
780 +               channel, value);
781 +       if (value > 5) {
782 +               n810bm_emergency(bm, "GND ADC sanity check failed");
783 +               return -EIO;
784 +       }
785 +
786 +       return 0;
787 +}
788 +
789 +static int n810bm_check_adc_sanity(struct n810bm *bm)
790 +{
791 +       int err;
792 +
793 +       /* Discard one conversion */
794 +       retu_write(bm, RETU_REG_ADCSCR, 0);
795 +       retu_read_adc(&n810bm_retu_device->dev, RETU_ADC_GND2);
796 +
797 +       err = adc_sanity_check(bm, RETU_ADC_GND2);
798 +       if (err)
799 +               return err;
800 +
801 +       return 0;
802 +}
803 +
804 +/* Measure the battery voltage. Returns the value in mV (or negative value on error). */
805 +static int n810bm_measure_batt_voltage(struct n810bm *bm)
806 +{
807 +       int adc;
808 +       unsigned int mv;
809 +       const unsigned int scale = 1000;
810 +
811 +       adc = retu_adc_average(bm, RETU_ADC_BATTVOLT, 5);
812 +       if (adc < 0)
813 +               return adc;
814 +       if (adc <= 0x37)
815 +               return 2800;
816 +       mv = 2800 + ((adc - 0x37) * (((4200 - 2800) * scale) / (0x236 - 0x37))) / scale;
817 +
818 +       //TODO compensate for power consumption
819 +       //TODO honor calibration values
820 +
821 +       return mv;
822 +}
823 +
824 +/* Measure the charger voltage. Returns the value in mV (or negative value on error). */
825 +static int n810bm_measure_charger_voltage(struct n810bm *bm)
826 +{
827 +       int adc;
828 +       unsigned int mv;
829 +
830 +       adc = retu_adc_average(bm, RETU_ADC_CHGVOLT, 5);
831 +       if (adc < 0)
832 +               return adc;
833 +       //TODO convert to mV
834 +       mv = adc;
835 +
836 +       return mv;
837 +}
838 +
839 +/* Measure backup battery voltage. Returns the value in mV (or negative value on error). */
840 +static int n810bm_measure_backup_batt_voltage(struct n810bm *bm)
841 +{
842 +       int adc;
843 +       unsigned int mv;
844 +
845 +       adc = retu_adc_average(bm, RETU_ADC_BKUPVOLT, 3);
846 +       if (adc < 0)
847 +               return adc;
848 +       //TODO convert to mV
849 +       mv = adc;
850 +
851 +       return mv;
852 +}
853 +
854 +/* Measure the battery temperature. Returns the value in K (or negative value on error). */
855 +static int n810bm_measure_batt_temp(struct n810bm *bm)
856 +{
857 +       int adc;
858 +       unsigned int k;
859 +
860 +       adc = retu_adc_average(bm, RETU_ADC_BATTEMP, 3);
861 +       if (adc < 0)
862 +               return adc;
863 +       //TODO convert to K
864 +       k = adc;
865 +
866 +       return k;
867 +}
868 +
869 +/* Read the battery capacity via BSI pin. */
870 +static enum n810bm_capacity n810bm_read_batt_capacity(struct n810bm *bm)
871 +{
872 +       int adc;
873 +       const unsigned int hyst = 20;
874 +
875 +       adc = retu_adc_average(bm, RETU_ADC_BSI, 5);
876 +       if (adc < 0) {
877 +               dev_err(&bm->pdev->dev, "Failed to read BSI ADC");
878 +               return N810BM_CAP_UNKNOWN;
879 +       }
880 +
881 +       if (adc >= 0x3B5 - hyst && adc <= 0x3B5 + hyst)
882 +               return N810BM_CAP_1500MAH;
883 +
884 +       dev_err(&bm->pdev->dev, "Capacity indicator 0x%X unknown", adc);
885 +
886 +       return N810BM_CAP_UNKNOWN;
887 +}
888 +
889 +/* Convert a battery voltage (in mV) to percentage. */
890 +static unsigned int n810bm_mvolt2percent(unsigned int mv)
891 +{
892 +       const unsigned int minv = 3700;
893 +       const unsigned int maxv = 4150;
894 +       unsigned int percent;
895 +
896 +       mv = clamp(mv, minv, maxv);
897 +       percent = (mv - minv) * 100 / (maxv - minv);
898 +
899 +       return percent;
900 +}
901 +
902 +static void n810bm_start_charge(struct n810bm *bm)
903 +{
904 +       int err;
905 +
906 +       WARN_ON(!bm->battery_present);
907 +       WARN_ON(!bm->charger_present);
908 +
909 +       /* Set PWM to zero */
910 +       bm->active_current_pwm = 0;
911 +       tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
912 +
913 +       /* Charge global enable */
914 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
915 +                     TAHVO_REG_CHGCTL_EN |
916 +                     TAHVO_REG_CHGCTL_PWMOVR |
917 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
918 +                     TAHVO_REG_CHGCTL_EN);
919 +
920 +       WARN_ON((int)bm->capacity <= 0);
921 +       bm->charger.capacity = bm->capacity;
922 +       err = lipocharge_start(&bm->charger);
923 +       WARN_ON(err);
924 +
925 +       /* Initialize current measurement circuitry */
926 +       n810bm_enable_current_measure(bm);
927 +       n810bm_set_current_measure_timer(bm, 250);
928 +
929 +       dev_info(&bm->pdev->dev, "Charging battery");
930 +       n810bm_notify_charger_state(bm);
931 +       n810bm_notify_charger_pwm(bm);
932 +}
933 +
934 +static void n810bm_stop_charge(struct n810bm *bm)
935 +{
936 +       if (lipocharge_is_charging(&bm->charger)) {
937 +               n810bm_set_current_measure_timer(bm, 0);
938 +               n810bm_disable_current_measure(bm);
939 +       }
940 +       lipocharge_stop(&bm->charger);
941 +
942 +       /* Set PWM to zero */
943 +       bm->active_current_pwm = 0;
944 +       tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
945 +
946 +       /* Charge global disable */
947 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
948 +                     TAHVO_REG_CHGCTL_EN |
949 +                     TAHVO_REG_CHGCTL_PWMOVR |
950 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
951 +                     0);
952 +
953 +       dev_info(&bm->pdev->dev, "Not charging battery");
954 +       n810bm_notify_charger_state(bm);
955 +       n810bm_notify_charger_pwm(bm);
956 +}
957 +
958 +/* Periodic check */
959 +static void n810bm_periodic_check_work(struct work_struct *work)
960 +{
961 +       struct n810bm *bm = container_of(to_delayed_work(work),
962 +                                        struct n810bm, periodic_check_work);
963 +       u16 status;
964 +       bool battery_was_present, charger_was_present;
965 +       int mv;
966 +
967 +       mutex_lock(&bm->mutex);
968 +
969 +       status = retu_read(bm, RETU_REG_STATUS);
970 +       battery_was_present = bm->battery_present;
971 +       charger_was_present = bm->charger_present;
972 +       bm->battery_present = !!(status & RETU_REG_STATUS_BATAVAIL);
973 +       bm->charger_present = !!(status & RETU_REG_STATUS_CHGPLUG);
974 +
975 +       if (bm->battery_present != battery_was_present) {
976 +               /* Battery state changed */
977 +               if (bm->battery_present) {
978 +                       bm->capacity = n810bm_read_batt_capacity(bm);
979 +                       if (bm->capacity == N810BM_CAP_UNKNOWN) {
980 +                               dev_err(&bm->pdev->dev, "Unknown battery detected");
981 +                       } else {
982 +                               dev_info(&bm->pdev->dev, "Detected %u mAh battery",
983 +                                        (unsigned int)bm->capacity);
984 +                       }
985 +               } else {
986 +                       bm->capacity = N810BM_CAP_NONE;
987 +                       dev_info(&bm->pdev->dev, "The main battery was removed");
988 +                       //TODO disable charging
989 +               }
990 +       }
991 +
992 +       if (bm->charger_present != charger_was_present) {
993 +               /* Charger state changed */
994 +               dev_info(&bm->pdev->dev, "The charger was %s",
995 +                        bm->charger_present ? "plugged in" : "removed");
996 +               n810bm_notify_charger_present(bm);
997 +       }
998 +
999 +       if ((bm->battery_present && !bm->charger_present) ||
1000 +           !n810bm_known_battery_present(bm)){
1001 +               /* We're draining the battery */
1002 +               mv = n810bm_measure_batt_voltage(bm);
1003 +               if (mv < 0) {
1004 +                       n810bm_emergency(bm,
1005 +                               "check: Failed to measure voltage");
1006 +               }
1007 +               if (mv < N810BM_MIN_VOLTAGE_THRES) {
1008 +                       n810bm_emergency(bm,
1009 +                               "check: Minimum voltage threshold reached");
1010 +               }
1011 +       }
1012 +
1013 +       if (bm->charger_present && n810bm_known_battery_present(bm)) {
1014 +               /* Known battery and charger are connected */
1015 +               if (bm->charger_enabled) {
1016 +                       /* Charger is enabled */
1017 +                       if (!lipocharge_is_charging(&bm->charger)) {
1018 +                               //TODO start charging, if battery is below some threshold
1019 +                               n810bm_start_charge(bm);
1020 +                       }
1021 +               }
1022 +       }
1023 +
1024 +       if (lipocharge_is_charging(&bm->charger) && !bm->charger_present) {
1025 +               /* Charger was unplugged. */
1026 +               n810bm_stop_charge(bm);
1027 +       }
1028 +
1029 +       mutex_unlock(&bm->mutex);
1030 +       schedule_delayed_work(&bm->periodic_check_work,
1031 +                             round_jiffies_relative(N810BM_CHECK_INTERVAL));
1032 +}
1033 +
1034 +/*XXX
1035 +static void n810bm_adc_irq_handler(unsigned long data)
1036 +{
1037 +       struct n810bm *bm = (struct n810bm *)data;
1038 +
1039 +       retu_ack_irq(RETU_INT_ADCS);
1040 +       //TODO
1041 +dev_info(&bm->pdev->dev, "ADC interrupt triggered\n");
1042 +}
1043 +*/
1044 +
1045 +static irqreturn_t n810bm_tahvo_current_measure_irq_handler(int irq, void *data)
1046 +{
1047 +       struct n810bm *bm = data;
1048 +       int res, ma, mv, temp;
1049 +
1050 +       mutex_lock(&bm->mutex);
1051 +       if (!lipocharge_is_charging(&bm->charger))
1052 +               goto out_unlock;
1053 +
1054 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1055 +                     TAHVO_REG_CHGCTL_PWMOVR |
1056 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
1057 +                     TAHVO_REG_CHGCTL_PWMOVR);
1058 +       ma = n810bm_measure_batt_current(bm);
1059 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1060 +                     TAHVO_REG_CHGCTL_PWMOVR |
1061 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
1062 +                     TAHVO_REG_CHGCTL_PWMOVR |
1063 +                     TAHVO_REG_CHGCTL_PWMOVRZERO);
1064 +       msleep(10);
1065 +       mv = n810bm_measure_batt_voltage(bm);
1066 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
1067 +                     TAHVO_REG_CHGCTL_PWMOVR |
1068 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
1069 +                     0);
1070 +       temp = n810bm_measure_batt_temp(bm);
1071 +       if (WARN_ON(mv < 0))
1072 +               goto out_unlock;
1073 +       if (WARN_ON(temp < 0))
1074 +               goto out_unlock;
1075 +
1076 +       if (bm->verbose_charge_log) {
1077 +               dev_info(&bm->pdev->dev,
1078 +                        "Battery charge state: %d mV, %d mA (%s)",
1079 +                        mv, ma,
1080 +                        (ma <= 0) ? "discharging" : "charging");
1081 +       }
1082 +       res = lipocharge_update_state(&bm->charger, mv, ma, temp);
1083 +       if (res) {
1084 +               if (res > 0)
1085 +                       dev_info(&bm->pdev->dev, "Battery fully charged");
1086 +               n810bm_stop_charge(bm);
1087 +       }
1088 +out_unlock:
1089 +       mutex_unlock(&bm->mutex);
1090 +
1091 +       return IRQ_HANDLED;
1092 +}
1093 +
1094 +#define DEFINE_ATTR_NOTIFY(attr_name)                                          \
1095 +       void n810bm_notify_##attr_name(struct n810bm *bm)                       \
1096 +       {                                                                       \
1097 +               set_bit(N810BM_NOTIFY_##attr_name, &bm->notify_flags);          \
1098 +               wmb();                                                          \
1099 +               schedule_work(&bm->notify_work);                                \
1100 +       }
1101 +
1102 +#define DEFINE_SHOW_INT_FUNC(name, member)                                     \
1103 +       static ssize_t n810bm_attr_##name##_show(struct device *dev,            \
1104 +                                                struct device_attribute *attr, \
1105 +                                                char *buf)                     \
1106 +       {                                                                       \
1107 +               struct n810bm *bm = device_to_n810bm(dev);                      \
1108 +               ssize_t count;                                                  \
1109 +                                                                               \
1110 +               mutex_lock(&bm->mutex);                                         \
1111 +               count = snprintf(buf, PAGE_SIZE, "%d\n", (int)(bm->member));    \
1112 +               mutex_unlock(&bm->mutex);                                       \
1113 +                                                                               \
1114 +               return count;                                                   \
1115 +       }
1116 +
1117 +#define DEFINE_STORE_INT_FUNC(name, member)                                    \
1118 +       static ssize_t n810bm_attr_##name##_store(struct device *dev,           \
1119 +                                                 struct device_attribute *attr,\
1120 +                                                 const char *buf, size_t count)\
1121 +       {                                                                       \
1122 +               struct n810bm *bm = device_to_n810bm(dev);                      \
1123 +               long val;                                                       \
1124 +               int err;                                                        \
1125 +                                                                               \
1126 +               mutex_lock(&bm->mutex);                                         \
1127 +               err = strict_strtol(buf, 0, &val);                              \
1128 +               if (!err)                                                       \
1129 +                       bm->member = (typeof(bm->member))val;                   \
1130 +               mutex_unlock(&bm->mutex);                                       \
1131 +                                                                               \
1132 +               return err ? err : count;                                       \
1133 +       }
1134 +
1135 +#define DEFINE_ATTR_SHOW_INT(name, member)                                     \
1136 +       DEFINE_SHOW_INT_FUNC(name, member)                                      \
1137 +       static DEVICE_ATTR(name, S_IRUGO,                                       \
1138 +                          n810bm_attr_##name##_show, NULL);
1139 +
1140 +#define DEFINE_ATTR_SHOW_STORE_INT(name, member)                               \
1141 +       DEFINE_SHOW_INT_FUNC(name, member)                                      \
1142 +       DEFINE_STORE_INT_FUNC(name, member)                                     \
1143 +       static DEVICE_ATTR(name, S_IRUGO | S_IWUSR,                             \
1144 +                          n810bm_attr_##name##_show,                           \
1145 +                          n810bm_attr_##name##_store);
1146 +
1147 +DEFINE_ATTR_SHOW_INT(battery_present, battery_present);
1148 +DEFINE_ATTR_SHOW_INT(charger_present, charger_present);
1149 +static DEFINE_ATTR_NOTIFY(charger_present);
1150 +DEFINE_ATTR_SHOW_INT(charger_state, charger.state);
1151 +static DEFINE_ATTR_NOTIFY(charger_state);
1152 +DEFINE_ATTR_SHOW_INT(charger_pwm, active_current_pwm);
1153 +static DEFINE_ATTR_NOTIFY(charger_pwm);
1154 +DEFINE_ATTR_SHOW_STORE_INT(charger_enable, charger_enabled);
1155 +DEFINE_ATTR_SHOW_STORE_INT(charger_verbose, verbose_charge_log);
1156 +
1157 +static ssize_t n810bm_attr_battery_level_show(struct device *dev,
1158 +                                             struct device_attribute *attr,
1159 +                                             char *buf)
1160 +{
1161 +       struct n810bm *bm = device_to_n810bm(dev);
1162 +       ssize_t count = -ENODEV;
1163 +       int millivolt;
1164 +
1165 +       mutex_lock(&bm->mutex);
1166 +       if (!bm->battery_present || lipocharge_is_charging(&bm->charger))
1167 +               millivolt = 0;
1168 +       else
1169 +               millivolt = n810bm_measure_batt_voltage(bm);
1170 +       if (millivolt >= 0) {
1171 +               count = snprintf(buf, PAGE_SIZE, "%u\n",
1172 +                                n810bm_mvolt2percent(millivolt));
1173 +       }
1174 +       mutex_unlock(&bm->mutex);
1175 +
1176 +       return count;
1177 +}
1178 +static DEVICE_ATTR(battery_level, S_IRUGO,
1179 +                  n810bm_attr_battery_level_show, NULL);
1180 +
1181 +static ssize_t n810bm_attr_battery_capacity_show(struct device *dev,
1182 +                                                struct device_attribute *attr,
1183 +                                                char *buf)
1184 +{
1185 +       struct n810bm *bm = device_to_n810bm(dev);
1186 +       ssize_t count;
1187 +       int capacity = 0;
1188 +
1189 +       mutex_lock(&bm->mutex);
1190 +       if (n810bm_known_battery_present(bm))
1191 +               capacity = (int)bm->capacity;
1192 +       count = snprintf(buf, PAGE_SIZE, "%d\n", capacity);
1193 +       mutex_unlock(&bm->mutex);
1194 +
1195 +       return count;
1196 +}
1197 +static DEVICE_ATTR(battery_capacity, S_IRUGO,
1198 +                  n810bm_attr_battery_capacity_show, NULL);
1199 +
1200 +static ssize_t n810bm_attr_battery_temp_show(struct device *dev,
1201 +                                            struct device_attribute *attr,
1202 +                                            char *buf)
1203 +{
1204 +       struct n810bm *bm = device_to_n810bm(dev);
1205 +       ssize_t count = -ENODEV;
1206 +       int k;
1207 +
1208 +       mutex_lock(&bm->mutex);
1209 +       k = n810bm_measure_batt_temp(bm);
1210 +       if (k >= 0)
1211 +               count = snprintf(buf, PAGE_SIZE, "%d\n", k);
1212 +       mutex_unlock(&bm->mutex);
1213 +
1214 +       return count;
1215 +}
1216 +static DEVICE_ATTR(battery_temp, S_IRUGO,
1217 +                  n810bm_attr_battery_temp_show, NULL);
1218 +
1219 +static ssize_t n810bm_attr_charger_voltage_show(struct device *dev,
1220 +                                               struct device_attribute *attr,
1221 +                                               char *buf)
1222 +{
1223 +       struct n810bm *bm = device_to_n810bm(dev);
1224 +       ssize_t count = -ENODEV;
1225 +       int mv = 0;
1226 +
1227 +       mutex_lock(&bm->mutex);
1228 +       if (bm->charger_present)
1229 +               mv = n810bm_measure_charger_voltage(bm);
1230 +       if (mv >= 0)
1231 +               count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
1232 +       mutex_unlock(&bm->mutex);
1233 +
1234 +       return count;
1235 +}
1236 +static DEVICE_ATTR(charger_voltage, S_IRUGO,
1237 +                  n810bm_attr_charger_voltage_show, NULL);
1238 +
1239 +static ssize_t n810bm_attr_backup_battery_voltage_show(struct device *dev,
1240 +                                                      struct device_attribute *attr,
1241 +                                                      char *buf)
1242 +{
1243 +       struct n810bm *bm = device_to_n810bm(dev);
1244 +       ssize_t count = -ENODEV;
1245 +       int mv;
1246 +
1247 +       mutex_lock(&bm->mutex);
1248 +       mv = n810bm_measure_backup_batt_voltage(bm);
1249 +       if (mv >= 0)
1250 +               count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
1251 +       mutex_unlock(&bm->mutex);
1252 +
1253 +       return count;
1254 +}
1255 +static DEVICE_ATTR(backup_battery_voltage, S_IRUGO,
1256 +                  n810bm_attr_backup_battery_voltage_show, NULL);
1257 +
1258 +static ssize_t n810bm_attr_battery_current_show(struct device *dev,
1259 +                                               struct device_attribute *attr,
1260 +                                               char *buf)
1261 +{
1262 +       struct n810bm *bm = device_to_n810bm(dev);
1263 +       ssize_t count = -ENODEV;
1264 +       int ma = 0;
1265 +
1266 +       mutex_lock(&bm->mutex);
1267 +       if (bm->battery_present)
1268 +               ma = n810bm_measure_batt_current_async(bm);
1269 +       count = snprintf(buf, PAGE_SIZE, "%d\n", ma);
1270 +       mutex_unlock(&bm->mutex);
1271 +
1272 +       return count;
1273 +}
1274 +static DEVICE_ATTR(battery_current, S_IRUGO,
1275 +                  n810bm_attr_battery_current_show, NULL);
1276 +
1277 +static const struct device_attribute *n810bm_attrs[] = {
1278 +       &dev_attr_battery_present,
1279 +       &dev_attr_battery_level,
1280 +       &dev_attr_battery_current,
1281 +       &dev_attr_battery_capacity,
1282 +       &dev_attr_battery_temp,
1283 +       &dev_attr_backup_battery_voltage,
1284 +       &dev_attr_charger_present,
1285 +       &dev_attr_charger_state,
1286 +       &dev_attr_charger_verbose,
1287 +       &dev_attr_charger_voltage,
1288 +       &dev_attr_charger_enable,
1289 +       &dev_attr_charger_pwm,
1290 +};
1291 +
1292 +static void n810bm_notify_work(struct work_struct *work)
1293 +{
1294 +       struct n810bm *bm = container_of(work, struct n810bm, notify_work);
1295 +       unsigned long notify_flags;
1296 +
1297 +       notify_flags = xchg(&bm->notify_flags, 0);
1298 +       mb();
1299 +
1300 +#define do_notify(attr_name)                                           \
1301 +       do {                                                            \
1302 +               if (notify_flags & (1 << N810BM_NOTIFY_##attr_name)) {  \
1303 +                       sysfs_notify(&bm->pdev->dev.kobj, NULL,         \
1304 +                                    dev_attr_##attr_name.attr.name);   \
1305 +               }                                                       \
1306 +       } while (0)
1307 +
1308 +       do_notify(charger_present);
1309 +       do_notify(charger_state);
1310 +       do_notify(charger_pwm);
1311 +}
1312 +
1313 +static int n810bm_charger_set_current_pwm(struct lipocharge *c,
1314 +                                         unsigned int duty_cycle)
1315 +{
1316 +       struct n810bm *bm = container_of(c, struct n810bm, charger);
1317 +       int err = -EINVAL;
1318 +
1319 +       WARN_ON(!mutex_is_locked(&bm->mutex));
1320 +       if (WARN_ON(duty_cycle > 0xFF))
1321 +               goto out;
1322 +       if (WARN_ON(!bm->charger_enabled))
1323 +               goto out;
1324 +       if (WARN_ON(!bm->battery_present || !bm->charger_present))
1325 +               goto out;
1326 +
1327 +       if (duty_cycle != bm->active_current_pwm) {
1328 +               bm->active_current_pwm = duty_cycle;
1329 +               tahvo_write(bm, TAHVO_REG_CHGCURR, duty_cycle);
1330 +               n810bm_notify_charger_pwm(bm);
1331 +       }
1332 +
1333 +       err = 0;
1334 +out:
1335 +
1336 +       return err;
1337 +}
1338 +
1339 +static void n810bm_charger_emergency(struct lipocharge *c)
1340 +{
1341 +       struct n810bm *bm = container_of(c, struct n810bm, charger);
1342 +
1343 +       n810bm_emergency(bm, "Battery charger fault");
1344 +}
1345 +
1346 +static void n810bm_hw_exit(struct n810bm *bm)
1347 +{
1348 +       n810bm_stop_charge(bm);
1349 +       retu_write(bm, RETU_REG_ADCSCR, 0);
1350 +}
1351 +
1352 +static int n810bm_hw_init(struct n810bm *bm)
1353 +{
1354 +       int err;
1355 +
1356 +       err = n810bm_check_adc_sanity(bm);
1357 +       if (err)
1358 +               return err;
1359 +
1360 +       n810bm_stop_charge(bm);
1361 +
1362 +       return 0;
1363 +}
1364 +
1365 +static void n810bm_cancel_and_flush_work(struct n810bm *bm)
1366 +{
1367 +       cancel_delayed_work_sync(&bm->periodic_check_work);
1368 +       cancel_work_sync(&bm->notify_work);
1369 +       flush_scheduled_work();
1370 +}
1371 +
1372 +static int n810bm_device_init(struct n810bm *bm)
1373 +{
1374 +       int attr_index;
1375 +       int err;
1376 +
1377 +       bm->charger.rate = LIPORATE_p6C;
1378 +       bm->charger.top_voltage = 4100;
1379 +       bm->charger.duty_cycle_max = 0xFF;
1380 +       bm->charger.set_current_pwm = n810bm_charger_set_current_pwm;
1381 +       bm->charger.emergency = n810bm_charger_emergency;
1382 +       lipocharge_init(&bm->charger, &bm->pdev->dev);
1383 +
1384 +       err = n810bm_hw_init(bm);
1385 +       if (err)
1386 +               goto error;
1387 +       for (attr_index = 0; attr_index < ARRAY_SIZE(n810bm_attrs); attr_index++) {
1388 +               err = device_create_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1389 +               if (err)
1390 +                       goto err_unwind_attrs;
1391 +       }
1392 +/*XXX
1393 +       err = retu_request_irq(RETU_INT_ADCS,
1394 +                              n810bm_adc_irq_handler,
1395 +                              (unsigned long)bm, "n810bm");
1396 +       if (err)
1397 +               goto err_unwind_attrs;
1398 +*/
1399 +       bm->tahvo_irq = platform_get_irq(n810bm_tahvo_device, 0);
1400 +       err = request_threaded_irq(bm->tahvo_irq, NULL,
1401 +                                  n810bm_tahvo_current_measure_irq_handler,
1402 +                                  IRQF_ONESHOT, "tahvo-n810bm", bm);
1403 +       if (err)
1404 +               goto err_free_retu_irq;
1405 +       disable_irq_nosync(bm->tahvo_irq);
1406 +       bm->tahvo_irq_enabled = 0;
1407 +
1408 +       schedule_delayed_work(&bm->periodic_check_work,
1409 +                             round_jiffies_relative(N810BM_CHECK_INTERVAL));
1410 +
1411 +       bm->initialized = 1;
1412 +       dev_info(&bm->pdev->dev, "Battery management initialized");
1413 +
1414 +       return 0;
1415 +
1416 +err_free_retu_irq:
1417 +//XXX  retu_free_irq(RETU_INT_ADCS);
1418 +err_unwind_attrs:
1419 +       for (attr_index--; attr_index >= 0; attr_index--)
1420 +               device_remove_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1421 +/*err_exit:*/
1422 +       n810bm_hw_exit(bm);
1423 +error:
1424 +       n810bm_cancel_and_flush_work(bm);
1425 +
1426 +       return err;
1427 +}
1428 +
1429 +static void n810bm_device_exit(struct n810bm *bm)
1430 +{
1431 +       int i;
1432 +
1433 +       if (!bm->initialized)
1434 +               return;
1435 +
1436 +       lipocharge_exit(&bm->charger);
1437 +       free_irq(bm->tahvo_irq, bm);
1438 +//XXX  retu_free_irq(RETU_INT_ADCS);
1439 +       for (i = 0; i < ARRAY_SIZE(n810bm_attrs); i++)
1440 +               device_remove_file(&bm->pdev->dev, n810bm_attrs[i]);
1441 +
1442 +       n810bm_cancel_and_flush_work(bm);
1443 +
1444 +       n810bm_hw_exit(bm);
1445 +
1446 +       bm->initialized = 0;
1447 +}
1448 +
1449 +static void n810bm_pmm_block_found(const struct firmware *fw, void *context)
1450 +{
1451 +       struct n810bm *bm = context;
1452 +       int err;
1453 +
1454 +       if (!fw) {
1455 +               dev_err(&bm->pdev->dev,
1456 +                       "CAL PMM block image file not found");
1457 +               goto err_release;
1458 +       }
1459 +       if (fw->size != N810BM_PMM_BLOCK_SIZE ||
1460 +           memcmp(fw->data, "BME-PMM-BLOCK01", 15) != 0) {
1461 +               dev_err(&bm->pdev->dev,
1462 +                       "CAL PMM block image file has an invalid format");
1463 +               goto err_release;
1464 +       }
1465 +
1466 +       err = n810bm_parse_pmm_block(bm, fw);
1467 +       if (err)
1468 +               goto err_release;
1469 +       release_firmware(fw);
1470 +
1471 +       err = n810bm_device_init(bm);
1472 +       if (err) {
1473 +               dev_err(&bm->pdev->dev,
1474 +                       "Failed to initialized battery management (%d)", err);
1475 +               goto error;
1476 +       }
1477 +
1478 +       return;
1479 +err_release:
1480 +       release_firmware(fw);
1481 +error:
1482 +       return;
1483 +}
1484 +
1485 +static int __devinit n810bm_probe(void)
1486 +{
1487 +       struct n810bm *bm;
1488 +       int err;
1489 +
1490 +       if (!n810bm_retu_device || !n810bm_tahvo_device)
1491 +               return 0;
1492 +
1493 +       bm = kzalloc(sizeof(*bm), GFP_KERNEL);
1494 +       if (!bm)
1495 +               return -ENOMEM;
1496 +       bm->pdev = n810bm_retu_device;
1497 +       platform_set_drvdata(n810bm_retu_device, bm);
1498 +       platform_set_drvdata(n810bm_tahvo_device, bm);
1499 +       mutex_init(&bm->mutex);
1500 +       INIT_DELAYED_WORK(&bm->periodic_check_work, n810bm_periodic_check_work);
1501 +       INIT_WORK(&bm->notify_work, n810bm_notify_work);
1502 +
1503 +       dev_info(&bm->pdev->dev, "Requesting CAL BME PMM block firmware file "
1504 +                N810BM_PMM_BLOCK_FILENAME);
1505 +       err = request_firmware_nowait(THIS_MODULE, 1,
1506 +                                     N810BM_PMM_BLOCK_FILENAME,
1507 +                                     &bm->pdev->dev, GFP_KERNEL,
1508 +                                     bm, n810bm_pmm_block_found);
1509 +       if (err) {
1510 +               dev_err(&bm->pdev->dev,
1511 +                       "Failed to request CAL PMM block image file (%d)", err);
1512 +               goto err_free;
1513 +       }
1514 +
1515 +       return 0;
1516 +
1517 +err_free:
1518 +       kfree(bm);
1519 +
1520 +       return err;
1521 +}
1522 +
1523 +static void __devexit n810bm_remove(void)
1524 +{
1525 +       struct n810bm *bm;
1526 +
1527 +       if (!n810bm_retu_device || !n810bm_tahvo_device)
1528 +               return;
1529 +       bm = platform_get_drvdata(n810bm_retu_device);
1530 +
1531 +       n810bm_device_exit(bm);
1532 +
1533 +       kfree(bm);
1534 +       platform_set_drvdata(n810bm_retu_device, NULL);
1535 +       platform_set_drvdata(n810bm_tahvo_device, NULL);
1536 +}
1537 +
1538 +static int __devinit n810bm_retu_probe(struct platform_device *pdev)
1539 +{
1540 +       n810bm_retu_device = pdev;
1541 +       return n810bm_probe();
1542 +}
1543 +
1544 +static int __devexit n810bm_retu_remove(struct platform_device *pdev)
1545 +{
1546 +       n810bm_remove();
1547 +       n810bm_retu_device = NULL;
1548 +       return 0;
1549 +}
1550 +
1551 +static int __devinit n810bm_tahvo_probe(struct platform_device *pdev)
1552 +{
1553 +       n810bm_tahvo_device = pdev;
1554 +       return n810bm_probe();
1555 +}
1556 +
1557 +static int __devexit n810bm_tahvo_remove(struct platform_device *pdev)
1558 +{
1559 +       n810bm_remove();
1560 +       n810bm_tahvo_device = NULL;
1561 +       return 0;
1562 +}
1563 +
1564 +static struct platform_driver n810bm_retu_driver = {
1565 +       .remove         = __devexit_p(n810bm_retu_remove),
1566 +       .driver         = {
1567 +               .name   = "retu-n810bm",
1568 +       }
1569 +};
1570 +
1571 +static struct platform_driver n810bm_tahvo_driver = {
1572 +       .remove         = __devexit_p(n810bm_tahvo_remove),
1573 +       .driver         = {
1574 +               .name   = "tahvo-n810bm",
1575 +       }
1576 +};
1577 +
1578 +static int __init n810bm_modinit(void)
1579 +{
1580 +       int err;
1581 +
1582 +       err = platform_driver_probe(&n810bm_retu_driver, n810bm_retu_probe);
1583 +       if (err)
1584 +               return err;
1585 +       err = platform_driver_probe(&n810bm_tahvo_driver, n810bm_tahvo_probe);
1586 +       if (err) {
1587 +               platform_driver_unregister(&n810bm_retu_driver);
1588 +               return err;
1589 +       }
1590 +
1591 +       return 0;
1592 +}
1593 +module_init(n810bm_modinit);
1594 +
1595 +static void __exit n810bm_modexit(void)
1596 +{
1597 +       platform_driver_unregister(&n810bm_tahvo_driver);
1598 +       platform_driver_unregister(&n810bm_retu_driver);
1599 +}
1600 +module_exit(n810bm_modexit);
1601 +
1602 +MODULE_DESCRIPTION("Nokia n810 battery management");
1603 +MODULE_FIRMWARE(N810BM_PMM_BLOCK_FILENAME);
1604 +MODULE_LICENSE("GPL");
1605 +MODULE_AUTHOR("Michael Buesch");
1606 Index: linux-3.1/drivers/cbus/lipocharge.c
1607 ===================================================================
1608 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
1609 +++ linux-3.1/drivers/cbus/lipocharge.c 2011-11-05 17:06:51.589348749 +0100
1610 @@ -0,0 +1,183 @@
1611 +/*
1612 + *   Generic LIPO battery charger
1613 + *
1614 + *   Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
1615 + *
1616 + *   This program is free software; you can redistribute it and/or
1617 + *   modify it under the terms of the GNU General Public License
1618 + *   as published by the Free Software Foundation; either version 2
1619 + *   of the License, or (at your option) any later version.
1620 + *
1621 + *   This program is distributed in the hope that it will be useful,
1622 + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
1623 + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1624 + *   GNU General Public License for more details.
1625 + */
1626 +
1627 +#define DEBUG
1628 +
1629 +#include "lipocharge.h"
1630 +
1631 +#include <linux/slab.h>
1632 +
1633 +
1634 +/* Hysteresis constants */
1635 +#define CURRENT_HYST           30 /* mA */
1636 +#define VOLTAGE_HYST           10 /* mV */
1637 +
1638 +/* Threshold constants */
1639 +#define FINISH_CURRENT_PERCENT 3
1640 +
1641 +
1642 +/* Returns the requested first-stage charge current in mA */
1643 +static inline unsigned int get_stage1_charge_current(struct lipocharge *c)
1644 +{
1645 +       /* current = (capacity * C) */
1646 +       return c->capacity * c->rate / 1000;
1647 +}
1648 +
1649 +void lipocharge_init(struct lipocharge *c, struct device *dev)
1650 +{
1651 +       c->dev = dev;
1652 +       c->state = LIPO_IDLE;
1653 +}
1654 +
1655 +void lipocharge_exit(struct lipocharge *c)
1656 +{
1657 +       c->state = LIPO_IDLE;
1658 +}
1659 +
1660 +int lipocharge_start(struct lipocharge *c)
1661 +{
1662 +       int err;
1663 +
1664 +       if (c->state != LIPO_IDLE)
1665 +               return -EBUSY;
1666 +       if (!c->set_current_pwm || !c->emergency)
1667 +               return -EINVAL;
1668 +       if (!c->top_voltage || c->top_voltage > 4200)
1669 +               return -EINVAL;
1670 +
1671 +       c->active_duty_cycle = 0;
1672 +       err = c->set_current_pwm(c, c->active_duty_cycle);
1673 +       if (err)
1674 +               return err;
1675 +       c->state = LIPO_FIRST_STAGE;
1676 +
1677 +       return 0;
1678 +}
1679 +
1680 +void lipocharge_stop(struct lipocharge *c)
1681 +{
1682 +       if (c->state == LIPO_IDLE)
1683 +               return;
1684 +       c->state = LIPO_IDLE;
1685 +}
1686 +
1687 +static int lipocharge_increase_current(struct lipocharge *c,
1688 +                                      unsigned int inc_permille)
1689 +{
1690 +       int old_pwm, new_pwm;
1691 +
1692 +       if (c->active_duty_cycle >= c->duty_cycle_max)
1693 +               return 0;
1694 +
1695 +       old_pwm = c->active_duty_cycle;
1696 +       new_pwm = old_pwm + (c->duty_cycle_max * inc_permille / 1000);
1697 +       new_pwm = min(new_pwm, (int)c->duty_cycle_max);
1698 +       c->active_duty_cycle = new_pwm;
1699 +
1700 +       dev_dbg(c->dev, "lipo: Increasing duty_cycle by "
1701 +               "%u permille (0x%02X -> 0x%02X)",
1702 +               inc_permille, old_pwm, new_pwm);
1703 +
1704 +       return c->set_current_pwm(c, c->active_duty_cycle);
1705 +}
1706 +
1707 +static int lipocharge_decrease_current(struct lipocharge *c,
1708 +                                      unsigned int dec_permille)
1709 +{
1710 +       int old_pwm, new_pwm;
1711 +
1712 +       if (c->active_duty_cycle <= 0)
1713 +               return 0;
1714 +
1715 +       old_pwm = c->active_duty_cycle;
1716 +       new_pwm = old_pwm - (c->duty_cycle_max * dec_permille / 1000);
1717 +       new_pwm = max(0, new_pwm);
1718 +       c->active_duty_cycle = new_pwm;
1719 +
1720 +       dev_dbg(c->dev, "lipo: Decreasing duty_cycle by "
1721 +               "%u permille (0x%02X -> 0x%02X)",
1722 +               dec_permille, old_pwm, new_pwm);
1723 +
1724 +       return c->set_current_pwm(c, c->active_duty_cycle);
1725 +}
1726 +
1727 +/** lipocharge_update_state - Update the charge state
1728 + * @c: The context.
1729 + * @voltage_mV: The measured battery voltage.
1730 + * @current_mA: The measured charge current.
1731 + *             negative -> drain.
1732 + *             positive -> charge.
1733 + * @temp_K: Battery temperature in K.
1734 + *
1735 + * Returns 0 on success, -1 on error.
1736 + * Returns 1, if the charging process is finished.
1737 + */
1738 +int lipocharge_update_state(struct lipocharge *c,
1739 +                           unsigned int voltage_mV,
1740 +                           int current_mA,
1741 +                           unsigned int temp_K)
1742 +{
1743 +       int requested_current, current_diff;
1744 +       int err;
1745 +       unsigned int permille;
1746 +
1747 +       //TODO temp
1748 +
1749 +restart:
1750 +       switch (c->state) {
1751 +       case LIPO_IDLE:
1752 +               dev_err(c->dev, "%s: called while idle", __func__);
1753 +               return -EINVAL;
1754 +       case LIPO_FIRST_STAGE:  /* Constant current */
1755 +//printk("GOT %u %d %u\n", voltage_mV, current_mA, temp_K);
1756 +               if (voltage_mV >= c->top_voltage) {
1757 +                       /* Float voltage reached.
1758 +                        * Switch charger mode to "constant current" */
1759 +                       c->state = LIPO_SECOND_STAGE;
1760 +                       dev_dbg(c->dev, "Switched to second charging stage.");
1761 +                       goto restart;
1762 +               }
1763 +               /* Float voltage not reached, yet.
1764 +                * Try to get the requested constant current. */
1765 +               requested_current = get_stage1_charge_current(c);
1766 +               if (current_mA < 0)
1767 +                       current_mA = 0;
1768 +               current_diff = requested_current - current_mA;
1769 +               if (abs(requested_current - current_mA) > CURRENT_HYST) {
1770 +                       if (current_diff > 0) {
1771 +                               /* Increase current */
1772 +                               permille = current_diff * 1000 / requested_current;
1773 +                               permille /= 2;
1774 +                               err = lipocharge_increase_current(c, permille);
1775 +                               if (err)
1776 +                                       return err;
1777 +                       } else {
1778 +                               /* Decrease current */
1779 +                               permille = (-current_diff) * 1000 / requested_current;
1780 +                               permille /= 2;
1781 +                               err = lipocharge_decrease_current(c, permille);
1782 +                               if (err)
1783 +                                       return err;
1784 +                       }
1785 +               }
1786 +               break;
1787 +       case LIPO_SECOND_STAGE: /* Constant voltage */
1788 +               //TODO
1789 +               break;
1790 +       }
1791 +
1792 +       return 0;
1793 +}
1794 Index: linux-3.1/drivers/cbus/lipocharge.h
1795 ===================================================================
1796 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
1797 +++ linux-3.1/drivers/cbus/lipocharge.h 2011-11-05 17:06:51.589348749 +0100
1798 @@ -0,0 +1,60 @@
1799 +#ifndef LIPOCHARGE_H_
1800 +#define LIPOCHARGE_H_
1801 +
1802 +#include <linux/types.h>
1803 +#include <linux/device.h>
1804 +
1805 +
1806 +#define LIPORATE(a,b)  (((a) * 1000) + ((b) * 100))
1807 +#define LIPORATE_p6C   LIPORATE(0,6)   /* 0.6C */
1808 +
1809 +enum lipocharge_state {
1810 +       LIPO_IDLE = 0,          /* Not charging */
1811 +       LIPO_FIRST_STAGE,       /* Charging: constant current */
1812 +       LIPO_SECOND_STAGE,      /* Charging: constant voltage */
1813 +};
1814 +
1815 +/** struct lipocharge - A generic LIPO charger
1816 + *
1817 + * @capacity: Battery capacity in mAh.
1818 + * @rate: Charge rate.
1819 + * @top_voltage: Fully charged voltage, in mV.
1820 + * @duty_cycle_max: Max value for duty_cycle.
1821 + *
1822 + * @set_charge_current: Set the charge current PWM duty cycle.
1823 + * @emergency: Something went wrong. Force shutdown.
1824 + */
1825 +struct lipocharge {
1826 +       unsigned int capacity;
1827 +       unsigned int rate;
1828 +       unsigned int top_voltage;
1829 +       unsigned int duty_cycle_max;
1830 +
1831 +       int (*set_current_pwm)(struct lipocharge *c, unsigned int duty_cycle);
1832 +       void (*emergency)(struct lipocharge *c);
1833 +
1834 +       /* internal */
1835 +       struct device *dev;
1836 +       enum lipocharge_state state;
1837 +       unsigned int active_duty_cycle;
1838 +
1839 +       //TODO implement timer to cut power after maximum charge time.
1840 +};
1841 +
1842 +void lipocharge_init(struct lipocharge *c, struct device *dev);
1843 +void lipocharge_exit(struct lipocharge *c);
1844 +
1845 +int lipocharge_start(struct lipocharge *c);
1846 +void lipocharge_stop(struct lipocharge *c);
1847 +
1848 +int lipocharge_update_state(struct lipocharge *c,
1849 +                           unsigned int voltage_mV,
1850 +                           int current_mA,
1851 +                           unsigned int temp_K);
1852 +
1853 +static inline bool lipocharge_is_charging(struct lipocharge *c)
1854 +{
1855 +       return (c->state != LIPO_IDLE);
1856 +}
1857 +
1858 +#endif /* LIPOCHARGE_H_ */
1859 Index: linux-3.1/drivers/cbus/cbus.c
1860 ===================================================================
1861 --- linux-3.1.orig/drivers/cbus/cbus.c  2011-11-05 17:03:39.610846102 +0100
1862 +++ linux-3.1/drivers/cbus/cbus.c       2011-11-05 17:06:51.589348749 +0100
1863 @@ -34,6 +34,7 @@
1864  #include <linux/gpio.h>
1865  #include <linux/platform_device.h>
1866  #include <linux/platform_data/cbus.h>
1867 +#include <linux/reboot.h>
1868  
1869  #include "cbus.h"
1870  
1871 @@ -324,6 +325,13 @@ static void __exit cbus_bus_exit(void)
1872  }
1873  module_exit(cbus_bus_exit);
1874  
1875 +void cbus_emergency(void)
1876 +{
1877 +       machine_power_off();
1878 +       panic("cbus: Failed to halt machine in emergency state\n");
1879 +}
1880 +EXPORT_SYMBOL(cbus_emergency);
1881 +
1882  MODULE_DESCRIPTION("CBUS serial protocol");
1883  MODULE_LICENSE("GPL");
1884  MODULE_AUTHOR("Juha Yrjölä");
1885 Index: linux-3.1/drivers/cbus/cbus.h
1886 ===================================================================
1887 --- linux-3.1.orig/drivers/cbus/cbus.h  2011-11-05 17:03:39.590846131 +0100
1888 +++ linux-3.1/drivers/cbus/cbus.h       2011-11-05 17:06:51.589348749 +0100
1889 @@ -27,4 +27,6 @@ extern int cbus_read_reg(struct device *
1890  extern int cbus_write_reg(struct device *, unsigned dev, unsigned reg,
1891                 unsigned val);
1892  
1893 +NORET_TYPE void cbus_emergency(void) ATTRIB_NORET;
1894 +
1895  #endif /* __DRIVERS_CBUS_CBUS_H */
1896 Index: linux-3.1/drivers/cbus/retu.c
1897 ===================================================================
1898 --- linux-3.1.orig/drivers/cbus/retu.c  2011-11-05 17:03:39.606846106 +0100
1899 +++ linux-3.1/drivers/cbus/retu.c       2011-11-05 17:06:51.589348749 +0100
1900 @@ -417,6 +417,11 @@ static int retu_allocate_children(struct
1901         if (!child)
1902                 return -ENOMEM;
1903  
1904 +       child = retu_allocate_child("retu-n810bm", parent, irq_base,
1905 +                       RETU_INT_ADCS, -1, 1);
1906 +       if (!child)
1907 +               return -ENOMEM;
1908 +
1909         return 0;
1910  }
1911  
1912 Index: linux-3.1/drivers/cbus/tahvo.c
1913 ===================================================================
1914 --- linux-3.1.orig/drivers/cbus/tahvo.c 2011-11-05 17:04:36.274768324 +0100
1915 +++ linux-3.1/drivers/cbus/tahvo.c      2011-11-05 17:06:51.589348749 +0100
1916 @@ -131,6 +131,7 @@ void tahvo_set_clear_reg_bits(struct dev
1917         __tahvo_write_reg(tahvo, reg, w);
1918         mutex_unlock(&tahvo->mutex);
1919  }
1920 +EXPORT_SYMBOL(tahvo_set_clear_reg_bits);
1921  
1922  static irqreturn_t tahvo_irq_handler(int irq, void *_tahvo)
1923  {
1924 @@ -307,6 +308,11 @@ static int tahvo_allocate_children(struc
1925         if (!child)
1926                 return -ENOMEM;
1927  
1928 +       child = tahvo_allocate_child("tahvo-n810bm", parent,
1929 +                                    irq_base + TAHVO_INT_BATCURR);
1930 +       if (!child)
1931 +               return -ENOMEM;
1932 +
1933         return 0;
1934  }
1935