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