linux/drivers/power/rx51_battery.c
<<
>>
Prefs
   1/*
   2 * Nokia RX-51 battery driver
   3 *
   4 * Copyright (C) 2012  Pali Rohár <pali.rohar@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (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 * You should have received a copy of the GNU General Public License along
  17 * with this program; if not, write to the Free Software Foundation, Inc.,
  18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19 */
  20
  21#include <linux/module.h>
  22#include <linux/param.h>
  23#include <linux/platform_device.h>
  24#include <linux/power_supply.h>
  25#include <linux/slab.h>
  26#include <linux/i2c/twl4030-madc.h>
  27
  28/* RX51 specific channels */
  29#define TWL4030_MADC_BTEMP_RX51 TWL4030_MADC_ADCIN0
  30#define TWL4030_MADC_BCI_RX51   TWL4030_MADC_ADCIN4
  31
  32struct rx51_device_info {
  33        struct device *dev;
  34        struct power_supply bat;
  35};
  36
  37/*
  38 * Read ADCIN channel value, code copied from maemo kernel
  39 */
  40static int rx51_battery_read_adc(int channel)
  41{
  42        struct twl4030_madc_request req;
  43
  44        req.channels = channel;
  45        req.do_avg = 1;
  46        req.method = TWL4030_MADC_SW1;
  47        req.func_cb = NULL;
  48        req.type = TWL4030_MADC_WAIT;
  49        req.raw = true;
  50
  51        if (twl4030_madc_conversion(&req) <= 0)
  52                return -ENODATA;
  53
  54        return req.rbuf[ffs(channel) - 1];
  55}
  56
  57/*
  58 * Read ADCIN channel 12 (voltage) and convert RAW value to micro voltage
  59 * This conversion formula was extracted from maemo program bsi-read
  60 */
  61static int rx51_battery_read_voltage(struct rx51_device_info *di)
  62{
  63        int voltage = rx51_battery_read_adc(TWL4030_MADC_VBAT);
  64
  65        if (voltage < 0)
  66                return voltage;
  67
  68        return 1000 * (10000 * voltage / 1705);
  69}
  70
  71/*
  72 * Temperature look-up tables
  73 * TEMP = (1/(t1 + 1/298) - 273.15)
  74 * Where t1 = (1/B) * ln((RAW_ADC_U * 2.5)/(R * I * 255))
  75 * Formula is based on experimental data, RX-51 CAL data, maemo program bme
  76 * and formula from da9052 driver with values R = 100, B = 3380, I = 0.00671
  77 */
  78
  79/*
  80 * Table1 (temperature for first 25 RAW values)
  81 * Usage: TEMP = rx51_temp_table1[RAW]
  82 *   RAW is between 1 and 24
  83 *   TEMP is between 201 C and 55 C
  84 */
  85static u8 rx51_temp_table1[] = {
  86        255, 201, 159, 138, 124, 114, 106,  99,  94,  89,  85,  82,  78,  75,
  87         73,  70,  68,  66,  64,  62,  61,  59,  57,  56,  55
  88};
  89
  90/*
  91 * Table2 (lowest RAW value for temperature)
  92 * Usage: RAW = rx51_temp_table2[TEMP-rx51_temp_table2_first]
  93 *   TEMP is between 53 C and -32 C
  94 *   RAW is between 25 and 993
  95 */
  96#define rx51_temp_table2_first 53
  97static u16 rx51_temp_table2[] = {
  98         25,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  39,
  99         40,  41,  43,  44,  46,  48,  49,  51,  53,  55,  57,  59,  61,  64,
 100         66,  69,  71,  74,  77,  80,  83,  86,  90,  94,  97, 101, 106, 110,
 101        115, 119, 125, 130, 136, 141, 148, 154, 161, 168, 176, 184, 202, 211,
 102        221, 231, 242, 254, 266, 279, 293, 308, 323, 340, 357, 375, 395, 415,
 103        437, 460, 485, 511, 539, 568, 600, 633, 669, 706, 747, 790, 836, 885,
 104        937, 993, 1024
 105};
 106
 107/*
 108 * Read ADCIN channel 0 (battery temp) and convert value to tenths of Celsius
 109 * Use Temperature look-up tables for conversation
 110 */
 111static int rx51_battery_read_temperature(struct rx51_device_info *di)
 112{
 113        int min = 0;
 114        int max = ARRAY_SIZE(rx51_temp_table2) - 1;
 115        int raw = rx51_battery_read_adc(TWL4030_MADC_BTEMP_RX51);
 116
 117        /* Zero and negative values are undefined */
 118        if (raw <= 0)
 119                return INT_MAX;
 120
 121        /* ADC channels are 10 bit, higher value are undefined */
 122        if (raw >= (1 << 10))
 123                return INT_MIN;
 124
 125        /* First check for temperature in first direct table */
 126        if (raw < ARRAY_SIZE(rx51_temp_table1))
 127                return rx51_temp_table1[raw] * 10;
 128
 129        /* Binary search RAW value in second inverse table */
 130        while (max - min > 1) {
 131                int mid = (max + min) / 2;
 132                if (rx51_temp_table2[mid] <= raw)
 133                        min = mid;
 134                else if (rx51_temp_table2[mid] > raw)
 135                        max = mid;
 136                if (rx51_temp_table2[mid] == raw)
 137                        break;
 138        }
 139
 140        return (rx51_temp_table2_first - min) * 10;
 141}
 142
 143/*
 144 * Read ADCIN channel 4 (BSI) and convert RAW value to micro Ah
 145 * This conversion formula was extracted from maemo program bsi-read
 146 */
 147static int rx51_battery_read_capacity(struct rx51_device_info *di)
 148{
 149        int capacity = rx51_battery_read_adc(TWL4030_MADC_BCI_RX51);
 150
 151        if (capacity < 0)
 152                return capacity;
 153
 154        return 1280 * (1200 * capacity)/(1024 - capacity);
 155}
 156
 157/*
 158 * Return power_supply property
 159 */
 160static int rx51_battery_get_property(struct power_supply *psy,
 161                                        enum power_supply_property psp,
 162                                        union power_supply_propval *val)
 163{
 164        struct rx51_device_info *di = container_of((psy),
 165                                struct rx51_device_info, bat);
 166
 167        switch (psp) {
 168        case POWER_SUPPLY_PROP_TECHNOLOGY:
 169                val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
 170                break;
 171        case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
 172                val->intval = 4200000;
 173                break;
 174        case POWER_SUPPLY_PROP_PRESENT:
 175                val->intval = rx51_battery_read_voltage(di) ? 1 : 0;
 176                break;
 177        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 178                val->intval = rx51_battery_read_voltage(di);
 179                break;
 180        case POWER_SUPPLY_PROP_TEMP:
 181                val->intval = rx51_battery_read_temperature(di);
 182                break;
 183        case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
 184                val->intval = rx51_battery_read_capacity(di);
 185                break;
 186        default:
 187                return -EINVAL;
 188        }
 189
 190        if (val->intval == INT_MAX || val->intval == INT_MIN)
 191                return -EINVAL;
 192
 193        return 0;
 194}
 195
 196static enum power_supply_property rx51_battery_props[] = {
 197        POWER_SUPPLY_PROP_TECHNOLOGY,
 198        POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
 199        POWER_SUPPLY_PROP_PRESENT,
 200        POWER_SUPPLY_PROP_VOLTAGE_NOW,
 201        POWER_SUPPLY_PROP_TEMP,
 202        POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 203};
 204
 205static int rx51_battery_probe(struct platform_device *pdev)
 206{
 207        struct rx51_device_info *di;
 208        int ret;
 209
 210        di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
 211        if (!di)
 212                return -ENOMEM;
 213
 214        platform_set_drvdata(pdev, di);
 215
 216        di->bat.name = dev_name(&pdev->dev);
 217        di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
 218        di->bat.properties = rx51_battery_props;
 219        di->bat.num_properties = ARRAY_SIZE(rx51_battery_props);
 220        di->bat.get_property = rx51_battery_get_property;
 221
 222        ret = power_supply_register(di->dev, &di->bat);
 223        if (ret)
 224                return ret;
 225
 226        return 0;
 227}
 228
 229static int rx51_battery_remove(struct platform_device *pdev)
 230{
 231        struct rx51_device_info *di = platform_get_drvdata(pdev);
 232
 233        power_supply_unregister(&di->bat);
 234
 235        return 0;
 236}
 237
 238static struct platform_driver rx51_battery_driver = {
 239        .probe = rx51_battery_probe,
 240        .remove = rx51_battery_remove,
 241        .driver = {
 242                .name = "rx51-battery",
 243                .owner = THIS_MODULE,
 244        },
 245};
 246module_platform_driver(rx51_battery_driver);
 247
 248MODULE_ALIAS("platform:rx51-battery");
 249MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
 250MODULE_DESCRIPTION("Nokia RX-51 battery driver");
 251MODULE_LICENSE("GPL");
 252
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.