aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2007-03-23 16:34:00 -0400
committerLen Brown <len.brown@intel.com>2007-03-25 23:37:55 -0400
commit3ede41c718c7845905231019e42d05a3ed329515 (patch)
tree6cac5d3152f8fa55198544556cf6f03bae5f3bb6 /drivers/misc
parent38f996ed21089fa4ae40526a5f428e3c792ea561 (diff)
ACPI: ibm-acpi: move driver to drivers/misc hierarchy
ibm-acpi is not an ACPICA driver, so move it to drivers/misc as per Len Brown's request. Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig37
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/ibm_acpi.c2783
-rw-r--r--drivers/misc/ibm_acpi.h437
4 files changed, 3258 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 80b199fa0aa9..5d2bcbf1e3d4 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -122,4 +122,41 @@ config SONY_LAPTOP
122 122
123 Read <file:Documentation/sony-laptop.txt> for more information. 123 Read <file:Documentation/sony-laptop.txt> for more information.
124 124
125config ACPI_IBM
126 tristate "IBM ThinkPad Laptop Extras"
127 depends on X86 && ACPI
128 select BACKLIGHT_CLASS_DEVICE
129 ---help---
130 This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds
131 support for Fn-Fx key combinations, Bluetooth control, video
132 output switching, ThinkLight control, UltraBay eject and more.
133 For more information about this driver see <file:Documentation/ibm-acpi.txt>
134 and <http://ibm-acpi.sf.net/> .
135
136 If you have an IBM ThinkPad laptop, say Y or M here.
137
138config ACPI_IBM_DOCK
139 bool "Legacy Docking Station Support"
140 depends on ACPI_IBM
141 depends on ACPI_DOCK=n
142 default n
143 ---help---
144 Allows the ibm_acpi driver to handle docking station events.
145 This support is obsoleted by CONFIG_HOTPLUG_PCI_ACPI. It will
146 allow locking and removing the laptop from the docking station,
147 but will not properly connect PCI devices.
148
149 If you are not sure, say N here.
150
151config ACPI_IBM_BAY
152 bool "Legacy Removable Bay Support"
153 depends on ACPI_IBM
154 default y
155 ---help---
156 Allows the ibm_acpi driver to handle removable bays. It will allow
157 disabling the device in the bay, and also generate notifications when
158 the bay lever is ejected or inserted.
159
160 If you are not sure, say Y here.
161
125endmenu 162endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7793ccd79049..848b398482d9 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_TIFM_CORE) += tifm_core.o
12obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o 12obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
13obj-$(CONFIG_SGI_IOC4) += ioc4.o 13obj-$(CONFIG_SGI_IOC4) += ioc4.o
14obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o 14obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
15obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o
diff --git a/drivers/misc/ibm_acpi.c b/drivers/misc/ibm_acpi.c
new file mode 100644
index 000000000000..ae03b8f6f7be
--- /dev/null
+++ b/drivers/misc/ibm_acpi.c
@@ -0,0 +1,2783 @@
1/*
2 * ibm_acpi.c - IBM ThinkPad ACPI Extras
3 *
4 *
5 * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
6 * Copyright (C) 2006-2007 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301, USA.
22 */
23
24#define IBM_VERSION "0.13"
25
26/*
27 * Changelog:
28 *
29 * 2006-11-22 0.13 new maintainer
30 * changelog now lives in git commit history, and will
31 * not be updated further in-file.
32 *
33 * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels
34 * 2005-03-17 0.11 support for 600e, 770x
35 * thanks to Jamie Lentin <lentinj@dial.pipex.com>
36 * support for 770e, G41
37 * G40 and G41 don't have a thinklight
38 * temperatures no longer experimental
39 * experimental brightness control
40 * experimental volume control
41 * experimental fan enable/disable
42 * 2005-01-16 0.10 fix module loading on R30, R31
43 * 2005-01-16 0.9 support for 570, R30, R31
44 * ultrabay support on A22p, A3x
45 * limit arg for cmos, led, beep, drop experimental status
46 * more capable led control on A21e, A22p, T20-22, X20
47 * experimental temperatures and fan speed
48 * experimental embedded controller register dump
49 * mark more functions as __init, drop incorrect __exit
50 * use MODULE_VERSION
51 * thanks to Henrik Brix Andersen <brix@gentoo.org>
52 * fix parameter passing on module loading
53 * thanks to Rusty Russell <rusty@rustcorp.com.au>
54 * thanks to Jim Radford <radford@blackbean.org>
55 * 2004-11-08 0.8 fix init error case, don't return from a macro
56 * thanks to Chris Wright <chrisw@osdl.org>
57 * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20
58 * fix led control on A21e
59 * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device
60 * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20
61 * proc file format changed
62 * video_switch command
63 * experimental cmos control
64 * experimental led control
65 * experimental acpi sounds
66 * 2004-09-16 0.4 support for module parameters
67 * hotkey mask can be prefixed by 0x
68 * video output switching
69 * video expansion control
70 * ultrabay eject support
71 * removed lcd brightness/on/off control, didn't work
72 * 2004-08-17 0.3 support for R40
73 * lcd off, brightness control
74 * thinklight on/off
75 * 2004-08-14 0.2 support for T series, X20
76 * bluetooth enable/disable
77 * hotkey events disabled by default
78 * removed fan control, currently useless
79 * 2004-08-09 0.1 initial release, support for X series
80 */
81
82#include "ibm_acpi.h"
83
84MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
85MODULE_DESCRIPTION(IBM_DESC);
86MODULE_VERSION(IBM_VERSION);
87MODULE_LICENSE("GPL");
88
89#define __unused __attribute__ ((unused))
90
91/****************************************************************************
92 ****************************************************************************
93 *
94 * ACPI Helpers and device model
95 *
96 ****************************************************************************
97 ****************************************************************************/
98
99/*************************************************************************
100 * ACPI basic handles
101 */
102
103static acpi_handle root_handle = NULL;
104
105#define IBM_HANDLE(object, parent, paths...) \
106 static acpi_handle object##_handle; \
107 static acpi_handle *object##_parent = &parent##_handle; \
108 static char *object##_path; \
109 static char *object##_paths[] = { paths }
110
111IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
112 "\\_SB.PCI.ISA.EC", /* 570 */
113 "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
114 "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
115 "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
116 "\\_SB.PCI0.ICH3.EC0", /* R31 */
117 "\\_SB.PCI0.LPC.EC", /* all others */
118 );
119
120IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */
121IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */
122
123
124/*************************************************************************
125 * Misc ACPI handles
126 */
127
128IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */
129 "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */
130 "\\CMS", /* R40, R40e */
131 ); /* all others */
132
133IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
134 "^HKEY", /* R30, R31 */
135 "HKEY", /* all others */
136 ); /* 570 */
137
138
139/*************************************************************************
140 * ACPI helpers
141 */
142
143static int acpi_evalf(acpi_handle handle,
144 void *res, char *method, char *fmt, ...)
145{
146 char *fmt0 = fmt;
147 struct acpi_object_list params;
148 union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
149 struct acpi_buffer result, *resultp;
150 union acpi_object out_obj;
151 acpi_status status;
152 va_list ap;
153 char res_type;
154 int success;
155 int quiet;
156
157 if (!*fmt) {
158 printk(IBM_ERR "acpi_evalf() called with empty format\n");
159 return 0;
160 }
161
162 if (*fmt == 'q') {
163 quiet = 1;
164 fmt++;
165 } else
166 quiet = 0;
167
168 res_type = *(fmt++);
169
170 params.count = 0;
171 params.pointer = &in_objs[0];
172
173 va_start(ap, fmt);
174 while (*fmt) {
175 char c = *(fmt++);
176 switch (c) {
177 case 'd': /* int */
178 in_objs[params.count].integer.value = va_arg(ap, int);
179 in_objs[params.count++].type = ACPI_TYPE_INTEGER;
180 break;
181 /* add more types as needed */
182 default:
183 printk(IBM_ERR "acpi_evalf() called "
184 "with invalid format character '%c'\n", c);
185 return 0;
186 }
187 }
188 va_end(ap);
189
190 if (res_type != 'v') {
191 result.length = sizeof(out_obj);
192 result.pointer = &out_obj;
193 resultp = &result;
194 } else
195 resultp = NULL;
196
197 status = acpi_evaluate_object(handle, method, &params, resultp);
198
199 switch (res_type) {
200 case 'd': /* int */
201 if (res)
202 *(int *)res = out_obj.integer.value;
203 success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
204 break;
205 case 'v': /* void */
206 success = status == AE_OK;
207 break;
208 /* add more types as needed */
209 default:
210 printk(IBM_ERR "acpi_evalf() called "
211 "with invalid format character '%c'\n", res_type);
212 return 0;
213 }
214
215 if (!success && !quiet)
216 printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
217 method, fmt0, status);
218
219 return success;
220}
221
222static void __unused acpi_print_int(acpi_handle handle, char *method)
223{
224 int i;
225
226 if (acpi_evalf(handle, &i, method, "d"))
227 printk(IBM_INFO "%s = 0x%x\n", method, i);
228 else
229 printk(IBM_ERR "error calling %s\n", method);
230}
231
232static int acpi_ec_read(int i, u8 * p)
233{
234 int v;
235
236 if (ecrd_handle) {
237 if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
238 return 0;
239 *p = v;
240 } else {
241 if (ec_read(i, p) < 0)
242 return 0;
243 }
244
245 return 1;
246}
247
248static int acpi_ec_write(int i, u8 v)
249{
250 if (ecwr_handle) {
251 if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
252 return 0;
253 } else {
254 if (ec_write(i, v) < 0)
255 return 0;
256 }
257
258 return 1;
259}
260
261static int _sta(acpi_handle handle)
262{
263 int status;
264
265 if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
266 status = 0;
267
268 return status;
269}
270
271/*************************************************************************
272 * ACPI device model
273 */
274
275static void __init ibm_handle_init(char *name,
276 acpi_handle * handle, acpi_handle parent,
277 char **paths, int num_paths, char **path)
278{
279 int i;
280 acpi_status status;
281
282 for (i = 0; i < num_paths; i++) {
283 status = acpi_get_handle(parent, paths[i], handle);
284 if (ACPI_SUCCESS(status)) {
285 *path = paths[i];
286 return;
287 }
288 }
289
290 *handle = NULL;
291}
292
293static void dispatch_notify(acpi_handle handle, u32 event, void *data)
294{
295 struct ibm_struct *ibm = data;
296
297 if (!ibm || !ibm->notify)
298 return;
299
300 ibm->notify(ibm, event);
301}
302
303static int __init setup_notify(struct ibm_struct *ibm)
304{
305 acpi_status status;
306 int ret;
307
308 if (!*ibm->handle)
309 return 0;
310
311 ret = acpi_bus_get_device(*ibm->handle, &ibm->device);
312 if (ret < 0) {
313 printk(IBM_ERR "%s device not present\n", ibm->name);
314 return -ENODEV;
315 }
316
317 acpi_driver_data(ibm->device) = ibm;
318 sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name);
319
320 status = acpi_install_notify_handler(*ibm->handle, ibm->type,
321 dispatch_notify, ibm);
322 if (ACPI_FAILURE(status)) {
323 if (status == AE_ALREADY_EXISTS) {
324 printk(IBM_NOTICE "another device driver is already handling %s events\n",
325 ibm->name);
326 } else {
327 printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
328 ibm->name, status);
329 }
330 return -ENODEV;
331 }
332 ibm->notify_installed = 1;
333 return 0;
334}
335
336static int __init ibm_device_add(struct acpi_device *device)
337{
338 return 0;
339}
340
341static int __init register_ibmacpi_subdriver(struct ibm_struct *ibm)
342{
343 int ret;
344
345 ibm->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
346 if (!ibm->driver) {
347 printk(IBM_ERR "kmalloc(ibm->driver) failed\n");
348 return -1;
349 }
350
351 sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name);
352 ibm->driver->ids = ibm->hid;
353 ibm->driver->ops.add = &ibm_device_add;
354
355 ret = acpi_bus_register_driver(ibm->driver);
356 if (ret < 0) {
357 printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
358 ibm->hid, ret);
359 kfree(ibm->driver);
360 }
361
362 return ret;
363}
364
365
366/****************************************************************************
367 ****************************************************************************
368 *
369 * Procfs Helpers
370 *
371 ****************************************************************************
372 ****************************************************************************/
373
374static int dispatch_read(char *page, char **start, off_t off, int count,
375 int *eof, void *data)
376{
377 struct ibm_struct *ibm = data;
378 int len;
379
380 if (!ibm || !ibm->read)
381 return -EINVAL;
382
383 len = ibm->read(page);
384 if (len < 0)
385 return len;
386
387 if (len <= off + count)
388 *eof = 1;
389 *start = page + off;
390 len -= off;
391 if (len > count)
392 len = count;
393 if (len < 0)
394 len = 0;
395
396 return len;
397}
398
399static int dispatch_write(struct file *file, const char __user * userbuf,
400 unsigned long count, void *data)
401{
402 struct ibm_struct *ibm = data;
403 char *kernbuf;
404 int ret;
405
406 if (!ibm || !ibm->write)
407 return -EINVAL;
408
409 kernbuf = kmalloc(count + 2, GFP_KERNEL);
410 if (!kernbuf)
411 return -ENOMEM;
412
413 if (copy_from_user(kernbuf, userbuf, count)) {
414 kfree(kernbuf);
415 return -EFAULT;
416 }
417
418 kernbuf[count] = 0;
419 strcat(kernbuf, ",");
420 ret = ibm->write(kernbuf);
421 if (ret == 0)
422 ret = count;
423
424 kfree(kernbuf);
425
426 return ret;
427}
428
429static char *next_cmd(char **cmds)
430{
431 char *start = *cmds;
432 char *end;
433
434 while ((end = strchr(start, ',')) && end == start)
435 start = end + 1;
436
437 if (!end)
438 return NULL;
439
440 *end = 0;
441 *cmds = end + 1;
442 return start;
443}
444
445
446/****************************************************************************
447 ****************************************************************************
448 *
449 * Subdrivers
450 *
451 ****************************************************************************
452 ****************************************************************************/
453
454/*************************************************************************
455 * ibm-acpi init subdriver
456 */
457
458static int ibm_acpi_driver_init(void)
459{
460 printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
461 printk(IBM_INFO "%s\n", IBM_URL);
462
463 if (ibm_thinkpad_ec_found)
464 printk(IBM_INFO "ThinkPad EC firmware %s\n",
465 ibm_thinkpad_ec_found);
466
467 return 0;
468}
469
470static int ibm_acpi_driver_read(char *p)
471{
472 int len = 0;
473
474 len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
475 len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
476
477 return len;
478}
479
480/*************************************************************************
481 * Hotkey subdriver
482 */
483
484static int hotkey_supported;
485static int hotkey_mask_supported;
486static int hotkey_orig_status;
487static int hotkey_orig_mask;
488
489static int hotkey_init(void)
490{
491 /* hotkey not supported on 570 */
492 hotkey_supported = hkey_handle != NULL;
493
494 if (hotkey_supported) {
495 /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
496 A30, R30, R31, T20-22, X20-21, X22-24 */
497 hotkey_mask_supported =
498 acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
499
500 if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask))
501 return -ENODEV;
502 }
503
504 return 0;
505}
506
507static void hotkey_exit(void)
508{
509 if (hotkey_supported)
510 hotkey_set(hotkey_orig_status, hotkey_orig_mask);
511}
512
513static void hotkey_notify(struct ibm_struct *ibm, u32 event)
514{
515 int hkey;
516
517 if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
518 acpi_bus_generate_event(ibm->device, event, hkey);
519 else {
520 printk(IBM_ERR "unknown hotkey event %d\n", event);
521 acpi_bus_generate_event(ibm->device, event, 0);
522 }
523}
524
525static int hotkey_get(int *status, int *mask)
526{
527 if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
528 return 0;
529
530 if (hotkey_mask_supported)
531 if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
532 return 0;
533
534 return 1;
535}
536
537static int hotkey_set(int status, int mask)
538{
539 int i;
540
541 if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
542 return 0;
543
544 if (hotkey_mask_supported)
545 for (i = 0; i < 32; i++) {
546 int bit = ((1 << i) & mask) != 0;
547 if (!acpi_evalf(hkey_handle,
548 NULL, "MHKM", "vdd", i + 1, bit))
549 return 0;
550 }
551
552 return 1;
553}
554
555static int hotkey_read(char *p)
556{
557 int status, mask;
558 int len = 0;
559
560 if (!hotkey_supported) {
561 len += sprintf(p + len, "status:\t\tnot supported\n");
562 return len;
563 }
564
565 if (!hotkey_get(&status, &mask))
566 return -EIO;
567
568 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
569 if (hotkey_mask_supported) {
570 len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
571 len += sprintf(p + len,
572 "commands:\tenable, disable, reset, <mask>\n");
573 } else {
574 len += sprintf(p + len, "mask:\t\tnot supported\n");
575 len += sprintf(p + len, "commands:\tenable, disable, reset\n");
576 }
577
578 return len;
579}
580
581static int hotkey_write(char *buf)
582{
583 int status, mask;
584 char *cmd;
585 int do_cmd = 0;
586
587 if (!hotkey_supported)
588 return -ENODEV;
589
590 if (!hotkey_get(&status, &mask))
591 return -EIO;
592
593 while ((cmd = next_cmd(&buf))) {
594 if (strlencmp(cmd, "enable") == 0) {
595 status = 1;
596 } else if (strlencmp(cmd, "disable") == 0) {
597 status = 0;
598 } else if (strlencmp(cmd, "reset") == 0) {
599 status = hotkey_orig_status;
600 mask = hotkey_orig_mask;
601 } else if (sscanf(cmd, "0x%x", &mask) == 1) {
602 /* mask set */
603 } else if (sscanf(cmd, "%x", &mask) == 1) {
604 /* mask set */
605 } else
606 return -EINVAL;
607 do_cmd = 1;
608 }
609
610 if (do_cmd && !hotkey_set(status, mask))
611 return -EIO;
612
613 return 0;
614}
615
616/*************************************************************************
617 * Bluetooth subdriver
618 */
619
620static int bluetooth_supported;
621
622static int bluetooth_init(void)
623{
624 /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
625 G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
626 bluetooth_supported = hkey_handle &&
627 acpi_evalf(hkey_handle, NULL, "GBDC", "qv");
628
629 return 0;
630}
631
632static int bluetooth_status(void)
633{
634 int status;
635
636 if (!bluetooth_supported ||
637 !acpi_evalf(hkey_handle, &status, "GBDC", "d"))
638 status = 0;
639
640 return status;
641}
642
643static int bluetooth_read(char *p)
644{
645 int len = 0;
646 int status = bluetooth_status();
647
648 if (!bluetooth_supported)
649 len += sprintf(p + len, "status:\t\tnot supported\n");
650 else if (!(status & 1))
651 len += sprintf(p + len, "status:\t\tnot installed\n");
652 else {
653 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
654 len += sprintf(p + len, "commands:\tenable, disable\n");
655 }
656
657 return len;
658}
659
660static int bluetooth_write(char *buf)
661{
662 int status = bluetooth_status();
663 char *cmd;
664 int do_cmd = 0;
665
666 if (!bluetooth_supported)
667 return -ENODEV;
668
669 while ((cmd = next_cmd(&buf))) {
670 if (strlencmp(cmd, "enable") == 0) {
671 status |= 2;
672 } else if (strlencmp(cmd, "disable") == 0) {
673 status &= ~2;
674 } else
675 return -EINVAL;
676 do_cmd = 1;
677 }
678
679 if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
680 return -EIO;
681
682 return 0;
683}
684
685/*************************************************************************
686 * Wan subdriver
687 */
688
689static int wan_supported;
690
691static int wan_init(void)
692{
693 wan_supported = hkey_handle &&
694 acpi_evalf(hkey_handle, NULL, "GWAN", "qv");
695
696 return 0;
697}
698
699static int wan_status(void)
700{
701 int status;
702
703 if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d"))
704 status = 0;
705
706 return status;
707}
708
709static int wan_read(char *p)
710{
711 int len = 0;
712 int status = wan_status();
713
714 if (!wan_supported)
715 len += sprintf(p + len, "status:\t\tnot supported\n");
716 else if (!(status & 1))
717 len += sprintf(p + len, "status:\t\tnot installed\n");
718 else {
719 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
720 len += sprintf(p + len, "commands:\tenable, disable\n");
721 }
722
723 return len;
724}
725
726static int wan_write(char *buf)
727{
728 int status = wan_status();
729 char *cmd;
730 int do_cmd = 0;
731
732 if (!wan_supported)
733 return -ENODEV;
734
735 while ((cmd = next_cmd(&buf))) {
736 if (strlencmp(cmd, "enable") == 0) {
737 status |= 2;
738 } else if (strlencmp(cmd, "disable") == 0) {
739 status &= ~2;
740 } else
741 return -EINVAL;
742 do_cmd = 1;
743 }
744
745 if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
746 return -EIO;
747
748 return 0;
749}
750
751/*************************************************************************
752 * Video subdriver
753 */
754
755static enum video_access_mode video_supported;
756static int video_orig_autosw;
757
758IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
759 "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
760 "\\_SB.PCI0.VID0", /* 770e */
761 "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
762 "\\_SB.PCI0.AGP.VID", /* all others */
763 ); /* R30, R31 */
764
765IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
766
767static int video_init(void)
768{
769 int ivga;
770
771 if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
772 /* G41, assume IVGA doesn't change */
773 vid_handle = vid2_handle;
774
775 if (!vid_handle)
776 /* video switching not supported on R30, R31 */
777 video_supported = IBMACPI_VIDEO_NONE;
778 else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
779 /* 570 */
780 video_supported = IBMACPI_VIDEO_570;
781 else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
782 /* 600e/x, 770e, 770x */
783 video_supported = IBMACPI_VIDEO_770;
784 else
785 /* all others */
786 video_supported = IBMACPI_VIDEO_NEW;
787
788 return 0;
789}
790
791static void video_exit(void)
792{
793 acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw);
794}
795
796static int video_status(void)
797{
798 int status = 0;
799 int i;
800
801 if (video_supported == IBMACPI_VIDEO_570) {
802 if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87))
803 status = i & 3;
804 } else if (video_supported == IBMACPI_VIDEO_770) {
805 if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
806 status |= 0x01 * i;
807 if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
808 status |= 0x02 * i;
809 } else if (video_supported == IBMACPI_VIDEO_NEW) {
810 acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1);
811 if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
812 status |= 0x02 * i;
813
814 acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0);
815 if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
816 status |= 0x01 * i;
817 if (acpi_evalf(NULL, &i, "\\VCDD", "d"))
818 status |= 0x08 * i;
819 }
820
821 return status;
822}
823
824static int video_autosw(void)
825{
826 int autosw = 0;
827
828 if (video_supported == IBMACPI_VIDEO_570)
829 acpi_evalf(vid_handle, &autosw, "SWIT", "d");
830 else if (video_supported == IBMACPI_VIDEO_770 ||
831 video_supported == IBMACPI_VIDEO_NEW)
832 acpi_evalf(vid_handle, &autosw, "^VDEE", "d");
833
834 return autosw & 1;
835}
836
837static int video_switch(void)
838{
839 int autosw = video_autosw();
840 int ret;
841
842 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
843 return -EIO;
844 ret = video_supported == IBMACPI_VIDEO_570 ?
845 acpi_evalf(ec_handle, NULL, "_Q16", "v") :
846 acpi_evalf(vid_handle, NULL, "VSWT", "v");
847 acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
848
849 return ret;
850}
851
852static int video_expand(void)
853{
854 if (video_supported == IBMACPI_VIDEO_570)
855 return acpi_evalf(ec_handle, NULL, "_Q17", "v");
856 else if (video_supported == IBMACPI_VIDEO_770)
857 return acpi_evalf(vid_handle, NULL, "VEXP", "v");
858 else
859 return acpi_evalf(NULL, NULL, "\\VEXP", "v");
860}
861
862static int video_switch2(int status)
863{
864 int ret;
865
866 if (video_supported == IBMACPI_VIDEO_570) {
867 ret = acpi_evalf(NULL, NULL,
868 "\\_SB.PHS2", "vdd", 0x8b, status | 0x80);
869 } else if (video_supported == IBMACPI_VIDEO_770) {
870 int autosw = video_autosw();
871 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
872 return -EIO;
873
874 ret = acpi_evalf(vid_handle, NULL,
875 "ASWT", "vdd", status * 0x100, 0);
876
877 acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
878 } else {
879 ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
880 acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
881 }
882
883 return ret;
884}
885
886static int video_read(char *p)
887{
888 int status = video_status();
889 int autosw = video_autosw();
890 int len = 0;
891
892 if (!video_supported) {
893 len += sprintf(p + len, "status:\t\tnot supported\n");
894 return len;
895 }
896
897 len += sprintf(p + len, "status:\t\tsupported\n");
898 len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
899 len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
900 if (video_supported == IBMACPI_VIDEO_NEW)
901 len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
902 len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
903 len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
904 len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
905 if (video_supported == IBMACPI_VIDEO_NEW)
906 len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
907 len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
908 len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
909
910 return len;
911}
912
913static int video_write(char *buf)
914{
915 char *cmd;
916 int enable, disable, status;
917
918 if (!video_supported)
919 return -ENODEV;
920
921 enable = disable = 0;
922
923 while ((cmd = next_cmd(&buf))) {
924 if (strlencmp(cmd, "lcd_enable") == 0) {
925 enable |= 0x01;
926 } else if (strlencmp(cmd, "lcd_disable") == 0) {
927 disable |= 0x01;
928 } else if (strlencmp(cmd, "crt_enable") == 0) {
929 enable |= 0x02;
930 } else if (strlencmp(cmd, "crt_disable") == 0) {
931 disable |= 0x02;
932 } else if (video_supported == IBMACPI_VIDEO_NEW &&
933 strlencmp(cmd, "dvi_enable") == 0) {
934 enable |= 0x08;
935 } else if (video_supported == IBMACPI_VIDEO_NEW &&
936 strlencmp(cmd, "dvi_disable") == 0) {
937 disable |= 0x08;
938 } else if (strlencmp(cmd, "auto_enable") == 0) {
939 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
940 return -EIO;
941 } else if (strlencmp(cmd, "auto_disable") == 0) {
942 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0))
943 return -EIO;
944 } else if (strlencmp(cmd, "video_switch") == 0) {
945 if (!video_switch())
946 return -EIO;
947 } else if (strlencmp(cmd, "expand_toggle") == 0) {
948 if (!video_expand())
949 return -EIO;
950 } else
951 return -EINVAL;
952 }
953
954 if (enable || disable) {
955 status = (video_status() & 0x0f & ~disable) | enable;
956 if (!video_switch2(status))
957 return -EIO;
958 }
959
960 return 0;
961}
962
963/*************************************************************************
964 * Light (thinklight) subdriver
965 */
966
967static int light_supported;
968static int light_status_supported;
969
970IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
971IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */
972
973static int light_init(void)
974{
975 /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
976 light_supported = (cmos_handle || lght_handle) && !ledb_handle;
977
978 if (light_supported)
979 /* light status not supported on
980 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
981 light_status_supported = acpi_evalf(ec_handle, NULL,
982 "KBLT", "qv");
983
984 return 0;
985}
986
987static int light_read(char *p)
988{
989 int len = 0;
990 int status = 0;
991
992 if (!light_supported) {
993 len += sprintf(p + len, "status:\t\tnot supported\n");
994 } else if (!light_status_supported) {
995 len += sprintf(p + len, "status:\t\tunknown\n");
996 len += sprintf(p + len, "commands:\ton, off\n");
997 } else {
998 if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
999 return -EIO;
1000 len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
1001 len += sprintf(p + len, "commands:\ton, off\n");
1002 }
1003
1004 return len;
1005}
1006
1007static int light_write(char *buf)
1008{
1009 int cmos_cmd, lght_cmd;
1010 char *cmd;
1011 int success;
1012
1013 if (!light_supported)
1014 return -ENODEV;
1015
1016 while ((cmd = next_cmd(&buf))) {
1017 if (strlencmp(cmd, "on") == 0) {
1018 cmos_cmd = 0x0c;
1019 lght_cmd = 1;
1020 } else if (strlencmp(cmd, "off") == 0) {
1021 cmos_cmd = 0x0d;
1022 lght_cmd = 0;
1023 } else
1024 return -EINVAL;
1025
1026 success = cmos_handle ?
1027 acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
1028 acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
1029 if (!success)
1030 return -EIO;
1031 }
1032
1033 return 0;
1034}
1035
1036/*************************************************************************
1037 * Dock subdriver
1038 */
1039
1040/* don't list other alternatives as we install a notify handler on the 570 */
1041IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
1042
1043#ifdef CONFIG_ACPI_IBM_DOCK
1044
1045IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
1046 "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
1047 "\\_SB.PCI0.PCI1.DOCK", /* all others */
1048 "\\_SB.PCI.ISA.SLCE", /* 570 */
1049 ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
1050
1051#define dock_docked() (_sta(dock_handle) & 1)
1052
1053static void dock_notify(struct ibm_struct *ibm, u32 event)
1054{
1055 int docked = dock_docked();
1056 int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID);
1057
1058 if (event == 1 && !pci) /* 570 */
1059 acpi_bus_generate_event(ibm->device, event, 1); /* button */
1060 else if (event == 1 && pci) /* 570 */
1061 acpi_bus_generate_event(ibm->device, event, 3); /* dock */
1062 else if (event == 3 && docked)
1063 acpi_bus_generate_event(ibm->device, event, 1); /* button */
1064 else if (event == 3 && !docked)
1065 acpi_bus_generate_event(ibm->device, event, 2); /* undock */
1066 else if (event == 0 && docked)
1067 acpi_bus_generate_event(ibm->device, event, 3); /* dock */
1068 else {
1069 printk(IBM_ERR "unknown dock event %d, status %d\n",
1070 event, _sta(dock_handle));
1071 acpi_bus_generate_event(ibm->device, event, 0); /* unknown */
1072 }
1073}
1074
1075static int dock_read(char *p)
1076{
1077 int len = 0;
1078 int docked = dock_docked();
1079
1080 if (!dock_handle)
1081 len += sprintf(p + len, "status:\t\tnot supported\n");
1082 else if (!docked)
1083 len += sprintf(p + len, "status:\t\tundocked\n");
1084 else {
1085 len += sprintf(p + len, "status:\t\tdocked\n");
1086 len += sprintf(p + len, "commands:\tdock, undock\n");
1087 }
1088
1089 return len;
1090}
1091
1092static int dock_write(char *buf)
1093{
1094 char *cmd;
1095
1096 if (!dock_docked())
1097 return -ENODEV;
1098
1099 while ((cmd = next_cmd(&buf))) {
1100 if (strlencmp(cmd, "undock") == 0) {
1101 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
1102 !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
1103 return -EIO;
1104 } else if (strlencmp(cmd, "dock") == 0) {
1105 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
1106 return -EIO;
1107 } else
1108 return -EINVAL;
1109 }
1110
1111 return 0;
1112}
1113
1114#endif /* CONFIG_ACPI_IBM_DOCK */
1115
1116/*************************************************************************
1117 * Bay subdriver
1118 */
1119
1120#ifdef CONFIG_ACPI_IBM_BAY
1121static int bay_status_supported;
1122static int bay_status2_supported;
1123static int bay_eject_supported;
1124static int bay_eject2_supported;
1125
1126IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
1127 "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
1128 "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
1129 "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
1130 ); /* A21e, R30, R31 */
1131IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
1132 "_EJ0", /* all others */
1133 ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
1134IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
1135 "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
1136 ); /* all others */
1137IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
1138 "_EJ0", /* 770x */
1139 ); /* all others */
1140
1141static int bay_init(void)
1142{
1143 bay_status_supported = bay_handle &&
1144 acpi_evalf(bay_handle, NULL, "_STA", "qv");
1145 bay_status2_supported = bay2_handle &&
1146 acpi_evalf(bay2_handle, NULL, "_STA", "qv");
1147
1148 bay_eject_supported = bay_handle && bay_ej_handle &&
1149 (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
1150 bay_eject2_supported = bay2_handle && bay2_ej_handle &&
1151 (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
1152
1153 return 0;
1154}
1155
1156static void bay_notify(struct ibm_struct *ibm, u32 event)
1157{
1158 acpi_bus_generate_event(ibm->device, event, 0);
1159}
1160
1161#define bay_occupied(b) (_sta(b##_handle) & 1)
1162
1163static int bay_read(char *p)
1164{
1165 int len = 0;
1166 int occupied = bay_occupied(bay);
1167 int occupied2 = bay_occupied(bay2);
1168 int eject, eject2;
1169
1170 len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ?
1171 (occupied ? "occupied" : "unoccupied") :
1172 "not supported");
1173 if (bay_status2_supported)
1174 len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
1175 "occupied" : "unoccupied");
1176
1177 eject = bay_eject_supported && occupied;
1178 eject2 = bay_eject2_supported && occupied2;
1179
1180 if (eject && eject2)
1181 len += sprintf(p + len, "commands:\teject, eject2\n");
1182 else if (eject)
1183 len += sprintf(p + len, "commands:\teject\n");
1184 else if (eject2)
1185 len += sprintf(p + len, "commands:\teject2\n");
1186
1187 return len;
1188}
1189
1190static int bay_write(char *buf)
1191{
1192 char *cmd;
1193
1194 if (!bay_eject_supported && !bay_eject2_supported)
1195 return -ENODEV;
1196
1197 while ((cmd = next_cmd(&buf))) {
1198 if (bay_eject_supported && strlencmp(cmd, "eject") == 0) {
1199 if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
1200 return -EIO;
1201 } else if (bay_eject2_supported &&
1202 strlencmp(cmd, "eject2") == 0) {
1203 if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
1204 return -EIO;
1205 } else
1206 return -EINVAL;
1207 }
1208
1209 return 0;
1210}
1211#endif /* CONFIG_ACPI_IBM_BAY */
1212
1213/*************************************************************************
1214 * CMOS subdriver
1215 */
1216
1217static int cmos_eval(int cmos_cmd)
1218{
1219 if (cmos_handle)
1220 return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd);
1221 else
1222 return 1;
1223}
1224
1225static int cmos_read(char *p)
1226{
1227 int len = 0;
1228
1229 /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
1230 R30, R31, T20-22, X20-21 */
1231 if (!cmos_handle)
1232 len += sprintf(p + len, "status:\t\tnot supported\n");
1233 else {
1234 len += sprintf(p + len, "status:\t\tsupported\n");
1235 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
1236 }
1237
1238 return len;
1239}
1240
1241static int cmos_write(char *buf)
1242{
1243 char *cmd;
1244 int cmos_cmd;
1245
1246 if (!cmos_handle)
1247 return -EINVAL;
1248
1249 while ((cmd = next_cmd(&buf))) {
1250 if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
1251 cmos_cmd >= 0 && cmos_cmd <= 21) {
1252 /* cmos_cmd set */
1253 } else
1254 return -EINVAL;
1255
1256 if (!cmos_eval(cmos_cmd))
1257 return -EIO;
1258 }
1259
1260 return 0;
1261}
1262
1263
1264/*************************************************************************
1265 * LED subdriver
1266 */
1267
1268static enum led_access_mode led_supported;
1269
1270IBM_HANDLE(led, ec, "SLED", /* 570 */
1271 "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
1272 "LED", /* all others */
1273 ); /* R30, R31 */
1274
1275static int led_init(void)
1276{
1277 if (!led_handle)
1278 /* led not supported on R30, R31 */
1279 led_supported = IBMACPI_LED_NONE;
1280 else if (strlencmp(led_path, "SLED") == 0)
1281 /* 570 */
1282 led_supported = IBMACPI_LED_570;
1283 else if (strlencmp(led_path, "SYSL") == 0)
1284 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
1285 led_supported = IBMACPI_LED_OLD;
1286 else
1287 /* all others */
1288 led_supported = IBMACPI_LED_NEW;
1289
1290 return 0;
1291}
1292
1293#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
1294
1295static int led_read(char *p)
1296{
1297 int len = 0;
1298
1299 if (!led_supported) {
1300 len += sprintf(p + len, "status:\t\tnot supported\n");
1301 return len;
1302 }
1303 len += sprintf(p + len, "status:\t\tsupported\n");
1304
1305 if (led_supported == IBMACPI_LED_570) {
1306 /* 570 */
1307 int i, status;
1308 for (i = 0; i < 8; i++) {
1309 if (!acpi_evalf(ec_handle,
1310 &status, "GLED", "dd", 1 << i))
1311 return -EIO;
1312 len += sprintf(p + len, "%d:\t\t%s\n",
1313 i, led_status(status));
1314 }
1315 }
1316
1317 len += sprintf(p + len, "commands:\t"
1318 "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
1319
1320 return len;
1321}
1322
1323/* off, on, blink */
1324static const int led_sled_arg1[] = { 0, 1, 3 };
1325static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
1326static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
1327static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
1328
1329static int led_write(char *buf)
1330{
1331 char *cmd;
1332 int led, ind, ret;
1333
1334 if (!led_supported)
1335 return -ENODEV;
1336
1337 while ((cmd = next_cmd(&buf))) {
1338 if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
1339 return -EINVAL;
1340
1341 if (strstr(cmd, "off")) {
1342 ind = 0;
1343 } else if (strstr(cmd, "on")) {
1344 ind = 1;
1345 } else if (strstr(cmd, "blink")) {
1346 ind = 2;
1347 } else
1348 return -EINVAL;
1349
1350 if (led_supported == IBMACPI_LED_570) {
1351 /* 570 */
1352 led = 1 << led;
1353 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
1354 led, led_sled_arg1[ind]))
1355 return -EIO;
1356 } else if (led_supported == IBMACPI_LED_OLD) {
1357 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
1358 led = 1 << led;
1359 ret = ec_write(IBMACPI_LED_EC_HLMS, led);
1360 if (ret >= 0)
1361 ret =
1362 ec_write(IBMACPI_LED_EC_HLBL,
1363 led * led_exp_hlbl[ind]);
1364 if (ret >= 0)
1365 ret =
1366 ec_write(IBMACPI_LED_EC_HLCL,
1367 led * led_exp_hlcl[ind]);
1368 if (ret < 0)
1369 return ret;
1370 } else {
1371 /* all others */
1372 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
1373 led, led_led_arg1[ind]))
1374 return -EIO;
1375 }
1376 }
1377
1378 return 0;
1379}
1380
1381/*************************************************************************
1382 * Beep subdriver
1383 */
1384
1385IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
1386
1387static int beep_read(char *p)
1388{
1389 int len = 0;
1390
1391 if (!beep_handle)
1392 len += sprintf(p + len, "status:\t\tnot supported\n");
1393 else {
1394 len += sprintf(p + len, "status:\t\tsupported\n");
1395 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
1396 }
1397
1398 return len;
1399}
1400
1401static int beep_write(char *buf)
1402{
1403 char *cmd;
1404 int beep_cmd;
1405
1406 if (!beep_handle)
1407 return -ENODEV;
1408
1409 while ((cmd = next_cmd(&buf))) {
1410 if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
1411 beep_cmd >= 0 && beep_cmd <= 17) {
1412 /* beep_cmd set */
1413 } else
1414 return -EINVAL;
1415 if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
1416 return -EIO;
1417 }
1418
1419 return 0;
1420}
1421
1422/*************************************************************************
1423 * Thermal subdriver
1424 */
1425
1426static enum thermal_access_mode thermal_read_mode;
1427
1428static int thermal_init(void)
1429{
1430 u8 t, ta1, ta2;
1431 int i;
1432 int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
1433
1434 if (ibm_thinkpad_ec_found && experimental) {
1435 /*
1436 * Direct EC access mode: sensors at registers
1437 * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
1438 * non-implemented, thermal sensors return 0x80 when
1439 * not available
1440 */
1441
1442 ta1 = ta2 = 0;
1443 for (i = 0; i < 8; i++) {
1444 if (likely(acpi_ec_read(0x78 + i, &t))) {
1445 ta1 |= t;
1446 } else {
1447 ta1 = 0;
1448 break;
1449 }
1450 if (likely(acpi_ec_read(0xC0 + i, &t))) {
1451 ta2 |= t;
1452 } else {
1453 ta1 = 0;
1454 break;
1455 }
1456 }
1457 if (ta1 == 0) {
1458 /* This is sheer paranoia, but we handle it anyway */
1459 if (acpi_tmp7) {
1460 printk(IBM_ERR
1461 "ThinkPad ACPI EC access misbehaving, "
1462 "falling back to ACPI TMPx access mode\n");
1463 thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;
1464 } else {
1465 printk(IBM_ERR
1466 "ThinkPad ACPI EC access misbehaving, "
1467 "disabling thermal sensors access\n");
1468 thermal_read_mode = IBMACPI_THERMAL_NONE;
1469 }
1470 } else {
1471 thermal_read_mode =
1472 (ta2 != 0) ?
1473 IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8;
1474 }
1475 } else if (acpi_tmp7) {
1476 if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
1477 /* 600e/x, 770e, 770x */
1478 thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT;
1479 } else {
1480 /* Standard ACPI TMPx access, max 8 sensors */
1481 thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07;
1482 }
1483 } else {
1484 /* temperatures not supported on 570, G4x, R30, R31, R32 */
1485 thermal_read_mode = IBMACPI_THERMAL_NONE;
1486 }
1487
1488 return 0;
1489}
1490
1491static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
1492{
1493 int i, t;
1494 s8 tmp;
1495 char tmpi[] = "TMPi";
1496
1497 if (!s)
1498 return -EINVAL;
1499
1500 switch (thermal_read_mode) {
1501#if IBMACPI_MAX_THERMAL_SENSORS >= 16
1502 case IBMACPI_THERMAL_TPEC_16:
1503 for (i = 0; i < 8; i++) {
1504 if (!acpi_ec_read(0xC0 + i, &tmp))
1505 return -EIO;
1506 s->temp[i + 8] = tmp * 1000;
1507 }
1508 /* fallthrough */
1509#endif
1510 case IBMACPI_THERMAL_TPEC_8:
1511 for (i = 0; i < 8; i++) {
1512 if (!acpi_ec_read(0x78 + i, &tmp))
1513 return -EIO;
1514 s->temp[i] = tmp * 1000;
1515 }
1516 return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8;
1517
1518 case IBMACPI_THERMAL_ACPI_UPDT:
1519 if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
1520 return -EIO;
1521 for (i = 0; i < 8; i++) {
1522 tmpi[3] = '0' + i;
1523 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
1524 return -EIO;
1525 s->temp[i] = (t - 2732) * 100;
1526 }
1527 return 8;
1528
1529 case IBMACPI_THERMAL_ACPI_TMP07:
1530 for (i = 0; i < 8; i++) {
1531 tmpi[3] = '0' + i;
1532 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
1533 return -EIO;
1534 s->temp[i] = t * 1000;
1535 }
1536 return 8;
1537
1538 case IBMACPI_THERMAL_NONE:
1539 default:
1540 return 0;
1541 }
1542}
1543
1544static int thermal_read(char *p)
1545{
1546 int len = 0;
1547 int n, i;
1548 struct ibm_thermal_sensors_struct t;
1549
1550 n = thermal_get_sensors(&t);
1551 if (unlikely(n < 0))
1552 return n;
1553
1554 len += sprintf(p + len, "temperatures:\t");
1555
1556 if (n > 0) {
1557 for (i = 0; i < (n - 1); i++)
1558 len += sprintf(p + len, "%d ", t.temp[i] / 1000);
1559 len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
1560 } else
1561 len += sprintf(p + len, "not supported\n");
1562
1563 return len;
1564}
1565
1566/*************************************************************************
1567 * EC Dump subdriver
1568 */
1569
1570static u8 ecdump_regs[256];
1571
1572static int ecdump_read(char *p)
1573{
1574 int len = 0;
1575 int i, j;
1576 u8 v;
1577
1578 len += sprintf(p + len, "EC "
1579 " +00 +01 +02 +03 +04 +05 +06 +07"
1580 " +08 +09 +0a +0b +0c +0d +0e +0f\n");
1581 for (i = 0; i < 256; i += 16) {
1582 len += sprintf(p + len, "EC 0x%02x:", i);
1583 for (j = 0; j < 16; j++) {
1584 if (!acpi_ec_read(i + j, &v))
1585 break;
1586 if (v != ecdump_regs[i + j])
1587 len += sprintf(p + len, " *%02x", v);
1588 else
1589 len += sprintf(p + len, " %02x", v);
1590 ecdump_regs[i + j] = v;
1591 }
1592 len += sprintf(p + len, "\n");
1593 if (j != 16)
1594 break;
1595 }
1596
1597 /* These are way too dangerous to advertise openly... */
1598#if 0
1599 len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
1600 " (<offset> is 00-ff, <value> is 00-ff)\n");
1601 len += sprintf(p + len, "commands:\t0x<offset> <value> "
1602 " (<offset> is 00-ff, <value> is 0-255)\n");
1603#endif
1604 return len;
1605}
1606
1607static int ecdump_write(char *buf)
1608{
1609 char *cmd;
1610 int i, v;
1611
1612 while ((cmd = next_cmd(&buf))) {
1613 if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
1614 /* i and v set */
1615 } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
1616 /* i and v set */
1617 } else
1618 return -EINVAL;
1619 if (i >= 0 && i < 256 && v >= 0 && v < 256) {
1620 if (!acpi_ec_write(i, v))
1621 return -EIO;
1622 } else
1623 return -EINVAL;
1624 }
1625
1626 return 0;
1627}
1628
1629/*************************************************************************
1630 * Backlight/brightness subdriver
1631 */
1632
1633static struct backlight_device *ibm_backlight_device = NULL;
1634
1635static struct backlight_ops ibm_backlight_data = {
1636 .get_brightness = brightness_get,
1637 .update_status = brightness_update_status,
1638};
1639
1640static int brightness_init(void)
1641{
1642 int b;
1643
1644 b = brightness_get(NULL);
1645 if (b < 0)
1646 return b;
1647
1648 ibm_backlight_device = backlight_device_register("ibm", NULL, NULL,
1649 &ibm_backlight_data);
1650 if (IS_ERR(ibm_backlight_device)) {
1651 printk(IBM_ERR "Could not register backlight device\n");
1652 return PTR_ERR(ibm_backlight_device);
1653 }
1654
1655 ibm_backlight_device->props.max_brightness = 7;
1656 ibm_backlight_device->props.brightness = b;
1657 backlight_update_status(ibm_backlight_device);
1658
1659 return 0;
1660}
1661
1662static void brightness_exit(void)
1663{
1664 if (ibm_backlight_device) {
1665 backlight_device_unregister(ibm_backlight_device);
1666 ibm_backlight_device = NULL;
1667 }
1668}
1669
1670static int brightness_update_status(struct backlight_device *bd)
1671{
1672 return brightness_set(
1673 (bd->props.fb_blank == FB_BLANK_UNBLANK &&
1674 bd->props.power == FB_BLANK_UNBLANK) ?
1675 bd->props.brightness : 0);
1676}
1677
1678static int brightness_get(struct backlight_device *bd)
1679{
1680 u8 level;
1681 if (!acpi_ec_read(brightness_offset, &level))
1682 return -EIO;
1683
1684 level &= 0x7;
1685
1686 return level;
1687}
1688
1689static int brightness_set(int value)
1690{
1691 int cmos_cmd, inc, i;
1692 int current_value = brightness_get(NULL);
1693
1694 value &= 7;
1695
1696 cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN;
1697 inc = value > current_value ? 1 : -1;
1698 for (i = current_value; i != value; i += inc) {
1699 if (!cmos_eval(cmos_cmd))
1700 return -EIO;
1701 if (!acpi_ec_write(brightness_offset, i + inc))
1702 return -EIO;
1703 }
1704
1705 return 0;
1706}
1707
1708static int brightness_read(char *p)
1709{
1710 int len = 0;
1711 int level;
1712
1713 if ((level = brightness_get(NULL)) < 0) {
1714 len += sprintf(p + len, "level:\t\tunreadable\n");
1715 } else {
1716 len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
1717 len += sprintf(p + len, "commands:\tup, down\n");
1718 len += sprintf(p + len, "commands:\tlevel <level>"
1719 " (<level> is 0-7)\n");
1720 }
1721
1722 return len;
1723}
1724
1725static int brightness_write(char *buf)
1726{
1727 int level;
1728 int new_level;
1729 char *cmd;
1730
1731 while ((cmd = next_cmd(&buf))) {
1732 if ((level = brightness_get(NULL)) < 0)
1733 return level;
1734 level &= 7;
1735
1736 if (strlencmp(cmd, "up") == 0) {
1737 new_level = level == 7 ? 7 : level + 1;
1738 } else if (strlencmp(cmd, "down") == 0) {
1739 new_level = level == 0 ? 0 : level - 1;
1740 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
1741 new_level >= 0 && new_level <= 7) {
1742 /* new_level set */
1743 } else
1744 return -EINVAL;
1745
1746 brightness_set(new_level);
1747 }
1748
1749 return 0;
1750}
1751
1752/*************************************************************************
1753 * Volume subdriver
1754 */
1755
1756static int volume_read(char *p)
1757{
1758 int len = 0;
1759 u8 level;
1760
1761 if (!acpi_ec_read(volume_offset, &level)) {
1762 len += sprintf(p + len, "level:\t\tunreadable\n");
1763 } else {
1764 len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
1765 len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
1766 len += sprintf(p + len, "commands:\tup, down, mute\n");
1767 len += sprintf(p + len, "commands:\tlevel <level>"
1768 " (<level> is 0-15)\n");
1769 }
1770
1771 return len;
1772}
1773
1774static int volume_write(char *buf)
1775{
1776 int cmos_cmd, inc, i;
1777 u8 level, mute;
1778 int new_level, new_mute;
1779 char *cmd;
1780
1781 while ((cmd = next_cmd(&buf))) {
1782 if (!acpi_ec_read(volume_offset, &level))
1783 return -EIO;
1784 new_mute = mute = level & 0x40;
1785 new_level = level = level & 0xf;
1786
1787 if (strlencmp(cmd, "up") == 0) {
1788 if (mute)
1789 new_mute = 0;
1790 else
1791 new_level = level == 15 ? 15 : level + 1;
1792 } else if (strlencmp(cmd, "down") == 0) {
1793 if (mute)
1794 new_mute = 0;
1795 else
1796 new_level = level == 0 ? 0 : level - 1;
1797 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
1798 new_level >= 0 && new_level <= 15) {
1799 /* new_level set */
1800 } else if (strlencmp(cmd, "mute") == 0) {
1801 new_mute = 0x40;
1802 } else
1803 return -EINVAL;
1804
1805 if (new_level != level) { /* mute doesn't change */
1806 cmos_cmd = new_level > level ? TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
1807 inc = new_level > level ? 1 : -1;
1808
1809 if (mute && (!cmos_eval(cmos_cmd) ||
1810 !acpi_ec_write(volume_offset, level)))
1811 return -EIO;
1812
1813 for (i = level; i != new_level; i += inc)
1814 if (!cmos_eval(cmos_cmd) ||
1815 !acpi_ec_write(volume_offset, i + inc))
1816 return -EIO;
1817
1818 if (mute && (!cmos_eval(TP_CMOS_VOLUME_MUTE) ||
1819 !acpi_ec_write(volume_offset,
1820 new_level + mute)))
1821 return -EIO;
1822 }
1823
1824 if (new_mute != mute) { /* level doesn't change */
1825 cmos_cmd = new_mute ? TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
1826
1827 if (!cmos_eval(cmos_cmd) ||
1828 !acpi_ec_write(volume_offset, level + new_mute))
1829 return -EIO;
1830 }
1831 }
1832
1833 return 0;
1834}
1835
1836
1837/*************************************************************************
1838 * Fan subdriver
1839 */
1840
1841/*
1842 * FAN ACCESS MODES
1843 *
1844 * IBMACPI_FAN_RD_ACPI_GFAN:
1845 * ACPI GFAN method: returns fan level
1846 *
1847 * see IBMACPI_FAN_WR_ACPI_SFAN
1848 * EC 0x2f not available if GFAN exists
1849 *
1850 * IBMACPI_FAN_WR_ACPI_SFAN:
1851 * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
1852 *
1853 * EC 0x2f might be available *for reading*, but never for writing.
1854 *
1855 * IBMACPI_FAN_WR_TPEC:
1856 * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported
1857 * on almost all ThinkPads
1858 *
1859 * Fan speed changes of any sort (including those caused by the
1860 * disengaged mode) are usually done slowly by the firmware as the
1861 * maximum ammount of fan duty cycle change per second seems to be
1862 * limited.
1863 *
1864 * Reading is not available if GFAN exists.
1865 * Writing is not available if SFAN exists.
1866 *
1867 * Bits
1868 * 7 automatic mode engaged;
1869 * (default operation mode of the ThinkPad)
1870 * fan level is ignored in this mode.
1871 * 6 disengage mode (takes precedence over bit 7);
1872 * not available on all thinkpads. May disable
1873 * the tachometer, and speeds up fan to 100% duty-cycle,
1874 * which speeds it up far above the standard RPM
1875 * levels. It is not impossible that it could cause
1876 * hardware damage.
1877 * 5-3 unused in some models. Extra bits for fan level
1878 * in others, but still useless as all values above
1879 * 7 map to the same speed as level 7 in these models.
1880 * 2-0 fan level (0..7 usually)
1881 * 0x00 = stop
1882 * 0x07 = max (set when temperatures critical)
1883 * Some ThinkPads may have other levels, see
1884 * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
1885 *
1886 * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
1887 * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
1888 * does so, its initial value is meaningless (0x07).
1889 *
1890 * For firmware bugs, refer to:
1891 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
1892 *
1893 * ----
1894 *
1895 * ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
1896 * Main fan tachometer reading (in RPM)
1897 *
1898 * This register is present on all ThinkPads with a new-style EC, and
1899 * it is known not to be present on the A21m/e, and T22, as there is
1900 * something else in offset 0x84 according to the ACPI DSDT. Other
1901 * ThinkPads from this same time period (and earlier) probably lack the
1902 * tachometer as well.
1903 *
1904 * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
1905 * was never fixed by IBM to report the EC firmware version string
1906 * probably support the tachometer (like the early X models), so
1907 * detecting it is quite hard. We need more data to know for sure.
1908 *
1909 * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
1910 * might result.
1911 *
1912 * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this
1913 * register is not invalidated in ThinkPads that disable tachometer
1914 * readings. Thus, the tachometer readings go stale.
1915 *
1916 * For firmware bugs, refer to:
1917 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
1918 *
1919 * IBMACPI_FAN_WR_ACPI_FANS:
1920 * ThinkPad X31, X40, X41. Not available in the X60.
1921 *
1922 * FANS ACPI handle: takes three arguments: low speed, medium speed,
1923 * high speed. ACPI DSDT seems to map these three speeds to levels
1924 * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
1925 * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
1926 *
1927 * The speeds are stored on handles
1928 * (FANA:FAN9), (FANC:FANB), (FANE:FAND).
1929 *
1930 * There are three default speed sets, acessible as handles:
1931 * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
1932 *
1933 * ACPI DSDT switches which set is in use depending on various
1934 * factors.
1935 *
1936 * IBMACPI_FAN_WR_TPEC is also available and should be used to
1937 * command the fan. The X31/X40/X41 seems to have 8 fan levels,
1938 * but the ACPI tables just mention level 7.
1939 */
1940
1941static enum fan_status_access_mode fan_status_access_mode;
1942static enum fan_control_access_mode fan_control_access_mode;
1943static enum fan_control_commands fan_control_commands;
1944
1945static int fan_control_status_known;
1946static u8 fan_control_initial_status;
1947
1948static void fan_watchdog_fire(struct work_struct *ignored);
1949static int fan_watchdog_maxinterval;
1950static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
1951
1952IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
1953IBM_HANDLE(gfan, ec, "GFAN", /* 570 */
1954 "\\FSPD", /* 600e/x, 770e, 770x */
1955 ); /* all others */
1956IBM_HANDLE(sfan, ec, "SFAN", /* 570 */
1957 "JFNS", /* 770x-JL */
1958 ); /* all others */
1959
1960static int fan_init(void)
1961{
1962 fan_status_access_mode = IBMACPI_FAN_NONE;
1963 fan_control_access_mode = IBMACPI_FAN_WR_NONE;
1964 fan_control_commands = 0;
1965 fan_control_status_known = 1;
1966 fan_watchdog_maxinterval = 0;
1967
1968 if (gfan_handle) {
1969 /* 570, 600e/x, 770e, 770x */
1970 fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN;
1971 } else {
1972 /* all other ThinkPads: note that even old-style
1973 * ThinkPad ECs supports the fan control register */
1974 if (likely(acpi_ec_read(fan_status_offset,
1975 &fan_control_initial_status))) {
1976 fan_status_access_mode = IBMACPI_FAN_RD_TPEC;
1977
1978 /* In some ThinkPads, neither the EC nor the ACPI
1979 * DSDT initialize the fan status, and it ends up
1980 * being set to 0x07 when it *could* be either
1981 * 0x07 or 0x80.
1982 *
1983 * Enable for TP-1Y (T43), TP-78 (R51e),
1984 * TP-76 (R52), TP-70 (T43, R52), which are known
1985 * to be buggy. */
1986 if (fan_control_initial_status == 0x07 &&
1987 ibm_thinkpad_ec_found &&
1988 ((ibm_thinkpad_ec_found[0] == '1' &&
1989 ibm_thinkpad_ec_found[1] == 'Y') ||
1990 (ibm_thinkpad_ec_found[0] == '7' &&
1991 (ibm_thinkpad_ec_found[1] == '6' ||
1992 ibm_thinkpad_ec_found[1] == '8' ||
1993 ibm_thinkpad_ec_found[1] == '0'))
1994 )) {
1995 printk(IBM_NOTICE
1996 "fan_init: initial fan status is "
1997 "unknown, assuming it is in auto "
1998 "mode\n");
1999 fan_control_status_known = 0;
2000 }
2001 } else {
2002 printk(IBM_ERR
2003 "ThinkPad ACPI EC access misbehaving, "
2004 "fan status and control unavailable\n");
2005 return 0;
2006 }
2007 }
2008
2009 if (sfan_handle) {
2010 /* 570, 770x-JL */
2011 fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN;
2012 fan_control_commands |=
2013 IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE;
2014 } else {
2015 if (!gfan_handle) {
2016 /* gfan without sfan means no fan control */
2017 /* all other models implement TP EC 0x2f control */
2018
2019 if (fans_handle) {
2020 /* X31, X40, X41 */
2021 fan_control_access_mode =
2022 IBMACPI_FAN_WR_ACPI_FANS;
2023 fan_control_commands |=
2024 IBMACPI_FAN_CMD_SPEED |
2025 IBMACPI_FAN_CMD_LEVEL |
2026 IBMACPI_FAN_CMD_ENABLE;
2027 } else {
2028 fan_control_access_mode = IBMACPI_FAN_WR_TPEC;
2029 fan_control_commands |=
2030 IBMACPI_FAN_CMD_LEVEL |
2031 IBMACPI_FAN_CMD_ENABLE;
2032 }
2033 }
2034 }
2035
2036 return 0;
2037}
2038
2039static int fan_get_status(u8 *status)
2040{
2041 u8 s;
2042
2043 /* TODO:
2044 * Add IBMACPI_FAN_RD_ACPI_FANS ? */
2045
2046 switch (fan_status_access_mode) {
2047 case IBMACPI_FAN_RD_ACPI_GFAN:
2048 /* 570, 600e/x, 770e, 770x */
2049
2050 if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
2051 return -EIO;
2052
2053 if (likely(status))
2054 *status = s & 0x07;
2055
2056 break;
2057
2058 case IBMACPI_FAN_RD_TPEC:
2059 /* all except 570, 600e/x, 770e, 770x */
2060 if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
2061 return -EIO;
2062
2063 if (likely(status))
2064 *status = s;
2065
2066 break;
2067
2068 default:
2069 return -ENXIO;
2070 }
2071
2072 return 0;
2073}
2074
2075static void fan_exit(void)
2076{
2077 cancel_delayed_work(&fan_watchdog_task);
2078 flush_scheduled_work();
2079}
2080
2081static int fan_get_speed(unsigned int *speed)
2082{
2083 u8 hi, lo;
2084
2085 switch (fan_status_access_mode) {
2086 case IBMACPI_FAN_RD_TPEC:
2087 /* all except 570, 600e/x, 770e, 770x */
2088 if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
2089 !acpi_ec_read(fan_rpm_offset + 1, &hi)))
2090 return -EIO;
2091
2092 if (likely(speed))
2093 *speed = (hi << 8) | lo;
2094
2095 break;
2096
2097 default:
2098 return -ENXIO;
2099 }
2100
2101 return 0;
2102}
2103
2104static void fan_watchdog_fire(struct work_struct *ignored)
2105{
2106 printk(IBM_NOTICE "fan watchdog: enabling fan\n");
2107 if (fan_set_enable()) {
2108 printk(IBM_ERR "fan watchdog: error while enabling fan\n");
2109 /* reschedule for later */
2110 fan_watchdog_reset();
2111 }
2112}
2113
2114static void fan_watchdog_reset(void)
2115{
2116 static int fan_watchdog_active = 0;
2117
2118 if (fan_watchdog_active)
2119 cancel_delayed_work(&fan_watchdog_task);
2120
2121 if (fan_watchdog_maxinterval > 0) {
2122 fan_watchdog_active = 1;
2123 if (!schedule_delayed_work(&fan_watchdog_task,
2124 msecs_to_jiffies(fan_watchdog_maxinterval
2125 * 1000))) {
2126 printk(IBM_ERR "failed to schedule the fan watchdog, "
2127 "watchdog will not trigger\n");
2128 }
2129 } else
2130 fan_watchdog_active = 0;
2131}
2132
2133static int fan_set_level(int level)
2134{
2135 switch (fan_control_access_mode) {
2136 case IBMACPI_FAN_WR_ACPI_SFAN:
2137 if (level >= 0 && level <= 7) {
2138 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
2139 return -EIO;
2140 } else
2141 return -EINVAL;
2142 break;
2143
2144 case IBMACPI_FAN_WR_ACPI_FANS:
2145 case IBMACPI_FAN_WR_TPEC:
2146 if ((level != IBMACPI_FAN_EC_AUTO) &&
2147 (level != IBMACPI_FAN_EC_DISENGAGED) &&
2148 ((level < 0) || (level > 7)))
2149 return -EINVAL;
2150
2151 if (!acpi_ec_write(fan_status_offset, level))
2152 return -EIO;
2153 else
2154 fan_control_status_known = 1;
2155 break;
2156
2157 default:
2158 return -ENXIO;
2159 }
2160 return 0;
2161}
2162
2163static int fan_set_enable(void)
2164{
2165 u8 s;
2166 int rc;
2167
2168 switch (fan_control_access_mode) {
2169 case IBMACPI_FAN_WR_ACPI_FANS:
2170 case IBMACPI_FAN_WR_TPEC:
2171 if ((rc = fan_get_status(&s)) < 0)
2172 return rc;
2173
2174 /* Don't go out of emergency fan mode */
2175 if (s != 7)
2176 s = IBMACPI_FAN_EC_AUTO;
2177
2178 if (!acpi_ec_write(fan_status_offset, s))
2179 return -EIO;
2180 else
2181 fan_control_status_known = 1;
2182 break;
2183
2184 case IBMACPI_FAN_WR_ACPI_SFAN:
2185 if ((rc = fan_get_status(&s)) < 0)
2186 return rc;
2187
2188 s &= 0x07;
2189
2190 /* Set fan to at least level 4 */
2191 if (s < 4)
2192 s = 4;
2193
2194 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
2195 return -EIO;
2196 break;
2197
2198 default:
2199 return -ENXIO;
2200 }
2201 return 0;
2202}
2203
2204static int fan_set_disable(void)
2205{
2206 switch (fan_control_access_mode) {
2207 case IBMACPI_FAN_WR_ACPI_FANS:
2208 case IBMACPI_FAN_WR_TPEC:
2209 if (!acpi_ec_write(fan_status_offset, 0x00))
2210 return -EIO;
2211 else
2212 fan_control_status_known = 1;
2213 break;
2214
2215 case IBMACPI_FAN_WR_ACPI_SFAN:
2216 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
2217 return -EIO;
2218 break;
2219
2220 default:
2221 return -ENXIO;
2222 }
2223 return 0;
2224}
2225
2226static int fan_set_speed(int speed)
2227{
2228 switch (fan_control_access_mode) {
2229 case IBMACPI_FAN_WR_ACPI_FANS:
2230 if (speed >= 0 && speed <= 65535) {
2231 if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
2232 speed, speed, speed))
2233 return -EIO;
2234 } else
2235 return -EINVAL;
2236 break;
2237
2238 default:
2239 return -ENXIO;
2240 }
2241 return 0;
2242}
2243
2244static int fan_read(char *p)
2245{
2246 int len = 0;
2247 int rc;
2248 u8 status;
2249 unsigned int speed = 0;
2250
2251 switch (fan_status_access_mode) {
2252 case IBMACPI_FAN_RD_ACPI_GFAN:
2253 /* 570, 600e/x, 770e, 770x */
2254 if ((rc = fan_get_status(&status)) < 0)
2255 return rc;
2256
2257 len += sprintf(p + len, "status:\t\t%s\n"
2258 "level:\t\t%d\n",
2259 (status != 0) ? "enabled" : "disabled", status);
2260 break;
2261
2262 case IBMACPI_FAN_RD_TPEC:
2263 /* all except 570, 600e/x, 770e, 770x */
2264 if ((rc = fan_get_status(&status)) < 0)
2265 return rc;
2266
2267 if (unlikely(!fan_control_status_known)) {
2268 if (status != fan_control_initial_status)
2269 fan_control_status_known = 1;
2270 else
2271 /* Return most likely status. In fact, it
2272 * might be the only possible status */
2273 status = IBMACPI_FAN_EC_AUTO;
2274 }
2275
2276 len += sprintf(p + len, "status:\t\t%s\n",
2277 (status != 0) ? "enabled" : "disabled");
2278
2279 /* No ThinkPad boots on disengaged mode, we can safely
2280 * assume the tachometer is online if fan control status
2281 * was unknown */
2282 if ((rc = fan_get_speed(&speed)) < 0)
2283 return rc;
2284
2285 len += sprintf(p + len, "speed:\t\t%d\n", speed);
2286
2287 if (status & IBMACPI_FAN_EC_DISENGAGED)
2288 /* Disengaged mode takes precedence */
2289 len += sprintf(p + len, "level:\t\tdisengaged\n");
2290 else if (status & IBMACPI_FAN_EC_AUTO)
2291 len += sprintf(p + len, "level:\t\tauto\n");
2292 else
2293 len += sprintf(p + len, "level:\t\t%d\n", status);
2294 break;
2295
2296 case IBMACPI_FAN_NONE:
2297 default:
2298 len += sprintf(p + len, "status:\t\tnot supported\n");
2299 }
2300
2301 if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) {
2302 len += sprintf(p + len, "commands:\tlevel <level>");
2303
2304 switch (fan_control_access_mode) {
2305 case IBMACPI_FAN_WR_ACPI_SFAN:
2306 len += sprintf(p + len, " (<level> is 0-7)\n");
2307 break;
2308
2309 default:
2310 len += sprintf(p + len, " (<level> is 0-7, "
2311 "auto, disengaged)\n");
2312 break;
2313 }
2314 }
2315
2316 if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE)
2317 len += sprintf(p + len, "commands:\tenable, disable\n"
2318 "commands:\twatchdog <timeout> (<timeout> is 0 (off), "
2319 "1-120 (seconds))\n");
2320
2321 if (fan_control_commands & IBMACPI_FAN_CMD_SPEED)
2322 len += sprintf(p + len, "commands:\tspeed <speed>"
2323 " (<speed> is 0-65535)\n");
2324
2325 return len;
2326}
2327
2328static int fan_write_cmd_level(const char *cmd, int *rc)
2329{
2330 int level;
2331
2332 if (strlencmp(cmd, "level auto") == 0)
2333 level = IBMACPI_FAN_EC_AUTO;
2334 else if (strlencmp(cmd, "level disengaged") == 0)
2335 level = IBMACPI_FAN_EC_DISENGAGED;
2336 else if (sscanf(cmd, "level %d", &level) != 1)
2337 return 0;
2338
2339 if ((*rc = fan_set_level(level)) == -ENXIO)
2340 printk(IBM_ERR "level command accepted for unsupported "
2341 "access mode %d", fan_control_access_mode);
2342
2343 return 1;
2344}
2345
2346static int fan_write_cmd_enable(const char *cmd, int *rc)
2347{
2348 if (strlencmp(cmd, "enable") != 0)
2349 return 0;
2350
2351 if ((*rc = fan_set_enable()) == -ENXIO)
2352 printk(IBM_ERR "enable command accepted for unsupported "
2353 "access mode %d", fan_control_access_mode);
2354
2355 return 1;
2356}
2357
2358static int fan_write_cmd_disable(const char *cmd, int *rc)
2359{
2360 if (strlencmp(cmd, "disable") != 0)
2361 return 0;
2362
2363 if ((*rc = fan_set_disable()) == -ENXIO)
2364 printk(IBM_ERR "disable command accepted for unsupported "
2365 "access mode %d", fan_control_access_mode);
2366
2367 return 1;
2368}
2369
2370static int fan_write_cmd_speed(const char *cmd, int *rc)
2371{
2372 int speed;
2373
2374 /* TODO:
2375 * Support speed <low> <medium> <high> ? */
2376
2377 if (sscanf(cmd, "speed %d", &speed) != 1)
2378 return 0;
2379
2380 if ((*rc = fan_set_speed(speed)) == -ENXIO)
2381 printk(IBM_ERR "speed command accepted for unsupported "
2382 "access mode %d", fan_control_access_mode);
2383
2384 return 1;
2385}
2386
2387static int fan_write_cmd_watchdog(const char *cmd, int *rc)
2388{
2389 int interval;
2390
2391 if (sscanf(cmd, "watchdog %d", &interval) != 1)
2392 return 0;
2393
2394 if (interval < 0 || interval > 120)
2395 *rc = -EINVAL;
2396 else
2397 fan_watchdog_maxinterval = interval;
2398
2399 return 1;
2400}
2401
2402static int fan_write(char *buf)
2403{
2404 char *cmd;
2405 int rc = 0;
2406
2407 while (!rc && (cmd = next_cmd(&buf))) {
2408 if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) &&
2409 fan_write_cmd_level(cmd, &rc)) &&
2410 !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) &&
2411 (fan_write_cmd_enable(cmd, &rc) ||
2412 fan_write_cmd_disable(cmd, &rc) ||
2413 fan_write_cmd_watchdog(cmd, &rc))) &&
2414 !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) &&
2415 fan_write_cmd_speed(cmd, &rc))
2416 )
2417 rc = -EINVAL;
2418 else if (!rc)
2419 fan_watchdog_reset();
2420 }
2421
2422 return rc;
2423}
2424
2425/****************************************************************************
2426 ****************************************************************************
2427 *
2428 * Infrastructure
2429 *
2430 ****************************************************************************
2431 ****************************************************************************/
2432
2433/* /proc support */
2434static struct proc_dir_entry *proc_dir = NULL;
2435
2436/* Subdriver registry */
2437static struct ibm_struct ibms[] = {
2438 {
2439 .name = "driver",
2440 .init = ibm_acpi_driver_init,
2441 .read = ibm_acpi_driver_read,
2442 },
2443 {
2444 .name = "hotkey",
2445 .hid = IBM_HKEY_HID,
2446 .init = hotkey_init,
2447 .read = hotkey_read,
2448 .write = hotkey_write,
2449 .exit = hotkey_exit,
2450 .notify = hotkey_notify,
2451 .handle = &hkey_handle,
2452 .type = ACPI_DEVICE_NOTIFY,
2453 },
2454 {
2455 .name = "bluetooth",
2456 .init = bluetooth_init,
2457 .read = bluetooth_read,
2458 .write = bluetooth_write,
2459 },
2460 {
2461 .name = "wan",
2462 .init = wan_init,
2463 .read = wan_read,
2464 .write = wan_write,
2465 .experimental = 1,
2466 },
2467 {
2468 .name = "video",
2469 .init = video_init,
2470 .read = video_read,
2471 .write = video_write,
2472 .exit = video_exit,
2473 },
2474 {
2475 .name = "light",
2476 .init = light_init,
2477 .read = light_read,
2478 .write = light_write,
2479 },
2480#ifdef CONFIG_ACPI_IBM_DOCK
2481 {
2482 .name = "dock",
2483 .read = dock_read,
2484 .write = dock_write,
2485 .notify = dock_notify,
2486 .handle = &dock_handle,
2487 .type = ACPI_SYSTEM_NOTIFY,
2488 },
2489 {
2490 .name = "dock",
2491 .hid = IBM_PCI_HID,
2492 .notify = dock_notify,
2493 .handle = &pci_handle,
2494 .type = ACPI_SYSTEM_NOTIFY,
2495 },
2496#endif
2497#ifdef CONFIG_ACPI_IBM_BAY
2498 {
2499 .name = "bay",
2500 .init = bay_init,
2501 .read = bay_read,
2502 .write = bay_write,
2503 .notify = bay_notify,
2504 .handle = &bay_handle,
2505 .type = ACPI_SYSTEM_NOTIFY,
2506 },
2507#endif /* CONFIG_ACPI_IBM_BAY */
2508 {
2509 .name = "cmos",
2510 .read = cmos_read,
2511 .write = cmos_write,
2512 },
2513 {
2514 .name = "led",
2515 .init = led_init,
2516 .read = led_read,
2517 .write = led_write,
2518 },
2519 {
2520 .name = "beep",
2521 .read = beep_read,
2522 .write = beep_write,
2523 },
2524 {
2525 .name = "thermal",
2526 .init = thermal_init,
2527 .read = thermal_read,
2528 },
2529 {
2530 .name = "ecdump",
2531 .read = ecdump_read,
2532 .write = ecdump_write,
2533 .experimental = 1,
2534 },
2535 {
2536 .name = "brightness",
2537 .read = brightness_read,
2538 .write = brightness_write,
2539 .init = brightness_init,
2540 .exit = brightness_exit,
2541 },
2542 {
2543 .name = "volume",
2544 .read = volume_read,
2545 .write = volume_write,
2546 },
2547 {
2548 .name = "fan",
2549 .read = fan_read,
2550 .write = fan_write,
2551 .init = fan_init,
2552 .exit = fan_exit,
2553 .experimental = 1,
2554 },
2555};
2556
2557/*
2558 * Module and infrastructure proble, init and exit handling
2559 */
2560
2561static int __init ibm_init(struct ibm_struct *ibm)
2562{
2563 int ret;
2564 struct proc_dir_entry *entry;
2565
2566 if (ibm->experimental && !experimental)
2567 return 0;
2568
2569 if (ibm->hid) {
2570 ret = register_ibmacpi_subdriver(ibm);
2571 if (ret < 0)
2572 return ret;
2573 ibm->driver_registered = 1;
2574 }
2575
2576 if (ibm->init) {
2577 ret = ibm->init();
2578 if (ret != 0)
2579 return ret;
2580 ibm->init_called = 1;
2581 }
2582
2583 if (ibm->read) {
2584 entry = create_proc_entry(ibm->name,
2585 S_IFREG | S_IRUGO | S_IWUSR,
2586 proc_dir);
2587 if (!entry) {
2588 printk(IBM_ERR "unable to create proc entry %s\n",
2589 ibm->name);
2590 return -ENODEV;
2591 }
2592 entry->owner = THIS_MODULE;
2593 entry->data = ibm;
2594 entry->read_proc = &dispatch_read;
2595 if (ibm->write)
2596 entry->write_proc = &dispatch_write;
2597 ibm->proc_created = 1;
2598 }
2599
2600 if (ibm->notify) {
2601 ret = setup_notify(ibm);
2602 if (ret == -ENODEV) {
2603 printk(IBM_NOTICE "disabling subdriver %s\n",
2604 ibm->name);
2605 ibm_exit(ibm);
2606 return 0;
2607 }
2608 if (ret < 0)
2609 return ret;
2610 }
2611
2612 return 0;
2613}
2614
2615static void ibm_exit(struct ibm_struct *ibm)
2616{
2617 if (ibm->notify_installed)
2618 acpi_remove_notify_handler(*ibm->handle, ibm->type,
2619 dispatch_notify);
2620
2621 if (ibm->proc_created)
2622 remove_proc_entry(ibm->name, proc_dir);
2623
2624 if (ibm->init_called && ibm->exit)
2625 ibm->exit();
2626
2627 if (ibm->driver_registered) {
2628 acpi_bus_unregister_driver(ibm->driver);
2629 kfree(ibm->driver);
2630 }
2631}
2632
2633/* Probing */
2634
2635static char *ibm_thinkpad_ec_found = NULL;
2636
2637static char* __init check_dmi_for_ec(void)
2638{
2639 struct dmi_device *dev = NULL;
2640 char ec_fw_string[18];
2641
2642 /*
2643 * ThinkPad T23 or newer, A31 or newer, R50e or newer,
2644 * X32 or newer, all Z series; Some models must have an
2645 * up-to-date BIOS or they will not be detected.
2646 *
2647 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
2648 */
2649 while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
2650 if (sscanf(dev->name,
2651 "IBM ThinkPad Embedded Controller -[%17c",
2652 ec_fw_string) == 1) {
2653 ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
2654 ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
2655 return kstrdup(ec_fw_string, GFP_KERNEL);
2656 }
2657 }
2658 return NULL;
2659}
2660
2661/* Module init, exit, parameters */
2662
2663static int __init set_ibm_param(const char *val, struct kernel_param *kp)
2664{
2665 unsigned int i;
2666
2667 for (i = 0; i < ARRAY_SIZE(ibms); i++)
2668 if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) {
2669 if (strlen(val) > sizeof(ibms[i].param) - 2)
2670 return -ENOSPC;
2671 strcpy(ibms[i].param, val);
2672 strcat(ibms[i].param, ",");
2673 return 0;
2674 }
2675
2676 return -EINVAL;
2677}
2678
2679static int experimental;
2680module_param(experimental, int, 0);
2681
2682#define IBM_PARAM(feature) \
2683 module_param_call(feature, set_ibm_param, NULL, NULL, 0)
2684
2685IBM_PARAM(hotkey);
2686IBM_PARAM(bluetooth);
2687IBM_PARAM(video);
2688IBM_PARAM(light);
2689#ifdef CONFIG_ACPI_IBM_DOCK
2690IBM_PARAM(dock);
2691#endif
2692#ifdef CONFIG_ACPI_IBM_BAY
2693IBM_PARAM(bay);
2694#endif /* CONFIG_ACPI_IBM_BAY */
2695IBM_PARAM(cmos);
2696IBM_PARAM(led);
2697IBM_PARAM(beep);
2698IBM_PARAM(ecdump);
2699IBM_PARAM(brightness);
2700IBM_PARAM(volume);
2701IBM_PARAM(fan);
2702
2703static int __init acpi_ibm_init(void)
2704{
2705 int ret, i;
2706
2707 if (acpi_disabled)
2708 return -ENODEV;
2709
2710 /* ec is required because many other handles are relative to it */
2711 IBM_HANDLE_INIT(ec);
2712 if (!ec_handle) {
2713 printk(IBM_ERR "ec object not found\n");
2714 return -ENODEV;
2715 }
2716
2717 /* Models with newer firmware report the EC in DMI */
2718 ibm_thinkpad_ec_found = check_dmi_for_ec();
2719
2720 /* these handles are not required */
2721 IBM_HANDLE_INIT(vid);
2722 IBM_HANDLE_INIT(vid2);
2723 IBM_HANDLE_INIT(ledb);
2724 IBM_HANDLE_INIT(led);
2725 IBM_HANDLE_INIT(hkey);
2726 IBM_HANDLE_INIT(lght);
2727 IBM_HANDLE_INIT(cmos);
2728#ifdef CONFIG_ACPI_IBM_DOCK
2729 IBM_HANDLE_INIT(dock);
2730#endif
2731 IBM_HANDLE_INIT(pci);
2732#ifdef CONFIG_ACPI_IBM_BAY
2733 IBM_HANDLE_INIT(bay);
2734 if (bay_handle)
2735 IBM_HANDLE_INIT(bay_ej);
2736 IBM_HANDLE_INIT(bay2);
2737 if (bay2_handle)
2738 IBM_HANDLE_INIT(bay2_ej);
2739#endif /* CONFIG_ACPI_IBM_BAY */
2740 IBM_HANDLE_INIT(beep);
2741 IBM_HANDLE_INIT(ecrd);
2742 IBM_HANDLE_INIT(ecwr);
2743 IBM_HANDLE_INIT(fans);
2744 IBM_HANDLE_INIT(gfan);
2745 IBM_HANDLE_INIT(sfan);
2746
2747 proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir);
2748 if (!proc_dir) {
2749 printk(IBM_ERR "unable to create proc dir %s", IBM_DIR);
2750 acpi_ibm_exit();
2751 return -ENODEV;
2752 }
2753 proc_dir->owner = THIS_MODULE;
2754
2755 for (i = 0; i < ARRAY_SIZE(ibms); i++) {
2756 ret = ibm_init(&ibms[i]);
2757 if (ret >= 0 && *ibms[i].param)
2758 ret = ibms[i].write(ibms[i].param);
2759 if (ret < 0) {
2760 acpi_ibm_exit();
2761 return ret;
2762 }
2763 }
2764
2765 return 0;
2766}
2767
2768static void acpi_ibm_exit(void)
2769{
2770 int i;
2771
2772 for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--)
2773 ibm_exit(&ibms[i]);
2774
2775 if (proc_dir)
2776 remove_proc_entry(IBM_DIR, acpi_root_dir);
2777
2778 if (ibm_thinkpad_ec_found)
2779 kfree(ibm_thinkpad_ec_found);
2780}
2781
2782module_init(acpi_ibm_init);
2783module_exit(acpi_ibm_exit);
diff --git a/drivers/misc/ibm_acpi.h b/drivers/misc/ibm_acpi.h
new file mode 100644
index 000000000000..7ebaaa40e183
--- /dev/null
+++ b/drivers/misc/ibm_acpi.h
@@ -0,0 +1,437 @@
1/*
2 * ibm_acpi.h - IBM ThinkPad ACPI Extras
3 *
4 *
5 * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
6 * Copyright (C) 2006-2007 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301, USA.
22 */
23
24#ifndef __IBM_ACPI_H__
25#define __IBM_ACPI_H__
26
27#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/types.h>
31#include <linux/string.h>
32
33#include <linux/proc_fs.h>
34#include <linux/backlight.h>
35#include <linux/fb.h>
36#include <asm/uaccess.h>
37
38#include <linux/dmi.h>
39#include <linux/jiffies.h>
40#include <linux/workqueue.h>
41
42#include <acpi/acpi_drivers.h>
43#include <acpi/acnamesp.h>
44
45
46/****************************************************************************
47 * Main driver
48 */
49
50#define IBM_NAME "ibm"
51#define IBM_DESC "IBM ThinkPad ACPI Extras"
52#define IBM_FILE "ibm_acpi"
53#define IBM_URL "http://ibm-acpi.sf.net/"
54
55#define IBM_DIR IBM_NAME
56
57#define IBM_LOG IBM_FILE ": "
58#define IBM_ERR KERN_ERR IBM_LOG
59#define IBM_NOTICE KERN_NOTICE IBM_LOG
60#define IBM_INFO KERN_INFO IBM_LOG
61#define IBM_DEBUG KERN_DEBUG IBM_LOG
62
63#define IBM_MAX_ACPI_ARGS 3
64
65/* ThinkPad CMOS commands */
66#define TP_CMOS_VOLUME_DOWN 0
67#define TP_CMOS_VOLUME_UP 1
68#define TP_CMOS_VOLUME_MUTE 2
69#define TP_CMOS_BRIGHTNESS_UP 4
70#define TP_CMOS_BRIGHTNESS_DOWN 5
71
72#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
73#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
74#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
75
76/* ACPI HIDs */
77#define IBM_HKEY_HID "IBM0068"
78#define IBM_PCI_HID "PNP0A03"
79
80/* ACPI helpers */
81static int acpi_evalf(acpi_handle handle,
82 void *res, char *method, char *fmt, ...);
83static int acpi_ec_read(int i, u8 * p);
84static int acpi_ec_write(int i, u8 v);
85static int _sta(acpi_handle handle);
86
87/* ACPI handles */
88static acpi_handle root_handle; /* root namespace */
89static acpi_handle ec_handle; /* EC */
90static acpi_handle ecrd_handle, ecwr_handle; /* 570 EC access */
91static acpi_handle cmos_handle, hkey_handle; /* basic thinkpad handles */
92
93static void ibm_handle_init(char *name,
94 acpi_handle * handle, acpi_handle parent,
95 char **paths, int num_paths, char **path);
96#define IBM_HANDLE_INIT(object) \
97 ibm_handle_init(#object, &object##_handle, *object##_parent, \
98 object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
99
100/* procfs support */
101static struct proc_dir_entry *proc_dir;
102static int ibm_acpi_driver_init(void);
103static int ibm_acpi_driver_read(char *p);
104
105/* procfs helpers */
106static int dispatch_read(char *page, char **start, off_t off, int count,
107 int *eof, void *data);
108static int dispatch_write(struct file *file, const char __user * userbuf,
109 unsigned long count, void *data);
110static char *next_cmd(char **cmds);
111
112/* Module */
113static int experimental;
114static char *ibm_thinkpad_ec_found;
115
116static char* check_dmi_for_ec(void);
117static int acpi_ibm_init(void);
118static void acpi_ibm_exit(void);
119
120
121/****************************************************************************
122 * Subdrivers
123 */
124
125struct ibm_struct {
126 char *name;
127 char param[32];
128
129 char *hid;
130 struct acpi_driver *driver;
131
132 int (*init) (void);
133 int (*read) (char *);
134 int (*write) (char *);
135 void (*exit) (void);
136
137 void (*notify) (struct ibm_struct *, u32);
138 acpi_handle *handle;
139 int type;
140 struct acpi_device *device;
141
142 int driver_registered;
143 int proc_created;
144 int init_called;
145 int notify_installed;
146
147 int experimental;
148};
149
150static struct ibm_struct ibms[];
151static int set_ibm_param(const char *val, struct kernel_param *kp);
152static int ibm_init(struct ibm_struct *ibm);
153static void ibm_exit(struct ibm_struct *ibm);
154
155/* ACPI devices */
156static void dispatch_notify(acpi_handle handle, u32 event, void *data);
157static int setup_notify(struct ibm_struct *ibm);
158static int ibm_device_add(struct acpi_device *device);
159static int register_ibmacpi_subdriver(struct ibm_struct *ibm);
160
161
162/*
163 * Bay subdriver
164 */
165
166#ifdef CONFIG_ACPI_IBM_BAY
167static int bay_status_supported, bay_eject_supported;
168static int bay_status2_supported, bay_eject2_supported;
169
170static acpi_handle bay_handle, bay_ej_handle;
171static acpi_handle bay2_handle, bay2_ej_handle;
172
173static int bay_init(void);
174static void bay_notify(struct ibm_struct *ibm, u32 event);
175static int bay_read(char *p);
176static int bay_write(char *buf);
177#endif /* CONFIG_ACPI_IBM_BAY */
178
179
180/*
181 * Beep subdriver
182 */
183
184static acpi_handle beep_handle;
185
186static int beep_read(char *p);
187static int beep_write(char *buf);
188
189
190/*
191 * Bluetooth subdriver
192 */
193
194static int bluetooth_supported;
195
196static int bluetooth_init(void);
197static int bluetooth_status(void);
198static int bluetooth_read(char *p);
199static int bluetooth_write(char *buf);
200
201
202/*
203 * Brightness (backlight) subdriver
204 */
205
206static struct backlight_device *ibm_backlight_device;
207static int brightness_offset = 0x31;
208
209static int brightness_init(void);
210static void brightness_exit(void);
211static int brightness_get(struct backlight_device *bd);
212static int brightness_set(int value);
213static int brightness_update_status(struct backlight_device *bd);
214static int brightness_read(char *p);
215static int brightness_write(char *buf);
216
217
218/*
219 * CMOS subdriver
220 */
221
222static int cmos_eval(int cmos_cmd);
223static int cmos_read(char *p);
224static int cmos_write(char *buf);
225
226
227/*
228 * Dock subdriver
229 */
230
231static acpi_handle pci_handle;
232#ifdef CONFIG_ACPI_IBM_DOCK
233static acpi_handle dock_handle;
234
235static void dock_notify(struct ibm_struct *ibm, u32 event);
236static int dock_read(char *p);
237static int dock_write(char *buf);
238#endif /* CONFIG_ACPI_IBM_DOCK */
239
240
241/*
242 * EC dump subdriver
243 */
244
245static int ecdump_read(char *p) ;
246static int ecdump_write(char *buf);
247
248
249/*
250 * Fan subdriver
251 */
252
253enum { /* Fan control constants */
254 fan_status_offset = 0x2f, /* EC register 0x2f */
255 fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM)
256 * 0x84 must be read before 0x85 */
257
258 IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer
259 * disengaged */
260 IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan
261 * control */
262};
263
264enum fan_status_access_mode {
265 IBMACPI_FAN_NONE = 0, /* No fan status or control */
266 IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */
267 IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
268};
269
270enum fan_control_access_mode {
271 IBMACPI_FAN_WR_NONE = 0, /* No fan control */
272 IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */
273 IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */
274 IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */
275};
276
277enum fan_control_commands {
278 IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */
279 IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */
280 IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd,
281 * and also watchdog cmd */
282};
283
284static enum fan_status_access_mode fan_status_access_mode;
285static enum fan_control_access_mode fan_control_access_mode;
286static enum fan_control_commands fan_control_commands;
287static int fan_control_status_known;
288static u8 fan_control_initial_status;
289static int fan_watchdog_maxinterval;
290
291static acpi_handle fans_handle, gfan_handle, sfan_handle;
292
293static int fan_init(void);
294static void fan_exit(void);
295static int fan_get_status(u8 *status);
296static int fan_get_speed(unsigned int *speed);
297static void fan_watchdog_fire(struct work_struct *ignored);
298static void fan_watchdog_reset(void);
299static int fan_set_level(int level);
300static int fan_set_enable(void);
301static int fan_set_disable(void);
302static int fan_set_speed(int speed);
303static int fan_read(char *p);
304static int fan_write(char *buf);
305static int fan_write_cmd_level(const char *cmd, int *rc);
306static int fan_write_cmd_enable(const char *cmd, int *rc);
307static int fan_write_cmd_disable(const char *cmd, int *rc);
308static int fan_write_cmd_speed(const char *cmd, int *rc);
309static int fan_write_cmd_watchdog(const char *cmd, int *rc);
310
311
312/*
313 * Hotkey subdriver
314 */
315
316static int hotkey_supported;
317static int hotkey_mask_supported;
318static int hotkey_orig_status;
319static int hotkey_orig_mask;
320
321static int hotkey_init(void);
322static void hotkey_exit(void);
323static int hotkey_get(int *status, int *mask);
324static int hotkey_set(int status, int mask);
325static void hotkey_notify(struct ibm_struct *ibm, u32 event);
326static int hotkey_read(char *p);
327static int hotkey_write(char *buf);
328
329
330/*
331 * LED subdriver
332 */
333
334enum led_access_mode {
335 IBMACPI_LED_NONE = 0,
336 IBMACPI_LED_570, /* 570 */
337 IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
338 IBMACPI_LED_NEW, /* all others */
339};
340
341enum { /* For IBMACPI_LED_OLD */
342 IBMACPI_LED_EC_HLCL = 0x0c, /* EC reg to get led to power on */
343 IBMACPI_LED_EC_HLBL = 0x0d, /* EC reg to blink a lit led */
344 IBMACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */
345};
346
347static enum led_access_mode led_supported;
348static acpi_handle led_handle;
349
350static int led_init(void);
351static int led_read(char *p);
352static int led_write(char *buf);
353
354/*
355 * Light (thinklight) subdriver
356 */
357
358static int light_supported;
359static int light_status_supported;
360static acpi_handle lght_handle, ledb_handle;
361
362static int light_init(void);
363static int light_read(char *p);
364static int light_write(char *buf);
365
366
367/*
368 * Thermal subdriver
369 */
370
371enum thermal_access_mode {
372 IBMACPI_THERMAL_NONE = 0, /* No thermal support */
373 IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */
374 IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */
375 IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */
376 IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */
377};
378
379#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */
380struct ibm_thermal_sensors_struct {
381 s32 temp[IBMACPI_MAX_THERMAL_SENSORS];
382};
383
384static int thermal_init(void);
385static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s);
386static int thermal_read(char *p);
387
388
389/*
390 * Video subdriver
391 */
392
393enum video_access_mode {
394 IBMACPI_VIDEO_NONE = 0,
395 IBMACPI_VIDEO_570, /* 570 */
396 IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */
397 IBMACPI_VIDEO_NEW, /* all others */
398};
399
400static enum video_access_mode video_supported;
401static int video_orig_autosw;
402static acpi_handle vid_handle, vid2_handle;
403
404static int video_init(void);
405static void video_exit(void);
406static int video_status(void);
407static int video_autosw(void);
408static int video_switch(void);
409static int video_switch2(int status);
410static int video_expand(void);
411static int video_read(char *p);
412static int video_write(char *buf);
413
414
415/*
416 * Volume subdriver
417 */
418
419static int volume_offset = 0x30;
420
421static int volume_read(char *p);
422static int volume_write(char *buf);
423
424
425/*
426 * Wan subdriver
427 */
428
429static int wan_supported;
430
431static int wan_init(void);
432static int wan_status(void);
433static int wan_read(char *p);
434static int wan_write(char *buf);
435
436
437#endif /* __IBM_ACPI_H */