Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

power: Add twl4030_madc battery driver.

This driver is used and tested on gta04 phone. It's using twl4030_madc
(similar to rx51 existing driver). Driver also implement charging and
discharging calibration data so user can define ranges and level.

Signed-off-by: Marek Belisko <marek@goldelico.com>
Signed-off-by: Lukas Märdian <lukas@goldelico.com>
Signed-off-by: Anton Vorontsov <anton@enomsg.org>

authored by

Marek Belisko and committed by
Anton Vorontsov
da0a00eb d24fed39

+292
+7
drivers/power/Kconfig
··· 216 216 help 217 217 Say Y here to enable support for iPAQ h1930/h1940/rx1950 battery 218 218 219 + config BATTERY_TWL4030_MADC 220 + tristate "TWL4030 MADC battery driver" 221 + depends on TWL4030_MADC 222 + help 223 + Say Y here to enable this dumb driver for batteries managed 224 + through the TWL4030 MADC. 225 + 219 226 config CHARGER_88PM860X 220 227 tristate "Marvell 88PM860x Charger driver" 221 228 depends on MFD_88PM860X && BATTERY_88PM860X
+1
drivers/power/Makefile
··· 34 34 obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o 35 35 obj-$(CONFIG_BATTERY_Z2) += z2_battery.o 36 36 obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o 37 + obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o 37 38 obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o 38 39 obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o 39 40 obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o
+245
drivers/power/twl4030_madc_battery.c
··· 1 + /* 2 + * Dumb driver for LiIon batteries using TWL4030 madc. 3 + * 4 + * Copyright 2013 Golden Delicious Computers 5 + * Lukas Märdian <lukas@goldelico.com> 6 + * 7 + * Based on dumb driver for gta01 battery 8 + * Copyright 2009 Openmoko, Inc 9 + * Balaji Rao <balajirrao@openmoko.org> 10 + */ 11 + 12 + #include <linux/module.h> 13 + #include <linux/param.h> 14 + #include <linux/delay.h> 15 + #include <linux/workqueue.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/power_supply.h> 18 + #include <linux/slab.h> 19 + #include <linux/sort.h> 20 + #include <linux/i2c/twl4030-madc.h> 21 + #include <linux/power/twl4030_madc_battery.h> 22 + 23 + struct twl4030_madc_battery { 24 + struct power_supply psy; 25 + struct twl4030_madc_bat_platform_data *pdata; 26 + }; 27 + 28 + static enum power_supply_property twl4030_madc_bat_props[] = { 29 + POWER_SUPPLY_PROP_PRESENT, 30 + POWER_SUPPLY_PROP_STATUS, 31 + POWER_SUPPLY_PROP_TECHNOLOGY, 32 + POWER_SUPPLY_PROP_VOLTAGE_NOW, 33 + POWER_SUPPLY_PROP_CURRENT_NOW, 34 + POWER_SUPPLY_PROP_CAPACITY, 35 + POWER_SUPPLY_PROP_CHARGE_FULL, 36 + POWER_SUPPLY_PROP_CHARGE_NOW, 37 + POWER_SUPPLY_PROP_TEMP, 38 + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 39 + }; 40 + 41 + static int madc_read(int index) 42 + { 43 + struct twl4030_madc_request req; 44 + int val; 45 + 46 + req.channels = index; 47 + req.method = TWL4030_MADC_SW2; 48 + req.type = TWL4030_MADC_WAIT; 49 + req.do_avg = 0; 50 + req.raw = false; 51 + req.func_cb = NULL; 52 + 53 + val = twl4030_madc_conversion(&req); 54 + if (val < 0) 55 + return val; 56 + 57 + return req.rbuf[ffs(index) - 1]; 58 + } 59 + 60 + static int twl4030_madc_bat_get_charging_status(void) 61 + { 62 + return (madc_read(TWL4030_MADC_ICHG) > 0) ? 1 : 0; 63 + } 64 + 65 + static int twl4030_madc_bat_get_voltage(void) 66 + { 67 + return madc_read(TWL4030_MADC_VBAT); 68 + } 69 + 70 + static int twl4030_madc_bat_get_current(void) 71 + { 72 + return madc_read(TWL4030_MADC_ICHG) * 1000; 73 + } 74 + 75 + static int twl4030_madc_bat_get_temp(void) 76 + { 77 + return madc_read(TWL4030_MADC_BTEMP) * 10; 78 + } 79 + 80 + static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat, 81 + int volt) 82 + { 83 + struct twl4030_madc_bat_calibration *calibration; 84 + int i, res = 0; 85 + 86 + /* choose charging curve */ 87 + if (twl4030_madc_bat_get_charging_status()) 88 + calibration = bat->pdata->charging; 89 + else 90 + calibration = bat->pdata->discharging; 91 + 92 + if (volt > calibration[0].voltage) { 93 + res = calibration[0].level; 94 + } else { 95 + for (i = 0; calibration[i+1].voltage >= 0; i++) { 96 + if (volt <= calibration[i].voltage && 97 + volt >= calibration[i+1].voltage) { 98 + /* interval found - interpolate within range */ 99 + res = calibration[i].level - 100 + ((calibration[i].voltage - volt) * 101 + (calibration[i].level - 102 + calibration[i+1].level)) / 103 + (calibration[i].voltage - 104 + calibration[i+1].voltage); 105 + break; 106 + } 107 + } 108 + } 109 + return res; 110 + } 111 + 112 + static int twl4030_madc_bat_get_property(struct power_supply *psy, 113 + enum power_supply_property psp, 114 + union power_supply_propval *val) 115 + { 116 + struct twl4030_madc_battery *bat = container_of(psy, 117 + struct twl4030_madc_battery, psy); 118 + 119 + switch (psp) { 120 + case POWER_SUPPLY_PROP_STATUS: 121 + if (twl4030_madc_bat_voltscale(bat, 122 + twl4030_madc_bat_get_voltage()) > 95) 123 + val->intval = POWER_SUPPLY_STATUS_FULL; 124 + else { 125 + if (twl4030_madc_bat_get_charging_status()) 126 + val->intval = POWER_SUPPLY_STATUS_CHARGING; 127 + else 128 + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 129 + } 130 + break; 131 + case POWER_SUPPLY_PROP_VOLTAGE_NOW: 132 + val->intval = twl4030_madc_bat_get_voltage() * 1000; 133 + break; 134 + case POWER_SUPPLY_PROP_TECHNOLOGY: 135 + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 136 + break; 137 + case POWER_SUPPLY_PROP_CURRENT_NOW: 138 + val->intval = twl4030_madc_bat_get_current(); 139 + break; 140 + case POWER_SUPPLY_PROP_PRESENT: 141 + /* assume battery is always present */ 142 + val->intval = 1; 143 + break; 144 + case POWER_SUPPLY_PROP_CHARGE_NOW: { 145 + int percent = twl4030_madc_bat_voltscale(bat, 146 + twl4030_madc_bat_get_voltage()); 147 + val->intval = (percent * bat->pdata->capacity) / 100; 148 + break; 149 + } 150 + case POWER_SUPPLY_PROP_CAPACITY: 151 + val->intval = twl4030_madc_bat_voltscale(bat, 152 + twl4030_madc_bat_get_voltage()); 153 + break; 154 + case POWER_SUPPLY_PROP_CHARGE_FULL: 155 + val->intval = bat->pdata->capacity; 156 + break; 157 + case POWER_SUPPLY_PROP_TEMP: 158 + val->intval = twl4030_madc_bat_get_temp(); 159 + break; 160 + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: { 161 + int percent = twl4030_madc_bat_voltscale(bat, 162 + twl4030_madc_bat_get_voltage()); 163 + /* in mAh */ 164 + int chg = (percent * (bat->pdata->capacity/1000))/100; 165 + 166 + /* assume discharge with 400 mA (ca. 1.5W) */ 167 + val->intval = (3600l * chg) / 400; 168 + break; 169 + } 170 + default: 171 + return -EINVAL; 172 + } 173 + 174 + return 0; 175 + } 176 + 177 + static void twl4030_madc_bat_ext_changed(struct power_supply *psy) 178 + { 179 + struct twl4030_madc_battery *bat = container_of(psy, 180 + struct twl4030_madc_battery, psy); 181 + 182 + power_supply_changed(&bat->psy); 183 + } 184 + 185 + static int twl4030_cmp(const void *a, const void *b) 186 + { 187 + return ((struct twl4030_madc_bat_calibration *)b)->voltage - 188 + ((struct twl4030_madc_bat_calibration *)a)->voltage; 189 + } 190 + 191 + static int twl4030_madc_battery_probe(struct platform_device *pdev) 192 + { 193 + struct twl4030_madc_battery *twl4030_madc_bat; 194 + struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data; 195 + 196 + twl4030_madc_bat = kzalloc(sizeof(*twl4030_madc_bat), GFP_KERNEL); 197 + if (!twl4030_madc_bat) 198 + return -ENOMEM; 199 + 200 + twl4030_madc_bat->psy.name = "twl4030_battery"; 201 + twl4030_madc_bat->psy.type = POWER_SUPPLY_TYPE_BATTERY; 202 + twl4030_madc_bat->psy.properties = twl4030_madc_bat_props; 203 + twl4030_madc_bat->psy.num_properties = 204 + ARRAY_SIZE(twl4030_madc_bat_props); 205 + twl4030_madc_bat->psy.get_property = twl4030_madc_bat_get_property; 206 + twl4030_madc_bat->psy.external_power_changed = 207 + twl4030_madc_bat_ext_changed; 208 + 209 + /* sort charging and discharging calibration data */ 210 + sort(pdata->charging, pdata->charging_size, 211 + sizeof(struct twl4030_madc_bat_calibration), 212 + twl4030_cmp, NULL); 213 + sort(pdata->discharging, pdata->discharging_size, 214 + sizeof(struct twl4030_madc_bat_calibration), 215 + twl4030_cmp, NULL); 216 + 217 + twl4030_madc_bat->pdata = pdata; 218 + platform_set_drvdata(pdev, twl4030_madc_bat); 219 + power_supply_register(&pdev->dev, &twl4030_madc_bat->psy); 220 + 221 + return 0; 222 + } 223 + 224 + static int twl4030_madc_battery_remove(struct platform_device *pdev) 225 + { 226 + struct twl4030_madc_battery *bat = platform_get_drvdata(pdev); 227 + 228 + power_supply_unregister(&bat->psy); 229 + kfree(bat); 230 + 231 + return 0; 232 + } 233 + 234 + static struct platform_driver twl4030_madc_battery_driver = { 235 + .driver = { 236 + .name = "twl4030_madc_battery", 237 + }, 238 + .probe = twl4030_madc_battery_probe, 239 + .remove = twl4030_madc_battery_remove, 240 + }; 241 + module_platform_driver(twl4030_madc_battery_driver); 242 + 243 + MODULE_LICENSE("GPL"); 244 + MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>"); 245 + MODULE_DESCRIPTION("twl4030_madc battery driver");
+39
include/linux/power/twl4030_madc_battery.h
··· 1 + /* 2 + * Dumb driver for LiIon batteries using TWL4030 madc. 3 + * 4 + * Copyright 2013 Golden Delicious Computers 5 + * Nikolaus Schaller <hns@goldelico.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License as published by the 9 + * Free Software Foundation; either version 2 of the License, or (at your 10 + * option) any later version. 11 + * 12 + * You should have received a copy of the GNU General Public License along 13 + * with this program; if not, write to the Free Software Foundation, Inc., 14 + * 675 Mass Ave, Cambridge, MA 02139, USA. 15 + * 16 + */ 17 + 18 + #ifndef __TWL4030_MADC_BATTERY_H 19 + #define __TWL4030_MADC_BATTERY_H 20 + 21 + /* 22 + * Usually we can assume 100% @ 4.15V and 0% @ 3.3V but curves differ for 23 + * charging and discharging! 24 + */ 25 + 26 + struct twl4030_madc_bat_calibration { 27 + short voltage; /* in mV - specify -1 for end of list */ 28 + short level; /* in percent (0 .. 100%) */ 29 + }; 30 + 31 + struct twl4030_madc_bat_platform_data { 32 + unsigned int capacity; /* total capacity in uAh */ 33 + struct twl4030_madc_bat_calibration *charging; 34 + int charging_size; 35 + struct twl4030_madc_bat_calibration *discharging; 36 + int discharging_size; 37 + }; 38 + 39 + #endif