FILE
wm97xx_battery.c
File preview with syntax highlighting.
linux-6.19.14/drivers/power/supply/wm97xx_battery.c
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/consumer.h> 19#include <linux/irq.h> 20#include <linux/slab.h> 21 22static struct work_struct bat_work; 23static struct gpio_desc *charge_gpiod; 24static DEFINE_MUTEX(work_lock); 25static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; 26static enum power_supply_property *prop; 27 28static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) 29{ 30 struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps); 31 32 return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent), 33 pdata->batt_aux) * pdata->batt_mult / 34 pdata->batt_div; 35} 36 37static unsigned long wm97xx_read_temp(struct power_supply *bat_ps) 38{ 39 struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps); 40 41 return wm97xx_read_aux_adc(dev_get_drvdata(bat_ps->dev.parent), 42 pdata->temp_aux) * pdata->temp_mult / 43 pdata->temp_div; 44} 45 46static int wm97xx_bat_get_property(struct power_supply *bat_ps, 47 enum power_supply_property psp, 48 union power_supply_propval *val) 49{ 50 struct wm97xx_batt_pdata *pdata = power_supply_get_drvdata(bat_ps); 51 52 switch (psp) { 53 case POWER_SUPPLY_PROP_STATUS: 54 val->intval = bat_status; 55 break; 56 case POWER_SUPPLY_PROP_TECHNOLOGY: 57 val->intval = pdata->batt_tech; 58 break; 59 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 60 if (pdata->batt_aux >= 0) 61 val->intval = wm97xx_read_bat(bat_ps); 62 else 63 return -EINVAL; 64 break; 65 case POWER_SUPPLY_PROP_TEMP: 66 if (pdata->temp_aux >= 0) 67 val->intval = wm97xx_read_temp(bat_ps); 68 else 69 return -EINVAL; 70 break; 71 case POWER_SUPPLY_PROP_VOLTAGE_MAX: 72 if (pdata->max_voltage >= 0) 73 val->intval = pdata->max_voltage; 74 else 75 return -EINVAL; 76 break; 77 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 78 if (pdata->min_voltage >= 0) 79 val->intval = pdata->min_voltage; 80 else 81 return -EINVAL; 82 break; 83 case POWER_SUPPLY_PROP_PRESENT: 84 val->intval = 1; 85 break; 86 default: 87 return -EINVAL; 88 } 89 return 0; 90} 91 92static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps) 93{ 94 schedule_work(&bat_work); 95} 96 97static void wm97xx_bat_update(struct power_supply *bat_ps) 98{ 99 int old_status = bat_status; 100 101 mutex_lock(&work_lock); 102 103 bat_status = (charge_gpiod) ? 104 (gpiod_get_value(charge_gpiod) ? 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 118static struct power_supply *bat_psy; 119static 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 126static void wm97xx_bat_work(struct work_struct *work) 127{ 128 wm97xx_bat_update(bat_psy); 129} 130 131static 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 138static int wm97xx_bat_suspend(struct device *dev) 139{ 140 flush_work(&bat_work); 141 return 0; 142} 143 144static int wm97xx_bat_resume(struct device *dev) 145{ 146 schedule_work(&bat_work); 147 return 0; 148} 149 150static const struct dev_pm_ops wm97xx_bat_pm_ops = { 151 .suspend = wm97xx_bat_suspend, 152 .resume = wm97xx_bat_resume, 153}; 154#endif 155 156static 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 charge_gpiod = devm_gpiod_get_optional(&dev->dev, NULL, GPIOD_IN); 175 if (IS_ERR(charge_gpiod)) 176 return dev_err_probe(&dev->dev, 177 PTR_ERR(charge_gpiod), 178 "failed to get charge GPIO\n"); 179 if (charge_gpiod) { 180 gpiod_set_consumer_name(charge_gpiod, "BATT CHRG"); 181 props++; /* POWER_SUPPLY_PROP_STATUS */ 182 } 183 184 if (pdata->batt_tech >= 0) 185 props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ 186 if (pdata->temp_aux >= 0) 187 props++; /* POWER_SUPPLY_PROP_TEMP */ 188 if (pdata->batt_aux >= 0) 189 props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ 190 if (pdata->max_voltage >= 0) 191 props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ 192 if (pdata->min_voltage >= 0) 193 props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ 194 195 prop = kcalloc(props, sizeof(*prop), GFP_KERNEL); 196 if (!prop) 197 return -ENOMEM; 198 199 prop[i++] = POWER_SUPPLY_PROP_PRESENT; 200 if (charge_gpiod) 201 prop[i++] = POWER_SUPPLY_PROP_STATUS; 202 if (pdata->batt_tech >= 0) 203 prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; 204 if (pdata->temp_aux >= 0) 205 prop[i++] = POWER_SUPPLY_PROP_TEMP; 206 if (pdata->batt_aux >= 0) 207 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; 208 if (pdata->max_voltage >= 0) 209 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; 210 if (pdata->min_voltage >= 0) 211 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; 212 213 INIT_WORK(&bat_work, wm97xx_bat_work); 214 215 if (!pdata->batt_name) { 216 dev_info(&dev->dev, "Please consider setting proper battery " 217 "name in platform definition file, falling " 218 "back to name \"wm97xx-batt\"\n"); 219 bat_psy_desc.name = "wm97xx-batt"; 220 } else 221 bat_psy_desc.name = pdata->batt_name; 222 223 bat_psy_desc.properties = prop; 224 bat_psy_desc.num_properties = props; 225 226 bat_psy = power_supply_register(&dev->dev, &bat_psy_desc, &cfg); 227 if (!IS_ERR(bat_psy)) { 228 schedule_work(&bat_work); 229 } else { 230 ret = PTR_ERR(bat_psy); 231 goto free; 232 } 233 234 if (charge_gpiod) { 235 ret = request_irq(gpiod_to_irq(charge_gpiod), wm97xx_chrg_irq, 236 0, "AC Detect", dev); 237 if (ret) { 238 dev_err_probe(&dev->dev, ret, 239 "failed to request GPIO irq\n"); 240 goto unregister; 241 } 242 } 243 244 return 0; 245 246unregister: 247 power_supply_unregister(bat_psy); 248 249free: 250 kfree(prop); 251 252 return ret; 253} 254 255static void wm97xx_bat_remove(struct platform_device *dev) 256{ 257 if (charge_gpiod) 258 free_irq(gpiod_to_irq(charge_gpiod), dev); 259 cancel_work_sync(&bat_work); 260 power_supply_unregister(bat_psy); 261 kfree(prop); 262} 263 264static struct platform_driver wm97xx_bat_driver = { 265 .driver = { 266 .name = "wm97xx-battery", 267#ifdef CONFIG_PM 268 .pm = &wm97xx_bat_pm_ops, 269#endif 270 }, 271 .probe = wm97xx_bat_probe, 272 .remove = wm97xx_bat_remove, 273}; 274 275module_platform_driver(wm97xx_bat_driver); 276 277MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); 278MODULE_DESCRIPTION("WM97xx battery driver");
~/
Read-only file preview.