ACPI: ibm-acpi: Use a enum to select the thermal sensor reading strategy
[powerpc.git] / drivers / acpi / ibm_acpi.c
1 /*
2  *  ibm_acpi.c - IBM ThinkPad ACPI Extras
3  *
4  *
5  *  Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #define IBM_VERSION "0.12a"
23
24 /*
25  *  Changelog:
26  *  
27  *  2005-08-17  0.12    fix compilation on 2.6.13-rc kernels
28  *  2005-03-17  0.11    support for 600e, 770x
29  *                          thanks to Jamie Lentin <lentinj@dial.pipex.com>
30  *                      support for 770e, G41
31  *                      G40 and G41 don't have a thinklight
32  *                      temperatures no longer experimental
33  *                      experimental brightness control
34  *                      experimental volume control
35  *                      experimental fan enable/disable
36  *  2005-01-16  0.10    fix module loading on R30, R31 
37  *  2005-01-16  0.9     support for 570, R30, R31
38  *                      ultrabay support on A22p, A3x
39  *                      limit arg for cmos, led, beep, drop experimental status
40  *                      more capable led control on A21e, A22p, T20-22, X20
41  *                      experimental temperatures and fan speed
42  *                      experimental embedded controller register dump
43  *                      mark more functions as __init, drop incorrect __exit
44  *                      use MODULE_VERSION
45  *                          thanks to Henrik Brix Andersen <brix@gentoo.org>
46  *                      fix parameter passing on module loading
47  *                          thanks to Rusty Russell <rusty@rustcorp.com.au>
48  *                          thanks to Jim Radford <radford@blackbean.org>
49  *  2004-11-08  0.8     fix init error case, don't return from a macro
50  *                          thanks to Chris Wright <chrisw@osdl.org>
51  *  2004-10-23  0.7     fix module loading on A21e, A22p, T20, T21, X20
52  *                      fix led control on A21e
53  *  2004-10-19  0.6     use acpi_bus_register_driver() to claim HKEY device
54  *  2004-10-18  0.5     thinklight support on A21e, G40, R32, T20, T21, X20
55  *                      proc file format changed
56  *                      video_switch command
57  *                      experimental cmos control
58  *                      experimental led control
59  *                      experimental acpi sounds
60  *  2004-09-16  0.4     support for module parameters
61  *                      hotkey mask can be prefixed by 0x
62  *                      video output switching
63  *                      video expansion control
64  *                      ultrabay eject support
65  *                      removed lcd brightness/on/off control, didn't work
66  *  2004-08-17  0.3     support for R40
67  *                      lcd off, brightness control
68  *                      thinklight on/off
69  *  2004-08-14  0.2     support for T series, X20
70  *                      bluetooth enable/disable
71  *                      hotkey events disabled by default
72  *                      removed fan control, currently useless
73  *  2004-08-09  0.1     initial release, support for X series
74  */
75
76 #include <linux/kernel.h>
77 #include <linux/module.h>
78 #include <linux/init.h>
79 #include <linux/types.h>
80 #include <linux/proc_fs.h>
81 #include <linux/backlight.h>
82 #include <asm/uaccess.h>
83
84 #include <acpi/acpi_drivers.h>
85 #include <acpi/acnamesp.h>
86
87 #define IBM_NAME "ibm"
88 #define IBM_DESC "IBM ThinkPad ACPI Extras"
89 #define IBM_FILE "ibm_acpi"
90 #define IBM_URL "http://ibm-acpi.sf.net/"
91
92 MODULE_AUTHOR("Borislav Deianov");
93 MODULE_DESCRIPTION(IBM_DESC);
94 MODULE_VERSION(IBM_VERSION);
95 MODULE_LICENSE("GPL");
96
97 #define IBM_DIR IBM_NAME
98
99 #define IBM_LOG IBM_FILE ": "
100 #define IBM_ERR    KERN_ERR    IBM_LOG
101 #define IBM_NOTICE KERN_NOTICE IBM_LOG
102 #define IBM_INFO   KERN_INFO   IBM_LOG
103 #define IBM_DEBUG  KERN_DEBUG  IBM_LOG
104
105 #define IBM_MAX_ACPI_ARGS 3
106
107 #define __unused __attribute__ ((unused))
108
109 static int experimental;
110 module_param(experimental, int, 0);
111
112 static acpi_handle root_handle = NULL;
113
114 #define IBM_HANDLE(object, parent, paths...)                    \
115         static acpi_handle  object##_handle;                    \
116         static acpi_handle *object##_parent = &parent##_handle; \
117         static char        *object##_path;                      \
118         static char        *object##_paths[] = { paths }
119
120 /*
121  * The following models are supported to various degrees:
122  *
123  * 570, 600e, 600x, 770e, 770x
124  * A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p
125  * G40, G41
126  * R30, R31, R32, R40, R40e, R50, R50e, R50p, R51
127  * T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43
128  * X20, X21, X22, X23, X24, X30, X31, X40
129  *
130  * The following models have no supported features:
131  *
132  * 240, 240x, i1400
133  *
134  * Still missing DSDTs for the following models:
135  *
136  * A20p, A22e, A22m
137  * R52
138  * S31
139  * T43p
140  */
141
142 IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0",      /* 240, 240x */
143            "\\_SB.PCI.ISA.EC",  /* 570 */
144            "\\_SB.PCI0.ISA0.EC0",       /* 600e/x, 770e, 770x */
145            "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
146            "\\_SB.PCI0.AD4S.EC0",       /* i1400, R30 */
147            "\\_SB.PCI0.ICH3.EC0",       /* R31 */
148            "\\_SB.PCI0.LPC.EC", /* all others */
149     );
150
151 IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA",      /* 570 */
152            "\\_SB.PCI0.AGP0.VID0",      /* 600e/x, 770x */
153            "\\_SB.PCI0.VID0",   /* 770e */
154            "\\_SB.PCI0.VID",    /* A21e, G4x, R50e, X30, X40 */
155            "\\_SB.PCI0.AGP.VID",        /* all others */
156     );                          /* R30, R31 */
157
158 IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID");  /* G41 */
159
160 IBM_HANDLE(cmos, root, "\\UCMS",        /* R50, R50e, R50p, R51, T4x, X31, X40 */
161            "\\CMOS",            /* A3x, G4x, R32, T23, T30, X22-24, X30 */
162            "\\CMS",             /* R40, R40e */
163     );                          /* all others */
164 #ifdef CONFIG_ACPI_IBM_DOCK
165 IBM_HANDLE(dock, root, "\\_SB.GDCK",    /* X30, X31, X40 */
166            "\\_SB.PCI0.DOCK",   /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
167            "\\_SB.PCI0.PCI1.DOCK",      /* all others */
168            "\\_SB.PCI.ISA.SLCE",        /* 570 */
169     );                          /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
170 #endif
171 IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST",        /* 570 */
172            "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
173            "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
174     );                          /* A21e, R30, R31 */
175
176 IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
177            "_EJ0",              /* all others */
178     );                          /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
179
180 IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV",     /* A3x, R32 */
181            "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
182     );                          /* all others */
183
184 IBM_HANDLE(bay2_ej, bay2, "_EJ3",       /* 600e/x, 770e, A3x */
185            "_EJ0",              /* 770x */
186     );                          /* all others */
187
188 /* don't list other alternatives as we install a notify handler on the 570 */
189 IBM_HANDLE(pci, root, "\\_SB.PCI");     /* 570 */
190
191 IBM_HANDLE(hkey, ec, "\\_SB.HKEY",      /* 600e/x, 770e, 770x */
192            "^HKEY",             /* R30, R31 */
193            "HKEY",              /* all others */
194     );                          /* 570 */
195
196 IBM_HANDLE(lght, root, "\\LGHT");       /* A21e, A2xm/p, T20-22, X20-21 */
197 IBM_HANDLE(ledb, ec, "LEDB");   /* G4x */
198
199 IBM_HANDLE(led, ec, "SLED",     /* 570 */
200            "SYSL",              /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
201            "LED",               /* all others */
202     );                          /* R30, R31 */
203
204 IBM_HANDLE(beep, ec, "BEEP");   /* all except R30, R31 */
205 IBM_HANDLE(ecrd, ec, "ECRD");   /* 570 */
206 IBM_HANDLE(ecwr, ec, "ECWR");   /* 570 */
207 IBM_HANDLE(fans, ec, "FANS");   /* X31, X40 */
208
209 IBM_HANDLE(gfan, ec, "GFAN",    /* 570 */
210            "\\FSPD",            /* 600e/x, 770e, 770x */
211     );                          /* all others */
212
213 IBM_HANDLE(sfan, ec, "SFAN",    /* 570 */
214            "JFNS",              /* 770x-JL */
215     );                          /* all others */
216
217 #define IBM_HKEY_HID    "IBM0068"
218 #define IBM_PCI_HID     "PNP0A03"
219
220 enum thermal_access_mode {
221         IBMACPI_THERMAL_NONE = 0,       /* No thermal support */
222         IBMACPI_THERMAL_ACPI_TMP07,     /* Use ACPI TMP0-7 */
223         IBMACPI_THERMAL_ACPI_UPDT,      /* Use ACPI TMP0-7 with UPDT */
224 };
225
226 #define IBMACPI_MAX_THERMAL_SENSORS 8   /* Max thermal sensors supported */
227 struct ibm_thermal_sensors_struct {
228         s32 temp[IBMACPI_MAX_THERMAL_SENSORS];
229 };
230
231 struct ibm_struct {
232         char *name;
233         char param[32];
234
235         char *hid;
236         struct acpi_driver *driver;
237
238         int (*init) (void);
239         int (*read) (char *);
240         int (*write) (char *);
241         void (*exit) (void);
242
243         void (*notify) (struct ibm_struct *, u32);
244         acpi_handle *handle;
245         int type;
246         struct acpi_device *device;
247
248         int driver_registered;
249         int proc_created;
250         int init_called;
251         int notify_installed;
252
253         int experimental;
254 };
255
256 static struct proc_dir_entry *proc_dir = NULL;
257
258 static struct backlight_device *ibm_backlight_device;
259
260 #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
261 #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
262 #define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
263
264 static int acpi_evalf(acpi_handle handle,
265                       void *res, char *method, char *fmt, ...)
266 {
267         char *fmt0 = fmt;
268         struct acpi_object_list params;
269         union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
270         struct acpi_buffer result, *resultp;
271         union acpi_object out_obj;
272         acpi_status status;
273         va_list ap;
274         char res_type;
275         int success;
276         int quiet;
277
278         if (!*fmt) {
279                 printk(IBM_ERR "acpi_evalf() called with empty format\n");
280                 return 0;
281         }
282
283         if (*fmt == 'q') {
284                 quiet = 1;
285                 fmt++;
286         } else
287                 quiet = 0;
288
289         res_type = *(fmt++);
290
291         params.count = 0;
292         params.pointer = &in_objs[0];
293
294         va_start(ap, fmt);
295         while (*fmt) {
296                 char c = *(fmt++);
297                 switch (c) {
298                 case 'd':       /* int */
299                         in_objs[params.count].integer.value = va_arg(ap, int);
300                         in_objs[params.count++].type = ACPI_TYPE_INTEGER;
301                         break;
302                         /* add more types as needed */
303                 default:
304                         printk(IBM_ERR "acpi_evalf() called "
305                                "with invalid format character '%c'\n", c);
306                         return 0;
307                 }
308         }
309         va_end(ap);
310
311         if (res_type != 'v') {
312                 result.length = sizeof(out_obj);
313                 result.pointer = &out_obj;
314                 resultp = &result;
315         } else
316                 resultp = NULL;
317
318         status = acpi_evaluate_object(handle, method, &params, resultp);
319
320         switch (res_type) {
321         case 'd':               /* int */
322                 if (res)
323                         *(int *)res = out_obj.integer.value;
324                 success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
325                 break;
326         case 'v':               /* void */
327                 success = status == AE_OK;
328                 break;
329                 /* add more types as needed */
330         default:
331                 printk(IBM_ERR "acpi_evalf() called "
332                        "with invalid format character '%c'\n", res_type);
333                 return 0;
334         }
335
336         if (!success && !quiet)
337                 printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
338                        method, fmt0, status);
339
340         return success;
341 }
342
343 static void __unused acpi_print_int(acpi_handle handle, char *method)
344 {
345         int i;
346
347         if (acpi_evalf(handle, &i, method, "d"))
348                 printk(IBM_INFO "%s = 0x%x\n", method, i);
349         else
350                 printk(IBM_ERR "error calling %s\n", method);
351 }
352
353 static char *next_cmd(char **cmds)
354 {
355         char *start = *cmds;
356         char *end;
357
358         while ((end = strchr(start, ',')) && end == start)
359                 start = end + 1;
360
361         if (!end)
362                 return NULL;
363
364         *end = 0;
365         *cmds = end + 1;
366         return start;
367 }
368
369 static int driver_init(void)
370 {
371         printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
372         printk(IBM_INFO "%s\n", IBM_URL);
373
374         return 0;
375 }
376
377 static int driver_read(char *p)
378 {
379         int len = 0;
380
381         len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
382         len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
383
384         return len;
385 }
386
387 static int hotkey_supported;
388 static int hotkey_mask_supported;
389 static int hotkey_orig_status;
390 static int hotkey_orig_mask;
391
392 static int hotkey_get(int *status, int *mask)
393 {
394         if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
395                 return 0;
396
397         if (hotkey_mask_supported)
398                 if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
399                         return 0;
400
401         return 1;
402 }
403
404 static int hotkey_set(int status, int mask)
405 {
406         int i;
407
408         if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
409                 return 0;
410
411         if (hotkey_mask_supported)
412                 for (i = 0; i < 32; i++) {
413                         int bit = ((1 << i) & mask) != 0;
414                         if (!acpi_evalf(hkey_handle,
415                                         NULL, "MHKM", "vdd", i + 1, bit))
416                                 return 0;
417                 }
418
419         return 1;
420 }
421
422 static int hotkey_init(void)
423 {
424         /* hotkey not supported on 570 */
425         hotkey_supported = hkey_handle != NULL;
426
427         if (hotkey_supported) {
428                 /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
429                    A30, R30, R31, T20-22, X20-21, X22-24 */
430                 hotkey_mask_supported =
431                     acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
432
433                 if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask))
434                         return -ENODEV;
435         }
436
437         return 0;
438 }
439
440 static int hotkey_read(char *p)
441 {
442         int status, mask;
443         int len = 0;
444
445         if (!hotkey_supported) {
446                 len += sprintf(p + len, "status:\t\tnot supported\n");
447                 return len;
448         }
449
450         if (!hotkey_get(&status, &mask))
451                 return -EIO;
452
453         len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
454         if (hotkey_mask_supported) {
455                 len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
456                 len += sprintf(p + len,
457                                "commands:\tenable, disable, reset, <mask>\n");
458         } else {
459                 len += sprintf(p + len, "mask:\t\tnot supported\n");
460                 len += sprintf(p + len, "commands:\tenable, disable, reset\n");
461         }
462
463         return len;
464 }
465
466 static int hotkey_write(char *buf)
467 {
468         int status, mask;
469         char *cmd;
470         int do_cmd = 0;
471
472         if (!hotkey_supported)
473                 return -ENODEV;
474
475         if (!hotkey_get(&status, &mask))
476                 return -EIO;
477
478         while ((cmd = next_cmd(&buf))) {
479                 if (strlencmp(cmd, "enable") == 0) {
480                         status = 1;
481                 } else if (strlencmp(cmd, "disable") == 0) {
482                         status = 0;
483                 } else if (strlencmp(cmd, "reset") == 0) {
484                         status = hotkey_orig_status;
485                         mask = hotkey_orig_mask;
486                 } else if (sscanf(cmd, "0x%x", &mask) == 1) {
487                         /* mask set */
488                 } else if (sscanf(cmd, "%x", &mask) == 1) {
489                         /* mask set */
490                 } else
491                         return -EINVAL;
492                 do_cmd = 1;
493         }
494
495         if (do_cmd && !hotkey_set(status, mask))
496                 return -EIO;
497
498         return 0;
499 }
500
501 static void hotkey_exit(void)
502 {
503         if (hotkey_supported)
504                 hotkey_set(hotkey_orig_status, hotkey_orig_mask);
505 }
506
507 static void hotkey_notify(struct ibm_struct *ibm, u32 event)
508 {
509         int hkey;
510
511         if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
512                 acpi_bus_generate_event(ibm->device, event, hkey);
513         else {
514                 printk(IBM_ERR "unknown hotkey event %d\n", event);
515                 acpi_bus_generate_event(ibm->device, event, 0);
516         }
517 }
518
519 static int bluetooth_supported;
520
521 static int bluetooth_init(void)
522 {
523         /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
524            G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
525         bluetooth_supported = hkey_handle &&
526             acpi_evalf(hkey_handle, NULL, "GBDC", "qv");
527
528         return 0;
529 }
530
531 static int bluetooth_status(void)
532 {
533         int status;
534
535         if (!bluetooth_supported ||
536             !acpi_evalf(hkey_handle, &status, "GBDC", "d"))
537                 status = 0;
538
539         return status;
540 }
541
542 static int bluetooth_read(char *p)
543 {
544         int len = 0;
545         int status = bluetooth_status();
546
547         if (!bluetooth_supported)
548                 len += sprintf(p + len, "status:\t\tnot supported\n");
549         else if (!(status & 1))
550                 len += sprintf(p + len, "status:\t\tnot installed\n");
551         else {
552                 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
553                 len += sprintf(p + len, "commands:\tenable, disable\n");
554         }
555
556         return len;
557 }
558
559 static int bluetooth_write(char *buf)
560 {
561         int status = bluetooth_status();
562         char *cmd;
563         int do_cmd = 0;
564
565         if (!bluetooth_supported)
566                 return -ENODEV;
567
568         while ((cmd = next_cmd(&buf))) {
569                 if (strlencmp(cmd, "enable") == 0) {
570                         status |= 2;
571                 } else if (strlencmp(cmd, "disable") == 0) {
572                         status &= ~2;
573                 } else
574                         return -EINVAL;
575                 do_cmd = 1;
576         }
577
578         if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
579                 return -EIO;
580
581         return 0;
582 }
583
584 static int wan_supported;
585
586 static int wan_init(void)
587 {
588         wan_supported = hkey_handle &&
589             acpi_evalf(hkey_handle, NULL, "GWAN", "qv");
590
591         return 0;
592 }
593
594 static int wan_status(void)
595 {
596         int status;
597
598         if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d"))
599                 status = 0;
600
601         return status;
602 }
603
604 static int wan_read(char *p)
605 {
606         int len = 0;
607         int status = wan_status();
608
609         if (!wan_supported)
610                 len += sprintf(p + len, "status:\t\tnot supported\n");
611         else if (!(status & 1))
612                 len += sprintf(p + len, "status:\t\tnot installed\n");
613         else {
614                 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
615                 len += sprintf(p + len, "commands:\tenable, disable\n");
616         }
617
618         return len;
619 }
620
621 static int wan_write(char *buf)
622 {
623         int status = wan_status();
624         char *cmd;
625         int do_cmd = 0;
626
627         if (!wan_supported)
628                 return -ENODEV;
629
630         while ((cmd = next_cmd(&buf))) {
631                 if (strlencmp(cmd, "enable") == 0) {
632                         status |= 2;
633                 } else if (strlencmp(cmd, "disable") == 0) {
634                         status &= ~2;
635                 } else
636                         return -EINVAL;
637                 do_cmd = 1;
638         }
639
640         if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
641                 return -EIO;
642
643         return 0;
644 }
645
646 static int video_supported;
647 static int video_orig_autosw;
648
649 #define VIDEO_570 1
650 #define VIDEO_770 2
651 #define VIDEO_NEW 3
652
653 static int video_init(void)
654 {
655         int ivga;
656
657         if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
658                 /* G41, assume IVGA doesn't change */
659                 vid_handle = vid2_handle;
660
661         if (!vid_handle)
662                 /* video switching not supported on R30, R31 */
663                 video_supported = 0;
664         else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
665                 /* 570 */
666                 video_supported = VIDEO_570;
667         else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
668                 /* 600e/x, 770e, 770x */
669                 video_supported = VIDEO_770;
670         else
671                 /* all others */
672                 video_supported = VIDEO_NEW;
673
674         return 0;
675 }
676
677 static int video_status(void)
678 {
679         int status = 0;
680         int i;
681
682         if (video_supported == VIDEO_570) {
683                 if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87))
684                         status = i & 3;
685         } else if (video_supported == VIDEO_770) {
686                 if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
687                         status |= 0x01 * i;
688                 if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
689                         status |= 0x02 * i;
690         } else if (video_supported == VIDEO_NEW) {
691                 acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1);
692                 if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
693                         status |= 0x02 * i;
694
695                 acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0);
696                 if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
697                         status |= 0x01 * i;
698                 if (acpi_evalf(NULL, &i, "\\VCDD", "d"))
699                         status |= 0x08 * i;
700         }
701
702         return status;
703 }
704
705 static int video_autosw(void)
706 {
707         int autosw = 0;
708
709         if (video_supported == VIDEO_570)
710                 acpi_evalf(vid_handle, &autosw, "SWIT", "d");
711         else if (video_supported == VIDEO_770 || video_supported == VIDEO_NEW)
712                 acpi_evalf(vid_handle, &autosw, "^VDEE", "d");
713
714         return autosw & 1;
715 }
716
717 static int video_read(char *p)
718 {
719         int status = video_status();
720         int autosw = video_autosw();
721         int len = 0;
722
723         if (!video_supported) {
724                 len += sprintf(p + len, "status:\t\tnot supported\n");
725                 return len;
726         }
727
728         len += sprintf(p + len, "status:\t\tsupported\n");
729         len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
730         len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
731         if (video_supported == VIDEO_NEW)
732                 len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
733         len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
734         len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
735         len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
736         if (video_supported == VIDEO_NEW)
737                 len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
738         len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
739         len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
740
741         return len;
742 }
743
744 static int video_switch(void)
745 {
746         int autosw = video_autosw();
747         int ret;
748
749         if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
750                 return -EIO;
751         ret = video_supported == VIDEO_570 ?
752             acpi_evalf(ec_handle, NULL, "_Q16", "v") :
753             acpi_evalf(vid_handle, NULL, "VSWT", "v");
754         acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
755
756         return ret;
757 }
758
759 static int video_expand(void)
760 {
761         if (video_supported == VIDEO_570)
762                 return acpi_evalf(ec_handle, NULL, "_Q17", "v");
763         else if (video_supported == VIDEO_770)
764                 return acpi_evalf(vid_handle, NULL, "VEXP", "v");
765         else
766                 return acpi_evalf(NULL, NULL, "\\VEXP", "v");
767 }
768
769 static int video_switch2(int status)
770 {
771         int ret;
772
773         if (video_supported == VIDEO_570) {
774                 ret = acpi_evalf(NULL, NULL,
775                                  "\\_SB.PHS2", "vdd", 0x8b, status | 0x80);
776         } else if (video_supported == VIDEO_770) {
777                 int autosw = video_autosw();
778                 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
779                         return -EIO;
780
781                 ret = acpi_evalf(vid_handle, NULL,
782                                  "ASWT", "vdd", status * 0x100, 0);
783
784                 acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
785         } else {
786                 ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
787                     acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
788         }
789
790         return ret;
791 }
792
793 static int video_write(char *buf)
794 {
795         char *cmd;
796         int enable, disable, status;
797
798         if (!video_supported)
799                 return -ENODEV;
800
801         enable = disable = 0;
802
803         while ((cmd = next_cmd(&buf))) {
804                 if (strlencmp(cmd, "lcd_enable") == 0) {
805                         enable |= 0x01;
806                 } else if (strlencmp(cmd, "lcd_disable") == 0) {
807                         disable |= 0x01;
808                 } else if (strlencmp(cmd, "crt_enable") == 0) {
809                         enable |= 0x02;
810                 } else if (strlencmp(cmd, "crt_disable") == 0) {
811                         disable |= 0x02;
812                 } else if (video_supported == VIDEO_NEW &&
813                            strlencmp(cmd, "dvi_enable") == 0) {
814                         enable |= 0x08;
815                 } else if (video_supported == VIDEO_NEW &&
816                            strlencmp(cmd, "dvi_disable") == 0) {
817                         disable |= 0x08;
818                 } else if (strlencmp(cmd, "auto_enable") == 0) {
819                         if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
820                                 return -EIO;
821                 } else if (strlencmp(cmd, "auto_disable") == 0) {
822                         if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0))
823                                 return -EIO;
824                 } else if (strlencmp(cmd, "video_switch") == 0) {
825                         if (!video_switch())
826                                 return -EIO;
827                 } else if (strlencmp(cmd, "expand_toggle") == 0) {
828                         if (!video_expand())
829                                 return -EIO;
830                 } else
831                         return -EINVAL;
832         }
833
834         if (enable || disable) {
835                 status = (video_status() & 0x0f & ~disable) | enable;
836                 if (!video_switch2(status))
837                         return -EIO;
838         }
839
840         return 0;
841 }
842
843 static void video_exit(void)
844 {
845         acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw);
846 }
847
848 static int light_supported;
849 static int light_status_supported;
850
851 static int light_init(void)
852 {
853         /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
854         light_supported = (cmos_handle || lght_handle) && !ledb_handle;
855
856         if (light_supported)
857                 /* light status not supported on
858                    570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
859                 light_status_supported = acpi_evalf(ec_handle, NULL,
860                                                     "KBLT", "qv");
861
862         return 0;
863 }
864
865 static int light_read(char *p)
866 {
867         int len = 0;
868         int status = 0;
869
870         if (!light_supported) {
871                 len += sprintf(p + len, "status:\t\tnot supported\n");
872         } else if (!light_status_supported) {
873                 len += sprintf(p + len, "status:\t\tunknown\n");
874                 len += sprintf(p + len, "commands:\ton, off\n");
875         } else {
876                 if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
877                         return -EIO;
878                 len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
879                 len += sprintf(p + len, "commands:\ton, off\n");
880         }
881
882         return len;
883 }
884
885 static int light_write(char *buf)
886 {
887         int cmos_cmd, lght_cmd;
888         char *cmd;
889         int success;
890
891         if (!light_supported)
892                 return -ENODEV;
893
894         while ((cmd = next_cmd(&buf))) {
895                 if (strlencmp(cmd, "on") == 0) {
896                         cmos_cmd = 0x0c;
897                         lght_cmd = 1;
898                 } else if (strlencmp(cmd, "off") == 0) {
899                         cmos_cmd = 0x0d;
900                         lght_cmd = 0;
901                 } else
902                         return -EINVAL;
903
904                 success = cmos_handle ?
905                     acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
906                     acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
907                 if (!success)
908                         return -EIO;
909         }
910
911         return 0;
912 }
913
914 static int _sta(acpi_handle handle)
915 {
916         int status;
917
918         if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
919                 status = 0;
920
921         return status;
922 }
923
924 #ifdef CONFIG_ACPI_IBM_DOCK
925 #define dock_docked() (_sta(dock_handle) & 1)
926
927 static int dock_read(char *p)
928 {
929         int len = 0;
930         int docked = dock_docked();
931
932         if (!dock_handle)
933                 len += sprintf(p + len, "status:\t\tnot supported\n");
934         else if (!docked)
935                 len += sprintf(p + len, "status:\t\tundocked\n");
936         else {
937                 len += sprintf(p + len, "status:\t\tdocked\n");
938                 len += sprintf(p + len, "commands:\tdock, undock\n");
939         }
940
941         return len;
942 }
943
944 static int dock_write(char *buf)
945 {
946         char *cmd;
947
948         if (!dock_docked())
949                 return -ENODEV;
950
951         while ((cmd = next_cmd(&buf))) {
952                 if (strlencmp(cmd, "undock") == 0) {
953                         if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
954                             !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
955                                 return -EIO;
956                 } else if (strlencmp(cmd, "dock") == 0) {
957                         if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
958                                 return -EIO;
959                 } else
960                         return -EINVAL;
961         }
962
963         return 0;
964 }
965
966 static void dock_notify(struct ibm_struct *ibm, u32 event)
967 {
968         int docked = dock_docked();
969         int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID);
970
971         if (event == 1 && !pci) /* 570 */
972                 acpi_bus_generate_event(ibm->device, event, 1); /* button */
973         else if (event == 1 && pci)     /* 570 */
974                 acpi_bus_generate_event(ibm->device, event, 3); /* dock */
975         else if (event == 3 && docked)
976                 acpi_bus_generate_event(ibm->device, event, 1); /* button */
977         else if (event == 3 && !docked)
978                 acpi_bus_generate_event(ibm->device, event, 2); /* undock */
979         else if (event == 0 && docked)
980                 acpi_bus_generate_event(ibm->device, event, 3); /* dock */
981         else {
982                 printk(IBM_ERR "unknown dock event %d, status %d\n",
983                        event, _sta(dock_handle));
984                 acpi_bus_generate_event(ibm->device, event, 0); /* unknown */
985         }
986 }
987 #endif
988
989 static int bay_status_supported;
990 static int bay_status2_supported;
991 static int bay_eject_supported;
992 static int bay_eject2_supported;
993
994 static int bay_init(void)
995 {
996         bay_status_supported = bay_handle &&
997             acpi_evalf(bay_handle, NULL, "_STA", "qv");
998         bay_status2_supported = bay2_handle &&
999             acpi_evalf(bay2_handle, NULL, "_STA", "qv");
1000
1001         bay_eject_supported = bay_handle && bay_ej_handle &&
1002             (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
1003         bay_eject2_supported = bay2_handle && bay2_ej_handle &&
1004             (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
1005
1006         return 0;
1007 }
1008
1009 #define bay_occupied(b) (_sta(b##_handle) & 1)
1010
1011 static int bay_read(char *p)
1012 {
1013         int len = 0;
1014         int occupied = bay_occupied(bay);
1015         int occupied2 = bay_occupied(bay2);
1016         int eject, eject2;
1017
1018         len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ?
1019                        (occupied ? "occupied" : "unoccupied") :
1020                        "not supported");
1021         if (bay_status2_supported)
1022                 len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
1023                                "occupied" : "unoccupied");
1024
1025         eject = bay_eject_supported && occupied;
1026         eject2 = bay_eject2_supported && occupied2;
1027
1028         if (eject && eject2)
1029                 len += sprintf(p + len, "commands:\teject, eject2\n");
1030         else if (eject)
1031                 len += sprintf(p + len, "commands:\teject\n");
1032         else if (eject2)
1033                 len += sprintf(p + len, "commands:\teject2\n");
1034
1035         return len;
1036 }
1037
1038 static int bay_write(char *buf)
1039 {
1040         char *cmd;
1041
1042         if (!bay_eject_supported && !bay_eject2_supported)
1043                 return -ENODEV;
1044
1045         while ((cmd = next_cmd(&buf))) {
1046                 if (bay_eject_supported && strlencmp(cmd, "eject") == 0) {
1047                         if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
1048                                 return -EIO;
1049                 } else if (bay_eject2_supported &&
1050                            strlencmp(cmd, "eject2") == 0) {
1051                         if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
1052                                 return -EIO;
1053                 } else
1054                         return -EINVAL;
1055         }
1056
1057         return 0;
1058 }
1059
1060 static void bay_notify(struct ibm_struct *ibm, u32 event)
1061 {
1062         acpi_bus_generate_event(ibm->device, event, 0);
1063 }
1064
1065 static int cmos_read(char *p)
1066 {
1067         int len = 0;
1068
1069         /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
1070            R30, R31, T20-22, X20-21 */
1071         if (!cmos_handle)
1072                 len += sprintf(p + len, "status:\t\tnot supported\n");
1073         else {
1074                 len += sprintf(p + len, "status:\t\tsupported\n");
1075                 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
1076         }
1077
1078         return len;
1079 }
1080
1081 static int cmos_eval(int cmos_cmd)
1082 {
1083         if (cmos_handle)
1084                 return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd);
1085         else
1086                 return 1;
1087 }
1088
1089 static int cmos_write(char *buf)
1090 {
1091         char *cmd;
1092         int cmos_cmd;
1093
1094         if (!cmos_handle)
1095                 return -EINVAL;
1096
1097         while ((cmd = next_cmd(&buf))) {
1098                 if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
1099                     cmos_cmd >= 0 && cmos_cmd <= 21) {
1100                         /* cmos_cmd set */
1101                 } else
1102                         return -EINVAL;
1103
1104                 if (!cmos_eval(cmos_cmd))
1105                         return -EIO;
1106         }
1107
1108         return 0;
1109 }
1110
1111 static int led_supported;
1112
1113 #define LED_570 1
1114 #define LED_OLD 2
1115 #define LED_NEW 3
1116
1117 static int led_init(void)
1118 {
1119         if (!led_handle)
1120                 /* led not supported on R30, R31 */
1121                 led_supported = 0;
1122         else if (strlencmp(led_path, "SLED") == 0)
1123                 /* 570 */
1124                 led_supported = LED_570;
1125         else if (strlencmp(led_path, "SYSL") == 0)
1126                 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
1127                 led_supported = LED_OLD;
1128         else
1129                 /* all others */
1130                 led_supported = LED_NEW;
1131
1132         return 0;
1133 }
1134
1135 #define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
1136
1137 static int led_read(char *p)
1138 {
1139         int len = 0;
1140
1141         if (!led_supported) {
1142                 len += sprintf(p + len, "status:\t\tnot supported\n");
1143                 return len;
1144         }
1145         len += sprintf(p + len, "status:\t\tsupported\n");
1146
1147         if (led_supported == LED_570) {
1148                 /* 570 */
1149                 int i, status;
1150                 for (i = 0; i < 8; i++) {
1151                         if (!acpi_evalf(ec_handle,
1152                                         &status, "GLED", "dd", 1 << i))
1153                                 return -EIO;
1154                         len += sprintf(p + len, "%d:\t\t%s\n",
1155                                        i, led_status(status));
1156                 }
1157         }
1158
1159         len += sprintf(p + len, "commands:\t"
1160                        "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
1161
1162         return len;
1163 }
1164
1165 /* off, on, blink */
1166 static const int led_sled_arg1[] = { 0, 1, 3 };
1167 static const int led_exp_hlbl[] = { 0, 0, 1 };  /* led# * */
1168 static const int led_exp_hlcl[] = { 0, 1, 1 };  /* led# * */
1169 static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
1170
1171 #define EC_HLCL 0x0c
1172 #define EC_HLBL 0x0d
1173 #define EC_HLMS 0x0e
1174
1175 static int led_write(char *buf)
1176 {
1177         char *cmd;
1178         int led, ind, ret;
1179
1180         if (!led_supported)
1181                 return -ENODEV;
1182
1183         while ((cmd = next_cmd(&buf))) {
1184                 if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
1185                         return -EINVAL;
1186
1187                 if (strstr(cmd, "off")) {
1188                         ind = 0;
1189                 } else if (strstr(cmd, "on")) {
1190                         ind = 1;
1191                 } else if (strstr(cmd, "blink")) {
1192                         ind = 2;
1193                 } else
1194                         return -EINVAL;
1195
1196                 if (led_supported == LED_570) {
1197                         /* 570 */
1198                         led = 1 << led;
1199                         if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
1200                                         led, led_sled_arg1[ind]))
1201                                 return -EIO;
1202                 } else if (led_supported == LED_OLD) {
1203                         /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
1204                         led = 1 << led;
1205                         ret = ec_write(EC_HLMS, led);
1206                         if (ret >= 0)
1207                                 ret =
1208                                     ec_write(EC_HLBL, led * led_exp_hlbl[ind]);
1209                         if (ret >= 0)
1210                                 ret =
1211                                     ec_write(EC_HLCL, led * led_exp_hlcl[ind]);
1212                         if (ret < 0)
1213                                 return ret;
1214                 } else {
1215                         /* all others */
1216                         if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
1217                                         led, led_led_arg1[ind]))
1218                                 return -EIO;
1219                 }
1220         }
1221
1222         return 0;
1223 }
1224
1225 static int beep_read(char *p)
1226 {
1227         int len = 0;
1228
1229         if (!beep_handle)
1230                 len += sprintf(p + len, "status:\t\tnot supported\n");
1231         else {
1232                 len += sprintf(p + len, "status:\t\tsupported\n");
1233                 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
1234         }
1235
1236         return len;
1237 }
1238
1239 static int beep_write(char *buf)
1240 {
1241         char *cmd;
1242         int beep_cmd;
1243
1244         if (!beep_handle)
1245                 return -ENODEV;
1246
1247         while ((cmd = next_cmd(&buf))) {
1248                 if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
1249                     beep_cmd >= 0 && beep_cmd <= 17) {
1250                         /* beep_cmd set */
1251                 } else
1252                         return -EINVAL;
1253                 if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
1254                         return -EIO;
1255         }
1256
1257         return 0;
1258 }
1259
1260 static int acpi_ec_read(int i, u8 * p)
1261 {
1262         int v;
1263
1264         if (ecrd_handle) {
1265                 if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
1266                         return 0;
1267                 *p = v;
1268         } else {
1269                 if (ec_read(i, p) < 0)
1270                         return 0;
1271         }
1272
1273         return 1;
1274 }
1275
1276 static int acpi_ec_write(int i, u8 v)
1277 {
1278         if (ecwr_handle) {
1279                 if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
1280                         return 0;
1281         } else {
1282                 if (ec_write(i, v) < 0)
1283                         return 0;
1284         }
1285
1286         return 1;
1287 }
1288
1289 static enum thermal_access_mode thermal_read_mode;
1290
1291 static int thermal_init(void)
1292 {
1293         if (acpi_evalf(ec_handle, NULL, "TMP7", "qv")) {
1294                 if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
1295                         /* 600e/x, 770e, 770x */
1296                         thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT;
1297                 } else {
1298                         /* Standard ACPI TMPx access, max 8 sensors */
1299                         thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;
1300                 }
1301         } else {
1302                 /* temperatures not supported on 570, G4x, R30, R31, R32 */
1303                 thermal_read_mode = IBMACPI_THERMAL_NONE;
1304         }
1305
1306         return 0;
1307 }
1308
1309 static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
1310 {
1311         int i, t;
1312         char tmpi[] = "TMPi";
1313
1314         if (!s)
1315                 return -EINVAL;
1316
1317         switch (thermal_read_mode) {
1318         case IBMACPI_THERMAL_ACPI_UPDT:
1319                 if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
1320                         return -EIO;
1321                 for (i = 0; i < 8; i++) {
1322                         tmpi[3] = '0' + i;
1323                         if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
1324                                 return -EIO;
1325                         s->temp[i] = (t - 2732) * 100;
1326                 }
1327                 return 8;
1328
1329         case IBMACPI_THERMAL_ACPI_TMP07:
1330                 for (i = 0; i < 8; i++) {
1331                         tmpi[3] = '0' + i;
1332                         if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
1333                                 return -EIO;
1334                         s->temp[i] = t * 1000;
1335                 }
1336                 return 8;
1337
1338         case IBMACPI_THERMAL_NONE:
1339         default:
1340                 return 0;
1341         }
1342 }
1343
1344 static int thermal_read(char *p)
1345 {
1346         int len = 0;
1347         int n, i;
1348         struct ibm_thermal_sensors_struct t;
1349
1350         n = thermal_get_sensors(&t);
1351         if (unlikely(n < 0))
1352                 return n;
1353
1354         len += sprintf(p + len, "temperatures:\t");
1355
1356         if (n > 0) {
1357                 for (i = 0; i < (n - 1); i++)
1358                         len += sprintf(p + len, "%d ", t.temp[i] / 1000);
1359                 len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
1360         } else
1361                 len += sprintf(p + len, "not supported\n");
1362
1363         return len;
1364 }
1365
1366 static u8 ecdump_regs[256];
1367
1368 static int ecdump_read(char *p)
1369 {
1370         int len = 0;
1371         int i, j;
1372         u8 v;
1373
1374         len += sprintf(p + len, "EC      "
1375                        " +00 +01 +02 +03 +04 +05 +06 +07"
1376                        " +08 +09 +0a +0b +0c +0d +0e +0f\n");
1377         for (i = 0; i < 256; i += 16) {
1378                 len += sprintf(p + len, "EC 0x%02x:", i);
1379                 for (j = 0; j < 16; j++) {
1380                         if (!acpi_ec_read(i + j, &v))
1381                                 break;
1382                         if (v != ecdump_regs[i + j])
1383                                 len += sprintf(p + len, " *%02x", v);
1384                         else
1385                                 len += sprintf(p + len, "  %02x", v);
1386                         ecdump_regs[i + j] = v;
1387                 }
1388                 len += sprintf(p + len, "\n");
1389                 if (j != 16)
1390                         break;
1391         }
1392
1393         /* These are way too dangerous to advertise openly... */
1394 #if 0
1395         len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
1396                        " (<offset> is 00-ff, <value> is 00-ff)\n");
1397         len += sprintf(p + len, "commands:\t0x<offset> <value>  "
1398                        " (<offset> is 00-ff, <value> is 0-255)\n");
1399 #endif
1400         return len;
1401 }
1402
1403 static int ecdump_write(char *buf)
1404 {
1405         char *cmd;
1406         int i, v;
1407
1408         while ((cmd = next_cmd(&buf))) {
1409                 if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
1410                         /* i and v set */
1411                 } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
1412                         /* i and v set */
1413                 } else
1414                         return -EINVAL;
1415                 if (i >= 0 && i < 256 && v >= 0 && v < 256) {
1416                         if (!acpi_ec_write(i, v))
1417                                 return -EIO;
1418                 } else
1419                         return -EINVAL;
1420         }
1421
1422         return 0;
1423 }
1424
1425 static int brightness_offset = 0x31;
1426
1427 static int brightness_get(struct backlight_device *bd)
1428 {
1429         u8 level;
1430         if (!acpi_ec_read(brightness_offset, &level))
1431                 return -EIO;
1432
1433         level &= 0x7;
1434         return level;
1435 }
1436
1437 static int brightness_read(char *p)
1438 {
1439         int len = 0;
1440         int level;
1441
1442         if ((level = brightness_get(NULL)) < 0) {
1443                 len += sprintf(p + len, "level:\t\tunreadable\n");
1444         } else {
1445                 len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
1446                 len += sprintf(p + len, "commands:\tup, down\n");
1447                 len += sprintf(p + len, "commands:\tlevel <level>"
1448                                " (<level> is 0-7)\n");
1449         }
1450
1451         return len;
1452 }
1453
1454 #define BRIGHTNESS_UP   4
1455 #define BRIGHTNESS_DOWN 5
1456
1457 static int brightness_set(int value)
1458 {
1459         int cmos_cmd, inc, i;
1460         int current_value = brightness_get(NULL);
1461
1462         value &= 7;
1463
1464         cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN;
1465         inc = value > current_value ? 1 : -1;
1466         for (i = current_value; i != value; i += inc) {
1467                 if (!cmos_eval(cmos_cmd))
1468                         return -EIO;
1469                 if (!acpi_ec_write(brightness_offset, i + inc))
1470                         return -EIO;
1471         }
1472
1473         return 0;
1474 }
1475
1476 static int brightness_write(char *buf)
1477 {
1478         int level;
1479         int new_level;
1480         char *cmd;
1481
1482         while ((cmd = next_cmd(&buf))) {
1483                 if ((level = brightness_get(NULL)) < 0)
1484                         return level;
1485                 level &= 7;
1486
1487                 if (strlencmp(cmd, "up") == 0) {
1488                         new_level = level == 7 ? 7 : level + 1;
1489                 } else if (strlencmp(cmd, "down") == 0) {
1490                         new_level = level == 0 ? 0 : level - 1;
1491                 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
1492                            new_level >= 0 && new_level <= 7) {
1493                         /* new_level set */
1494                 } else
1495                         return -EINVAL;
1496
1497                 brightness_set(new_level);
1498         }
1499
1500         return 0;
1501 }
1502
1503 static int brightness_update_status(struct backlight_device *bd)
1504 {
1505         return brightness_set(bd->props->brightness);
1506 }
1507
1508 static int volume_offset = 0x30;
1509
1510 static int volume_read(char *p)
1511 {
1512         int len = 0;
1513         u8 level;
1514
1515         if (!acpi_ec_read(volume_offset, &level)) {
1516                 len += sprintf(p + len, "level:\t\tunreadable\n");
1517         } else {
1518                 len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
1519                 len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
1520                 len += sprintf(p + len, "commands:\tup, down, mute\n");
1521                 len += sprintf(p + len, "commands:\tlevel <level>"
1522                                " (<level> is 0-15)\n");
1523         }
1524
1525         return len;
1526 }
1527
1528 #define VOLUME_DOWN     0
1529 #define VOLUME_UP       1
1530 #define VOLUME_MUTE     2
1531
1532 static int volume_write(char *buf)
1533 {
1534         int cmos_cmd, inc, i;
1535         u8 level, mute;
1536         int new_level, new_mute;
1537         char *cmd;
1538
1539         while ((cmd = next_cmd(&buf))) {
1540                 if (!acpi_ec_read(volume_offset, &level))
1541                         return -EIO;
1542                 new_mute = mute = level & 0x40;
1543                 new_level = level = level & 0xf;
1544
1545                 if (strlencmp(cmd, "up") == 0) {
1546                         if (mute)
1547                                 new_mute = 0;
1548                         else
1549                                 new_level = level == 15 ? 15 : level + 1;
1550                 } else if (strlencmp(cmd, "down") == 0) {
1551                         if (mute)
1552                                 new_mute = 0;
1553                         else
1554                                 new_level = level == 0 ? 0 : level - 1;
1555                 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
1556                            new_level >= 0 && new_level <= 15) {
1557                         /* new_level set */
1558                 } else if (strlencmp(cmd, "mute") == 0) {
1559                         new_mute = 0x40;
1560                 } else
1561                         return -EINVAL;
1562
1563                 if (new_level != level) {       /* mute doesn't change */
1564                         cmos_cmd = new_level > level ? VOLUME_UP : VOLUME_DOWN;
1565                         inc = new_level > level ? 1 : -1;
1566
1567                         if (mute && (!cmos_eval(cmos_cmd) ||
1568                                      !acpi_ec_write(volume_offset, level)))
1569                                 return -EIO;
1570
1571                         for (i = level; i != new_level; i += inc)
1572                                 if (!cmos_eval(cmos_cmd) ||
1573                                     !acpi_ec_write(volume_offset, i + inc))
1574                                         return -EIO;
1575
1576                         if (mute && (!cmos_eval(VOLUME_MUTE) ||
1577                                      !acpi_ec_write(volume_offset,
1578                                                     new_level + mute)))
1579                                 return -EIO;
1580                 }
1581
1582                 if (new_mute != mute) { /* level doesn't change */
1583                         cmos_cmd = new_mute ? VOLUME_MUTE : VOLUME_UP;
1584
1585                         if (!cmos_eval(cmos_cmd) ||
1586                             !acpi_ec_write(volume_offset, level + new_mute))
1587                                 return -EIO;
1588                 }
1589         }
1590
1591         return 0;
1592 }
1593
1594 static int fan_status_offset = 0x2f;
1595 static int fan_rpm_offset = 0x84;
1596
1597 static int fan_read(char *p)
1598 {
1599         int len = 0;
1600         int s;
1601         u8 lo, hi, status;
1602
1603         if (gfan_handle) {
1604                 /* 570, 600e/x, 770e, 770x */
1605                 if (!acpi_evalf(gfan_handle, &s, NULL, "d"))
1606                         return -EIO;
1607
1608                 len += sprintf(p + len, "level:\t\t%d\n", s);
1609         } else {
1610                 /* all except 570, 600e/x, 770e, 770x */
1611                 if (!acpi_ec_read(fan_status_offset, &status))
1612                         len += sprintf(p + len, "status:\t\tunreadable\n");
1613                 else
1614                         len += sprintf(p + len, "status:\t\t%s\n",
1615                                        enabled(status, 7));
1616
1617                 if (!acpi_ec_read(fan_rpm_offset, &lo) ||
1618                     !acpi_ec_read(fan_rpm_offset + 1, &hi))
1619                         len += sprintf(p + len, "speed:\t\tunreadable\n");
1620                 else
1621                         len += sprintf(p + len, "speed:\t\t%d\n",
1622                                        (hi << 8) + lo);
1623         }
1624
1625         if (sfan_handle)
1626                 /* 570, 770x-JL */
1627                 len += sprintf(p + len, "commands:\tlevel <level>"
1628                                " (<level> is 0-7)\n");
1629         if (!gfan_handle)
1630                 /* all except 570, 600e/x, 770e, 770x */
1631                 len += sprintf(p + len, "commands:\tenable, disable\n");
1632         if (fans_handle)
1633                 /* X31, X40 */
1634                 len += sprintf(p + len, "commands:\tspeed <speed>"
1635                                " (<speed> is 0-65535)\n");
1636
1637         return len;
1638 }
1639
1640 static int fan_write(char *buf)
1641 {
1642         char *cmd;
1643         int level, speed;
1644
1645         while ((cmd = next_cmd(&buf))) {
1646                 if (sfan_handle &&
1647                     sscanf(cmd, "level %d", &level) == 1 &&
1648                     level >= 0 && level <= 7) {
1649                         /* 570, 770x-JL */
1650                         if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
1651                                 return -EIO;
1652                 } else if (!gfan_handle && strlencmp(cmd, "enable") == 0) {
1653                         /* all except 570, 600e/x, 770e, 770x */
1654                         if (!acpi_ec_write(fan_status_offset, 0x80))
1655                                 return -EIO;
1656                 } else if (!gfan_handle && strlencmp(cmd, "disable") == 0) {
1657                         /* all except 570, 600e/x, 770e, 770x */
1658                         if (!acpi_ec_write(fan_status_offset, 0x00))
1659                                 return -EIO;
1660                 } else if (fans_handle &&
1661                            sscanf(cmd, "speed %d", &speed) == 1 &&
1662                            speed >= 0 && speed <= 65535) {
1663                         /* X31, X40 */
1664                         if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
1665                                         speed, speed, speed))
1666                                 return -EIO;
1667                 } else
1668                         return -EINVAL;
1669         }
1670
1671         return 0;
1672 }
1673
1674 static struct ibm_struct ibms[] = {
1675         {
1676          .name = "driver",
1677          .init = driver_init,
1678          .read = driver_read,
1679          },
1680         {
1681          .name = "hotkey",
1682          .hid = IBM_HKEY_HID,
1683          .init = hotkey_init,
1684          .read = hotkey_read,
1685          .write = hotkey_write,
1686          .exit = hotkey_exit,
1687          .notify = hotkey_notify,
1688          .handle = &hkey_handle,
1689          .type = ACPI_DEVICE_NOTIFY,
1690          },
1691         {
1692          .name = "bluetooth",
1693          .init = bluetooth_init,
1694          .read = bluetooth_read,
1695          .write = bluetooth_write,
1696          },
1697         {
1698          .name = "wan",
1699          .init = wan_init,
1700          .read = wan_read,
1701          .write = wan_write,
1702          .experimental = 1,
1703          },
1704         {
1705          .name = "video",
1706          .init = video_init,
1707          .read = video_read,
1708          .write = video_write,
1709          .exit = video_exit,
1710          },
1711         {
1712          .name = "light",
1713          .init = light_init,
1714          .read = light_read,
1715          .write = light_write,
1716          },
1717 #ifdef CONFIG_ACPI_IBM_DOCK
1718         {
1719          .name = "dock",
1720          .read = dock_read,
1721          .write = dock_write,
1722          .notify = dock_notify,
1723          .handle = &dock_handle,
1724          .type = ACPI_SYSTEM_NOTIFY,
1725          },
1726         {
1727          .name = "dock",
1728          .hid = IBM_PCI_HID,
1729          .notify = dock_notify,
1730          .handle = &pci_handle,
1731          .type = ACPI_SYSTEM_NOTIFY,
1732          },
1733 #endif
1734         {
1735          .name = "bay",
1736          .init = bay_init,
1737          .read = bay_read,
1738          .write = bay_write,
1739          .notify = bay_notify,
1740          .handle = &bay_handle,
1741          .type = ACPI_SYSTEM_NOTIFY,
1742          },
1743         {
1744          .name = "cmos",
1745          .read = cmos_read,
1746          .write = cmos_write,
1747          },
1748         {
1749          .name = "led",
1750          .init = led_init,
1751          .read = led_read,
1752          .write = led_write,
1753          },
1754         {
1755          .name = "beep",
1756          .read = beep_read,
1757          .write = beep_write,
1758          },
1759         {
1760          .name = "thermal",
1761          .init = thermal_init,
1762          .read = thermal_read,
1763          },
1764         {
1765          .name = "ecdump",
1766          .read = ecdump_read,
1767          .write = ecdump_write,
1768          .experimental = 1,
1769          },
1770         {
1771          .name = "brightness",
1772          .read = brightness_read,
1773          .write = brightness_write,
1774          },
1775         {
1776          .name = "volume",
1777          .read = volume_read,
1778          .write = volume_write,
1779          },
1780         {
1781          .name = "fan",
1782          .read = fan_read,
1783          .write = fan_write,
1784          .experimental = 1,
1785          },
1786 };
1787
1788 static int dispatch_read(char *page, char **start, off_t off, int count,
1789                          int *eof, void *data)
1790 {
1791         struct ibm_struct *ibm = (struct ibm_struct *)data;
1792         int len;
1793
1794         if (!ibm || !ibm->read)
1795                 return -EINVAL;
1796
1797         len = ibm->read(page);
1798         if (len < 0)
1799                 return len;
1800
1801         if (len <= off + count)
1802                 *eof = 1;
1803         *start = page + off;
1804         len -= off;
1805         if (len > count)
1806                 len = count;
1807         if (len < 0)
1808                 len = 0;
1809
1810         return len;
1811 }
1812
1813 static int dispatch_write(struct file *file, const char __user * userbuf,
1814                           unsigned long count, void *data)
1815 {
1816         struct ibm_struct *ibm = (struct ibm_struct *)data;
1817         char *kernbuf;
1818         int ret;
1819
1820         if (!ibm || !ibm->write)
1821                 return -EINVAL;
1822
1823         kernbuf = kmalloc(count + 2, GFP_KERNEL);
1824         if (!kernbuf)
1825                 return -ENOMEM;
1826
1827         if (copy_from_user(kernbuf, userbuf, count)) {
1828                 kfree(kernbuf);
1829                 return -EFAULT;
1830         }
1831
1832         kernbuf[count] = 0;
1833         strcat(kernbuf, ",");
1834         ret = ibm->write(kernbuf);
1835         if (ret == 0)
1836                 ret = count;
1837
1838         kfree(kernbuf);
1839
1840         return ret;
1841 }
1842
1843 static void dispatch_notify(acpi_handle handle, u32 event, void *data)
1844 {
1845         struct ibm_struct *ibm = (struct ibm_struct *)data;
1846
1847         if (!ibm || !ibm->notify)
1848                 return;
1849
1850         ibm->notify(ibm, event);
1851 }
1852
1853 static int __init setup_notify(struct ibm_struct *ibm)
1854 {
1855         acpi_status status;
1856         int ret;
1857
1858         if (!*ibm->handle)
1859                 return 0;
1860
1861         ret = acpi_bus_get_device(*ibm->handle, &ibm->device);
1862         if (ret < 0) {
1863                 printk(IBM_ERR "%s device not present\n", ibm->name);
1864                 return 0;
1865         }
1866
1867         acpi_driver_data(ibm->device) = ibm;
1868         sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name);
1869
1870         status = acpi_install_notify_handler(*ibm->handle, ibm->type,
1871                                              dispatch_notify, ibm);
1872         if (ACPI_FAILURE(status)) {
1873                 printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
1874                        ibm->name, status);
1875                 return -ENODEV;
1876         }
1877
1878         return 0;
1879 }
1880
1881 static int __init ibm_device_add(struct acpi_device *device)
1882 {
1883         return 0;
1884 }
1885
1886 static int __init register_driver(struct ibm_struct *ibm)
1887 {
1888         int ret;
1889
1890         ibm->driver = kmalloc(sizeof(struct acpi_driver), GFP_KERNEL);
1891         if (!ibm->driver) {
1892                 printk(IBM_ERR "kmalloc(ibm->driver) failed\n");
1893                 return -1;
1894         }
1895
1896         memset(ibm->driver, 0, sizeof(struct acpi_driver));
1897         sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name);
1898         ibm->driver->ids = ibm->hid;
1899         ibm->driver->ops.add = &ibm_device_add;
1900
1901         ret = acpi_bus_register_driver(ibm->driver);
1902         if (ret < 0) {
1903                 printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
1904                        ibm->hid, ret);
1905                 kfree(ibm->driver);
1906         }
1907
1908         return ret;
1909 }
1910
1911 static int __init ibm_init(struct ibm_struct *ibm)
1912 {
1913         int ret;
1914         struct proc_dir_entry *entry;
1915
1916         if (ibm->experimental && !experimental)
1917                 return 0;
1918
1919         if (ibm->hid) {
1920                 ret = register_driver(ibm);
1921                 if (ret < 0)
1922                         return ret;
1923                 ibm->driver_registered = 1;
1924         }
1925
1926         if (ibm->init) {
1927                 ret = ibm->init();
1928                 if (ret != 0)
1929                         return ret;
1930                 ibm->init_called = 1;
1931         }
1932
1933         if (ibm->read) {
1934                 entry = create_proc_entry(ibm->name,
1935                                           S_IFREG | S_IRUGO | S_IWUSR,
1936                                           proc_dir);
1937                 if (!entry) {
1938                         printk(IBM_ERR "unable to create proc entry %s\n",
1939                                ibm->name);
1940                         return -ENODEV;
1941                 }
1942                 entry->owner = THIS_MODULE;
1943                 entry->data = ibm;
1944                 entry->read_proc = &dispatch_read;
1945                 if (ibm->write)
1946                         entry->write_proc = &dispatch_write;
1947                 ibm->proc_created = 1;
1948         }
1949
1950         if (ibm->notify) {
1951                 ret = setup_notify(ibm);
1952                 if (ret < 0)
1953                         return ret;
1954                 ibm->notify_installed = 1;
1955         }
1956
1957         return 0;
1958 }
1959
1960 static void ibm_exit(struct ibm_struct *ibm)
1961 {
1962         if (ibm->notify_installed)
1963                 acpi_remove_notify_handler(*ibm->handle, ibm->type,
1964                                            dispatch_notify);
1965
1966         if (ibm->proc_created)
1967                 remove_proc_entry(ibm->name, proc_dir);
1968
1969         if (ibm->init_called && ibm->exit)
1970                 ibm->exit();
1971
1972         if (ibm->driver_registered) {
1973                 acpi_bus_unregister_driver(ibm->driver);
1974                 kfree(ibm->driver);
1975         }
1976 }
1977
1978 static void __init ibm_handle_init(char *name,
1979                                    acpi_handle * handle, acpi_handle parent,
1980                                    char **paths, int num_paths, char **path)
1981 {
1982         int i;
1983         acpi_status status;
1984
1985         for (i = 0; i < num_paths; i++) {
1986                 status = acpi_get_handle(parent, paths[i], handle);
1987                 if (ACPI_SUCCESS(status)) {
1988                         *path = paths[i];
1989                         return;
1990                 }
1991         }
1992
1993         *handle = NULL;
1994 }
1995
1996 #define IBM_HANDLE_INIT(object)                                         \
1997         ibm_handle_init(#object, &object##_handle, *object##_parent,    \
1998                 object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
1999
2000 static int set_ibm_param(const char *val, struct kernel_param *kp)
2001 {
2002         unsigned int i;
2003
2004         for (i = 0; i < ARRAY_SIZE(ibms); i++)
2005                 if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) {
2006                         if (strlen(val) > sizeof(ibms[i].param) - 2)
2007                                 return -ENOSPC;
2008                         strcpy(ibms[i].param, val);
2009                         strcat(ibms[i].param, ",");
2010                         return 0;
2011                 }
2012
2013         return -EINVAL;
2014 }
2015
2016 #define IBM_PARAM(feature) \
2017         module_param_call(feature, set_ibm_param, NULL, NULL, 0)
2018
2019 IBM_PARAM(hotkey);
2020 IBM_PARAM(bluetooth);
2021 IBM_PARAM(video);
2022 IBM_PARAM(light);
2023 #ifdef CONFIG_ACPI_IBM_DOCK
2024 IBM_PARAM(dock);
2025 #endif
2026 IBM_PARAM(bay);
2027 IBM_PARAM(cmos);
2028 IBM_PARAM(led);
2029 IBM_PARAM(beep);
2030 IBM_PARAM(ecdump);
2031 IBM_PARAM(brightness);
2032 IBM_PARAM(volume);
2033 IBM_PARAM(fan);
2034
2035 static struct backlight_properties ibm_backlight_data = {
2036         .owner = THIS_MODULE,
2037         .get_brightness = brightness_get,
2038         .update_status = brightness_update_status,
2039         .max_brightness = 7,
2040 };
2041
2042 static void acpi_ibm_exit(void)
2043 {
2044         int i;
2045
2046         if (ibm_backlight_device)
2047                 backlight_device_unregister(ibm_backlight_device);
2048
2049         for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--)
2050                 ibm_exit(&ibms[i]);
2051
2052         remove_proc_entry(IBM_DIR, acpi_root_dir);
2053 }
2054
2055 static int __init acpi_ibm_init(void)
2056 {
2057         int ret, i;
2058
2059         if (acpi_disabled)
2060                 return -ENODEV;
2061
2062         if (!acpi_specific_hotkey_enabled) {
2063                 printk(IBM_ERR "using generic hotkey driver\n");
2064                 return -ENODEV;
2065         }
2066
2067         /* ec is required because many other handles are relative to it */
2068         IBM_HANDLE_INIT(ec);
2069         if (!ec_handle) {
2070                 printk(IBM_ERR "ec object not found\n");
2071                 return -ENODEV;
2072         }
2073
2074         /* these handles are not required */
2075         IBM_HANDLE_INIT(vid);
2076         IBM_HANDLE_INIT(vid2);
2077         IBM_HANDLE_INIT(ledb);
2078         IBM_HANDLE_INIT(led);
2079         IBM_HANDLE_INIT(hkey);
2080         IBM_HANDLE_INIT(lght);
2081         IBM_HANDLE_INIT(cmos);
2082 #ifdef CONFIG_ACPI_IBM_DOCK
2083         IBM_HANDLE_INIT(dock);
2084 #endif
2085         IBM_HANDLE_INIT(pci);
2086         IBM_HANDLE_INIT(bay);
2087         if (bay_handle)
2088                 IBM_HANDLE_INIT(bay_ej);
2089         IBM_HANDLE_INIT(bay2);
2090         if (bay2_handle)
2091                 IBM_HANDLE_INIT(bay2_ej);
2092         IBM_HANDLE_INIT(beep);
2093         IBM_HANDLE_INIT(ecrd);
2094         IBM_HANDLE_INIT(ecwr);
2095         IBM_HANDLE_INIT(fans);
2096         IBM_HANDLE_INIT(gfan);
2097         IBM_HANDLE_INIT(sfan);
2098
2099         proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir);
2100         if (!proc_dir) {
2101                 printk(IBM_ERR "unable to create proc dir %s", IBM_DIR);
2102                 return -ENODEV;
2103         }
2104         proc_dir->owner = THIS_MODULE;
2105
2106         for (i = 0; i < ARRAY_SIZE(ibms); i++) {
2107                 ret = ibm_init(&ibms[i]);
2108                 if (ret >= 0 && *ibms[i].param)
2109                         ret = ibms[i].write(ibms[i].param);
2110                 if (ret < 0) {
2111                         acpi_ibm_exit();
2112                         return ret;
2113                 }
2114         }
2115
2116         ibm_backlight_device = backlight_device_register("ibm", NULL,
2117                                                          &ibm_backlight_data);
2118         if (IS_ERR(ibm_backlight_device)) {
2119                 printk(IBM_ERR "Could not register ibm backlight device\n");
2120                 ibm_backlight_device = NULL;
2121                 acpi_ibm_exit();
2122         }
2123
2124         return 0;
2125 }
2126
2127 module_init(acpi_ibm_init);
2128 module_exit(acpi_ibm_exit);