n810bm: Set minimum voltage threshold to 3200
[openwrt/svn-archive/archive.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 19:05:18.435536304 +0100
45 @@ -0,0 +1,1168 @@
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 3200 /* 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 + int mv;
616 +
617 + mutex_lock(&bm->mutex);
618 +
619 + status = retu_read(bm, RETU_REG_STATUS);
620 + battery_was_present = bm->battery_present;
621 + charger_was_present = bm->charger_present;
622 + bm->battery_present = !!(status & RETU_REG_STATUS_BATAVAIL);
623 + bm->charger_present = !!(status & RETU_REG_STATUS_CHGPLUG);
624 +
625 + if (bm->battery_present != battery_was_present) {
626 + /* Battery state changed */
627 + if (bm->battery_present) {
628 + bm->capacity = n810bm_read_batt_capacity(bm);
629 + if (bm->capacity == N810BM_CAP_UNKNOWN) {
630 + dev_err(&bm->pdev->dev, "Unknown battery detected");
631 + } else {
632 + dev_info(&bm->pdev->dev, "Detected %u mAh battery",
633 + (unsigned int)bm->capacity);
634 + }
635 + } else {
636 + bm->capacity = N810BM_CAP_NONE;
637 + dev_info(&bm->pdev->dev, "The main battery was removed");
638 + //TODO disable charging
639 + }
640 + }
641 +
642 + if (bm->charger_present != charger_was_present) {
643 + /* Charger state changed */
644 + dev_info(&bm->pdev->dev, "The charger was %s",
645 + bm->charger_present ? "plugged in" : "removed");
646 + }
647 +
648 + if ((bm->battery_present && !bm->charger_present) ||
649 + !n810bm_known_battery_present(bm)){
650 + /* We're draining the battery */
651 + mv = n810bm_measure_batt_voltage(bm);
652 + if (mv < 0) {
653 + n810bm_emergency(bm,
654 + "check: Failed to measure voltage");
655 + }
656 + if (mv < N810BM_MIN_VOLTAGE_THRES) {
657 + n810bm_emergency(bm,
658 + "check: Minimum voltage threshold reached");
659 + }
660 + }
661 +
662 + if (bm->charger_present && n810bm_known_battery_present(bm)) {
663 + /* Known battery and charger are connected */
664 + if (bm->charger_enabled) {
665 + /* Charger is enabled */
666 + if (!lipocharge_is_charging(&bm->charger)) {
667 + //TODO start charging, if battery is below some threshold
668 + n810bm_start_charge(bm);
669 + }
670 + }
671 + }
672 +
673 + if (lipocharge_is_charging(&bm->charger) && !bm->charger_present) {
674 + /* Charger was unplugged. */
675 + n810bm_stop_charge(bm);
676 + }
677 +
678 + mutex_unlock(&bm->mutex);
679 + schedule_delayed_work(&bm->periodic_check_work,
680 + round_jiffies_relative(N810BM_CHECK_INTERVAL));
681 +}
682 +
683 +static void n810bm_adc_irq_handler(unsigned long data)
684 +{
685 + struct n810bm *bm = (struct n810bm *)data;
686 +
687 + retu_ack_irq(RETU_INT_ADCS);
688 + //TODO
689 +dev_info(&bm->pdev->dev, "ADC interrupt triggered\n");
690 +}
691 +
692 +static void n810bm_tahvo_current_measure_work(struct work_struct *work)
693 +{
694 + struct n810bm *bm = container_of(work, struct n810bm, currmeas_irq_work);
695 + int res, ma, mv, temp;
696 +
697 + mutex_lock(&bm->mutex);
698 + if (!lipocharge_is_charging(&bm->charger))
699 + goto out_unlock;
700 +
701 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
702 + TAHVO_REG_CHGCTL_PWMOVR |
703 + TAHVO_REG_CHGCTL_PWMOVRZERO,
704 + TAHVO_REG_CHGCTL_PWMOVR);
705 + ma = n810bm_measure_batt_current(bm);
706 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
707 + TAHVO_REG_CHGCTL_PWMOVR |
708 + TAHVO_REG_CHGCTL_PWMOVRZERO,
709 + TAHVO_REG_CHGCTL_PWMOVR |
710 + TAHVO_REG_CHGCTL_PWMOVRZERO);
711 + msleep(10);
712 + mv = n810bm_measure_batt_voltage(bm);
713 + tahvo_maskset(bm, TAHVO_REG_CHGCTL,
714 + TAHVO_REG_CHGCTL_PWMOVR |
715 + TAHVO_REG_CHGCTL_PWMOVRZERO,
716 + 0);
717 + temp = n810bm_measure_batt_temp(bm);
718 + if (WARN_ON(mv < 0))
719 + goto out_unlock;
720 + if (WARN_ON(temp < 0))
721 + goto out_unlock;
722 +
723 + if (bm->verbose_charge_log) {
724 + dev_info(&bm->pdev->dev,
725 + "Battery charge state: %d mV, %d mA (%s)",
726 + mv, ma,
727 + (ma <= 0) ? "discharging" : "charging");
728 + }
729 + res = lipocharge_update_state(&bm->charger, mv, ma, temp);
730 + if (res) {
731 + if (res > 0)
732 + dev_info(&bm->pdev->dev, "Battery fully charged");
733 + n810bm_stop_charge(bm);
734 + }
735 +out_unlock:
736 + mutex_unlock(&bm->mutex);
737 +}
738 +
739 +static void n810bm_tahvo_current_measure_irq_handler(unsigned long data)
740 +{
741 + struct n810bm *bm = (struct n810bm *)data;
742 +
743 + tahvo_ack_irq(TAHVO_INT_BATCURR);
744 + schedule_work(&bm->currmeas_irq_work);
745 +}
746 +
747 +#define DEFINE_ATTR_NOTIFY(attr_name) \
748 + void n810bm_notify_##attr_name(struct n810bm *bm) \
749 + { \
750 + set_bit(N810BM_NOTIFY_##attr_name, &bm->notify_flags); \
751 + wmb(); \
752 + schedule_work(&bm->notify_work); \
753 + }
754 +
755 +#define DEFINE_SHOW_INT_FUNC(name, member) \
756 + static ssize_t n810bm_attr_##name##_show(struct device *dev, \
757 + struct device_attribute *attr, \
758 + char *buf) \
759 + { \
760 + struct n810bm *bm = device_to_n810bm(dev); \
761 + ssize_t count; \
762 + \
763 + mutex_lock(&bm->mutex); \
764 + count = snprintf(buf, PAGE_SIZE, "%d\n", (int)(bm->member)); \
765 + mutex_unlock(&bm->mutex); \
766 + \
767 + return count; \
768 + }
769 +
770 +#define DEFINE_STORE_INT_FUNC(name, member) \
771 + static ssize_t n810bm_attr_##name##_store(struct device *dev, \
772 + struct device_attribute *attr,\
773 + const char *buf, size_t count)\
774 + { \
775 + struct n810bm *bm = device_to_n810bm(dev); \
776 + long val; \
777 + int err; \
778 + \
779 + mutex_lock(&bm->mutex); \
780 + err = strict_strtol(buf, 0, &val); \
781 + if (!err) \
782 + bm->member = (typeof(bm->member))val; \
783 + mutex_unlock(&bm->mutex); \
784 + \
785 + return err ? err : count; \
786 + }
787 +
788 +#define DEFINE_ATTR_SHOW_INT(name, member) \
789 + DEFINE_SHOW_INT_FUNC(name, member) \
790 + static DEVICE_ATTR(name, S_IRUGO, \
791 + n810bm_attr_##name##_show, NULL);
792 +
793 +#define DEFINE_ATTR_SHOW_STORE_INT(name, member) \
794 + DEFINE_SHOW_INT_FUNC(name, member) \
795 + DEFINE_STORE_INT_FUNC(name, member) \
796 + static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
797 + n810bm_attr_##name##_show, \
798 + n810bm_attr_##name##_store);
799 +
800 +DEFINE_ATTR_SHOW_INT(battery_present, battery_present);
801 +DEFINE_ATTR_SHOW_INT(charger_present, charger_present);
802 +DEFINE_ATTR_SHOW_INT(charger_pwm, active_current_pwm);
803 +static DEFINE_ATTR_NOTIFY(charger_pwm);
804 +DEFINE_ATTR_SHOW_STORE_INT(charger_enable, charger_enabled);
805 +DEFINE_ATTR_SHOW_STORE_INT(charger_verbose, verbose_charge_log);
806 +
807 +static ssize_t n810bm_attr_battery_charging(struct device *dev,
808 + struct device_attribute *attr,
809 + char *buf)
810 +{
811 + struct n810bm *bm = device_to_n810bm(dev);
812 + ssize_t count;
813 +
814 + mutex_lock(&bm->mutex);
815 + count = snprintf(buf, PAGE_SIZE, "%d\n",
816 + (int)lipocharge_is_charging(&bm->charger));
817 + mutex_unlock(&bm->mutex);
818 +
819 + return count;
820 +}
821 +static DEVICE_ATTR(battery_charging, S_IRUGO,
822 + n810bm_attr_battery_charging, NULL);
823 +static DEFINE_ATTR_NOTIFY(battery_charging);
824 +
825 +static ssize_t n810bm_attr_battery_level_show(struct device *dev,
826 + struct device_attribute *attr,
827 + char *buf)
828 +{
829 + struct n810bm *bm = device_to_n810bm(dev);
830 + ssize_t count = -ENODEV;
831 + int millivolt;
832 +
833 + mutex_lock(&bm->mutex);
834 + if (!bm->battery_present || lipocharge_is_charging(&bm->charger))
835 + millivolt = 0;
836 + else
837 + millivolt = n810bm_measure_batt_voltage(bm);
838 + if (millivolt >= 0) {
839 + count = snprintf(buf, PAGE_SIZE, "%u\n",
840 + n810bm_mvolt2percent(millivolt));
841 + }
842 + mutex_unlock(&bm->mutex);
843 +
844 + return count;
845 +}
846 +static DEVICE_ATTR(battery_level, S_IRUGO,
847 + n810bm_attr_battery_level_show, NULL);
848 +
849 +static ssize_t n810bm_attr_battery_capacity_show(struct device *dev,
850 + struct device_attribute *attr,
851 + char *buf)
852 +{
853 + struct n810bm *bm = device_to_n810bm(dev);
854 + ssize_t count;
855 + int capacity = 0;
856 +
857 + mutex_lock(&bm->mutex);
858 + if (n810bm_known_battery_present(bm))
859 + capacity = (int)bm->capacity;
860 + count = snprintf(buf, PAGE_SIZE, "%d\n", capacity);
861 + mutex_unlock(&bm->mutex);
862 +
863 + return count;
864 +}
865 +static DEVICE_ATTR(battery_capacity, S_IRUGO,
866 + n810bm_attr_battery_capacity_show, NULL);
867 +
868 +static ssize_t n810bm_attr_battery_temp_show(struct device *dev,
869 + struct device_attribute *attr,
870 + char *buf)
871 +{
872 + struct n810bm *bm = device_to_n810bm(dev);
873 + ssize_t count = -ENODEV;
874 + int k;
875 +
876 + mutex_lock(&bm->mutex);
877 + k = n810bm_measure_batt_temp(bm);
878 + if (k >= 0)
879 + count = snprintf(buf, PAGE_SIZE, "%d\n", k);
880 + mutex_unlock(&bm->mutex);
881 +
882 + return count;
883 +}
884 +static DEVICE_ATTR(battery_temp, S_IRUGO,
885 + n810bm_attr_battery_temp_show, NULL);
886 +
887 +static ssize_t n810bm_attr_charger_voltage_show(struct device *dev,
888 + struct device_attribute *attr,
889 + char *buf)
890 +{
891 + struct n810bm *bm = device_to_n810bm(dev);
892 + ssize_t count = -ENODEV;
893 + int mv = 0;
894 +
895 + mutex_lock(&bm->mutex);
896 + if (bm->charger_present)
897 + mv = n810bm_measure_charger_voltage(bm);
898 + if (mv >= 0)
899 + count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
900 + mutex_unlock(&bm->mutex);
901 +
902 + return count;
903 +}
904 +static DEVICE_ATTR(charger_voltage, S_IRUGO,
905 + n810bm_attr_charger_voltage_show, NULL);
906 +
907 +static ssize_t n810bm_attr_backup_battery_voltage_show(struct device *dev,
908 + struct device_attribute *attr,
909 + char *buf)
910 +{
911 + struct n810bm *bm = device_to_n810bm(dev);
912 + ssize_t count = -ENODEV;
913 + int mv;
914 +
915 + mutex_lock(&bm->mutex);
916 + mv = n810bm_measure_backup_batt_voltage(bm);
917 + if (mv >= 0)
918 + count = snprintf(buf, PAGE_SIZE, "%d\n", mv);
919 + mutex_unlock(&bm->mutex);
920 +
921 + return count;
922 +}
923 +static DEVICE_ATTR(backup_battery_voltage, S_IRUGO,
924 + n810bm_attr_backup_battery_voltage_show, NULL);
925 +
926 +static ssize_t n810bm_attr_battery_current_show(struct device *dev,
927 + struct device_attribute *attr,
928 + char *buf)
929 +{
930 + struct n810bm *bm = device_to_n810bm(dev);
931 + ssize_t count = -ENODEV;
932 + int ma = 0;
933 +
934 + mutex_lock(&bm->mutex);
935 + if (bm->battery_present)
936 + ma = n810bm_measure_batt_current_async(bm);
937 + count = snprintf(buf, PAGE_SIZE, "%d\n", ma);
938 + mutex_unlock(&bm->mutex);
939 +
940 + return count;
941 +}
942 +static DEVICE_ATTR(battery_current, S_IRUGO,
943 + n810bm_attr_battery_current_show, NULL);
944 +
945 +static const struct device_attribute *n810bm_attrs[] = {
946 + &dev_attr_battery_present,
947 + &dev_attr_battery_level,
948 + &dev_attr_battery_charging,
949 + &dev_attr_battery_current,
950 + &dev_attr_battery_capacity,
951 + &dev_attr_battery_temp,
952 + &dev_attr_backup_battery_voltage,
953 + &dev_attr_charger_present,
954 + &dev_attr_charger_verbose,
955 + &dev_attr_charger_voltage,
956 + &dev_attr_charger_enable,
957 + &dev_attr_charger_pwm,
958 +};
959 +
960 +static void n810bm_notify_work(struct work_struct *work)
961 +{
962 + struct n810bm *bm = container_of(work, struct n810bm, notify_work);
963 + unsigned long notify_flags;
964 +
965 + notify_flags = xchg(&bm->notify_flags, 0);
966 + mb();
967 +
968 +#define do_notify(attr_name) \
969 + do { \
970 + if (notify_flags & (1 << N810BM_NOTIFY_##attr_name)) { \
971 + sysfs_notify(&bm->pdev->dev.kobj, NULL, \
972 + dev_attr_##attr_name.attr.name); \
973 + } \
974 + } while (0)
975 +
976 + do_notify(battery_charging);
977 + do_notify(charger_pwm);
978 +}
979 +
980 +static int n810bm_charger_set_current_pwm(struct lipocharge *c,
981 + unsigned int duty_cycle)
982 +{
983 + struct n810bm *bm = container_of(c, struct n810bm, charger);
984 + int err = -EINVAL;
985 +
986 + WARN_ON(!mutex_is_locked(&bm->mutex));
987 + if (WARN_ON(duty_cycle > 0xFF))
988 + goto out;
989 + if (WARN_ON(!bm->charger_enabled))
990 + goto out;
991 + if (WARN_ON(!bm->battery_present || !bm->charger_present))
992 + goto out;
993 +
994 + if (duty_cycle != bm->active_current_pwm) {
995 + bm->active_current_pwm = duty_cycle;
996 + tahvo_write(bm, TAHVO_REG_CHGCURR, duty_cycle);
997 + n810bm_notify_charger_pwm(bm);
998 + }
999 +
1000 + err = 0;
1001 +out:
1002 +
1003 + return err;
1004 +}
1005 +
1006 +static void n810bm_charger_emergency(struct lipocharge *c)
1007 +{
1008 + struct n810bm *bm = container_of(c, struct n810bm, charger);
1009 +
1010 + n810bm_emergency(bm, "Battery charger fault");
1011 +}
1012 +
1013 +static void n810bm_hw_exit(struct n810bm *bm)
1014 +{
1015 + n810bm_stop_charge(bm);
1016 + retu_write(bm, RETU_REG_ADCSCR, 0);
1017 +}
1018 +
1019 +static int n810bm_hw_init(struct n810bm *bm)
1020 +{
1021 + int err;
1022 +
1023 + err = n810bm_check_adc_sanity(bm);
1024 + if (err)
1025 + return err;
1026 +
1027 + n810bm_stop_charge(bm);
1028 +
1029 + return 0;
1030 +}
1031 +
1032 +static void n810bm_cancel_and_flush_work(struct n810bm *bm)
1033 +{
1034 + cancel_delayed_work_sync(&bm->periodic_check_work);
1035 + cancel_work_sync(&bm->notify_work);
1036 + cancel_work_sync(&bm->currmeas_irq_work);
1037 + flush_scheduled_work();
1038 +}
1039 +
1040 +static int n810bm_device_init(struct n810bm *bm)
1041 +{
1042 + int attr_index;
1043 + int err;
1044 +
1045 + bm->charger.rate = LIPORATE_p6C;
1046 + bm->charger.top_voltage = 4100;
1047 + bm->charger.duty_cycle_max = 0xFF;
1048 + bm->charger.set_current_pwm = n810bm_charger_set_current_pwm;
1049 + bm->charger.emergency = n810bm_charger_emergency;
1050 + lipocharge_init(&bm->charger, &bm->pdev->dev);
1051 +
1052 + err = n810bm_hw_init(bm);
1053 + if (err)
1054 + goto error;
1055 + for (attr_index = 0; attr_index < ARRAY_SIZE(n810bm_attrs); attr_index++) {
1056 + err = device_create_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1057 + if (err)
1058 + goto err_unwind_attrs;
1059 + }
1060 + err = retu_request_irq(RETU_INT_ADCS,
1061 + n810bm_adc_irq_handler,
1062 + (unsigned long)bm, "n810bm");
1063 + if (err)
1064 + goto err_unwind_attrs;
1065 + err = tahvo_request_irq(TAHVO_INT_BATCURR,
1066 + n810bm_tahvo_current_measure_irq_handler,
1067 + (unsigned long)bm, "n810bm");
1068 + if (err)
1069 + goto err_free_retu_irq;
1070 + tahvo_disable_irq(TAHVO_INT_BATCURR);
1071 +
1072 + schedule_delayed_work(&bm->periodic_check_work,
1073 + round_jiffies_relative(N810BM_CHECK_INTERVAL));
1074 +
1075 + bm->initialized = 1;
1076 + dev_info(&bm->pdev->dev, "Battery management initialized");
1077 +
1078 + return 0;
1079 +
1080 +err_free_retu_irq:
1081 + retu_free_irq(RETU_INT_ADCS);
1082 +err_unwind_attrs:
1083 + for (attr_index--; attr_index >= 0; attr_index--)
1084 + device_remove_file(&bm->pdev->dev, n810bm_attrs[attr_index]);
1085 +/*err_exit:*/
1086 + n810bm_hw_exit(bm);
1087 +error:
1088 + n810bm_cancel_and_flush_work(bm);
1089 +
1090 + return err;
1091 +}
1092 +
1093 +static void n810bm_device_exit(struct n810bm *bm)
1094 +{
1095 + int i;
1096 +
1097 + if (!bm->initialized)
1098 + return;
1099 +
1100 + lipocharge_exit(&bm->charger);
1101 + tahvo_free_irq(TAHVO_INT_BATCURR);
1102 + retu_free_irq(RETU_INT_ADCS);
1103 + for (i = 0; i < ARRAY_SIZE(n810bm_attrs); i++)
1104 + device_remove_file(&bm->pdev->dev, n810bm_attrs[i]);
1105 +
1106 + n810bm_cancel_and_flush_work(bm);
1107 +
1108 + n810bm_hw_exit(bm);
1109 + release_firmware(bm->pmm_block);
1110 +
1111 + bm->initialized = 0;
1112 +}
1113 +
1114 +static void n810bm_pmm_block_found(const struct firmware *fw, void *context)
1115 +{
1116 + struct n810bm *bm = context;
1117 + int err;
1118 +
1119 + if (!fw) {
1120 + dev_err(&bm->pdev->dev,
1121 + "CAL PMM block image file not found");
1122 + goto error;
1123 + }
1124 + if (fw->size != N810BM_PMM_BLOCK_SIZE ||
1125 + memcmp(fw->data, "BME-PMM-BLOCK01", 15) != 0) {
1126 + dev_err(&bm->pdev->dev,
1127 + "CAL PMM block image file has an invalid format");
1128 + goto error;
1129 + }
1130 +
1131 + bm->pmm_block = fw;
1132 + err = n810bm_device_init(bm);
1133 + if (err) {
1134 + dev_err(&bm->pdev->dev,
1135 + "Failed to initialized battery management (%d)", err);
1136 + goto error;
1137 + }
1138 +
1139 + return;
1140 +error:
1141 + release_firmware(fw);
1142 +}
1143 +
1144 +static int __devinit n810bm_probe(struct platform_device *pdev)
1145 +{
1146 + struct n810bm *bm;
1147 + int err;
1148 +
1149 + bm = kzalloc(sizeof(*bm), GFP_KERNEL);
1150 + if (!bm)
1151 + return -ENOMEM;
1152 + bm->pdev = pdev;
1153 + platform_set_drvdata(pdev, bm);
1154 + mutex_init(&bm->mutex);
1155 + INIT_DELAYED_WORK(&bm->periodic_check_work, n810bm_periodic_check_work);
1156 + INIT_WORK(&bm->notify_work, n810bm_notify_work);
1157 + INIT_WORK(&bm->currmeas_irq_work, n810bm_tahvo_current_measure_work);
1158 +
1159 + dev_info(&bm->pdev->dev, "Requesting CAL BME PMM block firmware file "
1160 + N810BM_PMM_BLOCK_FILENAME);
1161 + err = request_firmware_nowait(THIS_MODULE, 1,
1162 + N810BM_PMM_BLOCK_FILENAME,
1163 + &bm->pdev->dev, GFP_KERNEL,
1164 + bm, n810bm_pmm_block_found);
1165 + if (err) {
1166 + dev_err(&bm->pdev->dev,
1167 + "Failed to request CAL PMM block image file (%d)", err);
1168 + goto err_free;
1169 + }
1170 +
1171 + return 0;
1172 +
1173 +err_free:
1174 + kfree(bm);
1175 +
1176 + return err;
1177 +}
1178 +
1179 +static int __devexit n810bm_remove(struct platform_device *pdev)
1180 +{
1181 + struct n810bm *bm = platform_get_drvdata(pdev);
1182 +
1183 + n810bm_device_exit(bm);
1184 +
1185 + kfree(bm);
1186 + platform_set_drvdata(pdev, NULL);
1187 +
1188 + return 0;
1189 +}
1190 +
1191 +static struct platform_driver n810bm_driver = {
1192 + .remove = __devexit_p(n810bm_remove),
1193 + .driver = {
1194 + .name = "n810bm",
1195 + }
1196 +};
1197 +
1198 +static int __init n810bm_modinit(void)
1199 +{
1200 + return platform_driver_probe(&n810bm_driver, n810bm_probe);
1201 +}
1202 +module_init(n810bm_modinit);
1203 +
1204 +static void __exit n810bm_modexit(void)
1205 +{
1206 + platform_driver_unregister(&n810bm_driver);
1207 +}
1208 +module_exit(n810bm_modexit);
1209 +
1210 +MODULE_DESCRIPTION("Nokia n810 battery management");
1211 +MODULE_FIRMWARE(N810BM_PMM_BLOCK_FILENAME);
1212 +MODULE_LICENSE("GPL");
1213 +MODULE_AUTHOR("Michael Buesch");
1214 Index: linux-2.6.37/drivers/cbus/retu.c
1215 ===================================================================
1216 --- linux-2.6.37.orig/drivers/cbus/retu.c 2011-02-06 14:05:49.829387442 +0100
1217 +++ linux-2.6.37/drivers/cbus/retu.c 2011-02-08 17:33:18.981369017 +0100
1218 @@ -85,10 +85,10 @@
1219 *
1220 * This function writes a value to the specified register
1221 */
1222 -void retu_write_reg(int reg, u16 val)
1223 +int retu_write_reg(int reg, u16 val)
1224 {
1225 BUG_ON(!retu_initialized);
1226 - cbus_write_reg(cbus_host, RETU_ID, reg, val);
1227 + return cbus_write_reg(cbus_host, RETU_ID, reg, val);
1228 }
1229
1230 void retu_set_clear_reg_bits(int reg, u16 set, u16 clear)
1231 @@ -459,6 +459,7 @@
1232 EXPORT_SYMBOL(retu_ack_irq);
1233 EXPORT_SYMBOL(retu_read_reg);
1234 EXPORT_SYMBOL(retu_write_reg);
1235 +EXPORT_SYMBOL(retu_read_adc);
1236
1237 subsys_initcall(retu_init);
1238 module_exit(retu_exit);
1239 Index: linux-2.6.37/drivers/cbus/retu.h
1240 ===================================================================
1241 --- linux-2.6.37.orig/drivers/cbus/retu.h 2011-02-06 14:05:49.829387442 +0100
1242 +++ linux-2.6.37/drivers/cbus/retu.h 2011-02-06 14:05:49.886395793 +0100
1243 @@ -40,6 +40,8 @@
1244 #define RETU_REG_CTRL_CLR 0x0f /* Regulator clear register */
1245 #define RETU_REG_CTRL_SET 0x10 /* Regulator set register */
1246 #define RETU_REG_STATUS 0x16 /* Status register */
1247 +#define RETU_REG_STATUS_BATAVAIL 0x0100 /* Battery available */
1248 +#define RETU_REG_STATUS_CHGPLUG 0x1000 /* Charger is plugged in */
1249 #define RETU_REG_WATCHDOG 0x17 /* Watchdog register */
1250 #define RETU_REG_AUDTXR 0x18 /* Audio Codec Tx register */
1251 #define RETU_REG_MAX 0x1f
1252 @@ -57,8 +59,25 @@
1253
1254 #define MAX_RETU_IRQ_HANDLERS 16
1255
1256 +/* ADC channels */
1257 +#define RETU_ADC_GND 0x00 /* Ground */
1258 +#define RETU_ADC_BSI 0x01 /* Battery Size Indicator */
1259 +#define RETU_ADC_BATTEMP 0x02 /* Battery temperature */
1260 +#define RETU_ADC_CHGVOLT 0x03 /* Charger voltage */
1261 +#define RETU_ADC_HEADSET 0x04 /* Headset detection */
1262 +#define RETU_ADC_HOOKDET 0x05 /* Hook detection */
1263 +#define RETU_ADC_RFGP 0x06 /* RF GP */
1264 +#define RETU_ADC_WBTX 0x07 /* Wideband Tx detection */
1265 +#define RETU_ADC_BATTVOLT 0x08 /* Battery voltage measurement */
1266 +#define RETU_ADC_GND2 0x09 /* Ground */
1267 +#define RETU_ADC_LIGHTSENS 0x0A /* Light sensor */
1268 +#define RETU_ADC_LIGHTTEMP 0x0B /* Light sensor temperature */
1269 +#define RETU_ADC_BKUPVOLT 0x0C /* Backup battery voltage */
1270 +#define RETU_ADC_TEMP 0x0D /* RETU temperature */
1271 +
1272 +
1273 int retu_read_reg(int reg);
1274 -void retu_write_reg(int reg, u16 val);
1275 +int retu_write_reg(int reg, u16 val);
1276 void retu_set_clear_reg_bits(int reg, u16 set, u16 clear);
1277 int retu_read_adc(int channel);
1278 int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
1279 Index: linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c
1280 ===================================================================
1281 --- linux-2.6.37.orig/arch/arm/mach-omap2/board-n8x0.c 2011-02-06 14:05:49.815385390 +0100
1282 +++ linux-2.6.37/arch/arm/mach-omap2/board-n8x0.c 2011-02-06 14:05:49.886395793 +0100
1283 @@ -907,6 +907,17 @@
1284 ARRAY_SIZE(n8x0_gpio_switches));
1285 }
1286
1287 +static struct platform_device n810_bm_device = {
1288 + .name = "n810bm",
1289 + .id = -1,
1290 +};
1291 +
1292 +static void __init n810_bm_init(void)
1293 +{
1294 + if (platform_device_register(&n810_bm_device))
1295 + BUG();
1296 +}
1297 +
1298 static void __init n8x0_init_machine(void)
1299 {
1300 omap2420_mux_init(board_mux, OMAP_PACKAGE_ZAC);
1301 @@ -933,6 +944,8 @@
1302 n8x0_onenand_init();
1303 n8x0_mmc_init();
1304 n8x0_usb_init();
1305 +
1306 + n810_bm_init();
1307 }
1308
1309 MACHINE_START(NOKIA_N800, "Nokia N800")
1310 Index: linux-2.6.37/drivers/cbus/lipocharge.c
1311 ===================================================================
1312 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
1313 +++ linux-2.6.37/drivers/cbus/lipocharge.c 2011-02-09 12:42:58.243147563 +0100
1314 @@ -0,0 +1,183 @@
1315 +/*
1316 + * Generic LIPO battery charger
1317 + *
1318 + * Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de>
1319 + *
1320 + * This program is free software; you can redistribute it and/or
1321 + * modify it under the terms of the GNU General Public License
1322 + * as published by the Free Software Foundation; either version 2
1323 + * of the License, or (at your option) any later version.
1324 + *
1325 + * This program is distributed in the hope that it will be useful,
1326 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1327 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1328 + * GNU General Public License for more details.
1329 + */
1330 +
1331 +#define DEBUG
1332 +
1333 +#include "lipocharge.h"
1334 +
1335 +#include <linux/slab.h>
1336 +
1337 +
1338 +/* Hysteresis constants */
1339 +#define CURRENT_HYST 30 /* mA */
1340 +#define VOLTAGE_HYST 10 /* mV */
1341 +
1342 +/* Threshold constants */
1343 +#define FINISH_CURRENT_PERCENT 3
1344 +
1345 +
1346 +/* Returns the requested first-stage charge current in mA */
1347 +static inline unsigned int get_stage1_charge_current(struct lipocharge *c)
1348 +{
1349 + /* current = (capacity * C) */
1350 + return c->capacity * c->rate / 1000;
1351 +}
1352 +
1353 +void lipocharge_init(struct lipocharge *c, struct device *dev)
1354 +{
1355 + c->dev = dev;
1356 + c->state = LIPO_IDLE;
1357 +}
1358 +
1359 +void lipocharge_exit(struct lipocharge *c)
1360 +{
1361 + c->state = LIPO_IDLE;
1362 +}
1363 +
1364 +int lipocharge_start(struct lipocharge *c)
1365 +{
1366 + int err;
1367 +
1368 + if (c->state != LIPO_IDLE)
1369 + return -EBUSY;
1370 + if (!c->set_current_pwm || !c->emergency)
1371 + return -EINVAL;
1372 + if (!c->top_voltage || c->top_voltage > 4200)
1373 + return -EINVAL;
1374 +
1375 + c->active_duty_cycle = 0;
1376 + err = c->set_current_pwm(c, c->active_duty_cycle);
1377 + if (err)
1378 + return err;
1379 + c->state = LIPO_FIRST_STAGE;
1380 +
1381 + return 0;
1382 +}
1383 +
1384 +void lipocharge_stop(struct lipocharge *c)
1385 +{
1386 + if (c->state == LIPO_IDLE)
1387 + return;
1388 + c->state = LIPO_IDLE;
1389 +}
1390 +
1391 +static int lipocharge_increase_current(struct lipocharge *c,
1392 + unsigned int inc_permille)
1393 +{
1394 + int old_pwm, new_pwm;
1395 +
1396 + if (c->active_duty_cycle >= c->duty_cycle_max)
1397 + return 0;
1398 +
1399 + old_pwm = c->active_duty_cycle;
1400 + new_pwm = old_pwm + (c->duty_cycle_max * inc_permille / 1000);
1401 + new_pwm = min(new_pwm, (int)c->duty_cycle_max);
1402 + c->active_duty_cycle = new_pwm;
1403 +
1404 + dev_dbg(c->dev, "lipo: Increasing duty_cycle by "
1405 + "%u permille (0x%02X -> 0x%02X)",
1406 + inc_permille, old_pwm, new_pwm);
1407 +
1408 + return c->set_current_pwm(c, c->active_duty_cycle);
1409 +}
1410 +
1411 +static int lipocharge_decrease_current(struct lipocharge *c,
1412 + unsigned int dec_permille)
1413 +{
1414 + int old_pwm, new_pwm;
1415 +
1416 + if (c->active_duty_cycle <= 0)
1417 + return 0;
1418 +
1419 + old_pwm = c->active_duty_cycle;
1420 + new_pwm = old_pwm - (c->duty_cycle_max * dec_permille / 1000);
1421 + new_pwm = max(0, new_pwm);
1422 + c->active_duty_cycle = new_pwm;
1423 +
1424 + dev_dbg(c->dev, "lipo: Decreasing duty_cycle by "
1425 + "%u permille (0x%02X -> 0x%02X)",
1426 + dec_permille, old_pwm, new_pwm);
1427 +
1428 + return c->set_current_pwm(c, c->active_duty_cycle);
1429 +}
1430 +
1431 +/** lipocharge_update_state - Update the charge state
1432 + * @c: The context.
1433 + * @voltage_mV: The measured battery voltage.
1434 + * @current_mA: The measured charge current.
1435 + * negative -> drain.
1436 + * positive -> charge.
1437 + * @temp_K: Battery temperature in K.
1438 + *
1439 + * Returns 0 on success, -1 on error.
1440 + * Returns 1, if the charging process is finished.
1441 + */
1442 +int lipocharge_update_state(struct lipocharge *c,
1443 + unsigned int voltage_mV,
1444 + int current_mA,
1445 + unsigned int temp_K)
1446 +{
1447 + int requested_current, current_diff;
1448 + int err;
1449 + unsigned int permille;
1450 +
1451 + //TODO temp
1452 +
1453 +restart:
1454 + switch (c->state) {
1455 + case LIPO_IDLE:
1456 + dev_err(c->dev, "%s: called while idle", __func__);
1457 + return -EINVAL;
1458 + case LIPO_FIRST_STAGE: /* Constant current */
1459 +//printk("GOT %u %d %u\n", voltage_mV, current_mA, temp_K);
1460 + if (voltage_mV >= c->top_voltage) {
1461 + /* Float voltage reached.
1462 + * Switch charger mode to "constant current" */
1463 + c->state = LIPO_SECOND_STAGE;
1464 + dev_dbg(c->dev, "Switched to second charging stage.");
1465 + goto restart;
1466 + }
1467 + /* Float voltage not reached, yet.
1468 + * Try to get the requested constant current. */
1469 + requested_current = get_stage1_charge_current(c);
1470 + if (current_mA < 0)
1471 + current_mA = 0;
1472 + current_diff = requested_current - current_mA;
1473 + if (abs(requested_current - current_mA) > CURRENT_HYST) {
1474 + if (current_diff > 0) {
1475 + /* Increase current */
1476 + permille = current_diff * 1000 / requested_current;
1477 + permille /= 2;
1478 + err = lipocharge_increase_current(c, permille);
1479 + if (err)
1480 + return err;
1481 + } else {
1482 + /* Decrease current */
1483 + permille = (-current_diff) * 1000 / requested_current;
1484 + permille /= 2;
1485 + err = lipocharge_decrease_current(c, permille);
1486 + if (err)
1487 + return err;
1488 + }
1489 + }
1490 + break;
1491 + case LIPO_SECOND_STAGE: /* Constant voltage */
1492 + //TODO
1493 + break;
1494 + }
1495 +
1496 + return 0;
1497 +}
1498 Index: linux-2.6.37/drivers/cbus/lipocharge.h
1499 ===================================================================
1500 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
1501 +++ linux-2.6.37/drivers/cbus/lipocharge.h 2011-02-07 20:07:29.669098631 +0100
1502 @@ -0,0 +1,60 @@
1503 +#ifndef LIPOCHARGE_H_
1504 +#define LIPOCHARGE_H_
1505 +
1506 +#include <linux/types.h>
1507 +#include <linux/device.h>
1508 +
1509 +
1510 +#define LIPORATE(a,b) (((a) * 1000) + ((b) * 100))
1511 +#define LIPORATE_p6C LIPORATE(0,6) /* 0.6C */
1512 +
1513 +enum lipocharge_state {
1514 + LIPO_IDLE, /* Not charging */
1515 + LIPO_FIRST_STAGE, /* Charging: constant current */
1516 + LIPO_SECOND_STAGE, /* Charging: constant voltage */
1517 +};
1518 +
1519 +/** struct lipocharge - A generic LIPO charger
1520 + *
1521 + * @capacity: Battery capacity in mAh.
1522 + * @rate: Charge rate.
1523 + * @top_voltage: Fully charged voltage, in mV.
1524 + * @duty_cycle_max: Max value for duty_cycle.
1525 + *
1526 + * @set_charge_current: Set the charge current PWM duty cycle.
1527 + * @emergency: Something went wrong. Force shutdown.
1528 + */
1529 +struct lipocharge {
1530 + unsigned int capacity;
1531 + unsigned int rate;
1532 + unsigned int top_voltage;
1533 + unsigned int duty_cycle_max;
1534 +
1535 + int (*set_current_pwm)(struct lipocharge *c, unsigned int duty_cycle);
1536 + void (*emergency)(struct lipocharge *c);
1537 +
1538 + /* internal */
1539 + struct device *dev;
1540 + enum lipocharge_state state;
1541 + unsigned int active_duty_cycle;
1542 +
1543 + //TODO implement timer to cut power after maximum charge time.
1544 +};
1545 +
1546 +void lipocharge_init(struct lipocharge *c, struct device *dev);
1547 +void lipocharge_exit(struct lipocharge *c);
1548 +
1549 +int lipocharge_start(struct lipocharge *c);
1550 +void lipocharge_stop(struct lipocharge *c);
1551 +
1552 +int lipocharge_update_state(struct lipocharge *c,
1553 + unsigned int voltage_mV,
1554 + int current_mA,
1555 + unsigned int temp_K);
1556 +
1557 +static inline bool lipocharge_is_charging(struct lipocharge *c)
1558 +{
1559 + return (c->state != LIPO_IDLE);
1560 +}
1561 +
1562 +#endif /* LIPOCHARGE_H_ */
1563 Index: linux-2.6.37/drivers/cbus/tahvo.h
1564 ===================================================================
1565 --- linux-2.6.37.orig/drivers/cbus/tahvo.h 2011-02-06 14:05:49.830387588 +0100
1566 +++ linux-2.6.37/drivers/cbus/tahvo.h 2011-02-06 16:22:25.902331536 +0100
1567 @@ -30,17 +30,28 @@
1568 #define TAHVO_REG_IDR 0x01 /* Interrupt ID */
1569 #define TAHVO_REG_IDSR 0x02 /* Interrupt status */
1570 #define TAHVO_REG_IMR 0x03 /* Interrupt mask */
1571 +#define TAHVO_REG_CHGCURR 0x04 /* Charge current control PWM (8-bit) */
1572 #define TAHVO_REG_LEDPWMR 0x05 /* LED PWM */
1573 #define TAHVO_REG_USBR 0x06 /* USB control */
1574 +#define TAHVO_REG_CHGCTL 0x08 /* Charge control register */
1575 +#define TAHVO_REG_CHGCTL_EN 0x0001 /* Global charge enable */
1576 +#define TAHVO_REG_CHGCTL_PWMOVR 0x0004 /* PWM override. Force charge PWM to 0%/100% duty cycle. */
1577 +#define TAHVO_REG_CHGCTL_PWMOVRZERO 0x0008 /* If set, PWM override is 0% (If unset -> 100%) */
1578 +#define TAHVO_REG_CHGCTL_CURMEAS 0x0040 /* Enable battery current measurement. */
1579 +#define TAHVO_REG_CHGCTL_CURTIMRST 0x0080 /* Current measure timer reset. */
1580 +#define TAHVO_REG_BATCURRTIMER 0x0c /* Battery current measure timer (8-bit) */
1581 +#define TAHVO_REG_BATCURR 0x0d /* Battery (dis)charge current (signed 16-bit) */
1582 +
1583 #define TAHVO_REG_MAX 0x0d
1584
1585 /* Interrupt sources */
1586 #define TAHVO_INT_VBUSON 0
1587 +#define TAHVO_INT_BATCURR 7 /* Battery current measure timer */
1588
1589 #define MAX_TAHVO_IRQ_HANDLERS 8
1590
1591 int tahvo_read_reg(int reg);
1592 -void tahvo_write_reg(int reg, u16 val);
1593 +int tahvo_write_reg(int reg, u16 val);
1594 void tahvo_set_clear_reg_bits(int reg, u16 set, u16 clear);
1595 int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name);
1596 void tahvo_free_irq(int id);
1597 Index: linux-2.6.37/drivers/cbus/tahvo.c
1598 ===================================================================
1599 --- linux-2.6.37.orig/drivers/cbus/tahvo.c 2011-02-06 14:05:49.830387588 +0100
1600 +++ linux-2.6.37/drivers/cbus/tahvo.c 2011-02-06 14:05:49.886395793 +0100
1601 @@ -85,10 +85,10 @@
1602 *
1603 * This function writes a value to the specified register
1604 */
1605 -void tahvo_write_reg(int reg, u16 val)
1606 +int tahvo_write_reg(int reg, u16 val)
1607 {
1608 BUG_ON(!tahvo_initialized);
1609 - cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
1610 + return cbus_write_reg(cbus_host, TAHVO_ID, reg, val);
1611 }
1612
1613 /**
1614 Index: linux-2.6.37/drivers/cbus/cbus.c
1615 ===================================================================
1616 --- linux-2.6.37.orig/drivers/cbus/cbus.c 2011-02-08 17:34:34.988926130 +0100
1617 +++ linux-2.6.37/drivers/cbus/cbus.c 2011-02-08 17:38:16.980407594 +0100
1618 @@ -31,6 +31,7 @@
1619 #include <linux/gpio.h>
1620 #include <linux/platform_device.h>
1621 #include <linux/slab.h>
1622 +#include <linux/reboot.h>
1623
1624 #include <asm/io.h>
1625 #include <asm/mach-types.h>
1626 @@ -301,6 +302,13 @@
1627 }
1628 module_exit(cbus_bus_exit);
1629
1630 +void cbus_emergency(void)
1631 +{
1632 + machine_power_off();
1633 + panic("cbus: Failed to halt machine in emergency state\n");
1634 +}
1635 +EXPORT_SYMBOL(cbus_emergency);
1636 +
1637 MODULE_DESCRIPTION("CBUS serial protocol");
1638 MODULE_LICENSE("GPL");
1639 MODULE_AUTHOR("Juha Yrjölä");
1640 Index: linux-2.6.37/drivers/cbus/cbus.h
1641 ===================================================================
1642 --- linux-2.6.37.orig/drivers/cbus/cbus.h 2011-02-08 17:36:40.172074049 +0100
1643 +++ linux-2.6.37/drivers/cbus/cbus.h 2011-02-08 17:41:32.680647478 +0100
1644 @@ -33,4 +33,6 @@
1645 extern int cbus_read_reg(struct cbus_host *host, int dev, int reg);
1646 extern int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val);
1647
1648 +NORET_TYPE void cbus_emergency(void) ATTRIB_NORET;
1649 +
1650 #endif /* __DRIVERS_CBUS_CBUS_H */