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