olpc_battery: Fix up eeprom read function
[pandora-kernel.git] / drivers / power / olpc_battery.c
1 /*
2  * Battery driver for One Laptop Per Child board.
3  *
4  *      Copyright © 2006  David Woodhouse <dwmw2@infradead.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/err.h>
14 #include <linux/platform_device.h>
15 #include <linux/power_supply.h>
16 #include <linux/jiffies.h>
17 #include <linux/sched.h>
18 #include <asm/olpc.h>
19
20
21 #define EC_BAT_VOLTAGE  0x10    /* uint16_t,    *9.76/32,    mV   */
22 #define EC_BAT_CURRENT  0x11    /* int16_t,     *15.625/120, mA   */
23 #define EC_BAT_ACR      0x12    /* int16_t,     *6250/15,    µAh  */
24 #define EC_BAT_TEMP     0x13    /* uint16_t,    *100/256,   °C  */
25 #define EC_AMB_TEMP     0x14    /* uint16_t,    *100/256,   °C  */
26 #define EC_BAT_STATUS   0x15    /* uint8_t,     bitmask */
27 #define EC_BAT_SOC      0x16    /* uint8_t,     percentage */
28 #define EC_BAT_SERIAL   0x17    /* uint8_t[6] */
29 #define EC_BAT_EEPROM   0x18    /* uint8_t adr as input, uint8_t output */
30 #define EC_BAT_ERRCODE  0x1f    /* uint8_t,     bitmask */
31
32 #define BAT_STAT_PRESENT        0x01
33 #define BAT_STAT_FULL           0x02
34 #define BAT_STAT_LOW            0x04
35 #define BAT_STAT_DESTROY        0x08
36 #define BAT_STAT_AC             0x10
37 #define BAT_STAT_CHARGING       0x20
38 #define BAT_STAT_DISCHARGING    0x40
39
40 #define BAT_ERR_INFOFAIL        0x02
41 #define BAT_ERR_OVERVOLTAGE     0x04
42 #define BAT_ERR_OVERTEMP        0x05
43 #define BAT_ERR_GAUGESTOP       0x06
44 #define BAT_ERR_OUT_OF_CONTROL  0x07
45 #define BAT_ERR_ID_FAIL         0x09
46 #define BAT_ERR_ACR_FAIL        0x10
47
48 #define BAT_ADDR_MFR_TYPE       0x5F
49
50 /*********************************************************************
51  *              Power
52  *********************************************************************/
53
54 static int olpc_ac_get_prop(struct power_supply *psy,
55                             enum power_supply_property psp,
56                             union power_supply_propval *val)
57 {
58         int ret = 0;
59         uint8_t status;
60
61         switch (psp) {
62         case POWER_SUPPLY_PROP_ONLINE:
63                 ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
64                 if (ret)
65                         return ret;
66
67                 val->intval = !!(status & BAT_STAT_AC);
68                 break;
69         default:
70                 ret = -EINVAL;
71                 break;
72         }
73         return ret;
74 }
75
76 static enum power_supply_property olpc_ac_props[] = {
77         POWER_SUPPLY_PROP_ONLINE,
78 };
79
80 static struct power_supply olpc_ac = {
81         .name = "olpc-ac",
82         .type = POWER_SUPPLY_TYPE_MAINS,
83         .properties = olpc_ac_props,
84         .num_properties = ARRAY_SIZE(olpc_ac_props),
85         .get_property = olpc_ac_get_prop,
86 };
87
88 static char bat_serial[17]; /* Ick */
89
90 static int olpc_bat_get_status(union power_supply_propval *val, uint8_t ec_byte)
91 {
92         if (olpc_platform_info.ecver > 0x44) {
93                 if (ec_byte & BAT_STAT_CHARGING)
94                         val->intval = POWER_SUPPLY_STATUS_CHARGING;
95                 else if (ec_byte & BAT_STAT_DISCHARGING)
96                         val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
97                 else if (ec_byte & BAT_STAT_FULL)
98                         val->intval = POWER_SUPPLY_STATUS_FULL;
99                 else /* er,... */
100                         val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
101         } else {
102                 /* Older EC didn't report charge/discharge bits */
103                 if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */
104                         val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
105                 else if (ec_byte & BAT_STAT_FULL)
106                         val->intval = POWER_SUPPLY_STATUS_FULL;
107                 else /* Not _necessarily_ true but EC doesn't tell all yet */
108                         val->intval = POWER_SUPPLY_STATUS_CHARGING;
109         }
110
111         return 0;
112 }
113
114 static int olpc_bat_get_health(union power_supply_propval *val)
115 {
116         uint8_t ec_byte;
117         int ret;
118
119         ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
120         if (ret)
121                 return ret;
122
123         switch (ec_byte) {
124         case 0:
125                 val->intval = POWER_SUPPLY_HEALTH_GOOD;
126                 break;
127
128         case BAT_ERR_OVERTEMP:
129                 val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
130                 break;
131
132         case BAT_ERR_OVERVOLTAGE:
133                 val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
134                 break;
135
136         case BAT_ERR_INFOFAIL:
137         case BAT_ERR_OUT_OF_CONTROL:
138         case BAT_ERR_ID_FAIL:
139         case BAT_ERR_ACR_FAIL:
140                 val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
141                 break;
142
143         default:
144                 /* Eep. We don't know this failure code */
145                 ret = -EIO;
146         }
147
148         return ret;
149 }
150
151 static int olpc_bat_get_mfr(union power_supply_propval *val)
152 {
153         uint8_t ec_byte;
154         int ret;
155
156         ec_byte = BAT_ADDR_MFR_TYPE;
157         ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
158         if (ret)
159                 return ret;
160
161         switch (ec_byte >> 4) {
162         case 1:
163                 val->strval = "Gold Peak";
164                 break;
165         case 2:
166                 val->strval = "BYD";
167                 break;
168         default:
169                 val->strval = "Unknown";
170                 break;
171         }
172
173         return ret;
174 }
175
176 static int olpc_bat_get_tech(union power_supply_propval *val)
177 {
178         uint8_t ec_byte;
179         int ret;
180
181         ec_byte = BAT_ADDR_MFR_TYPE;
182         ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
183         if (ret)
184                 return ret;
185
186         switch (ec_byte & 0xf) {
187         case 1:
188                 val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
189                 break;
190         case 2:
191                 val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe;
192                 break;
193         default:
194                 val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
195                 break;
196         }
197
198         return ret;
199 }
200
201 /*********************************************************************
202  *              Battery properties
203  *********************************************************************/
204 static int olpc_bat_get_property(struct power_supply *psy,
205                                  enum power_supply_property psp,
206                                  union power_supply_propval *val)
207 {
208         int ret = 0;
209         __be16 ec_word;
210         uint8_t ec_byte;
211         __be64 ser_buf;
212
213         ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1);
214         if (ret)
215                 return ret;
216
217         /* Theoretically there's a race here -- the battery could be
218            removed immediately after we check whether it's present, and
219            then we query for some other property of the now-absent battery.
220            It doesn't matter though -- the EC will return the last-known
221            information, and it's as if we just ran that _little_ bit faster
222            and managed to read it out before the battery went away. */
223         if (!(ec_byte & BAT_STAT_PRESENT) && psp != POWER_SUPPLY_PROP_PRESENT)
224                 return -ENODEV;
225
226         switch (psp) {
227         case POWER_SUPPLY_PROP_STATUS:
228                 ret = olpc_bat_get_status(val, ec_byte);
229                 if (ret)
230                         return ret;
231                 break;
232         case POWER_SUPPLY_PROP_PRESENT:
233                 val->intval = !!(ec_byte & BAT_STAT_PRESENT);
234                 break;
235
236         case POWER_SUPPLY_PROP_HEALTH:
237                 if (ec_byte & BAT_STAT_DESTROY)
238                         val->intval = POWER_SUPPLY_HEALTH_DEAD;
239                 else {
240                         ret = olpc_bat_get_health(val);
241                         if (ret)
242                                 return ret;
243                 }
244                 break;
245
246         case POWER_SUPPLY_PROP_MANUFACTURER:
247                 ret = olpc_bat_get_mfr(val);
248                 if (ret)
249                         return ret;
250                 break;
251         case POWER_SUPPLY_PROP_TECHNOLOGY:
252                 ret = olpc_bat_get_tech(val);
253                 if (ret)
254                         return ret;
255                 break;
256         case POWER_SUPPLY_PROP_VOLTAGE_AVG:
257                 ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2);
258                 if (ret)
259                         return ret;
260
261                 val->intval = (int)be16_to_cpu(ec_word) * 9760L / 32;
262                 break;
263         case POWER_SUPPLY_PROP_CURRENT_AVG:
264                 ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2);
265                 if (ret)
266                         return ret;
267
268                 val->intval = (int)be16_to_cpu(ec_word) * 15625L / 120;
269                 break;
270         case POWER_SUPPLY_PROP_CAPACITY:
271                 ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1);
272                 if (ret)
273                         return ret;
274                 val->intval = ec_byte;
275                 break;
276         case POWER_SUPPLY_PROP_TEMP:
277                 ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2);
278                 if (ret)
279                         return ret;
280
281                 val->intval = (int)be16_to_cpu(ec_word) * 100 / 256;
282                 break;
283         case POWER_SUPPLY_PROP_TEMP_AMBIENT:
284                 ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2);
285                 if (ret)
286                         return ret;
287
288                 val->intval = (int)be16_to_cpu(ec_word) * 100 / 256;
289                 break;
290         case POWER_SUPPLY_PROP_CHARGE_COUNTER:
291                 ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2);
292                 if (ret)
293                         return ret;
294
295                 val->intval = (int)be16_to_cpu(ec_word) * 6250 / 15;
296                 break;
297         case POWER_SUPPLY_PROP_SERIAL_NUMBER:
298                 ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8);
299                 if (ret)
300                         return ret;
301
302                 sprintf(bat_serial, "%016llx", (long long)be64_to_cpu(ser_buf));
303                 val->strval = bat_serial;
304                 break;
305         default:
306                 ret = -EINVAL;
307                 break;
308         }
309
310         return ret;
311 }
312
313 static enum power_supply_property olpc_bat_props[] = {
314         POWER_SUPPLY_PROP_STATUS,
315         POWER_SUPPLY_PROP_PRESENT,
316         POWER_SUPPLY_PROP_HEALTH,
317         POWER_SUPPLY_PROP_TECHNOLOGY,
318         POWER_SUPPLY_PROP_VOLTAGE_AVG,
319         POWER_SUPPLY_PROP_CURRENT_AVG,
320         POWER_SUPPLY_PROP_CAPACITY,
321         POWER_SUPPLY_PROP_TEMP,
322         POWER_SUPPLY_PROP_TEMP_AMBIENT,
323         POWER_SUPPLY_PROP_MANUFACTURER,
324         POWER_SUPPLY_PROP_SERIAL_NUMBER,
325         POWER_SUPPLY_PROP_CHARGE_COUNTER,
326 };
327
328 /* EEPROM reading goes completely around the power_supply API, sadly */
329
330 #define EEPROM_START    0x20
331 #define EEPROM_END      0x80
332 #define EEPROM_SIZE     (EEPROM_END - EEPROM_START)
333
334 static ssize_t olpc_bat_eeprom_read(struct kobject *kobj,
335                 struct bin_attribute *attr, char *buf, loff_t off, size_t count)
336 {
337         uint8_t ec_byte;
338         int ret;
339         int i;
340
341         if (off >= EEPROM_SIZE)
342                 return 0;
343         if (off + count > EEPROM_SIZE)
344                 count = EEPROM_SIZE - off;
345
346         for (i = 0; i < count; i++) {
347                 ec_byte = EEPROM_START + off + i;
348                 ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &buf[i], 1);
349                 if (ret) {
350                         pr_err("olpc-battery: "
351                                "EC_BAT_EEPROM cmd @ 0x%x failed - %d!\n",
352                                ec_byte, ret);
353                         return -EIO;
354                 }
355         }
356
357         return count;
358 }
359
360 static struct bin_attribute olpc_bat_eeprom = {
361         .attr = {
362                 .name = "eeprom",
363                 .mode = S_IRUGO,
364                 .owner = THIS_MODULE,
365         },
366         .size = 0,
367         .read = olpc_bat_eeprom_read,
368 };
369
370 /*********************************************************************
371  *              Initialisation
372  *********************************************************************/
373
374 static struct platform_device *bat_pdev;
375
376 static struct power_supply olpc_bat = {
377         .properties = olpc_bat_props,
378         .num_properties = ARRAY_SIZE(olpc_bat_props),
379         .get_property = olpc_bat_get_property,
380         .use_for_apm = 1,
381 };
382
383 void olpc_battery_trigger_uevent(unsigned long cause)
384 {
385         if (cause & EC_SCI_SRC_ACPWR)
386                 kobject_uevent(&olpc_ac.dev->kobj, KOBJ_CHANGE);
387         if (cause & (EC_SCI_SRC_BATERR|EC_SCI_SRC_BATSOC|EC_SCI_SRC_BATTERY))
388                 kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE);
389 }
390
391 static int __init olpc_bat_init(void)
392 {
393         int ret = 0;
394         uint8_t status;
395
396         if (!olpc_platform_info.ecver)
397                 return -ENXIO;
398
399         /*
400          * We've seen a number of EC protocol changes; this driver requires
401          * the latest EC protocol, supported by 0x44 and above.
402          */
403         if (olpc_platform_info.ecver < 0x44) {
404                 printk(KERN_NOTICE "OLPC EC version 0x%02x too old for "
405                         "battery driver.\n", olpc_platform_info.ecver);
406                 return -ENXIO;
407         }
408
409         ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
410         if (ret)
411                 return ret;
412
413         /* Ignore the status. It doesn't actually matter */
414
415         bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0);
416         if (IS_ERR(bat_pdev))
417                 return PTR_ERR(bat_pdev);
418
419         ret = power_supply_register(&bat_pdev->dev, &olpc_ac);
420         if (ret)
421                 goto ac_failed;
422
423         olpc_bat.name = bat_pdev->name;
424
425         ret = power_supply_register(&bat_pdev->dev, &olpc_bat);
426         if (ret)
427                 goto battery_failed;
428
429         ret = device_create_bin_file(olpc_bat.dev, &olpc_bat_eeprom);
430         if (ret)
431                 goto eeprom_failed;
432
433         goto success;
434
435 eeprom_failed:
436         power_supply_unregister(&olpc_bat);
437 battery_failed:
438         power_supply_unregister(&olpc_ac);
439 ac_failed:
440         platform_device_unregister(bat_pdev);
441 success:
442         return ret;
443 }
444
445 static void __exit olpc_bat_exit(void)
446 {
447         device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom);
448         power_supply_unregister(&olpc_bat);
449         power_supply_unregister(&olpc_ac);
450         platform_device_unregister(bat_pdev);
451 }
452
453 module_init(olpc_bat_init);
454 module_exit(olpc_bat_exit);
455
456 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
457 MODULE_LICENSE("GPL");
458 MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine");