diff options
author | Len Brown <len.brown@intel.com> | 2006-12-16 00:34:18 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2006-12-16 00:34:18 -0500 |
commit | 6bfe5c9d6f4dcaa998f67e691359cf7b1c4b443d (patch) | |
tree | 94ee5b1c2b768a584b9bd0fe44382f80f2e4c96b | |
parent | b361735043e3001eadb1d40916fd1a4fca1a9363 (diff) | |
parent | f9ff43a6268d36acf8df18a76bb881a26a42dc1e (diff) |
Pull platform-drivers into test branch
-rw-r--r-- | Documentation/ibm-acpi.txt | 151 | ||||
-rw-r--r-- | drivers/acpi/Kconfig | 14 | ||||
-rw-r--r-- | drivers/acpi/asus_acpi.c | 62 | ||||
-rw-r--r-- | drivers/acpi/ibm_acpi.c | 1037 | ||||
-rw-r--r-- | drivers/acpi/toshiba_acpi.c | 88 |
5 files changed, 1131 insertions, 221 deletions
diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index e50595bfd8e..0132d363feb 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt | |||
@@ -398,25 +398,67 @@ Temperature sensors -- /proc/acpi/ibm/thermal | |||
398 | 398 | ||
399 | Most ThinkPads include six or more separate temperature sensors but | 399 | Most ThinkPads include six or more separate temperature sensors but |
400 | only expose the CPU temperature through the standard ACPI methods. | 400 | only expose the CPU temperature through the standard ACPI methods. |
401 | This feature shows readings from up to eight different sensors. Some | 401 | This feature shows readings from up to eight different sensors on older |
402 | readings may not be valid, e.g. may show large negative values. For | 402 | ThinkPads, and it has experimental support for up to sixteen different |
403 | example, on the X40, a typical output may be: | 403 | sensors on newer ThinkPads. Readings from sensors that are not available |
404 | return -128. | ||
404 | 405 | ||
406 | No commands can be written to this file. | ||
407 | |||
408 | EXPERIMENTAL: The 16-sensors feature is marked EXPERIMENTAL because the | ||
409 | implementation directly accesses hardware registers and may not work as | ||
410 | expected. USE WITH CAUTION! To use this feature, you need to supply the | ||
411 | experimental=1 parameter when loading the module. When EXPERIMENTAL | ||
412 | mode is enabled, reading the first 8 sensors on newer ThinkPads will | ||
413 | also use an new experimental thermal sensor access mode. | ||
414 | |||
415 | For example, on the X40, a typical output may be: | ||
405 | temperatures: 42 42 45 41 36 -128 33 -128 | 416 | temperatures: 42 42 45 41 36 -128 33 -128 |
406 | 417 | ||
407 | Thomas Gruber took his R51 apart and traced all six active sensors in | 418 | EXPERIMENTAL: On the T43/p, a typical output may be: |
408 | his laptop (the location of sensors may vary on other models): | 419 | temperatures: 48 48 36 52 38 -128 31 -128 48 52 48 -128 -128 -128 -128 -128 |
420 | |||
421 | The mapping of thermal sensors to physical locations varies depending on | ||
422 | system-board model (and thus, on ThinkPad model). | ||
423 | |||
424 | http://thinkwiki.org/wiki/Thermal_Sensors is a public wiki page that | ||
425 | tries to track down these locations for various models. | ||
426 | |||
427 | Most (newer?) models seem to follow this pattern: | ||
409 | 428 | ||
410 | 1: CPU | 429 | 1: CPU |
411 | 2: Mini PCI Module | 430 | 2: (depends on model) |
412 | 3: HDD | 431 | 3: (depends on model) |
413 | 4: GPU | 432 | 4: GPU |
414 | 5: Battery | 433 | 5: Main battery: main sensor |
415 | 6: N/A | 434 | 6: Bay battery: main sensor |
416 | 7: Battery | 435 | 7: Main battery: secondary sensor |
417 | 8: N/A | 436 | 8: Bay battery: secondary sensor |
437 | 9-15: (depends on model) | ||
438 | |||
439 | For the R51 (source: Thomas Gruber): | ||
440 | 2: Mini-PCI | ||
441 | 3: Internal HDD | ||
442 | |||
443 | For the T43, T43/p (source: Shmidoax/Thinkwiki.org) | ||
444 | http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_T43.2C_T43p | ||
445 | 2: System board, left side (near PCMCIA slot), reported as HDAPS temp | ||
446 | 3: PCMCIA slot | ||
447 | 9: MCH (northbridge) to DRAM Bus | ||
448 | 10: ICH (southbridge), under Mini-PCI card, under touchpad | ||
449 | 11: Power regulator, underside of system board, below F2 key | ||
450 | |||
451 | The A31 has a very atypical layout for the thermal sensors | ||
452 | (source: Milos Popovic, http://thinkwiki.org/wiki/Thermal_Sensors#ThinkPad_A31) | ||
453 | 1: CPU | ||
454 | 2: Main Battery: main sensor | ||
455 | 3: Power Converter | ||
456 | 4: Bay Battery: main sensor | ||
457 | 5: MCH (northbridge) | ||
458 | 6: PCMCIA/ambient | ||
459 | 7: Main Battery: secondary sensor | ||
460 | 8: Bay Battery: secondary sensor | ||
418 | 461 | ||
419 | No commands can be written to this file. | ||
420 | 462 | ||
421 | EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump | 463 | EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump |
422 | ------------------------------------------------------------------------ | 464 | ------------------------------------------------------------------------ |
@@ -529,27 +571,57 @@ directly accesses hardware registers and may not work as expected. USE | |||
529 | WITH CAUTION! To use this feature, you need to supply the | 571 | WITH CAUTION! To use this feature, you need to supply the |
530 | experimental=1 parameter when loading the module. | 572 | experimental=1 parameter when loading the module. |
531 | 573 | ||
532 | This feature attempts to show the current fan speed. The speed is read | 574 | This feature attempts to show the current fan speed, control mode and |
533 | directly from the hardware registers of the embedded controller. This | 575 | other fan data that might be available. The speed is read directly |
534 | is known to work on later R, T and X series ThinkPads but may show a | 576 | from the hardware registers of the embedded controller. This is known |
535 | bogus value on other models. | 577 | to work on later R, T and X series ThinkPads but may show a bogus |
578 | value on other models. | ||
579 | |||
580 | Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher | ||
581 | the level, the higher the fan speed, although adjacent levels often map | ||
582 | to the same fan speed. 7 is the highest level, where the fan reaches | ||
583 | the maximum recommended speed. Level "auto" means the EC changes the | ||
584 | fan level according to some internal algorithm, usually based on | ||
585 | readings from the thermal sensors. Level "disengaged" means the EC | ||
586 | disables the speed-locked closed-loop fan control, and drives the fan as | ||
587 | fast as it can go, which might exceed hardware limits, so use this level | ||
588 | with caution. | ||
589 | |||
590 | The fan usually ramps up or down slowly from one speed to another, | ||
591 | and it is normal for the EC to take several seconds to react to fan | ||
592 | commands. | ||
536 | 593 | ||
537 | The fan may be enabled or disabled with the following commands: | 594 | The fan may be enabled or disabled with the following commands: |
538 | 595 | ||
539 | echo enable >/proc/acpi/ibm/fan | 596 | echo enable >/proc/acpi/ibm/fan |
540 | echo disable >/proc/acpi/ibm/fan | 597 | echo disable >/proc/acpi/ibm/fan |
541 | 598 | ||
599 | Placing a fan on level 0 is the same as disabling it. Enabling a fan | ||
600 | will try to place it in a safe level if it is too slow or disabled. | ||
601 | |||
542 | WARNING WARNING WARNING: do not leave the fan disabled unless you are | 602 | WARNING WARNING WARNING: do not leave the fan disabled unless you are |
543 | monitoring the temperature sensor readings and you are ready to enable | 603 | monitoring all of the temperature sensor readings and you are ready to |
544 | it if necessary to avoid overheating. | 604 | enable it if necessary to avoid overheating. |
545 | 605 | ||
546 | The fan only runs if it's enabled *and* the various temperature | 606 | An enabled fan in level "auto" may stop spinning if the EC decides the |
547 | sensors which control it read high enough. On the X40, this seems to | 607 | ThinkPad is cool enough and doesn't need the extra airflow. This is |
548 | depend on the CPU and HDD temperatures. Specifically, the fan is | 608 | normal, and the EC will spin the fan up if the varios thermal readings |
549 | turned on when either the CPU temperature climbs to 56 degrees or the | 609 | rise too much. |
550 | HDD temperature climbs to 46 degrees. The fan is turned off when the | 610 | |
551 | CPU temperature drops to 49 degrees and the HDD temperature drops to | 611 | On the X40, this seems to depend on the CPU and HDD temperatures. |
552 | 41 degrees. These thresholds cannot currently be controlled. | 612 | Specifically, the fan is turned on when either the CPU temperature |
613 | climbs to 56 degrees or the HDD temperature climbs to 46 degrees. The | ||
614 | fan is turned off when the CPU temperature drops to 49 degrees and the | ||
615 | HDD temperature drops to 41 degrees. These thresholds cannot | ||
616 | currently be controlled. | ||
617 | |||
618 | The fan level can be controlled with the command: | ||
619 | |||
620 | echo 'level <level>' > /proc/acpi/ibm/thermal | ||
621 | |||
622 | Where <level> is an integer from 0 to 7, or one of the words "auto" | ||
623 | or "disengaged" (without the quotes). Not all ThinkPads support the | ||
624 | "auto" and "disengaged" levels. | ||
553 | 625 | ||
554 | On the X31 and X40 (and ONLY on those models), the fan speed can be | 626 | On the X31 and X40 (and ONLY on those models), the fan speed can be |
555 | controlled to a certain degree. Once the fan is running, it can be | 627 | controlled to a certain degree. Once the fan is running, it can be |
@@ -562,12 +634,9 @@ about 3700 to about 7350. Values outside this range either do not have | |||
562 | any effect or the fan speed eventually settles somewhere in that | 634 | any effect or the fan speed eventually settles somewhere in that |
563 | range. The fan cannot be stopped or started with this command. | 635 | range. The fan cannot be stopped or started with this command. |
564 | 636 | ||
565 | On the 570, temperature readings are not available through this | 637 | The ThinkPad's ACPI DSDT code will reprogram the fan on its own when |
566 | feature and the fan control works a little differently. The fan speed | 638 | certain conditions are met. It will override any fan programming done |
567 | is reported in levels from 0 (off) to 7 (max) and can be controlled | 639 | through ibm-acpi. |
568 | with the following command: | ||
569 | |||
570 | echo 'level <level>' > /proc/acpi/ibm/thermal | ||
571 | 640 | ||
572 | EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan | 641 | EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan |
573 | --------------------------------------- | 642 | --------------------------------------- |
@@ -601,6 +670,26 @@ example: | |||
601 | 670 | ||
602 | modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable | 671 | modprobe ibm_acpi hotkey=enable,0xffff video=auto_disable |
603 | 672 | ||
673 | The ibm-acpi kernel driver can be programmed to revert the fan level | ||
674 | to a safe setting if userspace does not issue one of the fan commands: | ||
675 | "enable", "disable", "level" or "watchdog" within a configurable | ||
676 | ammount of time. To do this, use the "watchdog" command. | ||
677 | |||
678 | echo 'watchdog <interval>' > /proc/acpi/ibm/fan | ||
679 | |||
680 | Interval is the ammount of time in seconds to wait for one of the | ||
681 | above mentioned fan commands before reseting the fan level to a safe | ||
682 | one. If set to zero, the watchdog is disabled (default). When the | ||
683 | watchdog timer runs out, it does the exact equivalent of the "enable" | ||
684 | fan command. | ||
685 | |||
686 | Note that the watchdog timer stops after it enables the fan. It will | ||
687 | be rearmed again automatically (using the same interval) when one of | ||
688 | the above mentioned fan commands is received. The fan watchdog is, | ||
689 | therefore, not suitable to protect against fan mode changes made | ||
690 | through means other than the "enable", "disable", and "level" fan | ||
691 | commands. | ||
692 | |||
604 | 693 | ||
605 | Example Configuration | 694 | Example Configuration |
606 | --------------------- | 695 | --------------------- |
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 0ed80122967..b0897a0b206 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -173,6 +173,7 @@ config ACPI_NUMA | |||
173 | config ACPI_ASUS | 173 | config ACPI_ASUS |
174 | tristate "ASUS/Medion Laptop Extras" | 174 | tristate "ASUS/Medion Laptop Extras" |
175 | depends on X86 | 175 | depends on X86 |
176 | select BACKLIGHT_CLASS_DEVICE | ||
176 | ---help--- | 177 | ---help--- |
177 | This driver provides support for extra features of ACPI-compatible | 178 | This driver provides support for extra features of ACPI-compatible |
178 | ASUS laptops. As some of Medion laptops are made by ASUS, it may also | 179 | ASUS laptops. As some of Medion laptops are made by ASUS, it may also |
@@ -201,6 +202,7 @@ config ACPI_ASUS | |||
201 | config ACPI_IBM | 202 | config ACPI_IBM |
202 | tristate "IBM ThinkPad Laptop Extras" | 203 | tristate "IBM ThinkPad Laptop Extras" |
203 | depends on X86 | 204 | depends on X86 |
205 | select BACKLIGHT_CLASS_DEVICE | ||
204 | ---help--- | 206 | ---help--- |
205 | This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds | 207 | This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds |
206 | support for Fn-Fx key combinations, Bluetooth control, video | 208 | support for Fn-Fx key combinations, Bluetooth control, video |
@@ -223,9 +225,21 @@ config ACPI_IBM_DOCK | |||
223 | 225 | ||
224 | If you are not sure, say N here. | 226 | If you are not sure, say N here. |
225 | 227 | ||
228 | config ACPI_IBM_BAY | ||
229 | bool "Legacy Removable Bay Support" | ||
230 | depends on ACPI_IBM | ||
231 | depends on ACPI_BAY=n | ||
232 | default n | ||
233 | ---help--- | ||
234 | Allows the ibm_acpi driver to handle removable bays. | ||
235 | This support is obsoleted by CONFIG_ACPI_BAY. | ||
236 | |||
237 | If you are not sure, say N here. | ||
238 | |||
226 | config ACPI_TOSHIBA | 239 | config ACPI_TOSHIBA |
227 | tristate "Toshiba Laptop Extras" | 240 | tristate "Toshiba Laptop Extras" |
228 | depends on X86 | 241 | depends on X86 |
242 | select BACKLIGHT_CLASS_DEVICE | ||
229 | ---help--- | 243 | ---help--- |
230 | This driver adds support for access to certain system settings | 244 | This driver adds support for access to certain system settings |
231 | on "legacy free" Toshiba laptops. These laptops can be recognized by | 245 | on "legacy free" Toshiba laptops. These laptops can be recognized by |
diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index c7ac9297a20..bf7bc25e680 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/init.h> | 35 | #include <linux/init.h> |
36 | #include <linux/types.h> | 36 | #include <linux/types.h> |
37 | #include <linux/proc_fs.h> | 37 | #include <linux/proc_fs.h> |
38 | #include <linux/backlight.h> | ||
38 | #include <acpi/acpi_drivers.h> | 39 | #include <acpi/acpi_drivers.h> |
39 | #include <acpi/acpi_bus.h> | 40 | #include <acpi/acpi_bus.h> |
40 | #include <asm/uaccess.h> | 41 | #include <asm/uaccess.h> |
@@ -402,6 +403,8 @@ static struct model_data model_conf[END_MODEL] = { | |||
402 | /* procdir we use */ | 403 | /* procdir we use */ |
403 | static struct proc_dir_entry *asus_proc_dir; | 404 | static struct proc_dir_entry *asus_proc_dir; |
404 | 405 | ||
406 | static struct backlight_device *asus_backlight_device; | ||
407 | |||
405 | /* | 408 | /* |
406 | * This header is made available to allow proper configuration given model, | 409 | * This header is made available to allow proper configuration given model, |
407 | * revision number , ... this info cannot go in struct asus_hotk because it is | 410 | * revision number , ... this info cannot go in struct asus_hotk because it is |
@@ -779,7 +782,7 @@ proc_write_lcd(struct file *file, const char __user * buffer, | |||
779 | return rv; | 782 | return rv; |
780 | } | 783 | } |
781 | 784 | ||
782 | static int read_brightness(void) | 785 | static int read_brightness(struct backlight_device *bd) |
783 | { | 786 | { |
784 | int value; | 787 | int value; |
785 | 788 | ||
@@ -801,9 +804,10 @@ static int read_brightness(void) | |||
801 | /* | 804 | /* |
802 | * Change the brightness level | 805 | * Change the brightness level |
803 | */ | 806 | */ |
804 | static void set_brightness(int value) | 807 | static int set_brightness(int value) |
805 | { | 808 | { |
806 | acpi_status status = 0; | 809 | acpi_status status = 0; |
810 | int ret = 0; | ||
807 | 811 | ||
808 | /* SPLV laptop */ | 812 | /* SPLV laptop */ |
809 | if (hotk->methods->brightness_set) { | 813 | if (hotk->methods->brightness_set) { |
@@ -811,11 +815,12 @@ static void set_brightness(int value) | |||
811 | value, NULL)) | 815 | value, NULL)) |
812 | printk(KERN_WARNING | 816 | printk(KERN_WARNING |
813 | "Asus ACPI: Error changing brightness\n"); | 817 | "Asus ACPI: Error changing brightness\n"); |
814 | return; | 818 | ret = -EIO; |
819 | goto out; | ||
815 | } | 820 | } |
816 | 821 | ||
817 | /* No SPLV method if we are here, act as appropriate */ | 822 | /* No SPLV method if we are here, act as appropriate */ |
818 | value -= read_brightness(); | 823 | value -= read_brightness(NULL); |
819 | while (value != 0) { | 824 | while (value != 0) { |
820 | status = acpi_evaluate_object(NULL, (value > 0) ? | 825 | status = acpi_evaluate_object(NULL, (value > 0) ? |
821 | hotk->methods->brightness_up : | 826 | hotk->methods->brightness_up : |
@@ -825,15 +830,22 @@ static void set_brightness(int value) | |||
825 | if (ACPI_FAILURE(status)) | 830 | if (ACPI_FAILURE(status)) |
826 | printk(KERN_WARNING | 831 | printk(KERN_WARNING |
827 | "Asus ACPI: Error changing brightness\n"); | 832 | "Asus ACPI: Error changing brightness\n"); |
833 | ret = -EIO; | ||
828 | } | 834 | } |
829 | return; | 835 | out: |
836 | return ret; | ||
837 | } | ||
838 | |||
839 | static int set_brightness_status(struct backlight_device *bd) | ||
840 | { | ||
841 | return set_brightness(bd->props->brightness); | ||
830 | } | 842 | } |
831 | 843 | ||
832 | static int | 844 | static int |
833 | proc_read_brn(char *page, char **start, off_t off, int count, int *eof, | 845 | proc_read_brn(char *page, char **start, off_t off, int count, int *eof, |
834 | void *data) | 846 | void *data) |
835 | { | 847 | { |
836 | return sprintf(page, "%d\n", read_brightness()); | 848 | return sprintf(page, "%d\n", read_brightness(NULL)); |
837 | } | 849 | } |
838 | 850 | ||
839 | static int | 851 | static int |
@@ -1333,6 +1345,26 @@ static int asus_hotk_remove(struct acpi_device *device, int type) | |||
1333 | return 0; | 1345 | return 0; |
1334 | } | 1346 | } |
1335 | 1347 | ||
1348 | static struct backlight_properties asus_backlight_data = { | ||
1349 | .owner = THIS_MODULE, | ||
1350 | .get_brightness = read_brightness, | ||
1351 | .update_status = set_brightness_status, | ||
1352 | .max_brightness = 15, | ||
1353 | }; | ||
1354 | |||
1355 | static void __exit asus_acpi_exit(void) | ||
1356 | { | ||
1357 | if (asus_backlight_device) | ||
1358 | backlight_device_unregister(asus_backlight_device); | ||
1359 | |||
1360 | acpi_bus_unregister_driver(&asus_hotk_driver); | ||
1361 | remove_proc_entry(PROC_ASUS, acpi_root_dir); | ||
1362 | |||
1363 | kfree(asus_info); | ||
1364 | |||
1365 | return; | ||
1366 | } | ||
1367 | |||
1336 | static int __init asus_acpi_init(void) | 1368 | static int __init asus_acpi_init(void) |
1337 | { | 1369 | { |
1338 | int result; | 1370 | int result; |
@@ -1370,17 +1402,15 @@ static int __init asus_acpi_init(void) | |||
1370 | return result; | 1402 | return result; |
1371 | } | 1403 | } |
1372 | 1404 | ||
1373 | return 0; | 1405 | asus_backlight_device = backlight_device_register("asus", NULL, |
1374 | } | 1406 | &asus_backlight_data); |
1375 | 1407 | if (IS_ERR(asus_backlight_device)) { | |
1376 | static void __exit asus_acpi_exit(void) | 1408 | printk(KERN_ERR "Could not register asus backlight device\n"); |
1377 | { | 1409 | asus_backlight_device = NULL; |
1378 | acpi_bus_unregister_driver(&asus_hotk_driver); | 1410 | asus_acpi_exit(); |
1379 | remove_proc_entry(PROC_ASUS, acpi_root_dir); | 1411 | } |
1380 | |||
1381 | kfree(asus_info); | ||
1382 | 1412 | ||
1383 | return; | 1413 | return 0; |
1384 | } | 1414 | } |
1385 | 1415 | ||
1386 | module_init(asus_acpi_init); | 1416 | module_init(asus_acpi_init); |
diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index 003a9876c96..92e7b6eb8f1 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * | 4 | * |
5 | * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> | 5 | * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> |
6 | * Copyright (C) 2006 Henrique de Moraes Holschuh <hmh@hmh.eng.br> | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
@@ -19,10 +20,14 @@ | |||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | */ | 21 | */ |
21 | 22 | ||
22 | #define IBM_VERSION "0.12a" | 23 | #define IBM_VERSION "0.13" |
23 | 24 | ||
24 | /* | 25 | /* |
25 | * Changelog: | 26 | * Changelog: |
27 | * | ||
28 | * 2006-11-22 0.13 new maintainer | ||
29 | * changelog now lives in git commit history, and will | ||
30 | * not be updated further in-file. | ||
26 | * | 31 | * |
27 | * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels | 32 | * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels |
28 | * 2005-03-17 0.11 support for 600e, 770x | 33 | * 2005-03-17 0.11 support for 600e, 770x |
@@ -77,9 +82,16 @@ | |||
77 | #include <linux/module.h> | 82 | #include <linux/module.h> |
78 | #include <linux/init.h> | 83 | #include <linux/init.h> |
79 | #include <linux/types.h> | 84 | #include <linux/types.h> |
85 | #include <linux/string.h> | ||
86 | |||
80 | #include <linux/proc_fs.h> | 87 | #include <linux/proc_fs.h> |
88 | #include <linux/backlight.h> | ||
81 | #include <asm/uaccess.h> | 89 | #include <asm/uaccess.h> |
82 | 90 | ||
91 | #include <linux/dmi.h> | ||
92 | #include <linux/jiffies.h> | ||
93 | #include <linux/workqueue.h> | ||
94 | |||
83 | #include <acpi/acpi_drivers.h> | 95 | #include <acpi/acpi_drivers.h> |
84 | #include <acpi/acnamesp.h> | 96 | #include <acpi/acnamesp.h> |
85 | 97 | ||
@@ -88,7 +100,7 @@ | |||
88 | #define IBM_FILE "ibm_acpi" | 100 | #define IBM_FILE "ibm_acpi" |
89 | #define IBM_URL "http://ibm-acpi.sf.net/" | 101 | #define IBM_URL "http://ibm-acpi.sf.net/" |
90 | 102 | ||
91 | MODULE_AUTHOR("Borislav Deianov"); | 103 | MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh"); |
92 | MODULE_DESCRIPTION(IBM_DESC); | 104 | MODULE_DESCRIPTION(IBM_DESC); |
93 | MODULE_VERSION(IBM_VERSION); | 105 | MODULE_VERSION(IBM_VERSION); |
94 | MODULE_LICENSE("GPL"); | 106 | MODULE_LICENSE("GPL"); |
@@ -116,28 +128,6 @@ static acpi_handle root_handle = NULL; | |||
116 | static char *object##_path; \ | 128 | static char *object##_path; \ |
117 | static char *object##_paths[] = { paths } | 129 | static char *object##_paths[] = { paths } |
118 | 130 | ||
119 | /* | ||
120 | * The following models are supported to various degrees: | ||
121 | * | ||
122 | * 570, 600e, 600x, 770e, 770x | ||
123 | * A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p | ||
124 | * G40, G41 | ||
125 | * R30, R31, R32, R40, R40e, R50, R50e, R50p, R51 | ||
126 | * T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43 | ||
127 | * X20, X21, X22, X23, X24, X30, X31, X40 | ||
128 | * | ||
129 | * The following models have no supported features: | ||
130 | * | ||
131 | * 240, 240x, i1400 | ||
132 | * | ||
133 | * Still missing DSDTs for the following models: | ||
134 | * | ||
135 | * A20p, A22e, A22m | ||
136 | * R52 | ||
137 | * S31 | ||
138 | * T43p | ||
139 | */ | ||
140 | |||
141 | IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ | 131 | IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ |
142 | "\\_SB.PCI.ISA.EC", /* 570 */ | 132 | "\\_SB.PCI.ISA.EC", /* 570 */ |
143 | "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ | 133 | "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ |
@@ -167,8 +157,10 @@ IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ | |||
167 | "\\_SB.PCI.ISA.SLCE", /* 570 */ | 157 | "\\_SB.PCI.ISA.SLCE", /* 570 */ |
168 | ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ | 158 | ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ |
169 | #endif | 159 | #endif |
160 | #ifdef CONFIG_ACPI_IBM_BAY | ||
170 | IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ | 161 | IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ |
171 | "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ | 162 | "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ |
163 | "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ | ||
172 | "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ | 164 | "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ |
173 | ); /* A21e, R30, R31 */ | 165 | ); /* A21e, R30, R31 */ |
174 | 166 | ||
@@ -183,6 +175,7 @@ IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ | |||
183 | IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ | 175 | IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ |
184 | "_EJ0", /* 770x */ | 176 | "_EJ0", /* 770x */ |
185 | ); /* all others */ | 177 | ); /* all others */ |
178 | #endif | ||
186 | 179 | ||
187 | /* don't list other alternatives as we install a notify handler on the 570 */ | 180 | /* don't list other alternatives as we install a notify handler on the 570 */ |
188 | IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ | 181 | IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ |
@@ -203,7 +196,7 @@ IBM_HANDLE(led, ec, "SLED", /* 570 */ | |||
203 | IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ | 196 | IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ |
204 | IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ | 197 | IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ |
205 | IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ | 198 | IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ |
206 | IBM_HANDLE(fans, ec, "FANS"); /* X31, X40 */ | 199 | IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ |
207 | 200 | ||
208 | IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ | 201 | IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ |
209 | "\\FSPD", /* 600e/x, 770e, 770x */ | 202 | "\\FSPD", /* 600e/x, 770e, 770x */ |
@@ -216,6 +209,152 @@ IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ | |||
216 | #define IBM_HKEY_HID "IBM0068" | 209 | #define IBM_HKEY_HID "IBM0068" |
217 | #define IBM_PCI_HID "PNP0A03" | 210 | #define IBM_PCI_HID "PNP0A03" |
218 | 211 | ||
212 | enum thermal_access_mode { | ||
213 | IBMACPI_THERMAL_NONE = 0, /* No thermal support */ | ||
214 | IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ | ||
215 | IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ | ||
216 | IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ | ||
217 | IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ | ||
218 | }; | ||
219 | |||
220 | #define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ | ||
221 | struct ibm_thermal_sensors_struct { | ||
222 | s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; | ||
223 | }; | ||
224 | |||
225 | /* | ||
226 | * FAN ACCESS MODES | ||
227 | * | ||
228 | * IBMACPI_FAN_RD_ACPI_GFAN: | ||
229 | * ACPI GFAN method: returns fan level | ||
230 | * | ||
231 | * see IBMACPI_FAN_WR_ACPI_SFAN | ||
232 | * EC 0x2f not available if GFAN exists | ||
233 | * | ||
234 | * IBMACPI_FAN_WR_ACPI_SFAN: | ||
235 | * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max) | ||
236 | * | ||
237 | * EC 0x2f might be available *for reading*, but never for writing. | ||
238 | * | ||
239 | * IBMACPI_FAN_WR_TPEC: | ||
240 | * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported | ||
241 | * on almost all ThinkPads | ||
242 | * | ||
243 | * Fan speed changes of any sort (including those caused by the | ||
244 | * disengaged mode) are usually done slowly by the firmware as the | ||
245 | * maximum ammount of fan duty cycle change per second seems to be | ||
246 | * limited. | ||
247 | * | ||
248 | * Reading is not available if GFAN exists. | ||
249 | * Writing is not available if SFAN exists. | ||
250 | * | ||
251 | * Bits | ||
252 | * 7 automatic mode engaged; | ||
253 | * (default operation mode of the ThinkPad) | ||
254 | * fan level is ignored in this mode. | ||
255 | * 6 disengage mode (takes precedence over bit 7); | ||
256 | * not available on all thinkpads. May disable | ||
257 | * the tachometer, and speeds up fan to 100% duty-cycle, | ||
258 | * which speeds it up far above the standard RPM | ||
259 | * levels. It is not impossible that it could cause | ||
260 | * hardware damage. | ||
261 | * 5-3 unused in some models. Extra bits for fan level | ||
262 | * in others, but still useless as all values above | ||
263 | * 7 map to the same speed as level 7 in these models. | ||
264 | * 2-0 fan level (0..7 usually) | ||
265 | * 0x00 = stop | ||
266 | * 0x07 = max (set when temperatures critical) | ||
267 | * Some ThinkPads may have other levels, see | ||
268 | * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41) | ||
269 | * | ||
270 | * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at | ||
271 | * boot. Apparently the EC does not intialize it, so unless ACPI DSDT | ||
272 | * does so, its initial value is meaningless (0x07). | ||
273 | * | ||
274 | * For firmware bugs, refer to: | ||
275 | * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues | ||
276 | * | ||
277 | * ---- | ||
278 | * | ||
279 | * ThinkPad EC register 0x84 (LSB), 0x85 (MSB): | ||
280 | * Main fan tachometer reading (in RPM) | ||
281 | * | ||
282 | * This register is present on all ThinkPads with a new-style EC, and | ||
283 | * it is known not to be present on the A21m/e, and T22, as there is | ||
284 | * something else in offset 0x84 according to the ACPI DSDT. Other | ||
285 | * ThinkPads from this same time period (and earlier) probably lack the | ||
286 | * tachometer as well. | ||
287 | * | ||
288 | * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare | ||
289 | * was never fixed by IBM to report the EC firmware version string | ||
290 | * probably support the tachometer (like the early X models), so | ||
291 | * detecting it is quite hard. We need more data to know for sure. | ||
292 | * | ||
293 | * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings | ||
294 | * might result. | ||
295 | * | ||
296 | * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this | ||
297 | * register is not invalidated in ThinkPads that disable tachometer | ||
298 | * readings. Thus, the tachometer readings go stale. | ||
299 | * | ||
300 | * For firmware bugs, refer to: | ||
301 | * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues | ||
302 | * | ||
303 | * IBMACPI_FAN_WR_ACPI_FANS: | ||
304 | * ThinkPad X31, X40, X41. Not available in the X60. | ||
305 | * | ||
306 | * FANS ACPI handle: takes three arguments: low speed, medium speed, | ||
307 | * high speed. ACPI DSDT seems to map these three speeds to levels | ||
308 | * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH | ||
309 | * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3") | ||
310 | * | ||
311 | * The speeds are stored on handles | ||
312 | * (FANA:FAN9), (FANC:FANB), (FANE:FAND). | ||
313 | * | ||
314 | * There are three default speed sets, acessible as handles: | ||
315 | * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H | ||
316 | * | ||
317 | * ACPI DSDT switches which set is in use depending on various | ||
318 | * factors. | ||
319 | * | ||
320 | * IBMACPI_FAN_WR_TPEC is also available and should be used to | ||
321 | * command the fan. The X31/X40/X41 seems to have 8 fan levels, | ||
322 | * but the ACPI tables just mention level 7. | ||
323 | */ | ||
324 | |||
325 | enum fan_status_access_mode { | ||
326 | IBMACPI_FAN_NONE = 0, /* No fan status or control */ | ||
327 | IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ | ||
328 | IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ | ||
329 | }; | ||
330 | |||
331 | enum fan_control_access_mode { | ||
332 | IBMACPI_FAN_WR_NONE = 0, /* No fan control */ | ||
333 | IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ | ||
334 | IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ | ||
335 | IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ | ||
336 | }; | ||
337 | |||
338 | enum fan_control_commands { | ||
339 | IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ | ||
340 | IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ | ||
341 | IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, | ||
342 | * and also watchdog cmd */ | ||
343 | }; | ||
344 | |||
345 | enum { /* Fan control constants */ | ||
346 | fan_status_offset = 0x2f, /* EC register 0x2f */ | ||
347 | fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) | ||
348 | * 0x84 must be read before 0x85 */ | ||
349 | |||
350 | IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer | ||
351 | * disengaged */ | ||
352 | IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan | ||
353 | * control */ | ||
354 | }; | ||
355 | |||
356 | static char *ibm_thinkpad_ec_found = NULL; | ||
357 | |||
219 | struct ibm_struct { | 358 | struct ibm_struct { |
220 | char *name; | 359 | char *name; |
221 | char param[32]; | 360 | char param[32]; |
@@ -243,6 +382,8 @@ struct ibm_struct { | |||
243 | 382 | ||
244 | static struct proc_dir_entry *proc_dir = NULL; | 383 | static struct proc_dir_entry *proc_dir = NULL; |
245 | 384 | ||
385 | static struct backlight_device *ibm_backlight_device = NULL; | ||
386 | |||
246 | #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") | 387 | #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") |
247 | #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") | 388 | #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") |
248 | #define strlencmp(a,b) (strncmp((a), (b), strlen(b))) | 389 | #define strlencmp(a,b) (strncmp((a), (b), strlen(b))) |
@@ -581,8 +722,7 @@ static int wan_status(void) | |||
581 | { | 722 | { |
582 | int status; | 723 | int status; |
583 | 724 | ||
584 | if (!wan_supported || | 725 | if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d")) |
585 | !acpi_evalf(hkey_handle, &status, "GWAN", "d")) | ||
586 | status = 0; | 726 | status = 0; |
587 | 727 | ||
588 | return status; | 728 | return status; |
@@ -630,12 +770,15 @@ static int wan_write(char *buf) | |||
630 | return 0; | 770 | return 0; |
631 | } | 771 | } |
632 | 772 | ||
633 | static int video_supported; | 773 | enum video_access_mode { |
634 | static int video_orig_autosw; | 774 | IBMACPI_VIDEO_NONE = 0, |
775 | IBMACPI_VIDEO_570, /* 570 */ | ||
776 | IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */ | ||
777 | IBMACPI_VIDEO_NEW, /* all others */ | ||
778 | }; | ||
635 | 779 | ||
636 | #define VIDEO_570 1 | 780 | static enum video_access_mode video_supported; |
637 | #define VIDEO_770 2 | 781 | static int video_orig_autosw; |
638 | #define VIDEO_NEW 3 | ||
639 | 782 | ||
640 | static int video_init(void) | 783 | static int video_init(void) |
641 | { | 784 | { |
@@ -647,16 +790,16 @@ static int video_init(void) | |||
647 | 790 | ||
648 | if (!vid_handle) | 791 | if (!vid_handle) |
649 | /* video switching not supported on R30, R31 */ | 792 | /* video switching not supported on R30, R31 */ |
650 | video_supported = 0; | 793 | video_supported = IBMACPI_VIDEO_NONE; |
651 | else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) | 794 | else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) |
652 | /* 570 */ | 795 | /* 570 */ |
653 | video_supported = VIDEO_570; | 796 | video_supported = IBMACPI_VIDEO_570; |
654 | else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) | 797 | else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) |
655 | /* 600e/x, 770e, 770x */ | 798 | /* 600e/x, 770e, 770x */ |
656 | video_supported = VIDEO_770; | 799 | video_supported = IBMACPI_VIDEO_770; |
657 | else | 800 | else |
658 | /* all others */ | 801 | /* all others */ |
659 | video_supported = VIDEO_NEW; | 802 | video_supported = IBMACPI_VIDEO_NEW; |
660 | 803 | ||
661 | return 0; | 804 | return 0; |
662 | } | 805 | } |
@@ -666,15 +809,15 @@ static int video_status(void) | |||
666 | int status = 0; | 809 | int status = 0; |
667 | int i; | 810 | int i; |
668 | 811 | ||
669 | if (video_supported == VIDEO_570) { | 812 | if (video_supported == IBMACPI_VIDEO_570) { |
670 | if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) | 813 | if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) |
671 | status = i & 3; | 814 | status = i & 3; |
672 | } else if (video_supported == VIDEO_770) { | 815 | } else if (video_supported == IBMACPI_VIDEO_770) { |
673 | if (acpi_evalf(NULL, &i, "\\VCDL", "d")) | 816 | if (acpi_evalf(NULL, &i, "\\VCDL", "d")) |
674 | status |= 0x01 * i; | 817 | status |= 0x01 * i; |
675 | if (acpi_evalf(NULL, &i, "\\VCDC", "d")) | 818 | if (acpi_evalf(NULL, &i, "\\VCDC", "d")) |
676 | status |= 0x02 * i; | 819 | status |= 0x02 * i; |
677 | } else if (video_supported == VIDEO_NEW) { | 820 | } else if (video_supported == IBMACPI_VIDEO_NEW) { |
678 | acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); | 821 | acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); |
679 | if (acpi_evalf(NULL, &i, "\\VCDC", "d")) | 822 | if (acpi_evalf(NULL, &i, "\\VCDC", "d")) |
680 | status |= 0x02 * i; | 823 | status |= 0x02 * i; |
@@ -693,9 +836,10 @@ static int video_autosw(void) | |||
693 | { | 836 | { |
694 | int autosw = 0; | 837 | int autosw = 0; |
695 | 838 | ||
696 | if (video_supported == VIDEO_570) | 839 | if (video_supported == IBMACPI_VIDEO_570) |
697 | acpi_evalf(vid_handle, &autosw, "SWIT", "d"); | 840 | acpi_evalf(vid_handle, &autosw, "SWIT", "d"); |
698 | else if (video_supported == VIDEO_770 || video_supported == VIDEO_NEW) | 841 | else if (video_supported == IBMACPI_VIDEO_770 || |
842 | video_supported == IBMACPI_VIDEO_NEW) | ||
699 | acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); | 843 | acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); |
700 | 844 | ||
701 | return autosw & 1; | 845 | return autosw & 1; |
@@ -715,12 +859,12 @@ static int video_read(char *p) | |||
715 | len += sprintf(p + len, "status:\t\tsupported\n"); | 859 | len += sprintf(p + len, "status:\t\tsupported\n"); |
716 | len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); | 860 | len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); |
717 | len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); | 861 | len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); |
718 | if (video_supported == VIDEO_NEW) | 862 | if (video_supported == IBMACPI_VIDEO_NEW) |
719 | len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); | 863 | len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); |
720 | len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); | 864 | len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); |
721 | len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); | 865 | len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); |
722 | len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); | 866 | len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); |
723 | if (video_supported == VIDEO_NEW) | 867 | if (video_supported == IBMACPI_VIDEO_NEW) |
724 | len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); | 868 | len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); |
725 | len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); | 869 | len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); |
726 | len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); | 870 | len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); |
@@ -735,7 +879,7 @@ static int video_switch(void) | |||
735 | 879 | ||
736 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) | 880 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) |
737 | return -EIO; | 881 | return -EIO; |
738 | ret = video_supported == VIDEO_570 ? | 882 | ret = video_supported == IBMACPI_VIDEO_570 ? |
739 | acpi_evalf(ec_handle, NULL, "_Q16", "v") : | 883 | acpi_evalf(ec_handle, NULL, "_Q16", "v") : |
740 | acpi_evalf(vid_handle, NULL, "VSWT", "v"); | 884 | acpi_evalf(vid_handle, NULL, "VSWT", "v"); |
741 | acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); | 885 | acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); |
@@ -745,9 +889,9 @@ static int video_switch(void) | |||
745 | 889 | ||
746 | static int video_expand(void) | 890 | static int video_expand(void) |
747 | { | 891 | { |
748 | if (video_supported == VIDEO_570) | 892 | if (video_supported == IBMACPI_VIDEO_570) |
749 | return acpi_evalf(ec_handle, NULL, "_Q17", "v"); | 893 | return acpi_evalf(ec_handle, NULL, "_Q17", "v"); |
750 | else if (video_supported == VIDEO_770) | 894 | else if (video_supported == IBMACPI_VIDEO_770) |
751 | return acpi_evalf(vid_handle, NULL, "VEXP", "v"); | 895 | return acpi_evalf(vid_handle, NULL, "VEXP", "v"); |
752 | else | 896 | else |
753 | return acpi_evalf(NULL, NULL, "\\VEXP", "v"); | 897 | return acpi_evalf(NULL, NULL, "\\VEXP", "v"); |
@@ -757,10 +901,10 @@ static int video_switch2(int status) | |||
757 | { | 901 | { |
758 | int ret; | 902 | int ret; |
759 | 903 | ||
760 | if (video_supported == VIDEO_570) { | 904 | if (video_supported == IBMACPI_VIDEO_570) { |
761 | ret = acpi_evalf(NULL, NULL, | 905 | ret = acpi_evalf(NULL, NULL, |
762 | "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); | 906 | "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); |
763 | } else if (video_supported == VIDEO_770) { | 907 | } else if (video_supported == IBMACPI_VIDEO_770) { |
764 | int autosw = video_autosw(); | 908 | int autosw = video_autosw(); |
765 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) | 909 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) |
766 | return -EIO; | 910 | return -EIO; |
@@ -796,10 +940,10 @@ static int video_write(char *buf) | |||
796 | enable |= 0x02; | 940 | enable |= 0x02; |
797 | } else if (strlencmp(cmd, "crt_disable") == 0) { | 941 | } else if (strlencmp(cmd, "crt_disable") == 0) { |
798 | disable |= 0x02; | 942 | disable |= 0x02; |
799 | } else if (video_supported == VIDEO_NEW && | 943 | } else if (video_supported == IBMACPI_VIDEO_NEW && |
800 | strlencmp(cmd, "dvi_enable") == 0) { | 944 | strlencmp(cmd, "dvi_enable") == 0) { |
801 | enable |= 0x08; | 945 | enable |= 0x08; |
802 | } else if (video_supported == VIDEO_NEW && | 946 | } else if (video_supported == IBMACPI_VIDEO_NEW && |
803 | strlencmp(cmd, "dvi_disable") == 0) { | 947 | strlencmp(cmd, "dvi_disable") == 0) { |
804 | disable |= 0x08; | 948 | disable |= 0x08; |
805 | } else if (strlencmp(cmd, "auto_enable") == 0) { | 949 | } else if (strlencmp(cmd, "auto_enable") == 0) { |
@@ -898,6 +1042,7 @@ static int light_write(char *buf) | |||
898 | return 0; | 1042 | return 0; |
899 | } | 1043 | } |
900 | 1044 | ||
1045 | #if defined(CONFIG_ACPI_IBM_DOCK) || defined(CONFIG_ACPI_IBM_BAY) | ||
901 | static int _sta(acpi_handle handle) | 1046 | static int _sta(acpi_handle handle) |
902 | { | 1047 | { |
903 | int status; | 1048 | int status; |
@@ -907,6 +1052,7 @@ static int _sta(acpi_handle handle) | |||
907 | 1052 | ||
908 | return status; | 1053 | return status; |
909 | } | 1054 | } |
1055 | #endif | ||
910 | #ifdef CONFIG_ACPI_IBM_DOCK | 1056 | #ifdef CONFIG_ACPI_IBM_DOCK |
911 | #define dock_docked() (_sta(dock_handle) & 1) | 1057 | #define dock_docked() (_sta(dock_handle) & 1) |
912 | 1058 | ||
@@ -972,6 +1118,7 @@ static void dock_notify(struct ibm_struct *ibm, u32 event) | |||
972 | } | 1118 | } |
973 | #endif | 1119 | #endif |
974 | 1120 | ||
1121 | #ifdef CONFIG_ACPI_IBM_BAY | ||
975 | static int bay_status_supported; | 1122 | static int bay_status_supported; |
976 | static int bay_status2_supported; | 1123 | static int bay_status2_supported; |
977 | static int bay_eject_supported; | 1124 | static int bay_eject_supported; |
@@ -1047,6 +1194,7 @@ static void bay_notify(struct ibm_struct *ibm, u32 event) | |||
1047 | { | 1194 | { |
1048 | acpi_bus_generate_event(ibm->device, event, 0); | 1195 | acpi_bus_generate_event(ibm->device, event, 0); |
1049 | } | 1196 | } |
1197 | #endif | ||
1050 | 1198 | ||
1051 | static int cmos_read(char *p) | 1199 | static int cmos_read(char *p) |
1052 | { | 1200 | { |
@@ -1094,26 +1242,28 @@ static int cmos_write(char *buf) | |||
1094 | return 0; | 1242 | return 0; |
1095 | } | 1243 | } |
1096 | 1244 | ||
1097 | static int led_supported; | 1245 | enum led_access_mode { |
1098 | 1246 | IBMACPI_LED_NONE = 0, | |
1099 | #define LED_570 1 | 1247 | IBMACPI_LED_570, /* 570 */ |
1100 | #define LED_OLD 2 | 1248 | IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ |
1101 | #define LED_NEW 3 | 1249 | IBMACPI_LED_NEW, /* all others */ |
1250 | }; | ||
1251 | static enum led_access_mode led_supported; | ||
1102 | 1252 | ||
1103 | static int led_init(void) | 1253 | static int led_init(void) |
1104 | { | 1254 | { |
1105 | if (!led_handle) | 1255 | if (!led_handle) |
1106 | /* led not supported on R30, R31 */ | 1256 | /* led not supported on R30, R31 */ |
1107 | led_supported = 0; | 1257 | led_supported = IBMACPI_LED_NONE; |
1108 | else if (strlencmp(led_path, "SLED") == 0) | 1258 | else if (strlencmp(led_path, "SLED") == 0) |
1109 | /* 570 */ | 1259 | /* 570 */ |
1110 | led_supported = LED_570; | 1260 | led_supported = IBMACPI_LED_570; |
1111 | else if (strlencmp(led_path, "SYSL") == 0) | 1261 | else if (strlencmp(led_path, "SYSL") == 0) |
1112 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ | 1262 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ |
1113 | led_supported = LED_OLD; | 1263 | led_supported = IBMACPI_LED_OLD; |
1114 | else | 1264 | else |
1115 | /* all others */ | 1265 | /* all others */ |
1116 | led_supported = LED_NEW; | 1266 | led_supported = IBMACPI_LED_NEW; |
1117 | 1267 | ||
1118 | return 0; | 1268 | return 0; |
1119 | } | 1269 | } |
@@ -1130,7 +1280,7 @@ static int led_read(char *p) | |||
1130 | } | 1280 | } |
1131 | len += sprintf(p + len, "status:\t\tsupported\n"); | 1281 | len += sprintf(p + len, "status:\t\tsupported\n"); |
1132 | 1282 | ||
1133 | if (led_supported == LED_570) { | 1283 | if (led_supported == IBMACPI_LED_570) { |
1134 | /* 570 */ | 1284 | /* 570 */ |
1135 | int i, status; | 1285 | int i, status; |
1136 | for (i = 0; i < 8; i++) { | 1286 | for (i = 0; i < 8; i++) { |
@@ -1179,13 +1329,13 @@ static int led_write(char *buf) | |||
1179 | } else | 1329 | } else |
1180 | return -EINVAL; | 1330 | return -EINVAL; |
1181 | 1331 | ||
1182 | if (led_supported == LED_570) { | 1332 | if (led_supported == IBMACPI_LED_570) { |
1183 | /* 570 */ | 1333 | /* 570 */ |
1184 | led = 1 << led; | 1334 | led = 1 << led; |
1185 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | 1335 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", |
1186 | led, led_sled_arg1[ind])) | 1336 | led, led_sled_arg1[ind])) |
1187 | return -EIO; | 1337 | return -EIO; |
1188 | } else if (led_supported == LED_OLD) { | 1338 | } else if (led_supported == IBMACPI_LED_OLD) { |
1189 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ | 1339 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ |
1190 | led = 1 << led; | 1340 | led = 1 << led; |
1191 | ret = ec_write(EC_HLMS, led); | 1341 | ret = ec_write(EC_HLMS, led); |
@@ -1272,50 +1422,142 @@ static int acpi_ec_write(int i, u8 v) | |||
1272 | return 1; | 1422 | return 1; |
1273 | } | 1423 | } |
1274 | 1424 | ||
1275 | static int thermal_tmp_supported; | 1425 | static enum thermal_access_mode thermal_read_mode; |
1276 | static int thermal_updt_supported; | ||
1277 | 1426 | ||
1278 | static int thermal_init(void) | 1427 | static int thermal_init(void) |
1279 | { | 1428 | { |
1280 | /* temperatures not supported on 570, G4x, R30, R31, R32 */ | 1429 | u8 t, ta1, ta2; |
1281 | thermal_tmp_supported = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); | 1430 | int i; |
1431 | int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); | ||
1432 | |||
1433 | if (ibm_thinkpad_ec_found && experimental) { | ||
1434 | /* | ||
1435 | * Direct EC access mode: sensors at registers | ||
1436 | * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for | ||
1437 | * non-implemented, thermal sensors return 0x80 when | ||
1438 | * not available | ||
1439 | */ | ||
1282 | 1440 | ||
1283 | /* 600e/x, 770e, 770x */ | 1441 | ta1 = ta2 = 0; |
1284 | thermal_updt_supported = acpi_evalf(ec_handle, NULL, "UPDT", "qv"); | 1442 | for (i = 0; i < 8; i++) { |
1443 | if (likely(acpi_ec_read(0x78 + i, &t))) { | ||
1444 | ta1 |= t; | ||
1445 | } else { | ||
1446 | ta1 = 0; | ||
1447 | break; | ||
1448 | } | ||
1449 | if (likely(acpi_ec_read(0xC0 + i, &t))) { | ||
1450 | ta2 |= t; | ||
1451 | } else { | ||
1452 | ta1 = 0; | ||
1453 | break; | ||
1454 | } | ||
1455 | } | ||
1456 | if (ta1 == 0) { | ||
1457 | /* This is sheer paranoia, but we handle it anyway */ | ||
1458 | if (acpi_tmp7) { | ||
1459 | printk(IBM_ERR | ||
1460 | "ThinkPad ACPI EC access misbehaving, " | ||
1461 | "falling back to ACPI TMPx access mode\n"); | ||
1462 | thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; | ||
1463 | } else { | ||
1464 | printk(IBM_ERR | ||
1465 | "ThinkPad ACPI EC access misbehaving, " | ||
1466 | "disabling thermal sensors access\n"); | ||
1467 | thermal_read_mode = IBMACPI_THERMAL_NONE; | ||
1468 | } | ||
1469 | } else { | ||
1470 | thermal_read_mode = | ||
1471 | (ta2 != 0) ? | ||
1472 | IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8; | ||
1473 | } | ||
1474 | } else if (acpi_tmp7) { | ||
1475 | if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { | ||
1476 | /* 600e/x, 770e, 770x */ | ||
1477 | thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; | ||
1478 | } else { | ||
1479 | /* Standard ACPI TMPx access, max 8 sensors */ | ||
1480 | thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; | ||
1481 | } | ||
1482 | } else { | ||
1483 | /* temperatures not supported on 570, G4x, R30, R31, R32 */ | ||
1484 | thermal_read_mode = IBMACPI_THERMAL_NONE; | ||
1485 | } | ||
1285 | 1486 | ||
1286 | return 0; | 1487 | return 0; |
1287 | } | 1488 | } |
1288 | 1489 | ||
1289 | static int thermal_read(char *p) | 1490 | static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) |
1290 | { | 1491 | { |
1291 | int len = 0; | 1492 | int i, t; |
1493 | s8 tmp; | ||
1494 | char tmpi[] = "TMPi"; | ||
1292 | 1495 | ||
1293 | if (!thermal_tmp_supported) | 1496 | if (!s) |
1294 | len += sprintf(p + len, "temperatures:\tnot supported\n"); | 1497 | return -EINVAL; |
1295 | else { | ||
1296 | int i, t; | ||
1297 | char tmpi[] = "TMPi"; | ||
1298 | s8 tmp[8]; | ||
1299 | 1498 | ||
1300 | if (thermal_updt_supported) | 1499 | switch (thermal_read_mode) { |
1301 | if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) | 1500 | #if IBMACPI_MAX_THERMAL_SENSORS >= 16 |
1501 | case IBMACPI_THERMAL_TPEC_16: | ||
1502 | for (i = 0; i < 8; i++) { | ||
1503 | if (!acpi_ec_read(0xC0 + i, &tmp)) | ||
1504 | return -EIO; | ||
1505 | s->temp[i + 8] = tmp * 1000; | ||
1506 | } | ||
1507 | /* fallthrough */ | ||
1508 | #endif | ||
1509 | case IBMACPI_THERMAL_TPEC_8: | ||
1510 | for (i = 0; i < 8; i++) { | ||
1511 | if (!acpi_ec_read(0x78 + i, &tmp)) | ||
1302 | return -EIO; | 1512 | return -EIO; |
1513 | s->temp[i] = tmp * 1000; | ||
1514 | } | ||
1515 | return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8; | ||
1303 | 1516 | ||
1517 | case IBMACPI_THERMAL_ACPI_UPDT: | ||
1518 | if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) | ||
1519 | return -EIO; | ||
1304 | for (i = 0; i < 8; i++) { | 1520 | for (i = 0; i < 8; i++) { |
1305 | tmpi[3] = '0' + i; | 1521 | tmpi[3] = '0' + i; |
1306 | if (!acpi_evalf(ec_handle, &t, tmpi, "d")) | 1522 | if (!acpi_evalf(ec_handle, &t, tmpi, "d")) |
1307 | return -EIO; | 1523 | return -EIO; |
1308 | if (thermal_updt_supported) | 1524 | s->temp[i] = (t - 2732) * 100; |
1309 | tmp[i] = (t - 2732 + 5) / 10; | ||
1310 | else | ||
1311 | tmp[i] = t; | ||
1312 | } | 1525 | } |
1526 | return 8; | ||
1313 | 1527 | ||
1314 | len += sprintf(p + len, | 1528 | case IBMACPI_THERMAL_ACPI_TMP07: |
1315 | "temperatures:\t%d %d %d %d %d %d %d %d\n", | 1529 | for (i = 0; i < 8; i++) { |
1316 | tmp[0], tmp[1], tmp[2], tmp[3], | 1530 | tmpi[3] = '0' + i; |
1317 | tmp[4], tmp[5], tmp[6], tmp[7]); | 1531 | if (!acpi_evalf(ec_handle, &t, tmpi, "d")) |
1532 | return -EIO; | ||
1533 | s->temp[i] = t * 1000; | ||
1534 | } | ||
1535 | return 8; | ||
1536 | |||
1537 | case IBMACPI_THERMAL_NONE: | ||
1538 | default: | ||
1539 | return 0; | ||
1318 | } | 1540 | } |
1541 | } | ||
1542 | |||
1543 | static int thermal_read(char *p) | ||
1544 | { | ||
1545 | int len = 0; | ||
1546 | int n, i; | ||
1547 | struct ibm_thermal_sensors_struct t; | ||
1548 | |||
1549 | n = thermal_get_sensors(&t); | ||
1550 | if (unlikely(n < 0)) | ||
1551 | return n; | ||
1552 | |||
1553 | len += sprintf(p + len, "temperatures:\t"); | ||
1554 | |||
1555 | if (n > 0) { | ||
1556 | for (i = 0; i < (n - 1); i++) | ||
1557 | len += sprintf(p + len, "%d ", t.temp[i] / 1000); | ||
1558 | len += sprintf(p + len, "%d\n", t.temp[i] / 1000); | ||
1559 | } else | ||
1560 | len += sprintf(p + len, "not supported\n"); | ||
1319 | 1561 | ||
1320 | return len; | 1562 | return len; |
1321 | } | 1563 | } |
@@ -1381,12 +1623,23 @@ static int ecdump_write(char *buf) | |||
1381 | 1623 | ||
1382 | static int brightness_offset = 0x31; | 1624 | static int brightness_offset = 0x31; |
1383 | 1625 | ||
1626 | static int brightness_get(struct backlight_device *bd) | ||
1627 | { | ||
1628 | u8 level; | ||
1629 | if (!acpi_ec_read(brightness_offset, &level)) | ||
1630 | return -EIO; | ||
1631 | |||
1632 | level &= 0x7; | ||
1633 | |||
1634 | return level; | ||
1635 | } | ||
1636 | |||
1384 | static int brightness_read(char *p) | 1637 | static int brightness_read(char *p) |
1385 | { | 1638 | { |
1386 | int len = 0; | 1639 | int len = 0; |
1387 | u8 level; | 1640 | int level; |
1388 | 1641 | ||
1389 | if (!acpi_ec_read(brightness_offset, &level)) { | 1642 | if ((level = brightness_get(NULL)) < 0) { |
1390 | len += sprintf(p + len, "level:\t\tunreadable\n"); | 1643 | len += sprintf(p + len, "level:\t\tunreadable\n"); |
1391 | } else { | 1644 | } else { |
1392 | len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); | 1645 | len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); |
@@ -1401,16 +1654,34 @@ static int brightness_read(char *p) | |||
1401 | #define BRIGHTNESS_UP 4 | 1654 | #define BRIGHTNESS_UP 4 |
1402 | #define BRIGHTNESS_DOWN 5 | 1655 | #define BRIGHTNESS_DOWN 5 |
1403 | 1656 | ||
1404 | static int brightness_write(char *buf) | 1657 | static int brightness_set(int value) |
1405 | { | 1658 | { |
1406 | int cmos_cmd, inc, i; | 1659 | int cmos_cmd, inc, i; |
1407 | u8 level; | 1660 | int current_value = brightness_get(NULL); |
1661 | |||
1662 | value &= 7; | ||
1663 | |||
1664 | cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; | ||
1665 | inc = value > current_value ? 1 : -1; | ||
1666 | for (i = current_value; i != value; i += inc) { | ||
1667 | if (!cmos_eval(cmos_cmd)) | ||
1668 | return -EIO; | ||
1669 | if (!acpi_ec_write(brightness_offset, i + inc)) | ||
1670 | return -EIO; | ||
1671 | } | ||
1672 | |||
1673 | return 0; | ||
1674 | } | ||
1675 | |||
1676 | static int brightness_write(char *buf) | ||
1677 | { | ||
1678 | int level; | ||
1408 | int new_level; | 1679 | int new_level; |
1409 | char *cmd; | 1680 | char *cmd; |
1410 | 1681 | ||
1411 | while ((cmd = next_cmd(&buf))) { | 1682 | while ((cmd = next_cmd(&buf))) { |
1412 | if (!acpi_ec_read(brightness_offset, &level)) | 1683 | if ((level = brightness_get(NULL)) < 0) |
1413 | return -EIO; | 1684 | return level; |
1414 | level &= 7; | 1685 | level &= 7; |
1415 | 1686 | ||
1416 | if (strlencmp(cmd, "up") == 0) { | 1687 | if (strlencmp(cmd, "up") == 0) { |
@@ -1423,19 +1694,44 @@ static int brightness_write(char *buf) | |||
1423 | } else | 1694 | } else |
1424 | return -EINVAL; | 1695 | return -EINVAL; |
1425 | 1696 | ||
1426 | cmos_cmd = new_level > level ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; | 1697 | brightness_set(new_level); |
1427 | inc = new_level > level ? 1 : -1; | ||
1428 | for (i = level; i != new_level; i += inc) { | ||
1429 | if (!cmos_eval(cmos_cmd)) | ||
1430 | return -EIO; | ||
1431 | if (!acpi_ec_write(brightness_offset, i + inc)) | ||
1432 | return -EIO; | ||
1433 | } | ||
1434 | } | 1698 | } |
1435 | 1699 | ||
1436 | return 0; | 1700 | return 0; |
1437 | } | 1701 | } |
1438 | 1702 | ||
1703 | static int brightness_update_status(struct backlight_device *bd) | ||
1704 | { | ||
1705 | return brightness_set(bd->props->brightness); | ||
1706 | } | ||
1707 | |||
1708 | static struct backlight_properties ibm_backlight_data = { | ||
1709 | .owner = THIS_MODULE, | ||
1710 | .get_brightness = brightness_get, | ||
1711 | .update_status = brightness_update_status, | ||
1712 | .max_brightness = 7, | ||
1713 | }; | ||
1714 | |||
1715 | static int brightness_init(void) | ||
1716 | { | ||
1717 | ibm_backlight_device = backlight_device_register("ibm", NULL, | ||
1718 | &ibm_backlight_data); | ||
1719 | if (IS_ERR(ibm_backlight_device)) { | ||
1720 | printk(IBM_ERR "Could not register backlight device\n"); | ||
1721 | return PTR_ERR(ibm_backlight_device); | ||
1722 | } | ||
1723 | |||
1724 | return 0; | ||
1725 | } | ||
1726 | |||
1727 | static void brightness_exit(void) | ||
1728 | { | ||
1729 | if (ibm_backlight_device) { | ||
1730 | backlight_device_unregister(ibm_backlight_device); | ||
1731 | ibm_backlight_device = NULL; | ||
1732 | } | ||
1733 | } | ||
1734 | |||
1439 | static int volume_offset = 0x30; | 1735 | static int volume_offset = 0x30; |
1440 | 1736 | ||
1441 | static int volume_read(char *p) | 1737 | static int volume_read(char *p) |
@@ -1522,86 +1818,482 @@ static int volume_write(char *buf) | |||
1522 | return 0; | 1818 | return 0; |
1523 | } | 1819 | } |
1524 | 1820 | ||
1525 | static int fan_status_offset = 0x2f; | 1821 | static enum fan_status_access_mode fan_status_access_mode; |
1526 | static int fan_rpm_offset = 0x84; | 1822 | static enum fan_control_access_mode fan_control_access_mode; |
1823 | static enum fan_control_commands fan_control_commands; | ||
1527 | 1824 | ||
1528 | static int fan_read(char *p) | 1825 | static int fan_control_status_known; |
1826 | static u8 fan_control_initial_status; | ||
1827 | |||
1828 | static void fan_watchdog_fire(void *ignored); | ||
1829 | static int fan_watchdog_maxinterval; | ||
1830 | static DECLARE_WORK(fan_watchdog_task, fan_watchdog_fire, NULL); | ||
1831 | |||
1832 | static int fan_init(void) | ||
1529 | { | 1833 | { |
1530 | int len = 0; | 1834 | fan_status_access_mode = IBMACPI_FAN_NONE; |
1531 | int s; | 1835 | fan_control_access_mode = IBMACPI_FAN_WR_NONE; |
1532 | u8 lo, hi, status; | 1836 | fan_control_commands = 0; |
1837 | fan_control_status_known = 1; | ||
1838 | fan_watchdog_maxinterval = 0; | ||
1533 | 1839 | ||
1534 | if (gfan_handle) { | 1840 | if (gfan_handle) { |
1535 | /* 570, 600e/x, 770e, 770x */ | 1841 | /* 570, 600e/x, 770e, 770x */ |
1536 | if (!acpi_evalf(gfan_handle, &s, NULL, "d")) | 1842 | fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN; |
1537 | return -EIO; | 1843 | } else { |
1844 | /* all other ThinkPads: note that even old-style | ||
1845 | * ThinkPad ECs supports the fan control register */ | ||
1846 | if (likely(acpi_ec_read(fan_status_offset, | ||
1847 | &fan_control_initial_status))) { | ||
1848 | fan_status_access_mode = IBMACPI_FAN_RD_TPEC; | ||
1849 | |||
1850 | /* In some ThinkPads, neither the EC nor the ACPI | ||
1851 | * DSDT initialize the fan status, and it ends up | ||
1852 | * being set to 0x07 when it *could* be either | ||
1853 | * 0x07 or 0x80. | ||
1854 | * | ||
1855 | * Enable for TP-1Y (T43), TP-78 (R51e), | ||
1856 | * TP-76 (R52), TP-70 (T43, R52), which are known | ||
1857 | * to be buggy. */ | ||
1858 | if (fan_control_initial_status == 0x07 && | ||
1859 | ibm_thinkpad_ec_found && | ||
1860 | ((ibm_thinkpad_ec_found[0] == '1' && | ||
1861 | ibm_thinkpad_ec_found[1] == 'Y') || | ||
1862 | (ibm_thinkpad_ec_found[0] == '7' && | ||
1863 | (ibm_thinkpad_ec_found[1] == '6' || | ||
1864 | ibm_thinkpad_ec_found[1] == '8' || | ||
1865 | ibm_thinkpad_ec_found[1] == '0')) | ||
1866 | )) { | ||
1867 | printk(IBM_NOTICE | ||
1868 | "fan_init: initial fan status is " | ||
1869 | "unknown, assuming it is in auto " | ||
1870 | "mode\n"); | ||
1871 | fan_control_status_known = 0; | ||
1872 | } | ||
1873 | } else { | ||
1874 | printk(IBM_ERR | ||
1875 | "ThinkPad ACPI EC access misbehaving, " | ||
1876 | "fan status and control unavailable\n"); | ||
1877 | return 0; | ||
1878 | } | ||
1879 | } | ||
1538 | 1880 | ||
1539 | len += sprintf(p + len, "level:\t\t%d\n", s); | 1881 | if (sfan_handle) { |
1882 | /* 570, 770x-JL */ | ||
1883 | fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN; | ||
1884 | fan_control_commands |= | ||
1885 | IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; | ||
1540 | } else { | 1886 | } else { |
1887 | if (!gfan_handle) { | ||
1888 | /* gfan without sfan means no fan control */ | ||
1889 | /* all other models implement TP EC 0x2f control */ | ||
1890 | |||
1891 | if (fans_handle) { | ||
1892 | /* X31, X40, X41 */ | ||
1893 | fan_control_access_mode = | ||
1894 | IBMACPI_FAN_WR_ACPI_FANS; | ||
1895 | fan_control_commands |= | ||
1896 | IBMACPI_FAN_CMD_SPEED | | ||
1897 | IBMACPI_FAN_CMD_LEVEL | | ||
1898 | IBMACPI_FAN_CMD_ENABLE; | ||
1899 | } else { | ||
1900 | fan_control_access_mode = IBMACPI_FAN_WR_TPEC; | ||
1901 | fan_control_commands |= | ||
1902 | IBMACPI_FAN_CMD_LEVEL | | ||
1903 | IBMACPI_FAN_CMD_ENABLE; | ||
1904 | } | ||
1905 | } | ||
1906 | } | ||
1907 | |||
1908 | return 0; | ||
1909 | } | ||
1910 | |||
1911 | static int fan_get_status(u8 *status) | ||
1912 | { | ||
1913 | u8 s; | ||
1914 | |||
1915 | /* TODO: | ||
1916 | * Add IBMACPI_FAN_RD_ACPI_FANS ? */ | ||
1917 | |||
1918 | switch (fan_status_access_mode) { | ||
1919 | case IBMACPI_FAN_RD_ACPI_GFAN: | ||
1920 | /* 570, 600e/x, 770e, 770x */ | ||
1921 | |||
1922 | if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) | ||
1923 | return -EIO; | ||
1924 | |||
1925 | if (likely(status)) | ||
1926 | *status = s & 0x07; | ||
1927 | |||
1928 | break; | ||
1929 | |||
1930 | case IBMACPI_FAN_RD_TPEC: | ||
1541 | /* all except 570, 600e/x, 770e, 770x */ | 1931 | /* all except 570, 600e/x, 770e, 770x */ |
1542 | if (!acpi_ec_read(fan_status_offset, &status)) | 1932 | if (unlikely(!acpi_ec_read(fan_status_offset, &s))) |
1543 | len += sprintf(p + len, "status:\t\tunreadable\n"); | 1933 | return -EIO; |
1544 | else | ||
1545 | len += sprintf(p + len, "status:\t\t%s\n", | ||
1546 | enabled(status, 7)); | ||
1547 | 1934 | ||
1548 | if (!acpi_ec_read(fan_rpm_offset, &lo) || | 1935 | if (likely(status)) |
1549 | !acpi_ec_read(fan_rpm_offset + 1, &hi)) | 1936 | *status = s; |
1550 | len += sprintf(p + len, "speed:\t\tunreadable\n"); | 1937 | |
1551 | else | 1938 | break; |
1552 | len += sprintf(p + len, "speed:\t\t%d\n", | 1939 | |
1553 | (hi << 8) + lo); | 1940 | default: |
1941 | return -ENXIO; | ||
1554 | } | 1942 | } |
1555 | 1943 | ||
1556 | if (sfan_handle) | 1944 | return 0; |
1557 | /* 570, 770x-JL */ | 1945 | } |
1558 | len += sprintf(p + len, "commands:\tlevel <level>" | 1946 | |
1559 | " (<level> is 0-7)\n"); | 1947 | static int fan_get_speed(unsigned int *speed) |
1560 | if (!gfan_handle) | 1948 | { |
1949 | u8 hi, lo; | ||
1950 | |||
1951 | switch (fan_status_access_mode) { | ||
1952 | case IBMACPI_FAN_RD_TPEC: | ||
1561 | /* all except 570, 600e/x, 770e, 770x */ | 1953 | /* all except 570, 600e/x, 770e, 770x */ |
1562 | len += sprintf(p + len, "commands:\tenable, disable\n"); | 1954 | if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || |
1563 | if (fans_handle) | 1955 | !acpi_ec_read(fan_rpm_offset + 1, &hi))) |
1564 | /* X31, X40 */ | 1956 | return -EIO; |
1957 | |||
1958 | if (likely(speed)) | ||
1959 | *speed = (hi << 8) | lo; | ||
1960 | |||
1961 | break; | ||
1962 | |||
1963 | default: | ||
1964 | return -ENXIO; | ||
1965 | } | ||
1966 | |||
1967 | return 0; | ||
1968 | } | ||
1969 | |||
1970 | static void fan_exit(void) | ||
1971 | { | ||
1972 | cancel_delayed_work(&fan_watchdog_task); | ||
1973 | flush_scheduled_work(); | ||
1974 | } | ||
1975 | |||
1976 | static void fan_watchdog_reset(void) | ||
1977 | { | ||
1978 | static int fan_watchdog_active = 0; | ||
1979 | |||
1980 | if (fan_watchdog_active) | ||
1981 | cancel_delayed_work(&fan_watchdog_task); | ||
1982 | |||
1983 | if (fan_watchdog_maxinterval > 0) { | ||
1984 | fan_watchdog_active = 1; | ||
1985 | if (!schedule_delayed_work(&fan_watchdog_task, | ||
1986 | msecs_to_jiffies(fan_watchdog_maxinterval | ||
1987 | * 1000))) { | ||
1988 | printk(IBM_ERR "failed to schedule the fan watchdog, " | ||
1989 | "watchdog will not trigger\n"); | ||
1990 | } | ||
1991 | } else | ||
1992 | fan_watchdog_active = 0; | ||
1993 | } | ||
1994 | |||
1995 | static int fan_read(char *p) | ||
1996 | { | ||
1997 | int len = 0; | ||
1998 | int rc; | ||
1999 | u8 status; | ||
2000 | unsigned int speed = 0; | ||
2001 | |||
2002 | switch (fan_status_access_mode) { | ||
2003 | case IBMACPI_FAN_RD_ACPI_GFAN: | ||
2004 | /* 570, 600e/x, 770e, 770x */ | ||
2005 | if ((rc = fan_get_status(&status)) < 0) | ||
2006 | return rc; | ||
2007 | |||
2008 | len += sprintf(p + len, "status:\t\t%s\n" | ||
2009 | "level:\t\t%d\n", | ||
2010 | (status != 0) ? "enabled" : "disabled", status); | ||
2011 | break; | ||
2012 | |||
2013 | case IBMACPI_FAN_RD_TPEC: | ||
2014 | /* all except 570, 600e/x, 770e, 770x */ | ||
2015 | if ((rc = fan_get_status(&status)) < 0) | ||
2016 | return rc; | ||
2017 | |||
2018 | if (unlikely(!fan_control_status_known)) { | ||
2019 | if (status != fan_control_initial_status) | ||
2020 | fan_control_status_known = 1; | ||
2021 | else | ||
2022 | /* Return most likely status. In fact, it | ||
2023 | * might be the only possible status */ | ||
2024 | status = IBMACPI_FAN_EC_AUTO; | ||
2025 | } | ||
2026 | |||
2027 | len += sprintf(p + len, "status:\t\t%s\n", | ||
2028 | (status != 0) ? "enabled" : "disabled"); | ||
2029 | |||
2030 | /* No ThinkPad boots on disengaged mode, we can safely | ||
2031 | * assume the tachometer is online if fan control status | ||
2032 | * was unknown */ | ||
2033 | if ((rc = fan_get_speed(&speed)) < 0) | ||
2034 | return rc; | ||
2035 | |||
2036 | len += sprintf(p + len, "speed:\t\t%d\n", speed); | ||
2037 | |||
2038 | if (status & IBMACPI_FAN_EC_DISENGAGED) | ||
2039 | /* Disengaged mode takes precedence */ | ||
2040 | len += sprintf(p + len, "level:\t\tdisengaged\n"); | ||
2041 | else if (status & IBMACPI_FAN_EC_AUTO) | ||
2042 | len += sprintf(p + len, "level:\t\tauto\n"); | ||
2043 | else | ||
2044 | len += sprintf(p + len, "level:\t\t%d\n", status); | ||
2045 | break; | ||
2046 | |||
2047 | case IBMACPI_FAN_NONE: | ||
2048 | default: | ||
2049 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
2050 | } | ||
2051 | |||
2052 | if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) { | ||
2053 | len += sprintf(p + len, "commands:\tlevel <level>"); | ||
2054 | |||
2055 | switch (fan_control_access_mode) { | ||
2056 | case IBMACPI_FAN_WR_ACPI_SFAN: | ||
2057 | len += sprintf(p + len, " (<level> is 0-7)\n"); | ||
2058 | break; | ||
2059 | |||
2060 | default: | ||
2061 | len += sprintf(p + len, " (<level> is 0-7, " | ||
2062 | "auto, disengaged)\n"); | ||
2063 | break; | ||
2064 | } | ||
2065 | } | ||
2066 | |||
2067 | if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) | ||
2068 | len += sprintf(p + len, "commands:\tenable, disable\n" | ||
2069 | "commands:\twatchdog <timeout> (<timeout> is 0 (off), " | ||
2070 | "1-120 (seconds))\n"); | ||
2071 | |||
2072 | if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) | ||
1565 | len += sprintf(p + len, "commands:\tspeed <speed>" | 2073 | len += sprintf(p + len, "commands:\tspeed <speed>" |
1566 | " (<speed> is 0-65535)\n"); | 2074 | " (<speed> is 0-65535)\n"); |
1567 | 2075 | ||
1568 | return len; | 2076 | return len; |
1569 | } | 2077 | } |
1570 | 2078 | ||
1571 | static int fan_write(char *buf) | 2079 | static int fan_set_level(int level) |
1572 | { | 2080 | { |
1573 | char *cmd; | 2081 | switch (fan_control_access_mode) { |
1574 | int level, speed; | 2082 | case IBMACPI_FAN_WR_ACPI_SFAN: |
1575 | 2083 | if (level >= 0 && level <= 7) { | |
1576 | while ((cmd = next_cmd(&buf))) { | ||
1577 | if (sfan_handle && | ||
1578 | sscanf(cmd, "level %d", &level) == 1 && | ||
1579 | level >= 0 && level <= 7) { | ||
1580 | /* 570, 770x-JL */ | ||
1581 | if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) | 2084 | if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) |
1582 | return -EIO; | 2085 | return -EIO; |
1583 | } else if (!gfan_handle && strlencmp(cmd, "enable") == 0) { | 2086 | } else |
1584 | /* all except 570, 600e/x, 770e, 770x */ | 2087 | return -EINVAL; |
1585 | if (!acpi_ec_write(fan_status_offset, 0x80)) | 2088 | break; |
1586 | return -EIO; | 2089 | |
1587 | } else if (!gfan_handle && strlencmp(cmd, "disable") == 0) { | 2090 | case IBMACPI_FAN_WR_ACPI_FANS: |
1588 | /* all except 570, 600e/x, 770e, 770x */ | 2091 | case IBMACPI_FAN_WR_TPEC: |
1589 | if (!acpi_ec_write(fan_status_offset, 0x00)) | 2092 | if ((level != IBMACPI_FAN_EC_AUTO) && |
1590 | return -EIO; | 2093 | (level != IBMACPI_FAN_EC_DISENGAGED) && |
1591 | } else if (fans_handle && | 2094 | ((level < 0) || (level > 7))) |
1592 | sscanf(cmd, "speed %d", &speed) == 1 && | 2095 | return -EINVAL; |
1593 | speed >= 0 && speed <= 65535) { | 2096 | |
1594 | /* X31, X40 */ | 2097 | if (!acpi_ec_write(fan_status_offset, level)) |
2098 | return -EIO; | ||
2099 | else | ||
2100 | fan_control_status_known = 1; | ||
2101 | break; | ||
2102 | |||
2103 | default: | ||
2104 | return -ENXIO; | ||
2105 | } | ||
2106 | return 0; | ||
2107 | } | ||
2108 | |||
2109 | static int fan_set_enable(void) | ||
2110 | { | ||
2111 | u8 s; | ||
2112 | int rc; | ||
2113 | |||
2114 | switch (fan_control_access_mode) { | ||
2115 | case IBMACPI_FAN_WR_ACPI_FANS: | ||
2116 | case IBMACPI_FAN_WR_TPEC: | ||
2117 | if ((rc = fan_get_status(&s)) < 0) | ||
2118 | return rc; | ||
2119 | |||
2120 | /* Don't go out of emergency fan mode */ | ||
2121 | if (s != 7) | ||
2122 | s = IBMACPI_FAN_EC_AUTO; | ||
2123 | |||
2124 | if (!acpi_ec_write(fan_status_offset, s)) | ||
2125 | return -EIO; | ||
2126 | else | ||
2127 | fan_control_status_known = 1; | ||
2128 | break; | ||
2129 | |||
2130 | case IBMACPI_FAN_WR_ACPI_SFAN: | ||
2131 | if ((rc = fan_get_status(&s)) < 0) | ||
2132 | return rc; | ||
2133 | |||
2134 | s &= 0x07; | ||
2135 | |||
2136 | /* Set fan to at least level 4 */ | ||
2137 | if (s < 4) | ||
2138 | s = 4; | ||
2139 | |||
2140 | if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s)) | ||
2141 | return -EIO; | ||
2142 | break; | ||
2143 | |||
2144 | default: | ||
2145 | return -ENXIO; | ||
2146 | } | ||
2147 | return 0; | ||
2148 | } | ||
2149 | |||
2150 | static int fan_set_disable(void) | ||
2151 | { | ||
2152 | switch (fan_control_access_mode) { | ||
2153 | case IBMACPI_FAN_WR_ACPI_FANS: | ||
2154 | case IBMACPI_FAN_WR_TPEC: | ||
2155 | if (!acpi_ec_write(fan_status_offset, 0x00)) | ||
2156 | return -EIO; | ||
2157 | else | ||
2158 | fan_control_status_known = 1; | ||
2159 | break; | ||
2160 | |||
2161 | case IBMACPI_FAN_WR_ACPI_SFAN: | ||
2162 | if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) | ||
2163 | return -EIO; | ||
2164 | break; | ||
2165 | |||
2166 | default: | ||
2167 | return -ENXIO; | ||
2168 | } | ||
2169 | return 0; | ||
2170 | } | ||
2171 | |||
2172 | static int fan_set_speed(int speed) | ||
2173 | { | ||
2174 | switch (fan_control_access_mode) { | ||
2175 | case IBMACPI_FAN_WR_ACPI_FANS: | ||
2176 | if (speed >= 0 && speed <= 65535) { | ||
1595 | if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", | 2177 | if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", |
1596 | speed, speed, speed)) | 2178 | speed, speed, speed)) |
1597 | return -EIO; | 2179 | return -EIO; |
1598 | } else | 2180 | } else |
1599 | return -EINVAL; | 2181 | return -EINVAL; |
1600 | } | 2182 | break; |
1601 | 2183 | ||
2184 | default: | ||
2185 | return -ENXIO; | ||
2186 | } | ||
1602 | return 0; | 2187 | return 0; |
1603 | } | 2188 | } |
1604 | 2189 | ||
2190 | static int fan_write_cmd_level(const char *cmd, int *rc) | ||
2191 | { | ||
2192 | int level; | ||
2193 | |||
2194 | if (strlencmp(cmd, "level auto") == 0) | ||
2195 | level = IBMACPI_FAN_EC_AUTO; | ||
2196 | else if (strlencmp(cmd, "level disengaged") == 0) | ||
2197 | level = IBMACPI_FAN_EC_DISENGAGED; | ||
2198 | else if (sscanf(cmd, "level %d", &level) != 1) | ||
2199 | return 0; | ||
2200 | |||
2201 | if ((*rc = fan_set_level(level)) == -ENXIO) | ||
2202 | printk(IBM_ERR "level command accepted for unsupported " | ||
2203 | "access mode %d", fan_control_access_mode); | ||
2204 | |||
2205 | return 1; | ||
2206 | } | ||
2207 | |||
2208 | static int fan_write_cmd_enable(const char *cmd, int *rc) | ||
2209 | { | ||
2210 | if (strlencmp(cmd, "enable") != 0) | ||
2211 | return 0; | ||
2212 | |||
2213 | if ((*rc = fan_set_enable()) == -ENXIO) | ||
2214 | printk(IBM_ERR "enable command accepted for unsupported " | ||
2215 | "access mode %d", fan_control_access_mode); | ||
2216 | |||
2217 | return 1; | ||
2218 | } | ||
2219 | |||
2220 | static int fan_write_cmd_disable(const char *cmd, int *rc) | ||
2221 | { | ||
2222 | if (strlencmp(cmd, "disable") != 0) | ||
2223 | return 0; | ||
2224 | |||
2225 | if ((*rc = fan_set_disable()) == -ENXIO) | ||
2226 | printk(IBM_ERR "disable command accepted for unsupported " | ||
2227 | "access mode %d", fan_control_access_mode); | ||
2228 | |||
2229 | return 1; | ||
2230 | } | ||
2231 | |||
2232 | static int fan_write_cmd_speed(const char *cmd, int *rc) | ||
2233 | { | ||
2234 | int speed; | ||
2235 | |||
2236 | /* TODO: | ||
2237 | * Support speed <low> <medium> <high> ? */ | ||
2238 | |||
2239 | if (sscanf(cmd, "speed %d", &speed) != 1) | ||
2240 | return 0; | ||
2241 | |||
2242 | if ((*rc = fan_set_speed(speed)) == -ENXIO) | ||
2243 | printk(IBM_ERR "speed command accepted for unsupported " | ||
2244 | "access mode %d", fan_control_access_mode); | ||
2245 | |||
2246 | return 1; | ||
2247 | } | ||
2248 | |||
2249 | static int fan_write_cmd_watchdog(const char *cmd, int *rc) | ||
2250 | { | ||
2251 | int interval; | ||
2252 | |||
2253 | if (sscanf(cmd, "watchdog %d", &interval) != 1) | ||
2254 | return 0; | ||
2255 | |||
2256 | if (interval < 0 || interval > 120) | ||
2257 | *rc = -EINVAL; | ||
2258 | else | ||
2259 | fan_watchdog_maxinterval = interval; | ||
2260 | |||
2261 | return 1; | ||
2262 | } | ||
2263 | |||
2264 | static int fan_write(char *buf) | ||
2265 | { | ||
2266 | char *cmd; | ||
2267 | int rc = 0; | ||
2268 | |||
2269 | while (!rc && (cmd = next_cmd(&buf))) { | ||
2270 | if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) && | ||
2271 | fan_write_cmd_level(cmd, &rc)) && | ||
2272 | !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && | ||
2273 | (fan_write_cmd_enable(cmd, &rc) || | ||
2274 | fan_write_cmd_disable(cmd, &rc) || | ||
2275 | fan_write_cmd_watchdog(cmd, &rc))) && | ||
2276 | !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && | ||
2277 | fan_write_cmd_speed(cmd, &rc)) | ||
2278 | ) | ||
2279 | rc = -EINVAL; | ||
2280 | else if (!rc) | ||
2281 | fan_watchdog_reset(); | ||
2282 | } | ||
2283 | |||
2284 | return rc; | ||
2285 | } | ||
2286 | |||
2287 | static void fan_watchdog_fire(void *ignored) | ||
2288 | { | ||
2289 | printk(IBM_NOTICE "fan watchdog: enabling fan\n"); | ||
2290 | if (fan_set_enable()) { | ||
2291 | printk(IBM_ERR "fan watchdog: error while enabling fan\n"); | ||
2292 | /* reschedule for later */ | ||
2293 | fan_watchdog_reset(); | ||
2294 | } | ||
2295 | } | ||
2296 | |||
1605 | static struct ibm_struct ibms[] = { | 2297 | static struct ibm_struct ibms[] = { |
1606 | { | 2298 | { |
1607 | .name = "driver", | 2299 | .name = "driver", |
@@ -1662,6 +2354,7 @@ static struct ibm_struct ibms[] = { | |||
1662 | .type = ACPI_SYSTEM_NOTIFY, | 2354 | .type = ACPI_SYSTEM_NOTIFY, |
1663 | }, | 2355 | }, |
1664 | #endif | 2356 | #endif |
2357 | #ifdef CONFIG_ACPI_IBM_BAY | ||
1665 | { | 2358 | { |
1666 | .name = "bay", | 2359 | .name = "bay", |
1667 | .init = bay_init, | 2360 | .init = bay_init, |
@@ -1671,6 +2364,7 @@ static struct ibm_struct ibms[] = { | |||
1671 | .handle = &bay_handle, | 2364 | .handle = &bay_handle, |
1672 | .type = ACPI_SYSTEM_NOTIFY, | 2365 | .type = ACPI_SYSTEM_NOTIFY, |
1673 | }, | 2366 | }, |
2367 | #endif | ||
1674 | { | 2368 | { |
1675 | .name = "cmos", | 2369 | .name = "cmos", |
1676 | .read = cmos_read, | 2370 | .read = cmos_read, |
@@ -1702,6 +2396,8 @@ static struct ibm_struct ibms[] = { | |||
1702 | .name = "brightness", | 2396 | .name = "brightness", |
1703 | .read = brightness_read, | 2397 | .read = brightness_read, |
1704 | .write = brightness_write, | 2398 | .write = brightness_write, |
2399 | .init = brightness_init, | ||
2400 | .exit = brightness_exit, | ||
1705 | }, | 2401 | }, |
1706 | { | 2402 | { |
1707 | .name = "volume", | 2403 | .name = "volume", |
@@ -1712,6 +2408,8 @@ static struct ibm_struct ibms[] = { | |||
1712 | .name = "fan", | 2408 | .name = "fan", |
1713 | .read = fan_read, | 2409 | .read = fan_read, |
1714 | .write = fan_write, | 2410 | .write = fan_write, |
2411 | .init = fan_init, | ||
2412 | .exit = fan_exit, | ||
1715 | .experimental = 1, | 2413 | .experimental = 1, |
1716 | }, | 2414 | }, |
1717 | }; | 2415 | }; |
@@ -1825,7 +2523,7 @@ static int __init register_driver(struct ibm_struct *ibm) | |||
1825 | } | 2523 | } |
1826 | 2524 | ||
1827 | memset(ibm->driver, 0, sizeof(struct acpi_driver)); | 2525 | memset(ibm->driver, 0, sizeof(struct acpi_driver)); |
1828 | sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name); | 2526 | sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name); |
1829 | ibm->driver->ids = ibm->hid; | 2527 | ibm->driver->ids = ibm->hid; |
1830 | ibm->driver->ops.add = &ibm_device_add; | 2528 | ibm->driver->ops.add = &ibm_device_add; |
1831 | 2529 | ||
@@ -1954,7 +2652,9 @@ IBM_PARAM(light); | |||
1954 | #ifdef CONFIG_ACPI_IBM_DOCK | 2652 | #ifdef CONFIG_ACPI_IBM_DOCK |
1955 | IBM_PARAM(dock); | 2653 | IBM_PARAM(dock); |
1956 | #endif | 2654 | #endif |
2655 | #ifdef CONFIG_ACPI_IBM_BAY | ||
1957 | IBM_PARAM(bay); | 2656 | IBM_PARAM(bay); |
2657 | #endif | ||
1958 | IBM_PARAM(cmos); | 2658 | IBM_PARAM(cmos); |
1959 | IBM_PARAM(led); | 2659 | IBM_PARAM(led); |
1960 | IBM_PARAM(beep); | 2660 | IBM_PARAM(beep); |
@@ -1971,6 +2671,33 @@ static void acpi_ibm_exit(void) | |||
1971 | ibm_exit(&ibms[i]); | 2671 | ibm_exit(&ibms[i]); |
1972 | 2672 | ||
1973 | remove_proc_entry(IBM_DIR, acpi_root_dir); | 2673 | remove_proc_entry(IBM_DIR, acpi_root_dir); |
2674 | |||
2675 | if (ibm_thinkpad_ec_found) | ||
2676 | kfree(ibm_thinkpad_ec_found); | ||
2677 | } | ||
2678 | |||
2679 | static char* __init check_dmi_for_ec(void) | ||
2680 | { | ||
2681 | struct dmi_device *dev = NULL; | ||
2682 | char ec_fw_string[18]; | ||
2683 | |||
2684 | /* | ||
2685 | * ThinkPad T23 or newer, A31 or newer, R50e or newer, | ||
2686 | * X32 or newer, all Z series; Some models must have an | ||
2687 | * up-to-date BIOS or they will not be detected. | ||
2688 | * | ||
2689 | * See http://thinkwiki.org/wiki/List_of_DMI_IDs | ||
2690 | */ | ||
2691 | while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { | ||
2692 | if (sscanf(dev->name, | ||
2693 | "IBM ThinkPad Embedded Controller -[%17c", | ||
2694 | ec_fw_string) == 1) { | ||
2695 | ec_fw_string[sizeof(ec_fw_string) - 1] = 0; | ||
2696 | ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; | ||
2697 | return kstrdup(ec_fw_string, GFP_KERNEL); | ||
2698 | } | ||
2699 | } | ||
2700 | return NULL; | ||
1974 | } | 2701 | } |
1975 | 2702 | ||
1976 | static int __init acpi_ibm_init(void) | 2703 | static int __init acpi_ibm_init(void) |
@@ -1992,6 +2719,12 @@ static int __init acpi_ibm_init(void) | |||
1992 | return -ENODEV; | 2719 | return -ENODEV; |
1993 | } | 2720 | } |
1994 | 2721 | ||
2722 | /* Models with newer firmware report the EC in DMI */ | ||
2723 | ibm_thinkpad_ec_found = check_dmi_for_ec(); | ||
2724 | if (ibm_thinkpad_ec_found) | ||
2725 | printk(IBM_INFO "ThinkPad EC firmware %s\n", | ||
2726 | ibm_thinkpad_ec_found); | ||
2727 | |||
1995 | /* these handles are not required */ | 2728 | /* these handles are not required */ |
1996 | IBM_HANDLE_INIT(vid); | 2729 | IBM_HANDLE_INIT(vid); |
1997 | IBM_HANDLE_INIT(vid2); | 2730 | IBM_HANDLE_INIT(vid2); |
@@ -2004,12 +2737,14 @@ static int __init acpi_ibm_init(void) | |||
2004 | IBM_HANDLE_INIT(dock); | 2737 | IBM_HANDLE_INIT(dock); |
2005 | #endif | 2738 | #endif |
2006 | IBM_HANDLE_INIT(pci); | 2739 | IBM_HANDLE_INIT(pci); |
2740 | #ifdef CONFIG_ACPI_IBM_BAY | ||
2007 | IBM_HANDLE_INIT(bay); | 2741 | IBM_HANDLE_INIT(bay); |
2008 | if (bay_handle) | 2742 | if (bay_handle) |
2009 | IBM_HANDLE_INIT(bay_ej); | 2743 | IBM_HANDLE_INIT(bay_ej); |
2010 | IBM_HANDLE_INIT(bay2); | 2744 | IBM_HANDLE_INIT(bay2); |
2011 | if (bay2_handle) | 2745 | if (bay2_handle) |
2012 | IBM_HANDLE_INIT(bay2_ej); | 2746 | IBM_HANDLE_INIT(bay2_ej); |
2747 | #endif | ||
2013 | IBM_HANDLE_INIT(beep); | 2748 | IBM_HANDLE_INIT(beep); |
2014 | IBM_HANDLE_INIT(ecrd); | 2749 | IBM_HANDLE_INIT(ecrd); |
2015 | IBM_HANDLE_INIT(ecwr); | 2750 | IBM_HANDLE_INIT(ecwr); |
diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c index 7fe0b7ae973..2f35f891593 100644 --- a/drivers/acpi/toshiba_acpi.c +++ b/drivers/acpi/toshiba_acpi.c | |||
@@ -41,6 +41,8 @@ | |||
41 | #include <linux/init.h> | 41 | #include <linux/init.h> |
42 | #include <linux/types.h> | 42 | #include <linux/types.h> |
43 | #include <linux/proc_fs.h> | 43 | #include <linux/proc_fs.h> |
44 | #include <linux/backlight.h> | ||
45 | |||
44 | #include <asm/uaccess.h> | 46 | #include <asm/uaccess.h> |
45 | 47 | ||
46 | #include <acpi/acpi_drivers.h> | 48 | #include <acpi/acpi_drivers.h> |
@@ -210,6 +212,7 @@ static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result) | |||
210 | } | 212 | } |
211 | 213 | ||
212 | static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; | 214 | static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; |
215 | static struct backlight_device *toshiba_backlight_device; | ||
213 | static int force_fan; | 216 | static int force_fan; |
214 | static int last_key_event; | 217 | static int last_key_event; |
215 | static int key_event_valid; | 218 | static int key_event_valid; |
@@ -271,14 +274,23 @@ dispatch_write(struct file *file, const char __user * buffer, | |||
271 | return result; | 274 | return result; |
272 | } | 275 | } |
273 | 276 | ||
274 | static char *read_lcd(char *p) | 277 | static int get_lcd(struct backlight_device *bd) |
275 | { | 278 | { |
276 | u32 hci_result; | 279 | u32 hci_result; |
277 | u32 value; | 280 | u32 value; |
278 | 281 | ||
279 | hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result); | 282 | hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result); |
280 | if (hci_result == HCI_SUCCESS) { | 283 | if (hci_result == HCI_SUCCESS) { |
281 | value = value >> HCI_LCD_BRIGHTNESS_SHIFT; | 284 | return (value >> HCI_LCD_BRIGHTNESS_SHIFT); |
285 | } else | ||
286 | return -EFAULT; | ||
287 | } | ||
288 | |||
289 | static char *read_lcd(char *p) | ||
290 | { | ||
291 | int value = get_lcd(NULL); | ||
292 | |||
293 | if (value >= 0) { | ||
282 | p += sprintf(p, "brightness: %d\n", value); | 294 | p += sprintf(p, "brightness: %d\n", value); |
283 | p += sprintf(p, "brightness_levels: %d\n", | 295 | p += sprintf(p, "brightness_levels: %d\n", |
284 | HCI_LCD_BRIGHTNESS_LEVELS); | 296 | HCI_LCD_BRIGHTNESS_LEVELS); |
@@ -289,22 +301,34 @@ static char *read_lcd(char *p) | |||
289 | return p; | 301 | return p; |
290 | } | 302 | } |
291 | 303 | ||
304 | static int set_lcd(int value) | ||
305 | { | ||
306 | u32 hci_result; | ||
307 | |||
308 | value = value << HCI_LCD_BRIGHTNESS_SHIFT; | ||
309 | hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); | ||
310 | if (hci_result != HCI_SUCCESS) | ||
311 | return -EFAULT; | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int set_lcd_status(struct backlight_device *bd) | ||
317 | { | ||
318 | return set_lcd(bd->props->brightness); | ||
319 | } | ||
320 | |||
292 | static unsigned long write_lcd(const char *buffer, unsigned long count) | 321 | static unsigned long write_lcd(const char *buffer, unsigned long count) |
293 | { | 322 | { |
294 | int value; | 323 | int value; |
295 | u32 hci_result; | 324 | int ret = count; |
296 | 325 | ||
297 | if (sscanf(buffer, " brightness : %i", &value) == 1 && | 326 | if (sscanf(buffer, " brightness : %i", &value) == 1 && |
298 | value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { | 327 | value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) |
299 | value = value << HCI_LCD_BRIGHTNESS_SHIFT; | 328 | ret = set_lcd(value); |
300 | hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); | 329 | else |
301 | if (hci_result != HCI_SUCCESS) | 330 | ret = -EINVAL; |
302 | return -EFAULT; | 331 | return ret; |
303 | } else { | ||
304 | return -EINVAL; | ||
305 | } | ||
306 | |||
307 | return count; | ||
308 | } | 332 | } |
309 | 333 | ||
310 | static char *read_video(char *p) | 334 | static char *read_video(char *p) |
@@ -506,6 +530,26 @@ static acpi_status __exit remove_device(void) | |||
506 | return AE_OK; | 530 | return AE_OK; |
507 | } | 531 | } |
508 | 532 | ||
533 | static struct backlight_properties toshiba_backlight_data = { | ||
534 | .owner = THIS_MODULE, | ||
535 | .get_brightness = get_lcd, | ||
536 | .update_status = set_lcd_status, | ||
537 | .max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1, | ||
538 | }; | ||
539 | |||
540 | static void __exit toshiba_acpi_exit(void) | ||
541 | { | ||
542 | if (toshiba_backlight_device) | ||
543 | backlight_device_unregister(toshiba_backlight_device); | ||
544 | |||
545 | remove_device(); | ||
546 | |||
547 | if (toshiba_proc_dir) | ||
548 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | ||
549 | |||
550 | return; | ||
551 | } | ||
552 | |||
509 | static int __init toshiba_acpi_init(void) | 553 | static int __init toshiba_acpi_init(void) |
510 | { | 554 | { |
511 | acpi_status status = AE_OK; | 555 | acpi_status status = AE_OK; |
@@ -546,17 +590,15 @@ static int __init toshiba_acpi_init(void) | |||
546 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | 590 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); |
547 | } | 591 | } |
548 | 592 | ||
549 | return (ACPI_SUCCESS(status)) ? 0 : -ENODEV; | 593 | toshiba_backlight_device = backlight_device_register("toshiba", NULL, |
550 | } | 594 | &toshiba_backlight_data); |
551 | 595 | if (IS_ERR(toshiba_backlight_device)) { | |
552 | static void __exit toshiba_acpi_exit(void) | 596 | printk(KERN_ERR "Could not register toshiba backlight device\n"); |
553 | { | 597 | toshiba_backlight_device = NULL; |
554 | remove_device(); | 598 | toshiba_acpi_exit(); |
555 | 599 | } | |
556 | if (toshiba_proc_dir) | ||
557 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | ||
558 | 600 | ||
559 | return; | 601 | return (ACPI_SUCCESS(status)) ? 0 : -ENODEV; |
560 | } | 602 | } |
561 | 603 | ||
562 | module_init(toshiba_acpi_init); | 604 | module_init(toshiba_acpi_init); |