Update DEP-5 uri
[debian/omnibook.git] / battery.c
1 /*
2  * battery.c -- battery related functions
3  * 
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2, or (at your option) any
7  * later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * Written by Soós Péter <sp@osb.hu>, 2002-2004
15  * Modified by Mathieu Bérard <mathieu.berard@crans.org>, 2006
16  */
17
18 #include "omnibook.h"
19 #include "hardware.h"
20
21 struct omnibook_battery_info {
22         u8 type;                /* 1 - Li-Ion, 2 NiMH */
23         u16 sn;                 /* Serial number */
24         u16 dv;                 /* Design Voltage */
25         u16 dc;                 /* Design Capacity */
26 };
27
28 struct omnibook_battery_state {
29         u16 pv;                 /* Present Voltage */
30         u16 rc;                 /* Remaining Capacity */
31         u16 lc;                 /* Last Full Capacity */
32         u8 gauge;               /* Gauge in % */
33         u8 status;              /* 0 - unknown, 1 - charged, 2 - discharging, 3 - charging, 4 - critical) */
34 };
35
36 enum {
37         OMNIBOOK_BATTSTAT_UNKNOWN,
38         OMNIBOOK_BATTSTAT_CHARGED,
39         OMNIBOOK_BATTSTAT_DISCHARGING,
40         OMNIBOOK_BATTSTAT_CHARGING,
41         OMNIBOOK_BATTSTAT_CRITICAL
42 };
43
44 #define BAT_OFFSET 0x10
45
46 static int __backend_u16_read(struct omnibook_operation *io_op, u16 *data)
47 {
48         int retval;
49         u8 byte;
50
51         retval = __backend_byte_read(io_op, &byte);
52         if (retval)
53                 return retval;
54         *data = byte;
55         io_op->read_addr += 1;
56         retval = __backend_byte_read(io_op, &byte);
57         *data += (byte << 8);
58         return retval;
59 }
60
61 static int omnibook_battery_present(struct omnibook_operation *io_op, int num)
62 {
63         int retval;
64         u8 bat;
65         int i;
66
67         /*
68          * XE3GF
69          * TSP10
70          * TSM30X
71          * TSM70
72          */
73         if (omnibook_ectype & (XE3GF | TSP10 | TSM70 | TSM30X)) {
74                 io_op->read_addr = XE3GF_BAL;
75                 io_op->read_mask = XE3GF_BAL0_MASK;
76                 for (i = 0; i < num; i++)
77                         io_op->read_mask = io_op->read_mask << 1;
78                 retval = __backend_byte_read(io_op, &bat);
79         /*
80          * XE3GC
81          * AMILOD
82          */
83         } else if (omnibook_ectype & (XE3GC | AMILOD)) {
84                 io_op->read_addr = XE3GC_BAT;
85                 io_op->read_mask = XE3GC_BAT0_MASK;
86                 for (i = 0; i < num; i++)
87                         io_op->read_mask = io_op->read_mask << 1;
88                 retval = __backend_byte_read(io_op, &bat);
89         } else
90                 retval = -ENODEV;
91
92         /* restore default read_mask */
93         io_op->read_mask = 0;
94
95         return !!bat;
96 }
97
98 /*
99  * Get static battery information
100  * All info have to be reread every time because battery sould be cahnged
101  * when laptop is on AC power 
102  * return values:
103  *  < 0 - ERROR
104  *    0 - OK
105  *    1 - Battery is not present
106  *    2 - Not supported
107  */
108 static int omnibook_get_battery_info(struct omnibook_operation *io_op,
109                                      int num,
110                                      struct omnibook_battery_info *battinfo)
111 {
112         int retval;
113         /*
114          * XE3GF
115          * TSP10
116          * TSM70
117          * TSM30X
118          */
119         if (omnibook_ectype & (XE3GF | TSP10 | TSM70 | TSM30X)) {
120                 retval = omnibook_battery_present(io_op, num);
121                 if (retval < 0)
122                         return retval;
123                 if (retval) {
124                         io_op->read_addr = XE3GF_BTY0 + (BAT_OFFSET * num);
125                         if ((retval = __backend_byte_read(io_op, &(*battinfo).type)))
126                                 return retval;
127                         io_op->read_addr = XE3GF_BSN0 + (BAT_OFFSET * num);
128                         if ((retval = __backend_u16_read(io_op, &(*battinfo).sn)))
129                                 return retval;
130                         io_op->read_addr = XE3GF_BDV0 + (BAT_OFFSET * num);
131                         if ((retval = __backend_u16_read(io_op, &(*battinfo).dv)))
132                                 return retval;
133                         io_op->read_addr = XE3GF_BDC0 + (BAT_OFFSET * num);
134                         if ((retval = __backend_u16_read(io_op, &(*battinfo).dc)))
135                                 return retval;
136
137                         (*battinfo).type = ((*battinfo).type & XE3GF_BTY_MASK) ? 1 : 0;
138                 } else
139                         return 1;
140         /*
141          * XE3GC
142          */
143         } else if (omnibook_ectype & (XE3GC)) {
144                 retval = omnibook_battery_present(io_op, num);
145                 if (retval < 0)
146                         return retval;
147                 if (retval) {
148                         io_op->read_addr = XE3GC_BDV0 + (BAT_OFFSET * num);
149                         if ((retval = __backend_u16_read(io_op, &(*battinfo).dv)))
150                                 return retval;
151                         io_op->read_addr = XE3GC_BDC0 + (BAT_OFFSET * num);
152                         if ((retval = __backend_u16_read(io_op, &(*battinfo).dc)))
153                                 return retval;
154                         io_op->read_addr = XE3GC_BTY0 + (BAT_OFFSET * num);
155                         if ((retval = __backend_byte_read(io_op, &(*battinfo).type)))
156                                 return retval;
157
158                         (*battinfo).type = ((*battinfo).type & XE3GC_BTY_MASK) ? 1 : 0;
159                         (*battinfo).sn = 0;     /* Unknown */
160                 } else
161                         return 1;
162                 /*
163                  * AMILOD
164                  */
165         } else if (omnibook_ectype & (AMILOD)) {
166                 retval = omnibook_battery_present(io_op, num);
167                 if (retval < 0)
168                         return retval;
169                 if (retval) {
170                         io_op->read_addr = AMILOD_BDV0 + (BAT_OFFSET * num);
171                         if ((retval = __backend_u16_read(io_op, &(*battinfo).dv)))
172                                 return retval;
173                         io_op->read_addr = AMILOD_BDC0 + (BAT_OFFSET * num);
174                         if ((retval = __backend_u16_read(io_op, &(*battinfo).dc)))
175                                 return retval;
176                         io_op->read_addr = AMILOD_BTY0 + (BAT_OFFSET * num);
177                         if ((retval = __backend_byte_read(io_op, &(*battinfo).type)))
178                                 return retval;
179
180                         (*battinfo).type = ((*battinfo).type & AMILOD_BTY_MASK) ? 1 : 0;
181                         (*battinfo).sn = 0;     /* Unknown */
182                 } else
183                         return 1;
184                 /*
185                  * FIXME
186                  * OB500
187                  * OB510
188                  */
189         } else if (omnibook_ectype & (OB500 | OB510)) {
190                 switch (num) {
191                 case 0:
192                 case 1:
193                 case 2:
194                         break;
195                 default:
196                         return -EINVAL;
197                 }
198                 /*
199                  * OB6000
200                  * OB6100
201                  * XE4500
202                  */
203         } else if (omnibook_ectype & (OB6000 | OB6100 | XE4500)) {
204                 switch (num) {
205                 case 0:
206                 case 1:
207                         break;
208                 default:
209                         return -EINVAL;
210                 }
211         } else
212                 return 2;
213
214         return 0;
215 }
216
217 /*
218  * Get battery status
219  * return values:
220  *  < 0 - ERROR
221  *    0 - OK
222  *    1 - Battery is not present
223  *    2 - Not supported
224  */
225 static int omnibook_get_battery_status(struct omnibook_operation *io_op, 
226                                        int num,
227                                        struct omnibook_battery_state *battstat)
228 {
229         int retval;
230         u8 status;
231         u16 dc;
232         int gauge;
233
234         /*
235          * XE3GF
236          * TSP10
237          * TSM70
238          */
239         if (omnibook_ectype & (XE3GF | TSP10 | TSM70 | TSM30X)) {
240                 retval = omnibook_battery_present(io_op, num);
241                 if (retval < 0)
242                         return retval;
243                 if (retval) {
244                         io_op->read_addr = XE3GF_BST0 + (BAT_OFFSET * num);
245                         if ((retval = __backend_byte_read(io_op, &status)))
246                                 return retval;
247                         io_op->read_addr = XE3GF_BRC0 + (BAT_OFFSET * num);
248                         if ((retval = __backend_u16_read(io_op, &(*battstat).rc)))
249                                 return retval;
250                         io_op->read_addr = XE3GF_BPV0 + (BAT_OFFSET * num);
251                         if ((retval = __backend_u16_read(io_op, &(*battstat).pv)))
252                                 return retval;
253                         io_op->read_addr = XE3GF_BFC0 + (BAT_OFFSET * num);
254                         if ((retval = __backend_u16_read(io_op, &(*battstat).lc)))
255                                 return retval;
256                         io_op->read_addr = XE3GF_GAU0 + (BAT_OFFSET * num);
257                         if ((retval = __backend_byte_read(io_op, &(*battstat).gauge)))
258                                 return retval;
259
260                         if (status & XE3GF_BST_MASK_CRT)
261                                 (*battstat).status = OMNIBOOK_BATTSTAT_CRITICAL;
262                         else if (status & XE3GF_BST_MASK_CHR)
263                                 (*battstat).status = OMNIBOOK_BATTSTAT_CHARGING;
264                         else if (status & XE3GF_BST_MASK_DSC)
265                                 (*battstat).status = OMNIBOOK_BATTSTAT_DISCHARGING;
266                         else if (status & (XE3GF_BST_MASK_CHR | XE3GF_BST_MASK_DSC))
267                                 (*battstat).status = OMNIBOOK_BATTSTAT_UNKNOWN;
268                         else {
269                                 (*battstat).status = OMNIBOOK_BATTSTAT_CHARGED;
270                         }
271                 } else
272                         return 1;
273         /*
274          * XE3GC
275          */
276         } else if (omnibook_ectype & (XE3GC)) {
277                 retval = omnibook_battery_present(io_op, num);
278                 if (retval < 0)
279                         return retval;
280                 if (retval) {
281                         io_op->read_addr = XE3GC_BST0 + (BAT_OFFSET * num);
282                         if ((retval = __backend_byte_read(io_op, &status)))
283                                 return retval;
284                         io_op->read_addr = XE3GC_BRC0 + (BAT_OFFSET * num);
285                         if ((retval = __backend_u16_read(io_op, &(*battstat).rc)))
286                                 return retval;
287                         io_op->read_addr = XE3GC_BPV0 + (BAT_OFFSET * num);
288                         if ((retval = __backend_u16_read(io_op, &(*battstat).pv)))
289                                 return retval;
290                         io_op->read_addr = XE3GC_BDC0 + (BAT_OFFSET * num);
291                         if ((retval = __backend_u16_read(io_op, &dc)))
292                                 return retval;
293
294                         if (status & XE3GC_BST_MASK_CRT)
295                                 (*battstat).status = OMNIBOOK_BATTSTAT_CRITICAL;
296                         else if (status & XE3GC_BST_MASK_CHR)
297                                 (*battstat).status = OMNIBOOK_BATTSTAT_CHARGING;
298                         else if (status & XE3GC_BST_MASK_DSC)
299                                 (*battstat).status = OMNIBOOK_BATTSTAT_DISCHARGING;
300                         else if (status & (XE3GC_BST_MASK_CHR | XE3GC_BST_MASK_DSC))
301                                 (*battstat).status = OMNIBOOK_BATTSTAT_UNKNOWN;
302                         else {
303                                 (*battstat).status = OMNIBOOK_BATTSTAT_CHARGED;
304                         }
305                         gauge = ((*battstat).rc * 100) / dc;
306                         (*battstat).gauge = gauge;
307                         (*battstat).lc = 0;     /* Unknown */
308                 } else
309                         return 1;
310         /*
311          * AMILOD
312          */
313         } else if (omnibook_ectype & (AMILOD)) {
314                 retval = omnibook_battery_present(io_op, num);
315                 if (retval < 0)
316                         return retval;
317                 if (retval) {
318                         io_op->read_addr = AMILOD_BST0 + (BAT_OFFSET * num);
319                         if ((retval = __backend_byte_read(io_op, &status)))
320                                 return retval;
321                         io_op->read_addr = AMILOD_BRC0 + (BAT_OFFSET * num);
322                         if ((retval = __backend_u16_read(io_op, &(*battstat).rc)))
323                                 return retval;
324                         io_op->read_addr = AMILOD_BPV0 + (BAT_OFFSET * num);
325                         if ((retval = __backend_u16_read(io_op, &(*battstat).pv)))
326                                 return retval;
327                         io_op->read_addr = AMILOD_BDC0 + (BAT_OFFSET * num);
328                         if ((retval = __backend_u16_read(io_op, &dc)))
329                                 return retval;
330
331                         if (status & AMILOD_BST_MASK_CRT)
332                                 (*battstat).status = OMNIBOOK_BATTSTAT_CRITICAL;
333                         else if (status & AMILOD_BST_MASK_CHR)
334                                 (*battstat).status = OMNIBOOK_BATTSTAT_CHARGING;
335                         else if (status & AMILOD_BST_MASK_DSC)
336                                 (*battstat).status = OMNIBOOK_BATTSTAT_DISCHARGING;
337                         else if (status & (AMILOD_BST_MASK_CHR | AMILOD_BST_MASK_DSC))
338                                 (*battstat).status = OMNIBOOK_BATTSTAT_UNKNOWN;
339                         else {
340                                 (*battstat).status = OMNIBOOK_BATTSTAT_CHARGED;
341                         }
342                         gauge = ((*battstat).rc * 100) / dc;
343                         (*battstat).gauge = gauge;
344                         (*battstat).lc = 0;     /* Unknown */
345                 } else
346                         return 1;
347                 /*
348                  * OB500
349                  * OB510
350                  */
351         } else if (omnibook_ectype & (OB500 | OB510)) {
352                 switch (num) {
353                 case 0:
354                         io_op->read_addr = OB500_BT1S;
355                         if ((retval = __backend_byte_read(io_op, &status)))
356                                 return retval;
357                         io_op->read_addr = OB500_BT1C;
358                         if ((retval = __backend_u16_read(io_op, &(*battstat).rc)))
359                                 return retval;
360                         io_op->read_addr = OB500_BT1V;
361                         if ((retval = __backend_u16_read(io_op, &(*battstat).pv)))
362                                 return retval;
363                         break;
364                 case 1:
365                         io_op->read_addr = OB500_BT2S;
366                         if ((retval = __backend_byte_read(io_op, &status)))
367                                 return retval;
368                         io_op->read_addr = OB500_BT2C;
369                         if ((retval = __backend_u16_read(io_op, &(*battstat).rc)))
370                                 return retval;
371                         io_op->read_addr = OB500_BT2V;
372                         if ((retval = __backend_u16_read(io_op, &(*battstat).pv)))
373                                 return retval;
374                         break;
375                 case 2:
376                         io_op->read_addr = OB500_BT3S;
377                         if ((retval = __backend_byte_read(io_op, &status)))
378                                 return retval;
379                         io_op->read_addr = OB500_BT3C;
380                         if ((retval = __backend_u16_read(io_op, &(*battstat).rc)))
381                                 return retval;
382                         io_op->read_addr = OB500_BT3V;
383                         if ((retval = __backend_u16_read(io_op, &(*battstat).pv)))
384                                 return retval;
385                         break;
386                 default:
387                         return -EINVAL;
388                 }
389                 if (status & OB500_BST_MASK_CRT)
390                         (*battstat).status = OMNIBOOK_BATTSTAT_CRITICAL;
391                 else if (status & OB500_BST_MASK_CHR)
392                         (*battstat).status = OMNIBOOK_BATTSTAT_CHARGING;
393                 else if (status & OB500_BST_MASK_DSC)
394                         (*battstat).status = OMNIBOOK_BATTSTAT_DISCHARGING;
395                 else if (status & (OB500_BST_MASK_CHR | OB500_BST_MASK_DSC))
396                         (*battstat).status = OMNIBOOK_BATTSTAT_UNKNOWN;
397                 else {
398                         (*battstat).status = OMNIBOOK_BATTSTAT_CHARGED;
399                 }
400                 /*
401                  * OB6000
402                  * OB6100
403                  * XE4500
404                  */
405         } else if (omnibook_ectype & (OB6000 | OB6100 | XE4500)) {
406                 switch (num) {
407                 case 0:
408                         io_op->read_addr = OB500_BT1S;
409                         if ((retval = __backend_byte_read(io_op, &status)))
410                                 return retval;
411                         io_op->read_addr = OB500_BT1C;
412                         if ((retval = __backend_u16_read(io_op, &(*battstat).rc)))
413                                 return retval;
414                         io_op->read_addr = OB500_BT1V;
415                         if ((retval = __backend_u16_read(io_op, &(*battstat).pv)))
416                                 return retval;
417                         break;
418                 case 1:
419                         io_op->read_addr = OB500_BT3S;
420                         if ((retval = __backend_byte_read(io_op, &status)))
421                                 return retval;
422                         io_op->read_addr = OB500_BT3C;
423                         if ((retval = __backend_u16_read(io_op, &(*battstat).rc)))
424                                 return retval;
425                         io_op->read_addr = OB500_BT3V;
426                         if ((retval = __backend_u16_read(io_op, &(*battstat).pv)))
427                                 return retval;
428                         break;
429                 default:
430                         return -EINVAL;
431                 }
432                 if (status & OB500_BST_MASK_CRT)
433                         (*battstat).status = OMNIBOOK_BATTSTAT_CRITICAL;
434                 else if (status & OB500_BST_MASK_CHR)
435                         (*battstat).status = OMNIBOOK_BATTSTAT_CHARGING;
436                 else if (status & OB500_BST_MASK_DSC)
437                         (*battstat).status = OMNIBOOK_BATTSTAT_DISCHARGING;
438                 else if (status & (OB500_BST_MASK_CHR | OB500_BST_MASK_DSC))
439                         (*battstat).status = OMNIBOOK_BATTSTAT_UNKNOWN;
440                 else {
441                         (*battstat).status = OMNIBOOK_BATTSTAT_CHARGED;
442                 }
443         } else {
444                 return 2;
445         }
446         return 0;
447 }
448
449 static int omnibook_battery_read(char *buffer, struct omnibook_operation *io_op)
450 {
451         char *statustr;
452         char *typestr;
453         int max = 0;
454         int num = 0;
455         int len = 0;
456         int retval;
457         int i;
458         struct omnibook_battery_info battinfo;
459         struct omnibook_battery_state battstat;
460         /*
461          * XE3GF
462          * XE3GC
463          * 0B6000
464          * 0B6100
465          * XE4500
466          * AMILOD
467          * TSP10
468          */
469         if (omnibook_ectype & (XE3GF | XE3GC | OB6000 | OB6100 | XE4500 | AMILOD | TSP10))
470                 max = 2;
471         /*
472          * OB500
473          * 0B510
474          */
475         else if (omnibook_ectype & (OB500 | OB510))
476                 max = 3;
477         /*
478          * TSM30X
479          * TSM70
480          */
481         else if (omnibook_ectype & (TSM70 | TSM30X))
482                 max = 1;
483
484         if(mutex_lock_interruptible(&io_op->backend->mutex))
485                         return -ERESTARTSYS;
486
487         for (i = 0; i < max; i++) {
488                 retval = omnibook_get_battery_info(io_op, i, &battinfo);
489                 if (retval == 0) {
490                         num++;
491                         omnibook_get_battery_status(io_op, i, &battstat);
492                         typestr = (battinfo.type) ? "Li-Ion" : "NiMH";
493                         switch (battstat.status) {
494                         case OMNIBOOK_BATTSTAT_CHARGED:
495                                 statustr = "charged";
496                                 break;
497                         case OMNIBOOK_BATTSTAT_DISCHARGING:
498                                 statustr = "discharging";
499                                 break;
500                         case OMNIBOOK_BATTSTAT_CHARGING:
501                                 statustr = "charging";
502                                 break;
503                         case OMNIBOOK_BATTSTAT_CRITICAL:
504                                 statustr = "critical";
505                                 break;
506                         default:
507                                 statustr = "unknown";
508                         }
509
510                         len += sprintf(buffer + len, "Battery:            %11d\n", i);
511                         len += sprintf(buffer + len, "Type:               %11s\n", typestr);
512                         if (battinfo.sn)
513                                 len +=
514                                     sprintf(buffer + len, "Serial Number:      %11d\n",
515                                             battinfo.sn);
516                         len += sprintf(buffer + len, "Present Voltage:    %11d mV\n", battstat.pv);
517                         len += sprintf(buffer + len, "Design Voltage:     %11d mV\n", battinfo.dv);
518                         len += sprintf(buffer + len, "Remaining Capacity: %11d mAh\n", battstat.rc);
519                         if (battstat.lc)
520                                 len +=
521                                     sprintf(buffer + len, "Last Full Capacity: %11d mAh\n",
522                                             battstat.lc);
523                         len += sprintf(buffer + len, "Design Capacity:    %11d mAh\n", battinfo.dc);
524                         len +=
525                             sprintf(buffer + len, "Gauge:              %11d %%\n", battstat.gauge);
526                         len += sprintf(buffer + len, "Status:             %11s\n", statustr);
527                         len += sprintf(buffer + len, "\n");
528                 }
529         }
530         if (num == 0)
531                 len += sprintf(buffer + len, "No battery present\n");
532
533         mutex_unlock(&io_op->backend->mutex);
534
535         return len;
536 }
537
538 static struct omnibook_tbl battery_table[] __initdata = {
539         {XE3GF | XE3GC | AMILOD | TSP10 | TSM70 | TSM30X, {EC,}},
540         {0,}
541 };
542
543 static struct omnibook_feature __declared_feature battery_driver = {
544         .name = "battery",
545 #ifdef CONFIG_OMNIBOOK_LEGACY
546         .enabled = 1,
547 #else
548         .enabled = 0,
549 #endif
550         .read = omnibook_battery_read,
551         .ectypes = XE3GF | XE3GC | AMILOD | TSP10 | TSM70 | TSM30X,     /* FIXME: OB500|OB6000|OB6100|XE4500 */
552         .tbl = battery_table,
553 };
554
555 module_param_named(battery, battery_driver.enabled, int, S_IRUGO);
556 MODULE_PARM_DESC(battery, "Use 0 to disable, 1 to enable battery status monitoring");
557 /* End of file */