fix-pm-gsm-disable-serial-drive-when-off.patch
[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/platform_device.h>
33 #include <linux/power_supply.h>
34 #include <linux/bq27000_battery.h>
35
36 enum bq27000_regs {
37         /* RAM regs */
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 */
42         BQ27000_AR_H,
43                 /* read-only after this */
44         BQ27000_ARTTE_L, /* At-Rate Time To Empty H L */
45         BQ27000_ARTTE_H,
46         BQ27000_TEMP_L, /* Reported Temperature H L */
47         BQ27000_TEMP_H,
48         BQ27000_VOLT_L, /* Reported Voltage H L */
49         BQ27000_VOLT_H,
50         BQ27000_FLAGS, /* Status Flags */
51         BQ27000_RSOC, /* Relative State of Charge */
52         BQ27000_NAC_L, /* Nominal Available Capacity H L */
53         BQ27000_NAC_H,
54         BQ27000_CACD_L, /* Discharge Compensated H L */
55         BQ27000_CACD_H,
56         BQ27000_CACT_L, /* Temperature Compensated H L */
57         BQ27000_CACT_H,
58         BQ27000_LMD_L, /* Last measured discharge H L */
59         BQ27000_LMD_H,
60         BQ27000_AI_L, /* Average Current H L */
61         BQ27000_AI_H,
62         BQ27000_TTE_L, /* Time to Empty H L */
63         BQ27000_TTE_H,
64         BQ27000_TTF_L, /* Time to Full H L */
65         BQ27000_TTF_H,
66         BQ27000_SI_L, /* Standby Current H L */
67         BQ27000_SI_H,
68         BQ27000_STTE_L, /* Standby Time To Empty H L */
69         BQ27000_STTE_H,
70         BQ27000_MLI_L, /* Max Load Current H L */
71         BQ27000_MLI_H,
72         BQ27000_MLTTE_L, /* Max Load Time To Empty H L */
73         BQ27000_MLTTE_H,
74         BQ27000_SAE_L, /* Available Energy H L */
75         BQ27000_SAE_H,
76         BQ27000_AP_L, /* Available Power H L */
77         BQ27000_AP_H,
78         BQ27000_TTECP_L, /* Time to Empty at Constant Power H L */
79         BQ27000_TTECP_H,
80         BQ27000_CYCL_L, /* Cycle count since learning cycle H L */
81         BQ27000_CYCL_H,
82         BQ27000_CYCT_L, /* Cycle Count Total H L */
83         BQ27000_CYCT_H,
84         BQ27000_CSOC, /* Compensated State Of Charge */
85         /* EEPROM regs */
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 */
98 };
99
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 */
109 };
110
111 #define NANOVOLTS_UNIT 3750
112
113 struct bq27000_device_info {
114         struct device *dev;
115         struct power_supply bat;
116         struct bq27000_platform_data *pdata;
117 };
118
119 /*
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
123  * to avoid trouble
124  */
125
126 static int hdq_read16(struct bq27000_device_info *di, int address)
127 {
128         int acc;
129         int high;
130         int retries = 3;
131
132         while (retries--) {
133
134                 high = (di->pdata->hdq_read)(address + 1); /* high part */
135
136                 if (high < 0)
137                         return high;
138                 acc = (di->pdata->hdq_read)(address);
139                 if (acc < 0)
140                         return acc;
141
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;
145         }
146
147         return -ETIME;
148 }
149
150 #define to_bq27000_device_info(x) container_of((x), \
151                                                struct bq27000_device_info, \
152                                                bat);
153
154 static void bq27000_battery_external_power_changed(struct power_supply *psy)
155 {
156         struct bq27000_device_info *di = to_bq27000_device_info(psy);
157
158         dev_dbg(di->dev, "%s\n", __FUNCTION__);
159 }
160
161 static int bq27000_battery_get_property(struct power_supply *psy,
162                                        enum power_supply_property psp,
163                                        union power_supply_propval *val)
164 {
165         int v, n;
166         struct bq27000_device_info *di = to_bq27000_device_info(psy);
167
168         if (!(di->pdata->hdq_initialized)())
169                 return -EINVAL;
170
171         switch (psp) {
172         case POWER_SUPPLY_PROP_STATUS:
173                 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
174
175                 if (!di->pdata->get_charger_online_status)
176                         goto use_bat;
177                 if ((di->pdata->get_charger_online_status)()) {
178                         /*
179                          * charger is definitively present
180                          * we report our state in terms of what it says it
181                          * is doing
182                          */
183                         if (!di->pdata->get_charger_active_status)
184                                 goto use_bat;
185                         if ((di->pdata->get_charger_active_status)())
186                                 val->intval = POWER_SUPPLY_STATUS_CHARGING;
187                         else
188                                 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
189                         break;
190                 }
191 use_bat:
192                 /*
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.
197                  */
198                 v = hdq_read16(di, BQ27000_AI_L);
199                 if (v < 0)
200                         return v;
201
202                 /* no real activity on the battery */
203                 if (v < 2) {
204                         if (!hdq_read16(di, BQ27000_TTF_L))
205                                 val->intval = POWER_SUPPLY_STATUS_FULL;
206                         else
207                                 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
208                         break;
209                 }
210                 /* power is actually going in or out... */
211                 v = (di->pdata->hdq_read)(BQ27000_FLAGS);
212                 if (v < 0)
213                         return v;
214                 if (v & BQ27000_STATUS_CHGS)
215                         val->intval = POWER_SUPPLY_STATUS_CHARGING;
216                 else
217                         val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
218                 break;
219         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
220                 v = hdq_read16(di, BQ27000_VOLT_L);
221                 if (v < 0)
222                         return v;
223                 /* mV -> uV */
224                 val->intval = v * 1000;
225                 break;
226         case POWER_SUPPLY_PROP_CURRENT_NOW:
227                 v = (di->pdata->hdq_read)(BQ27000_FLAGS);
228                 if (v < 0)
229                         return v;
230                 if (v & BQ27000_STATUS_CHGS)
231                         n = -NANOVOLTS_UNIT;
232                 else
233                         n = NANOVOLTS_UNIT;
234                 v = hdq_read16(di, BQ27000_AI_L);
235                 if (v < 0)
236                         return v;
237                 val->intval = (v * n) / di->pdata->rsense_mohms;
238                 break;
239         case POWER_SUPPLY_PROP_CHARGE_FULL:
240                 v = hdq_read16(di, BQ27000_LMD_L);
241                 if (v < 0)
242                         return v;
243                 val->intval = (v * 3570) / di->pdata->rsense_mohms;
244                 break;
245         case POWER_SUPPLY_PROP_TEMP:
246                 v = hdq_read16(di, BQ27000_TEMP_L);
247                 if (v < 0)
248                         return v;
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;
252                 break;
253         case POWER_SUPPLY_PROP_TECHNOLOGY:
254                 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
255                 break;
256         case POWER_SUPPLY_PROP_CAPACITY:
257                 val->intval = (di->pdata->hdq_read)(BQ27000_RSOC);
258                 if (val->intval < 0)
259                         return val->intval;
260                 break;
261         case POWER_SUPPLY_PROP_PRESENT:
262                 v = (di->pdata->hdq_read)(BQ27000_RSOC);
263                 val->intval = !(v < 0);
264                 break;
265         case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
266                 v = hdq_read16(di, BQ27000_TTE_L);
267                 if (v < 0)
268                         return v;
269                 val->intval = 60 * v;
270                 break;
271         case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
272                 v = hdq_read16(di, BQ27000_TTF_L);
273                 if (v < 0)
274                         return v;
275                 val->intval = 60 * v;
276                 break;
277         case POWER_SUPPLY_PROP_ONLINE:
278                 if (di->pdata->get_charger_online_status)
279                         val->intval = (di->pdata->get_charger_online_status)();
280                 else
281                         return -EINVAL;
282                 break;
283         default:
284                 return -EINVAL;
285         }
286
287         return 0;
288 }
289
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
302 };
303
304 static int bq27000_battery_probe(struct platform_device *pdev)
305 {
306         int retval = 0;
307         struct bq27000_device_info *di;
308         struct bq27000_platform_data *pdata;
309
310         dev_info(&pdev->dev, "BQ27000 Battery Driver (C) 2008 Openmoko, Inc\n");
311
312         di = kzalloc(sizeof(*di), GFP_KERNEL);
313         if (!di) {
314                 retval = -ENOMEM;
315                 goto di_alloc_failed;
316         }
317
318         platform_set_drvdata(pdev, di);
319
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;
331         di->pdata = pdata;
332
333         retval = power_supply_register(&pdev->dev, &di->bat);
334         if (retval) {
335                 dev_err(di->dev, "failed to register battery\n");
336                 goto batt_failed;
337         }
338
339         return 0;
340
341 batt_failed:
342         kfree(di);
343 di_alloc_failed:
344         return retval;
345 }
346
347 static int bq27000_battery_remove(struct platform_device *pdev)
348 {
349         struct bq27000_device_info *di = platform_get_drvdata(pdev);
350
351         power_supply_unregister(&di->bat);
352
353         return 0;
354 }
355
356 void bq27000_charging_state_change(struct platform_device *pdev)
357 {
358         struct bq27000_device_info *di = platform_get_drvdata(pdev);
359
360         if (!di)
361             return;
362
363         power_supply_changed(&di->bat);
364 }
365 EXPORT_SYMBOL_GPL(bq27000_charging_state_change);
366
367 #ifdef CONFIG_PM
368
369 static int bq27000_battery_suspend(struct platform_device *pdev,
370                                   pm_message_t state)
371 {
372         return 0;
373 }
374
375 static int bq27000_battery_resume(struct platform_device *pdev)
376 {
377         return 0;
378 }
379
380 #else
381
382 #define bq27000_battery_suspend NULL
383 #define bq27000_battery_resume NULL
384
385 #endif /* CONFIG_PM */
386
387 static struct platform_driver bq27000_battery_driver = {
388         .driver = {
389                 .name = "bq27000-battery",
390         },
391         .probe    = bq27000_battery_probe,
392         .remove   = bq27000_battery_remove,
393         .suspend  = bq27000_battery_suspend,
394         .resume   = bq27000_battery_resume,
395 };
396
397 static int __init bq27000_battery_init(void)
398 {
399         return platform_driver_register(&bq27000_battery_driver);
400 }
401
402 static void __exit bq27000_battery_exit(void)
403 {
404         platform_driver_unregister(&bq27000_battery_driver);
405 }
406
407 module_init(bq27000_battery_init);
408 module_exit(bq27000_battery_exit);
409
410 MODULE_LICENSE("GPL");
411 MODULE_AUTHOR("Andy Green <andy@openmoko.com>");
412 MODULE_DESCRIPTION("bq27000 battery driver");