1/* 2 * Battery measurement code for Zipit Z2 3 * 4 * Copyright (C) 2009 Peter Edwards <sweetlilmre@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 */ 11 12#include <linux/module.h> 13#include <linux/gpio.h> 14#include <linux/i2c.h> 15#include <linux/interrupt.h> 16#include <linux/irq.h> 17#include <linux/power_supply.h> 18#include <linux/slab.h> 19#include <linux/z2_battery.h> 20 21#define Z2_DEFAULT_NAME "Z2" 22 23struct z2_charger { 24 struct z2_battery_info *info; 25 int bat_status; 26 struct i2c_client *client; 27 struct power_supply batt_ps; 28 struct mutex work_lock; 29 struct work_struct bat_work; 30}; 31 32static unsigned long z2_read_bat(struct z2_charger *charger) 33{ 34 int data; 35 data = i2c_smbus_read_byte_data(charger->client, 36 charger->info->batt_I2C_reg); 37 if (data < 0) 38 return 0; 39 40 return data * charger->info->batt_mult / charger->info->batt_div; 41} 42 43static int z2_batt_get_property(struct power_supply *batt_ps, 44 enum power_supply_property psp, 45 union power_supply_propval *val) 46{ 47 struct z2_charger *charger = container_of(batt_ps, struct z2_charger, 48 batt_ps); 49 struct z2_battery_info *info = charger->info; 50 51 switch (psp) { 52 case POWER_SUPPLY_PROP_STATUS: 53 val->intval = charger->bat_status; 54 break; 55 case POWER_SUPPLY_PROP_TECHNOLOGY: 56 val->intval = info->batt_tech; 57 break; 58 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 59 if (info->batt_I2C_reg >= 0) 60 val->intval = z2_read_bat(charger); 61 else 62 return -EINVAL; 63 break; 64 case POWER_SUPPLY_PROP_VOLTAGE_MAX: 65 if (info->max_voltage >= 0) 66 val->intval = info->max_voltage; 67 else 68 return -EINVAL; 69 break; 70 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 71 if (info->min_voltage >= 0) 72 val->intval = info->min_voltage; 73 else 74 return -EINVAL; 75 break; 76 case POWER_SUPPLY_PROP_PRESENT: 77 val->intval = 1; 78 break; 79 default: 80 return -EINVAL; 81 } 82 83 return 0; 84} 85 86static void z2_batt_ext_power_changed(struct power_supply *batt_ps) 87{ 88 struct z2_charger *charger = container_of(batt_ps, struct z2_charger, 89 batt_ps); 90 schedule_work(&charger->bat_work); 91} 92 93static void z2_batt_update(struct z2_charger *charger) 94{ 95 int old_status = charger->bat_status; 96 struct z2_battery_info *info; 97 98 info = charger->info; 99 100 mutex_lock(&charger->work_lock); 101 102 charger->bat_status = (info->charge_gpio >= 0) ? 103 (gpio_get_value(info->charge_gpio) ? 104 POWER_SUPPLY_STATUS_CHARGING : 105 POWER_SUPPLY_STATUS_DISCHARGING) : 106 POWER_SUPPLY_STATUS_UNKNOWN; 107 108 if (old_status != charger->bat_status) { 109 pr_debug("%s: %i -> %i\n", charger->batt_ps.name, old_status, 110 charger->bat_status); 111 power_supply_changed(&charger->batt_ps); 112 } 113 114 mutex_unlock(&charger->work_lock); 115} 116 117static void z2_batt_work(struct work_struct *work) 118{ 119 struct z2_charger *charger; 120 charger = container_of(work, struct z2_charger, bat_work); 121 z2_batt_update(charger); 122} 123 124static irqreturn_t z2_charge_switch_irq(int irq, void *devid) 125{ 126 struct z2_charger *charger = devid; 127 schedule_work(&charger->bat_work); 128 return IRQ_HANDLED; 129} 130 131static int z2_batt_ps_init(struct z2_charger *charger, int props) 132{ 133 int i = 0; 134 enum power_supply_property *prop; 135 struct z2_battery_info *info = charger->info; 136 137 if (info->charge_gpio >= 0) 138 props++; /* POWER_SUPPLY_PROP_STATUS */ 139 if (info->batt_tech >= 0) 140 props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ 141 if (info->batt_I2C_reg >= 0) 142 props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ 143 if (info->max_voltage >= 0) 144 props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ 145 if (info->min_voltage >= 0) 146 props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ 147 148 prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); 149 if (!prop) 150 return -ENOMEM; 151 152 prop[i++] = POWER_SUPPLY_PROP_PRESENT; 153 if (info->charge_gpio >= 0) 154 prop[i++] = POWER_SUPPLY_PROP_STATUS; 155 if (info->batt_tech >= 0) 156 prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; 157 if (info->batt_I2C_reg >= 0) 158 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; 159 if (info->max_voltage >= 0) 160 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; 161 if (info->min_voltage >= 0) 162 prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; 163 164 if (!info->batt_name) { 165 dev_info(&charger->client->dev, 166 "Please consider setting proper battery " 167 "name in platform definition file, falling " 168 "back to name \" Z2_DEFAULT_NAME \"\n"); 169 charger->batt_ps.name = Z2_DEFAULT_NAME; 170 } else 171 charger->batt_ps.name = info->batt_name; 172 173 charger->batt_ps.properties = prop; 174 charger->batt_ps.num_properties = props; 175 charger->batt_ps.type = POWER_SUPPLY_TYPE_BATTERY; 176 charger->batt_ps.get_property = z2_batt_get_property; 177 charger->batt_ps.external_power_changed = z2_batt_ext_power_changed; 178 charger->batt_ps.use_for_apm = 1; 179 180 return 0; 181} 182 183static int __devinit z2_batt_probe(struct i2c_client *client, 184 const struct i2c_device_id *id) 185{ 186 int ret = 0; 187 int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ 188 struct z2_charger *charger; 189 struct z2_battery_info *info = client->dev.platform_data; 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 207 mutex_init(&charger->work_lock); 208 209 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) { 210 ret = gpio_request(info->charge_gpio, "BATT CHRG"); 211 if (ret) 212 goto err; 213 214 ret = gpio_direction_input(info->charge_gpio); 215 if (ret) 216 goto err2; 217 218 irq_set_irq_type(gpio_to_irq(info->charge_gpio), 219 IRQ_TYPE_EDGE_BOTH); 220 ret = request_irq(gpio_to_irq(info->charge_gpio), 221 z2_charge_switch_irq, 0, 222 "AC Detect", charger); 223 if (ret) 224 goto err3; 225 } 226 227 ret = z2_batt_ps_init(charger, props); 228 if (ret) 229 goto err3; 230 231 INIT_WORK(&charger->bat_work, z2_batt_work); 232 233 ret = power_supply_register(&client->dev, &charger->batt_ps); 234 if (ret) 235 goto err4; 236 237 schedule_work(&charger->bat_work); 238 239 return 0; 240 241err4: 242 kfree(charger->batt_ps.properties); 243err3: 244 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) 245 free_irq(gpio_to_irq(info->charge_gpio), charger); 246err2: 247 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) 248 gpio_free(info->charge_gpio); 249err: 250 kfree(charger); 251 return ret; 252} 253 254static int __devexit z2_batt_remove(struct i2c_client *client) 255{ 256 struct z2_charger *charger = i2c_get_clientdata(client); 257 struct z2_battery_info *info = charger->info; 258 259 cancel_work_sync(&charger->bat_work); 260 power_supply_unregister(&charger->batt_ps); 261 262 kfree(charger->batt_ps.properties); 263 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) { 264 free_irq(gpio_to_irq(info->charge_gpio), charger); 265 gpio_free(info->charge_gpio); 266 } 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_sync(&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 .owner = THIS_MODULE, 313 .pm = Z2_BATTERY_PM_OPS 314 }, 315 .probe = z2_batt_probe, 316 .remove = __devexit_p(z2_batt_remove), 317 .id_table = z2_batt_id, 318}; 319 320static int __init z2_batt_init(void) 321{ 322 return i2c_add_driver(&z2_batt_driver); 323} 324 325static void __exit z2_batt_exit(void) 326{ 327 i2c_del_driver(&z2_batt_driver); 328} 329 330module_init(z2_batt_init); 331module_exit(z2_batt_exit); 332 333MODULE_LICENSE("GPL"); 334MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>"); 335MODULE_DESCRIPTION("Zipit Z2 battery driver"); 336