root/drivers/power/supply/wm97xx_battery.c

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

DEFINITIONS

This source file includes following definitions.
  1. wm97xx_read_bat
  2. wm97xx_read_temp
  3. wm97xx_bat_get_property
  4. wm97xx_bat_external_power_changed
  5. wm97xx_bat_update
  6. wm97xx_bat_work
  7. wm97xx_chrg_irq
  8. wm97xx_bat_suspend
  9. wm97xx_bat_resume
  10. wm97xx_bat_probe
  11. wm97xx_bat_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Battery measurement code for WM97xx
   4  *
   5  * based on tosa_battery.c
   6  *
   7  * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com>
   8  */
   9 
  10 #include <linux/init.h>
  11 #include <linux/kernel.h>
  12 #include <linux/module.h>
  13 #include <linux/platform_device.h>
  14 #include <linux/power_supply.h>
  15 #include <linux/wm97xx.h>
  16 #include <linux/spinlock.h>
  17 #include <linux/interrupt.h>
  18 #include <linux/gpio.h>
  19 #include <linux/irq.h>
  20 #include <linux/slab.h>
  21 
  22 static struct work_struct bat_work;
  23 static DEFINE_MUTEX(work_lock);
  24 static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
  25 static enum power_supply_property *prop;
  26 
  27 static unsigned long wm97xx_read_bat(struct power_supply *bat_ps)
  28 {
  29         struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps);
  30 
  31         return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent),
  32                                         pdata->batt_aux) * pdata->batt_mult /
  33                                         pdata->batt_div;
  34 }
  35 
  36 static unsigned long wm97xx_read_temp(struct power_supply *bat_ps)
  37 {
  38         struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps);
  39 
  40         return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent),
  41                                         pdata->temp_aux) * pdata->temp_mult /
  42                                         pdata->temp_div;
  43 }
  44 
  45 static int wm97xx_bat_get_property(struct power_supply *bat_ps,
  46                             enum power_supply_property psp,
  47                             union power_supply_propval *val)
  48 {
  49         struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps);
  50 
  51         switch (psp) {
  52         case POWER_SUPPLY_PROP_STATUS:
  53                 val->intval = bat_status;
  54                 break;
  55         case POWER_SUPPLY_PROP_TECHNOLOGY:
  56                 val->intval = pdata->batt_tech;
  57                 break;
  58         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  59                 if (pdata->batt_aux >= 0)
  60                         val->intval = wm97xx_read_bat(bat_ps);
  61                 else
  62                         return -EINVAL;
  63                 break;
  64         case POWER_SUPPLY_PROP_TEMP:
  65                 if (pdata->temp_aux >= 0)
  66                         val->intval = wm97xx_read_temp(bat_ps);
  67                 else
  68                         return -EINVAL;
  69                 break;
  70         case POWER_SUPPLY_PROP_VOLTAGE_MAX:
  71                 if (pdata->max_voltage >= 0)
  72                         val->intval = pdata->max_voltage;
  73                 else
  74                         return -EINVAL;
  75                 break;
  76         case POWER_SUPPLY_PROP_VOLTAGE_MIN:
  77                 if (pdata->min_voltage >= 0)
  78                         val->intval = pdata->min_voltage;
  79                 else
  80                         return -EINVAL;
  81                 break;
  82         case POWER_SUPPLY_PROP_PRESENT:
  83                 val->intval = 1;
  84                 break;
  85         default:
  86                 return -EINVAL;
  87         }
  88         return 0;
  89 }
  90 
  91 static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps)
  92 {
  93         schedule_work(&bat_work);
  94 }
  95 
  96 static void wm97xx_bat_update(struct power_supply *bat_ps)
  97 {
  98         int old_status = bat_status;
  99         struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps);
 100 
 101         mutex_lock(&work_lock);
 102 
 103         bat_status = (pdata->charge_gpio >= 0) ?
 104                         (gpio_get_value(pdata->charge_gpio) ?
 105                         POWER_SUPPLY_STATUS_DISCHARGING :
 106                         POWER_SUPPLY_STATUS_CHARGING) :
 107                         POWER_SUPPLY_STATUS_UNKNOWN;
 108 
 109         if (old_status != bat_status) {
 110                 pr_debug("%s: %i -> %i\n", bat_ps->desc->name, old_status,
 111                                         bat_status);
 112                 power_supply_changed(bat_ps);
 113         }
 114 
 115         mutex_unlock(&work_lock);
 116 }
 117 
 118 static struct power_supply *bat_psy;
 119 static struct power_supply_desc bat_psy_desc = {
 120         .type                   = POWER_SUPPLY_TYPE_BATTERY,
 121         .get_property           = wm97xx_bat_get_property,
 122         .external_power_changed = wm97xx_bat_external_power_changed,
 123         .use_for_apm            = 1,
 124 };
 125 
 126 static void wm97xx_bat_work(struct work_struct *work)
 127 {
 128         wm97xx_bat_update(bat_psy);
 129 }
 130 
 131 static irqreturn_t wm97xx_chrg_irq(int irq, void *data)
 132 {
 133         schedule_work(&bat_work);
 134         return IRQ_HANDLED;
 135 }
 136 
 137 #ifdef CONFIG_PM
 138 static int wm97xx_bat_suspend(struct device *dev)
 139 {
 140         flush_work(&bat_work);
 141         return 0;
 142 }
 143 
 144 static int wm97xx_bat_resume(struct device *dev)
 145 {
 146         schedule_work(&bat_work);
 147         return 0;
 148 }
 149 
 150 static const struct dev_pm_ops wm97xx_bat_pm_ops = {
 151         .suspend        = wm97xx_bat_suspend,
 152         .resume         = wm97xx_bat_resume,
 153 };
 154 #endif
 155 
 156 static int wm97xx_bat_probe(struct platform_device *dev)
 157 {
 158         int ret = 0;
 159         int props = 1;  /* POWER_SUPPLY_PROP_PRESENT */
 160         int i = 0;
 161         struct wm97xx_batt_pdata *pdata = dev->dev.platform_data;
 162         struct power_supply_config cfg = {};
 163 
 164         if (!pdata) {
 165                 dev_err(&dev->dev, "No platform data supplied\n");
 166                 return -EINVAL;
 167         }
 168 
 169         cfg.drv_data = pdata;
 170 
 171         if (dev->id != -1)
 172                 return -EINVAL;
 173 
 174         if (gpio_is_valid(pdata->charge_gpio)) {
 175                 ret = gpio_request(pdata->charge_gpio, "BATT CHRG");
 176                 if (ret)
 177                         goto err;
 178                 ret = gpio_direction_input(pdata->charge_gpio);
 179                 if (ret)
 180                         goto err2;
 181                 ret = request_irq(gpio_to_irq(pdata->charge_gpio),
 182                                 wm97xx_chrg_irq, 0,
 183                                 "AC Detect", dev);
 184                 if (ret)
 185                         goto err2;
 186                 props++;        /* POWER_SUPPLY_PROP_STATUS */
 187         }
 188 
 189         if (pdata->batt_tech >= 0)
 190                 props++;        /* POWER_SUPPLY_PROP_TECHNOLOGY */
 191         if (pdata->temp_aux >= 0)
 192                 props++;        /* POWER_SUPPLY_PROP_TEMP */
 193         if (pdata->batt_aux >= 0)
 194                 props++;        /* POWER_SUPPLY_PROP_VOLTAGE_NOW */
 195         if (pdata->max_voltage >= 0)
 196                 props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MAX */
 197         if (pdata->min_voltage >= 0)
 198                 props++;        /* POWER_SUPPLY_PROP_VOLTAGE_MIN */
 199 
 200         prop = kcalloc(props, sizeof(*prop), GFP_KERNEL);
 201         if (!prop) {
 202                 ret = -ENOMEM;
 203                 goto err3;
 204         }
 205 
 206         prop[i++] = POWER_SUPPLY_PROP_PRESENT;
 207         if (pdata->charge_gpio >= 0)
 208                 prop[i++] = POWER_SUPPLY_PROP_STATUS;
 209         if (pdata->batt_tech >= 0)
 210                 prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
 211         if (pdata->temp_aux >= 0)
 212                 prop[i++] = POWER_SUPPLY_PROP_TEMP;
 213         if (pdata->batt_aux >= 0)
 214                 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
 215         if (pdata->max_voltage >= 0)
 216                 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
 217         if (pdata->min_voltage >= 0)
 218                 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
 219 
 220         INIT_WORK(&bat_work, wm97xx_bat_work);
 221 
 222         if (!pdata->batt_name) {
 223                 dev_info(&dev->dev, "Please consider setting proper battery "
 224                                 "name in platform definition file, falling "
 225                                 "back to name \"wm97xx-batt\"\n");
 226                 bat_psy_desc.name = "wm97xx-batt";
 227         } else
 228                 bat_psy_desc.name = pdata->batt_name;
 229 
 230         bat_psy_desc.properties = prop;
 231         bat_psy_desc.num_properties = props;
 232 
 233         bat_psy = power_supply_register(&dev->dev, &bat_psy_desc, &cfg);
 234         if (!IS_ERR(bat_psy)) {
 235                 schedule_work(&bat_work);
 236         } else {
 237                 ret = PTR_ERR(bat_psy);
 238                 goto err4;
 239         }
 240 
 241         return 0;
 242 err4:
 243         kfree(prop);
 244 err3:
 245         if (gpio_is_valid(pdata->charge_gpio))
 246                 free_irq(gpio_to_irq(pdata->charge_gpio), dev);
 247 err2:
 248         if (gpio_is_valid(pdata->charge_gpio))
 249                 gpio_free(pdata->charge_gpio);
 250 err:
 251         return ret;
 252 }
 253 
 254 static int wm97xx_bat_remove(struct platform_device *dev)
 255 {
 256         struct wm97xx_batt_pdata *pdata = dev->dev.platform_data;
 257 
 258         if (pdata && gpio_is_valid(pdata->charge_gpio)) {
 259                 free_irq(gpio_to_irq(pdata->charge_gpio), dev);
 260                 gpio_free(pdata->charge_gpio);
 261         }
 262         cancel_work_sync(&bat_work);
 263         power_supply_unregister(bat_psy);
 264         kfree(prop);
 265         return 0;
 266 }
 267 
 268 static struct platform_driver wm97xx_bat_driver = {
 269         .driver = {
 270                 .name   = "wm97xx-battery",
 271 #ifdef CONFIG_PM
 272                 .pm     = &wm97xx_bat_pm_ops,
 273 #endif
 274         },
 275         .probe          = wm97xx_bat_probe,
 276         .remove         = wm97xx_bat_remove,
 277 };
 278 
 279 module_platform_driver(wm97xx_bat_driver);
 280 
 281 MODULE_LICENSE("GPL");
 282 MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
 283 MODULE_DESCRIPTION("WM97xx battery driver");

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