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->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 = kzalloc(props * sizeof(*prop), GFP_KERNEL); 147 if (!prop) 148 return -ENOMEM; 149 150 prop[i++] = POWER_SUPPLY_PROP_PRESENT; 151 if (info->charge_gpio >= 0) 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.name = Z2_DEFAULT_NAME; 168 } else 169 charger->batt_ps.name = info->batt_name; 170 171 charger->batt_ps.properties = prop; 172 charger->batt_ps.num_properties = props; 173 charger->batt_ps.type = POWER_SUPPLY_TYPE_BATTERY; 174 charger->batt_ps.get_property = z2_batt_get_property; 175 charger->batt_ps.external_power_changed = z2_batt_ext_power_changed; 176 charger->batt_ps.use_for_apm = 1; 177 178 return 0; 179} 180 181static int __devinit z2_batt_probe(struct i2c_client *client, 182 const struct i2c_device_id *id) 183{ 184 int ret = 0; 185 int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ 186 struct z2_charger *charger; 187 struct z2_battery_info *info = client->dev.platform_data; 188 189 if (info == NULL) { 190 dev_err(&client->dev, 191 "Please set platform device platform_data" 192 " to a valid z2_battery_info pointer!\n"); 193 return -EINVAL; 194 } 195 196 charger = kzalloc(sizeof(*charger), GFP_KERNEL); 197 if (charger == NULL) 198 return -ENOMEM; 199 200 charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN; 201 charger->info = info; 202 charger->client = client; 203 i2c_set_clientdata(client, charger); 204 205 mutex_init(&charger->work_lock); 206 207 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) { 208 ret = gpio_request(info->charge_gpio, "BATT CHRG"); 209 if (ret) 210 goto err; 211 212 ret = gpio_direction_input(info->charge_gpio); 213 if (ret) 214 goto err2; 215 216 set_irq_type(gpio_to_irq(info->charge_gpio), 217 IRQ_TYPE_EDGE_BOTH); 218 ret = request_irq(gpio_to_irq(info->charge_gpio), 219 z2_charge_switch_irq, IRQF_DISABLED, 220 "AC Detect", charger); 221 if (ret) 222 goto err3; 223 } 224 225 ret = z2_batt_ps_init(charger, props); 226 if (ret) 227 goto err3; 228 229 INIT_WORK(&charger->bat_work, z2_batt_work); 230 231 ret = power_supply_register(&client->dev, &charger->batt_ps); 232 if (ret) 233 goto err4; 234 235 schedule_work(&charger->bat_work); 236 237 return 0; 238 239err4: 240 kfree(charger->batt_ps.properties); 241err3: 242 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) 243 free_irq(gpio_to_irq(info->charge_gpio), charger); 244err2: 245 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) 246 gpio_free(info->charge_gpio); 247err: 248 kfree(charger); 249 return ret; 250} 251 252static int __devexit z2_batt_remove(struct i2c_client *client) 253{ 254 struct z2_charger *charger = i2c_get_clientdata(client); 255 struct z2_battery_info *info = charger->info; 256 257 flush_scheduled_work(); 258 power_supply_unregister(&charger->batt_ps); 259 260 kfree(charger->batt_ps.properties); 261 if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) { 262 free_irq(gpio_to_irq(info->charge_gpio), charger); 263 gpio_free(info->charge_gpio); 264 } 265 266 kfree(charger); 267 268 return 0; 269} 270 271#ifdef CONFIG_PM 272static int z2_batt_suspend(struct i2c_client *client, pm_message_t state) 273{ 274 flush_scheduled_work(); 275 return 0; 276} 277 278static int z2_batt_resume(struct i2c_client *client) 279{ 280 struct z2_charger *charger = i2c_get_clientdata(client); 281 282 schedule_work(&charger->bat_work); 283 return 0; 284} 285#else 286#define z2_batt_suspend NULL 287#define z2_batt_resume NULL 288#endif 289 290static const struct i2c_device_id z2_batt_id[] = { 291 { "aer915", 0 }, 292 { } 293}; 294 295static struct i2c_driver z2_batt_driver = { 296 .driver = { 297 .name = "z2-battery", 298 .owner = THIS_MODULE, 299 }, 300 .probe = z2_batt_probe, 301 .remove = z2_batt_remove, 302 .suspend = z2_batt_suspend, 303 .resume = z2_batt_resume, 304 .id_table = z2_batt_id, 305}; 306 307static int __init z2_batt_init(void) 308{ 309 return i2c_add_driver(&z2_batt_driver); 310} 311 312static void __exit z2_batt_exit(void) 313{ 314 i2c_del_driver(&z2_batt_driver); 315} 316 317module_init(z2_batt_init); 318module_exit(z2_batt_exit); 319 320MODULE_LICENSE("GPL"); 321MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>"); 322MODULE_DESCRIPTION("Zipit Z2 battery driver"); 323