2f2061629a165e8b67c7c4793855ee67b58e7d11
[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/drivers/cbus/Kconfig
14 ===================================================================
15 --- linux-2.6.37.orig/drivers/cbus/Kconfig      2011-02-06 00:24:48.502005279 +0100
16 +++ linux-2.6.37/drivers/cbus/Kconfig   2011-02-06 00:24:48.550008091 +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/drivers/cbus/Makefile
31 ===================================================================
32 --- linux-2.6.37.orig/drivers/cbus/Makefile     2011-02-06 00:24:48.493004751 +0100
33 +++ linux-2.6.37/drivers/cbus/Makefile  2011-02-06 00:24:48.550008091 +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/drivers/cbus/n810bm_main.c
42 ===================================================================
43 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
44 +++ linux-2.6.37/drivers/cbus/n810bm_main.c     2011-02-06 13:36:49.581078785 +0100
45 @@ -0,0 +1,959 @@
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/spinlock.h>
73 +#include <linux/timer.h>
74 +#include <linux/reboot.h>
75 +#include <linux/firmware.h>
76 +
77 +#include "retu.h"
78 +#include "tahvo.h"
79 +#include "lipocharge.h"
80 +
81 +
82 +#define N810BM_PMM_BLOCK_FILENAME      "n810-cal-bme-pmm.fw"
83 +#define N810BM_PMM_BLOCK_SIZE          0x600
84 +
85 +#define N810BM_CHECK_INTERVAL          (HZ * 2)
86 +#define N810BM_MIN_VOLTAGE_THRES       3300 /* Absolute minimum voltage threshold */
87 +
88 +
89 +/* RETU_ADC_BSI
90 + * The battery size indicator ADC measures the resistance between
91 + * the battery BSI pin and ground. This is used to detect the battery
92 + * capacity, as the BSI resistor is related to capacity.
93 + *
94 + * Manually measured lookup table.
95 + * Hard to measure, thus not very accurate.
96 + *
97 + * Resistance  |  ADC value
98 + * ========================
99 + * 120k        |  0x3AC
100 + * 110k        |  0x37C
101 + * 100k        |  0x351
102 + *  90k        |  0x329
103 + */
104 +
105 +/* RETU_ADC_BATTVOLT
106 + * Manually measured lookup table.
107 + * Hard to measure, thus not very accurate.
108 + *
109 + * Voltage  |  ADC value
110 + * =====================
111 + * 2.80V    |  0x037
112 + * 2.90V    |  0x05E
113 + * 3.00V    |  0x090
114 + * 3.10V    |  0x0A4
115 + * 3.20V    |  0x0CC
116 + * 3.30V    |  0x0EF
117 + * 3.40V    |  0x115
118 + * 3.50V    |  0x136
119 + * 3.60V    |  0x15C
120 + * 3.70V    |  0x187
121 + * 3.80V    |  0x1A5
122 + * 3.90V    |  0x1C9
123 + * 4.00V    |  0x1ED
124 + * 4.10V    |  0x212
125 + * 4.20V    |  0x236
126 + */
127 +
128 +
129 +enum n810bm_capacity {
130 +       N810BM_CAP_UNKNOWN      = -1,
131 +       N810BM_CAP_NONE         = 0,
132 +       N810BM_CAP_1500MAH      = 1500, /* 1500 mAh battery */
133 +};
134 +
135 +struct n810bm {
136 +       bool battery_present;                   /* A battery is inserted */
137 +       bool charger_present;                   /* The charger is connected */
138 +       enum n810bm_capacity capacity;          /* The capacity of the inserted battery (if any) */
139 +
140 +       bool charger_enabled;                   /* Want to charge? */ //TODO
141 +       struct lipocharge charger;              /* Charger subsystem */
142 +
143 +       struct platform_device *pdev;
144 +       const struct firmware *pmm_block;       /* CAL PMM block */
145 +
146 +       struct timer_list check_timer;
147 +
148 +       bool initialized;                       /* The hardware was initialized */
149 +       spinlock_t lock;
150 +};
151 +
152 +
153 +static inline struct n810bm * device_to_n810bm(struct device *dev)
154 +{
155 +       struct platform_device *pdev = to_platform_device(dev);
156 +       struct n810bm *bm = platform_get_drvdata(pdev);
157 +
158 +       return bm;
159 +}
160 +
161 +static NORET_TYPE void n810bm_emergency(struct n810bm *bm, const char *message) ATTRIB_NORET;
162 +static void n810bm_emergency(struct n810bm *bm, const char *message)
163 +{
164 +       printk(KERN_EMERG "n810 battery management fatal fault: %s\n", message);
165 +       /* Force a hard shutdown. */
166 +       machine_power_off();
167 +       panic("n810bm: Failed to halt machine in emergency state\n");
168 +}
169 +
170 +static u16 tahvo_read(struct n810bm *bm, unsigned int reg)
171 +{
172 +       int ret;
173 +       unsigned long flags;
174 +
175 +       spin_lock_irqsave(&tahvo_lock, flags);
176 +       ret = tahvo_read_reg(reg);
177 +       spin_unlock_irqrestore(&tahvo_lock, flags);
178 +       if (ret < 0 || ret > 0xFFFF)
179 +               n810bm_emergency(bm, "tahvo_read");
180 +
181 +       return ret;
182 +}
183 +
184 +static void tahvo_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
185 +{
186 +       int ret;
187 +       unsigned long flags;
188 +       u16 value;
189 +
190 +       spin_lock_irqsave(&tahvo_lock, flags);
191 +       if (~mask) {
192 +               ret = tahvo_read_reg(reg);
193 +               if (ret < 0 || ret > 0xFFFF)
194 +                       goto fatal_unlock;
195 +               value = ret;
196 +       } else
197 +               value = 0;
198 +       value &= ~mask;
199 +       value |= set;
200 +       ret = tahvo_write_reg(reg, value);
201 +       if (ret)
202 +               goto fatal_unlock;
203 +       spin_unlock_irqrestore(&tahvo_lock, flags);
204 +
205 +       return;
206 +
207 +fatal_unlock:
208 +       spin_unlock_irqrestore(&tahvo_lock, flags);
209 +       n810bm_emergency(bm, "tahvo_maskset");
210 +}
211 +
212 +static inline void tahvo_write(struct n810bm *bm, unsigned int reg, u16 value)
213 +{
214 +       tahvo_maskset(bm, reg, 0xFFFF, value);
215 +}
216 +
217 +static inline void tahvo_set(struct n810bm *bm, unsigned int reg, u16 mask)
218 +{
219 +       tahvo_maskset(bm, reg, mask, mask);
220 +}
221 +
222 +static inline void tahvo_clear(struct n810bm *bm, unsigned int reg, u16 mask)
223 +{
224 +       tahvo_maskset(bm, reg, mask, 0);
225 +}
226 +
227 +static u16 retu_read(struct n810bm *bm, unsigned int reg)
228 +{
229 +       int ret;
230 +       unsigned long flags;
231 +
232 +       spin_lock_irqsave(&retu_lock, flags);
233 +       ret = retu_read_reg(reg);
234 +       spin_unlock_irqrestore(&retu_lock, flags);
235 +       if (ret < 0 || ret > 0xFFFF)
236 +               n810bm_emergency(bm, "retu_read");
237 +
238 +       return ret;
239 +}
240 +
241 +static void retu_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(&retu_lock, flags);
248 +       if (~mask) {
249 +               ret = retu_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 = retu_write_reg(reg, value);
258 +       if (ret)
259 +               goto fatal_unlock;
260 +       spin_unlock_irqrestore(&retu_lock, flags);
261 +
262 +       return;
263 +
264 +fatal_unlock:
265 +       spin_unlock_irqrestore(&retu_lock, flags);
266 +       n810bm_emergency(bm, "retu_maskset");
267 +}
268 +
269 +static inline void retu_write(struct n810bm *bm, unsigned int reg, u16 value)
270 +{
271 +       retu_maskset(bm, reg, 0xFFFF, value);
272 +}
273 +
274 +static int retu_adc_average(struct n810bm *bm, unsigned int chan,
275 +                           unsigned int nr_passes)
276 +{
277 +       unsigned int i, value = 0;
278 +       int ret;
279 +
280 +       if (WARN_ON(!nr_passes))
281 +               return 0;
282 +       for (i = 0; i < nr_passes; i++) {
283 +               ret = retu_read_adc(chan);
284 +               if (ret < 0)
285 +                       return ret;
286 +               value += ret;
287 +       }
288 +       value /= nr_passes;
289 +
290 +       return value;
291 +}
292 +
293 +/* Set the current measure timer that triggers on Tahvo IRQ 7 */
294 +static void n810bm_set_current_measure_timer(struct n810bm *bm,
295 +                                            u16 millisec_interval)
296 +{
297 +       u16 value = millisec_interval;
298 +
299 +       if (value <= 0xF905) {
300 +               value = ((u64)0x10624DD3 * (u64)(value + 0xF9)) >> 32;
301 +               value /= 16;
302 +       } else
303 +               value = 0xFF;
304 +
305 +       tahvo_write(bm, TAHVO_REG_BATCURRTIMER, value & 0xFF);
306 +
307 +       tahvo_set(bm, TAHVO_REG_CHGCTL,
308 +                 TAHVO_REG_CHGCTL_CURTIMRST);
309 +       tahvo_clear(bm, TAHVO_REG_CHGCTL,
310 +                   TAHVO_REG_CHGCTL_CURTIMRST);
311 +}
312 +
313 +static void n810bm_enable_current_measure(struct n810bm *bm, bool slow, bool irq)
314 +{
315 +       u16 millisec_interval;
316 +
317 +       if (slow)
318 +               millisec_interval = 1000;
319 +       else
320 +               millisec_interval = 250;
321 +
322 +       /* Enable the current measurement circuitry */
323 +       tahvo_set(bm, TAHVO_REG_CHGCTL,
324 +                 TAHVO_REG_CHGCTL_CURMEAS);
325 +
326 +       /* Setup the measurement timer */
327 +       n810bm_set_current_measure_timer(bm, millisec_interval);
328 +       if (irq)
329 +               tahvo_enable_irq(TAHVO_INT_BATCURR);
330 +
331 +       //TODO also do a software timer for safety.
332 +}
333 +
334 +static void n810bm_disable_current_measure(struct n810bm *bm)
335 +{
336 +       /* Disable the measurement timer */
337 +       n810bm_set_current_measure_timer(bm, 0);
338 +       tahvo_disable_irq(TAHVO_INT_BATCURR);
339 +
340 +       /* Disable the current measurement circuitry */
341 +       tahvo_clear(bm, TAHVO_REG_CHGCTL,
342 +                   TAHVO_REG_CHGCTL_CURMEAS);
343 +}
344 +
345 +/* Measure the actual battery current. Returns a signed value in mA.
346 + * Does only work, if current measurement was enabled. */
347 +static int n810bm_measure_batt_current(struct n810bm *bm)
348 +{
349 +       u16 retval;
350 +       int adc = 0, ma, i;
351 +
352 +       for (i = 0; i < 3; i++) {
353 +               retval = tahvo_read(bm, TAHVO_REG_BATCURR);
354 +               adc += (s16)retval; /* Value is signed */
355 +       }
356 +       adc /= 3;
357 +
358 +       //TODO convert to mA
359 +       ma = adc;
360 +
361 +       return ma;
362 +}
363 +
364 +static int adc_sanity_check(struct n810bm *bm, unsigned int channel)
365 +{
366 +       int value;
367 +
368 +       value = retu_read_adc(channel);
369 +       if (value < 0) {
370 +               dev_err(&bm->pdev->dev, "Failed to read GND ADC channel %u",
371 +                       channel);
372 +               return -EIO;
373 +       }
374 +       dev_dbg(&bm->pdev->dev,
375 +               "GND ADC channel %u sanity check got value: %d",
376 +               channel, value);
377 +       if (value > 5) {
378 +               n810bm_emergency(bm, "GND ADC sanity check failed");
379 +               return -EIO;
380 +       }
381 +
382 +       return 0;
383 +}
384 +
385 +static int n810bm_check_adc_sanity(struct n810bm *bm)
386 +{
387 +       int err;
388 +
389 +       /* Discard one conversion */
390 +       retu_write(bm, RETU_REG_ADCSCR, 0);
391 +       retu_read_adc(RETU_ADC_GND2);
392 +
393 +       err = adc_sanity_check(bm, RETU_ADC_GND2);
394 +       if (err)
395 +               return err;
396 +
397 +       return 0;
398 +}
399 +
400 +/* Measure the battery voltage. Returns the value in mV (or negative value on error). */
401 +static int n810bm_measure_batt_voltage(struct n810bm *bm)
402 +{
403 +       int adc;
404 +       unsigned int mv;
405 +       const unsigned int scale = 1000;
406 +
407 +       adc = retu_adc_average(bm, RETU_ADC_BATTVOLT, 5);
408 +       if (adc < 0)
409 +               return adc;
410 +       if (adc <= 0x37)
411 +               return 2800;
412 +       mv = 2800 + ((adc - 0x37) * (((4200 - 2800) * scale) / (0x236 - 0x37))) / scale;
413 +
414 +       return mv;
415 +}
416 +
417 +/* Measure the charger voltage. Returns the value in mV (or negative value on error). */
418 +static int n810bm_measure_charger_voltage(struct n810bm *bm)
419 +{
420 +       int adc;
421 +       unsigned int mv;
422 +
423 +       adc = retu_adc_average(bm, RETU_ADC_CHGVOLT, 5);
424 +       if (adc < 0)
425 +               return adc;
426 +       //TODO convert to mV
427 +       mv = adc;
428 +
429 +       return mv;
430 +}
431 +
432 +/* Measure backup battery voltage. Returns the value in mV (or negative value on error). */
433 +static int n810bm_measure_backup_batt_voltage(struct n810bm *bm)
434 +{
435 +       int adc;
436 +       unsigned int mv;
437 +
438 +       adc = retu_adc_average(bm, RETU_ADC_BKUPVOLT, 3);
439 +       if (adc < 0)
440 +               return adc;
441 +       //TODO convert to mV
442 +       mv = adc;
443 +
444 +       return mv;
445 +}
446 +
447 +/* Measure the battery temperature. Returns the value in K (or negative value on error). */
448 +static int n810bm_measure_batt_temp(struct n810bm *bm)
449 +{
450 +       int adc;
451 +       unsigned int k;
452 +
453 +       adc = retu_adc_average(bm, RETU_ADC_BATTEMP, 3);
454 +       if (adc < 0)
455 +               return adc;
456 +       //TODO convert to K
457 +       k = adc;
458 +
459 +       return k;
460 +}
461 +
462 +/* Read the battery capacity via BSI pin. */
463 +static enum n810bm_capacity n810bm_read_batt_capacity(struct n810bm *bm)
464 +{
465 +       int adc;
466 +       const unsigned int hyst = 20;
467 +
468 +       adc = retu_adc_average(bm, RETU_ADC_BSI, 5);
469 +       if (adc < 0) {
470 +               dev_err(&bm->pdev->dev, "Failed to read BSI ADC");
471 +               return N810BM_CAP_UNKNOWN;
472 +       }
473 +
474 +       if (adc >= 0x3B5 - hyst && adc <= 0x3B5 + hyst)
475 +               return N810BM_CAP_1500MAH;
476 +
477 +       dev_err(&bm->pdev->dev, "Capacity indicator 0x%X unknown", adc);
478 +
479 +       return N810BM_CAP_UNKNOWN;
480 +}
481 +
482 +/* Convert a battery voltage (in mV) to percentage. */
483 +static unsigned int n810bm_mvolt2percent(unsigned int mv)
484 +{
485 +       const unsigned int minv = 3700;
486 +       const unsigned int maxv = 4150;
487 +       unsigned int percent;
488 +
489 +       mv = clamp(mv, minv, maxv);
490 +       percent = (mv - minv) * 100 / (maxv - minv);
491 +
492 +       return percent;
493 +}
494 +
495 +/* Periodic check */
496 +static void n810bm_check_timer(unsigned long data)
497 +{
498 +       struct n810bm *bm = (struct n810bm *)data;
499 +       unsigned long flags;
500 +       u16 status;
501 +       bool battery_was_present, charger_was_present;
502 +       bool force_charge = 0;
503 +       int mv;
504 +
505 +       spin_lock_irqsave(&bm->lock, flags);
506 +
507 +       status = retu_read(bm, RETU_REG_STATUS);
508 +       battery_was_present = bm->battery_present;
509 +       charger_was_present = bm->charger_present;
510 +       bm->battery_present = !!(status & RETU_REG_STATUS_BATAVAIL);
511 +       bm->charger_present = !!(status & RETU_REG_STATUS_CHGPLUG);
512 +
513 +       if (bm->battery_present != battery_was_present) {
514 +               /* Battery state changed */
515 +               if (bm->battery_present) {
516 +                       bm->capacity = n810bm_read_batt_capacity(bm);
517 +                       if (bm->capacity == N810BM_CAP_UNKNOWN) {
518 +                               dev_err(&bm->pdev->dev, "Unknown battery detected");
519 +                       } else {
520 +                               dev_info(&bm->pdev->dev, "Detected %u mAh battery",
521 +                                        (unsigned int)bm->capacity);
522 +                       }
523 +               } else {
524 +                       bm->capacity = N810BM_CAP_NONE;
525 +                       dev_info(&bm->pdev->dev, "The main battery was removed");
526 +                       //TODO what do if charging?
527 +               }
528 +       }
529 +
530 +       if (bm->charger_present != charger_was_present) {
531 +               /* Charger state changed */
532 +               dev_info(&bm->pdev->dev, "The charger was %s",
533 +                        bm->charger_present ? "plugged in" : "removed");
534 +       }
535 +
536 +       if (bm->battery_present && !lipocharge_is_charging(&bm->charger)) {
537 +               mv = n810bm_measure_batt_voltage(bm);
538 +               if (mv < 0)
539 +                       n810bm_emergency(bm, "check timer: Failed to measure");
540 +               if (!bm->charger_present) {
541 +                       if (mv < N810BM_MIN_VOLTAGE_THRES) {
542 +                               n810bm_emergency(bm, "check timer: "
543 +                                       "Minimum voltage threshold reached");
544 +                       }
545 +                       force_charge = 1;
546 +               }
547 +       }
548 +
549 +       if (bm->charger_present && bm->battery_present) {
550 +               if (bm->charger_enabled || force_charge) {
551 +                       if (!lipocharge_is_charging(&bm->charger)) {
552 +                               //TODO start charging, if battery is below some threshold
553 +                       }
554 +               }
555 +       }
556 +
557 +       mod_timer(&bm->check_timer, round_jiffies(jiffies + N810BM_CHECK_INTERVAL));
558 +       spin_unlock_irqrestore(&bm->lock, flags);
559 +
560 +       return;
561 +}
562 +
563 +static void n810bm_adc_irq_handler(unsigned long data)
564 +{
565 +       struct n810bm *bm = (struct n810bm *)data;
566 +
567 +       retu_ack_irq(RETU_INT_ADCS);
568 +       //TODO
569 +dev_dbg(&bm->pdev->dev, "ADC interrupt triggered\n");
570 +}
571 +
572 +static void n810bm_tahvo_current_measure_irq_handler(unsigned long data)
573 +{
574 +       struct n810bm *bm = (struct n810bm *)data;
575 +
576 +       tahvo_ack_irq(TAHVO_INT_BATCURR);
577 +       //TODO
578 +dev_dbg(&bm->pdev->dev, "Tahvo measure IRQ triggered\n");
579 +}
580 +
581 +#define DEFINE_SHOW_INT_FUNC(name, member)                                     \
582 +       static ssize_t n810bm_##name##_show(struct device *dev,                 \
583 +                                           struct device_attribute *attr,      \
584 +                                           char *buf)                          \
585 +       {                                                                       \
586 +               struct n810bm *bm = device_to_n810bm(dev);                      \
587 +               ssize_t count;                                                  \
588 +                                                                               \
589 +               spin_lock_irq(&bm->lock);                                       \
590 +               count = snprintf(buf, PAGE_SIZE, "%d\n", (int)(bm->member));    \
591 +               spin_unlock_irq(&bm->lock);                                     \
592 +                                                                               \
593 +               return count;                                                   \
594 +       }
595 +
596 +#define DEFINE_STORE_INT_FUNC(name, member)                                    \
597 +       static ssize_t n810bm_##name##_store(struct device *dev,                \
598 +                                            struct device_attribute *attr,     \
599 +                                            const char *buf, size_t count)     \
600 +       {                                                                       \
601 +               struct n810bm *bm = device_to_n810bm(dev);                      \
602 +               long val;                                                       \
603 +               int err;                                                        \
604 +                                                                               \
605 +               spin_lock_irq(&bm->lock);                                       \
606 +               err = strict_strtol(buf, 0, &val);                              \
607 +               if (!err)                                                       \
608 +                       bm->member = (typeof(bm->member))val;                   \
609 +               spin_unlock_irq(&bm->lock);                                     \
610 +                                                                               \
611 +               return err ? err : count;                                       \
612 +       }
613 +
614 +#define DEFINE_ATTR_SHOW_INT(name, member)                                     \
615 +       DEFINE_SHOW_INT_FUNC(name, member)                                      \
616 +       static DEVICE_ATTR(name, 0444, n810bm_##name##_show, NULL);
617 +
618 +#define DEFINE_ATTR_SHOW_STORE_INT(name, member)                               \
619 +       DEFINE_SHOW_INT_FUNC(name, member)                                      \
620 +       DEFINE_STORE_INT_FUNC(name, member)                                     \
621 +       static DEVICE_ATTR(name, 0644, n810bm_##name##_show,                    \
622 +                                      n810bm_##name##_store);
623 +
624 +DEFINE_ATTR_SHOW_INT(batt_present, battery_present);
625 +DEFINE_ATTR_SHOW_INT(charger_present, charger_present);
626 +DEFINE_ATTR_SHOW_STORE_INT(charger_enable, charger_enabled);
627 +
628 +static ssize_t n810bm_attr_charge_show(struct device *dev,
629 +                                      struct device_attribute *attr,
630 +                                      char *buf)
631 +{
632 +       struct n810bm *bm = device_to_n810bm(dev);
633 +       int err = -ENODEV;
634 +       ssize_t count = 0;
635 +       int millivolt;
636 +
637 +       spin_lock_irq(&bm->lock);
638 +       if (bm->battery_present) {
639 +               millivolt = n810bm_measure_batt_voltage(bm);
640 +               if (millivolt >= 0) {
641 +                       count = snprintf(buf, PAGE_SIZE, "%u\n",
642 +                                        n810bm_mvolt2percent(millivolt));
643 +                       err = 0;
644 +               }
645 +       } else {
646 +               count = snprintf(buf, PAGE_SIZE, "no battery\n");
647 +               err = 0;
648 +       }
649 +       spin_unlock_irq(&bm->lock);
650 +
651 +       return err ? err : count;
652 +}
653 +static DEVICE_ATTR(batt_charge, 0444, n810bm_attr_charge_show, NULL);
654 +
655 +static ssize_t n810bm_attr_capacity_show(struct device *dev,
656 +                                        struct device_attribute *attr,
657 +                                        char *buf)
658 +{
659 +       struct n810bm *bm = device_to_n810bm(dev);
660 +       ssize_t count;
661 +
662 +       spin_lock_irq(&bm->lock);
663 +       if (bm->battery_present) {
664 +               count = snprintf(buf, PAGE_SIZE, "%d\n",
665 +                                (int)bm->capacity);
666 +       } else
667 +               count = snprintf(buf, PAGE_SIZE, "no battery\n");
668 +       spin_unlock_irq(&bm->lock);
669 +
670 +       return count;
671 +}
672 +static DEVICE_ATTR(batt_capacity, 0444, n810bm_attr_capacity_show, NULL);
673 +
674 +static ssize_t n810bm_attr_battemp_show(struct device *dev,
675 +                                       struct device_attribute *attr,
676 +                                       char *buf)
677 +{
678 +       struct n810bm *bm = device_to_n810bm(dev);
679 +       ssize_t count = 0;
680 +       int k, err = -ENODEV;
681 +
682 +       spin_lock_irq(&bm->lock);
683 +       k = n810bm_measure_batt_temp(bm);
684 +       if (k >= 0) {
685 +               count = snprintf(buf, PAGE_SIZE, "%d\n", k);
686 +               err = 0;
687 +       }
688 +       spin_unlock_irq(&bm->lock);
689 +
690 +       return err ? err : count;
691 +}
692 +static DEVICE_ATTR(batt_temp, 0444, n810bm_attr_battemp_show, NULL);
693 +
694 +static ssize_t n810bm_attr_charger_voltage_show(struct device *dev,
695 +                                               struct device_attribute *attr,
696 +                                               char *buf)
697 +{
698 +       struct n810bm *bm = device_to_n810bm(dev);
699 +       ssize_t count = 0;
700 +       int mv, err = -ENODEV;
701 +
702 +       spin_lock_irq(&bm->lock);
703 +       if (bm->charger_present) {
704 +               mv = n810bm_measure_charger_voltage(bm);
705 +               if (mv >= 0) {
706 +                       count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
707 +                       err = 0;
708 +               }
709 +       } else {
710 +               count = snprintf(buf, PAGE_SIZE, "no charger\n");
711 +               err = 0;
712 +       }
713 +       spin_unlock_irq(&bm->lock);
714 +
715 +       return err ? err : count;
716 +}
717 +static DEVICE_ATTR(charger_voltage, 0444, n810bm_attr_charger_voltage_show, NULL);
718 +
719 +static ssize_t n810bm_attr_backup_batt_voltage_show(struct device *dev,
720 +                                                   struct device_attribute *attr,
721 +                                                   char *buf)
722 +{
723 +       struct n810bm *bm = device_to_n810bm(dev);
724 +       ssize_t count = 0;
725 +       int mv, err = -ENODEV;
726 +
727 +       spin_lock_irq(&bm->lock);
728 +       mv = n810bm_measure_backup_batt_voltage(bm);
729 +       if (mv >= 0) {
730 +               count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
731 +               err = 0;
732 +       }
733 +       spin_unlock_irq(&bm->lock);
734 +
735 +       return err ? err : count;
736 +}
737 +static DEVICE_ATTR(backup_batt_voltage, 0444, n810bm_attr_backup_batt_voltage_show, NULL);
738 +
739 +static ssize_t n810bm_attr_batt_current_show(struct device *dev,
740 +                                            struct device_attribute *attr,
741 +                                            char *buf)
742 +{
743 +       struct n810bm *bm = device_to_n810bm(dev);
744 +       ssize_t count = 0;
745 +       int ma;
746 +
747 +       spin_lock_irq(&bm->lock);
748 +       if (bm->battery_present) {
749 +               ma = n810bm_measure_batt_current(bm);//FIXME
750 +               count = snprintf(buf, PAGE_SIZE, "%d\n", ma);
751 +       } else
752 +               count = snprintf(buf, PAGE_SIZE, "no battery\n");
753 +       spin_unlock_irq(&bm->lock);
754 +
755 +       return count;
756 +}
757 +static DEVICE_ATTR(batt_current, 0444, n810bm_attr_batt_current_show, NULL);
758 +
759 +//TODO remove this
760 +static ssize_t n810bm_attr_charger_status_show(struct device *dev,
761 +                                              struct device_attribute *attr,
762 +                                              char *buf)
763 +{
764 +       struct n810bm *bm = device_to_n810bm(dev);
765 +       ssize_t count = 0;
766 +       unsigned int stat;
767 +
768 +       spin_lock_irq(&bm->lock);
769 +       stat = retu_read(bm, RETU_REG_STATUS);
770 +       count = snprintf(buf, PAGE_SIZE, "0x%X\n", stat);
771 +       spin_unlock_irq(&bm->lock);
772 +
773 +       return count;
774 +}
775 +static DEVICE_ATTR(charger_status, 0444, n810bm_attr_charger_status_show, NULL);
776 +
777 +//TODO remove this
778 +static ssize_t n810bm_attr_charge_current_show(struct device *dev,
779 +                                              struct device_attribute *attr,
780 +                                              char *buf)
781 +{
782 +       struct n810bm *bm = device_to_n810bm(dev);
783 +       ssize_t count = 0;
784 +       unsigned int val;
785 +
786 +       spin_lock_irq(&bm->lock);
787 +       val = tahvo_read(bm, TAHVO_REG_CHGCURR);
788 +       count = snprintf(buf, PAGE_SIZE, "0x%X\n", val);
789 +       spin_unlock_irq(&bm->lock);
790 +
791 +       return count;
792 +}
793 +
794 +static ssize_t n810bm_attr_charge_current_store(struct device *dev,
795 +                                               struct device_attribute *attr,
796 +                                               const char *buf, size_t count)
797 +{
798 +       struct n810bm *bm = device_to_n810bm(dev);
799 +       unsigned long val;
800 +       int err;
801 +
802 +       spin_lock_irq(&bm->lock);
803 +       err = strict_strtoul(buf, 0, &val);
804 +       if (!err && val <= 0xFF)
805 +               tahvo_write(bm, TAHVO_REG_CHGCURR, val);
806 +       spin_unlock_irq(&bm->lock);
807 +
808 +       return err ? err : count;
809 +}
810 +static DEVICE_ATTR(charge_current, 0644,
811 +                  n810bm_attr_charge_current_show,
812 +                  n810bm_attr_charge_current_store);
813 +
814 +static const struct device_attribute *n810bm_attrs[] = {
815 +       &dev_attr_batt_present,
816 +       &dev_attr_batt_charge,
817 +       &dev_attr_batt_current,
818 +       &dev_attr_batt_capacity,
819 +       &dev_attr_batt_temp,
820 +       &dev_attr_backup_batt_voltage,
821 +       &dev_attr_charger_present,
822 +       &dev_attr_charger_voltage,
823 +       &dev_attr_charger_status,
824 +       &dev_attr_charger_enable,
825 +       &dev_attr_charge_current,
826 +};
827 +
828 +static void n810bm_hw_exit(struct n810bm *bm)
829 +{
830 +       retu_write(bm, RETU_REG_ADCSCR, 0);
831 +       //TODO
832 +}
833 +
834 +static int n810bm_hw_init(struct n810bm *bm)
835 +{
836 +       int err;
837 +
838 +       err = n810bm_check_adc_sanity(bm);
839 +       if (err)
840 +               return err;
841 +
842 +       return 0;
843 +}
844 +
845 +static int n810bm_device_init(struct n810bm *bm)
846 +{
847 +       int attr_index;
848 +       int err;
849 +
850 +       err = n810bm_hw_init(bm);
851 +       if (err)
852 +               goto error;
853 +       for (attr_index = 0; attr_index < ARRAY_SIZE(n810bm_attrs); attr_index++) {
854 +               err = device_create_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
855 +               if (err)
856 +                       goto err_unwind_attrs;
857 +       }
858 +       err = retu_request_irq(RETU_INT_ADCS,
859 +                              n810bm_adc_irq_handler,
860 +                              (unsigned long)bm, "n810bm");
861 +       if (err)
862 +               goto err_unwind_attrs;
863 +       err = tahvo_request_irq(TAHVO_INT_BATCURR,
864 +                               n810bm_tahvo_current_measure_irq_handler,
865 +                               (unsigned long)bm, "n810bm");
866 +       if (err)
867 +               goto err_free_retu_irq;
868 +
869 +       lipocharge_init(&bm->charger);
870 +       mod_timer(&bm->check_timer, round_jiffies(jiffies + N810BM_CHECK_INTERVAL));
871 +
872 +       bm->initialized = 1;
873 +       dev_info(&bm->pdev->dev, "Battery management initialized");
874 +
875 +       return 0;
876 +
877 +err_free_retu_irq:
878 +       retu_free_irq(RETU_INT_ADCS);
879 +err_unwind_attrs:
880 +       for (attr_index--; attr_index >= 0; attr_index--)
881 +               device_remove_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
882 +/*err_exit:*/
883 +       n810bm_hw_exit(bm);
884 +error:
885 +       return err;
886 +}
887 +
888 +static void n810bm_device_exit(struct n810bm *bm)
889 +{
890 +       int i;
891 +
892 +       if (!bm->initialized)
893 +               return;
894 +
895 +       lipocharge_exit(&bm->charger);
896 +       tahvo_free_irq(TAHVO_INT_BATCURR);
897 +       retu_free_irq(RETU_INT_ADCS);
898 +       del_timer_sync(&bm->check_timer);
899 +       for (i = 0; i < ARRAY_SIZE(n810bm_attrs); i++)
900 +               device_remove_file(&bm->pdev->dev, n810bm_attrs[i]);
901 +       n810bm_hw_exit(bm);
902 +       release_firmware(bm->pmm_block);
903 +
904 +       bm->initialized = 0;
905 +}
906 +
907 +static void n810bm_pmm_block_found(const struct firmware *fw, void *context)
908 +{
909 +       struct n810bm *bm = context;
910 +       int err;
911 +
912 +       if (!fw) {
913 +               dev_err(&bm->pdev->dev,
914 +                       "CAL PMM block image file not found");
915 +               goto error;
916 +       }
917 +       if (fw->size != N810BM_PMM_BLOCK_SIZE ||
918 +           memcmp(fw->data, "BME-PMM-BLOCK01", 15) != 0) {
919 +               dev_err(&bm->pdev->dev,
920 +                       "CAL PMM block image file has an invalid format");
921 +               goto error;
922 +       }
923 +
924 +       bm->pmm_block = fw;
925 +       err = n810bm_device_init(bm);
926 +       if (err) {
927 +               dev_err(&bm->pdev->dev,
928 +                       "Failed to initialized battery management (%d)", err);
929 +               goto error;
930 +       }
931 +
932 +       return;
933 +error:
934 +       release_firmware(fw);
935 +}
936 +
937 +static int __devinit n810bm_probe(struct platform_device *pdev)
938 +{
939 +       struct n810bm *bm;
940 +       int err;
941 +
942 +       bm = kzalloc(sizeof(*bm), GFP_KERNEL);
943 +       if (!bm)
944 +               return -ENOMEM;
945 +       bm->pdev = pdev;
946 +       platform_set_drvdata(pdev, bm);
947 +       spin_lock_init(&bm->lock);
948 +       setup_timer(&bm->check_timer, n810bm_check_timer, (unsigned long)bm);
949 +
950 +       dev_info(&bm->pdev->dev, "Requesting CAL BME PMM block firmware file "
951 +                N810BM_PMM_BLOCK_FILENAME);
952 +       err = request_firmware_nowait(THIS_MODULE, 1,
953 +                                     N810BM_PMM_BLOCK_FILENAME,
954 +                                     &bm->pdev->dev, GFP_KERNEL,
955 +                                     bm, n810bm_pmm_block_found);
956 +       if (err) {
957 +               dev_err(&bm->pdev->dev,
958 +                       "Failed to request CAL PMM block image file (%d)", err);
959 +               goto err_free;
960 +       }
961 +
962 +       return 0;
963 +
964 +err_free:
965 +       kfree(bm);
966 +
967 +       return err;
968 +}
969 +
970 +static int __devexit n810bm_remove(struct platform_device *pdev)
971 +{
972 +       struct n810bm *bm = platform_get_drvdata(pdev);
973 +
974 +       n810bm_device_exit(bm);
975 +
976 +       kfree(bm);
977 +       platform_set_drvdata(pdev, NULL);
978 +
979 +       return 0;
980 +}
981 +
982 +static struct platform_driver n810bm_driver = {
983 +       .remove         = __devexit_p(n810bm_remove),
984 +       .driver         = {
985 +               .name   = "n810bm",
986 +       }
987 +};
988 +
989 +static int __init n810bm_modinit(void)
990 +{
991 +       return platform_driver_probe(&n810bm_driver, n810bm_probe);
992 +}
993 +module_init(n810bm_modinit);
994 +
995 +static void __exit n810bm_modexit(void)
996 +{
997 +       platform_driver_unregister(&n810bm_driver);
998 +}
999 +module_exit(n810bm_modexit);
1000 +
1001 +MODULE_DESCRIPTION("Nokia n810 battery management");
1002 +MODULE_FIRMWARE(N810BM_PMM_BLOCK_FILENAME);
1003 +MODULE_LICENSE("GPL");
1004 +MODULE_AUTHOR("Michael Buesch");
1005 Index: linux-2.6.37/drivers/cbus/retu.c
1006 ===================================================================
1007 --- linux-2.6.37.orig/drivers/cbus/retu.c       2011-02-06 00:24:48.493004751 +0100
1008 +++ linux-2.6.37/drivers/cbus/retu.c    2011-02-06 00:24:48.551008149 +0100
1009 @@ -85,10 +85,10 @@
1010   *
1011   * This function writes a value to the specified register
1012   */
1013 -void retu_write_reg(int reg, u16 val)
1014 +int retu_write_reg(int reg, u16 val)
1015  {
1016         BUG_ON(!retu_initialized);
1017 -       cbus_write_reg(cbus_host, RETU_ID, reg, val);
1018 +       return cbus_write_reg(cbus_host, RETU_ID, reg, val);
1019  }
1020  
1021  void retu_set_clear_reg_bits(int reg, u16 set, u16 clear)
1022 Index: linux-2.6.37/drivers/cbus/retu.h
1023 ===================================================================
1024 --- linux-2.6.37.orig/drivers/cbus/retu.h       2011-02-06 00:24:48.493004751 +0100
1025 +++ linux-2.6.37/drivers/cbus/retu.h    2011-02-06 00:24:48.551008149 +0100
1026 @@ -40,6 +40,8 @@
1027  #define RETU_REG_CTRL_CLR      0x0f    /* Regulator clear register */
1028  #define RETU_REG_CTRL_SET      0x10    /* Regulator set register */
1029  #define RETU_REG_STATUS                0x16    /* Status register */
1030 +#define  RETU_REG_STATUS_BATAVAIL      0x0100 /* Battery available */
1031 +#define  RETU_REG_STATUS_CHGPLUG       0x1000 /* Charger is plugged in */
1032  #define RETU_REG_WATCHDOG      0x17    /* Watchdog register */
1033  #define RETU_REG_AUDTXR                0x18    /* Audio Codec Tx register */
1034  #define RETU_REG_MAX           0x1f
1035 @@ -57,8 +59,25 @@
1036  
1037  #define        MAX_RETU_IRQ_HANDLERS   16
1038  
1039 +/* ADC channels */
1040 +#define RETU_ADC_GND           0x00 /* Ground */
1041 +#define RETU_ADC_BSI           0x01 /* Battery Size Indicator */
1042 +#define RETU_ADC_BATTEMP       0x02 /* Battery temperature */
1043 +#define RETU_ADC_CHGVOLT       0x03 /* Charger voltage */
1044 +#define RETU_ADC_HEADSET       0x04 /* Headset detection */
1045 +#define RETU_ADC_HOOKDET       0x05 /* Hook detection */
1046 +#define RETU_ADC_RFGP          0x06 /* RF GP */
1047 +#define RETU_ADC_WBTX          0x07 /* Wideband Tx detection */
1048 +#define RETU_ADC_BATTVOLT      0x08 /* Battery voltage measurement */
1049 +#define RETU_ADC_GND2          0x09 /* Ground */
1050 +#define RETU_ADC_LIGHTSENS     0x0A /* Light sensor */
1051 +#define RETU_ADC_LIGHTTEMP     0x0B /* Light sensor temperature */
1052 +#define RETU_ADC_BKUPVOLT      0x0C /* Backup battery voltage */
1053 +#define RETU_ADC_TEMP          0x0D /* RETU temperature */
1054 +
1055 +
1056  int retu_read_reg(int reg);
1057 -void retu_write_reg(int reg, u16 val);
1058 +int retu_write_reg(int reg, u16 val);
1059  void retu_set_clear_reg_bits(int reg, u16 set, u16 clear);
1060  int retu_read_adc(int channel);
1061  int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
1062 Index: linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c
1063 ===================================================================
1064 --- linux-2.6.37.orig/arch/arm/mach-omap2/board-n8x0.c  2011-02-06 00:24:48.478003872 +0100
1065 +++ linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c       2011-02-06 00:24:48.551008149 +0100
1066 @@ -907,6 +907,17 @@
1067                                     ARRAY_SIZE(n8x0_gpio_switches));
1068  }
1069  
1070 +static struct platform_device n810_bm_device = {
1071 +       .name           = "n810bm",
1072 +       .id             = -1,
1073 +};
1074 +
1075 +static void __init n810_bm_init(void)
1076 +{
1077 +       if (platform_device_register(&n810_bm_device))
1078 +               BUG();
1079 +}
1080 +
1081  static void __init n8x0_init_machine(void)
1082  {
1083         omap2420_mux_init(board_mux, OMAP_PACKAGE_ZAC);
1084 @@ -933,6 +944,8 @@
1085         n8x0_onenand_init();
1086         n8x0_mmc_init();
1087         n8x0_usb_init();
1088 +
1089 +       n810_bm_init();
1090  }
1091  
1092  MACHINE_START(NOKIA_N800, "Nokia N800")
1093 Index: linux-2.6.37/drivers/cbus/lipocharge.c
1094 ===================================================================
1095 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
1096 +++ linux-2.6.37/drivers/cbus/lipocharge.c      2011-02-06 12:57:01.427475867 +0100
1097 @@ -0,0 +1,58 @@
1098 +/*
1099 + *   Generic LIPO battery charger
1100 + *
1101 + *   Copyright (c) 2010 Michael Buesch <mb@bu3sch.de>
1102 + *
1103 + *   This program is free software; you can redistribute it and/or
1104 + *   modify it under the terms of the GNU General Public License
1105 + *   as published by the Free Software Foundation; either version 2
1106 + *   of the License, or (at your option) any later version.
1107 + *
1108 + *   This program is distributed in the hope that it will be useful,
1109 + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
1110 + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1111 + *   GNU General Public License for more details.
1112 + */
1113 +
1114 +#include "lipocharge.h"
1115 +
1116 +#include <linux/slab.h>
1117 +
1118 +
1119 +static void lipocharge_timer(unsigned long data)
1120 +{
1121 +       struct lipocharge *c = (struct lipocharge *)data;
1122 +
1123 +       spin_lock(&c->lock);
1124 +       //TODO
1125 +       spin_unlock(&c->lock);
1126 +}
1127 +
1128 +void lipocharge_init(struct lipocharge *c)
1129 +{
1130 +       spin_lock_init(&c->lock);
1131 +       setup_timer(&c->timer, lipocharge_timer, (unsigned long)c);
1132 +}
1133 +
1134 +void lipocharge_exit(struct lipocharge *c)
1135 +{
1136 +}
1137 +
1138 +int lipocharge_start(struct lipocharge *c)
1139 +{
1140 +       if (!c->set_charge_current || !c->get_charge_current ||
1141 +           !c->get_voltage ||
1142 +           !c->finished || !c->emergency)
1143 +               return -EINVAL;
1144 +       if (!c->top_voltage || c->top_voltage > 4200)
1145 +               return -EINVAL;
1146 +       //TODO
1147 +
1148 +       return 0;
1149 +}
1150 +
1151 +void lipocharge_stop(struct lipocharge *c)
1152 +{
1153 +       del_timer_sync(&c->timer);
1154 +       //TODO
1155 +}
1156 Index: linux-2.6.37/drivers/cbus/lipocharge.h
1157 ===================================================================
1158 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
1159 +++ linux-2.6.37/drivers/cbus/lipocharge.h      2011-02-06 12:36:29.800830614 +0100
1160 @@ -0,0 +1,55 @@
1161 +#ifndef LIPOCHARGE_H_
1162 +#define LIPOCHARGE_H_
1163 +
1164 +#include <linux/timer.h>
1165 +#include <linux/spinlock.h>
1166 +
1167 +
1168 +#define LIPORATE(a,b)  (((a) * 1000) + (b))
1169 +#define LIPORATE_1C    LIPORATE(1,0)   /* 1C */
1170 +#define LIPORATE_p8C   LIPORATE(0,8)   /* 0.8C */
1171 +#define LIPORATE_p6C   LIPORATE(0,6)   /* 0.6C */
1172 +
1173 +/** struct lipocharge - A generic LIPO charger
1174 + *
1175 + * @capacity: Battery capacity in mAh.
1176 + * @rate: Charge rate.
1177 + * @top_voltage: Fully charged voltage, in mV.
1178 + *
1179 + * @set_charge_current: Set the charge current, in mA.
1180 + * @get_charge_current: Get the battery current, in signed mA.
1181 + * @get_voltage: Get the battery voltage, in mV.
1182 + *
1183 + * @finished: Charging finished.
1184 + * @emergency: Something went wrong. Force shutdown.
1185 + */
1186 +struct lipocharge {
1187 +       unsigned int capacity;
1188 +       unsigned int rate;
1189 +       unsigned int top_voltage;
1190 +
1191 +       int (*set_charge_current)(struct lipocharge *c, unsigned int ma);
1192 +       int (*get_charge_current)(struct lipocharge *c, int *ma);
1193 +       int (*get_voltage)(struct lipocharge *c, unsigned int *mv);
1194 +
1195 +       void (*finished)(struct lipocharge *c);
1196 +       void (*emergency)(struct lipocharge *c);
1197 +
1198 +       /* internal */
1199 +       spinlock_t lock;
1200 +       struct timer_list timer;
1201 +       bool charging;
1202 +};
1203 +
1204 +void lipocharge_init(struct lipocharge *c);
1205 +void lipocharge_exit(struct lipocharge *c);
1206 +
1207 +int lipocharge_start(struct lipocharge *c);
1208 +void lipocharge_stop(struct lipocharge *c);
1209 +
1210 +static inline bool lipocharge_is_charging(struct lipocharge *c)
1211 +{
1212 +       return c->charging;
1213 +}
1214 +
1215 +#endif /* LIPOCHARGE_H_ */
1216 Index: linux-2.6.37/drivers/cbus/tahvo.h
1217 ===================================================================
1218 --- linux-2.6.37.orig/drivers/cbus/tahvo.h      2011-02-06 00:24:48.494004810 +0100
1219 +++ linux-2.6.37/drivers/cbus/tahvo.h   2011-02-06 00:24:48.551008149 +0100
1220 @@ -30,17 +30,28 @@
1221  #define TAHVO_REG_IDR          0x01    /* Interrupt ID */
1222  #define TAHVO_REG_IDSR         0x02    /* Interrupt status */
1223  #define TAHVO_REG_IMR          0x03    /* Interrupt mask */
1224 +#define TAHVO_REG_CHGCURR      0x04    /* Charge current control PWM (8-bit) */
1225  #define TAHVO_REG_LEDPWMR      0x05    /* LED PWM */
1226  #define TAHVO_REG_USBR         0x06    /* USB control */
1227 +#define TAHVO_REG_CHGCTL       0x08    /* Charge control register */
1228 +#define  TAHVO_REG_CHGCTL_EN           0x0001  /* Global charge enable */
1229 +#define  TAHVO_REG_CHGCTL_PWMOVR       0x0004  /* PWM override. Force charge PWM to 100% duty cycle. */
1230 +#define  TAHVO_REG_CHGCTL_UNK8         0x0008  /* XXX: Unknown. Written on init. */
1231 +#define  TAHVO_REG_CHGCTL_CURMEAS      0x0040  /* Enable battery current measurement. */
1232 +#define  TAHVO_REG_CHGCTL_CURTIMRST    0x0080  /* Current measure timer reset. */
1233 +#define TAHVO_REG_BATCURRTIMER 0x0c    /* Battery current measure timer (8-bit) */
1234 +#define TAHVO_REG_BATCURR      0x0d    /* Battery (dis)charge current (signed 16-bit) */
1235 +
1236  #define TAHVO_REG_MAX          0x0d
1237  
1238  /* Interrupt sources */
1239  #define TAHVO_INT_VBUSON       0
1240 +#define TAHVO_INT_BATCURR      7 /* Battery current measure timer */
1241  
1242  #define MAX_TAHVO_IRQ_HANDLERS 8
1243  
1244  int tahvo_read_reg(int reg);
1245 -void tahvo_write_reg(int reg, u16 val);
1246 +int tahvo_write_reg(int reg, u16 val);
1247  void tahvo_set_clear_reg_bits(int reg, u16 set, u16 clear);
1248  int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
1249  void tahvo_free_irq(int id);
1250 Index: linux-2.6.37/drivers/cbus/tahvo.c
1251 ===================================================================
1252 --- linux-2.6.37.orig/drivers/cbus/tahvo.c      2011-02-06 00:24:48.494004810 +0100
1253 +++ linux-2.6.37/drivers/cbus/tahvo.c   2011-02-06 00:24:48.552008207 +0100
1254 @@ -85,10 +85,10 @@
1255   *
1256   * This function writes a value to the specified register
1257   */
1258 -void tahvo_write_reg(int reg, u16 val)
1259 +int tahvo_write_reg(int reg, u16 val)
1260  {
1261         BUG_ON(!tahvo_initialized);
1262 -       cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
1263 +       return cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
1264  }
1265  
1266  /**