aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorLen Brown <len.brown@intel.com>2007-04-28 23:11:19 -0400
committerLen Brown <len.brown@intel.com>2007-04-28 23:11:19 -0400
commitf188291aec9b17ef7cec01db66b9cdb6fae26372 (patch)
tree9ed965938f635be09b0d124e91fd4aec07701714 /drivers/misc
parentcfaae3ee4a0d00c6b22780057e958d625499e90c (diff)
parent836a53f42f3b5d5cb3a0751587ea33801e4b120d (diff)
Pull thinkpad into release branch
Conflicts: drivers/misc/Kconfig Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig51
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/thinkpad_acpi.c4312
-rw-r--r--drivers/misc/thinkpad_acpi.h572
4 files changed, 4936 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 75dbc584b574..a3c525b2616a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -127,4 +127,55 @@ config SONY_LAPTOP_OLD
127 ---help--- 127 ---help---
128 Build the sonypi driver compatibility code into the sony-laptop driver. 128 Build the sonypi driver compatibility code into the sony-laptop driver.
129 129
130config THINKPAD_ACPI
131 tristate "ThinkPad ACPI Laptop Extras"
132 depends on X86 && ACPI
133 select BACKLIGHT_CLASS_DEVICE
134 select HWMON
135 ---help---
136 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
137 support for Fn-Fx key combinations, Bluetooth control, video
138 output switching, ThinkLight control, UltraBay eject and more.
139 For more information about this driver see
140 <file:Documentation/thinkpad-acpi.txt> and <http://ibm-acpi.sf.net/> .
141
142 This driver was formely known as ibm-acpi.
143
144 If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
145
146config THINKPAD_ACPI_DEBUG
147 bool "Verbose debug mode"
148 depends on THINKPAD_ACPI
149 default n
150 ---help---
151 Enables extra debugging information, at the expense of a slightly
152 increase in driver size.
153
154 If you are not sure, say N here.
155
156config THINKPAD_ACPI_DOCK
157 bool "Legacy Docking Station Support"
158 depends on THINKPAD_ACPI
159 depends on ACPI_DOCK=n
160 default n
161 ---help---
162 Allows the thinkpad_acpi driver to handle docking station events.
163 This support was made obsolete by the generic ACPI docking station
164 support (CONFIG_ACPI_DOCK). It will allow locking and removing the
165 laptop from the docking station, but will not properly connect PCI
166 devices.
167
168 If you are not sure, say N here.
169
170config THINKPAD_ACPI_BAY
171 bool "Legacy Removable Bay Support"
172 depends on THINKPAD_ACPI
173 default y
174 ---help---
175 Allows the thinkpad_acpi driver to handle removable bays. It will
176 eletrically disable the device in the bay, and also generate
177 notifications when the bay lever is ejected or inserted.
178
179 If you are not sure, say Y here.
180
130endmenu 181endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7793ccd79049..e32516459138 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_THINKPAD_ACPI) += thinkpad_acpi.o
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
new file mode 100644
index 000000000000..6c36a55cb3d1
--- /dev/null
+++ b/drivers/misc/thinkpad_acpi.c
@@ -0,0 +1,4312 @@
1/*
2 * thinkpad_acpi.c - 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.14"
25#define TPACPI_SYSFS_VERSION 0x000100
26
27/*
28 * Changelog:
29 * 2007-03-27 0.14 renamed to thinkpad_acpi and moved to
30 * drivers/misc.
31 *
32 * 2006-11-22 0.13 new maintainer
33 * changelog now lives in git commit history, and will
34 * not be updated further in-file.
35 *
36 * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels
37 * 2005-03-17 0.11 support for 600e, 770x
38 * thanks to Jamie Lentin <lentinj@dial.pipex.com>
39 * support for 770e, G41
40 * G40 and G41 don't have a thinklight
41 * temperatures no longer experimental
42 * experimental brightness control
43 * experimental volume control
44 * experimental fan enable/disable
45 * 2005-01-16 0.10 fix module loading on R30, R31
46 * 2005-01-16 0.9 support for 570, R30, R31
47 * ultrabay support on A22p, A3x
48 * limit arg for cmos, led, beep, drop experimental status
49 * more capable led control on A21e, A22p, T20-22, X20
50 * experimental temperatures and fan speed
51 * experimental embedded controller register dump
52 * mark more functions as __init, drop incorrect __exit
53 * use MODULE_VERSION
54 * thanks to Henrik Brix Andersen <brix@gentoo.org>
55 * fix parameter passing on module loading
56 * thanks to Rusty Russell <rusty@rustcorp.com.au>
57 * thanks to Jim Radford <radford@blackbean.org>
58 * 2004-11-08 0.8 fix init error case, don't return from a macro
59 * thanks to Chris Wright <chrisw@osdl.org>
60 * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20
61 * fix led control on A21e
62 * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device
63 * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20
64 * proc file format changed
65 * video_switch command
66 * experimental cmos control
67 * experimental led control
68 * experimental acpi sounds
69 * 2004-09-16 0.4 support for module parameters
70 * hotkey mask can be prefixed by 0x
71 * video output switching
72 * video expansion control
73 * ultrabay eject support
74 * removed lcd brightness/on/off control, didn't work
75 * 2004-08-17 0.3 support for R40
76 * lcd off, brightness control
77 * thinklight on/off
78 * 2004-08-14 0.2 support for T series, X20
79 * bluetooth enable/disable
80 * hotkey events disabled by default
81 * removed fan control, currently useless
82 * 2004-08-09 0.1 initial release, support for X series
83 */
84
85#include "thinkpad_acpi.h"
86
87MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
88MODULE_DESCRIPTION(IBM_DESC);
89MODULE_VERSION(IBM_VERSION);
90MODULE_LICENSE("GPL");
91
92/* Please remove this in year 2009 */
93MODULE_ALIAS("ibm_acpi");
94
95#define __unused __attribute__ ((unused))
96
97/****************************************************************************
98 ****************************************************************************
99 *
100 * ACPI Helpers and device model
101 *
102 ****************************************************************************
103 ****************************************************************************/
104
105/*************************************************************************
106 * ACPI basic handles
107 */
108
109static acpi_handle root_handle = NULL;
110
111#define IBM_HANDLE(object, parent, paths...) \
112 static acpi_handle object##_handle; \
113 static acpi_handle *object##_parent = &parent##_handle; \
114 static char *object##_path; \
115 static char *object##_paths[] = { paths }
116
117IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
118 "\\_SB.PCI.ISA.EC", /* 570 */
119 "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
120 "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
121 "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
122 "\\_SB.PCI0.ICH3.EC0", /* R31 */
123 "\\_SB.PCI0.LPC.EC", /* all others */
124 );
125
126IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */
127IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */
128
129
130/*************************************************************************
131 * Misc ACPI handles
132 */
133
134IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */
135 "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */
136 "\\CMS", /* R40, R40e */
137 ); /* all others */
138
139IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
140 "^HKEY", /* R30, R31 */
141 "HKEY", /* all others */
142 ); /* 570 */
143
144
145/*************************************************************************
146 * ACPI helpers
147 */
148
149static int acpi_evalf(acpi_handle handle,
150 void *res, char *method, char *fmt, ...)
151{
152 char *fmt0 = fmt;
153 struct acpi_object_list params;
154 union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
155 struct acpi_buffer result, *resultp;
156 union acpi_object out_obj;
157 acpi_status status;
158 va_list ap;
159 char res_type;
160 int success;
161 int quiet;
162
163 if (!*fmt) {
164 printk(IBM_ERR "acpi_evalf() called with empty format\n");
165 return 0;
166 }
167
168 if (*fmt == 'q') {
169 quiet = 1;
170 fmt++;
171 } else
172 quiet = 0;
173
174 res_type = *(fmt++);
175
176 params.count = 0;
177 params.pointer = &in_objs[0];
178
179 va_start(ap, fmt);
180 while (*fmt) {
181 char c = *(fmt++);
182 switch (c) {
183 case 'd': /* int */
184 in_objs[params.count].integer.value = va_arg(ap, int);
185 in_objs[params.count++].type = ACPI_TYPE_INTEGER;
186 break;
187 /* add more types as needed */
188 default:
189 printk(IBM_ERR "acpi_evalf() called "
190 "with invalid format character '%c'\n", c);
191 return 0;
192 }
193 }
194 va_end(ap);
195
196 if (res_type != 'v') {
197 result.length = sizeof(out_obj);
198 result.pointer = &out_obj;
199 resultp = &result;
200 } else
201 resultp = NULL;
202
203 status = acpi_evaluate_object(handle, method, &params, resultp);
204
205 switch (res_type) {
206 case 'd': /* int */
207 if (res)
208 *(int *)res = out_obj.integer.value;
209 success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
210 break;
211 case 'v': /* void */
212 success = status == AE_OK;
213 break;
214 /* add more types as needed */
215 default:
216 printk(IBM_ERR "acpi_evalf() called "
217 "with invalid format character '%c'\n", res_type);
218 return 0;
219 }
220
221 if (!success && !quiet)
222 printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
223 method, fmt0, status);
224
225 return success;
226}
227
228static void __unused acpi_print_int(acpi_handle handle, char *method)
229{
230 int i;
231
232 if (acpi_evalf(handle, &i, method, "d"))
233 printk(IBM_INFO "%s = 0x%x\n", method, i);
234 else
235 printk(IBM_ERR "error calling %s\n", method);
236}
237
238static int acpi_ec_read(int i, u8 * p)
239{
240 int v;
241
242 if (ecrd_handle) {
243 if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
244 return 0;
245 *p = v;
246 } else {
247 if (ec_read(i, p) < 0)
248 return 0;
249 }
250
251 return 1;
252}
253
254static int acpi_ec_write(int i, u8 v)
255{
256 if (ecwr_handle) {
257 if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
258 return 0;
259 } else {
260 if (ec_write(i, v) < 0)
261 return 0;
262 }
263
264 return 1;
265}
266
267static int _sta(acpi_handle handle)
268{
269 int status;
270
271 if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
272 status = 0;
273
274 return status;
275}
276
277static int issue_thinkpad_cmos_command(int cmos_cmd)
278{
279 if (!cmos_handle)
280 return -ENXIO;
281
282 if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
283 return -EIO;
284
285 return 0;
286}
287
288/*************************************************************************
289 * ACPI device model
290 */
291
292static void drv_acpi_handle_init(char *name,
293 acpi_handle *handle, acpi_handle parent,
294 char **paths, int num_paths, char **path)
295{
296 int i;
297 acpi_status status;
298
299 vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n",
300 name);
301
302 for (i = 0; i < num_paths; i++) {
303 status = acpi_get_handle(parent, paths[i], handle);
304 if (ACPI_SUCCESS(status)) {
305 *path = paths[i];
306 dbg_printk(TPACPI_DBG_INIT,
307 "Found ACPI handle %s for %s\n",
308 *path, name);
309 return;
310 }
311 }
312
313 vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n",
314 name);
315 *handle = NULL;
316}
317
318static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
319{
320 struct ibm_struct *ibm = data;
321
322 if (!ibm || !ibm->acpi || !ibm->acpi->notify)
323 return;
324
325 ibm->acpi->notify(ibm, event);
326}
327
328static int __init setup_acpi_notify(struct ibm_struct *ibm)
329{
330 acpi_status status;
331 int rc;
332
333 BUG_ON(!ibm->acpi);
334
335 if (!*ibm->acpi->handle)
336 return 0;
337
338 vdbg_printk(TPACPI_DBG_INIT,
339 "setting up ACPI notify for %s\n", ibm->name);
340
341 rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device);
342 if (rc < 0) {
343 printk(IBM_ERR "acpi_bus_get_device(%s) failed: %d\n",
344 ibm->name, rc);
345 return -ENODEV;
346 }
347
348 acpi_driver_data(ibm->acpi->device) = ibm;
349 sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",
350 IBM_ACPI_EVENT_PREFIX,
351 ibm->name);
352
353 status = acpi_install_notify_handler(*ibm->acpi->handle,
354 ibm->acpi->type, dispatch_acpi_notify, ibm);
355 if (ACPI_FAILURE(status)) {
356 if (status == AE_ALREADY_EXISTS) {
357 printk(IBM_NOTICE "another device driver is already handling %s events\n",
358 ibm->name);
359 } else {
360 printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
361 ibm->name, status);
362 }
363 return -ENODEV;
364 }
365 ibm->flags.acpi_notify_installed = 1;
366 return 0;
367}
368
369static int __init tpacpi_device_add(struct acpi_device *device)
370{
371 return 0;
372}
373
374static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
375{
376 int rc;
377
378 dbg_printk(TPACPI_DBG_INIT,
379 "registering %s as an ACPI driver\n", ibm->name);
380
381 BUG_ON(!ibm->acpi);
382
383 ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
384 if (!ibm->acpi->driver) {
385 printk(IBM_ERR "kzalloc(ibm->driver) failed\n");
386 return -ENOMEM;
387 }
388
389 sprintf(ibm->acpi->driver->name, "%s_%s", IBM_NAME, ibm->name);
390 ibm->acpi->driver->ids = ibm->acpi->hid;
391 ibm->acpi->driver->ops.add = &tpacpi_device_add;
392
393 rc = acpi_bus_register_driver(ibm->acpi->driver);
394 if (rc < 0) {
395 printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
396 ibm->acpi->hid, rc);
397 kfree(ibm->acpi->driver);
398 ibm->acpi->driver = NULL;
399 } else if (!rc)
400 ibm->flags.acpi_driver_registered = 1;
401
402 return rc;
403}
404
405
406/****************************************************************************
407 ****************************************************************************
408 *
409 * Procfs Helpers
410 *
411 ****************************************************************************
412 ****************************************************************************/
413
414static int dispatch_procfs_read(char *page, char **start, off_t off,
415 int count, int *eof, void *data)
416{
417 struct ibm_struct *ibm = data;
418 int len;
419
420 if (!ibm || !ibm->read)
421 return -EINVAL;
422
423 len = ibm->read(page);
424 if (len < 0)
425 return len;
426
427 if (len <= off + count)
428 *eof = 1;
429 *start = page + off;
430 len -= off;
431 if (len > count)
432 len = count;
433 if (len < 0)
434 len = 0;
435
436 return len;
437}
438
439static int dispatch_procfs_write(struct file *file,
440 const char __user * userbuf,
441 unsigned long count, void *data)
442{
443 struct ibm_struct *ibm = data;
444 char *kernbuf;
445 int ret;
446
447 if (!ibm || !ibm->write)
448 return -EINVAL;
449
450 kernbuf = kmalloc(count + 2, GFP_KERNEL);
451 if (!kernbuf)
452 return -ENOMEM;
453
454 if (copy_from_user(kernbuf, userbuf, count)) {
455 kfree(kernbuf);
456 return -EFAULT;
457 }
458
459 kernbuf[count] = 0;
460 strcat(kernbuf, ",");
461 ret = ibm->write(kernbuf);
462 if (ret == 0)
463 ret = count;
464
465 kfree(kernbuf);
466
467 return ret;
468}
469
470static char *next_cmd(char **cmds)
471{
472 char *start = *cmds;
473 char *end;
474
475 while ((end = strchr(start, ',')) && end == start)
476 start = end + 1;
477
478 if (!end)
479 return NULL;
480
481 *end = 0;
482 *cmds = end + 1;
483 return start;
484}
485
486
487/****************************************************************************
488 ****************************************************************************
489 *
490 * Device model: hwmon and platform
491 *
492 ****************************************************************************
493 ****************************************************************************/
494
495static struct platform_device *tpacpi_pdev = NULL;
496static struct class_device *tpacpi_hwmon = NULL;
497
498static struct platform_driver tpacpi_pdriver = {
499 .driver = {
500 .name = IBM_DRVR_NAME,
501 .owner = THIS_MODULE,
502 },
503};
504
505
506/*************************************************************************
507 * thinkpad-acpi driver attributes
508 */
509
510/* interface_version --------------------------------------------------- */
511static ssize_t tpacpi_driver_interface_version_show(
512 struct device_driver *drv,
513 char *buf)
514{
515 return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
516}
517
518static DRIVER_ATTR(interface_version, S_IRUGO,
519 tpacpi_driver_interface_version_show, NULL);
520
521/* debug_level --------------------------------------------------------- */
522static ssize_t tpacpi_driver_debug_show(struct device_driver *drv,
523 char *buf)
524{
525 return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
526}
527
528static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
529 const char *buf, size_t count)
530{
531 unsigned long t;
532
533 if (parse_strtoul(buf, 0xffff, &t))
534 return -EINVAL;
535
536 dbg_level = t;
537
538 return count;
539}
540
541static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
542 tpacpi_driver_debug_show, tpacpi_driver_debug_store);
543
544/* version ------------------------------------------------------------- */
545static ssize_t tpacpi_driver_version_show(struct device_driver *drv,
546 char *buf)
547{
548 return snprintf(buf, PAGE_SIZE, "%s v%s\n", IBM_DESC, IBM_VERSION);
549}
550
551static DRIVER_ATTR(version, S_IRUGO,
552 tpacpi_driver_version_show, NULL);
553
554/* --------------------------------------------------------------------- */
555
556static struct driver_attribute* tpacpi_driver_attributes[] = {
557 &driver_attr_debug_level, &driver_attr_version,
558 &driver_attr_interface_version,
559};
560
561static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
562{
563 int i, res;
564
565 i = 0;
566 res = 0;
567 while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) {
568 res = driver_create_file(drv, tpacpi_driver_attributes[i]);
569 i++;
570 }
571
572 return res;
573}
574
575static void tpacpi_remove_driver_attributes(struct device_driver *drv)
576{
577 int i;
578
579 for(i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++)
580 driver_remove_file(drv, tpacpi_driver_attributes[i]);
581}
582
583/*************************************************************************
584 * sysfs support helpers
585 */
586
587struct attribute_set_obj {
588 struct attribute_set s;
589 struct attribute *a;
590} __attribute__((packed));
591
592static struct attribute_set *create_attr_set(unsigned int max_members,
593 const char* name)
594{
595 struct attribute_set_obj *sobj;
596
597 if (max_members == 0)
598 return NULL;
599
600 /* Allocates space for implicit NULL at the end too */
601 sobj = kzalloc(sizeof(struct attribute_set_obj) +
602 max_members * sizeof(struct attribute *),
603 GFP_KERNEL);
604 if (!sobj)
605 return NULL;
606 sobj->s.max_members = max_members;
607 sobj->s.group.attrs = &sobj->a;
608 sobj->s.group.name = name;
609
610 return &sobj->s;
611}
612
613/* not multi-threaded safe, use it in a single thread per set */
614static int add_to_attr_set(struct attribute_set* s, struct attribute *attr)
615{
616 if (!s || !attr)
617 return -EINVAL;
618
619 if (s->members >= s->max_members)
620 return -ENOMEM;
621
622 s->group.attrs[s->members] = attr;
623 s->members++;
624
625 return 0;
626}
627
628static int add_many_to_attr_set(struct attribute_set* s,
629 struct attribute **attr,
630 unsigned int count)
631{
632 int i, res;
633
634 for (i = 0; i < count; i++) {
635 res = add_to_attr_set(s, attr[i]);
636 if (res)
637 return res;
638 }
639
640 return 0;
641}
642
643static void delete_attr_set(struct attribute_set* s, struct kobject *kobj)
644{
645 sysfs_remove_group(kobj, &s->group);
646 destroy_attr_set(s);
647}
648
649static int parse_strtoul(const char *buf,
650 unsigned long max, unsigned long *value)
651{
652 char *endp;
653
654 *value = simple_strtoul(buf, &endp, 0);
655 while (*endp && isspace(*endp))
656 endp++;
657 if (*endp || *value > max)
658 return -EINVAL;
659
660 return 0;
661}
662
663/****************************************************************************
664 ****************************************************************************
665 *
666 * Subdrivers
667 *
668 ****************************************************************************
669 ****************************************************************************/
670
671/*************************************************************************
672 * thinkpad-acpi init subdriver
673 */
674
675static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
676{
677 printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
678 printk(IBM_INFO "%s\n", IBM_URL);
679
680 if (ibm_thinkpad_ec_found)
681 printk(IBM_INFO "ThinkPad EC firmware %s\n",
682 ibm_thinkpad_ec_found);
683
684 return 0;
685}
686
687static int thinkpad_acpi_driver_read(char *p)
688{
689 int len = 0;
690
691 len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
692 len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
693
694 return len;
695}
696
697static struct ibm_struct thinkpad_acpi_driver_data = {
698 .name = "driver",
699 .read = thinkpad_acpi_driver_read,
700};
701
702/*************************************************************************
703 * Hotkey subdriver
704 */
705
706static int hotkey_orig_status;
707static int hotkey_orig_mask;
708
709static struct attribute_set *hotkey_dev_attributes = NULL;
710
711/* sysfs hotkey enable ------------------------------------------------- */
712static ssize_t hotkey_enable_show(struct device *dev,
713 struct device_attribute *attr,
714 char *buf)
715{
716 int res, status, mask;
717
718 res = hotkey_get(&status, &mask);
719 if (res)
720 return res;
721
722 return snprintf(buf, PAGE_SIZE, "%d\n", status);
723}
724
725static ssize_t hotkey_enable_store(struct device *dev,
726 struct device_attribute *attr,
727 const char *buf, size_t count)
728{
729 unsigned long t;
730 int res, status, mask;
731
732 if (parse_strtoul(buf, 1, &t))
733 return -EINVAL;
734
735 res = hotkey_get(&status, &mask);
736 if (!res)
737 res = hotkey_set(t, mask);
738
739 return (res) ? res : count;
740}
741
742static struct device_attribute dev_attr_hotkey_enable =
743 __ATTR(enable, S_IWUSR | S_IRUGO,
744 hotkey_enable_show, hotkey_enable_store);
745
746/* sysfs hotkey mask --------------------------------------------------- */
747static ssize_t hotkey_mask_show(struct device *dev,
748 struct device_attribute *attr,
749 char *buf)
750{
751 int res, status, mask;
752
753 res = hotkey_get(&status, &mask);
754 if (res)
755 return res;
756
757 return snprintf(buf, PAGE_SIZE, "0x%04x\n", mask);
758}
759
760static ssize_t hotkey_mask_store(struct device *dev,
761 struct device_attribute *attr,
762 const char *buf, size_t count)
763{
764 unsigned long t;
765 int res, status, mask;
766
767 if (parse_strtoul(buf, 0xffff, &t))
768 return -EINVAL;
769
770 res = hotkey_get(&status, &mask);
771 if (!res)
772 hotkey_set(status, t);
773
774 return (res) ? res : count;
775}
776
777static struct device_attribute dev_attr_hotkey_mask =
778 __ATTR(mask, S_IWUSR | S_IRUGO,
779 hotkey_mask_show, hotkey_mask_store);
780
781/* sysfs hotkey bios_enabled ------------------------------------------- */
782static ssize_t hotkey_bios_enabled_show(struct device *dev,
783 struct device_attribute *attr,
784 char *buf)
785{
786 return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status);
787}
788
789static struct device_attribute dev_attr_hotkey_bios_enabled =
790 __ATTR(bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL);
791
792/* sysfs hotkey bios_mask ---------------------------------------------- */
793static ssize_t hotkey_bios_mask_show(struct device *dev,
794 struct device_attribute *attr,
795 char *buf)
796{
797 return snprintf(buf, PAGE_SIZE, "0x%04x\n", hotkey_orig_mask);
798}
799
800static struct device_attribute dev_attr_hotkey_bios_mask =
801 __ATTR(bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL);
802
803/* --------------------------------------------------------------------- */
804
805static struct attribute *hotkey_mask_attributes[] = {
806 &dev_attr_hotkey_mask.attr,
807 &dev_attr_hotkey_bios_enabled.attr,
808 &dev_attr_hotkey_bios_mask.attr,
809};
810
811static int __init hotkey_init(struct ibm_init_struct *iibm)
812{
813 int res;
814
815 vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
816
817 IBM_ACPIHANDLE_INIT(hkey);
818 mutex_init(&hotkey_mutex);
819
820 /* hotkey not supported on 570 */
821 tp_features.hotkey = hkey_handle != NULL;
822
823 vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
824 str_supported(tp_features.hotkey));
825
826 if (tp_features.hotkey) {
827 hotkey_dev_attributes = create_attr_set(4,
828 TPACPI_HOTKEY_SYSFS_GROUP);
829 if (!hotkey_dev_attributes)
830 return -ENOMEM;
831 res = add_to_attr_set(hotkey_dev_attributes,
832 &dev_attr_hotkey_enable.attr);
833 if (res)
834 return res;
835
836 /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
837 A30, R30, R31, T20-22, X20-21, X22-24 */
838 tp_features.hotkey_mask =
839 acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
840
841 vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
842 str_supported(tp_features.hotkey_mask));
843
844 res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask);
845 if (!res && tp_features.hotkey_mask) {
846 res = add_many_to_attr_set(hotkey_dev_attributes,
847 hotkey_mask_attributes,
848 ARRAY_SIZE(hotkey_mask_attributes));
849 }
850 if (!res)
851 res = register_attr_set_with_sysfs(
852 hotkey_dev_attributes,
853 &tpacpi_pdev->dev.kobj);
854
855 if (res)
856 return res;
857 }
858
859 return (tp_features.hotkey)? 0 : 1;
860}
861
862static void hotkey_exit(void)
863{
864 int res;
865
866 if (tp_features.hotkey) {
867 dbg_printk(TPACPI_DBG_EXIT, "restoring original hotkey mask\n");
868 res = hotkey_set(hotkey_orig_status, hotkey_orig_mask);
869 if (res)
870 printk(IBM_ERR "failed to restore hotkey to BIOS defaults\n");
871 }
872
873 if (hotkey_dev_attributes) {
874 delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
875 hotkey_dev_attributes = NULL;
876 }
877}
878
879static void hotkey_notify(struct ibm_struct *ibm, u32 event)
880{
881 int hkey;
882
883 if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
884 acpi_bus_generate_event(ibm->acpi->device, event, hkey);
885 else {
886 printk(IBM_ERR "unknown hotkey event %d\n", event);
887 acpi_bus_generate_event(ibm->acpi->device, event, 0);
888 }
889}
890
891/*
892 * Call with hotkey_mutex held
893 */
894static int hotkey_get(int *status, int *mask)
895{
896 if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
897 return -EIO;
898
899 if (tp_features.hotkey_mask)
900 if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
901 return -EIO;
902
903 return 0;
904}
905
906/*
907 * Call with hotkey_mutex held
908 */
909static int hotkey_set(int status, int mask)
910{
911 int i;
912
913 if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
914 return -EIO;
915
916 if (tp_features.hotkey_mask)
917 for (i = 0; i < 32; i++) {
918 int bit = ((1 << i) & mask) != 0;
919 if (!acpi_evalf(hkey_handle,
920 NULL, "MHKM", "vdd", i + 1, bit))
921 return -EIO;
922 }
923
924 return 0;
925}
926
927/* procfs -------------------------------------------------------------- */
928static int hotkey_read(char *p)
929{
930 int res, status, mask;
931 int len = 0;
932
933 if (!tp_features.hotkey) {
934 len += sprintf(p + len, "status:\t\tnot supported\n");
935 return len;
936 }
937
938 res = mutex_lock_interruptible(&hotkey_mutex);
939 if (res < 0)
940 return res;
941 res = hotkey_get(&status, &mask);
942 mutex_unlock(&hotkey_mutex);
943 if (res)
944 return res;
945
946 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
947 if (tp_features.hotkey_mask) {
948 len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
949 len += sprintf(p + len,
950 "commands:\tenable, disable, reset, <mask>\n");
951 } else {
952 len += sprintf(p + len, "mask:\t\tnot supported\n");
953 len += sprintf(p + len, "commands:\tenable, disable, reset\n");
954 }
955
956 return len;
957}
958
959static int hotkey_write(char *buf)
960{
961 int res, status, mask;
962 char *cmd;
963 int do_cmd = 0;
964
965 if (!tp_features.hotkey)
966 return -ENODEV;
967
968 res = mutex_lock_interruptible(&hotkey_mutex);
969 if (res < 0)
970 return res;
971
972 res = hotkey_get(&status, &mask);
973 if (res)
974 goto errexit;
975
976 res = 0;
977 while ((cmd = next_cmd(&buf))) {
978 if (strlencmp(cmd, "enable") == 0) {
979 status = 1;
980 } else if (strlencmp(cmd, "disable") == 0) {
981 status = 0;
982 } else if (strlencmp(cmd, "reset") == 0) {
983 status = hotkey_orig_status;
984 mask = hotkey_orig_mask;
985 } else if (sscanf(cmd, "0x%x", &mask) == 1) {
986 /* mask set */
987 } else if (sscanf(cmd, "%x", &mask) == 1) {
988 /* mask set */
989 } else {
990 res = -EINVAL;
991 goto errexit;
992 }
993 do_cmd = 1;
994 }
995
996 if (do_cmd)
997 res = hotkey_set(status, mask);
998
999errexit:
1000 mutex_unlock(&hotkey_mutex);
1001 return res;
1002}
1003
1004static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {
1005 .hid = IBM_HKEY_HID,
1006 .notify = hotkey_notify,
1007 .handle = &hkey_handle,
1008 .type = ACPI_DEVICE_NOTIFY,
1009};
1010
1011static struct ibm_struct hotkey_driver_data = {
1012 .name = "hotkey",
1013 .read = hotkey_read,
1014 .write = hotkey_write,
1015 .exit = hotkey_exit,
1016 .acpi = &ibm_hotkey_acpidriver,
1017};
1018
1019/*************************************************************************
1020 * Bluetooth subdriver
1021 */
1022
1023/* sysfs bluetooth enable ---------------------------------------------- */
1024static ssize_t bluetooth_enable_show(struct device *dev,
1025 struct device_attribute *attr,
1026 char *buf)
1027{
1028 int status;
1029
1030 status = bluetooth_get_radiosw();
1031 if (status < 0)
1032 return status;
1033
1034 return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
1035}
1036
1037static ssize_t bluetooth_enable_store(struct device *dev,
1038 struct device_attribute *attr,
1039 const char *buf, size_t count)
1040{
1041 unsigned long t;
1042 int res;
1043
1044 if (parse_strtoul(buf, 1, &t))
1045 return -EINVAL;
1046
1047 res = bluetooth_set_radiosw(t);
1048
1049 return (res) ? res : count;
1050}
1051
1052static struct device_attribute dev_attr_bluetooth_enable =
1053 __ATTR(enable, S_IWUSR | S_IRUGO,
1054 bluetooth_enable_show, bluetooth_enable_store);
1055
1056/* --------------------------------------------------------------------- */
1057
1058static struct attribute *bluetooth_attributes[] = {
1059 &dev_attr_bluetooth_enable.attr,
1060 NULL
1061};
1062
1063static const struct attribute_group bluetooth_attr_group = {
1064 .name = TPACPI_BLUETH_SYSFS_GROUP,
1065 .attrs = bluetooth_attributes,
1066};
1067
1068static int __init bluetooth_init(struct ibm_init_struct *iibm)
1069{
1070 int res;
1071 int status = 0;
1072
1073 vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n");
1074
1075 IBM_ACPIHANDLE_INIT(hkey);
1076
1077 /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
1078 G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
1079 tp_features.bluetooth = hkey_handle &&
1080 acpi_evalf(hkey_handle, &status, "GBDC", "qd");
1081
1082 vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n",
1083 str_supported(tp_features.bluetooth),
1084 status);
1085
1086 if (tp_features.bluetooth) {
1087 if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
1088 /* no bluetooth hardware present in system */
1089 tp_features.bluetooth = 0;
1090 dbg_printk(TPACPI_DBG_INIT,
1091 "bluetooth hardware not installed\n");
1092 } else {
1093 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
1094 &bluetooth_attr_group);
1095 if (res)
1096 return res;
1097 }
1098 }
1099
1100 return (tp_features.bluetooth)? 0 : 1;
1101}
1102
1103static void bluetooth_exit(void)
1104{
1105 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
1106 &bluetooth_attr_group);
1107}
1108
1109static int bluetooth_get_radiosw(void)
1110{
1111 int status;
1112
1113 if (!tp_features.bluetooth)
1114 return -ENODEV;
1115
1116 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
1117 return -EIO;
1118
1119 return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);
1120}
1121
1122static int bluetooth_set_radiosw(int radio_on)
1123{
1124 int status;
1125
1126 if (!tp_features.bluetooth)
1127 return -ENODEV;
1128
1129 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
1130 return -EIO;
1131 if (radio_on)
1132 status |= TP_ACPI_BLUETOOTH_RADIOSSW;
1133 else
1134 status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
1135 if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
1136 return -EIO;
1137
1138 return 0;
1139}
1140
1141/* procfs -------------------------------------------------------------- */
1142static int bluetooth_read(char *p)
1143{
1144 int len = 0;
1145 int status = bluetooth_get_radiosw();
1146
1147 if (!tp_features.bluetooth)
1148 len += sprintf(p + len, "status:\t\tnot supported\n");
1149 else {
1150 len += sprintf(p + len, "status:\t\t%s\n",
1151 (status)? "enabled" : "disabled");
1152 len += sprintf(p + len, "commands:\tenable, disable\n");
1153 }
1154
1155 return len;
1156}
1157
1158static int bluetooth_write(char *buf)
1159{
1160 char *cmd;
1161
1162 if (!tp_features.bluetooth)
1163 return -ENODEV;
1164
1165 while ((cmd = next_cmd(&buf))) {
1166 if (strlencmp(cmd, "enable") == 0) {
1167 bluetooth_set_radiosw(1);
1168 } else if (strlencmp(cmd, "disable") == 0) {
1169 bluetooth_set_radiosw(0);
1170 } else
1171 return -EINVAL;
1172 }
1173
1174 return 0;
1175}
1176
1177static struct ibm_struct bluetooth_driver_data = {
1178 .name = "bluetooth",
1179 .read = bluetooth_read,
1180 .write = bluetooth_write,
1181 .exit = bluetooth_exit,
1182};
1183
1184/*************************************************************************
1185 * Wan subdriver
1186 */
1187
1188/* sysfs wan enable ---------------------------------------------------- */
1189static ssize_t wan_enable_show(struct device *dev,
1190 struct device_attribute *attr,
1191 char *buf)
1192{
1193 int status;
1194
1195 status = wan_get_radiosw();
1196 if (status < 0)
1197 return status;
1198
1199 return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
1200}
1201
1202static ssize_t wan_enable_store(struct device *dev,
1203 struct device_attribute *attr,
1204 const char *buf, size_t count)
1205{
1206 unsigned long t;
1207 int res;
1208
1209 if (parse_strtoul(buf, 1, &t))
1210 return -EINVAL;
1211
1212 res = wan_set_radiosw(t);
1213
1214 return (res) ? res : count;
1215}
1216
1217static struct device_attribute dev_attr_wan_enable =
1218 __ATTR(enable, S_IWUSR | S_IRUGO,
1219 wan_enable_show, wan_enable_store);
1220
1221/* --------------------------------------------------------------------- */
1222
1223static struct attribute *wan_attributes[] = {
1224 &dev_attr_wan_enable.attr,
1225 NULL
1226};
1227
1228static const struct attribute_group wan_attr_group = {
1229 .name = TPACPI_WAN_SYSFS_GROUP,
1230 .attrs = wan_attributes,
1231};
1232
1233static int __init wan_init(struct ibm_init_struct *iibm)
1234{
1235 int res;
1236 int status = 0;
1237
1238 vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n");
1239
1240 IBM_ACPIHANDLE_INIT(hkey);
1241
1242 tp_features.wan = hkey_handle &&
1243 acpi_evalf(hkey_handle, &status, "GWAN", "qd");
1244
1245 vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n",
1246 str_supported(tp_features.wan),
1247 status);
1248
1249 if (tp_features.wan) {
1250 if (!(status & TP_ACPI_WANCARD_HWPRESENT)) {
1251 /* no wan hardware present in system */
1252 tp_features.wan = 0;
1253 dbg_printk(TPACPI_DBG_INIT,
1254 "wan hardware not installed\n");
1255 } else {
1256 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
1257 &wan_attr_group);
1258 if (res)
1259 return res;
1260 }
1261 }
1262
1263 return (tp_features.wan)? 0 : 1;
1264}
1265
1266static void wan_exit(void)
1267{
1268 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
1269 &wan_attr_group);
1270}
1271
1272static int wan_get_radiosw(void)
1273{
1274 int status;
1275
1276 if (!tp_features.wan)
1277 return -ENODEV;
1278
1279 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
1280 return -EIO;
1281
1282 return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0);
1283}
1284
1285static int wan_set_radiosw(int radio_on)
1286{
1287 int status;
1288
1289 if (!tp_features.wan)
1290 return -ENODEV;
1291
1292 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
1293 return -EIO;
1294 if (radio_on)
1295 status |= TP_ACPI_WANCARD_RADIOSSW;
1296 else
1297 status &= ~TP_ACPI_WANCARD_RADIOSSW;
1298 if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
1299 return -EIO;
1300
1301 return 0;
1302}
1303
1304/* procfs -------------------------------------------------------------- */
1305static int wan_read(char *p)
1306{
1307 int len = 0;
1308 int status = wan_get_radiosw();
1309
1310 if (!tp_features.wan)
1311 len += sprintf(p + len, "status:\t\tnot supported\n");
1312 else {
1313 len += sprintf(p + len, "status:\t\t%s\n",
1314 (status)? "enabled" : "disabled");
1315 len += sprintf(p + len, "commands:\tenable, disable\n");
1316 }
1317
1318 return len;
1319}
1320
1321static int wan_write(char *buf)
1322{
1323 char *cmd;
1324
1325 if (!tp_features.wan)
1326 return -ENODEV;
1327
1328 while ((cmd = next_cmd(&buf))) {
1329 if (strlencmp(cmd, "enable") == 0) {
1330 wan_set_radiosw(1);
1331 } else if (strlencmp(cmd, "disable") == 0) {
1332 wan_set_radiosw(0);
1333 } else
1334 return -EINVAL;
1335 }
1336
1337 return 0;
1338}
1339
1340static struct ibm_struct wan_driver_data = {
1341 .name = "wan",
1342 .read = wan_read,
1343 .write = wan_write,
1344 .exit = wan_exit,
1345 .flags.experimental = 1,
1346};
1347
1348/*************************************************************************
1349 * Video subdriver
1350 */
1351
1352static enum video_access_mode video_supported;
1353static int video_orig_autosw;
1354
1355IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
1356 "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
1357 "\\_SB.PCI0.VID0", /* 770e */
1358 "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
1359 "\\_SB.PCI0.AGP.VID", /* all others */
1360 ); /* R30, R31 */
1361
1362IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
1363
1364static int __init video_init(struct ibm_init_struct *iibm)
1365{
1366 int ivga;
1367
1368 vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
1369
1370 IBM_ACPIHANDLE_INIT(vid);
1371 IBM_ACPIHANDLE_INIT(vid2);
1372
1373 if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
1374 /* G41, assume IVGA doesn't change */
1375 vid_handle = vid2_handle;
1376
1377 if (!vid_handle)
1378 /* video switching not supported on R30, R31 */
1379 video_supported = TPACPI_VIDEO_NONE;
1380 else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
1381 /* 570 */
1382 video_supported = TPACPI_VIDEO_570;
1383 else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
1384 /* 600e/x, 770e, 770x */
1385 video_supported = TPACPI_VIDEO_770;
1386 else
1387 /* all others */
1388 video_supported = TPACPI_VIDEO_NEW;
1389
1390 vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n",
1391 str_supported(video_supported != TPACPI_VIDEO_NONE),
1392 video_supported);
1393
1394 return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1;
1395}
1396
1397static void video_exit(void)
1398{
1399 dbg_printk(TPACPI_DBG_EXIT,
1400 "restoring original video autoswitch mode\n");
1401 if (video_autosw_set(video_orig_autosw))
1402 printk(IBM_ERR "error while trying to restore original "
1403 "video autoswitch mode\n");
1404}
1405
1406static int video_outputsw_get(void)
1407{
1408 int status = 0;
1409 int i;
1410
1411 switch (video_supported) {
1412 case TPACPI_VIDEO_570:
1413 if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd",
1414 TP_ACPI_VIDEO_570_PHSCMD))
1415 return -EIO;
1416 status = i & TP_ACPI_VIDEO_570_PHSMASK;
1417 break;
1418 case TPACPI_VIDEO_770:
1419 if (!acpi_evalf(NULL, &i, "\\VCDL", "d"))
1420 return -EIO;
1421 if (i)
1422 status |= TP_ACPI_VIDEO_S_LCD;
1423 if (!acpi_evalf(NULL, &i, "\\VCDC", "d"))
1424 return -EIO;
1425 if (i)
1426 status |= TP_ACPI_VIDEO_S_CRT;
1427 break;
1428 case TPACPI_VIDEO_NEW:
1429 if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) ||
1430 !acpi_evalf(NULL, &i, "\\VCDC", "d"))
1431 return -EIO;
1432 if (i)
1433 status |= TP_ACPI_VIDEO_S_CRT;
1434
1435 if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) ||
1436 !acpi_evalf(NULL, &i, "\\VCDL", "d"))
1437 return -EIO;
1438 if (i)
1439 status |= TP_ACPI_VIDEO_S_LCD;
1440 if (!acpi_evalf(NULL, &i, "\\VCDD", "d"))
1441 return -EIO;
1442 if (i)
1443 status |= TP_ACPI_VIDEO_S_DVI;
1444 break;
1445 default:
1446 return -ENOSYS;
1447 }
1448
1449 return status;
1450}
1451
1452static int video_outputsw_set(int status)
1453{
1454 int autosw;
1455 int res = 0;
1456
1457 switch (video_supported) {
1458 case TPACPI_VIDEO_570:
1459 res = acpi_evalf(NULL, NULL,
1460 "\\_SB.PHS2", "vdd",
1461 TP_ACPI_VIDEO_570_PHS2CMD,
1462 status | TP_ACPI_VIDEO_570_PHS2SET);
1463 break;
1464 case TPACPI_VIDEO_770:
1465 autosw = video_autosw_get();
1466 if (autosw < 0)
1467 return autosw;
1468
1469 res = video_autosw_set(1);
1470 if (res)
1471 return res;
1472 res = acpi_evalf(vid_handle, NULL,
1473 "ASWT", "vdd", status * 0x100, 0);
1474 if (!autosw && video_autosw_set(autosw)) {
1475 printk(IBM_ERR "video auto-switch left enabled due to error\n");
1476 return -EIO;
1477 }
1478 break;
1479 case TPACPI_VIDEO_NEW:
1480 res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
1481 acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
1482 break;
1483 default:
1484 return -ENOSYS;
1485 }
1486
1487 return (res)? 0 : -EIO;
1488}
1489
1490static int video_autosw_get(void)
1491{
1492 int autosw = 0;
1493
1494 switch (video_supported) {
1495 case TPACPI_VIDEO_570:
1496 if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d"))
1497 return -EIO;
1498 break;
1499 case TPACPI_VIDEO_770:
1500 case TPACPI_VIDEO_NEW:
1501 if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d"))
1502 return -EIO;
1503 break;
1504 default:
1505 return -ENOSYS;
1506 }
1507
1508 return autosw & 1;
1509}
1510
1511static int video_autosw_set(int enable)
1512{
1513 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0))
1514 return -EIO;
1515 return 0;
1516}
1517
1518static int video_outputsw_cycle(void)
1519{
1520 int autosw = video_autosw_get();
1521 int res;
1522
1523 if (autosw < 0)
1524 return autosw;
1525
1526 switch (video_supported) {
1527 case TPACPI_VIDEO_570:
1528 res = video_autosw_set(1);
1529 if (res)
1530 return res;
1531 res = acpi_evalf(ec_handle, NULL, "_Q16", "v");
1532 break;
1533 case TPACPI_VIDEO_770:
1534 case TPACPI_VIDEO_NEW:
1535 res = video_autosw_set(1);
1536 if (res)
1537 return res;
1538 res = acpi_evalf(vid_handle, NULL, "VSWT", "v");
1539 break;
1540 default:
1541 return -ENOSYS;
1542 }
1543 if (!autosw && video_autosw_set(autosw)) {
1544 printk(IBM_ERR "video auto-switch left enabled due to error\n");
1545 return -EIO;
1546 }
1547
1548 return (res)? 0 : -EIO;
1549}
1550
1551static int video_expand_toggle(void)
1552{
1553 switch (video_supported) {
1554 case TPACPI_VIDEO_570:
1555 return acpi_evalf(ec_handle, NULL, "_Q17", "v")?
1556 0 : -EIO;
1557 case TPACPI_VIDEO_770:
1558 return acpi_evalf(vid_handle, NULL, "VEXP", "v")?
1559 0 : -EIO;
1560 case TPACPI_VIDEO_NEW:
1561 return acpi_evalf(NULL, NULL, "\\VEXP", "v")?
1562 0 : -EIO;
1563 default:
1564 return -ENOSYS;
1565 }
1566 /* not reached */
1567}
1568
1569static int video_read(char *p)
1570{
1571 int status, autosw;
1572 int len = 0;
1573
1574 if (video_supported == TPACPI_VIDEO_NONE) {
1575 len += sprintf(p + len, "status:\t\tnot supported\n");
1576 return len;
1577 }
1578
1579 status = video_outputsw_get();
1580 if (status < 0)
1581 return status;
1582
1583 autosw = video_autosw_get();
1584 if (autosw < 0)
1585 return autosw;
1586
1587 len += sprintf(p + len, "status:\t\tsupported\n");
1588 len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
1589 len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
1590 if (video_supported == TPACPI_VIDEO_NEW)
1591 len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
1592 len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
1593 len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
1594 len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
1595 if (video_supported == TPACPI_VIDEO_NEW)
1596 len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
1597 len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
1598 len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
1599
1600 return len;
1601}
1602
1603static int video_write(char *buf)
1604{
1605 char *cmd;
1606 int enable, disable, status;
1607 int res;
1608
1609 if (video_supported == TPACPI_VIDEO_NONE)
1610 return -ENODEV;
1611
1612 enable = 0;
1613 disable = 0;
1614
1615 while ((cmd = next_cmd(&buf))) {
1616 if (strlencmp(cmd, "lcd_enable") == 0) {
1617 enable |= TP_ACPI_VIDEO_S_LCD;
1618 } else if (strlencmp(cmd, "lcd_disable") == 0) {
1619 disable |= TP_ACPI_VIDEO_S_LCD;
1620 } else if (strlencmp(cmd, "crt_enable") == 0) {
1621 enable |= TP_ACPI_VIDEO_S_CRT;
1622 } else if (strlencmp(cmd, "crt_disable") == 0) {
1623 disable |= TP_ACPI_VIDEO_S_CRT;
1624 } else if (video_supported == TPACPI_VIDEO_NEW &&
1625 strlencmp(cmd, "dvi_enable") == 0) {
1626 enable |= TP_ACPI_VIDEO_S_DVI;
1627 } else if (video_supported == TPACPI_VIDEO_NEW &&
1628 strlencmp(cmd, "dvi_disable") == 0) {
1629 disable |= TP_ACPI_VIDEO_S_DVI;
1630 } else if (strlencmp(cmd, "auto_enable") == 0) {
1631 res = video_autosw_set(1);
1632 if (res)
1633 return res;
1634 } else if (strlencmp(cmd, "auto_disable") == 0) {
1635 res = video_autosw_set(0);
1636 if (res)
1637 return res;
1638 } else if (strlencmp(cmd, "video_switch") == 0) {
1639 res = video_outputsw_cycle();
1640 if (res)
1641 return res;
1642 } else if (strlencmp(cmd, "expand_toggle") == 0) {
1643 res = video_expand_toggle();
1644 if (res)
1645 return res;
1646 } else
1647 return -EINVAL;
1648 }
1649
1650 if (enable || disable) {
1651 status = video_outputsw_get();
1652 if (status < 0)
1653 return status;
1654 res = video_outputsw_set((status & ~disable) | enable);
1655 if (res)
1656 return res;
1657 }
1658
1659 return 0;
1660}
1661
1662static struct ibm_struct video_driver_data = {
1663 .name = "video",
1664 .read = video_read,
1665 .write = video_write,
1666 .exit = video_exit,
1667};
1668
1669/*************************************************************************
1670 * Light (thinklight) subdriver
1671 */
1672
1673IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
1674IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */
1675
1676static int __init light_init(struct ibm_init_struct *iibm)
1677{
1678 vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
1679
1680 IBM_ACPIHANDLE_INIT(ledb);
1681 IBM_ACPIHANDLE_INIT(lght);
1682 IBM_ACPIHANDLE_INIT(cmos);
1683
1684 /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
1685 tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
1686
1687 if (tp_features.light)
1688 /* light status not supported on
1689 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
1690 tp_features.light_status =
1691 acpi_evalf(ec_handle, NULL, "KBLT", "qv");
1692
1693 vdbg_printk(TPACPI_DBG_INIT, "light is %s\n",
1694 str_supported(tp_features.light));
1695
1696 return (tp_features.light)? 0 : 1;
1697}
1698
1699static int light_read(char *p)
1700{
1701 int len = 0;
1702 int status = 0;
1703
1704 if (!tp_features.light) {
1705 len += sprintf(p + len, "status:\t\tnot supported\n");
1706 } else if (!tp_features.light_status) {
1707 len += sprintf(p + len, "status:\t\tunknown\n");
1708 len += sprintf(p + len, "commands:\ton, off\n");
1709 } else {
1710 if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
1711 return -EIO;
1712 len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
1713 len += sprintf(p + len, "commands:\ton, off\n");
1714 }
1715
1716 return len;
1717}
1718
1719static int light_write(char *buf)
1720{
1721 int cmos_cmd, lght_cmd;
1722 char *cmd;
1723 int success;
1724
1725 if (!tp_features.light)
1726 return -ENODEV;
1727
1728 while ((cmd = next_cmd(&buf))) {
1729 if (strlencmp(cmd, "on") == 0) {
1730 cmos_cmd = 0x0c;
1731 lght_cmd = 1;
1732 } else if (strlencmp(cmd, "off") == 0) {
1733 cmos_cmd = 0x0d;
1734 lght_cmd = 0;
1735 } else
1736 return -EINVAL;
1737
1738 success = cmos_handle ?
1739 acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
1740 acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
1741 if (!success)
1742 return -EIO;
1743 }
1744
1745 return 0;
1746}
1747
1748static struct ibm_struct light_driver_data = {
1749 .name = "light",
1750 .read = light_read,
1751 .write = light_write,
1752};
1753
1754/*************************************************************************
1755 * Dock subdriver
1756 */
1757
1758#ifdef CONFIG_THINKPAD_ACPI_DOCK
1759
1760IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
1761 "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
1762 "\\_SB.PCI0.PCI1.DOCK", /* all others */
1763 "\\_SB.PCI.ISA.SLCE", /* 570 */
1764 ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
1765
1766/* don't list other alternatives as we install a notify handler on the 570 */
1767IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
1768
1769static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
1770 {
1771 .notify = dock_notify,
1772 .handle = &dock_handle,
1773 .type = ACPI_SYSTEM_NOTIFY,
1774 },
1775 {
1776 .hid = IBM_PCI_HID,
1777 .notify = dock_notify,
1778 .handle = &pci_handle,
1779 .type = ACPI_SYSTEM_NOTIFY,
1780 },
1781};
1782
1783static struct ibm_struct dock_driver_data[2] = {
1784 {
1785 .name = "dock",
1786 .read = dock_read,
1787 .write = dock_write,
1788 .acpi = &ibm_dock_acpidriver[0],
1789 },
1790 {
1791 .name = "dock",
1792 .acpi = &ibm_dock_acpidriver[1],
1793 },
1794};
1795
1796#define dock_docked() (_sta(dock_handle) & 1)
1797
1798static int __init dock_init(struct ibm_init_struct *iibm)
1799{
1800 vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n");
1801
1802 IBM_ACPIHANDLE_INIT(dock);
1803
1804 vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n",
1805 str_supported(dock_handle != NULL));
1806
1807 return (dock_handle)? 0 : 1;
1808}
1809
1810static int __init dock_init2(struct ibm_init_struct *iibm)
1811{
1812 int dock2_needed;
1813
1814 vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n");
1815
1816 if (dock_driver_data[0].flags.acpi_driver_registered &&
1817 dock_driver_data[0].flags.acpi_notify_installed) {
1818 IBM_ACPIHANDLE_INIT(pci);
1819 dock2_needed = (pci_handle != NULL);
1820 vdbg_printk(TPACPI_DBG_INIT,
1821 "dock PCI handler for the TP 570 is %s\n",
1822 str_supported(dock2_needed));
1823 } else {
1824 vdbg_printk(TPACPI_DBG_INIT,
1825 "dock subdriver part 2 not required\n");
1826 dock2_needed = 0;
1827 }
1828
1829 return (dock2_needed)? 0 : 1;
1830}
1831
1832static void dock_notify(struct ibm_struct *ibm, u32 event)
1833{
1834 int docked = dock_docked();
1835 int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, IBM_PCI_HID);
1836
1837 if (event == 1 && !pci) /* 570 */
1838 acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */
1839 else if (event == 1 && pci) /* 570 */
1840 acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */
1841 else if (event == 3 && docked)
1842 acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */
1843 else if (event == 3 && !docked)
1844 acpi_bus_generate_event(ibm->acpi->device, event, 2); /* undock */
1845 else if (event == 0 && docked)
1846 acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */
1847 else {
1848 printk(IBM_ERR "unknown dock event %d, status %d\n",
1849 event, _sta(dock_handle));
1850 acpi_bus_generate_event(ibm->acpi->device, event, 0); /* unknown */
1851 }
1852}
1853
1854static int dock_read(char *p)
1855{
1856 int len = 0;
1857 int docked = dock_docked();
1858
1859 if (!dock_handle)
1860 len += sprintf(p + len, "status:\t\tnot supported\n");
1861 else if (!docked)
1862 len += sprintf(p + len, "status:\t\tundocked\n");
1863 else {
1864 len += sprintf(p + len, "status:\t\tdocked\n");
1865 len += sprintf(p + len, "commands:\tdock, undock\n");
1866 }
1867
1868 return len;
1869}
1870
1871static int dock_write(char *buf)
1872{
1873 char *cmd;
1874
1875 if (!dock_docked())
1876 return -ENODEV;
1877
1878 while ((cmd = next_cmd(&buf))) {
1879 if (strlencmp(cmd, "undock") == 0) {
1880 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
1881 !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
1882 return -EIO;
1883 } else if (strlencmp(cmd, "dock") == 0) {
1884 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
1885 return -EIO;
1886 } else
1887 return -EINVAL;
1888 }
1889
1890 return 0;
1891}
1892
1893#endif /* CONFIG_THINKPAD_ACPI_DOCK */
1894
1895/*************************************************************************
1896 * Bay subdriver
1897 */
1898
1899#ifdef CONFIG_THINKPAD_ACPI_BAY
1900IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
1901 "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
1902 "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
1903 "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
1904 ); /* A21e, R30, R31 */
1905IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
1906 "_EJ0", /* all others */
1907 ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
1908IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
1909 "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
1910 ); /* all others */
1911IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
1912 "_EJ0", /* 770x */
1913 ); /* all others */
1914
1915static int __init bay_init(struct ibm_init_struct *iibm)
1916{
1917 vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n");
1918
1919 IBM_ACPIHANDLE_INIT(bay);
1920 if (bay_handle)
1921 IBM_ACPIHANDLE_INIT(bay_ej);
1922 IBM_ACPIHANDLE_INIT(bay2);
1923 if (bay2_handle)
1924 IBM_ACPIHANDLE_INIT(bay2_ej);
1925
1926 tp_features.bay_status = bay_handle &&
1927 acpi_evalf(bay_handle, NULL, "_STA", "qv");
1928 tp_features.bay_status2 = bay2_handle &&
1929 acpi_evalf(bay2_handle, NULL, "_STA", "qv");
1930
1931 tp_features.bay_eject = bay_handle && bay_ej_handle &&
1932 (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
1933 tp_features.bay_eject2 = bay2_handle && bay2_ej_handle &&
1934 (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
1935
1936 vdbg_printk(TPACPI_DBG_INIT,
1937 "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n",
1938 str_supported(tp_features.bay_status),
1939 str_supported(tp_features.bay_eject),
1940 str_supported(tp_features.bay_status2),
1941 str_supported(tp_features.bay_eject2));
1942
1943 return (tp_features.bay_status || tp_features.bay_eject ||
1944 tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1;
1945}
1946
1947static void bay_notify(struct ibm_struct *ibm, u32 event)
1948{
1949 acpi_bus_generate_event(ibm->acpi->device, event, 0);
1950}
1951
1952#define bay_occupied(b) (_sta(b##_handle) & 1)
1953
1954static int bay_read(char *p)
1955{
1956 int len = 0;
1957 int occupied = bay_occupied(bay);
1958 int occupied2 = bay_occupied(bay2);
1959 int eject, eject2;
1960
1961 len += sprintf(p + len, "status:\t\t%s\n",
1962 tp_features.bay_status ?
1963 (occupied ? "occupied" : "unoccupied") :
1964 "not supported");
1965 if (tp_features.bay_status2)
1966 len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
1967 "occupied" : "unoccupied");
1968
1969 eject = tp_features.bay_eject && occupied;
1970 eject2 = tp_features.bay_eject2 && occupied2;
1971
1972 if (eject && eject2)
1973 len += sprintf(p + len, "commands:\teject, eject2\n");
1974 else if (eject)
1975 len += sprintf(p + len, "commands:\teject\n");
1976 else if (eject2)
1977 len += sprintf(p + len, "commands:\teject2\n");
1978
1979 return len;
1980}
1981
1982static int bay_write(char *buf)
1983{
1984 char *cmd;
1985
1986 if (!tp_features.bay_eject && !tp_features.bay_eject2)
1987 return -ENODEV;
1988
1989 while ((cmd = next_cmd(&buf))) {
1990 if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) {
1991 if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
1992 return -EIO;
1993 } else if (tp_features.bay_eject2 &&
1994 strlencmp(cmd, "eject2") == 0) {
1995 if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
1996 return -EIO;
1997 } else
1998 return -EINVAL;
1999 }
2000
2001 return 0;
2002}
2003
2004static struct tp_acpi_drv_struct ibm_bay_acpidriver = {
2005 .notify = bay_notify,
2006 .handle = &bay_handle,
2007 .type = ACPI_SYSTEM_NOTIFY,
2008};
2009
2010static struct ibm_struct bay_driver_data = {
2011 .name = "bay",
2012 .read = bay_read,
2013 .write = bay_write,
2014 .acpi = &ibm_bay_acpidriver,
2015};
2016
2017#endif /* CONFIG_THINKPAD_ACPI_BAY */
2018
2019/*************************************************************************
2020 * CMOS subdriver
2021 */
2022
2023/* sysfs cmos_command -------------------------------------------------- */
2024static ssize_t cmos_command_store(struct device *dev,
2025 struct device_attribute *attr,
2026 const char *buf, size_t count)
2027{
2028 unsigned long cmos_cmd;
2029 int res;
2030
2031 if (parse_strtoul(buf, 21, &cmos_cmd))
2032 return -EINVAL;
2033
2034 res = issue_thinkpad_cmos_command(cmos_cmd);
2035 return (res)? res : count;
2036}
2037
2038static struct device_attribute dev_attr_cmos_command =
2039 __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store);
2040
2041/* --------------------------------------------------------------------- */
2042
2043static int __init cmos_init(struct ibm_init_struct *iibm)
2044{
2045 int res;
2046
2047 vdbg_printk(TPACPI_DBG_INIT,
2048 "initializing cmos commands subdriver\n");
2049
2050 IBM_ACPIHANDLE_INIT(cmos);
2051
2052 vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n",
2053 str_supported(cmos_handle != NULL));
2054
2055 res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
2056 if (res)
2057 return res;
2058
2059 return (cmos_handle)? 0 : 1;
2060}
2061
2062static void cmos_exit(void)
2063{
2064 device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
2065}
2066
2067static int cmos_read(char *p)
2068{
2069 int len = 0;
2070
2071 /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
2072 R30, R31, T20-22, X20-21 */
2073 if (!cmos_handle)
2074 len += sprintf(p + len, "status:\t\tnot supported\n");
2075 else {
2076 len += sprintf(p + len, "status:\t\tsupported\n");
2077 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
2078 }
2079
2080 return len;
2081}
2082
2083static int cmos_write(char *buf)
2084{
2085 char *cmd;
2086 int cmos_cmd, res;
2087
2088 while ((cmd = next_cmd(&buf))) {
2089 if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
2090 cmos_cmd >= 0 && cmos_cmd <= 21) {
2091 /* cmos_cmd set */
2092 } else
2093 return -EINVAL;
2094
2095 res = issue_thinkpad_cmos_command(cmos_cmd);
2096 if (res)
2097 return res;
2098 }
2099
2100 return 0;
2101}
2102
2103static struct ibm_struct cmos_driver_data = {
2104 .name = "cmos",
2105 .read = cmos_read,
2106 .write = cmos_write,
2107 .exit = cmos_exit,
2108};
2109
2110/*************************************************************************
2111 * LED subdriver
2112 */
2113
2114static enum led_access_mode led_supported;
2115
2116IBM_HANDLE(led, ec, "SLED", /* 570 */
2117 "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
2118 "LED", /* all others */
2119 ); /* R30, R31 */
2120
2121static int __init led_init(struct ibm_init_struct *iibm)
2122{
2123 vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
2124
2125 IBM_ACPIHANDLE_INIT(led);
2126
2127 if (!led_handle)
2128 /* led not supported on R30, R31 */
2129 led_supported = TPACPI_LED_NONE;
2130 else if (strlencmp(led_path, "SLED") == 0)
2131 /* 570 */
2132 led_supported = TPACPI_LED_570;
2133 else if (strlencmp(led_path, "SYSL") == 0)
2134 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
2135 led_supported = TPACPI_LED_OLD;
2136 else
2137 /* all others */
2138 led_supported = TPACPI_LED_NEW;
2139
2140 vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
2141 str_supported(led_supported), led_supported);
2142
2143 return (led_supported != TPACPI_LED_NONE)? 0 : 1;
2144}
2145
2146#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
2147
2148static int led_read(char *p)
2149{
2150 int len = 0;
2151
2152 if (!led_supported) {
2153 len += sprintf(p + len, "status:\t\tnot supported\n");
2154 return len;
2155 }
2156 len += sprintf(p + len, "status:\t\tsupported\n");
2157
2158 if (led_supported == TPACPI_LED_570) {
2159 /* 570 */
2160 int i, status;
2161 for (i = 0; i < 8; i++) {
2162 if (!acpi_evalf(ec_handle,
2163 &status, "GLED", "dd", 1 << i))
2164 return -EIO;
2165 len += sprintf(p + len, "%d:\t\t%s\n",
2166 i, led_status(status));
2167 }
2168 }
2169
2170 len += sprintf(p + len, "commands:\t"
2171 "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
2172
2173 return len;
2174}
2175
2176/* off, on, blink */
2177static const int led_sled_arg1[] = { 0, 1, 3 };
2178static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
2179static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
2180static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
2181
2182static int led_write(char *buf)
2183{
2184 char *cmd;
2185 int led, ind, ret;
2186
2187 if (!led_supported)
2188 return -ENODEV;
2189
2190 while ((cmd = next_cmd(&buf))) {
2191 if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
2192 return -EINVAL;
2193
2194 if (strstr(cmd, "off")) {
2195 ind = 0;
2196 } else if (strstr(cmd, "on")) {
2197 ind = 1;
2198 } else if (strstr(cmd, "blink")) {
2199 ind = 2;
2200 } else
2201 return -EINVAL;
2202
2203 if (led_supported == TPACPI_LED_570) {
2204 /* 570 */
2205 led = 1 << led;
2206 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
2207 led, led_sled_arg1[ind]))
2208 return -EIO;
2209 } else if (led_supported == TPACPI_LED_OLD) {
2210 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
2211 led = 1 << led;
2212 ret = ec_write(TPACPI_LED_EC_HLMS, led);
2213 if (ret >= 0)
2214 ret =
2215 ec_write(TPACPI_LED_EC_HLBL,
2216 led * led_exp_hlbl[ind]);
2217 if (ret >= 0)
2218 ret =
2219 ec_write(TPACPI_LED_EC_HLCL,
2220 led * led_exp_hlcl[ind]);
2221 if (ret < 0)
2222 return ret;
2223 } else {
2224 /* all others */
2225 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
2226 led, led_led_arg1[ind]))
2227 return -EIO;
2228 }
2229 }
2230
2231 return 0;
2232}
2233
2234static struct ibm_struct led_driver_data = {
2235 .name = "led",
2236 .read = led_read,
2237 .write = led_write,
2238};
2239
2240/*************************************************************************
2241 * Beep subdriver
2242 */
2243
2244IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
2245
2246static int __init beep_init(struct ibm_init_struct *iibm)
2247{
2248 vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
2249
2250 IBM_ACPIHANDLE_INIT(beep);
2251
2252 vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
2253 str_supported(beep_handle != NULL));
2254
2255 return (beep_handle)? 0 : 1;
2256}
2257
2258static int beep_read(char *p)
2259{
2260 int len = 0;
2261
2262 if (!beep_handle)
2263 len += sprintf(p + len, "status:\t\tnot supported\n");
2264 else {
2265 len += sprintf(p + len, "status:\t\tsupported\n");
2266 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
2267 }
2268
2269 return len;
2270}
2271
2272static int beep_write(char *buf)
2273{
2274 char *cmd;
2275 int beep_cmd;
2276
2277 if (!beep_handle)
2278 return -ENODEV;
2279
2280 while ((cmd = next_cmd(&buf))) {
2281 if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
2282 beep_cmd >= 0 && beep_cmd <= 17) {
2283 /* beep_cmd set */
2284 } else
2285 return -EINVAL;
2286 if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
2287 return -EIO;
2288 }
2289
2290 return 0;
2291}
2292
2293static struct ibm_struct beep_driver_data = {
2294 .name = "beep",
2295 .read = beep_read,
2296 .write = beep_write,
2297};
2298
2299/*************************************************************************
2300 * Thermal subdriver
2301 */
2302
2303static enum thermal_access_mode thermal_read_mode;
2304
2305/* sysfs temp##_input -------------------------------------------------- */
2306
2307static ssize_t thermal_temp_input_show(struct device *dev,
2308 struct device_attribute *attr,
2309 char *buf)
2310{
2311 struct sensor_device_attribute *sensor_attr =
2312 to_sensor_dev_attr(attr);
2313 int idx = sensor_attr->index;
2314 s32 value;
2315 int res;
2316
2317 res = thermal_get_sensor(idx, &value);
2318 if (res)
2319 return res;
2320 if (value == TP_EC_THERMAL_TMP_NA * 1000)
2321 return -ENXIO;
2322
2323 return snprintf(buf, PAGE_SIZE, "%d\n", value);
2324}
2325
2326#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
2327 SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, thermal_temp_input_show, NULL, _idxB)
2328
2329static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = {
2330 THERMAL_SENSOR_ATTR_TEMP(1, 0),
2331 THERMAL_SENSOR_ATTR_TEMP(2, 1),
2332 THERMAL_SENSOR_ATTR_TEMP(3, 2),
2333 THERMAL_SENSOR_ATTR_TEMP(4, 3),
2334 THERMAL_SENSOR_ATTR_TEMP(5, 4),
2335 THERMAL_SENSOR_ATTR_TEMP(6, 5),
2336 THERMAL_SENSOR_ATTR_TEMP(7, 6),
2337 THERMAL_SENSOR_ATTR_TEMP(8, 7),
2338 THERMAL_SENSOR_ATTR_TEMP(9, 8),
2339 THERMAL_SENSOR_ATTR_TEMP(10, 9),
2340 THERMAL_SENSOR_ATTR_TEMP(11, 10),
2341 THERMAL_SENSOR_ATTR_TEMP(12, 11),
2342 THERMAL_SENSOR_ATTR_TEMP(13, 12),
2343 THERMAL_SENSOR_ATTR_TEMP(14, 13),
2344 THERMAL_SENSOR_ATTR_TEMP(15, 14),
2345 THERMAL_SENSOR_ATTR_TEMP(16, 15),
2346};
2347
2348#define THERMAL_ATTRS(X) \
2349 &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
2350
2351static struct attribute *thermal_temp_input_attr[] = {
2352 THERMAL_ATTRS(8),
2353 THERMAL_ATTRS(9),
2354 THERMAL_ATTRS(10),
2355 THERMAL_ATTRS(11),
2356 THERMAL_ATTRS(12),
2357 THERMAL_ATTRS(13),
2358 THERMAL_ATTRS(14),
2359 THERMAL_ATTRS(15),
2360 THERMAL_ATTRS(0),
2361 THERMAL_ATTRS(1),
2362 THERMAL_ATTRS(2),
2363 THERMAL_ATTRS(3),
2364 THERMAL_ATTRS(4),
2365 THERMAL_ATTRS(5),
2366 THERMAL_ATTRS(6),
2367 THERMAL_ATTRS(7),
2368 NULL
2369};
2370
2371static const struct attribute_group thermal_temp_input16_group = {
2372 .attrs = thermal_temp_input_attr
2373};
2374
2375static const struct attribute_group thermal_temp_input8_group = {
2376 .attrs = &thermal_temp_input_attr[8]
2377};
2378
2379#undef THERMAL_SENSOR_ATTR_TEMP
2380#undef THERMAL_ATTRS
2381
2382/* --------------------------------------------------------------------- */
2383
2384static int __init thermal_init(struct ibm_init_struct *iibm)
2385{
2386 u8 t, ta1, ta2;
2387 int i;
2388 int acpi_tmp7;
2389 int res;
2390
2391 vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
2392
2393 acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
2394
2395 if (ibm_thinkpad_ec_found && experimental) {
2396 /*
2397 * Direct EC access mode: sensors at registers
2398 * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
2399 * non-implemented, thermal sensors return 0x80 when
2400 * not available
2401 */
2402
2403 ta1 = ta2 = 0;
2404 for (i = 0; i < 8; i++) {
2405 if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
2406 ta1 |= t;
2407 } else {
2408 ta1 = 0;
2409 break;
2410 }
2411 if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
2412 ta2 |= t;
2413 } else {
2414 ta1 = 0;
2415 break;
2416 }
2417 }
2418 if (ta1 == 0) {
2419 /* This is sheer paranoia, but we handle it anyway */
2420 if (acpi_tmp7) {
2421 printk(IBM_ERR
2422 "ThinkPad ACPI EC access misbehaving, "
2423 "falling back to ACPI TMPx access mode\n");
2424 thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
2425 } else {
2426 printk(IBM_ERR
2427 "ThinkPad ACPI EC access misbehaving, "
2428 "disabling thermal sensors access\n");
2429 thermal_read_mode = TPACPI_THERMAL_NONE;
2430 }
2431 } else {
2432 thermal_read_mode =
2433 (ta2 != 0) ?
2434 TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
2435 }
2436 } else if (acpi_tmp7) {
2437 if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
2438 /* 600e/x, 770e, 770x */
2439 thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
2440 } else {
2441 /* Standard ACPI TMPx access, max 8 sensors */
2442 thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
2443 }
2444 } else {
2445 /* temperatures not supported on 570, G4x, R30, R31, R32 */
2446 thermal_read_mode = TPACPI_THERMAL_NONE;
2447 }
2448
2449 vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
2450 str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
2451 thermal_read_mode);
2452
2453 switch(thermal_read_mode) {
2454 case TPACPI_THERMAL_TPEC_16:
2455 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2456 &thermal_temp_input16_group);
2457 if (res)
2458 return res;
2459 break;
2460 case TPACPI_THERMAL_TPEC_8:
2461 case TPACPI_THERMAL_ACPI_TMP07:
2462 case TPACPI_THERMAL_ACPI_UPDT:
2463 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2464 &thermal_temp_input8_group);
2465 if (res)
2466 return res;
2467 break;
2468 case TPACPI_THERMAL_NONE:
2469 default:
2470 return 1;
2471 }
2472
2473 return 0;
2474}
2475
2476static void thermal_exit(void)
2477{
2478 switch(thermal_read_mode) {
2479 case TPACPI_THERMAL_TPEC_16:
2480 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2481 &thermal_temp_input16_group);
2482 break;
2483 case TPACPI_THERMAL_TPEC_8:
2484 case TPACPI_THERMAL_ACPI_TMP07:
2485 case TPACPI_THERMAL_ACPI_UPDT:
2486 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2487 &thermal_temp_input16_group);
2488 break;
2489 case TPACPI_THERMAL_NONE:
2490 default:
2491 break;
2492 }
2493}
2494
2495/* idx is zero-based */
2496static int thermal_get_sensor(int idx, s32 *value)
2497{
2498 int t;
2499 s8 tmp;
2500 char tmpi[5];
2501
2502 t = TP_EC_THERMAL_TMP0;
2503
2504 switch (thermal_read_mode) {
2505#if TPACPI_MAX_THERMAL_SENSORS >= 16
2506 case TPACPI_THERMAL_TPEC_16:
2507 if (idx >= 8 && idx <= 15) {
2508 t = TP_EC_THERMAL_TMP8;
2509 idx -= 8;
2510 }
2511 /* fallthrough */
2512#endif
2513 case TPACPI_THERMAL_TPEC_8:
2514 if (idx <= 7) {
2515 if (!acpi_ec_read(t + idx, &tmp))
2516 return -EIO;
2517 *value = tmp * 1000;
2518 return 0;
2519 }
2520 break;
2521
2522 case TPACPI_THERMAL_ACPI_UPDT:
2523 if (idx <= 7) {
2524 snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
2525 if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
2526 return -EIO;
2527 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
2528 return -EIO;
2529 *value = (t - 2732) * 100;
2530 return 0;
2531 }
2532 break;
2533
2534 case TPACPI_THERMAL_ACPI_TMP07:
2535 if (idx <= 7) {
2536 snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
2537 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
2538 return -EIO;
2539 *value = t * 1000;
2540 return 0;
2541 }
2542 break;
2543
2544 case TPACPI_THERMAL_NONE:
2545 default:
2546 return -ENOSYS;
2547 }
2548
2549 return -EINVAL;
2550}
2551
2552static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
2553{
2554 int res, i;
2555 int n;
2556
2557 n = 8;
2558 i = 0;
2559
2560 if (!s)
2561 return -EINVAL;
2562
2563 if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
2564 n = 16;
2565
2566 for(i = 0 ; i < n; i++) {
2567 res = thermal_get_sensor(i, &s->temp[i]);
2568 if (res)
2569 return res;
2570 }
2571
2572 return n;
2573}
2574
2575static int thermal_read(char *p)
2576{
2577 int len = 0;
2578 int n, i;
2579 struct ibm_thermal_sensors_struct t;
2580
2581 n = thermal_get_sensors(&t);
2582 if (unlikely(n < 0))
2583 return n;
2584
2585 len += sprintf(p + len, "temperatures:\t");
2586
2587 if (n > 0) {
2588 for (i = 0; i < (n - 1); i++)
2589 len += sprintf(p + len, "%d ", t.temp[i] / 1000);
2590 len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
2591 } else
2592 len += sprintf(p + len, "not supported\n");
2593
2594 return len;
2595}
2596
2597static struct ibm_struct thermal_driver_data = {
2598 .name = "thermal",
2599 .read = thermal_read,
2600 .exit = thermal_exit,
2601};
2602
2603/*************************************************************************
2604 * EC Dump subdriver
2605 */
2606
2607static u8 ecdump_regs[256];
2608
2609static int ecdump_read(char *p)
2610{
2611 int len = 0;
2612 int i, j;
2613 u8 v;
2614
2615 len += sprintf(p + len, "EC "
2616 " +00 +01 +02 +03 +04 +05 +06 +07"
2617 " +08 +09 +0a +0b +0c +0d +0e +0f\n");
2618 for (i = 0; i < 256; i += 16) {
2619 len += sprintf(p + len, "EC 0x%02x:", i);
2620 for (j = 0; j < 16; j++) {
2621 if (!acpi_ec_read(i + j, &v))
2622 break;
2623 if (v != ecdump_regs[i + j])
2624 len += sprintf(p + len, " *%02x", v);
2625 else
2626 len += sprintf(p + len, " %02x", v);
2627 ecdump_regs[i + j] = v;
2628 }
2629 len += sprintf(p + len, "\n");
2630 if (j != 16)
2631 break;
2632 }
2633
2634 /* These are way too dangerous to advertise openly... */
2635#if 0
2636 len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
2637 " (<offset> is 00-ff, <value> is 00-ff)\n");
2638 len += sprintf(p + len, "commands:\t0x<offset> <value> "
2639 " (<offset> is 00-ff, <value> is 0-255)\n");
2640#endif
2641 return len;
2642}
2643
2644static int ecdump_write(char *buf)
2645{
2646 char *cmd;
2647 int i, v;
2648
2649 while ((cmd = next_cmd(&buf))) {
2650 if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
2651 /* i and v set */
2652 } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
2653 /* i and v set */
2654 } else
2655 return -EINVAL;
2656 if (i >= 0 && i < 256 && v >= 0 && v < 256) {
2657 if (!acpi_ec_write(i, v))
2658 return -EIO;
2659 } else
2660 return -EINVAL;
2661 }
2662
2663 return 0;
2664}
2665
2666static struct ibm_struct ecdump_driver_data = {
2667 .name = "ecdump",
2668 .read = ecdump_read,
2669 .write = ecdump_write,
2670 .flags.experimental = 1,
2671};
2672
2673/*************************************************************************
2674 * Backlight/brightness subdriver
2675 */
2676
2677static struct backlight_device *ibm_backlight_device = NULL;
2678
2679static struct backlight_ops ibm_backlight_data = {
2680 .get_brightness = brightness_get,
2681 .update_status = brightness_update_status,
2682};
2683
2684static int __init brightness_init(struct ibm_init_struct *iibm)
2685{
2686 int b;
2687
2688 vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
2689
2690 b = brightness_get(NULL);
2691 if (b < 0)
2692 return b;
2693
2694 ibm_backlight_device = backlight_device_register(
2695 TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
2696 &ibm_backlight_data);
2697 if (IS_ERR(ibm_backlight_device)) {
2698 printk(IBM_ERR "Could not register backlight device\n");
2699 return PTR_ERR(ibm_backlight_device);
2700 }
2701 vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
2702
2703 ibm_backlight_device->props.max_brightness = 7;
2704 ibm_backlight_device->props.brightness = b;
2705 backlight_update_status(ibm_backlight_device);
2706
2707 return 0;
2708}
2709
2710static void brightness_exit(void)
2711{
2712 if (ibm_backlight_device) {
2713 vdbg_printk(TPACPI_DBG_EXIT,
2714 "calling backlight_device_unregister()\n");
2715 backlight_device_unregister(ibm_backlight_device);
2716 ibm_backlight_device = NULL;
2717 }
2718}
2719
2720static int brightness_update_status(struct backlight_device *bd)
2721{
2722 return brightness_set(
2723 (bd->props.fb_blank == FB_BLANK_UNBLANK &&
2724 bd->props.power == FB_BLANK_UNBLANK) ?
2725 bd->props.brightness : 0);
2726}
2727
2728static int brightness_get(struct backlight_device *bd)
2729{
2730 u8 level;
2731 if (!acpi_ec_read(brightness_offset, &level))
2732 return -EIO;
2733
2734 level &= 0x7;
2735
2736 return level;
2737}
2738
2739static int brightness_set(int value)
2740{
2741 int cmos_cmd, inc, i;
2742 int current_value = brightness_get(NULL);
2743
2744 value &= 7;
2745
2746 cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN;
2747 inc = value > current_value ? 1 : -1;
2748 for (i = current_value; i != value; i += inc) {
2749 if (issue_thinkpad_cmos_command(cmos_cmd))
2750 return -EIO;
2751 if (!acpi_ec_write(brightness_offset, i + inc))
2752 return -EIO;
2753 }
2754
2755 return 0;
2756}
2757
2758static int brightness_read(char *p)
2759{
2760 int len = 0;
2761 int level;
2762
2763 if ((level = brightness_get(NULL)) < 0) {
2764 len += sprintf(p + len, "level:\t\tunreadable\n");
2765 } else {
2766 len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
2767 len += sprintf(p + len, "commands:\tup, down\n");
2768 len += sprintf(p + len, "commands:\tlevel <level>"
2769 " (<level> is 0-7)\n");
2770 }
2771
2772 return len;
2773}
2774
2775static int brightness_write(char *buf)
2776{
2777 int level;
2778 int new_level;
2779 char *cmd;
2780
2781 while ((cmd = next_cmd(&buf))) {
2782 if ((level = brightness_get(NULL)) < 0)
2783 return level;
2784 level &= 7;
2785
2786 if (strlencmp(cmd, "up") == 0) {
2787 new_level = level == 7 ? 7 : level + 1;
2788 } else if (strlencmp(cmd, "down") == 0) {
2789 new_level = level == 0 ? 0 : level - 1;
2790 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
2791 new_level >= 0 && new_level <= 7) {
2792 /* new_level set */
2793 } else
2794 return -EINVAL;
2795
2796 brightness_set(new_level);
2797 }
2798
2799 return 0;
2800}
2801
2802static struct ibm_struct brightness_driver_data = {
2803 .name = "brightness",
2804 .read = brightness_read,
2805 .write = brightness_write,
2806 .exit = brightness_exit,
2807};
2808
2809/*************************************************************************
2810 * Volume subdriver
2811 */
2812
2813static int volume_read(char *p)
2814{
2815 int len = 0;
2816 u8 level;
2817
2818 if (!acpi_ec_read(volume_offset, &level)) {
2819 len += sprintf(p + len, "level:\t\tunreadable\n");
2820 } else {
2821 len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
2822 len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
2823 len += sprintf(p + len, "commands:\tup, down, mute\n");
2824 len += sprintf(p + len, "commands:\tlevel <level>"
2825 " (<level> is 0-15)\n");
2826 }
2827
2828 return len;
2829}
2830
2831static int volume_write(char *buf)
2832{
2833 int cmos_cmd, inc, i;
2834 u8 level, mute;
2835 int new_level, new_mute;
2836 char *cmd;
2837
2838 while ((cmd = next_cmd(&buf))) {
2839 if (!acpi_ec_read(volume_offset, &level))
2840 return -EIO;
2841 new_mute = mute = level & 0x40;
2842 new_level = level = level & 0xf;
2843
2844 if (strlencmp(cmd, "up") == 0) {
2845 if (mute)
2846 new_mute = 0;
2847 else
2848 new_level = level == 15 ? 15 : level + 1;
2849 } else if (strlencmp(cmd, "down") == 0) {
2850 if (mute)
2851 new_mute = 0;
2852 else
2853 new_level = level == 0 ? 0 : level - 1;
2854 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
2855 new_level >= 0 && new_level <= 15) {
2856 /* new_level set */
2857 } else if (strlencmp(cmd, "mute") == 0) {
2858 new_mute = 0x40;
2859 } else
2860 return -EINVAL;
2861
2862 if (new_level != level) { /* mute doesn't change */
2863 cmos_cmd = new_level > level ? TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
2864 inc = new_level > level ? 1 : -1;
2865
2866 if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
2867 !acpi_ec_write(volume_offset, level)))
2868 return -EIO;
2869
2870 for (i = level; i != new_level; i += inc)
2871 if (issue_thinkpad_cmos_command(cmos_cmd) ||
2872 !acpi_ec_write(volume_offset, i + inc))
2873 return -EIO;
2874
2875 if (mute && (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
2876 !acpi_ec_write(volume_offset,
2877 new_level + mute)))
2878 return -EIO;
2879 }
2880
2881 if (new_mute != mute) { /* level doesn't change */
2882 cmos_cmd = new_mute ? TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
2883
2884 if (issue_thinkpad_cmos_command(cmos_cmd) ||
2885 !acpi_ec_write(volume_offset, level + new_mute))
2886 return -EIO;
2887 }
2888 }
2889
2890 return 0;
2891}
2892
2893static struct ibm_struct volume_driver_data = {
2894 .name = "volume",
2895 .read = volume_read,
2896 .write = volume_write,
2897};
2898
2899/*************************************************************************
2900 * Fan subdriver
2901 */
2902
2903/*
2904 * FAN ACCESS MODES
2905 *
2906 * TPACPI_FAN_RD_ACPI_GFAN:
2907 * ACPI GFAN method: returns fan level
2908 *
2909 * see TPACPI_FAN_WR_ACPI_SFAN
2910 * EC 0x2f (HFSP) not available if GFAN exists
2911 *
2912 * TPACPI_FAN_WR_ACPI_SFAN:
2913 * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
2914 *
2915 * EC 0x2f (HFSP) might be available *for reading*, but do not use
2916 * it for writing.
2917 *
2918 * TPACPI_FAN_WR_TPEC:
2919 * ThinkPad EC register 0x2f (HFSP): fan control loop mode
2920 * Supported on almost all ThinkPads
2921 *
2922 * Fan speed changes of any sort (including those caused by the
2923 * disengaged mode) are usually done slowly by the firmware as the
2924 * maximum ammount of fan duty cycle change per second seems to be
2925 * limited.
2926 *
2927 * Reading is not available if GFAN exists.
2928 * Writing is not available if SFAN exists.
2929 *
2930 * Bits
2931 * 7 automatic mode engaged;
2932 * (default operation mode of the ThinkPad)
2933 * fan level is ignored in this mode.
2934 * 6 full speed mode (takes precedence over bit 7);
2935 * not available on all thinkpads. May disable
2936 * the tachometer while the fan controller ramps up
2937 * the speed (which can take up to a few *minutes*).
2938 * Speeds up fan to 100% duty-cycle, which is far above
2939 * the standard RPM levels. It is not impossible that
2940 * it could cause hardware damage.
2941 * 5-3 unused in some models. Extra bits for fan level
2942 * in others, but still useless as all values above
2943 * 7 map to the same speed as level 7 in these models.
2944 * 2-0 fan level (0..7 usually)
2945 * 0x00 = stop
2946 * 0x07 = max (set when temperatures critical)
2947 * Some ThinkPads may have other levels, see
2948 * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
2949 *
2950 * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
2951 * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
2952 * does so, its initial value is meaningless (0x07).
2953 *
2954 * For firmware bugs, refer to:
2955 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
2956 *
2957 * ----
2958 *
2959 * ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
2960 * Main fan tachometer reading (in RPM)
2961 *
2962 * This register is present on all ThinkPads with a new-style EC, and
2963 * it is known not to be present on the A21m/e, and T22, as there is
2964 * something else in offset 0x84 according to the ACPI DSDT. Other
2965 * ThinkPads from this same time period (and earlier) probably lack the
2966 * tachometer as well.
2967 *
2968 * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
2969 * was never fixed by IBM to report the EC firmware version string
2970 * probably support the tachometer (like the early X models), so
2971 * detecting it is quite hard. We need more data to know for sure.
2972 *
2973 * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
2974 * might result.
2975 *
2976 * FIRMWARE BUG: may go stale while the EC is switching to full speed
2977 * mode.
2978 *
2979 * For firmware bugs, refer to:
2980 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
2981 *
2982 * TPACPI_FAN_WR_ACPI_FANS:
2983 * ThinkPad X31, X40, X41. Not available in the X60.
2984 *
2985 * FANS ACPI handle: takes three arguments: low speed, medium speed,
2986 * high speed. ACPI DSDT seems to map these three speeds to levels
2987 * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
2988 * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
2989 *
2990 * The speeds are stored on handles
2991 * (FANA:FAN9), (FANC:FANB), (FANE:FAND).
2992 *
2993 * There are three default speed sets, acessible as handles:
2994 * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
2995 *
2996 * ACPI DSDT switches which set is in use depending on various
2997 * factors.
2998 *
2999 * TPACPI_FAN_WR_TPEC is also available and should be used to
3000 * command the fan. The X31/X40/X41 seems to have 8 fan levels,
3001 * but the ACPI tables just mention level 7.
3002 */
3003
3004static enum fan_status_access_mode fan_status_access_mode;
3005static enum fan_control_access_mode fan_control_access_mode;
3006static enum fan_control_commands fan_control_commands;
3007
3008static u8 fan_control_initial_status;
3009static u8 fan_control_desired_level;
3010
3011static void fan_watchdog_fire(struct work_struct *ignored);
3012static int fan_watchdog_maxinterval;
3013static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
3014
3015IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
3016IBM_HANDLE(gfan, ec, "GFAN", /* 570 */
3017 "\\FSPD", /* 600e/x, 770e, 770x */
3018 ); /* all others */
3019IBM_HANDLE(sfan, ec, "SFAN", /* 570 */
3020 "JFNS", /* 770x-JL */
3021 ); /* all others */
3022
3023/*
3024 * SYSFS fan layout: hwmon compatible (device)
3025 *
3026 * pwm*_enable:
3027 * 0: "disengaged" mode
3028 * 1: manual mode
3029 * 2: native EC "auto" mode (recommended, hardware default)
3030 *
3031 * pwm*: set speed in manual mode, ignored otherwise.
3032 * 0 is level 0; 255 is level 7. Intermediate points done with linear
3033 * interpolation.
3034 *
3035 * fan*_input: tachometer reading, RPM
3036 *
3037 *
3038 * SYSFS fan layout: extensions
3039 *
3040 * fan_watchdog (driver):
3041 * fan watchdog interval in seconds, 0 disables (default), max 120
3042 */
3043
3044/* sysfs fan pwm1_enable ----------------------------------------------- */
3045static ssize_t fan_pwm1_enable_show(struct device *dev,
3046 struct device_attribute *attr,
3047 char *buf)
3048{
3049 int res, mode;
3050 u8 status;
3051
3052 res = fan_get_status_safe(&status);
3053 if (res)
3054 return res;
3055
3056 if (unlikely(tp_features.fan_ctrl_status_undef)) {
3057 if (status != fan_control_initial_status) {
3058 tp_features.fan_ctrl_status_undef = 0;
3059 } else {
3060 /* Return most likely status. In fact, it
3061 * might be the only possible status */
3062 status = TP_EC_FAN_AUTO;
3063 }
3064 }
3065
3066 if (status & TP_EC_FAN_FULLSPEED) {
3067 mode = 0;
3068 } else if (status & TP_EC_FAN_AUTO) {
3069 mode = 2;
3070 } else
3071 mode = 1;
3072
3073 return snprintf(buf, PAGE_SIZE, "%d\n", mode);
3074}
3075
3076static ssize_t fan_pwm1_enable_store(struct device *dev,
3077 struct device_attribute *attr,
3078 const char *buf, size_t count)
3079{
3080 unsigned long t;
3081 int res, level;
3082
3083 if (parse_strtoul(buf, 2, &t))
3084 return -EINVAL;
3085
3086 switch (t) {
3087 case 0:
3088 level = TP_EC_FAN_FULLSPEED;
3089 break;
3090 case 1:
3091 level = TPACPI_FAN_LAST_LEVEL;
3092 break;
3093 case 2:
3094 level = TP_EC_FAN_AUTO;
3095 break;
3096 case 3:
3097 /* reserved for software-controlled auto mode */
3098 return -ENOSYS;
3099 default:
3100 return -EINVAL;
3101 }
3102
3103 res = fan_set_level_safe(level);
3104 if (res == -ENXIO)
3105 return -EINVAL;
3106 else if (res < 0)
3107 return res;
3108
3109 fan_watchdog_reset();
3110
3111 return count;
3112}
3113
3114static struct device_attribute dev_attr_fan_pwm1_enable =
3115 __ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
3116 fan_pwm1_enable_show, fan_pwm1_enable_store);
3117
3118/* sysfs fan pwm1 ------------------------------------------------------ */
3119static ssize_t fan_pwm1_show(struct device *dev,
3120 struct device_attribute *attr,
3121 char *buf)
3122{
3123 int res;
3124 u8 status;
3125
3126 res = fan_get_status_safe(&status);
3127 if (res)
3128 return res;
3129
3130 if (unlikely(tp_features.fan_ctrl_status_undef)) {
3131 if (status != fan_control_initial_status) {
3132 tp_features.fan_ctrl_status_undef = 0;
3133 } else {
3134 status = TP_EC_FAN_AUTO;
3135 }
3136 }
3137
3138 if ((status &
3139 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0)
3140 status = fan_control_desired_level;
3141
3142 if (status > 7)
3143 status = 7;
3144
3145 return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7);
3146}
3147
3148static ssize_t fan_pwm1_store(struct device *dev,
3149 struct device_attribute *attr,
3150 const char *buf, size_t count)
3151{
3152 unsigned long s;
3153 int rc;
3154 u8 status, newlevel;
3155
3156 if (parse_strtoul(buf, 255, &s))
3157 return -EINVAL;
3158
3159 /* scale down from 0-255 to 0-7 */
3160 newlevel = (s >> 5) & 0x07;
3161
3162 rc = mutex_lock_interruptible(&fan_mutex);
3163 if (rc < 0)
3164 return rc;
3165
3166 rc = fan_get_status(&status);
3167 if (!rc && (status &
3168 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
3169 rc = fan_set_level(newlevel);
3170 if (rc == -ENXIO)
3171 rc = -EINVAL;
3172 else if (!rc) {
3173 fan_update_desired_level(newlevel);
3174 fan_watchdog_reset();
3175 }
3176 }
3177
3178 mutex_unlock(&fan_mutex);
3179 return (rc)? rc : count;
3180}
3181
3182static struct device_attribute dev_attr_fan_pwm1 =
3183 __ATTR(pwm1, S_IWUSR | S_IRUGO,
3184 fan_pwm1_show, fan_pwm1_store);
3185
3186/* sysfs fan fan1_input ------------------------------------------------ */
3187static ssize_t fan_fan1_input_show(struct device *dev,
3188 struct device_attribute *attr,
3189 char *buf)
3190{
3191 int res;
3192 unsigned int speed;
3193
3194 res = fan_get_speed(&speed);
3195 if (res < 0)
3196 return res;
3197
3198 return snprintf(buf, PAGE_SIZE, "%u\n", speed);
3199}
3200
3201static struct device_attribute dev_attr_fan_fan1_input =
3202 __ATTR(fan1_input, S_IRUGO,
3203 fan_fan1_input_show, NULL);
3204
3205/* sysfs fan fan_watchdog (driver) ------------------------------------- */
3206static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
3207 char *buf)
3208{
3209 return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
3210}
3211
3212static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
3213 const char *buf, size_t count)
3214{
3215 unsigned long t;
3216
3217 if (parse_strtoul(buf, 120, &t))
3218 return -EINVAL;
3219
3220 if (!fan_control_allowed)
3221 return -EPERM;
3222
3223 fan_watchdog_maxinterval = t;
3224 fan_watchdog_reset();
3225
3226 return count;
3227}
3228
3229static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
3230 fan_fan_watchdog_show, fan_fan_watchdog_store);
3231
3232/* --------------------------------------------------------------------- */
3233static struct attribute *fan_attributes[] = {
3234 &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
3235 &dev_attr_fan_fan1_input.attr,
3236 NULL
3237};
3238
3239static const struct attribute_group fan_attr_group = {
3240 .attrs = fan_attributes,
3241};
3242
3243static int __init fan_init(struct ibm_init_struct *iibm)
3244{
3245 int rc;
3246
3247 vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n");
3248
3249 mutex_init(&fan_mutex);
3250 fan_status_access_mode = TPACPI_FAN_NONE;
3251 fan_control_access_mode = TPACPI_FAN_WR_NONE;
3252 fan_control_commands = 0;
3253 fan_watchdog_maxinterval = 0;
3254 tp_features.fan_ctrl_status_undef = 0;
3255 fan_control_desired_level = 7;
3256
3257 IBM_ACPIHANDLE_INIT(fans);
3258 IBM_ACPIHANDLE_INIT(gfan);
3259 IBM_ACPIHANDLE_INIT(sfan);
3260
3261 if (gfan_handle) {
3262 /* 570, 600e/x, 770e, 770x */
3263 fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
3264 } else {
3265 /* all other ThinkPads: note that even old-style
3266 * ThinkPad ECs supports the fan control register */
3267 if (likely(acpi_ec_read(fan_status_offset,
3268 &fan_control_initial_status))) {
3269 fan_status_access_mode = TPACPI_FAN_RD_TPEC;
3270
3271 /* In some ThinkPads, neither the EC nor the ACPI
3272 * DSDT initialize the fan status, and it ends up
3273 * being set to 0x07 when it *could* be either
3274 * 0x07 or 0x80.
3275 *
3276 * Enable for TP-1Y (T43), TP-78 (R51e),
3277 * TP-76 (R52), TP-70 (T43, R52), which are known
3278 * to be buggy. */
3279 if (fan_control_initial_status == 0x07 &&
3280 ibm_thinkpad_ec_found &&
3281 ((ibm_thinkpad_ec_found[0] == '1' &&
3282 ibm_thinkpad_ec_found[1] == 'Y') ||
3283 (ibm_thinkpad_ec_found[0] == '7' &&
3284 (ibm_thinkpad_ec_found[1] == '6' ||
3285 ibm_thinkpad_ec_found[1] == '8' ||
3286 ibm_thinkpad_ec_found[1] == '0'))
3287 )) {
3288 printk(IBM_NOTICE
3289 "fan_init: initial fan status is "
3290 "unknown, assuming it is in auto "
3291 "mode\n");
3292 tp_features.fan_ctrl_status_undef = 1;
3293 }
3294 } else {
3295 printk(IBM_ERR
3296 "ThinkPad ACPI EC access misbehaving, "
3297 "fan status and control unavailable\n");
3298 return 1;
3299 }
3300 }
3301
3302 if (sfan_handle) {
3303 /* 570, 770x-JL */
3304 fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
3305 fan_control_commands |=
3306 TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
3307 } else {
3308 if (!gfan_handle) {
3309 /* gfan without sfan means no fan control */
3310 /* all other models implement TP EC 0x2f control */
3311
3312 if (fans_handle) {
3313 /* X31, X40, X41 */
3314 fan_control_access_mode =
3315 TPACPI_FAN_WR_ACPI_FANS;
3316 fan_control_commands |=
3317 TPACPI_FAN_CMD_SPEED |
3318 TPACPI_FAN_CMD_LEVEL |
3319 TPACPI_FAN_CMD_ENABLE;
3320 } else {
3321 fan_control_access_mode = TPACPI_FAN_WR_TPEC;
3322 fan_control_commands |=
3323 TPACPI_FAN_CMD_LEVEL |
3324 TPACPI_FAN_CMD_ENABLE;
3325 }
3326 }
3327 }
3328
3329 vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n",
3330 str_supported(fan_status_access_mode != TPACPI_FAN_NONE ||
3331 fan_control_access_mode != TPACPI_FAN_WR_NONE),
3332 fan_status_access_mode, fan_control_access_mode);
3333
3334 /* fan control master switch */
3335 if (!fan_control_allowed) {
3336 fan_control_access_mode = TPACPI_FAN_WR_NONE;
3337 fan_control_commands = 0;
3338 dbg_printk(TPACPI_DBG_INIT,
3339 "fan control features disabled by parameter\n");
3340 }
3341
3342 /* update fan_control_desired_level */
3343 if (fan_status_access_mode != TPACPI_FAN_NONE)
3344 fan_get_status_safe(NULL);
3345
3346 if (fan_status_access_mode != TPACPI_FAN_NONE ||
3347 fan_control_access_mode != TPACPI_FAN_WR_NONE) {
3348 rc = sysfs_create_group(&tpacpi_pdev->dev.kobj,
3349 &fan_attr_group);
3350 if (!(rc < 0))
3351 rc = driver_create_file(&tpacpi_pdriver.driver,
3352 &driver_attr_fan_watchdog);
3353 if (rc < 0)
3354 return rc;
3355 return 0;
3356 } else
3357 return 1;
3358}
3359
3360/*
3361 * Call with fan_mutex held
3362 */
3363static void fan_update_desired_level(u8 status)
3364{
3365 if ((status &
3366 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
3367 if (status > 7)
3368 fan_control_desired_level = 7;
3369 else
3370 fan_control_desired_level = status;
3371 }
3372}
3373
3374static int fan_get_status(u8 *status)
3375{
3376 u8 s;
3377
3378 /* TODO:
3379 * Add TPACPI_FAN_RD_ACPI_FANS ? */
3380
3381 switch (fan_status_access_mode) {
3382 case TPACPI_FAN_RD_ACPI_GFAN:
3383 /* 570, 600e/x, 770e, 770x */
3384
3385 if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
3386 return -EIO;
3387
3388 if (likely(status))
3389 *status = s & 0x07;
3390
3391 break;
3392
3393 case TPACPI_FAN_RD_TPEC:
3394 /* all except 570, 600e/x, 770e, 770x */
3395 if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
3396 return -EIO;
3397
3398 if (likely(status))
3399 *status = s;
3400
3401 break;
3402
3403 default:
3404 return -ENXIO;
3405 }
3406
3407 return 0;
3408}
3409
3410static int fan_get_status_safe(u8 *status)
3411{
3412 int rc;
3413 u8 s;
3414
3415 rc = mutex_lock_interruptible(&fan_mutex);
3416 if (rc < 0)
3417 return rc;
3418 rc = fan_get_status(&s);
3419 if (!rc)
3420 fan_update_desired_level(s);
3421 mutex_unlock(&fan_mutex);
3422
3423 if (status)
3424 *status = s;
3425
3426 return rc;
3427}
3428
3429static void fan_exit(void)
3430{
3431 vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n");
3432
3433 /* FIXME: can we really do this unconditionally? */
3434 sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group);
3435 driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog);
3436
3437 cancel_delayed_work(&fan_watchdog_task);
3438 flush_scheduled_work();
3439}
3440
3441static int fan_get_speed(unsigned int *speed)
3442{
3443 u8 hi, lo;
3444
3445 switch (fan_status_access_mode) {
3446 case TPACPI_FAN_RD_TPEC:
3447 /* all except 570, 600e/x, 770e, 770x */
3448 if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
3449 !acpi_ec_read(fan_rpm_offset + 1, &hi)))
3450 return -EIO;
3451
3452 if (likely(speed))
3453 *speed = (hi << 8) | lo;
3454
3455 break;
3456
3457 default:
3458 return -ENXIO;
3459 }
3460
3461 return 0;
3462}
3463
3464static void fan_watchdog_fire(struct work_struct *ignored)
3465{
3466 int rc;
3467
3468 printk(IBM_NOTICE "fan watchdog: enabling fan\n");
3469 rc = fan_set_enable();
3470 if (rc < 0) {
3471 printk(IBM_ERR "fan watchdog: error %d while enabling fan, "
3472 "will try again later...\n", -rc);
3473 /* reschedule for later */
3474 fan_watchdog_reset();
3475 }
3476}
3477
3478static void fan_watchdog_reset(void)
3479{
3480 static int fan_watchdog_active = 0;
3481
3482 if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
3483 return;
3484
3485 if (fan_watchdog_active)
3486 cancel_delayed_work(&fan_watchdog_task);
3487
3488 if (fan_watchdog_maxinterval > 0) {
3489 fan_watchdog_active = 1;
3490 if (!schedule_delayed_work(&fan_watchdog_task,
3491 msecs_to_jiffies(fan_watchdog_maxinterval
3492 * 1000))) {
3493 printk(IBM_ERR "failed to schedule the fan watchdog, "
3494 "watchdog will not trigger\n");
3495 }
3496 } else
3497 fan_watchdog_active = 0;
3498}
3499
3500static int fan_set_level(int level)
3501{
3502 if (!fan_control_allowed)
3503 return -EPERM;
3504
3505 switch (fan_control_access_mode) {
3506 case TPACPI_FAN_WR_ACPI_SFAN:
3507 if (level >= 0 && level <= 7) {
3508 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
3509 return -EIO;
3510 } else
3511 return -EINVAL;
3512 break;
3513
3514 case TPACPI_FAN_WR_ACPI_FANS:
3515 case TPACPI_FAN_WR_TPEC:
3516 if ((level != TP_EC_FAN_AUTO) &&
3517 (level != TP_EC_FAN_FULLSPEED) &&
3518 ((level < 0) || (level > 7)))
3519 return -EINVAL;
3520
3521 /* safety net should the EC not support AUTO
3522 * or FULLSPEED mode bits and just ignore them */
3523 if (level & TP_EC_FAN_FULLSPEED)
3524 level |= 7; /* safety min speed 7 */
3525 else if (level & TP_EC_FAN_FULLSPEED)
3526 level |= 4; /* safety min speed 4 */
3527
3528 if (!acpi_ec_write(fan_status_offset, level))
3529 return -EIO;
3530 else
3531 tp_features.fan_ctrl_status_undef = 0;
3532 break;
3533
3534 default:
3535 return -ENXIO;
3536 }
3537 return 0;
3538}
3539
3540static int fan_set_level_safe(int level)
3541{
3542 int rc;
3543
3544 if (!fan_control_allowed)
3545 return -EPERM;
3546
3547 rc = mutex_lock_interruptible(&fan_mutex);
3548 if (rc < 0)
3549 return rc;
3550
3551 if (level == TPACPI_FAN_LAST_LEVEL)
3552 level = fan_control_desired_level;
3553
3554 rc = fan_set_level(level);
3555 if (!rc)
3556 fan_update_desired_level(level);
3557
3558 mutex_unlock(&fan_mutex);
3559 return rc;
3560}
3561
3562static int fan_set_enable(void)
3563{
3564 u8 s;
3565 int rc;
3566
3567 if (!fan_control_allowed)
3568 return -EPERM;
3569
3570 rc = mutex_lock_interruptible(&fan_mutex);
3571 if (rc < 0)
3572 return rc;
3573
3574 switch (fan_control_access_mode) {
3575 case TPACPI_FAN_WR_ACPI_FANS:
3576 case TPACPI_FAN_WR_TPEC:
3577 rc = fan_get_status(&s);
3578 if (rc < 0)
3579 break;
3580
3581 /* Don't go out of emergency fan mode */
3582 if (s != 7) {
3583 s &= 0x07;
3584 s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */
3585 }
3586
3587 if (!acpi_ec_write(fan_status_offset, s))
3588 rc = -EIO;
3589 else {
3590 tp_features.fan_ctrl_status_undef = 0;
3591 rc = 0;
3592 }
3593 break;
3594
3595 case TPACPI_FAN_WR_ACPI_SFAN:
3596 rc = fan_get_status(&s);
3597 if (rc < 0)
3598 break;
3599
3600 s &= 0x07;
3601
3602 /* Set fan to at least level 4 */
3603 s |= 4;
3604
3605 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
3606 rc= -EIO;
3607 else
3608 rc = 0;
3609 break;
3610
3611 default:
3612 rc = -ENXIO;
3613 }
3614
3615 mutex_unlock(&fan_mutex);
3616 return rc;
3617}
3618
3619static int fan_set_disable(void)
3620{
3621 int rc;
3622
3623 if (!fan_control_allowed)
3624 return -EPERM;
3625
3626 rc = mutex_lock_interruptible(&fan_mutex);
3627 if (rc < 0)
3628 return rc;
3629
3630 rc = 0;
3631 switch (fan_control_access_mode) {
3632 case TPACPI_FAN_WR_ACPI_FANS:
3633 case TPACPI_FAN_WR_TPEC:
3634 if (!acpi_ec_write(fan_status_offset, 0x00))
3635 rc = -EIO;
3636 else {
3637 fan_control_desired_level = 0;
3638 tp_features.fan_ctrl_status_undef = 0;
3639 }
3640 break;
3641
3642 case TPACPI_FAN_WR_ACPI_SFAN:
3643 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
3644 rc = -EIO;
3645 else
3646 fan_control_desired_level = 0;
3647 break;
3648
3649 default:
3650 rc = -ENXIO;
3651 }
3652
3653
3654 mutex_unlock(&fan_mutex);
3655 return rc;
3656}
3657
3658static int fan_set_speed(int speed)
3659{
3660 int rc;
3661
3662 if (!fan_control_allowed)
3663 return -EPERM;
3664
3665 rc = mutex_lock_interruptible(&fan_mutex);
3666 if (rc < 0)
3667 return rc;
3668
3669 rc = 0;
3670 switch (fan_control_access_mode) {
3671 case TPACPI_FAN_WR_ACPI_FANS:
3672 if (speed >= 0 && speed <= 65535) {
3673 if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
3674 speed, speed, speed))
3675 rc = -EIO;
3676 } else
3677 rc = -EINVAL;
3678 break;
3679
3680 default:
3681 rc = -ENXIO;
3682 }
3683
3684 mutex_unlock(&fan_mutex);
3685 return rc;
3686}
3687
3688static int fan_read(char *p)
3689{
3690 int len = 0;
3691 int rc;
3692 u8 status;
3693 unsigned int speed = 0;
3694
3695 switch (fan_status_access_mode) {
3696 case TPACPI_FAN_RD_ACPI_GFAN:
3697 /* 570, 600e/x, 770e, 770x */
3698 if ((rc = fan_get_status_safe(&status)) < 0)
3699 return rc;
3700
3701 len += sprintf(p + len, "status:\t\t%s\n"
3702 "level:\t\t%d\n",
3703 (status != 0) ? "enabled" : "disabled", status);
3704 break;
3705
3706 case TPACPI_FAN_RD_TPEC:
3707 /* all except 570, 600e/x, 770e, 770x */
3708 if ((rc = fan_get_status_safe(&status)) < 0)
3709 return rc;
3710
3711 if (unlikely(tp_features.fan_ctrl_status_undef)) {
3712 if (status != fan_control_initial_status)
3713 tp_features.fan_ctrl_status_undef = 0;
3714 else
3715 /* Return most likely status. In fact, it
3716 * might be the only possible status */
3717 status = TP_EC_FAN_AUTO;
3718 }
3719
3720 len += sprintf(p + len, "status:\t\t%s\n",
3721 (status != 0) ? "enabled" : "disabled");
3722
3723 if ((rc = fan_get_speed(&speed)) < 0)
3724 return rc;
3725
3726 len += sprintf(p + len, "speed:\t\t%d\n", speed);
3727
3728 if (status & TP_EC_FAN_FULLSPEED)
3729 /* Disengaged mode takes precedence */
3730 len += sprintf(p + len, "level:\t\tdisengaged\n");
3731 else if (status & TP_EC_FAN_AUTO)
3732 len += sprintf(p + len, "level:\t\tauto\n");
3733 else
3734 len += sprintf(p + len, "level:\t\t%d\n", status);
3735 break;
3736
3737 case TPACPI_FAN_NONE:
3738 default:
3739 len += sprintf(p + len, "status:\t\tnot supported\n");
3740 }
3741
3742 if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
3743 len += sprintf(p + len, "commands:\tlevel <level>");
3744
3745 switch (fan_control_access_mode) {
3746 case TPACPI_FAN_WR_ACPI_SFAN:
3747 len += sprintf(p + len, " (<level> is 0-7)\n");
3748 break;
3749
3750 default:
3751 len += sprintf(p + len, " (<level> is 0-7, "
3752 "auto, disengaged, full-speed)\n");
3753 break;
3754 }
3755 }
3756
3757 if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
3758 len += sprintf(p + len, "commands:\tenable, disable\n"
3759 "commands:\twatchdog <timeout> (<timeout> is 0 (off), "
3760 "1-120 (seconds))\n");
3761
3762 if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
3763 len += sprintf(p + len, "commands:\tspeed <speed>"
3764 " (<speed> is 0-65535)\n");
3765
3766 return len;
3767}
3768
3769static int fan_write_cmd_level(const char *cmd, int *rc)
3770{
3771 int level;
3772
3773 if (strlencmp(cmd, "level auto") == 0)
3774 level = TP_EC_FAN_AUTO;
3775 else if ((strlencmp(cmd, "level disengaged") == 0) |
3776 (strlencmp(cmd, "level full-speed") == 0))
3777 level = TP_EC_FAN_FULLSPEED;
3778 else if (sscanf(cmd, "level %d", &level) != 1)
3779 return 0;
3780
3781 if ((*rc = fan_set_level_safe(level)) == -ENXIO)
3782 printk(IBM_ERR "level command accepted for unsupported "
3783 "access mode %d", fan_control_access_mode);
3784
3785 return 1;
3786}
3787
3788static int fan_write_cmd_enable(const char *cmd, int *rc)
3789{
3790 if (strlencmp(cmd, "enable") != 0)
3791 return 0;
3792
3793 if ((*rc = fan_set_enable()) == -ENXIO)
3794 printk(IBM_ERR "enable command accepted for unsupported "
3795 "access mode %d", fan_control_access_mode);
3796
3797 return 1;
3798}
3799
3800static int fan_write_cmd_disable(const char *cmd, int *rc)
3801{
3802 if (strlencmp(cmd, "disable") != 0)
3803 return 0;
3804
3805 if ((*rc = fan_set_disable()) == -ENXIO)
3806 printk(IBM_ERR "disable command accepted for unsupported "
3807 "access mode %d", fan_control_access_mode);
3808
3809 return 1;
3810}
3811
3812static int fan_write_cmd_speed(const char *cmd, int *rc)
3813{
3814 int speed;
3815
3816 /* TODO:
3817 * Support speed <low> <medium> <high> ? */
3818
3819 if (sscanf(cmd, "speed %d", &speed) != 1)
3820 return 0;
3821
3822 if ((*rc = fan_set_speed(speed)) == -ENXIO)
3823 printk(IBM_ERR "speed command accepted for unsupported "
3824 "access mode %d", fan_control_access_mode);
3825
3826 return 1;
3827}
3828
3829static int fan_write_cmd_watchdog(const char *cmd, int *rc)
3830{
3831 int interval;
3832
3833 if (sscanf(cmd, "watchdog %d", &interval) != 1)
3834 return 0;
3835
3836 if (interval < 0 || interval > 120)
3837 *rc = -EINVAL;
3838 else
3839 fan_watchdog_maxinterval = interval;
3840
3841 return 1;
3842}
3843
3844static int fan_write(char *buf)
3845{
3846 char *cmd;
3847 int rc = 0;
3848
3849 while (!rc && (cmd = next_cmd(&buf))) {
3850 if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
3851 fan_write_cmd_level(cmd, &rc)) &&
3852 !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
3853 (fan_write_cmd_enable(cmd, &rc) ||
3854 fan_write_cmd_disable(cmd, &rc) ||
3855 fan_write_cmd_watchdog(cmd, &rc))) &&
3856 !((fan_control_commands & TPACPI_FAN_CMD_SPEED) &&
3857 fan_write_cmd_speed(cmd, &rc))
3858 )
3859 rc = -EINVAL;
3860 else if (!rc)
3861 fan_watchdog_reset();
3862 }
3863
3864 return rc;
3865}
3866
3867static struct ibm_struct fan_driver_data = {
3868 .name = "fan",
3869 .read = fan_read,
3870 .write = fan_write,
3871 .exit = fan_exit,
3872};
3873
3874/****************************************************************************
3875 ****************************************************************************
3876 *
3877 * Infrastructure
3878 *
3879 ****************************************************************************
3880 ****************************************************************************/
3881
3882/* /proc support */
3883static struct proc_dir_entry *proc_dir = NULL;
3884
3885/* Subdriver registry */
3886static LIST_HEAD(tpacpi_all_drivers);
3887
3888
3889/*
3890 * Module and infrastructure proble, init and exit handling
3891 */
3892
3893#ifdef CONFIG_THINKPAD_ACPI_DEBUG
3894static const char * __init str_supported(int is_supported)
3895{
3896 static char text_unsupported[] __initdata = "not supported";
3897
3898 return (is_supported)? &text_unsupported[4] : &text_unsupported[0];
3899}
3900#endif /* CONFIG_THINKPAD_ACPI_DEBUG */
3901
3902static int __init ibm_init(struct ibm_init_struct *iibm)
3903{
3904 int ret;
3905 struct ibm_struct *ibm = iibm->data;
3906 struct proc_dir_entry *entry;
3907
3908 BUG_ON(ibm == NULL);
3909
3910 INIT_LIST_HEAD(&ibm->all_drivers);
3911
3912 if (ibm->flags.experimental && !experimental)
3913 return 0;
3914
3915 dbg_printk(TPACPI_DBG_INIT,
3916 "probing for %s\n", ibm->name);
3917
3918 if (iibm->init) {
3919 ret = iibm->init(iibm);
3920 if (ret > 0)
3921 return 0; /* probe failed */
3922 if (ret)
3923 return ret;
3924
3925 ibm->flags.init_called = 1;
3926 }
3927
3928 if (ibm->acpi) {
3929 if (ibm->acpi->hid) {
3930 ret = register_tpacpi_subdriver(ibm);
3931 if (ret)
3932 goto err_out;
3933 }
3934
3935 if (ibm->acpi->notify) {
3936 ret = setup_acpi_notify(ibm);
3937 if (ret == -ENODEV) {
3938 printk(IBM_NOTICE "disabling subdriver %s\n",
3939 ibm->name);
3940 ret = 0;
3941 goto err_out;
3942 }
3943 if (ret < 0)
3944 goto err_out;
3945 }
3946 }
3947
3948 dbg_printk(TPACPI_DBG_INIT,
3949 "%s installed\n", ibm->name);
3950
3951 if (ibm->read) {
3952 entry = create_proc_entry(ibm->name,
3953 S_IFREG | S_IRUGO | S_IWUSR,
3954 proc_dir);
3955 if (!entry) {
3956 printk(IBM_ERR "unable to create proc entry %s\n",
3957 ibm->name);
3958 ret = -ENODEV;
3959 goto err_out;
3960 }
3961 entry->owner = THIS_MODULE;
3962 entry->data = ibm;
3963 entry->read_proc = &dispatch_procfs_read;
3964 if (ibm->write)
3965 entry->write_proc = &dispatch_procfs_write;
3966 ibm->flags.proc_created = 1;
3967 }
3968
3969 list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers);
3970
3971 return 0;
3972
3973err_out:
3974 dbg_printk(TPACPI_DBG_INIT,
3975 "%s: at error exit path with result %d\n",
3976 ibm->name, ret);
3977
3978 ibm_exit(ibm);
3979 return (ret < 0)? ret : 0;
3980}
3981
3982static void ibm_exit(struct ibm_struct *ibm)
3983{
3984 dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name);
3985
3986 list_del_init(&ibm->all_drivers);
3987
3988 if (ibm->flags.acpi_notify_installed) {
3989 dbg_printk(TPACPI_DBG_EXIT,
3990 "%s: acpi_remove_notify_handler\n", ibm->name);
3991 BUG_ON(!ibm->acpi);
3992 acpi_remove_notify_handler(*ibm->acpi->handle,
3993 ibm->acpi->type,
3994 dispatch_acpi_notify);
3995 ibm->flags.acpi_notify_installed = 0;
3996 ibm->flags.acpi_notify_installed = 0;
3997 }
3998
3999 if (ibm->flags.proc_created) {
4000 dbg_printk(TPACPI_DBG_EXIT,
4001 "%s: remove_proc_entry\n", ibm->name);
4002 remove_proc_entry(ibm->name, proc_dir);
4003 ibm->flags.proc_created = 0;
4004 }
4005
4006 if (ibm->flags.acpi_driver_registered) {
4007 dbg_printk(TPACPI_DBG_EXIT,
4008 "%s: acpi_bus_unregister_driver\n", ibm->name);
4009 BUG_ON(!ibm->acpi);
4010 acpi_bus_unregister_driver(ibm->acpi->driver);
4011 kfree(ibm->acpi->driver);
4012 ibm->acpi->driver = NULL;
4013 ibm->flags.acpi_driver_registered = 0;
4014 }
4015
4016 if (ibm->flags.init_called && ibm->exit) {
4017 ibm->exit();
4018 ibm->flags.init_called = 0;
4019 }
4020
4021 dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name);
4022}
4023
4024/* Probing */
4025
4026static char *ibm_thinkpad_ec_found = NULL;
4027
4028static char* __init check_dmi_for_ec(void)
4029{
4030 struct dmi_device *dev = NULL;
4031 char ec_fw_string[18];
4032
4033 /*
4034 * ThinkPad T23 or newer, A31 or newer, R50e or newer,
4035 * X32 or newer, all Z series; Some models must have an
4036 * up-to-date BIOS or they will not be detected.
4037 *
4038 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
4039 */
4040 while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
4041 if (sscanf(dev->name,
4042 "IBM ThinkPad Embedded Controller -[%17c",
4043 ec_fw_string) == 1) {
4044 ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
4045 ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
4046 return kstrdup(ec_fw_string, GFP_KERNEL);
4047 }
4048 }
4049 return NULL;
4050}
4051
4052static int __init probe_for_thinkpad(void)
4053{
4054 int is_thinkpad;
4055
4056 if (acpi_disabled)
4057 return -ENODEV;
4058
4059 /*
4060 * Non-ancient models have better DMI tagging, but very old models
4061 * don't.
4062 */
4063 is_thinkpad = dmi_name_in_vendors("ThinkPad");
4064
4065 /* ec is required because many other handles are relative to it */
4066 IBM_ACPIHANDLE_INIT(ec);
4067 if (!ec_handle) {
4068 if (is_thinkpad)
4069 printk(IBM_ERR
4070 "Not yet supported ThinkPad detected!\n");
4071 return -ENODEV;
4072 }
4073
4074 /*
4075 * Risks a regression on very old machines, but reduces potential
4076 * false positives a damn great deal
4077 */
4078 if (!is_thinkpad)
4079 is_thinkpad = dmi_name_in_vendors("IBM");
4080
4081 if (!is_thinkpad && !force_load)
4082 return -ENODEV;
4083
4084 return 0;
4085}
4086
4087
4088/* Module init, exit, parameters */
4089
4090static struct ibm_init_struct ibms_init[] __initdata = {
4091 {
4092 .init = thinkpad_acpi_driver_init,
4093 .data = &thinkpad_acpi_driver_data,
4094 },
4095 {
4096 .init = hotkey_init,
4097 .data = &hotkey_driver_data,
4098 },
4099 {
4100 .init = bluetooth_init,
4101 .data = &bluetooth_driver_data,
4102 },
4103 {
4104 .init = wan_init,
4105 .data = &wan_driver_data,
4106 },
4107 {
4108 .init = video_init,
4109 .data = &video_driver_data,
4110 },
4111 {
4112 .init = light_init,
4113 .data = &light_driver_data,
4114 },
4115#ifdef CONFIG_THINKPAD_ACPI_DOCK
4116 {
4117 .init = dock_init,
4118 .data = &dock_driver_data[0],
4119 },
4120 {
4121 .init = dock_init2,
4122 .data = &dock_driver_data[1],
4123 },
4124#endif
4125#ifdef CONFIG_THINKPAD_ACPI_BAY
4126 {
4127 .init = bay_init,
4128 .data = &bay_driver_data,
4129 },
4130#endif
4131 {
4132 .init = cmos_init,
4133 .data = &cmos_driver_data,
4134 },
4135 {
4136 .init = led_init,
4137 .data = &led_driver_data,
4138 },
4139 {
4140 .init = beep_init,
4141 .data = &beep_driver_data,
4142 },
4143 {
4144 .init = thermal_init,
4145 .data = &thermal_driver_data,
4146 },
4147 {
4148 .data = &ecdump_driver_data,
4149 },
4150 {
4151 .init = brightness_init,
4152 .data = &brightness_driver_data,
4153 },
4154 {
4155 .data = &volume_driver_data,
4156 },
4157 {
4158 .init = fan_init,
4159 .data = &fan_driver_data,
4160 },
4161};
4162
4163static int __init set_ibm_param(const char *val, struct kernel_param *kp)
4164{
4165 unsigned int i;
4166 struct ibm_struct *ibm;
4167
4168 for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
4169 ibm = ibms_init[i].data;
4170 BUG_ON(ibm == NULL);
4171
4172 if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
4173 if (strlen(val) > sizeof(ibms_init[i].param) - 2)
4174 return -ENOSPC;
4175 strcpy(ibms_init[i].param, val);
4176 strcat(ibms_init[i].param, ",");
4177 return 0;
4178 }
4179 }
4180
4181 return -EINVAL;
4182}
4183
4184static int experimental;
4185module_param(experimental, int, 0);
4186
4187static u32 dbg_level;
4188module_param_named(debug, dbg_level, uint, 0);
4189
4190static int force_load;
4191module_param(force_load, int, 0);
4192
4193static int fan_control_allowed;
4194module_param_named(fan_control, fan_control_allowed, int, 0);
4195
4196#define IBM_PARAM(feature) \
4197 module_param_call(feature, set_ibm_param, NULL, NULL, 0)
4198
4199IBM_PARAM(hotkey);
4200IBM_PARAM(bluetooth);
4201IBM_PARAM(video);
4202IBM_PARAM(light);
4203#ifdef CONFIG_THINKPAD_ACPI_DOCK
4204IBM_PARAM(dock);
4205#endif
4206#ifdef CONFIG_THINKPAD_ACPI_BAY
4207IBM_PARAM(bay);
4208#endif /* CONFIG_THINKPAD_ACPI_BAY */
4209IBM_PARAM(cmos);
4210IBM_PARAM(led);
4211IBM_PARAM(beep);
4212IBM_PARAM(ecdump);
4213IBM_PARAM(brightness);
4214IBM_PARAM(volume);
4215IBM_PARAM(fan);
4216
4217static int __init thinkpad_acpi_module_init(void)
4218{
4219 int ret, i;
4220
4221 /* Driver-level probe */
4222 ret = probe_for_thinkpad();
4223 if (ret)
4224 return ret;
4225
4226 /* Driver initialization */
4227 ibm_thinkpad_ec_found = check_dmi_for_ec();
4228 IBM_ACPIHANDLE_INIT(ecrd);
4229 IBM_ACPIHANDLE_INIT(ecwr);
4230
4231 proc_dir = proc_mkdir(IBM_PROC_DIR, acpi_root_dir);
4232 if (!proc_dir) {
4233 printk(IBM_ERR "unable to create proc dir " IBM_PROC_DIR);
4234 thinkpad_acpi_module_exit();
4235 return -ENODEV;
4236 }
4237 proc_dir->owner = THIS_MODULE;
4238
4239 ret = platform_driver_register(&tpacpi_pdriver);
4240 if (ret) {
4241 printk(IBM_ERR "unable to register platform driver\n");
4242 thinkpad_acpi_module_exit();
4243 return ret;
4244 }
4245 ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
4246 if (ret) {
4247 printk(IBM_ERR "unable to create sysfs driver attributes\n");
4248 thinkpad_acpi_module_exit();
4249 return ret;
4250 }
4251
4252
4253 /* Device initialization */
4254 tpacpi_pdev = platform_device_register_simple(IBM_DRVR_NAME, -1,
4255 NULL, 0);
4256 if (IS_ERR(tpacpi_pdev)) {
4257 ret = PTR_ERR(tpacpi_pdev);
4258 tpacpi_pdev = NULL;
4259 printk(IBM_ERR "unable to register platform device\n");
4260 thinkpad_acpi_module_exit();
4261 return ret;
4262 }
4263 tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev);
4264 if (IS_ERR(tpacpi_hwmon)) {
4265 ret = PTR_ERR(tpacpi_hwmon);
4266 tpacpi_hwmon = NULL;
4267 printk(IBM_ERR "unable to register hwmon device\n");
4268 thinkpad_acpi_module_exit();
4269 return ret;
4270 }
4271 for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
4272 ret = ibm_init(&ibms_init[i]);
4273 if (ret >= 0 && *ibms_init[i].param)
4274 ret = ibms_init[i].data->write(ibms_init[i].param);
4275 if (ret < 0) {
4276 thinkpad_acpi_module_exit();
4277 return ret;
4278 }
4279 }
4280
4281 return 0;
4282}
4283
4284static void thinkpad_acpi_module_exit(void)
4285{
4286 struct ibm_struct *ibm, *itmp;
4287
4288 list_for_each_entry_safe_reverse(ibm, itmp,
4289 &tpacpi_all_drivers,
4290 all_drivers) {
4291 ibm_exit(ibm);
4292 }
4293
4294 dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
4295
4296 if (tpacpi_hwmon)
4297 hwmon_device_unregister(tpacpi_hwmon);
4298
4299 if (tpacpi_pdev)
4300 platform_device_unregister(tpacpi_pdev);
4301
4302 tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
4303 platform_driver_unregister(&tpacpi_pdriver);
4304
4305 if (proc_dir)
4306 remove_proc_entry(IBM_PROC_DIR, acpi_root_dir);
4307
4308 kfree(ibm_thinkpad_ec_found);
4309}
4310
4311module_init(thinkpad_acpi_module_init);
4312module_exit(thinkpad_acpi_module_exit);
diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h
new file mode 100644
index 000000000000..440145a02617
--- /dev/null
+++ b/drivers/misc/thinkpad_acpi.h
@@ -0,0 +1,572 @@
1/*
2 * thinkpad_acpi.h - 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 __THINKPAD_ACPI_H__
25#define __THINKPAD_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#include <linux/list.h>
33#include <linux/mutex.h>
34
35#include <linux/proc_fs.h>
36#include <linux/sysfs.h>
37#include <linux/backlight.h>
38#include <linux/fb.h>
39#include <linux/platform_device.h>
40#include <linux/hwmon.h>
41#include <linux/hwmon-sysfs.h>
42#include <asm/uaccess.h>
43
44#include <linux/dmi.h>
45#include <linux/jiffies.h>
46#include <linux/workqueue.h>
47
48#include <acpi/acpi_drivers.h>
49#include <acpi/acnamesp.h>
50
51
52/****************************************************************************
53 * Main driver
54 */
55
56#define IBM_NAME "thinkpad"
57#define IBM_DESC "ThinkPad ACPI Extras"
58#define IBM_FILE "thinkpad_acpi"
59#define IBM_URL "http://ibm-acpi.sf.net/"
60#define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net"
61
62#define IBM_PROC_DIR "ibm"
63#define IBM_ACPI_EVENT_PREFIX "ibm"
64#define IBM_DRVR_NAME IBM_FILE
65
66#define IBM_LOG IBM_FILE ": "
67#define IBM_ERR KERN_ERR IBM_LOG
68#define IBM_NOTICE KERN_NOTICE IBM_LOG
69#define IBM_INFO KERN_INFO IBM_LOG
70#define IBM_DEBUG KERN_DEBUG IBM_LOG
71
72#define IBM_MAX_ACPI_ARGS 3
73
74/* ThinkPad CMOS commands */
75#define TP_CMOS_VOLUME_DOWN 0
76#define TP_CMOS_VOLUME_UP 1
77#define TP_CMOS_VOLUME_MUTE 2
78#define TP_CMOS_BRIGHTNESS_UP 4
79#define TP_CMOS_BRIGHTNESS_DOWN 5
80
81#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
82#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
83#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
84
85/* Debugging */
86#define TPACPI_DBG_ALL 0xffff
87#define TPACPI_DBG_ALL 0xffff
88#define TPACPI_DBG_INIT 0x0001
89#define TPACPI_DBG_EXIT 0x0002
90#define dbg_printk(a_dbg_level, format, arg...) \
91 do { if (dbg_level & a_dbg_level) \
92 printk(IBM_DEBUG "%s: " format, __func__ , ## arg); } while (0)
93#ifdef CONFIG_THINKPAD_ACPI_DEBUG
94#define vdbg_printk(a_dbg_level, format, arg...) \
95 dbg_printk(a_dbg_level, format, ## arg)
96static const char *str_supported(int is_supported);
97#else
98#define vdbg_printk(a_dbg_level, format, arg...)
99#endif
100
101/* ACPI HIDs */
102#define IBM_HKEY_HID "IBM0068"
103#define IBM_PCI_HID "PNP0A03"
104
105/* ACPI helpers */
106static int __must_check acpi_evalf(acpi_handle handle,
107 void *res, char *method, char *fmt, ...);
108static int __must_check acpi_ec_read(int i, u8 * p);
109static int __must_check acpi_ec_write(int i, u8 v);
110static int __must_check _sta(acpi_handle handle);
111
112/* ACPI handles */
113static acpi_handle root_handle; /* root namespace */
114static acpi_handle ec_handle; /* EC */
115static acpi_handle ecrd_handle, ecwr_handle; /* 570 EC access */
116static acpi_handle cmos_handle, hkey_handle; /* basic thinkpad handles */
117
118static void drv_acpi_handle_init(char *name,
119 acpi_handle *handle, acpi_handle parent,
120 char **paths, int num_paths, char **path);
121#define IBM_ACPIHANDLE_INIT(object) \
122 drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
123 object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
124
125/* ThinkPad ACPI helpers */
126static int issue_thinkpad_cmos_command(int cmos_cmd);
127
128/* procfs support */
129static struct proc_dir_entry *proc_dir;
130
131/* procfs helpers */
132static int dispatch_procfs_read(char *page, char **start, off_t off,
133 int count, int *eof, void *data);
134static int dispatch_procfs_write(struct file *file,
135 const char __user * userbuf,
136 unsigned long count, void *data);
137static char *next_cmd(char **cmds);
138
139/* sysfs support */
140struct attribute_set {
141 unsigned int members, max_members;
142 struct attribute_group group;
143};
144
145static struct attribute_set *create_attr_set(unsigned int max_members,
146 const char* name);
147#define destroy_attr_set(_set) \
148 kfree(_set);
149static int add_to_attr_set(struct attribute_set* s, struct attribute *attr);
150static int add_many_to_attr_set(struct attribute_set* s,
151 struct attribute **attr,
152 unsigned int count);
153#define register_attr_set_with_sysfs(_attr_set, _kobj) \
154 sysfs_create_group(_kobj, &_attr_set->group)
155static void delete_attr_set(struct attribute_set* s, struct kobject *kobj);
156
157static int parse_strtoul(const char *buf, unsigned long max,
158 unsigned long *value);
159
160/* Device model */
161static struct platform_device *tpacpi_pdev;
162static struct class_device *tpacpi_hwmon;
163static struct platform_driver tpacpi_pdriver;
164static int tpacpi_create_driver_attributes(struct device_driver *drv);
165static void tpacpi_remove_driver_attributes(struct device_driver *drv);
166
167/* Module */
168static int experimental;
169static u32 dbg_level;
170static int force_load;
171static char *ibm_thinkpad_ec_found;
172
173static char* check_dmi_for_ec(void);
174static int thinkpad_acpi_module_init(void);
175static void thinkpad_acpi_module_exit(void);
176
177
178/****************************************************************************
179 * Subdrivers
180 */
181
182struct ibm_struct;
183
184struct tp_acpi_drv_struct {
185 char *hid;
186 struct acpi_driver *driver;
187
188 void (*notify) (struct ibm_struct *, u32);
189 acpi_handle *handle;
190 u32 type;
191 struct acpi_device *device;
192};
193
194struct ibm_struct {
195 char *name;
196
197 int (*read) (char *);
198 int (*write) (char *);
199 void (*exit) (void);
200
201 struct list_head all_drivers;
202
203 struct tp_acpi_drv_struct *acpi;
204
205 struct {
206 u8 acpi_driver_registered:1;
207 u8 acpi_notify_installed:1;
208 u8 proc_created:1;
209 u8 init_called:1;
210 u8 experimental:1;
211 } flags;
212};
213
214struct ibm_init_struct {
215 char param[32];
216
217 int (*init) (struct ibm_init_struct *);
218 struct ibm_struct *data;
219};
220
221static struct {
222#ifdef CONFIG_THINKPAD_ACPI_BAY
223 u16 bay_status:1;
224 u16 bay_eject:1;
225 u16 bay_status2:1;
226 u16 bay_eject2:1;
227#endif
228 u16 bluetooth:1;
229 u16 hotkey:1;
230 u16 hotkey_mask:1;
231 u16 light:1;
232 u16 light_status:1;
233 u16 wan:1;
234 u16 fan_ctrl_status_undef:1;
235} tp_features;
236
237static struct list_head tpacpi_all_drivers;
238
239static struct ibm_init_struct ibms_init[];
240static int set_ibm_param(const char *val, struct kernel_param *kp);
241static int ibm_init(struct ibm_init_struct *iibm);
242static void ibm_exit(struct ibm_struct *ibm);
243
244
245/*
246 * procfs master subdriver
247 */
248static int thinkpad_acpi_driver_init(struct ibm_init_struct *iibm);
249static int thinkpad_acpi_driver_read(char *p);
250
251
252/*
253 * Bay subdriver
254 */
255
256#ifdef CONFIG_THINKPAD_ACPI_BAY
257static acpi_handle bay_handle, bay_ej_handle;
258static acpi_handle bay2_handle, bay2_ej_handle;
259
260static int bay_init(struct ibm_init_struct *iibm);
261static void bay_notify(struct ibm_struct *ibm, u32 event);
262static int bay_read(char *p);
263static int bay_write(char *buf);
264#endif /* CONFIG_THINKPAD_ACPI_BAY */
265
266
267/*
268 * Beep subdriver
269 */
270
271static acpi_handle beep_handle;
272
273static int beep_read(char *p);
274static int beep_write(char *buf);
275
276
277/*
278 * Bluetooth subdriver
279 */
280
281#define TPACPI_BLUETH_SYSFS_GROUP "bluetooth"
282
283enum {
284 /* ACPI GBDC/SBDC bits */
285 TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */
286 TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */
287 TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */
288};
289
290static int bluetooth_init(struct ibm_init_struct *iibm);
291static int bluetooth_get_radiosw(void);
292static int bluetooth_set_radiosw(int radio_on);
293static int bluetooth_read(char *p);
294static int bluetooth_write(char *buf);
295
296
297/*
298 * Brightness (backlight) subdriver
299 */
300
301#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
302
303static struct backlight_device *ibm_backlight_device;
304static int brightness_offset = 0x31;
305
306static int brightness_init(struct ibm_init_struct *iibm);
307static void brightness_exit(void);
308static int brightness_get(struct backlight_device *bd);
309static int brightness_set(int value);
310static int brightness_update_status(struct backlight_device *bd);
311static int brightness_read(char *p);
312static int brightness_write(char *buf);
313
314
315/*
316 * CMOS subdriver
317 */
318
319static int cmos_read(char *p);
320static int cmos_write(char *buf);
321
322
323/*
324 * Dock subdriver
325 */
326
327#ifdef CONFIG_THINKPAD_ACPI_DOCK
328static acpi_handle pci_handle;
329static acpi_handle dock_handle;
330
331static void dock_notify(struct ibm_struct *ibm, u32 event);
332static int dock_read(char *p);
333static int dock_write(char *buf);
334#endif /* CONFIG_THINKPAD_ACPI_DOCK */
335
336
337/*
338 * EC dump subdriver
339 */
340
341static int ecdump_read(char *p) ;
342static int ecdump_write(char *buf);
343
344
345/*
346 * Fan subdriver
347 */
348
349enum { /* Fan control constants */
350 fan_status_offset = 0x2f, /* EC register 0x2f */
351 fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM)
352 * 0x84 must be read before 0x85 */
353
354 TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */
355 TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */
356
357 TPACPI_FAN_LAST_LEVEL = 0x100, /* Use cached last-seen fan level */
358};
359
360enum fan_status_access_mode {
361 TPACPI_FAN_NONE = 0, /* No fan status or control */
362 TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */
363 TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
364};
365
366enum fan_control_access_mode {
367 TPACPI_FAN_WR_NONE = 0, /* No fan control */
368 TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */
369 TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */
370 TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */
371};
372
373enum fan_control_commands {
374 TPACPI_FAN_CMD_SPEED = 0x0001, /* speed command */
375 TPACPI_FAN_CMD_LEVEL = 0x0002, /* level command */
376 TPACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd,
377 * and also watchdog cmd */
378};
379
380static int fan_control_allowed;
381
382static enum fan_status_access_mode fan_status_access_mode;
383static enum fan_control_access_mode fan_control_access_mode;
384static enum fan_control_commands fan_control_commands;
385static u8 fan_control_initial_status;
386static u8 fan_control_desired_level;
387static int fan_watchdog_maxinterval;
388
389static struct mutex fan_mutex;
390
391static acpi_handle fans_handle, gfan_handle, sfan_handle;
392
393static int fan_init(struct ibm_init_struct *iibm);
394static void fan_exit(void);
395static int fan_get_status(u8 *status);
396static int fan_get_status_safe(u8 *status);
397static int fan_get_speed(unsigned int *speed);
398static void fan_update_desired_level(u8 status);
399static void fan_watchdog_fire(struct work_struct *ignored);
400static void fan_watchdog_reset(void);
401static int fan_set_level(int level);
402static int fan_set_level_safe(int level);
403static int fan_set_enable(void);
404static int fan_set_disable(void);
405static int fan_set_speed(int speed);
406static int fan_read(char *p);
407static int fan_write(char *buf);
408static int fan_write_cmd_level(const char *cmd, int *rc);
409static int fan_write_cmd_enable(const char *cmd, int *rc);
410static int fan_write_cmd_disable(const char *cmd, int *rc);
411static int fan_write_cmd_speed(const char *cmd, int *rc);
412static int fan_write_cmd_watchdog(const char *cmd, int *rc);
413
414
415/*
416 * Hotkey subdriver
417 */
418
419#define TPACPI_HOTKEY_SYSFS_GROUP "hotkey"
420
421static int hotkey_orig_status;
422static int hotkey_orig_mask;
423
424static struct mutex hotkey_mutex;
425
426static int hotkey_init(struct ibm_init_struct *iibm);
427static void hotkey_exit(void);
428static int hotkey_get(int *status, int *mask);
429static int hotkey_set(int status, int mask);
430static void hotkey_notify(struct ibm_struct *ibm, u32 event);
431static int hotkey_read(char *p);
432static int hotkey_write(char *buf);
433
434
435/*
436 * LED subdriver
437 */
438
439enum led_access_mode {
440 TPACPI_LED_NONE = 0,
441 TPACPI_LED_570, /* 570 */
442 TPACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
443 TPACPI_LED_NEW, /* all others */
444};
445
446enum { /* For TPACPI_LED_OLD */
447 TPACPI_LED_EC_HLCL = 0x0c, /* EC reg to get led to power on */
448 TPACPI_LED_EC_HLBL = 0x0d, /* EC reg to blink a lit led */
449 TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */
450};
451
452static enum led_access_mode led_supported;
453static acpi_handle led_handle;
454
455static int led_init(struct ibm_init_struct *iibm);
456static int led_read(char *p);
457static int led_write(char *buf);
458
459/*
460 * Light (thinklight) subdriver
461 */
462
463static acpi_handle lght_handle, ledb_handle;
464
465static int light_init(struct ibm_init_struct *iibm);
466static int light_read(char *p);
467static int light_write(char *buf);
468
469
470/*
471 * Thermal subdriver
472 */
473
474enum thermal_access_mode {
475 TPACPI_THERMAL_NONE = 0, /* No thermal support */
476 TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */
477 TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */
478 TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */
479 TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */
480};
481
482enum { /* TPACPI_THERMAL_TPEC_* */
483 TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */
484 TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */
485 TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */
486};
487
488#define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */
489struct ibm_thermal_sensors_struct {
490 s32 temp[TPACPI_MAX_THERMAL_SENSORS];
491};
492
493static enum thermal_access_mode thermal_read_mode;
494
495static int thermal_init(struct ibm_init_struct *iibm);
496static int thermal_get_sensor(int idx, s32 *value);
497static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s);
498static int thermal_read(char *p);
499
500
501/*
502 * Video subdriver
503 */
504
505enum video_access_mode {
506 TPACPI_VIDEO_NONE = 0,
507 TPACPI_VIDEO_570, /* 570 */
508 TPACPI_VIDEO_770, /* 600e/x, 770e, 770x */
509 TPACPI_VIDEO_NEW, /* all others */
510};
511
512enum { /* video status flags, based on VIDEO_570 */
513 TP_ACPI_VIDEO_S_LCD = 0x01, /* LCD output enabled */
514 TP_ACPI_VIDEO_S_CRT = 0x02, /* CRT output enabled */
515 TP_ACPI_VIDEO_S_DVI = 0x08, /* DVI output enabled */
516};
517
518enum { /* TPACPI_VIDEO_570 constants */
519 TP_ACPI_VIDEO_570_PHSCMD = 0x87, /* unknown magic constant :( */
520 TP_ACPI_VIDEO_570_PHSMASK = 0x03, /* PHS bits that map to
521 * video_status_flags */
522 TP_ACPI_VIDEO_570_PHS2CMD = 0x8b, /* unknown magic constant :( */
523 TP_ACPI_VIDEO_570_PHS2SET = 0x80, /* unknown magic constant :( */
524};
525
526static enum video_access_mode video_supported;
527static int video_orig_autosw;
528static acpi_handle vid_handle, vid2_handle;
529
530static int video_init(struct ibm_init_struct *iibm);
531static void video_exit(void);
532static int video_outputsw_get(void);
533static int video_outputsw_set(int status);
534static int video_autosw_get(void);
535static int video_autosw_set(int enable);
536static int video_outputsw_cycle(void);
537static int video_expand_toggle(void);
538static int video_read(char *p);
539static int video_write(char *buf);
540
541
542/*
543 * Volume subdriver
544 */
545
546static int volume_offset = 0x30;
547
548static int volume_read(char *p);
549static int volume_write(char *buf);
550
551
552/*
553 * Wan subdriver
554 */
555
556#define TPACPI_WAN_SYSFS_GROUP "wwan"
557
558enum {
559 /* ACPI GWAN/SWAN bits */
560 TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */
561 TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */
562 TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */
563};
564
565static int wan_init(struct ibm_init_struct *iibm);
566static int wan_get_radiosw(void);
567static int wan_set_radiosw(int radio_on);
568static int wan_read(char *p);
569static int wan_write(char *buf);
570
571
572#endif /* __THINKPAD_ACPI_H */