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