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

Configure Feed

Select the types of activity you want to include in your feed.

at master 237 lines 6.8 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Dumb driver for LiIon batteries using TWL4030 madc. 4 * 5 * Copyright 2013 Golden Delicious Computers 6 * Lukas Märdian <lukas@goldelico.com> 7 * 8 * Based on dumb driver for gta01 battery 9 * Copyright 2009 Openmoko, Inc 10 * Balaji Rao <balajirrao@openmoko.org> 11 */ 12 13#include <linux/module.h> 14#include <linux/delay.h> 15#include <linux/platform_device.h> 16#include <linux/power_supply.h> 17#include <linux/slab.h> 18#include <linux/sort.h> 19#include <linux/power/twl4030_madc_battery.h> 20#include <linux/iio/consumer.h> 21 22struct twl4030_madc_battery { 23 struct power_supply *psy; 24 struct twl4030_madc_bat_platform_data *pdata; 25 struct iio_channel *channel_temp; 26 struct iio_channel *channel_ichg; 27 struct iio_channel *channel_vbat; 28}; 29 30static enum power_supply_property twl4030_madc_bat_props[] = { 31 POWER_SUPPLY_PROP_PRESENT, 32 POWER_SUPPLY_PROP_STATUS, 33 POWER_SUPPLY_PROP_TECHNOLOGY, 34 POWER_SUPPLY_PROP_VOLTAGE_NOW, 35 POWER_SUPPLY_PROP_CURRENT_NOW, 36 POWER_SUPPLY_PROP_CAPACITY, 37 POWER_SUPPLY_PROP_CHARGE_FULL, 38 POWER_SUPPLY_PROP_CHARGE_NOW, 39 POWER_SUPPLY_PROP_TEMP, 40 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 41}; 42 43static int madc_read(struct iio_channel *channel) 44{ 45 int val, err; 46 err = iio_read_channel_processed(channel, &val); 47 if (err < 0) 48 return err; 49 50 return val; 51} 52 53static int twl4030_madc_bat_get_charging_status(struct twl4030_madc_battery *bt) 54{ 55 return (madc_read(bt->channel_ichg) > 0) ? 1 : 0; 56} 57 58static int twl4030_madc_bat_get_voltage(struct twl4030_madc_battery *bt) 59{ 60 return madc_read(bt->channel_vbat); 61} 62 63static int twl4030_madc_bat_get_current(struct twl4030_madc_battery *bt) 64{ 65 return madc_read(bt->channel_ichg) * 1000; 66} 67 68static int twl4030_madc_bat_get_temp(struct twl4030_madc_battery *bt) 69{ 70 return madc_read(bt->channel_temp) * 10; 71} 72 73static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat, 74 int volt) 75{ 76 struct twl4030_madc_bat_calibration *calibration; 77 int i, res = 0; 78 79 /* choose charging curve */ 80 if (twl4030_madc_bat_get_charging_status(bat)) 81 calibration = bat->pdata->charging; 82 else 83 calibration = bat->pdata->discharging; 84 85 if (volt > calibration[0].voltage) { 86 res = calibration[0].level; 87 } else { 88 for (i = 0; calibration[i+1].voltage >= 0; i++) { 89 if (volt <= calibration[i].voltage && 90 volt >= calibration[i+1].voltage) { 91 /* interval found - interpolate within range */ 92 res = calibration[i].level - 93 ((calibration[i].voltage - volt) * 94 (calibration[i].level - 95 calibration[i+1].level)) / 96 (calibration[i].voltage - 97 calibration[i+1].voltage); 98 break; 99 } 100 } 101 } 102 return res; 103} 104 105static int twl4030_madc_bat_get_property(struct power_supply *psy, 106 enum power_supply_property psp, 107 union power_supply_propval *val) 108{ 109 struct twl4030_madc_battery *bat = power_supply_get_drvdata(psy); 110 111 switch (psp) { 112 case POWER_SUPPLY_PROP_STATUS: 113 if (twl4030_madc_bat_voltscale(bat, 114 twl4030_madc_bat_get_voltage(bat)) > 95) 115 val->intval = POWER_SUPPLY_STATUS_FULL; 116 else { 117 if (twl4030_madc_bat_get_charging_status(bat)) 118 val->intval = POWER_SUPPLY_STATUS_CHARGING; 119 else 120 val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 121 } 122 break; 123 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 124 val->intval = twl4030_madc_bat_get_voltage(bat) * 1000; 125 break; 126 case POWER_SUPPLY_PROP_TECHNOLOGY: 127 val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 128 break; 129 case POWER_SUPPLY_PROP_CURRENT_NOW: 130 val->intval = twl4030_madc_bat_get_current(bat); 131 break; 132 case POWER_SUPPLY_PROP_PRESENT: 133 /* assume battery is always present */ 134 val->intval = 1; 135 break; 136 case POWER_SUPPLY_PROP_CHARGE_NOW: { 137 int percent = twl4030_madc_bat_voltscale(bat, 138 twl4030_madc_bat_get_voltage(bat)); 139 val->intval = (percent * bat->pdata->capacity) / 100; 140 break; 141 } 142 case POWER_SUPPLY_PROP_CAPACITY: 143 val->intval = twl4030_madc_bat_voltscale(bat, 144 twl4030_madc_bat_get_voltage(bat)); 145 break; 146 case POWER_SUPPLY_PROP_CHARGE_FULL: 147 val->intval = bat->pdata->capacity; 148 break; 149 case POWER_SUPPLY_PROP_TEMP: 150 val->intval = twl4030_madc_bat_get_temp(bat); 151 break; 152 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: { 153 int percent = twl4030_madc_bat_voltscale(bat, 154 twl4030_madc_bat_get_voltage(bat)); 155 /* in mAh */ 156 int chg = (percent * (bat->pdata->capacity/1000))/100; 157 158 /* assume discharge with 400 mA (ca. 1.5W) */ 159 val->intval = (3600l * chg) / 400; 160 break; 161 } 162 default: 163 return -EINVAL; 164 } 165 166 return 0; 167} 168 169static const struct power_supply_desc twl4030_madc_bat_desc = { 170 .name = "twl4030_battery", 171 .type = POWER_SUPPLY_TYPE_BATTERY, 172 .properties = twl4030_madc_bat_props, 173 .num_properties = ARRAY_SIZE(twl4030_madc_bat_props), 174 .get_property = twl4030_madc_bat_get_property, 175 .external_power_changed = power_supply_changed, 176}; 177 178static int twl4030_cmp(const void *a, const void *b) 179{ 180 return ((struct twl4030_madc_bat_calibration *)b)->voltage - 181 ((struct twl4030_madc_bat_calibration *)a)->voltage; 182} 183 184static int twl4030_madc_battery_probe(struct platform_device *pdev) 185{ 186 struct twl4030_madc_battery *twl4030_madc_bat; 187 struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data; 188 struct power_supply_config psy_cfg = {}; 189 190 twl4030_madc_bat = devm_kzalloc(&pdev->dev, sizeof(*twl4030_madc_bat), 191 GFP_KERNEL); 192 if (!twl4030_madc_bat) 193 return -ENOMEM; 194 195 twl4030_madc_bat->channel_temp = devm_iio_channel_get(&pdev->dev, "temp"); 196 if (IS_ERR(twl4030_madc_bat->channel_temp)) 197 return PTR_ERR(twl4030_madc_bat->channel_temp); 198 199 twl4030_madc_bat->channel_ichg = devm_iio_channel_get(&pdev->dev, "ichg"); 200 if (IS_ERR(twl4030_madc_bat->channel_ichg)) 201 return PTR_ERR(twl4030_madc_bat->channel_ichg); 202 203 twl4030_madc_bat->channel_vbat = devm_iio_channel_get(&pdev->dev, "vbat"); 204 if (IS_ERR(twl4030_madc_bat->channel_vbat)) 205 return PTR_ERR(twl4030_madc_bat->channel_vbat); 206 207 /* sort charging and discharging calibration data */ 208 sort(pdata->charging, pdata->charging_size, 209 sizeof(struct twl4030_madc_bat_calibration), 210 twl4030_cmp, NULL); 211 sort(pdata->discharging, pdata->discharging_size, 212 sizeof(struct twl4030_madc_bat_calibration), 213 twl4030_cmp, NULL); 214 215 twl4030_madc_bat->pdata = pdata; 216 psy_cfg.drv_data = twl4030_madc_bat; 217 twl4030_madc_bat->psy = devm_power_supply_register(&pdev->dev, 218 &twl4030_madc_bat_desc, 219 &psy_cfg); 220 if (IS_ERR(twl4030_madc_bat->psy)) 221 return PTR_ERR(twl4030_madc_bat->psy); 222 223 return 0; 224} 225 226static struct platform_driver twl4030_madc_battery_driver = { 227 .driver = { 228 .name = "twl4030_madc_battery", 229 }, 230 .probe = twl4030_madc_battery_probe, 231}; 232module_platform_driver(twl4030_madc_battery_driver); 233 234MODULE_LICENSE("GPL"); 235MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>"); 236MODULE_DESCRIPTION("twl4030_madc battery driver"); 237MODULE_ALIAS("platform:twl4030_madc_battery");