31eeeb857ac78c787e617305d7d022d9812490e5
[librecmc/librecmc.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 14:05:49.838388760 +0100
16 +++ linux-2.6.37/drivers/cbus/Kconfig   2011-02-06 14:05:49.885395646 +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 14:05:49.829387442 +0100
33 +++ linux-2.6.37/drivers/cbus/Makefile  2011-02-06 14:05:49.885395646 +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-09 13:47:23.831144291 +0100
45 @@ -0,0 +1,1169 @@
46 +/*
47 + *   Nokia n810 battery management
48 + *
49 + *   WARNING: This driver is based on unconfirmed documentation.
50 + *            It is possibly dangerous to use this software.
51 + *            Use this software at your own risk!
52 + *
53 + *   Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
54 + *
55 + *   This program is free software; you can redistribute it and/or
56 + *   modify it under the terms of the GNU General Public License
57 + *   as published by the Free Software Foundation; either version 2
58 + *   of the License, or (at your option) any later version.
59 + *
60 + *   This program is distributed in the hope that it will be useful,
61 + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
62 + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
63 + *   GNU General Public License for more details.
64 + */
65 +
66 +#define DEBUG
67 +
68 +#include <linux/module.h>
69 +#include <linux/device.h>
70 +#include <linux/platform_device.h>
71 +#include <linux/slab.h>
72 +#include <linux/mutex.h>
73 +#include <linux/timer.h>
74 +#include <linux/firmware.h>
75 +#include <linux/bitops.h>
76 +#include <linux/workqueue.h>
77 +#include <linux/delay.h>
78 +
79 +#include "cbus.h"
80 +#include "retu.h"
81 +#include "tahvo.h"
82 +#include "lipocharge.h"
83 +
84 +
85 +#define N810BM_PMM_BLOCK_FILENAME      "n810-cal-bme-pmm.fw"
86 +#define N810BM_PMM_BLOCK_SIZE          0x600
87 +
88 +#define N810BM_CHECK_INTERVAL          (HZ * 2)
89 +#define N810BM_MIN_VOLTAGE_THRES       3300 /* Absolute minimum voltage threshold */
90 +
91 +
92 +/* RETU_ADC_BSI
93 + * The battery size indicator ADC measures the resistance between
94 + * the battery BSI pin and ground. This is used to detect the battery
95 + * capacity, as the BSI resistor is related to capacity.
96 + *
97 + * Manually measured lookup table.
98 + * Hard to measure, thus not very accurate.
99 + *
100 + * Resistance  |  ADC value
101 + * ========================
102 + * 120k        |  0x3AC
103 + * 110k        |  0x37C
104 + * 100k        |  0x351
105 + *  90k        |  0x329
106 + */
107 +
108 +/* RETU_ADC_BATTVOLT
109 + * Manually measured lookup table.
110 + * Hard to measure, thus not very accurate.
111 + *
112 + * Voltage  |  ADC value
113 + * =====================
114 + * 2.80V    |  0x037
115 + * 2.90V    |  0x05E
116 + * 3.00V    |  0x090
117 + * 3.10V    |  0x0A4
118 + * 3.20V    |  0x0CC
119 + * 3.30V    |  0x0EF
120 + * 3.40V    |  0x115
121 + * 3.50V    |  0x136
122 + * 3.60V    |  0x15C
123 + * 3.70V    |  0x187
124 + * 3.80V    |  0x1A5
125 + * 3.90V    |  0x1C9
126 + * 4.00V    |  0x1ED
127 + * 4.10V    |  0x212
128 + * 4.20V    |  0x236
129 + */
130 +
131 +
132 +enum n810bm_capacity {
133 +       N810BM_CAP_UNKNOWN      = -1,
134 +       N810BM_CAP_NONE         = 0,
135 +       N810BM_CAP_1500MAH      = 1500, /* 1500 mAh battery */
136 +};
137 +
138 +enum n810bm_notify_flags {
139 +       N810BM_NOTIFY_battery_charging,
140 +       N810BM_NOTIFY_charger_pwm,
141 +};
142 +
143 +struct n810bm {
144 +       bool battery_present;                   /* A battery is inserted */
145 +       bool charger_present;                   /* The charger is connected */
146 +       enum n810bm_capacity capacity;          /* The capacity of the inserted battery (if any) */
147 +
148 +       bool charger_enabled;                   /* Want to charge? */
149 +       struct lipocharge charger;              /* Charger subsystem */
150 +       unsigned int active_current_pwm;        /* Active value of TAHVO_REG_CHGCURR */
151 +       int current_measure_enabled;            /* Current measure enable refcount */
152 +
153 +       struct platform_device *pdev;
154 +       const struct firmware *pmm_block;       /* CAL PMM block */
155 +
156 +       bool verbose_charge_log;                /* Verbose charge logging */
157 +
158 +       unsigned long notify_flags;
159 +       struct work_struct notify_work;
160 +       struct work_struct currmeas_irq_work;
161 +       struct delayed_work periodic_check_work;
162 +
163 +       bool initialized;                       /* The hardware was initialized */
164 +       struct mutex mutex;
165 +};
166 +
167 +static void n810bm_notify_battery_charging(struct n810bm *bm);
168 +static void n810bm_notify_charger_pwm(struct n810bm *bm);
169 +
170 +
171 +static inline struct n810bm * device_to_n810bm(struct device *dev)
172 +{
173 +       struct platform_device *pdev = to_platform_device(dev);
174 +       struct n810bm *bm = platform_get_drvdata(pdev);
175 +
176 +       return bm;
177 +}
178 +
179 +static inline bool n810bm_known_battery_present(struct n810bm *bm)
180 +{
181 +       return bm->battery_present &&
182 +              bm->capacity != N810BM_CAP_UNKNOWN &&
183 +              bm->capacity != N810BM_CAP_NONE;
184 +}
185 +
186 +static NORET_TYPE void n810bm_emergency(struct n810bm *bm, const char *message) ATTRIB_NORET;
187 +static void n810bm_emergency(struct n810bm *bm, const char *message)
188 +{
189 +       printk(KERN_EMERG "n810 battery management fatal fault: %s\n", message);
190 +       cbus_emergency();
191 +}
192 +
193 +static u16 tahvo_read(struct n810bm *bm, unsigned int reg)
194 +{
195 +       int ret;
196 +       unsigned long flags;
197 +
198 +       spin_lock_irqsave(&tahvo_lock, flags);
199 +       ret = tahvo_read_reg(reg);
200 +       spin_unlock_irqrestore(&tahvo_lock, flags);
201 +       if (ret < 0 || ret > 0xFFFF)
202 +               n810bm_emergency(bm, "tahvo_read");
203 +
204 +       return ret;
205 +}
206 +
207 +static void tahvo_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
208 +{
209 +       int ret;
210 +       unsigned long flags;
211 +       u16 value;
212 +
213 +       spin_lock_irqsave(&tahvo_lock, flags);
214 +       if (~mask) {
215 +               ret = tahvo_read_reg(reg);
216 +               if (ret < 0 || ret > 0xFFFF)
217 +                       goto fatal_unlock;
218 +               value = ret;
219 +       } else
220 +               value = 0;
221 +       value &= ~mask;
222 +       value |= set;
223 +       ret = tahvo_write_reg(reg, value);
224 +       if (ret)
225 +               goto fatal_unlock;
226 +       spin_unlock_irqrestore(&tahvo_lock, flags);
227 +
228 +       return;
229 +
230 +fatal_unlock:
231 +       spin_unlock_irqrestore(&tahvo_lock, flags);
232 +       n810bm_emergency(bm, "tahvo_maskset");
233 +}
234 +
235 +static inline void tahvo_write(struct n810bm *bm, unsigned int reg, u16 value)
236 +{
237 +       tahvo_maskset(bm, reg, 0xFFFF, value);
238 +}
239 +
240 +static inline void tahvo_set(struct n810bm *bm, unsigned int reg, u16 mask)
241 +{
242 +       tahvo_maskset(bm, reg, mask, mask);
243 +}
244 +
245 +static inline void tahvo_clear(struct n810bm *bm, unsigned int reg, u16 mask)
246 +{
247 +       tahvo_maskset(bm, reg, mask, 0);
248 +}
249 +
250 +static u16 retu_read(struct n810bm *bm, unsigned int reg)
251 +{
252 +       int ret;
253 +       unsigned long flags;
254 +
255 +       spin_lock_irqsave(&retu_lock, flags);
256 +       ret = retu_read_reg(reg);
257 +       spin_unlock_irqrestore(&retu_lock, flags);
258 +       if (ret < 0 || ret > 0xFFFF)
259 +               n810bm_emergency(bm, "retu_read");
260 +
261 +       return ret;
262 +}
263 +
264 +static void retu_maskset(struct n810bm *bm, unsigned int reg, u16 mask, u16 set)
265 +{
266 +       int ret;
267 +       unsigned long flags;
268 +       u16 value;
269 +
270 +       spin_lock_irqsave(&retu_lock, flags);
271 +       if (~mask) {
272 +               ret = retu_read_reg(reg);
273 +               if (ret < 0 || ret > 0xFFFF)
274 +                       goto fatal_unlock;
275 +               value = ret;
276 +       } else
277 +               value = 0;
278 +       value &= ~mask;
279 +       value |= set;
280 +       ret = retu_write_reg(reg, value);
281 +       if (ret)
282 +               goto fatal_unlock;
283 +       spin_unlock_irqrestore(&retu_lock, flags);
284 +
285 +       return;
286 +
287 +fatal_unlock:
288 +       spin_unlock_irqrestore(&retu_lock, flags);
289 +       n810bm_emergency(bm, "retu_maskset");
290 +}
291 +
292 +static inline void retu_write(struct n810bm *bm, unsigned int reg, u16 value)
293 +{
294 +       retu_maskset(bm, reg, 0xFFFF, value);
295 +}
296 +
297 +static int retu_adc_average(struct n810bm *bm, unsigned int chan,
298 +                           unsigned int nr_passes)
299 +{
300 +       unsigned int i, value = 0;
301 +       int ret;
302 +
303 +       if (WARN_ON(!nr_passes))
304 +               return 0;
305 +       for (i = 0; i < nr_passes; i++) {
306 +               ret = retu_read_adc(chan);
307 +               if (ret < 0)
308 +                       return ret;
309 +               value += ret;
310 +       }
311 +       value /= nr_passes;
312 +
313 +       return value;
314 +}
315 +
316 +/* Set the current measure timer that triggers on Tahvo IRQ 7
317 + * An interval of zero disables the timer. */
318 +static void n810bm_set_current_measure_timer(struct n810bm *bm,
319 +                                            u16 millisec_interval)
320 +{
321 +       u16 value = millisec_interval;
322 +
323 +       if (value <= 0xF905) {
324 +               value = ((u64)0x10624DD3 * (u64)(value + 0xF9)) >> 32;
325 +               value /= 16;
326 +       } else
327 +               value = 0xFF;
328 +
329 +       tahvo_write(bm, TAHVO_REG_BATCURRTIMER, value & 0xFF);
330 +
331 +       tahvo_set(bm, TAHVO_REG_CHGCTL,
332 +                 TAHVO_REG_CHGCTL_CURTIMRST);
333 +       tahvo_clear(bm, TAHVO_REG_CHGCTL,
334 +                   TAHVO_REG_CHGCTL_CURTIMRST);
335 +
336 +       if (millisec_interval)
337 +               tahvo_enable_irq(TAHVO_INT_BATCURR);
338 +       else
339 +               tahvo_disable_irq(TAHVO_INT_BATCURR);
340 +
341 +       //TODO also do a software timer for safety.
342 +}
343 +
344 +static void n810bm_enable_current_measure(struct n810bm *bm)
345 +{
346 +       WARN_ON(bm->current_measure_enabled < 0);
347 +       if (!bm->current_measure_enabled) {
348 +               /* Enable the current measurement circuitry */
349 +               tahvo_set(bm, TAHVO_REG_CHGCTL,
350 +                         TAHVO_REG_CHGCTL_CURMEAS);
351 +               dev_dbg(&bm->pdev->dev,
352 +                       "Current measurement circuitry enabled");
353 +       }
354 +       bm->current_measure_enabled++;
355 +}
356 +
357 +static void n810bm_disable_current_measure(struct n810bm *bm)
358 +{
359 +       bm->current_measure_enabled--;
360 +       WARN_ON(bm->current_measure_enabled < 0);
361 +       if (!bm->current_measure_enabled) {
362 +               /* Disable the current measurement circuitry */
363 +               tahvo_clear(bm, TAHVO_REG_CHGCTL,
364 +                           TAHVO_REG_CHGCTL_CURMEAS);
365 +               dev_dbg(&bm->pdev->dev,
366 +                       "Current measurement circuitry disabled");
367 +       }
368 +}
369 +
370 +/* Measure the actual battery current. Returns a signed value in mA.
371 + * Does only work, if current measurement was enabled. */
372 +static int n810bm_measure_batt_current(struct n810bm *bm)
373 +{
374 +       u16 retval;
375 +       int adc = 0, ma, i;
376 +
377 +       if (WARN_ON(bm->current_measure_enabled <= 0))
378 +               return 0;
379 +       for (i = 0; i < 3; i++) {
380 +               retval = tahvo_read(bm, TAHVO_REG_BATCURR);
381 +               adc += (s16)retval; /* Value is signed */
382 +       }
383 +       adc /= 3;
384 +
385 +       //TODO convert to mA
386 +       ma = adc;
387 +
388 +       return ma;
389 +}
390 +
391 +/* Requires bm->mutex locked */
392 +static int n810bm_measure_batt_current_async(struct n810bm *bm)
393 +{
394 +       int ma;
395 +       bool charging = lipocharge_is_charging(&bm->charger);
396 +
397 +       n810bm_enable_current_measure(bm);
398 +       if (!charging)
399 +               WARN_ON(bm->active_current_pwm != 0);
400 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
401 +                     TAHVO_REG_CHGCTL_EN |
402 +                     TAHVO_REG_CHGCTL_PWMOVR |
403 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
404 +                     TAHVO_REG_CHGCTL_EN |
405 +                     TAHVO_REG_CHGCTL_PWMOVR |
406 +                     (charging ? 0 : TAHVO_REG_CHGCTL_PWMOVRZERO));
407 +       ma = n810bm_measure_batt_current(bm);
408 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
409 +                     TAHVO_REG_CHGCTL_EN |
410 +                     TAHVO_REG_CHGCTL_PWMOVR |
411 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
412 +                     (charging ? TAHVO_REG_CHGCTL_EN : 0));
413 +       n810bm_disable_current_measure(bm);
414 +
415 +       return ma;
416 +}
417 +
418 +static int adc_sanity_check(struct n810bm *bm, unsigned int channel)
419 +{
420 +       int value;
421 +
422 +       value = retu_read_adc(channel);
423 +       if (value < 0) {
424 +               dev_err(&bm->pdev->dev, "Failed to read GND ADC channel %u",
425 +                       channel);
426 +               return -EIO;
427 +       }
428 +       dev_dbg(&bm->pdev->dev,
429 +               "GND ADC channel %u sanity check got value: %d",
430 +               channel, value);
431 +       if (value > 5) {
432 +               n810bm_emergency(bm, "GND ADC sanity check failed");
433 +               return -EIO;
434 +       }
435 +
436 +       return 0;
437 +}
438 +
439 +static int n810bm_check_adc_sanity(struct n810bm *bm)
440 +{
441 +       int err;
442 +
443 +       /* Discard one conversion */
444 +       retu_write(bm, RETU_REG_ADCSCR, 0);
445 +       retu_read_adc(RETU_ADC_GND2);
446 +
447 +       err = adc_sanity_check(bm, RETU_ADC_GND2);
448 +       if (err)
449 +               return err;
450 +
451 +       return 0;
452 +}
453 +
454 +/* Measure the battery voltage. Returns the value in mV (or negative value on error). */
455 +static int n810bm_measure_batt_voltage(struct n810bm *bm)
456 +{
457 +       int adc;
458 +       unsigned int mv;
459 +       const unsigned int scale = 1000;
460 +
461 +       adc = retu_adc_average(bm, RETU_ADC_BATTVOLT, 5);
462 +       if (adc < 0)
463 +               return adc;
464 +       if (adc <= 0x37)
465 +               return 2800;
466 +       mv = 2800 + ((adc - 0x37) * (((4200 - 2800) * scale) / (0x236 - 0x37))) / scale;
467 +
468 +       //TODO compensate for power consumption
469 +       //TODO honor calibration values
470 +
471 +       return mv;
472 +}
473 +
474 +/* Measure the charger voltage. Returns the value in mV (or negative value on error). */
475 +static int n810bm_measure_charger_voltage(struct n810bm *bm)
476 +{
477 +       int adc;
478 +       unsigned int mv;
479 +
480 +       adc = retu_adc_average(bm, RETU_ADC_CHGVOLT, 5);
481 +       if (adc < 0)
482 +               return adc;
483 +       //TODO convert to mV
484 +       mv = adc;
485 +
486 +       return mv;
487 +}
488 +
489 +/* Measure backup battery voltage. Returns the value in mV (or negative value on error). */
490 +static int n810bm_measure_backup_batt_voltage(struct n810bm *bm)
491 +{
492 +       int adc;
493 +       unsigned int mv;
494 +
495 +       adc = retu_adc_average(bm, RETU_ADC_BKUPVOLT, 3);
496 +       if (adc < 0)
497 +               return adc;
498 +       //TODO convert to mV
499 +       mv = adc;
500 +
501 +       return mv;
502 +}
503 +
504 +/* Measure the battery temperature. Returns the value in K (or negative value on error). */
505 +static int n810bm_measure_batt_temp(struct n810bm *bm)
506 +{
507 +       int adc;
508 +       unsigned int k;
509 +
510 +       adc = retu_adc_average(bm, RETU_ADC_BATTEMP, 3);
511 +       if (adc < 0)
512 +               return adc;
513 +       //TODO convert to K
514 +       k = adc;
515 +
516 +       return k;
517 +}
518 +
519 +/* Read the battery capacity via BSI pin. */
520 +static enum n810bm_capacity n810bm_read_batt_capacity(struct n810bm *bm)
521 +{
522 +       int adc;
523 +       const unsigned int hyst = 20;
524 +
525 +       adc = retu_adc_average(bm, RETU_ADC_BSI, 5);
526 +       if (adc < 0) {
527 +               dev_err(&bm->pdev->dev, "Failed to read BSI ADC");
528 +               return N810BM_CAP_UNKNOWN;
529 +       }
530 +
531 +       if (adc >= 0x3B5 - hyst && adc <= 0x3B5 + hyst)
532 +               return N810BM_CAP_1500MAH;
533 +
534 +       dev_err(&bm->pdev->dev, "Capacity indicator 0x%X unknown", adc);
535 +
536 +       return N810BM_CAP_UNKNOWN;
537 +}
538 +
539 +/* Convert a battery voltage (in mV) to percentage. */
540 +static unsigned int n810bm_mvolt2percent(unsigned int mv)
541 +{
542 +       const unsigned int minv = 3700;
543 +       const unsigned int maxv = 4150;
544 +       unsigned int percent;
545 +
546 +       mv = clamp(mv, minv, maxv);
547 +       percent = (mv - minv) * 100 / (maxv - minv);
548 +
549 +       return percent;
550 +}
551 +
552 +static void n810bm_start_charge(struct n810bm *bm)
553 +{
554 +       int err;
555 +
556 +       WARN_ON(!bm->battery_present);
557 +       WARN_ON(!bm->charger_present);
558 +
559 +       /* Set PWM to zero */
560 +       bm->active_current_pwm = 0;
561 +       tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
562 +
563 +       /* Charge global enable */
564 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
565 +                     TAHVO_REG_CHGCTL_EN |
566 +                     TAHVO_REG_CHGCTL_PWMOVR |
567 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
568 +                     TAHVO_REG_CHGCTL_EN);
569 +
570 +       WARN_ON((int)bm->capacity <= 0);
571 +       bm->charger.capacity = bm->capacity;
572 +       err = lipocharge_start(&bm->charger);
573 +       WARN_ON(err);
574 +
575 +       /* Initialize current measurement circuitry */
576 +       n810bm_enable_current_measure(bm);
577 +       n810bm_set_current_measure_timer(bm, 250);
578 +
579 +       dev_info(&bm->pdev->dev, "Charging battery");
580 +       n810bm_notify_charger_pwm(bm);
581 +       n810bm_notify_battery_charging(bm);
582 +}
583 +
584 +static void n810bm_stop_charge(struct n810bm *bm)
585 +{
586 +       if (lipocharge_is_charging(&bm->charger)) {
587 +               n810bm_set_current_measure_timer(bm, 0);
588 +               n810bm_disable_current_measure(bm);
589 +       }
590 +       lipocharge_stop(&bm->charger);
591 +
592 +       /* Set PWM to zero */
593 +       bm->active_current_pwm = 0;
594 +       tahvo_write(bm, TAHVO_REG_CHGCURR, bm->active_current_pwm);
595 +
596 +       /* Charge global disable */
597 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
598 +                     TAHVO_REG_CHGCTL_EN |
599 +                     TAHVO_REG_CHGCTL_PWMOVR |
600 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
601 +                     0);
602 +
603 +       dev_info(&bm->pdev->dev, "Not charging battery");
604 +       n810bm_notify_charger_pwm(bm);
605 +       n810bm_notify_battery_charging(bm);
606 +}
607 +
608 +/* Periodic check */
609 +static void n810bm_periodic_check_work(struct work_struct *work)
610 +{
611 +       struct n810bm *bm = container_of(to_delayed_work(work),
612 +                                        struct n810bm, periodic_check_work);
613 +       u16 status;
614 +       bool battery_was_present, charger_was_present;
615 +       bool force_charge = 0;
616 +       int mv;
617 +
618 +       mutex_lock(&bm->mutex);
619 +
620 +       status = retu_read(bm, RETU_REG_STATUS);
621 +       battery_was_present = bm->battery_present;
622 +       charger_was_present = bm->charger_present;
623 +       bm->battery_present = !!(status & RETU_REG_STATUS_BATAVAIL);
624 +       bm->charger_present = !!(status & RETU_REG_STATUS_CHGPLUG);
625 +
626 +       if (bm->battery_present != battery_was_present) {
627 +               /* Battery state changed */
628 +               if (bm->battery_present) {
629 +                       bm->capacity = n810bm_read_batt_capacity(bm);
630 +                       if (bm->capacity == N810BM_CAP_UNKNOWN) {
631 +                               dev_err(&bm->pdev->dev, "Unknown battery detected");
632 +                       } else {
633 +                               dev_info(&bm->pdev->dev, "Detected %u mAh battery",
634 +                                        (unsigned int)bm->capacity);
635 +                       }
636 +               } else {
637 +                       bm->capacity = N810BM_CAP_NONE;
638 +                       dev_info(&bm->pdev->dev, "The main battery was removed");
639 +                       //TODO disable charging
640 +               }
641 +       }
642 +
643 +       if (bm->charger_present != charger_was_present) {
644 +               /* Charger state changed */
645 +               dev_info(&bm->pdev->dev, "The charger was %s",
646 +                        bm->charger_present ? "plugged in" : "removed");
647 +       }
648 +
649 +       if (bm->battery_present && !lipocharge_is_charging(&bm->charger)) {
650 +               /* We're draining the battery */
651 +               mv = n810bm_measure_batt_voltage(bm);
652 +               if (mv < 0)
653 +                       n810bm_emergency(bm, "check timer: Failed to measure");
654 +               if (!bm->charger_present) {
655 +                       if (mv < N810BM_MIN_VOLTAGE_THRES) {
656 +                               n810bm_emergency(bm, "check timer: "
657 +                                       "Minimum voltage threshold reached");
658 +                       }
659 +                       force_charge = 1;
660 +               }
661 +       }
662 +
663 +       if (bm->charger_present && n810bm_known_battery_present(bm)) {
664 +               /* Known battery and charger are connected */
665 +               if (bm->charger_enabled || force_charge) {
666 +                       /* Charger is enabled */
667 +                       if (!lipocharge_is_charging(&bm->charger)) {
668 +                               //TODO start charging, if battery is below some threshold
669 +                               n810bm_start_charge(bm);
670 +                       }
671 +               }
672 +       }
673 +
674 +       if (lipocharge_is_charging(&bm->charger) && !bm->charger_present) {
675 +               /* Charger was unplugged. */
676 +               n810bm_stop_charge(bm);
677 +       }
678 +
679 +       mutex_unlock(&bm->mutex);
680 +       schedule_delayed_work(&bm->periodic_check_work,
681 +                             round_jiffies_relative(N810BM_CHECK_INTERVAL));
682 +}
683 +
684 +static void n810bm_adc_irq_handler(unsigned long data)
685 +{
686 +       struct n810bm *bm = (struct n810bm *)data;
687 +
688 +       retu_ack_irq(RETU_INT_ADCS);
689 +       //TODO
690 +dev_info(&bm->pdev->dev, "ADC interrupt triggered\n");
691 +}
692 +
693 +static void n810bm_tahvo_current_measure_work(struct work_struct *work)
694 +{
695 +       struct n810bm *bm = container_of(work, struct n810bm, currmeas_irq_work);
696 +       int res, ma, mv, temp;
697 +
698 +       mutex_lock(&bm->mutex);
699 +       if (!lipocharge_is_charging(&bm->charger))
700 +               goto out_unlock;
701 +
702 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
703 +                     TAHVO_REG_CHGCTL_PWMOVR |
704 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
705 +                     TAHVO_REG_CHGCTL_PWMOVR);
706 +       ma = n810bm_measure_batt_current(bm);
707 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
708 +                     TAHVO_REG_CHGCTL_PWMOVR |
709 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
710 +                     TAHVO_REG_CHGCTL_PWMOVR |
711 +                     TAHVO_REG_CHGCTL_PWMOVRZERO);
712 +       msleep(10);
713 +       mv = n810bm_measure_batt_voltage(bm);
714 +       tahvo_maskset(bm, TAHVO_REG_CHGCTL,
715 +                     TAHVO_REG_CHGCTL_PWMOVR |
716 +                     TAHVO_REG_CHGCTL_PWMOVRZERO,
717 +                     0);
718 +       temp = n810bm_measure_batt_temp(bm);
719 +       if (WARN_ON(mv < 0))
720 +               goto out_unlock;
721 +       if (WARN_ON(temp < 0))
722 +               goto out_unlock;
723 +
724 +       if (bm->verbose_charge_log) {
725 +               dev_info(&bm->pdev->dev,
726 +                        "Battery charge state: %d mV, %d mA (%s)",
727 +                        mv, ma,
728 +                        (ma <= 0) ? "discharging" : "charging");
729 +       }
730 +       res = lipocharge_update_state(&bm->charger, mv, ma, temp);
731 +       if (res) {
732 +               if (res > 0)
733 +                       dev_info(&bm->pdev->dev, "Battery fully charged");
734 +               n810bm_stop_charge(bm);
735 +       }
736 +out_unlock:
737 +       mutex_unlock(&bm->mutex);
738 +}
739 +
740 +static void n810bm_tahvo_current_measure_irq_handler(unsigned long data)
741 +{
742 +       struct n810bm *bm = (struct n810bm *)data;
743 +
744 +       tahvo_ack_irq(TAHVO_INT_BATCURR);
745 +       schedule_work(&bm->currmeas_irq_work);
746 +}
747 +
748 +#define DEFINE_ATTR_NOTIFY(attr_name)                                          \
749 +       void n810bm_notify_##attr_name(struct n810bm *bm)                       \
750 +       {                                                                       \
751 +               set_bit(N810BM_NOTIFY_##attr_name, &bm->notify_flags);          \
752 +               wmb();                                                          \
753 +               schedule_work(&bm->notify_work);                                \
754 +       }
755 +
756 +#define DEFINE_SHOW_INT_FUNC(name, member)                                     \
757 +       static ssize_t n810bm_attr_##name##_show(struct device *dev,            \
758 +                                                struct device_attribute *attr, \
759 +                                                char *buf)                     \
760 +       {                                                                       \
761 +               struct n810bm *bm = device_to_n810bm(dev);                      \
762 +               ssize_t count;                                                  \
763 +                                                                               \
764 +               mutex_lock(&bm->mutex);                                         \
765 +               count = snprintf(buf, PAGE_SIZE, "%d\n", (int)(bm->member));    \
766 +               mutex_unlock(&bm->mutex);                                       \
767 +                                                                               \
768 +               return count;                                                   \
769 +       }
770 +
771 +#define DEFINE_STORE_INT_FUNC(name, member)                                    \
772 +       static ssize_t n810bm_attr_##name##_store(struct device *dev,           \
773 +                                                 struct device_attribute *attr,\
774 +                                                 const char *buf, size_t count)\
775 +       {                                                                       \
776 +               struct n810bm *bm = device_to_n810bm(dev);                      \
777 +               long val;                                                       \
778 +               int err;                                                        \
779 +                                                                               \
780 +               mutex_lock(&bm->mutex);                                         \
781 +               err = strict_strtol(buf, 0, &val);                              \
782 +               if (!err)                                                       \
783 +                       bm->member = (typeof(bm->member))val;                   \
784 +               mutex_unlock(&bm->mutex);                                       \
785 +                                                                               \
786 +               return err ? err : count;                                       \
787 +       }
788 +
789 +#define DEFINE_ATTR_SHOW_INT(name, member)                                     \
790 +       DEFINE_SHOW_INT_FUNC(name, member)                                      \
791 +       static DEVICE_ATTR(name, S_IRUGO,                                       \
792 +                          n810bm_attr_##name##_show, NULL);
793 +
794 +#define DEFINE_ATTR_SHOW_STORE_INT(name, member)                               \
795 +       DEFINE_SHOW_INT_FUNC(name, member)                                      \
796 +       DEFINE_STORE_INT_FUNC(name, member)                                     \
797 +       static DEVICE_ATTR(name, S_IRUGO | S_IWUSR,                             \
798 +                          n810bm_attr_##name##_show,                           \
799 +                          n810bm_attr_##name##_store);
800 +
801 +DEFINE_ATTR_SHOW_INT(battery_present, battery_present);
802 +DEFINE_ATTR_SHOW_INT(charger_present, charger_present);
803 +DEFINE_ATTR_SHOW_INT(charger_pwm, active_current_pwm);
804 +static DEFINE_ATTR_NOTIFY(charger_pwm);
805 +DEFINE_ATTR_SHOW_STORE_INT(charger_enable, charger_enabled);
806 +DEFINE_ATTR_SHOW_STORE_INT(charger_verbose, verbose_charge_log);
807 +
808 +static ssize_t n810bm_attr_battery_charging(struct device *dev,
809 +                                           struct device_attribute *attr,
810 +                                           char *buf)
811 +{
812 +       struct n810bm *bm = device_to_n810bm(dev);
813 +       ssize_t count;
814 +
815 +       mutex_lock(&bm->mutex);
816 +       count = snprintf(buf, PAGE_SIZE, "%d\n",
817 +                        (int)lipocharge_is_charging(&bm->charger));
818 +       mutex_unlock(&bm->mutex);
819 +
820 +       return count;
821 +}
822 +static DEVICE_ATTR(battery_charging, S_IRUGO,
823 +                  n810bm_attr_battery_charging, NULL);
824 +static DEFINE_ATTR_NOTIFY(battery_charging);
825 +
826 +static ssize_t n810bm_attr_battery_level_show(struct device *dev,
827 +                                             struct device_attribute *attr,
828 +                                             char *buf)
829 +{
830 +       struct n810bm *bm = device_to_n810bm(dev);
831 +       ssize_t count = -ENODEV;
832 +       int millivolt;
833 +
834 +       mutex_lock(&bm->mutex);
835 +       if (!bm->battery_present || lipocharge_is_charging(&bm->charger))
836 +               millivolt = 0;
837 +       else
838 +               millivolt = n810bm_measure_batt_voltage(bm);
839 +       if (millivolt >= 0) {
840 +               count = snprintf(buf, PAGE_SIZE, "%u\n",
841 +                                n810bm_mvolt2percent(millivolt));
842 +       }
843 +       mutex_unlock(&bm->mutex);
844 +
845 +       return count;
846 +}
847 +static DEVICE_ATTR(battery_level, S_IRUGO,
848 +                  n810bm_attr_battery_level_show, NULL);
849 +
850 +static ssize_t n810bm_attr_battery_capacity_show(struct device *dev,
851 +                                                struct device_attribute *attr,
852 +                                                char *buf)
853 +{
854 +       struct n810bm *bm = device_to_n810bm(dev);
855 +       ssize_t count;
856 +       int capacity = 0;
857 +
858 +       mutex_lock(&bm->mutex);
859 +       if (n810bm_known_battery_present(bm))
860 +               capacity = (int)bm->capacity;
861 +       count = snprintf(buf, PAGE_SIZE, "%d\n", capacity);
862 +       mutex_unlock(&bm->mutex);
863 +
864 +       return count;
865 +}
866 +static DEVICE_ATTR(battery_capacity, S_IRUGO,
867 +                  n810bm_attr_battery_capacity_show, NULL);
868 +
869 +static ssize_t n810bm_attr_battery_temp_show(struct device *dev,
870 +                                            struct device_attribute *attr,
871 +                                            char *buf)
872 +{
873 +       struct n810bm *bm = device_to_n810bm(dev);
874 +       ssize_t count = -ENODEV;
875 +       int k;
876 +
877 +       mutex_lock(&bm->mutex);
878 +       k = n810bm_measure_batt_temp(bm);
879 +       if (k >= 0)
880 +               count = snprintf(buf, PAGE_SIZE, "%d\n", k);
881 +       mutex_unlock(&bm->mutex);
882 +
883 +       return count;
884 +}
885 +static DEVICE_ATTR(battery_temp, S_IRUGO,
886 +                  n810bm_attr_battery_temp_show, NULL);
887 +
888 +static ssize_t n810bm_attr_charger_voltage_show(struct device *dev,
889 +                                               struct device_attribute *attr,
890 +                                               char *buf)
891 +{
892 +       struct n810bm *bm = device_to_n810bm(dev);
893 +       ssize_t count = -ENODEV;
894 +       int mv = 0;
895 +
896 +       mutex_lock(&bm->mutex);
897 +       if (bm->charger_present)
898 +               mv = n810bm_measure_charger_voltage(bm);
899 +       if (mv >= 0)
900 +               count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
901 +       mutex_unlock(&bm->mutex);
902 +
903 +       return count;
904 +}
905 +static DEVICE_ATTR(charger_voltage, S_IRUGO,
906 +                  n810bm_attr_charger_voltage_show, NULL);
907 +
908 +static ssize_t n810bm_attr_backup_battery_voltage_show(struct device *dev,
909 +                                                      struct device_attribute *attr,
910 +                                                      char *buf)
911 +{
912 +       struct n810bm *bm = device_to_n810bm(dev);
913 +       ssize_t count = -ENODEV;
914 +       int mv;
915 +
916 +       mutex_lock(&bm->mutex);
917 +       mv = n810bm_measure_backup_batt_voltage(bm);
918 +       if (mv >= 0)
919 +               count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
920 +       mutex_unlock(&bm->mutex);
921 +
922 +       return count;
923 +}
924 +static DEVICE_ATTR(backup_battery_voltage, S_IRUGO,
925 +                  n810bm_attr_backup_battery_voltage_show, NULL);
926 +
927 +static ssize_t n810bm_attr_battery_current_show(struct device *dev,
928 +                                               struct device_attribute *attr,
929 +                                               char *buf)
930 +{
931 +       struct n810bm *bm = device_to_n810bm(dev);
932 +       ssize_t count = -ENODEV;
933 +       int ma = 0;
934 +
935 +       mutex_lock(&bm->mutex);
936 +       if (bm->battery_present)
937 +               ma = n810bm_measure_batt_current_async(bm);
938 +       count = snprintf(buf, PAGE_SIZE, "%d\n", ma);
939 +       mutex_unlock(&bm->mutex);
940 +
941 +       return count;
942 +}
943 +static DEVICE_ATTR(battery_current, S_IRUGO,
944 +                  n810bm_attr_battery_current_show, NULL);
945 +
946 +static const struct device_attribute *n810bm_attrs[] = {
947 +       &dev_attr_battery_present,
948 +       &dev_attr_battery_level,
949 +       &dev_attr_battery_charging,
950 +       &dev_attr_battery_current,
951 +       &dev_attr_battery_capacity,
952 +       &dev_attr_battery_temp,
953 +       &dev_attr_backup_battery_voltage,
954 +       &dev_attr_charger_present,
955 +       &dev_attr_charger_verbose,
956 +       &dev_attr_charger_voltage,
957 +       &dev_attr_charger_enable,
958 +       &dev_attr_charger_pwm,
959 +};
960 +
961 +static void n810bm_notify_work(struct work_struct *work)
962 +{
963 +       struct n810bm *bm = container_of(work, struct n810bm, notify_work);
964 +       unsigned long notify_flags;
965 +
966 +       notify_flags = xchg(&bm->notify_flags, 0);
967 +       mb();
968 +
969 +#define do_notify(attr_name)                                           \
970 +       do {                                                            \
971 +               if (notify_flags & (1 << N810BM_NOTIFY_##attr_name)) {  \
972 +                       sysfs_notify(&bm->pdev->dev.kobj, NULL,         \
973 +                                    dev_attr_##attr_name.attr.name);   \
974 +               }                                                       \
975 +       } while (0)
976 +
977 +       do_notify(battery_charging);
978 +       do_notify(charger_pwm);
979 +}
980 +
981 +static int n810bm_charger_set_current_pwm(struct lipocharge *c,
982 +                                         unsigned int duty_cycle)
983 +{
984 +       struct n810bm *bm = container_of(c, struct n810bm, charger);
985 +       int err = -EINVAL;
986 +
987 +       WARN_ON(!mutex_is_locked(&bm->mutex));
988 +       if (WARN_ON(duty_cycle > 0xFF))
989 +               goto out;
990 +       if (WARN_ON(!bm->charger_enabled))
991 +               goto out;
992 +       if (WARN_ON(!bm->battery_present || !bm->charger_present))
993 +               goto out;
994 +
995 +       if (duty_cycle != bm->active_current_pwm) {
996 +               bm->active_current_pwm = duty_cycle;
997 +               tahvo_write(bm, TAHVO_REG_CHGCURR, duty_cycle);
998 +               n810bm_notify_charger_pwm(bm);
999 +       }
1000 +
1001 +       err = 0;
1002 +out:
1003 +
1004 +       return err;
1005 +}
1006 +
1007 +static void n810bm_charger_emergency(struct lipocharge *c)
1008 +{
1009 +       struct n810bm *bm = container_of(c, struct n810bm, charger);
1010 +
1011 +       n810bm_emergency(bm, "Battery charger fault");
1012 +}
1013 +
1014 +static void n810bm_hw_exit(struct n810bm *bm)
1015 +{
1016 +       n810bm_stop_charge(bm);
1017 +       retu_write(bm, RETU_REG_ADCSCR, 0);
1018 +}
1019 +
1020 +static int n810bm_hw_init(struct n810bm *bm)
1021 +{
1022 +       int err;
1023 +
1024 +       err = n810bm_check_adc_sanity(bm);
1025 +       if (err)
1026 +               return err;
1027 +
1028 +       n810bm_stop_charge(bm);
1029 +
1030 +       return 0;
1031 +}
1032 +
1033 +static void n810bm_cancel_and_flush_work(struct n810bm *bm)
1034 +{
1035 +       cancel_delayed_work_sync(&bm->periodic_check_work);
1036 +       cancel_work_sync(&bm->notify_work);
1037 +       cancel_work_sync(&bm->currmeas_irq_work);
1038 +       flush_scheduled_work();
1039 +}
1040 +
1041 +static int n810bm_device_init(struct n810bm *bm)
1042 +{
1043 +       int attr_index;
1044 +       int err;
1045 +
1046 +       bm->charger.rate = LIPORATE_p6C;
1047 +       bm->charger.top_voltage = 4100;
1048 +       bm->charger.duty_cycle_max = 0xFF;
1049 +       bm->charger.set_current_pwm = n810bm_charger_set_current_pwm;
1050 +       bm->charger.emergency = n810bm_charger_emergency;
1051 +       lipocharge_init(&bm->charger, &bm->pdev->dev);
1052 +
1053 +       err = n810bm_hw_init(bm);
1054 +       if (err)
1055 +               goto error;
1056 +       for (attr_index = 0; attr_index < ARRAY_SIZE(n810bm_attrs); attr_index++) {
1057 +               err = device_create_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1058 +               if (err)
1059 +                       goto err_unwind_attrs;
1060 +       }
1061 +       err = retu_request_irq(RETU_INT_ADCS,
1062 +                              n810bm_adc_irq_handler,
1063 +                              (unsigned long)bm, "n810bm");
1064 +       if (err)
1065 +               goto err_unwind_attrs;
1066 +       err = tahvo_request_irq(TAHVO_INT_BATCURR,
1067 +                               n810bm_tahvo_current_measure_irq_handler,
1068 +                               (unsigned long)bm, "n810bm");
1069 +       if (err)
1070 +               goto err_free_retu_irq;
1071 +       tahvo_disable_irq(TAHVO_INT_BATCURR);
1072 +
1073 +       schedule_delayed_work(&bm->periodic_check_work,
1074 +                             round_jiffies_relative(N810BM_CHECK_INTERVAL));
1075 +
1076 +       bm->initialized = 1;
1077 +       dev_info(&bm->pdev->dev, "Battery management initialized");
1078 +
1079 +       return 0;
1080 +
1081 +err_free_retu_irq:
1082 +       retu_free_irq(RETU_INT_ADCS);
1083 +err_unwind_attrs:
1084 +       for (attr_index--; attr_index >= 0; attr_index--)
1085 +               device_remove_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1086 +/*err_exit:*/
1087 +       n810bm_hw_exit(bm);
1088 +error:
1089 +       n810bm_cancel_and_flush_work(bm);
1090 +
1091 +       return err;
1092 +}
1093 +
1094 +static void n810bm_device_exit(struct n810bm *bm)
1095 +{
1096 +       int i;
1097 +
1098 +       if (!bm->initialized)
1099 +               return;
1100 +
1101 +       lipocharge_exit(&bm->charger);
1102 +       tahvo_free_irq(TAHVO_INT_BATCURR);
1103 +       retu_free_irq(RETU_INT_ADCS);
1104 +       for (i = 0; i < ARRAY_SIZE(n810bm_attrs); i++)
1105 +               device_remove_file(&bm->pdev->dev, n810bm_attrs[i]);
1106 +
1107 +       n810bm_cancel_and_flush_work(bm);
1108 +
1109 +       n810bm_hw_exit(bm);
1110 +       release_firmware(bm->pmm_block);
1111 +
1112 +       bm->initialized = 0;
1113 +}
1114 +
1115 +static void n810bm_pmm_block_found(const struct firmware *fw, void *context)
1116 +{
1117 +       struct n810bm *bm = context;
1118 +       int err;
1119 +
1120 +       if (!fw) {
1121 +               dev_err(&bm->pdev->dev,
1122 +                       "CAL PMM block image file not found");
1123 +               goto error;
1124 +       }
1125 +       if (fw->size != N810BM_PMM_BLOCK_SIZE ||
1126 +           memcmp(fw->data, "BME-PMM-BLOCK01", 15) != 0) {
1127 +               dev_err(&bm->pdev->dev,
1128 +                       "CAL PMM block image file has an invalid format");
1129 +               goto error;
1130 +       }
1131 +
1132 +       bm->pmm_block = fw;
1133 +       err = n810bm_device_init(bm);
1134 +       if (err) {
1135 +               dev_err(&bm->pdev->dev,
1136 +                       "Failed to initialized battery management (%d)", err);
1137 +               goto error;
1138 +       }
1139 +
1140 +       return;
1141 +error:
1142 +       release_firmware(fw);
1143 +}
1144 +
1145 +static int __devinit n810bm_probe(struct platform_device *pdev)
1146 +{
1147 +       struct n810bm *bm;
1148 +       int err;
1149 +
1150 +       bm = kzalloc(sizeof(*bm), GFP_KERNEL);
1151 +       if (!bm)
1152 +               return -ENOMEM;
1153 +       bm->pdev = pdev;
1154 +       platform_set_drvdata(pdev, bm);
1155 +       mutex_init(&bm->mutex);
1156 +       INIT_DELAYED_WORK(&bm->periodic_check_work, n810bm_periodic_check_work);
1157 +       INIT_WORK(&bm->notify_work, n810bm_notify_work);
1158 +       INIT_WORK(&bm->currmeas_irq_work, n810bm_tahvo_current_measure_work);
1159 +
1160 +       dev_info(&bm->pdev->dev, "Requesting CAL BME PMM block firmware file "
1161 +                N810BM_PMM_BLOCK_FILENAME);
1162 +       err = request_firmware_nowait(THIS_MODULE, 1,
1163 +                                     N810BM_PMM_BLOCK_FILENAME,
1164 +                                     &bm->pdev->dev, GFP_KERNEL,
1165 +                                     bm, n810bm_pmm_block_found);
1166 +       if (err) {
1167 +               dev_err(&bm->pdev->dev,
1168 +                       "Failed to request CAL PMM block image file (%d)", err);
1169 +               goto err_free;
1170 +       }
1171 +
1172 +       return 0;
1173 +
1174 +err_free:
1175 +       kfree(bm);
1176 +
1177 +       return err;
1178 +}
1179 +
1180 +static int __devexit n810bm_remove(struct platform_device *pdev)
1181 +{
1182 +       struct n810bm *bm = platform_get_drvdata(pdev);
1183 +
1184 +       n810bm_device_exit(bm);
1185 +
1186 +       kfree(bm);
1187 +       platform_set_drvdata(pdev, NULL);
1188 +
1189 +       return 0;
1190 +}
1191 +
1192 +static struct platform_driver n810bm_driver = {
1193 +       .remove         = __devexit_p(n810bm_remove),
1194 +       .driver         = {
1195 +               .name   = "n810bm",
1196 +       }
1197 +};
1198 +
1199 +static int __init n810bm_modinit(void)
1200 +{
1201 +       return platform_driver_probe(&n810bm_driver, n810bm_probe);
1202 +}
1203 +module_init(n810bm_modinit);
1204 +
1205 +static void __exit n810bm_modexit(void)
1206 +{
1207 +       platform_driver_unregister(&n810bm_driver);
1208 +}
1209 +module_exit(n810bm_modexit);
1210 +
1211 +MODULE_DESCRIPTION("Nokia n810 battery management");
1212 +MODULE_FIRMWARE(N810BM_PMM_BLOCK_FILENAME);
1213 +MODULE_LICENSE("GPL");
1214 +MODULE_AUTHOR("Michael Buesch");
1215 Index: linux-2.6.37/drivers/cbus/retu.c
1216 ===================================================================
1217 --- linux-2.6.37.orig/drivers/cbus/retu.c       2011-02-06 14:05:49.829387442 +0100
1218 +++ linux-2.6.37/drivers/cbus/retu.c    2011-02-08 17:33:18.981369017 +0100
1219 @@ -85,10 +85,10 @@
1220   *
1221   * This function writes a value to the specified register
1222   */
1223 -void retu_write_reg(int reg, u16 val)
1224 +int retu_write_reg(int reg, u16 val)
1225  {
1226         BUG_ON(!retu_initialized);
1227 -       cbus_write_reg(cbus_host, RETU_ID, reg, val);
1228 +       return cbus_write_reg(cbus_host, RETU_ID, reg, val);
1229  }
1230  
1231  void retu_set_clear_reg_bits(int reg, u16 set, u16 clear)
1232 @@ -459,6 +459,7 @@
1233  EXPORT_SYMBOL(retu_ack_irq);
1234  EXPORT_SYMBOL(retu_read_reg);
1235  EXPORT_SYMBOL(retu_write_reg);
1236 +EXPORT_SYMBOL(retu_read_adc);
1237  
1238  subsys_initcall(retu_init);
1239  module_exit(retu_exit);
1240 Index: linux-2.6.37/drivers/cbus/retu.h
1241 ===================================================================
1242 --- linux-2.6.37.orig/drivers/cbus/retu.h       2011-02-06 14:05:49.829387442 +0100
1243 +++ linux-2.6.37/drivers/cbus/retu.h    2011-02-06 14:05:49.886395793 +0100
1244 @@ -40,6 +40,8 @@
1245  #define RETU_REG_CTRL_CLR      0x0f    /* Regulator clear register */
1246  #define RETU_REG_CTRL_SET      0x10    /* Regulator set register */
1247  #define RETU_REG_STATUS                0x16    /* Status register */
1248 +#define  RETU_REG_STATUS_BATAVAIL      0x0100 /* Battery available */
1249 +#define  RETU_REG_STATUS_CHGPLUG       0x1000 /* Charger is plugged in */
1250  #define RETU_REG_WATCHDOG      0x17    /* Watchdog register */
1251  #define RETU_REG_AUDTXR                0x18    /* Audio Codec Tx register */
1252  #define RETU_REG_MAX           0x1f
1253 @@ -57,8 +59,25 @@
1254  
1255  #define        MAX_RETU_IRQ_HANDLERS   16
1256  
1257 +/* ADC channels */
1258 +#define RETU_ADC_GND           0x00 /* Ground */
1259 +#define RETU_ADC_BSI           0x01 /* Battery Size Indicator */
1260 +#define RETU_ADC_BATTEMP       0x02 /* Battery temperature */
1261 +#define RETU_ADC_CHGVOLT       0x03 /* Charger voltage */
1262 +#define RETU_ADC_HEADSET       0x04 /* Headset detection */
1263 +#define RETU_ADC_HOOKDET       0x05 /* Hook detection */
1264 +#define RETU_ADC_RFGP          0x06 /* RF GP */
1265 +#define RETU_ADC_WBTX          0x07 /* Wideband Tx detection */
1266 +#define RETU_ADC_BATTVOLT      0x08 /* Battery voltage measurement */
1267 +#define RETU_ADC_GND2          0x09 /* Ground */
1268 +#define RETU_ADC_LIGHTSENS     0x0A /* Light sensor */
1269 +#define RETU_ADC_LIGHTTEMP     0x0B /* Light sensor temperature */
1270 +#define RETU_ADC_BKUPVOLT      0x0C /* Backup battery voltage */
1271 +#define RETU_ADC_TEMP          0x0D /* RETU temperature */
1272 +
1273 +
1274  int retu_read_reg(int reg);
1275 -void retu_write_reg(int reg, u16 val);
1276 +int retu_write_reg(int reg, u16 val);
1277  void retu_set_clear_reg_bits(int reg, u16 set, u16 clear);
1278  int retu_read_adc(int channel);
1279  int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
1280 Index: linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c
1281 ===================================================================
1282 --- linux-2.6.37.orig/arch/arm/mach-omap2/board-n8x0.c  2011-02-06 14:05:49.815385390 +0100
1283 +++ linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c       2011-02-06 14:05:49.886395793 +0100
1284 @@ -907,6 +907,17 @@
1285                                     ARRAY_SIZE(n8x0_gpio_switches));
1286  }
1287  
1288 +static struct platform_device n810_bm_device = {
1289 +       .name           = "n810bm",
1290 +       .id             = -1,
1291 +};
1292 +
1293 +static void __init n810_bm_init(void)
1294 +{
1295 +       if (platform_device_register(&n810_bm_device))
1296 +               BUG();
1297 +}
1298 +
1299  static void __init n8x0_init_machine(void)
1300  {
1301         omap2420_mux_init(board_mux, OMAP_PACKAGE_ZAC);
1302 @@ -933,6 +944,8 @@
1303         n8x0_onenand_init();
1304         n8x0_mmc_init();
1305         n8x0_usb_init();
1306 +
1307 +       n810_bm_init();
1308  }
1309  
1310  MACHINE_START(NOKIA_N800, "Nokia N800")
1311 Index: linux-2.6.37/drivers/cbus/lipocharge.c
1312 ===================================================================
1313 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
1314 +++ linux-2.6.37/drivers/cbus/lipocharge.c      2011-02-09 12:42:58.243147563 +0100
1315 @@ -0,0 +1,183 @@
1316 +/*
1317 + *   Generic LIPO battery charger
1318 + *
1319 + *   Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
1320 + *
1321 + *   This program is free software; you can redistribute it and/or
1322 + *   modify it under the terms of the GNU General Public License
1323 + *   as published by the Free Software Foundation; either version 2
1324 + *   of the License, or (at your option) any later version.
1325 + *
1326 + *   This program is distributed in the hope that it will be useful,
1327 + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
1328 + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1329 + *   GNU General Public License for more details.
1330 + */
1331 +
1332 +#define DEBUG
1333 +
1334 +#include "lipocharge.h"
1335 +
1336 +#include <linux/slab.h>
1337 +
1338 +
1339 +/* Hysteresis constants */
1340 +#define CURRENT_HYST           30 /* mA */
1341 +#define VOLTAGE_HYST           10 /* mV */
1342 +
1343 +/* Threshold constants */
1344 +#define FINISH_CURRENT_PERCENT 3
1345 +
1346 +
1347 +/* Returns the requested first-stage charge current in mA */
1348 +static inline unsigned int get_stage1_charge_current(struct lipocharge *c)
1349 +{
1350 +       /* current = (capacity * C) */
1351 +       return c->capacity * c->rate / 1000;
1352 +}
1353 +
1354 +void lipocharge_init(struct lipocharge *c, struct device *dev)
1355 +{
1356 +       c->dev = dev;
1357 +       c->state = LIPO_IDLE;
1358 +}
1359 +
1360 +void lipocharge_exit(struct lipocharge *c)
1361 +{
1362 +       c->state = LIPO_IDLE;
1363 +}
1364 +
1365 +int lipocharge_start(struct lipocharge *c)
1366 +{
1367 +       int err;
1368 +
1369 +       if (c->state != LIPO_IDLE)
1370 +               return -EBUSY;
1371 +       if (!c->set_current_pwm || !c->emergency)
1372 +               return -EINVAL;
1373 +       if (!c->top_voltage || c->top_voltage > 4200)
1374 +               return -EINVAL;
1375 +
1376 +       c->active_duty_cycle = 0;
1377 +       err = c->set_current_pwm(c, c->active_duty_cycle);
1378 +       if (err)
1379 +               return err;
1380 +       c->state = LIPO_FIRST_STAGE;
1381 +
1382 +       return 0;
1383 +}
1384 +
1385 +void lipocharge_stop(struct lipocharge *c)
1386 +{
1387 +       if (c->state == LIPO_IDLE)
1388 +               return;
1389 +       c->state = LIPO_IDLE;
1390 +}
1391 +
1392 +static int lipocharge_increase_current(struct lipocharge *c,
1393 +                                      unsigned int inc_permille)
1394 +{
1395 +       int old_pwm, new_pwm;
1396 +
1397 +       if (c->active_duty_cycle >= c->duty_cycle_max)
1398 +               return 0;
1399 +
1400 +       old_pwm = c->active_duty_cycle;
1401 +       new_pwm = old_pwm + (c->duty_cycle_max * inc_permille / 1000);
1402 +       new_pwm = min(new_pwm, (int)c->duty_cycle_max);
1403 +       c->active_duty_cycle = new_pwm;
1404 +
1405 +       dev_dbg(c->dev, "lipo: Increasing duty_cycle by "
1406 +               "%u permille (0x%02X -> 0x%02X)",
1407 +               inc_permille, old_pwm, new_pwm);
1408 +
1409 +       return c->set_current_pwm(c, c->active_duty_cycle);
1410 +}
1411 +
1412 +static int lipocharge_decrease_current(struct lipocharge *c,
1413 +                                      unsigned int dec_permille)
1414 +{
1415 +       int old_pwm, new_pwm;
1416 +
1417 +       if (c->active_duty_cycle <= 0)
1418 +               return 0;
1419 +
1420 +       old_pwm = c->active_duty_cycle;
1421 +       new_pwm = old_pwm - (c->duty_cycle_max * dec_permille / 1000);
1422 +       new_pwm = max(0, new_pwm);
1423 +       c->active_duty_cycle = new_pwm;
1424 +
1425 +       dev_dbg(c->dev, "lipo: Decreasing duty_cycle by "
1426 +               "%u permille (0x%02X -> 0x%02X)",
1427 +               dec_permille, old_pwm, new_pwm);
1428 +
1429 +       return c->set_current_pwm(c, c->active_duty_cycle);
1430 +}
1431 +
1432 +/** lipocharge_update_state - Update the charge state
1433 + * @c: The context.
1434 + * @voltage_mV: The measured battery voltage.
1435 + * @current_mA: The measured charge current.
1436 + *             negative -> drain.
1437 + *             positive -> charge.
1438 + * @temp_K: Battery temperature in K.
1439 + *
1440 + * Returns 0 on success, -1 on error.
1441 + * Returns 1, if the charging process is finished.
1442 + */
1443 +int lipocharge_update_state(struct lipocharge *c,
1444 +                           unsigned int voltage_mV,
1445 +                           int current_mA,
1446 +                           unsigned int temp_K)
1447 +{
1448 +       int requested_current, current_diff;
1449 +       int err;
1450 +       unsigned int permille;
1451 +
1452 +       //TODO temp
1453 +
1454 +restart:
1455 +       switch (c->state) {
1456 +       case LIPO_IDLE:
1457 +               dev_err(c->dev, "%s: called while idle", __func__);
1458 +               return -EINVAL;
1459 +       case LIPO_FIRST_STAGE:  /* Constant current */
1460 +//printk("GOT %u %d %u\n", voltage_mV, current_mA, temp_K);
1461 +               if (voltage_mV >= c->top_voltage) {
1462 +                       /* Float voltage reached.
1463 +                        * Switch charger mode to "constant current" */
1464 +                       c->state = LIPO_SECOND_STAGE;
1465 +                       dev_dbg(c->dev, "Switched to second charging stage.");
1466 +                       goto restart;
1467 +               }
1468 +               /* Float voltage not reached, yet.
1469 +                * Try to get the requested constant current. */
1470 +               requested_current = get_stage1_charge_current(c);
1471 +               if (current_mA < 0)
1472 +                       current_mA = 0;
1473 +               current_diff = requested_current - current_mA;
1474 +               if (abs(requested_current - current_mA) > CURRENT_HYST) {
1475 +                       if (current_diff > 0) {
1476 +                               /* Increase current */
1477 +                               permille = current_diff * 1000 / requested_current;
1478 +                               permille /= 2;
1479 +                               err = lipocharge_increase_current(c, permille);
1480 +                               if (err)
1481 +                                       return err;
1482 +                       } else {
1483 +                               /* Decrease current */
1484 +                               permille = (-current_diff) * 1000 / requested_current;
1485 +                               permille /= 2;
1486 +                               err = lipocharge_decrease_current(c, permille);
1487 +                               if (err)
1488 +                                       return err;
1489 +                       }
1490 +               }
1491 +               break;
1492 +       case LIPO_SECOND_STAGE: /* Constant voltage */
1493 +               //TODO
1494 +               break;
1495 +       }
1496 +
1497 +       return 0;
1498 +}
1499 Index: linux-2.6.37/drivers/cbus/lipocharge.h
1500 ===================================================================
1501 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
1502 +++ linux-2.6.37/drivers/cbus/lipocharge.h      2011-02-07 20:07:29.669098631 +0100
1503 @@ -0,0 +1,60 @@
1504 +#ifndef LIPOCHARGE_H_
1505 +#define LIPOCHARGE_H_
1506 +
1507 +#include <linux/types.h>
1508 +#include <linux/device.h>
1509 +
1510 +
1511 +#define LIPORATE(a,b)  (((a) * 1000) + ((b) * 100))
1512 +#define LIPORATE_p6C   LIPORATE(0,6)   /* 0.6C */
1513 +
1514 +enum lipocharge_state {
1515 +       LIPO_IDLE,              /* Not charging */
1516 +       LIPO_FIRST_STAGE,       /* Charging: constant current */
1517 +       LIPO_SECOND_STAGE,      /* Charging: constant voltage */
1518 +};
1519 +
1520 +/** struct lipocharge - A generic LIPO charger
1521 + *
1522 + * @capacity: Battery capacity in mAh.
1523 + * @rate: Charge rate.
1524 + * @top_voltage: Fully charged voltage, in mV.
1525 + * @duty_cycle_max: Max value for duty_cycle.
1526 + *
1527 + * @set_charge_current: Set the charge current PWM duty cycle.
1528 + * @emergency: Something went wrong. Force shutdown.
1529 + */
1530 +struct lipocharge {
1531 +       unsigned int capacity;
1532 +       unsigned int rate;
1533 +       unsigned int top_voltage;
1534 +       unsigned int duty_cycle_max;
1535 +
1536 +       int (*set_current_pwm)(struct lipocharge *c, unsigned int duty_cycle);
1537 +       void (*emergency)(struct lipocharge *c);
1538 +
1539 +       /* internal */
1540 +       struct device *dev;
1541 +       enum lipocharge_state state;
1542 +       unsigned int active_duty_cycle;
1543 +
1544 +       //TODO implement timer to cut power after maximum charge time.
1545 +};
1546 +
1547 +void lipocharge_init(struct lipocharge *c, struct device *dev);
1548 +void lipocharge_exit(struct lipocharge *c);
1549 +
1550 +int lipocharge_start(struct lipocharge *c);
1551 +void lipocharge_stop(struct lipocharge *c);
1552 +
1553 +int lipocharge_update_state(struct lipocharge *c,
1554 +                           unsigned int voltage_mV,
1555 +                           int current_mA,
1556 +                           unsigned int temp_K);
1557 +
1558 +static inline bool lipocharge_is_charging(struct lipocharge *c)
1559 +{
1560 +       return (c->state != LIPO_IDLE);
1561 +}
1562 +
1563 +#endif /* LIPOCHARGE_H_ */
1564 Index: linux-2.6.37/drivers/cbus/tahvo.h
1565 ===================================================================
1566 --- linux-2.6.37.orig/drivers/cbus/tahvo.h      2011-02-06 14:05:49.830387588 +0100
1567 +++ linux-2.6.37/drivers/cbus/tahvo.h   2011-02-06 16:22:25.902331536 +0100
1568 @@ -30,17 +30,28 @@
1569  #define TAHVO_REG_IDR          0x01    /* Interrupt ID */
1570  #define TAHVO_REG_IDSR         0x02    /* Interrupt status */
1571  #define TAHVO_REG_IMR          0x03    /* Interrupt mask */
1572 +#define TAHVO_REG_CHGCURR      0x04    /* Charge current control PWM (8-bit) */
1573  #define TAHVO_REG_LEDPWMR      0x05    /* LED PWM */
1574  #define TAHVO_REG_USBR         0x06    /* USB control */
1575 +#define TAHVO_REG_CHGCTL       0x08    /* Charge control register */
1576 +#define  TAHVO_REG_CHGCTL_EN           0x0001  /* Global charge enable */
1577 +#define  TAHVO_REG_CHGCTL_PWMOVR       0x0004  /* PWM override. Force charge PWM to 0%/100% duty cycle. */
1578 +#define  TAHVO_REG_CHGCTL_PWMOVRZERO   0x0008  /* If set, PWM override is 0% (If unset -> 100%) */
1579 +#define  TAHVO_REG_CHGCTL_CURMEAS      0x0040  /* Enable battery current measurement. */
1580 +#define  TAHVO_REG_CHGCTL_CURTIMRST    0x0080  /* Current measure timer reset. */
1581 +#define TAHVO_REG_BATCURRTIMER 0x0c    /* Battery current measure timer (8-bit) */
1582 +#define TAHVO_REG_BATCURR      0x0d    /* Battery (dis)charge current (signed 16-bit) */
1583 +
1584  #define TAHVO_REG_MAX          0x0d
1585  
1586  /* Interrupt sources */
1587  #define TAHVO_INT_VBUSON       0
1588 +#define TAHVO_INT_BATCURR      7 /* Battery current measure timer */
1589  
1590  #define MAX_TAHVO_IRQ_HANDLERS 8
1591  
1592  int tahvo_read_reg(int reg);
1593 -void tahvo_write_reg(int reg, u16 val);
1594 +int tahvo_write_reg(int reg, u16 val);
1595  void tahvo_set_clear_reg_bits(int reg, u16 set, u16 clear);
1596  int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
1597  void tahvo_free_irq(int id);
1598 Index: linux-2.6.37/drivers/cbus/tahvo.c
1599 ===================================================================
1600 --- linux-2.6.37.orig/drivers/cbus/tahvo.c      2011-02-06 14:05:49.830387588 +0100
1601 +++ linux-2.6.37/drivers/cbus/tahvo.c   2011-02-06 14:05:49.886395793 +0100
1602 @@ -85,10 +85,10 @@
1603   *
1604   * This function writes a value to the specified register
1605   */
1606 -void tahvo_write_reg(int reg, u16 val)
1607 +int tahvo_write_reg(int reg, u16 val)
1608  {
1609         BUG_ON(!tahvo_initialized);
1610 -       cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
1611 +       return cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
1612  }
1613  
1614  /**
1615 Index: linux-2.6.37/drivers/cbus/cbus.c
1616 ===================================================================
1617 --- linux-2.6.37.orig/drivers/cbus/cbus.c       2011-02-08 17:34:34.988926130 +0100
1618 +++ linux-2.6.37/drivers/cbus/cbus.c    2011-02-08 17:38:16.980407594 +0100
1619 @@ -31,6 +31,7 @@
1620  #include <linux/gpio.h>
1621  #include <linux/platform_device.h>
1622  #include <linux/slab.h>
1623 +#include <linux/reboot.h>
1624  
1625  #include <asm/io.h>
1626  #include <asm/mach-types.h>
1627 @@ -301,6 +302,13 @@
1628  }
1629  module_exit(cbus_bus_exit);
1630  
1631 +void cbus_emergency(void)
1632 +{
1633 +       machine_power_off();
1634 +       panic("cbus: Failed to halt machine in emergency state\n");
1635 +}
1636 +EXPORT_SYMBOL(cbus_emergency);
1637 +
1638  MODULE_DESCRIPTION("CBUS serial protocol");
1639  MODULE_LICENSE("GPL");
1640  MODULE_AUTHOR("Juha Yrjölä");
1641 Index: linux-2.6.37/drivers/cbus/cbus.h
1642 ===================================================================
1643 --- linux-2.6.37.orig/drivers/cbus/cbus.h       2011-02-08 17:36:40.172074049 +0100
1644 +++ linux-2.6.37/drivers/cbus/cbus.h    2011-02-08 17:41:32.680647478 +0100
1645 @@ -33,4 +33,6 @@
1646  extern int cbus_read_reg(struct cbus_host *host, int dev, int reg);
1647  extern int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val);
1648  
1649 +NORET_TYPE void cbus_emergency(void) ATTRIB_NORET;
1650 +
1651  #endif /* __DRIVERS_CBUS_CBUS_H */