root/drivers/power/supply/twl4030_madc_battery.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. madc_read
  2. twl4030_madc_bat_get_charging_status
  3. twl4030_madc_bat_get_voltage
  4. twl4030_madc_bat_get_current
  5. twl4030_madc_bat_get_temp
  6. twl4030_madc_bat_voltscale
  7. twl4030_madc_bat_get_property
  8. twl4030_madc_bat_ext_changed
  9. twl4030_cmp
  10. twl4030_madc_battery_probe
  11. twl4030_madc_battery_remove

   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/param.h>
  15 #include <linux/delay.h>
  16 #include <linux/workqueue.h>
  17 #include <linux/platform_device.h>
  18 #include <linux/power_supply.h>
  19 #include <linux/slab.h>
  20 #include <linux/sort.h>
  21 #include <linux/power/twl4030_madc_battery.h>
  22 #include <linux/iio/consumer.h>
  23 
  24 struct twl4030_madc_battery {
  25         struct power_supply *psy;
  26         struct twl4030_madc_bat_platform_data *pdata;
  27         struct iio_channel *channel_temp;
  28         struct iio_channel *channel_ichg;
  29         struct iio_channel *channel_vbat;
  30 };
  31 
  32 static enum power_supply_property twl4030_madc_bat_props[] = {
  33         POWER_SUPPLY_PROP_PRESENT,
  34         POWER_SUPPLY_PROP_STATUS,
  35         POWER_SUPPLY_PROP_TECHNOLOGY,
  36         POWER_SUPPLY_PROP_VOLTAGE_NOW,
  37         POWER_SUPPLY_PROP_CURRENT_NOW,
  38         POWER_SUPPLY_PROP_CAPACITY,
  39         POWER_SUPPLY_PROP_CHARGE_FULL,
  40         POWER_SUPPLY_PROP_CHARGE_NOW,
  41         POWER_SUPPLY_PROP_TEMP,
  42         POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
  43 };
  44 
  45 static int madc_read(struct iio_channel *channel)
  46 {
  47         int val, err;
  48         err = iio_read_channel_processed(channel, &val);
  49         if (err < 0)
  50                 return err;
  51 
  52         return val;
  53 }
  54 
  55 static int twl4030_madc_bat_get_charging_status(struct twl4030_madc_battery *bt)
  56 {
  57         return (madc_read(bt->channel_ichg) > 0) ? 1 : 0;
  58 }
  59 
  60 static int twl4030_madc_bat_get_voltage(struct twl4030_madc_battery *bt)
  61 {
  62         return madc_read(bt->channel_vbat);
  63 }
  64 
  65 static int twl4030_madc_bat_get_current(struct twl4030_madc_battery *bt)
  66 {
  67         return madc_read(bt->channel_ichg) * 1000;
  68 }
  69 
  70 static int twl4030_madc_bat_get_temp(struct twl4030_madc_battery *bt)
  71 {
  72         return madc_read(bt->channel_temp) * 10;
  73 }
  74 
  75 static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat,
  76                                         int volt)
  77 {
  78         struct twl4030_madc_bat_calibration *calibration;
  79         int i, res = 0;
  80 
  81         /* choose charging curve */
  82         if (twl4030_madc_bat_get_charging_status(bat))
  83                 calibration = bat->pdata->charging;
  84         else
  85                 calibration = bat->pdata->discharging;
  86 
  87         if (volt > calibration[0].voltage) {
  88                 res = calibration[0].level;
  89         } else {
  90                 for (i = 0; calibration[i+1].voltage >= 0; i++) {
  91                         if (volt <= calibration[i].voltage &&
  92                                         volt >= calibration[i+1].voltage) {
  93                                 /* interval found - interpolate within range */
  94                                 res = calibration[i].level -
  95                                         ((calibration[i].voltage - volt) *
  96                                         (calibration[i].level -
  97                                         calibration[i+1].level)) /
  98                                         (calibration[i].voltage -
  99                                         calibration[i+1].voltage);
 100                                 break;
 101                         }
 102                 }
 103         }
 104         return res;
 105 }
 106 
 107 static int twl4030_madc_bat_get_property(struct power_supply *psy,
 108                                         enum power_supply_property psp,
 109                                         union power_supply_propval *val)
 110 {
 111         struct twl4030_madc_battery *bat = power_supply_get_drvdata(psy);
 112 
 113         switch (psp) {
 114         case POWER_SUPPLY_PROP_STATUS:
 115                 if (twl4030_madc_bat_voltscale(bat,
 116                                 twl4030_madc_bat_get_voltage(bat)) > 95)
 117                         val->intval = POWER_SUPPLY_STATUS_FULL;
 118                 else {
 119                         if (twl4030_madc_bat_get_charging_status(bat))
 120                                 val->intval = POWER_SUPPLY_STATUS_CHARGING;
 121                         else
 122                                 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 123                 }
 124                 break;
 125         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 126                 val->intval = twl4030_madc_bat_get_voltage(bat) * 1000;
 127                 break;
 128         case POWER_SUPPLY_PROP_TECHNOLOGY:
 129                 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
 130                 break;
 131         case POWER_SUPPLY_PROP_CURRENT_NOW:
 132                 val->intval = twl4030_madc_bat_get_current(bat);
 133                 break;
 134         case POWER_SUPPLY_PROP_PRESENT:
 135                 /* assume battery is always present */
 136                 val->intval = 1;
 137                 break;
 138         case POWER_SUPPLY_PROP_CHARGE_NOW: {
 139                         int percent = twl4030_madc_bat_voltscale(bat,
 140                                         twl4030_madc_bat_get_voltage(bat));
 141                         val->intval = (percent * bat->pdata->capacity) / 100;
 142                         break;
 143                 }
 144         case POWER_SUPPLY_PROP_CAPACITY:
 145                 val->intval = twl4030_madc_bat_voltscale(bat,
 146                                         twl4030_madc_bat_get_voltage(bat));
 147                 break;
 148         case POWER_SUPPLY_PROP_CHARGE_FULL:
 149                 val->intval = bat->pdata->capacity;
 150                 break;
 151         case POWER_SUPPLY_PROP_TEMP:
 152                 val->intval = twl4030_madc_bat_get_temp(bat);
 153                 break;
 154         case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: {
 155                         int percent = twl4030_madc_bat_voltscale(bat,
 156                                         twl4030_madc_bat_get_voltage(bat));
 157                         /* in mAh */
 158                         int chg = (percent * (bat->pdata->capacity/1000))/100;
 159 
 160                         /* assume discharge with 400 mA (ca. 1.5W) */
 161                         val->intval = (3600l * chg) / 400;
 162                         break;
 163                 }
 164         default:
 165                 return -EINVAL;
 166         }
 167 
 168         return 0;
 169 }
 170 
 171 static void twl4030_madc_bat_ext_changed(struct power_supply *psy)
 172 {
 173         power_supply_changed(psy);
 174 }
 175 
 176 static const struct power_supply_desc twl4030_madc_bat_desc = {
 177         .name                   = "twl4030_battery",
 178         .type                   = POWER_SUPPLY_TYPE_BATTERY,
 179         .properties             = twl4030_madc_bat_props,
 180         .num_properties         = ARRAY_SIZE(twl4030_madc_bat_props),
 181         .get_property           = twl4030_madc_bat_get_property,
 182         .external_power_changed = twl4030_madc_bat_ext_changed,
 183 
 184 };
 185 
 186 static int twl4030_cmp(const void *a, const void *b)
 187 {
 188         return ((struct twl4030_madc_bat_calibration *)b)->voltage -
 189                 ((struct twl4030_madc_bat_calibration *)a)->voltage;
 190 }
 191 
 192 static int twl4030_madc_battery_probe(struct platform_device *pdev)
 193 {
 194         struct twl4030_madc_battery *twl4030_madc_bat;
 195         struct twl4030_madc_bat_platform_data *pdata = pdev->dev.platform_data;
 196         struct power_supply_config psy_cfg = {};
 197         int ret = 0;
 198 
 199         twl4030_madc_bat = devm_kzalloc(&pdev->dev, sizeof(*twl4030_madc_bat),
 200                                 GFP_KERNEL);
 201         if (!twl4030_madc_bat)
 202                 return -ENOMEM;
 203 
 204         twl4030_madc_bat->channel_temp = iio_channel_get(&pdev->dev, "temp");
 205         if (IS_ERR(twl4030_madc_bat->channel_temp)) {
 206                 ret = PTR_ERR(twl4030_madc_bat->channel_temp);
 207                 goto err;
 208         }
 209 
 210         twl4030_madc_bat->channel_ichg = iio_channel_get(&pdev->dev, "ichg");
 211         if (IS_ERR(twl4030_madc_bat->channel_ichg)) {
 212                 ret = PTR_ERR(twl4030_madc_bat->channel_ichg);
 213                 goto err_temp;
 214         }
 215 
 216         twl4030_madc_bat->channel_vbat = iio_channel_get(&pdev->dev, "vbat");
 217         if (IS_ERR(twl4030_madc_bat->channel_vbat)) {
 218                 ret = PTR_ERR(twl4030_madc_bat->channel_vbat);
 219                 goto err_ichg;
 220         }
 221 
 222         /* sort charging and discharging calibration data */
 223         sort(pdata->charging, pdata->charging_size,
 224                 sizeof(struct twl4030_madc_bat_calibration),
 225                 twl4030_cmp, NULL);
 226         sort(pdata->discharging, pdata->discharging_size,
 227                 sizeof(struct twl4030_madc_bat_calibration),
 228                 twl4030_cmp, NULL);
 229 
 230         twl4030_madc_bat->pdata = pdata;
 231         platform_set_drvdata(pdev, twl4030_madc_bat);
 232         psy_cfg.drv_data = twl4030_madc_bat;
 233         twl4030_madc_bat->psy = power_supply_register(&pdev->dev,
 234                                                       &twl4030_madc_bat_desc,
 235                                                       &psy_cfg);
 236         if (IS_ERR(twl4030_madc_bat->psy)) {
 237                 ret = PTR_ERR(twl4030_madc_bat->psy);
 238                 goto err_vbat;
 239         }
 240 
 241         return 0;
 242 
 243 err_vbat:
 244         iio_channel_release(twl4030_madc_bat->channel_vbat);
 245 err_ichg:
 246         iio_channel_release(twl4030_madc_bat->channel_ichg);
 247 err_temp:
 248         iio_channel_release(twl4030_madc_bat->channel_temp);
 249 err:
 250         return ret;
 251 }
 252 
 253 static int twl4030_madc_battery_remove(struct platform_device *pdev)
 254 {
 255         struct twl4030_madc_battery *bat = platform_get_drvdata(pdev);
 256 
 257         power_supply_unregister(bat->psy);
 258 
 259         iio_channel_release(bat->channel_vbat);
 260         iio_channel_release(bat->channel_ichg);
 261         iio_channel_release(bat->channel_temp);
 262 
 263         return 0;
 264 }
 265 
 266 static struct platform_driver twl4030_madc_battery_driver = {
 267         .driver = {
 268                 .name = "twl4030_madc_battery",
 269         },
 270         .probe  = twl4030_madc_battery_probe,
 271         .remove = twl4030_madc_battery_remove,
 272 };
 273 module_platform_driver(twl4030_madc_battery_driver);
 274 
 275 MODULE_LICENSE("GPL");
 276 MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>");
 277 MODULE_DESCRIPTION("twl4030_madc battery driver");
 278 MODULE_ALIAS("platform:twl4030_madc_battery");

/* [<][>][^][v][top][bottom][index][help] */