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/platform_device.h>
33 #include <linux/power_supply.h>
34 #include <linux/bq27000_battery.h>
38 /* read-write after this */
39 BQ27000_CTRL = 0, /* Device Control Register */
40 BQ27000_MODE, /* Device Mode Register */
41 BQ27000_AR_L, /* At-Rate H L */
43 /* read-only after this */
44 BQ27000_ARTTE_L, /* At-Rate Time To Empty H L */
46 BQ27000_TEMP_L, /* Reported Temperature H L */
48 BQ27000_VOLT_L, /* Reported Voltage H L */
50 BQ27000_FLAGS, /* Status Flags */
51 BQ27000_RSOC, /* Relative State of Charge */
52 BQ27000_NAC_L, /* Nominal Available Capacity H L */
54 BQ27000_CACD_L, /* Discharge Compensated H L */
56 BQ27000_CACT_L, /* Temperature Compensated H L */
58 BQ27000_LMD_L, /* Last measured discharge H L */
60 BQ27000_AI_L, /* Average Current H L */
62 BQ27000_TTE_L, /* Time to Empty H L */
64 BQ27000_TTF_L, /* Time to Full H L */
66 BQ27000_SI_L, /* Standby Current H L */
68 BQ27000_STTE_L, /* Standby Time To Empty H L */
70 BQ27000_MLI_L, /* Max Load Current H L */
72 BQ27000_MLTTE_L, /* Max Load Time To Empty H L */
74 BQ27000_SAE_L, /* Available Energy H L */
76 BQ27000_AP_L, /* Available Power H L */
78 BQ27000_TTECP_L, /* Time to Empty at Constant Power H L */
80 BQ27000_CYCL_L, /* Cycle count since learning cycle H L */
82 BQ27000_CYCT_L, /* Cycle Count Total H L */
84 BQ27000_CSOC, /* Compensated State Of Charge */
86 /* read-write after this */
87 BQ27000_EE_EE_EN = 0x6e, /* EEPROM Program Enable */
88 BQ27000_EE_ILMD = 0x76, /* Initial Last Measured Discharge High Byte */
89 BQ27000_EE_SEDVF, /* Scaled EDVF Threshold */
90 BQ27000_EE_SEDV1, /* Scaled EDV1 Threshold */
91 BQ27000_EE_ISLC, /* Initial Standby Load Current */
92 BQ27000_EE_DMFSD, /* Digital Magnitude Filter and Self Discharge */
93 BQ27000_EE_TAPER, /* Aging Estimate Enable, Charge Termination Taper */
94 BQ27000_EE_PKCFG, /* Pack Configuration Values */
95 BQ27000_EE_IMLC, /* Initial Max Load Current or ID #3 */
96 BQ27000_EE_DCOMP, /* Discharge rate compensation constants or ID #2 */
97 BQ27000_EE_TCOMP, /* Temperature Compensation constants or ID #1 */
100 enum bq27000_status_flags {
101 BQ27000_STATUS_CHGS = 0x80, /* 1 = being charged */
102 BQ27000_STATUS_NOACT = 0x40, /* 1 = no activity */
103 BQ27000_STATUS_IMIN = 0x20, /* 1 = Lion taper current mode */
104 BQ27000_STATUS_CI = 0x10, /* 1 = capacity likely innacurate */
105 BQ27000_STATUS_CALIP = 0x08, /* 1 = calibration in progress */
106 BQ27000_STATUS_VDQ = 0x04, /* 1 = capacity should be accurate */
107 BQ27000_STATUS_EDV1 = 0x02, /* 1 = end of discharge.. <6% left */
108 BQ27000_STATUS_EDVF = 0x01, /* 1 = no, it's really empty now */
111 #define NANOVOLTS_UNIT 3750
113 struct bq27000_device_info {
115 struct power_supply bat;
116 struct bq27000_platform_data *pdata;
120 * reading 16 bit values over HDQ has a special hazard where the
121 * hdq device firmware can update the 16-bit register during the time we
122 * read the two halves. TI document SLUS556D recommends the algorithm here
126 static int hdq_read16(struct bq27000_device_info *di, int address)
134 high = (di->pdata->hdq_read)(address + 1); /* high part */
138 acc = (di->pdata->hdq_read)(address);
142 /* confirm high didn't change between reading it and low */
143 if (high == (di->pdata->hdq_read)(address + 1))
144 return (high << 8) | acc;
150 #define to_bq27000_device_info(x) container_of((x), \
151 struct bq27000_device_info, \
154 static void bq27000_battery_external_power_changed(struct power_supply *psy)
156 struct bq27000_device_info *di = to_bq27000_device_info(psy);
158 dev_dbg(di->dev, "%s\n", __FUNCTION__);
161 static int bq27000_battery_get_property(struct power_supply *psy,
162 enum power_supply_property psp,
163 union power_supply_propval *val)
166 struct bq27000_device_info *di = to_bq27000_device_info(psy);
168 if (!(di->pdata->hdq_initialized)())
172 case POWER_SUPPLY_PROP_STATUS:
173 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
175 if (!di->pdata->get_charger_online_status)
177 if ((di->pdata->get_charger_online_status)()) {
179 * charger is definitively present
180 * we report our state in terms of what it says it
183 if (!di->pdata->get_charger_active_status)
185 if ((di->pdata->get_charger_active_status)())
186 val->intval = POWER_SUPPLY_STATUS_CHARGING;
188 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
193 * either the charger is not connected, or the
194 * platform doesn't give info about charger, use battery state
195 * but... battery state can be out of date by 4 seconds or
196 * so... use the platform callbacks if possible.
198 v = hdq_read16(di, BQ27000_AI_L);
202 /* no real activity on the battery */
204 if (!hdq_read16(di, BQ27000_TTF_L))
205 val->intval = POWER_SUPPLY_STATUS_FULL;
207 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
210 /* power is actually going in or out... */
211 v = (di->pdata->hdq_read)(BQ27000_FLAGS);
214 if (v & BQ27000_STATUS_CHGS)
215 val->intval = POWER_SUPPLY_STATUS_CHARGING;
217 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
219 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
220 v = hdq_read16(di, BQ27000_VOLT_L);
224 val->intval = v * 1000;
226 case POWER_SUPPLY_PROP_CURRENT_NOW:
227 v = (di->pdata->hdq_read)(BQ27000_FLAGS);
230 if (v & BQ27000_STATUS_CHGS)
234 v = hdq_read16(di, BQ27000_AI_L);
237 val->intval = (v * n) / di->pdata->rsense_mohms;
239 case POWER_SUPPLY_PROP_CHARGE_FULL:
240 v = hdq_read16(di, BQ27000_LMD_L);
243 val->intval = (v * 3570) / di->pdata->rsense_mohms;
245 case POWER_SUPPLY_PROP_TEMP:
246 v = hdq_read16(di, BQ27000_TEMP_L);
249 /* K (in 0.25K units) is 273.15 up from C (in 0.1C)*/
250 /* 10926 = 27315 * 4 / 10 */
251 val->intval = (((long)v * 10l) - 10926) / 4;
253 case POWER_SUPPLY_PROP_TECHNOLOGY:
254 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
256 case POWER_SUPPLY_PROP_CAPACITY:
257 val->intval = (di->pdata->hdq_read)(BQ27000_RSOC);
261 case POWER_SUPPLY_PROP_PRESENT:
262 v = (di->pdata->hdq_read)(BQ27000_RSOC);
263 val->intval = !(v < 0);
265 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
266 v = hdq_read16(di, BQ27000_TTE_L);
269 val->intval = 60 * v;
271 case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
272 v = hdq_read16(di, BQ27000_TTF_L);
275 val->intval = 60 * v;
277 case POWER_SUPPLY_PROP_ONLINE:
278 if (di->pdata->get_charger_online_status)
279 val->intval = (di->pdata->get_charger_online_status)();
290 static enum power_supply_property bq27000_battery_props[] = {
291 POWER_SUPPLY_PROP_STATUS,
292 POWER_SUPPLY_PROP_VOLTAGE_NOW,
293 POWER_SUPPLY_PROP_CURRENT_NOW,
294 POWER_SUPPLY_PROP_CHARGE_FULL,
295 POWER_SUPPLY_PROP_TEMP,
296 POWER_SUPPLY_PROP_TECHNOLOGY,
297 POWER_SUPPLY_PROP_PRESENT,
298 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
299 POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
300 POWER_SUPPLY_PROP_CAPACITY,
301 POWER_SUPPLY_PROP_ONLINE
304 static int bq27000_battery_probe(struct platform_device *pdev)
307 struct bq27000_device_info *di;
308 struct bq27000_platform_data *pdata;
310 dev_info(&pdev->dev, "BQ27000 Battery Driver (C) 2008 Openmoko, Inc\n");
312 di = kzalloc(sizeof(*di), GFP_KERNEL);
315 goto di_alloc_failed;
318 platform_set_drvdata(pdev, di);
320 pdata = pdev->dev.platform_data;
321 di->dev = &pdev->dev;
322 /* di->w1_dev = pdev->dev.parent; */
323 di->bat.name = pdata->name;
324 di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
325 di->bat.properties = bq27000_battery_props;
326 di->bat.num_properties = ARRAY_SIZE(bq27000_battery_props);
327 di->bat.get_property = bq27000_battery_get_property;
328 di->bat.external_power_changed =
329 bq27000_battery_external_power_changed;
330 di->bat.use_for_apm = 1;
333 retval = power_supply_register(&pdev->dev, &di->bat);
335 dev_err(di->dev, "failed to register battery\n");
347 static int bq27000_battery_remove(struct platform_device *pdev)
349 struct bq27000_device_info *di = platform_get_drvdata(pdev);
351 power_supply_unregister(&di->bat);
356 void bq27000_charging_state_change(struct platform_device *pdev)
358 struct bq27000_device_info *di = platform_get_drvdata(pdev);
363 power_supply_changed(&di->bat);
365 EXPORT_SYMBOL_GPL(bq27000_charging_state_change);
369 static int bq27000_battery_suspend(struct platform_device *pdev,
375 static int bq27000_battery_resume(struct platform_device *pdev)
382 #define bq27000_battery_suspend NULL
383 #define bq27000_battery_resume NULL
385 #endif /* CONFIG_PM */
387 static struct platform_driver bq27000_battery_driver = {
389 .name = "bq27000-battery",
391 .probe = bq27000_battery_probe,
392 .remove = bq27000_battery_remove,
393 .suspend = bq27000_battery_suspend,
394 .resume = bq27000_battery_resume,
397 static int __init bq27000_battery_init(void)
399 return platform_driver_register(&bq27000_battery_driver);
402 static void __exit bq27000_battery_exit(void)
404 platform_driver_unregister(&bq27000_battery_driver);
407 module_init(bq27000_battery_init);
408 module_exit(bq27000_battery_exit);
410 MODULE_LICENSE("GPL");
411 MODULE_AUTHOR("Andy Green <andy@openmoko.com>");
412 MODULE_DESCRIPTION("bq27000 battery driver");