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