source: trunk/target/linux/omap24xx/patches-2.6.38/900-n810-battery-management.patch @ 25774

Last change on this file since 25774 was 25774, checked in by mb, 5 years ago

n810bm: Compile fix for kernel with lock debugging

File size: 51.2 KB
  • drivers/cbus/Kconfig

    old new  
    7272          to Retu/Vilma. Detection state and events are exposed through 
    7373          sysfs. 
    7474 
     75config N810BM 
     76        depends on CBUS_RETU && CBUS_TAHVO 
     77        tristate "Nokia n810 battery management" 
     78        ---help--- 
     79          Nokia n810 device battery management. 
     80 
     81          If unsure, say N. 
     82 
    7583endmenu 
  • drivers/cbus/Makefile

    old new  
    1111obj-$(CONFIG_CBUS_RETU_RTC)     += retu-rtc.o 
    1212obj-$(CONFIG_CBUS_RETU_WDT)     += retu-wdt.o 
    1313obj-$(CONFIG_CBUS_RETU_HEADSET) += retu-headset.o 
     14n810bm-y                        += n810bm_main.o 
     15n810bm-y                        += lipocharge.o 
     16obj-$(CONFIG_N810BM)            += n810bm.o 
  • new file linux-2.6.38-rc6/drivers/cbus/n810bm_main.c

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

    - +  
     1/* 
     2 *   Generic LIPO battery charger 
     3 * 
     4 *   Copyright (c) 2010-2011 Michael Buesch <mb@bu3sch.de> 
     5 * 
     6 *   This program is free software; you can redistribute it and/or 
     7 *   modify it under the terms of the GNU General Public License 
     8 *   as published by the Free Software Foundation; either version 2 
     9 *   of the License, or (at your option) any later version. 
     10 * 
     11 *   This program is distributed in the hope that it will be useful, 
     12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
     13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     14 *   GNU General Public License for more details. 
     15 */ 
     16 
     17#define DEBUG 
     18 
     19#include "lipocharge.h" 
     20 
     21#include <linux/slab.h> 
     22 
     23 
     24/* Hysteresis constants */ 
     25#define CURRENT_HYST            30 /* mA */ 
     26#define VOLTAGE_HYST            10 /* mV */ 
     27 
     28/* Threshold constants */ 
     29#define FINISH_CURRENT_PERCENT  3 
     30 
     31 
     32/* Returns the requested first-stage charge current in mA */ 
     33static inline unsigned int get_stage1_charge_current(struct lipocharge *c) 
     34{ 
     35        /* current = (capacity * C) */ 
     36        return c->capacity * c->rate / 1000; 
     37} 
     38 
     39void lipocharge_init(struct lipocharge *c, struct device *dev) 
     40{ 
     41        c->dev = dev; 
     42        c->state = LIPO_IDLE; 
     43} 
     44 
     45void lipocharge_exit(struct lipocharge *c) 
     46{ 
     47        c->state = LIPO_IDLE; 
     48} 
     49 
     50int lipocharge_start(struct lipocharge *c) 
     51{ 
     52        int err; 
     53 
     54        if (c->state != LIPO_IDLE) 
     55                return -EBUSY; 
     56        if (!c->set_current_pwm || !c->emergency) 
     57                return -EINVAL; 
     58        if (!c->top_voltage || c->top_voltage > 4200) 
     59                return -EINVAL; 
     60 
     61        c->active_duty_cycle = 0; 
     62        err = c->set_current_pwm(c, c->active_duty_cycle); 
     63        if (err) 
     64                return err; 
     65        c->state = LIPO_FIRST_STAGE; 
     66 
     67        return 0; 
     68} 
     69 
     70void lipocharge_stop(struct lipocharge *c) 
     71{ 
     72        if (c->state == LIPO_IDLE) 
     73                return; 
     74        c->state = LIPO_IDLE; 
     75} 
     76 
     77static int lipocharge_increase_current(struct lipocharge *c, 
     78                                       unsigned int inc_permille) 
     79{ 
     80        int old_pwm, new_pwm; 
     81 
     82        if (c->active_duty_cycle >= c->duty_cycle_max) 
     83                return 0; 
     84 
     85        old_pwm = c->active_duty_cycle; 
     86        new_pwm = old_pwm + (c->duty_cycle_max * inc_permille / 1000); 
     87        new_pwm = min(new_pwm, (int)c->duty_cycle_max); 
     88        c->active_duty_cycle = new_pwm; 
     89 
     90        dev_dbg(c->dev, "lipo: Increasing duty_cycle by " 
     91                "%u permille (0x%02X -> 0x%02X)", 
     92                inc_permille, old_pwm, new_pwm); 
     93 
     94        return c->set_current_pwm(c, c->active_duty_cycle); 
     95} 
     96 
     97static int lipocharge_decrease_current(struct lipocharge *c, 
     98                                       unsigned int dec_permille) 
     99{ 
     100        int old_pwm, new_pwm; 
     101 
     102        if (c->active_duty_cycle <= 0) 
     103                return 0; 
     104 
     105        old_pwm = c->active_duty_cycle; 
     106        new_pwm = old_pwm - (c->duty_cycle_max * dec_permille / 1000); 
     107        new_pwm = max(0, new_pwm); 
     108        c->active_duty_cycle = new_pwm; 
     109 
     110        dev_dbg(c->dev, "lipo: Decreasing duty_cycle by " 
     111                "%u permille (0x%02X -> 0x%02X)", 
     112                dec_permille, old_pwm, new_pwm); 
     113 
     114        return c->set_current_pwm(c, c->active_duty_cycle); 
     115} 
     116 
     117/** lipocharge_update_state - Update the charge state 
     118 * @c: The context. 
     119 * @voltage_mV: The measured battery voltage. 
     120 * @current_mA: The measured charge current. 
     121 *              negative -> drain. 
     122 *              positive -> charge. 
     123 * @temp_K: Battery temperature in K. 
     124 * 
     125 * Returns 0 on success, -1 on error. 
     126 * Returns 1, if the charging process is finished. 
     127 */ 
     128int lipocharge_update_state(struct lipocharge *c, 
     129                            unsigned int voltage_mV, 
     130                            int current_mA, 
     131                            unsigned int temp_K) 
     132{ 
     133        int requested_current, current_diff; 
     134        int err; 
     135        unsigned int permille; 
     136 
     137        //TODO temp 
     138 
     139restart: 
     140        switch (c->state) { 
     141        case LIPO_IDLE: 
     142                dev_err(c->dev, "%s: called while idle", __func__); 
     143                return -EINVAL; 
     144        case LIPO_FIRST_STAGE:  /* Constant current */ 
     145//printk("GOT %u %d %u\n", voltage_mV, current_mA, temp_K); 
     146                if (voltage_mV >= c->top_voltage) { 
     147                        /* Float voltage reached. 
     148                         * Switch charger mode to "constant current" */ 
     149                        c->state = LIPO_SECOND_STAGE; 
     150                        dev_dbg(c->dev, "Switched to second charging stage."); 
     151                        goto restart; 
     152                } 
     153                /* Float voltage not reached, yet. 
     154                 * Try to get the requested constant current. */ 
     155                requested_current = get_stage1_charge_current(c); 
     156                if (current_mA < 0) 
     157                        current_mA = 0; 
     158                current_diff = requested_current - current_mA; 
     159                if (abs(requested_current - current_mA) > CURRENT_HYST) { 
     160                        if (current_diff > 0) { 
     161                                /* Increase current */ 
     162                                permille = current_diff * 1000 / requested_current; 
     163                                permille /= 2; 
     164                                err = lipocharge_increase_current(c, permille); 
     165                                if (err) 
     166                                        return err; 
     167                        } else { 
     168                                /* Decrease current */ 
     169                                permille = (-current_diff) * 1000 / requested_current; 
     170                                permille /= 2; 
     171                                err = lipocharge_decrease_current(c, permille); 
     172                                if (err) 
     173                                        return err; 
     174                        } 
     175                } 
     176                break; 
     177        case LIPO_SECOND_STAGE: /* Constant voltage */ 
     178                //TODO 
     179                break; 
     180        } 
     181 
     182        return 0; 
     183} 
  • new file linux-2.6.38-rc6/drivers/cbus/lipocharge.h

    - +  
     1#ifndef LIPOCHARGE_H_ 
     2#define LIPOCHARGE_H_ 
     3 
     4#include <linux/types.h> 
     5#include <linux/device.h> 
     6 
     7 
     8#define LIPORATE(a,b)   (((a) * 1000) + ((b) * 100)) 
     9#define LIPORATE_p6C    LIPORATE(0,6)   /* 0.6C */ 
     10 
     11enum lipocharge_state { 
     12        LIPO_IDLE,              /* Not charging */ 
     13        LIPO_FIRST_STAGE,       /* Charging: constant current */ 
     14        LIPO_SECOND_STAGE,      /* Charging: constant voltage */ 
     15}; 
     16 
     17/** struct lipocharge - A generic LIPO charger 
     18 * 
     19 * @capacity: Battery capacity in mAh. 
     20 * @rate: Charge rate. 
     21 * @top_voltage: Fully charged voltage, in mV. 
     22 * @duty_cycle_max: Max value for duty_cycle. 
     23 * 
     24 * @set_charge_current: Set the charge current PWM duty cycle. 
     25 * @emergency: Something went wrong. Force shutdown. 
     26 */ 
     27struct lipocharge { 
     28        unsigned int capacity; 
     29        unsigned int rate; 
     30        unsigned int top_voltage; 
     31        unsigned int duty_cycle_max; 
     32 
     33        int (*set_current_pwm)(struct lipocharge *c, unsigned int duty_cycle); 
     34        void (*emergency)(struct lipocharge *c); 
     35 
     36        /* internal */ 
     37        struct device *dev; 
     38        enum lipocharge_state state; 
     39        unsigned int active_duty_cycle; 
     40 
     41        //TODO implement timer to cut power after maximum charge time. 
     42}; 
     43 
     44void lipocharge_init(struct lipocharge *c, struct device *dev); 
     45void lipocharge_exit(struct lipocharge *c); 
     46 
     47int lipocharge_start(struct lipocharge *c); 
     48void lipocharge_stop(struct lipocharge *c); 
     49 
     50int lipocharge_update_state(struct lipocharge *c, 
     51                            unsigned int voltage_mV, 
     52                            int current_mA, 
     53                            unsigned int temp_K); 
     54 
     55static inline bool lipocharge_is_charging(struct lipocharge *c) 
     56{ 
     57        return (c->state != LIPO_IDLE); 
     58} 
     59 
     60#endif /* LIPOCHARGE_H_ */ 
  • drivers/cbus/cbus.c

    old new  
    3535#include <linux/platform_device.h> 
    3636 
    3737#include <plat/cbus.h> 
     38#include <linux/reboot.h> 
    3839 
    3940#include "cbus.h" 
    4041 
     
    323324} 
    324325module_exit(cbus_bus_exit); 
    325326 
     327void cbus_emergency(void) 
     328{ 
     329        machine_power_off(); 
     330        panic("cbus: Failed to halt machine in emergency state\n"); 
     331} 
     332EXPORT_SYMBOL(cbus_emergency); 
     333 
    326334MODULE_DESCRIPTION("CBUS serial protocol"); 
    327335MODULE_LICENSE("GPL"); 
    328336MODULE_AUTHOR("Juha Yrjölä"); 
  • drivers/cbus/cbus.h

    old new  
    2626extern int cbus_read_reg(unsigned dev, unsigned reg); 
    2727extern int cbus_write_reg(unsigned dev, unsigned reg, unsigned val); 
    2828 
     29NORET_TYPE void cbus_emergency(void) ATTRIB_NORET; 
     30 
    2931#endif /* __DRIVERS_CBUS_CBUS_H */ 
  • drivers/cbus/retu.c

    old new  
    423423        if (!child) 
    424424                return -ENOMEM; 
    425425 
     426        child = retu_allocate_child("retu-n810bm", parent, irq_base, 
     427                        RETU_INT_ADCS, -1, 1); 
     428        if (!child) 
     429                return -ENOMEM; 
     430 
    426431        return 0; 
    427432} 
    428433 
  • drivers/cbus/tahvo.c

    old new  
    5454 
    5555static struct tasklet_struct tahvo_tasklet; 
    5656spinlock_t tahvo_lock = SPIN_LOCK_UNLOCKED; 
     57EXPORT_SYMBOL(tahvo_lock); 
    5758 
    5859struct tahvo_irq_handler_desc { 
    5960        int (*func)(unsigned long); 
     
    115116        tahvo_write_reg(reg, w); 
    116117        spin_unlock_irqrestore(&tahvo_lock, flags); 
    117118} 
     119EXPORT_SYMBOL(tahvo_set_clear_reg_bits); 
    118120 
    119121/* 
    120122 * Disable given TAHVO interrupt 
Note: See TracBrowser for help on using the repository browser.