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 v5.12-rc5 322 lines 8.0 kB view raw
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 struct z2_battery_info *info; 94 95 info = charger->info; 96 97 mutex_lock(&charger->work_lock); 98 99 charger->bat_status = charger->charge_gpiod ? 100 (gpiod_get_value(charger->charge_gpiod) ? 101 POWER_SUPPLY_STATUS_CHARGING : 102 POWER_SUPPLY_STATUS_DISCHARGING) : 103 POWER_SUPPLY_STATUS_UNKNOWN; 104 105 if (old_status != charger->bat_status) { 106 pr_debug("%s: %i -> %i\n", charger->batt_ps->desc->name, 107 old_status, 108 charger->bat_status); 109 power_supply_changed(charger->batt_ps); 110 } 111 112 mutex_unlock(&charger->work_lock); 113} 114 115static void z2_batt_work(struct work_struct *work) 116{ 117 struct z2_charger *charger; 118 charger = container_of(work, struct z2_charger, bat_work); 119 z2_batt_update(charger); 120} 121 122static irqreturn_t z2_charge_switch_irq(int irq, void *devid) 123{ 124 struct z2_charger *charger = devid; 125 schedule_work(&charger->bat_work); 126 return IRQ_HANDLED; 127} 128 129static int z2_batt_ps_init(struct z2_charger *charger, int props) 130{ 131 int i = 0; 132 enum power_supply_property *prop; 133 struct z2_battery_info *info = charger->info; 134 135 if (charger->charge_gpiod) 136 props++; /* POWER_SUPPLY_PROP_STATUS */ 137 if (info->batt_tech >= 0) 138 props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ 139 if (info->batt_I2C_reg >= 0) 140 props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ 141 if (info->max_voltage >= 0) 142 props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ 143 if (info->min_voltage >= 0) 144 props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ 145 146 prop = kcalloc(props, sizeof(*prop), GFP_KERNEL); 147 if (!prop) 148 return -ENOMEM; 149 150 prop[i++] = POWER_SUPPLY_PROP_PRESENT; 151 if (charger->charge_gpiod) 152 prop[i++] = POWER_SUPPLY_PROP_STATUS; 153 if (info->batt_tech >= 0) 154 prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; 155 if (info->batt_I2C_reg >= 0) 156 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; 157 if (info->max_voltage >= 0) 158 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; 159 if (info->min_voltage >= 0) 160 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; 161 162 if (!info->batt_name) { 163 dev_info(&charger->client->dev, 164 "Please consider setting proper battery " 165 "name in platform definition file, falling " 166 "back to name \" Z2_DEFAULT_NAME \"\n"); 167 charger->batt_ps_desc.name = Z2_DEFAULT_NAME; 168 } else 169 charger->batt_ps_desc.name = info->batt_name; 170 171 charger->batt_ps_desc.properties = prop; 172 charger->batt_ps_desc.num_properties = props; 173 charger->batt_ps_desc.type = POWER_SUPPLY_TYPE_BATTERY; 174 charger->batt_ps_desc.get_property = z2_batt_get_property; 175 charger->batt_ps_desc.external_power_changed = 176 z2_batt_ext_power_changed; 177 charger->batt_ps_desc.use_for_apm = 1; 178 179 return 0; 180} 181 182static int z2_batt_probe(struct i2c_client *client, 183 const struct i2c_device_id *id) 184{ 185 int ret = 0; 186 int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ 187 struct z2_charger *charger; 188 struct z2_battery_info *info = client->dev.platform_data; 189 struct power_supply_config psy_cfg = {}; 190 191 if (info == NULL) { 192 dev_err(&client->dev, 193 "Please set platform device platform_data" 194 " to a valid z2_battery_info pointer!\n"); 195 return -EINVAL; 196 } 197 198 charger = kzalloc(sizeof(*charger), GFP_KERNEL); 199 if (charger == NULL) 200 return -ENOMEM; 201 202 charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN; 203 charger->info = info; 204 charger->client = client; 205 i2c_set_clientdata(client, charger); 206 psy_cfg.drv_data = charger; 207 208 mutex_init(&charger->work_lock); 209 210 charger->charge_gpiod = devm_gpiod_get_optional(&client->dev, 211 NULL, GPIOD_IN); 212 if (IS_ERR(charger->charge_gpiod)) 213 return dev_err_probe(&client->dev, 214 PTR_ERR(charger->charge_gpiod), 215 "failed to get charge GPIO\n"); 216 217 if (charger->charge_gpiod) { 218 gpiod_set_consumer_name(charger->charge_gpiod, "BATT CHRG"); 219 220 irq_set_irq_type(gpiod_to_irq(charger->charge_gpiod), 221 IRQ_TYPE_EDGE_BOTH); 222 ret = request_irq(gpiod_to_irq(charger->charge_gpiod), 223 z2_charge_switch_irq, 0, 224 "AC Detect", charger); 225 if (ret) 226 goto err; 227 } 228 229 ret = z2_batt_ps_init(charger, props); 230 if (ret) 231 goto err3; 232 233 INIT_WORK(&charger->bat_work, z2_batt_work); 234 235 charger->batt_ps = power_supply_register(&client->dev, 236 &charger->batt_ps_desc, 237 &psy_cfg); 238 if (IS_ERR(charger->batt_ps)) { 239 ret = PTR_ERR(charger->batt_ps); 240 goto err4; 241 } 242 243 schedule_work(&charger->bat_work); 244 245 return 0; 246 247err4: 248 kfree(charger->batt_ps_desc.properties); 249err3: 250 if (charger->charge_gpiod) 251 free_irq(gpiod_to_irq(charger->charge_gpiod), charger); 252err: 253 kfree(charger); 254 return ret; 255} 256 257static int z2_batt_remove(struct i2c_client *client) 258{ 259 struct z2_charger *charger = i2c_get_clientdata(client); 260 261 cancel_work_sync(&charger->bat_work); 262 power_supply_unregister(charger->batt_ps); 263 264 kfree(charger->batt_ps_desc.properties); 265 if (charger->charge_gpiod) 266 free_irq(gpiod_to_irq(charger->charge_gpiod), charger); 267 268 kfree(charger); 269 270 return 0; 271} 272 273#ifdef CONFIG_PM 274static int z2_batt_suspend(struct device *dev) 275{ 276 struct i2c_client *client = to_i2c_client(dev); 277 struct z2_charger *charger = i2c_get_clientdata(client); 278 279 flush_work(&charger->bat_work); 280 return 0; 281} 282 283static int z2_batt_resume(struct device *dev) 284{ 285 struct i2c_client *client = to_i2c_client(dev); 286 struct z2_charger *charger = i2c_get_clientdata(client); 287 288 schedule_work(&charger->bat_work); 289 return 0; 290} 291 292static const struct dev_pm_ops z2_battery_pm_ops = { 293 .suspend = z2_batt_suspend, 294 .resume = z2_batt_resume, 295}; 296 297#define Z2_BATTERY_PM_OPS (&z2_battery_pm_ops) 298 299#else 300#define Z2_BATTERY_PM_OPS (NULL) 301#endif 302 303static const struct i2c_device_id z2_batt_id[] = { 304 { "aer915", 0 }, 305 { } 306}; 307MODULE_DEVICE_TABLE(i2c, z2_batt_id); 308 309static struct i2c_driver z2_batt_driver = { 310 .driver = { 311 .name = "z2-battery", 312 .pm = Z2_BATTERY_PM_OPS 313 }, 314 .probe = z2_batt_probe, 315 .remove = z2_batt_remove, 316 .id_table = z2_batt_id, 317}; 318module_i2c_driver(z2_batt_driver); 319 320MODULE_LICENSE("GPL"); 321MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>"); 322MODULE_DESCRIPTION("Zipit Z2 battery driver");