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