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