2 * Driver for batteries with bq27000 chips inside via HDQ
4 * Copyright 2008 Openmoko, Inc
5 * Andy Green <andy@openmoko.com>
7 * based on ds2760 driver, original copyright notice for that --->
9 * Copyright © 2007 Anton Vorontsov
10 * 2004-2007 Matt Reimer
11 * 2004 Szabolcs Gyurko
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.
17 * Author: Anton Vorontsov <cbou@mail.ru>
20 * Matt Reimer <mreimer@vpop.net>
21 * April 2004, 2005, 2007
23 * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
27 #include <linux/module.h>
28 #include <linux/param.h>
29 #include <linux/jiffies.h>
30 #include <linux/delay.h>
32 #include <linux/workqueue.h>
33 #include <linux/platform_device.h>
34 #include <linux/power_supply.h>
35 #include <linux/bq27000_battery.h>
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 */
44 /* read-only after this */
45 BQ27000_ARTTE_L
, /* At-Rate Time To Empty H L */
47 BQ27000_TEMP_L
, /* Reported Temperature H L */
49 BQ27000_VOLT_L
, /* Reported Voltage H L */
51 BQ27000_FLAGS
, /* Status Flags */
52 BQ27000_RSOC
, /* Relative State of Charge */
53 BQ27000_NAC_L
, /* Nominal Available Capacity H L */
55 BQ27000_CACD_L
, /* Discharge Compensated H L */
57 BQ27000_CACT_L
, /* Temperature Compensated H L */
59 BQ27000_LMD_L
, /* Last measured discharge H L */
61 BQ27000_AI_L
, /* Average Current H L */
63 BQ27000_TTE_L
, /* Time to Empty H L */
65 BQ27000_TTF_L
, /* Time to Full H L */
67 BQ27000_SI_L
, /* Standby Current H L */
69 BQ27000_STTE_L
, /* Standby Time To Empty H L */
71 BQ27000_MLI_L
, /* Max Load Current H L */
73 BQ27000_MLTTE_L
, /* Max Load Time To Empty H L */
75 BQ27000_SAE_L
, /* Available Energy H L */
77 BQ27000_AP_L
, /* Available Power H L */
79 BQ27000_TTECP_L
, /* Time to Empty at Constant Power H L */
81 BQ27000_CYCL_L
, /* Cycle count since learning cycle H L */
83 BQ27000_CYCT_L
, /* Cycle Count Total H L */
85 BQ27000_CSOC
, /* Compensated State Of Charge */
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 */
101 enum 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 */
112 #define NANOVOLTS_UNIT 3750
114 struct bq27000_bat_regs
{
125 struct bq27000_device_info
{
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
;
133 struct bq27000_bat_regs regs
;
136 static unsigned int cache_time
= 5000;
137 module_param(cache_time
, uint
, 0644);
138 MODULE_PARM_DESC(cache_time
, "cache time in milliseconds");
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
147 static int hdq_read16(struct bq27000_device_info
*di
, int address
)
155 high
= (di
->pdata
->hdq_read
)(address
+ 1); /* high part */
159 acc
= (di
->pdata
->hdq_read
)(address
);
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
;
171 static void bq27000_battery_external_power_changed(struct power_supply
*psy
)
173 struct bq27000_device_info
*di
= container_of(psy
, struct bq27000_device_info
, bat
);
175 dev_dbg(di
->dev
, "%s\n", __FUNCTION__
);
176 schedule_delayed_work(&di
->work
, 0);
179 static int bq27000_battery_get_property(struct power_supply
*psy
,
180 enum power_supply_property psp
,
181 union power_supply_propval
*val
)
184 struct bq27000_device_info
*di
= container_of(psy
, struct bq27000_device_info
, bat
);
186 if (di
->regs
.rsoc
< 0 && psp
!= POWER_SUPPLY_PROP_PRESENT
)
190 case POWER_SUPPLY_PROP_STATUS
:
191 val
->intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
193 if (!di
->pdata
->get_charger_online_status
)
195 if ((di
->pdata
->get_charger_online_status
)()) {
197 * charger is definitively present
198 * we report our state in terms of what it says it
201 if (!di
->pdata
->get_charger_active_status
)
204 if ((di
->pdata
->get_charger_active_status
)()) {
205 val
->intval
= POWER_SUPPLY_STATUS_CHARGING
;
208 val
->intval
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
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 --->
218 val
->intval
= POWER_SUPPLY_STATUS_DISCHARGING
;
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.
229 /* no real activity on the battery */
230 if (di
->regs
.ai
< 2) {
232 val
->intval
= POWER_SUPPLY_STATUS_FULL
;
234 val
->intval
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
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
;
243 val
->intval
= POWER_SUPPLY_STATUS_DISCHARGING
;
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
;
253 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
254 if (di
->regs
.volt
< 0)
255 return di
->regs
.volt
;
257 val
->intval
= di
->regs
.volt
* 1000;
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
)
268 val
->intval
= (di
->regs
.ai
* n
) / di
->pdata
->rsense_mohms
;
270 case POWER_SUPPLY_PROP_CHARGE_FULL
:
271 if (di
->regs
.lmd
< 0)
273 val
->intval
= (di
->regs
.lmd
* 3570) / di
->pdata
->rsense_mohms
;
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;
282 case POWER_SUPPLY_PROP_TECHNOLOGY
:
283 val
->intval
= POWER_SUPPLY_TECHNOLOGY_LION
;
285 case POWER_SUPPLY_PROP_CAPACITY
:
286 val
->intval
= di
->regs
.rsoc
;
290 case POWER_SUPPLY_PROP_PRESENT
:
291 val
->intval
= !(di
->regs
.rsoc
< 0);
293 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW
:
294 if (di
->regs
.tte
< 0)
296 val
->intval
= 60 * di
->regs
.tte
;
298 case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW
:
299 if (di
->regs
.ttf
< 0)
301 val
->intval
= 60 * di
->regs
.ttf
;
303 case POWER_SUPPLY_PROP_ONLINE
:
304 if (di
->pdata
->get_charger_online_status
)
305 val
->intval
= (di
->pdata
->get_charger_online_status
)();
316 static void bq27000_battery_work(struct work_struct
*work
)
318 struct bq27000_device_info
*di
=
319 container_of(work
, struct bq27000_device_info
, work
.work
);
321 if ((di
->pdata
->hdq_initialized
)()) {
322 struct bq27000_bat_regs regs
;
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
);
333 if (memcmp (®s
, &di
->regs
, sizeof(regs
)) != 0) {
335 power_supply_changed(&di
->bat
);
339 if (!schedule_delayed_work(&di
->work
, cache_time
))
340 dev_err(di
->dev
, "battery service reschedule failed\n");
343 static 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
358 static int bq27000_battery_probe(struct platform_device
*pdev
)
361 struct bq27000_device_info
*di
;
362 struct bq27000_platform_data
*pdata
;
364 dev_info(&pdev
->dev
, "BQ27000 Battery Driver (C) 2008 Openmoko, Inc\n");
366 di
= kzalloc(sizeof(*di
), GFP_KERNEL
);
369 goto di_alloc_failed
;
372 platform_set_drvdata(pdev
, di
);
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;
387 retval
= power_supply_register(&pdev
->dev
, &di
->bat
);
389 dev_err(di
->dev
, "failed to register battery\n");
393 INIT_DELAYED_WORK(&di
->work
, bq27000_battery_work
);
395 if (!schedule_delayed_work(&di
->work
, 0))
396 dev_err(di
->dev
, "failed to schedule bq27000_battery_work\n");
406 static int bq27000_battery_remove(struct platform_device
*pdev
)
408 struct bq27000_device_info
*di
= platform_get_drvdata(pdev
);
410 cancel_delayed_work(&di
->work
);
412 power_supply_unregister(&di
->bat
);
417 void bq27000_charging_state_change(struct platform_device
*pdev
)
419 struct bq27000_device_info
*di
= platform_get_drvdata(pdev
);
424 EXPORT_SYMBOL_GPL(bq27000_charging_state_change
);
428 static int bq27000_battery_suspend(struct platform_device
*pdev
,
431 struct bq27000_device_info
*di
= platform_get_drvdata(pdev
);
433 cancel_delayed_work(&di
->work
);
437 static int bq27000_battery_resume(struct platform_device
*pdev
)
439 struct bq27000_device_info
*di
= platform_get_drvdata(pdev
);
441 schedule_delayed_work(&di
->work
, 0);
447 #define bq27000_battery_suspend NULL
448 #define bq27000_battery_resume NULL
450 #endif /* CONFIG_PM */
452 static struct platform_driver bq27000_battery_driver
= {
454 .name
= "bq27000-battery",
456 .probe
= bq27000_battery_probe
,
457 .remove
= bq27000_battery_remove
,
458 .suspend
= bq27000_battery_suspend
,
459 .resume
= bq27000_battery_resume
,
462 static int __init
bq27000_battery_init(void)
464 return platform_driver_register(&bq27000_battery_driver
);
467 static void __exit
bq27000_battery_exit(void)
469 platform_driver_unregister(&bq27000_battery_driver
);
472 module_init(bq27000_battery_init
);
473 module_exit(bq27000_battery_exit
);
475 MODULE_LICENSE("GPL");
476 MODULE_AUTHOR("Andy Green <andy@openmoko.com>");
477 MODULE_DESCRIPTION("bq27000 battery driver");