1/* 2 * Driver for batteries with bq27000 chips inside via HDQ 3 * 4 * Copyright 2008 Openmoko, Inc 5 * Andy Green <andy@openmoko.com> 6 * 7 * based on ds2760 driver, original copyright notice for that ---> 8 * 9 * Copyright �� 2007 Anton Vorontsov 10 * 2004-2007 Matt Reimer 11 * 2004 Szabolcs Gyurko 12 * 13 * Use consistent with the GNU GPL is permitted, 14 * provided that this copyright notice is 15 * preserved in its entirety in all copies and derived works. 16 * 17 * Author: Anton Vorontsov <cbou@mail.ru> 18 * February 2007 19 * 20 * Matt Reimer <mreimer@vpop.net> 21 * April 2004, 2005, 2007 22 * 23 * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> 24 * September 2004 25 */ 26 27#include <linux/module.h> 28#include <linux/param.h> 29#include <linux/jiffies.h> 30#include <linux/delay.h> 31#include <linux/pm.h> 32#include <linux/workqueue.h> 33#include <linux/platform_device.h> 34#include <linux/power_supply.h> 35#include <linux/bq27000_battery.h> 36 37enum bq27000_regs { 38 /* RAM regs */ 39 /* read-write after this */ 40 BQ27000_CTRL = 0, /* Device Control Register */ 41 BQ27000_MODE, /* Device Mode Register */ 42 BQ27000_AR_L, /* At-Rate H L */ 43 BQ27000_AR_H, 44 /* read-only after this */ 45 BQ27000_ARTTE_L, /* At-Rate Time To Empty H L */ 46 BQ27000_ARTTE_H, 47 BQ27000_TEMP_L, /* Reported Temperature H L */ 48 BQ27000_TEMP_H, 49 BQ27000_VOLT_L, /* Reported Voltage H L */ 50 BQ27000_VOLT_H, 51 BQ27000_FLAGS, /* Status Flags */ 52 BQ27000_RSOC, /* Relative State of Charge */ 53 BQ27000_NAC_L, /* Nominal Available Capacity H L */ 54 BQ27000_NAC_H, 55 BQ27000_CACD_L, /* Discharge Compensated H L */ 56 BQ27000_CACD_H, 57 BQ27000_CACT_L, /* Temperature Compensated H L */ 58 BQ27000_CACT_H, 59 BQ27000_LMD_L, /* Last measured discharge H L */ 60 BQ27000_LMD_H, 61 BQ27000_AI_L, /* Average Current H L */ 62 BQ27000_AI_H, 63 BQ27000_TTE_L, /* Time to Empty H L */ 64 BQ27000_TTE_H, 65 BQ27000_TTF_L, /* Time to Full H L */ 66 BQ27000_TTF_H, 67 BQ27000_SI_L, /* Standby Current H L */ 68 BQ27000_SI_H, 69 BQ27000_STTE_L, /* Standby Time To Empty H L */ 70 BQ27000_STTE_H, 71 BQ27000_MLI_L, /* Max Load Current H L */ 72 BQ27000_MLI_H, 73 BQ27000_MLTTE_L, /* Max Load Time To Empty H L */ 74 BQ27000_MLTTE_H, 75 BQ27000_SAE_L, /* Available Energy H L */ 76 BQ27000_SAE_H, 77 BQ27000_AP_L, /* Available Power H L */ 78 BQ27000_AP_H, 79 BQ27000_TTECP_L, /* Time to Empty at Constant Power H L */ 80 BQ27000_TTECP_H, 81 BQ27000_CYCL_L, /* Cycle count since learning cycle H L */ 82 BQ27000_CYCL_H, 83 BQ27000_CYCT_L, /* Cycle Count Total H L */ 84 BQ27000_CYCT_H, 85 BQ27000_CSOC, /* Compensated State Of Charge */ 86 /* EEPROM regs */ 87 /* read-write after this */ 88 BQ27000_EE_EE_EN = 0x6e, /* EEPROM Program Enable */ 89 BQ27000_EE_ILMD = 0x76, /* Initial Last Measured Discharge High Byte */ 90 BQ27000_EE_SEDVF, /* Scaled EDVF Threshold */ 91 BQ27000_EE_SEDV1, /* Scaled EDV1 Threshold */ 92 BQ27000_EE_ISLC, /* Initial Standby Load Current */ 93 BQ27000_EE_DMFSD, /* Digital Magnitude Filter and Self Discharge */ 94 BQ27000_EE_TAPER, /* Aging Estimate Enable, Charge Termination Taper */ 95 BQ27000_EE_PKCFG, /* Pack Configuration Values */ 96 BQ27000_EE_IMLC, /* Initial Max Load Current or ID #3 */ 97 BQ27000_EE_DCOMP, /* Discharge rate compensation constants or ID #2 */ 98 BQ27000_EE_TCOMP, /* Temperature Compensation constants or ID #1 */ 99}; 100 101enum bq27000_status_flags { 102 BQ27000_STATUS_CHGS = 0x80, /* 1 = being charged */ 103 BQ27000_STATUS_NOACT = 0x40, /* 1 = no activity */ 104 BQ27000_STATUS_IMIN = 0x20, /* 1 = Lion taper current mode */ 105 BQ27000_STATUS_CI = 0x10, /* 1 = capacity likely innacurate */ 106 BQ27000_STATUS_CALIP = 0x08, /* 1 = calibration in progress */ 107 BQ27000_STATUS_VDQ = 0x04, /* 1 = capacity should be accurate */ 108 BQ27000_STATUS_EDV1 = 0x02, /* 1 = end of discharge.. <6% left */ 109 BQ27000_STATUS_EDVF = 0x01, /* 1 = no, it's really empty now */ 110}; 111 112#define NANOVOLTS_UNIT 3750 113 114struct bq27000_bat_regs { 115 int ai; 116 int flags; 117 int lmd; 118 int rsoc; 119 int temp; 120 int tte; 121 int ttf; 122 int volt; 123}; 124 125struct bq27000_device_info { 126 struct device *dev; 127 struct power_supply bat; 128 struct power_supply ac; 129 struct power_supply usb; 130 struct delayed_work work; 131 struct bq27000_platform_data *pdata; 132 133 struct bq27000_bat_regs regs; 134}; 135 136static unsigned int cache_time = 5000; 137module_param(cache_time, uint, 0644); 138MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); 139 140/* 141 * reading 16 bit values over HDQ has a special hazard where the 142 * hdq device firmware can update the 16-bit register during the time we 143 * read the two halves. TI document SLUS556D recommends the algorithm here 144 * to avoid trouble 145 */ 146 147static int hdq_read16(struct bq27000_device_info *di, int address) 148{ 149 int acc; 150 int high; 151 int retries = 3; 152 153 while (retries--) { 154 155 high = (di->pdata->hdq_read)(address + 1); /* high part */ 156 157 if (high < 0) 158 return high; 159 acc = (di->pdata->hdq_read)(address); 160 if (acc < 0) 161 return acc; 162 163 /* confirm high didn't change between reading it and low */ 164 if (high == (di->pdata->hdq_read)(address + 1)) 165 return (high << 8) | acc; 166 } 167 168 return -ETIME; 169} 170 171static void bq27000_battery_external_power_changed(struct power_supply *psy) 172{ 173 struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, bat); 174 175 dev_dbg(di->dev, "%s\n", __FUNCTION__); 176 schedule_delayed_work(&di->work, 0); 177} 178 179static int bq27000_battery_get_property(struct power_supply *psy, 180 enum power_supply_property psp, 181 union power_supply_propval *val) 182{ 183 int n; 184 struct bq27000_device_info *di = container_of(psy, struct bq27000_device_info, bat); 185 186 if (di->regs.rsoc < 0 && psp != POWER_SUPPLY_PROP_PRESENT) 187 return -ENODEV; 188 189 switch (psp) { 190 case POWER_SUPPLY_PROP_STATUS: 191 val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 192 193 if (!di->pdata->get_charger_online_status) 194 goto use_bat; 195 if ((di->pdata->get_charger_online_status)()) { 196 /* 197 * charger is definitively present 198 * we report our state in terms of what it says it 199 * is doing 200 */ 201 if (!di->pdata->get_charger_active_status) 202 goto use_bat; 203 204 if ((di->pdata->get_charger_active_status)()) { 205 val->intval = POWER_SUPPLY_STATUS_CHARGING; 206 break; 207 } 208 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 209 break; 210 } 211 212 /* 213 * platform provided definite indication of charger presence, 214 * and it is telling us it isn't there... but we are on so we 215 * must be running from battery ---> 216 */ 217 218 val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 219 break; 220 221use_bat: 222 /* 223 * either the charger is not connected, or the 224 * platform doesn't give info about charger, use battery state 225 * but... battery state can be out of date by 4 seconds or 226 * so... use the platform callbacks if possible. 227 */ 228 229 /* no real activity on the battery */ 230 if (di->regs.ai < 2) { 231 if (!di->regs.ttf) 232 val->intval = POWER_SUPPLY_STATUS_FULL; 233 else 234 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 235 break; 236 } 237 /* power is actually going in or out... */ 238 if (di->regs.flags < 0) 239 return di->regs.flags; 240 if (di->regs.flags & BQ27000_STATUS_CHGS) 241 val->intval = POWER_SUPPLY_STATUS_CHARGING; 242 else 243 val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 244 break; 245 case POWER_SUPPLY_PROP_HEALTH: 246 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 247 /* Do we have accurate readings... */ 248 if (di->regs.flags < 0) 249 return di->regs.flags; 250 if (di->regs.flags & BQ27000_STATUS_VDQ) 251 val->intval = POWER_SUPPLY_HEALTH_GOOD; 252 break; 253 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 254 if (di->regs.volt < 0) 255 return di->regs.volt; 256 /* mV -> uV */ 257 val->intval = di->regs.volt * 1000; 258 break; 259 case POWER_SUPPLY_PROP_CURRENT_NOW: 260 if (di->regs.flags < 0) 261 return di->regs.flags; 262 if (di->regs.flags & BQ27000_STATUS_CHGS) 263 n = -NANOVOLTS_UNIT; 264 else 265 n = NANOVOLTS_UNIT; 266 if (di->regs.ai < 0) 267 return di->regs.ai; 268 val->intval = (di->regs.ai * n) / di->pdata->rsense_mohms; 269 break; 270 case POWER_SUPPLY_PROP_CHARGE_FULL: 271 if (di->regs.lmd < 0) 272 return di->regs.lmd; 273 val->intval = (di->regs.lmd * 3570) / di->pdata->rsense_mohms; 274 break; 275 case POWER_SUPPLY_PROP_TEMP: 276 if (di->regs.temp < 0) 277 return di->regs.temp; 278 /* K (in 0.25K units) is 273.15 up from C (in 0.1C)*/ 279 /* 10926 = 27315 * 4 / 10 */ 280 val->intval = (((long)di->regs.temp * 10l) - 10926) / 4; 281 break; 282 case POWER_SUPPLY_PROP_TECHNOLOGY: 283 val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 284 break; 285 case POWER_SUPPLY_PROP_CAPACITY: 286 val->intval = di->regs.rsoc; 287 if (val->intval < 0) 288 return val->intval; 289 break; 290 case POWER_SUPPLY_PROP_PRESENT: 291 val->intval = !(di->regs.rsoc < 0); 292 break; 293 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: 294 if (di->regs.tte < 0) 295 return di->regs.tte; 296 val->intval = 60 * di->regs.tte; 297 break; 298 case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: 299 if (di->regs.ttf < 0) 300 return di->regs.ttf; 301 val->intval = 60 * di->regs.ttf; 302 break; 303 case POWER_SUPPLY_PROP_ONLINE: 304 if (di->pdata->get_charger_online_status) 305 val->intval = (di->pdata->get_charger_online_status)(); 306 else 307 return -EINVAL; 308 break; 309 default: 310 return -EINVAL; 311 } 312 313 return 0; 314} 315 316static void bq27000_battery_work(struct work_struct *work) 317{ 318 struct bq27000_device_info *di = 319 container_of(work, struct bq27000_device_info, work.work); 320 321 if ((di->pdata->hdq_initialized)()) { 322 struct bq27000_bat_regs regs; 323 324 regs.ai = hdq_read16(di, BQ27000_AI_L); 325 regs.flags = (di->pdata->hdq_read)(BQ27000_FLAGS); 326 regs.lmd = hdq_read16(di, BQ27000_LMD_L); 327 regs.rsoc = (di->pdata->hdq_read)(BQ27000_RSOC); 328 regs.temp = hdq_read16(di, BQ27000_TEMP_L); 329 regs.tte = hdq_read16(di, BQ27000_TTE_L); 330 regs.ttf = hdq_read16(di, BQ27000_TTF_L); 331 regs.volt = hdq_read16(di, BQ27000_VOLT_L); 332 333 if (memcmp (®s, &di->regs, sizeof(regs)) != 0) { 334 di->regs = regs; 335 power_supply_changed(&di->bat); 336 } 337 } 338 339 if (!schedule_delayed_work(&di->work, cache_time)) 340 dev_err(di->dev, "battery service reschedule failed\n"); 341} 342 343static enum power_supply_property bq27000_battery_props[] = { 344 POWER_SUPPLY_PROP_STATUS, 345 POWER_SUPPLY_PROP_HEALTH, 346 POWER_SUPPLY_PROP_VOLTAGE_NOW, 347 POWER_SUPPLY_PROP_CURRENT_NOW, 348 POWER_SUPPLY_PROP_CHARGE_FULL, 349 POWER_SUPPLY_PROP_TEMP, 350 POWER_SUPPLY_PROP_TECHNOLOGY, 351 POWER_SUPPLY_PROP_PRESENT, 352 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, 353 POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, 354 POWER_SUPPLY_PROP_CAPACITY, 355 POWER_SUPPLY_PROP_ONLINE 356}; 357 358static int bq27000_battery_probe(struct platform_device *pdev) 359{ 360 int retval = 0; 361 struct bq27000_device_info *di; 362 struct bq27000_platform_data *pdata; 363 364 dev_info(&pdev->dev, "BQ27000 Battery Driver (C) 2008 Openmoko, Inc\n"); 365 366 di = kzalloc(sizeof(*di), GFP_KERNEL); 367 if (!di) { 368 retval = -ENOMEM; 369 goto di_alloc_failed; 370 } 371 372 platform_set_drvdata(pdev, di); 373 374 pdata = pdev->dev.platform_data; 375 di->dev = &pdev->dev; 376 /* di->w1_dev = pdev->dev.parent; */ 377 di->bat.name = pdata->name; 378 di->bat.type = POWER_SUPPLY_TYPE_BATTERY; 379 di->bat.properties = bq27000_battery_props; 380 di->bat.num_properties = ARRAY_SIZE(bq27000_battery_props); 381 di->bat.get_property = bq27000_battery_get_property; 382 di->bat.external_power_changed = 383 bq27000_battery_external_power_changed; 384 di->bat.use_for_apm = 1; 385 di->pdata = pdata; 386 387 retval = power_supply_register(&pdev->dev, &di->bat); 388 if (retval) { 389 dev_err(di->dev, "failed to register battery\n"); 390 goto batt_failed; 391 } 392 393 INIT_DELAYED_WORK(&di->work, bq27000_battery_work); 394 395 if (!schedule_delayed_work(&di->work, 0)) 396 dev_err(di->dev, "failed to schedule bq27000_battery_work\n"); 397 398 return 0; 399 400batt_failed: 401 kfree(di); 402di_alloc_failed: 403 return retval; 404} 405 406static int bq27000_battery_remove(struct platform_device *pdev) 407{ 408 struct bq27000_device_info *di = platform_get_drvdata(pdev); 409 410 cancel_delayed_work(&di->work); 411 412 power_supply_unregister(&di->bat); 413 414 return 0; 415} 416 417void bq27000_charging_state_change(struct platform_device *pdev) 418{ 419 struct bq27000_device_info *di = platform_get_drvdata(pdev); 420 421 if (!di) 422 return; 423} 424EXPORT_SYMBOL_GPL(bq27000_charging_state_change); 425 426#ifdef CONFIG_PM 427 428static int bq27000_battery_suspend(struct platform_device *pdev, 429 pm_message_t state) 430{ 431 struct bq27000_device_info *di = platform_get_drvdata(pdev); 432 433 cancel_delayed_work(&di->work); 434 return 0; 435} 436 437static int bq27000_battery_resume(struct platform_device *pdev) 438{ 439 struct bq27000_device_info *di = platform_get_drvdata(pdev); 440 441 schedule_delayed_work(&di->work, 0); 442 return 0; 443} 444 445#else 446 447#define bq27000_battery_suspend NULL 448#define bq27000_battery_resume NULL 449 450#endif /* CONFIG_PM */ 451 452static struct platform_driver bq27000_battery_driver = { 453 .driver = { 454 .name = "bq27000-battery", 455 }, 456 .probe = bq27000_battery_probe, 457 .remove = bq27000_battery_remove, 458 .suspend = bq27000_battery_suspend, 459 .resume = bq27000_battery_resume, 460}; 461 462static int __init bq27000_battery_init(void) 463{ 464 return platform_driver_register(&bq27000_battery_driver); 465} 466 467static void __exit bq27000_battery_exit(void) 468{ 469 platform_driver_unregister(&bq27000_battery_driver); 470} 471 472module_init(bq27000_battery_init); 473module_exit(bq27000_battery_exit); 474 475MODULE_LICENSE("GPL"); 476MODULE_AUTHOR("Andy Green <andy@openmoko.com>"); 477MODULE_DESCRIPTION("bq27000 battery driver"); 478