z2_battery.c (7903B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Battery measurement code for Zipit Z2 4 * 5 * Copyright (C) 2009 Peter Edwards <sweetlilmre@gmail.com> 6 */ 7 8#include <linux/module.h> 9#include <linux/gpio/consumer.h> 10#include <linux/i2c.h> 11#include <linux/interrupt.h> 12#include <linux/irq.h> 13#include <linux/power_supply.h> 14#include <linux/slab.h> 15#include <linux/z2_battery.h> 16 17#define Z2_DEFAULT_NAME "Z2" 18 19struct z2_charger { 20 struct z2_battery_info *info; 21 struct gpio_desc *charge_gpiod; 22 int bat_status; 23 struct i2c_client *client; 24 struct power_supply *batt_ps; 25 struct power_supply_desc batt_ps_desc; 26 struct mutex work_lock; 27 struct work_struct bat_work; 28}; 29 30static unsigned long z2_read_bat(struct z2_charger *charger) 31{ 32 int data; 33 data = i2c_smbus_read_byte_data(charger->client, 34 charger->info->batt_I2C_reg); 35 if (data < 0) 36 return 0; 37 38 return data * charger->info->batt_mult / charger->info->batt_div; 39} 40 41static int z2_batt_get_property(struct power_supply *batt_ps, 42 enum power_supply_property psp, 43 union power_supply_propval *val) 44{ 45 struct z2_charger *charger = power_supply_get_drvdata(batt_ps); 46 struct z2_battery_info *info = charger->info; 47 48 switch (psp) { 49 case POWER_SUPPLY_PROP_STATUS: 50 val->intval = charger->bat_status; 51 break; 52 case POWER_SUPPLY_PROP_TECHNOLOGY: 53 val->intval = info->batt_tech; 54 break; 55 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 56 if (info->batt_I2C_reg >= 0) 57 val->intval = z2_read_bat(charger); 58 else 59 return -EINVAL; 60 break; 61 case POWER_SUPPLY_PROP_VOLTAGE_MAX: 62 if (info->max_voltage >= 0) 63 val->intval = info->max_voltage; 64 else 65 return -EINVAL; 66 break; 67 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 68 if (info->min_voltage >= 0) 69 val->intval = info->min_voltage; 70 else 71 return -EINVAL; 72 break; 73 case POWER_SUPPLY_PROP_PRESENT: 74 val->intval = 1; 75 break; 76 default: 77 return -EINVAL; 78 } 79 80 return 0; 81} 82 83static void z2_batt_ext_power_changed(struct power_supply *batt_ps) 84{ 85 struct z2_charger *charger = power_supply_get_drvdata(batt_ps); 86 87 schedule_work(&charger->bat_work); 88} 89 90static void z2_batt_update(struct z2_charger *charger) 91{ 92 int old_status = charger->bat_status; 93 94 mutex_lock(&charger->work_lock); 95 96 charger->bat_status = charger->charge_gpiod ? 97 (gpiod_get_value(charger->charge_gpiod) ? 98 POWER_SUPPLY_STATUS_CHARGING : 99 POWER_SUPPLY_STATUS_DISCHARGING) : 100 POWER_SUPPLY_STATUS_UNKNOWN; 101 102 if (old_status != charger->bat_status) { 103 pr_debug("%s: %i -> %i\n", charger->batt_ps->desc->name, 104 old_status, 105 charger->bat_status); 106 power_supply_changed(charger->batt_ps); 107 } 108 109 mutex_unlock(&charger->work_lock); 110} 111 112static void z2_batt_work(struct work_struct *work) 113{ 114 struct z2_charger *charger; 115 charger = container_of(work, struct z2_charger, bat_work); 116 z2_batt_update(charger); 117} 118 119static irqreturn_t z2_charge_switch_irq(int irq, void *devid) 120{ 121 struct z2_charger *charger = devid; 122 schedule_work(&charger->bat_work); 123 return IRQ_HANDLED; 124} 125 126static int z2_batt_ps_init(struct z2_charger *charger, int props) 127{ 128 int i = 0; 129 enum power_supply_property *prop; 130 struct z2_battery_info *info = charger->info; 131 132 if (charger->charge_gpiod) 133 props++; /* POWER_SUPPLY_PROP_STATUS */ 134 if (info->batt_tech >= 0) 135 props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ 136 if (info->batt_I2C_reg >= 0) 137 props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ 138 if (info->max_voltage >= 0) 139 props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ 140 if (info->min_voltage >= 0) 141 props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ 142 143 prop = kcalloc(props, sizeof(*prop), GFP_KERNEL); 144 if (!prop) 145 return -ENOMEM; 146 147 prop[i++] = POWER_SUPPLY_PROP_PRESENT; 148 if (charger->charge_gpiod) 149 prop[i++] = POWER_SUPPLY_PROP_STATUS; 150 if (info->batt_tech >= 0) 151 prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; 152 if (info->batt_I2C_reg >= 0) 153 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; 154 if (info->max_voltage >= 0) 155 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; 156 if (info->min_voltage >= 0) 157 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; 158 159 if (!info->batt_name) { 160 dev_info(&charger->client->dev, 161 "Please consider setting proper battery " 162 "name in platform definition file, falling " 163 "back to name \" Z2_DEFAULT_NAME \"\n"); 164 charger->batt_ps_desc.name = Z2_DEFAULT_NAME; 165 } else 166 charger->batt_ps_desc.name = info->batt_name; 167 168 charger->batt_ps_desc.properties = prop; 169 charger->batt_ps_desc.num_properties = props; 170 charger->batt_ps_desc.type = POWER_SUPPLY_TYPE_BATTERY; 171 charger->batt_ps_desc.get_property = z2_batt_get_property; 172 charger->batt_ps_desc.external_power_changed = 173 z2_batt_ext_power_changed; 174 charger->batt_ps_desc.use_for_apm = 1; 175 176 return 0; 177} 178 179static int z2_batt_probe(struct i2c_client *client, 180 const struct i2c_device_id *id) 181{ 182 int ret = 0; 183 int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ 184 struct z2_charger *charger; 185 struct z2_battery_info *info = client->dev.platform_data; 186 struct power_supply_config psy_cfg = {}; 187 188 if (info == NULL) { 189 dev_err(&client->dev, 190 "Please set platform device platform_data" 191 " to a valid z2_battery_info pointer!\n"); 192 return -EINVAL; 193 } 194 195 charger = kzalloc(sizeof(*charger), GFP_KERNEL); 196 if (charger == NULL) 197 return -ENOMEM; 198 199 charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN; 200 charger->info = info; 201 charger->client = client; 202 i2c_set_clientdata(client, charger); 203 psy_cfg.drv_data = charger; 204 205 mutex_init(&charger->work_lock); 206 207 charger->charge_gpiod = devm_gpiod_get_optional(&client->dev, 208 NULL, GPIOD_IN); 209 if (IS_ERR(charger->charge_gpiod)) 210 return dev_err_probe(&client->dev, 211 PTR_ERR(charger->charge_gpiod), 212 "failed to get charge GPIO\n"); 213 214 if (charger->charge_gpiod) { 215 gpiod_set_consumer_name(charger->charge_gpiod, "BATT CHRG"); 216 217 irq_set_irq_type(gpiod_to_irq(charger->charge_gpiod), 218 IRQ_TYPE_EDGE_BOTH); 219 ret = request_irq(gpiod_to_irq(charger->charge_gpiod), 220 z2_charge_switch_irq, 0, 221 "AC Detect", charger); 222 if (ret) 223 goto err; 224 } 225 226 ret = z2_batt_ps_init(charger, props); 227 if (ret) 228 goto err3; 229 230 INIT_WORK(&charger->bat_work, z2_batt_work); 231 232 charger->batt_ps = power_supply_register(&client->dev, 233 &charger->batt_ps_desc, 234 &psy_cfg); 235 if (IS_ERR(charger->batt_ps)) { 236 ret = PTR_ERR(charger->batt_ps); 237 goto err4; 238 } 239 240 schedule_work(&charger->bat_work); 241 242 return 0; 243 244err4: 245 kfree(charger->batt_ps_desc.properties); 246err3: 247 if (charger->charge_gpiod) 248 free_irq(gpiod_to_irq(charger->charge_gpiod), charger); 249err: 250 kfree(charger); 251 return ret; 252} 253 254static int z2_batt_remove(struct i2c_client *client) 255{ 256 struct z2_charger *charger = i2c_get_clientdata(client); 257 258 cancel_work_sync(&charger->bat_work); 259 power_supply_unregister(charger->batt_ps); 260 261 kfree(charger->batt_ps_desc.properties); 262 if (charger->charge_gpiod) 263 free_irq(gpiod_to_irq(charger->charge_gpiod), charger); 264 265 kfree(charger); 266 267 return 0; 268} 269 270#ifdef CONFIG_PM 271static int z2_batt_suspend(struct device *dev) 272{ 273 struct i2c_client *client = to_i2c_client(dev); 274 struct z2_charger *charger = i2c_get_clientdata(client); 275 276 flush_work(&charger->bat_work); 277 return 0; 278} 279 280static int z2_batt_resume(struct device *dev) 281{ 282 struct i2c_client *client = to_i2c_client(dev); 283 struct z2_charger *charger = i2c_get_clientdata(client); 284 285 schedule_work(&charger->bat_work); 286 return 0; 287} 288 289static const struct dev_pm_ops z2_battery_pm_ops = { 290 .suspend = z2_batt_suspend, 291 .resume = z2_batt_resume, 292}; 293 294#define Z2_BATTERY_PM_OPS (&z2_battery_pm_ops) 295 296#else 297#define Z2_BATTERY_PM_OPS (NULL) 298#endif 299 300static const struct i2c_device_id z2_batt_id[] = { 301 { "aer915", 0 }, 302 { } 303}; 304MODULE_DEVICE_TABLE(i2c, z2_batt_id); 305 306static struct i2c_driver z2_batt_driver = { 307 .driver = { 308 .name = "z2-battery", 309 .pm = Z2_BATTERY_PM_OPS 310 }, 311 .probe = z2_batt_probe, 312 .remove = z2_batt_remove, 313 .id_table = z2_batt_id, 314}; 315module_i2c_driver(z2_batt_driver); 316 317MODULE_LICENSE("GPL"); 318MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>"); 319MODULE_DESCRIPTION("Zipit Z2 battery driver");