diff options
-rw-r--r-- | Documentation/thinkpad-acpi.txt | 157 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 326 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.h | 6 |
3 files changed, 415 insertions, 74 deletions
diff --git a/Documentation/thinkpad-acpi.txt b/Documentation/thinkpad-acpi.txt index 80c0bf28e392..339ce21e59df 100644 --- a/Documentation/thinkpad-acpi.txt +++ b/Documentation/thinkpad-acpi.txt | |||
@@ -642,8 +642,11 @@ distinct. The unmute the volume after the mute command, use either the | |||
642 | up or down command (the level command will not unmute the volume). | 642 | up or down command (the level command will not unmute the volume). |
643 | The current volume level and mute state is shown in the file. | 643 | The current volume level and mute state is shown in the file. |
644 | 644 | ||
645 | EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan | 645 | EXPERIMENTAL: fan speed, fan enable/disable |
646 | ----------------------------------------------------------------- | 646 | ------------------------------------------- |
647 | |||
648 | procfs: /proc/acpi/ibm/fan | ||
649 | sysfs device attributes: (hwmon) fan_input, pwm1, pwm1_enable | ||
647 | 650 | ||
648 | This feature is marked EXPERIMENTAL because the implementation | 651 | This feature is marked EXPERIMENTAL because the implementation |
649 | directly accesses hardware registers and may not work as expected. USE | 652 | directly accesses hardware registers and may not work as expected. USE |
@@ -656,27 +659,26 @@ from the hardware registers of the embedded controller. This is known | |||
656 | to work on later R, T and X series ThinkPads but may show a bogus | 659 | to work on later R, T and X series ThinkPads but may show a bogus |
657 | value on other models. | 660 | value on other models. |
658 | 661 | ||
659 | Most ThinkPad fans work in "levels". Level 0 stops the fan. The higher | 662 | Fan levels: |
660 | the level, the higher the fan speed, although adjacent levels often map | ||
661 | to the same fan speed. 7 is the highest level, where the fan reaches | ||
662 | the maximum recommended speed. Level "auto" means the EC changes the | ||
663 | fan level according to some internal algorithm, usually based on | ||
664 | readings from the thermal sensors. Level "disengaged" means the EC | ||
665 | disables the speed-locked closed-loop fan control, and drives the fan as | ||
666 | fast as it can go, which might exceed hardware limits, so use this level | ||
667 | with caution. | ||
668 | 663 | ||
669 | The fan usually ramps up or down slowly from one speed to another, | 664 | Most ThinkPad fans work in "levels" at the firmware interface. Level 0 |
670 | and it is normal for the EC to take several seconds to react to fan | 665 | stops the fan. The higher the level, the higher the fan speed, although |
671 | commands. | 666 | adjacent levels often map to the same fan speed. 7 is the highest |
667 | level, where the fan reaches the maximum recommended speed. | ||
672 | 668 | ||
673 | The fan may be enabled or disabled with the following commands: | 669 | Level "auto" means the EC changes the fan level according to some |
670 | internal algorithm, usually based on readings from the thermal sensors. | ||
674 | 671 | ||
675 | echo enable >/proc/acpi/ibm/fan | 672 | There is also a "full-speed" level, also known as "disengaged" level. |
676 | echo disable >/proc/acpi/ibm/fan | 673 | In this level, the EC disables the speed-locked closed-loop fan control, |
674 | and drives the fan as fast as it can go, which might exceed hardware | ||
675 | limits, so use this level with caution. | ||
677 | 676 | ||
678 | Placing a fan on level 0 is the same as disabling it. Enabling a fan | 677 | The fan usually ramps up or down slowly from one speed to another, and |
679 | will try to place it in a safe level if it is too slow or disabled. | 678 | it is normal for the EC to take several seconds to react to fan |
679 | commands. The full-speed level may take up to two minutes to ramp up to | ||
680 | maximum speed, and in some ThinkPads, the tachometer readings go stale | ||
681 | while the EC is transitioning to the full-speed level. | ||
680 | 682 | ||
681 | WARNING WARNING WARNING: do not leave the fan disabled unless you are | 683 | WARNING WARNING WARNING: do not leave the fan disabled unless you are |
682 | monitoring all of the temperature sensor readings and you are ready to | 684 | monitoring all of the temperature sensor readings and you are ready to |
@@ -694,48 +696,101 @@ fan is turned off when the CPU temperature drops to 49 degrees and the | |||
694 | HDD temperature drops to 41 degrees. These thresholds cannot | 696 | HDD temperature drops to 41 degrees. These thresholds cannot |
695 | currently be controlled. | 697 | currently be controlled. |
696 | 698 | ||
699 | The ThinkPad's ACPI DSDT code will reprogram the fan on its own when | ||
700 | certain conditions are met. It will override any fan programming done | ||
701 | through thinkpad-acpi. | ||
702 | |||
703 | The thinkpad-acpi kernel driver can be programmed to revert the fan | ||
704 | level to a safe setting if userspace does not issue one of the procfs | ||
705 | fan commands: "enable", "disable", "level" or "watchdog", or if there | ||
706 | are no writes to pwm1_enable (or to pwm1 *if and only if* pwm1_enable is | ||
707 | set to 1, manual mode) within a configurable amount of time of up to | ||
708 | 120 seconds. This functionality is called fan safety watchdog. | ||
709 | |||
710 | Note that the watchdog timer stops after it enables the fan. It will be | ||
711 | rearmed again automatically (using the same interval) when one of the | ||
712 | above mentioned fan commands is received. The fan watchdog is, | ||
713 | therefore, not suitable to protect against fan mode changes made through | ||
714 | means other than the "enable", "disable", and "level" procfs fan | ||
715 | commands, or the hwmon fan control sysfs interface. | ||
716 | |||
717 | Procfs notes: | ||
718 | |||
719 | The fan may be enabled or disabled with the following commands: | ||
720 | |||
721 | echo enable >/proc/acpi/ibm/fan | ||
722 | echo disable >/proc/acpi/ibm/fan | ||
723 | |||
724 | Placing a fan on level 0 is the same as disabling it. Enabling a fan | ||
725 | will try to place it in a safe level if it is too slow or disabled. | ||
726 | |||
697 | The fan level can be controlled with the command: | 727 | The fan level can be controlled with the command: |
698 | 728 | ||
699 | echo 'level <level>' > /proc/acpi/ibm/thermal | 729 | echo 'level <level>' > /proc/acpi/ibm/fan |
700 | 730 | ||
701 | Where <level> is an integer from 0 to 7, or one of the words "auto" | 731 | Where <level> is an integer from 0 to 7, or one of the words "auto" or |
702 | or "disengaged" (without the quotes). Not all ThinkPads support the | 732 | "full-speed" (without the quotes). Not all ThinkPads support the "auto" |
703 | "auto" and "disengaged" levels. | 733 | and "full-speed" levels. The driver accepts "disengaged" as an alias for |
734 | "full-speed", and reports it as "disengaged" for backwards | ||
735 | compatibility. | ||
704 | 736 | ||
705 | On the X31 and X40 (and ONLY on those models), the fan speed can be | 737 | On the X31 and X40 (and ONLY on those models), the fan speed can be |
706 | controlled to a certain degree. Once the fan is running, it can be | 738 | controlled to a certain degree. Once the fan is running, it can be |
707 | forced to run faster or slower with the following command: | 739 | forced to run faster or slower with the following command: |
708 | 740 | ||
709 | echo 'speed <speed>' > /proc/acpi/ibm/thermal | 741 | echo 'speed <speed>' > /proc/acpi/ibm/fan |
710 | 742 | ||
711 | The sustainable range of fan speeds on the X40 appears to be from | 743 | The sustainable range of fan speeds on the X40 appears to be from about |
712 | about 3700 to about 7350. Values outside this range either do not have | 744 | 3700 to about 7350. Values outside this range either do not have any |
713 | any effect or the fan speed eventually settles somewhere in that | 745 | effect or the fan speed eventually settles somewhere in that range. The |
714 | range. The fan cannot be stopped or started with this command. | 746 | fan cannot be stopped or started with this command. This functionality |
747 | is incomplete, and not available through the sysfs interface. | ||
715 | 748 | ||
716 | The ThinkPad's ACPI DSDT code will reprogram the fan on its own when | 749 | To program the safety watchdog, use the "watchdog" command. |
717 | certain conditions are met. It will override any fan programming done | 750 | |
718 | through thinkpad-acpi. | 751 | echo 'watchdog <interval in seconds>' > /proc/acpi/ibm/fan |
752 | |||
753 | If you want to disable the watchdog, use 0 as the interval. | ||
754 | |||
755 | Sysfs notes: | ||
756 | |||
757 | The sysfs interface follows the hwmon subsystem guidelines for the most | ||
758 | part, and the exception is the fan safety watchdog. | ||
759 | |||
760 | hwmon device attribute pwm1_enable: | ||
761 | 0: PWM offline (fan is set to full-speed mode) | ||
762 | 1: Manual PWM control (use pwm1 to set fan level) | ||
763 | 2: Hardware PWM control (EC "auto" mode) | ||
764 | 3: reserved (Software PWM control, not implemented yet) | ||
765 | |||
766 | Modes 0 and 2 are not supported by all ThinkPads, and the driver | ||
767 | is not always able to detect this. If it does know a mode is | ||
768 | unsupported, it will return -EINVAL. | ||
769 | |||
770 | hwmon device attribute pwm1: | ||
771 | Fan level, scaled from the firmware values of 0-7 to the hwmon | ||
772 | scale of 0-255. 0 means fan stopped, 255 means highest normal | ||
773 | speed (level 7). | ||
774 | |||
775 | This attribute only commands the fan if pmw1_enable is set to 1 | ||
776 | (manual PWM control). | ||
777 | |||
778 | hwmon device attribute fan1_input: | ||
779 | Fan tachometer reading, in RPM. May go stale on certain | ||
780 | ThinkPads while the EC transitions the PWM to offline mode, | ||
781 | which can take up to two minutes. May return rubbish on older | ||
782 | ThinkPads. | ||
783 | |||
784 | driver attribute fan_watchdog: | ||
785 | Fan safety watchdog timer interval, in seconds. Minimum is | ||
786 | 1 second, maximum is 120 seconds. 0 disables the watchdog. | ||
787 | |||
788 | To stop the fan: set pwm1 to zero, and pwm1_enable to 1. | ||
789 | |||
790 | To start the fan in a safe mode: set pwm1_enable to 2. If that fails | ||
791 | with ENOTSUP, set it to 1 and set pwm1 to at least 128 (255 would be the | ||
792 | safest choice, though). | ||
719 | 793 | ||
720 | The thinkpad-acpi kernel driver can be programmed to revert the fan | ||
721 | level to a safe setting if userspace does not issue one of the fan | ||
722 | commands: "enable", "disable", "level" or "watchdog" within a | ||
723 | configurable ammount of time. To do this, use the "watchdog" command. | ||
724 | |||
725 | echo 'watchdog <interval>' > /proc/acpi/ibm/fan | ||
726 | |||
727 | Interval is the ammount of time in seconds to wait for one of the | ||
728 | above mentioned fan commands before reseting the fan level to a safe | ||
729 | one. If set to zero, the watchdog is disabled (default). When the | ||
730 | watchdog timer runs out, it does the exact equivalent of the "enable" | ||
731 | fan command. | ||
732 | |||
733 | Note that the watchdog timer stops after it enables the fan. It will | ||
734 | be rearmed again automatically (using the same interval) when one of | ||
735 | the above mentioned fan commands is received. The fan watchdog is, | ||
736 | therefore, not suitable to protect against fan mode changes made | ||
737 | through means other than the "enable", "disable", and "level" fan | ||
738 | commands. | ||
739 | 794 | ||
740 | EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan | 795 | EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan |
741 | --------------------------------------- | 796 | --------------------------------------- |
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index d5526e882ddd..a4d7ee472396 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -2695,6 +2695,7 @@ static enum fan_control_access_mode fan_control_access_mode; | |||
2695 | static enum fan_control_commands fan_control_commands; | 2695 | static enum fan_control_commands fan_control_commands; |
2696 | 2696 | ||
2697 | static u8 fan_control_initial_status; | 2697 | static u8 fan_control_initial_status; |
2698 | static u8 fan_control_desired_level; | ||
2698 | 2699 | ||
2699 | static void fan_watchdog_fire(struct work_struct *ignored); | 2700 | static void fan_watchdog_fire(struct work_struct *ignored); |
2700 | static int fan_watchdog_maxinterval; | 2701 | static int fan_watchdog_maxinterval; |
@@ -2708,8 +2709,222 @@ IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ | |||
2708 | "JFNS", /* 770x-JL */ | 2709 | "JFNS", /* 770x-JL */ |
2709 | ); /* all others */ | 2710 | ); /* all others */ |
2710 | 2711 | ||
2712 | /* | ||
2713 | * SYSFS fan layout: hwmon compatible (device) | ||
2714 | * | ||
2715 | * pwm*_enable: | ||
2716 | * 0: "disengaged" mode | ||
2717 | * 1: manual mode | ||
2718 | * 2: native EC "auto" mode (recommended, hardware default) | ||
2719 | * | ||
2720 | * pwm*: set speed in manual mode, ignored otherwise. | ||
2721 | * 0 is level 0; 255 is level 7. Intermediate points done with linear | ||
2722 | * interpolation. | ||
2723 | * | ||
2724 | * fan*_input: tachometer reading, RPM | ||
2725 | * | ||
2726 | * | ||
2727 | * SYSFS fan layout: extensions | ||
2728 | * | ||
2729 | * fan_watchdog (driver): | ||
2730 | * fan watchdog interval in seconds, 0 disables (default), max 120 | ||
2731 | */ | ||
2732 | |||
2733 | /* sysfs fan pwm1_enable ----------------------------------------------- */ | ||
2734 | static ssize_t fan_pwm1_enable_show(struct device *dev, | ||
2735 | struct device_attribute *attr, | ||
2736 | char *buf) | ||
2737 | { | ||
2738 | int res, mode; | ||
2739 | u8 status; | ||
2740 | |||
2741 | res = fan_get_status_safe(&status); | ||
2742 | if (res) | ||
2743 | return res; | ||
2744 | |||
2745 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
2746 | if (status != fan_control_initial_status) { | ||
2747 | tp_features.fan_ctrl_status_undef = 0; | ||
2748 | } else { | ||
2749 | /* Return most likely status. In fact, it | ||
2750 | * might be the only possible status */ | ||
2751 | status = TP_EC_FAN_AUTO; | ||
2752 | } | ||
2753 | } | ||
2754 | |||
2755 | if (status & TP_EC_FAN_FULLSPEED) { | ||
2756 | mode = 0; | ||
2757 | } else if (status & TP_EC_FAN_AUTO) { | ||
2758 | mode = 2; | ||
2759 | } else | ||
2760 | mode = 1; | ||
2761 | |||
2762 | return snprintf(buf, PAGE_SIZE, "%d\n", mode); | ||
2763 | } | ||
2764 | |||
2765 | static ssize_t fan_pwm1_enable_store(struct device *dev, | ||
2766 | struct device_attribute *attr, | ||
2767 | const char *buf, size_t count) | ||
2768 | { | ||
2769 | unsigned long t; | ||
2770 | int res, level; | ||
2771 | |||
2772 | if (parse_strtoul(buf, 2, &t)) | ||
2773 | return -EINVAL; | ||
2774 | |||
2775 | switch (t) { | ||
2776 | case 0: | ||
2777 | level = TP_EC_FAN_FULLSPEED; | ||
2778 | break; | ||
2779 | case 1: | ||
2780 | level = TPACPI_FAN_LAST_LEVEL; | ||
2781 | break; | ||
2782 | case 2: | ||
2783 | level = TP_EC_FAN_AUTO; | ||
2784 | break; | ||
2785 | case 3: | ||
2786 | /* reserved for software-controlled auto mode */ | ||
2787 | return -ENOSYS; | ||
2788 | default: | ||
2789 | return -EINVAL; | ||
2790 | } | ||
2791 | |||
2792 | res = fan_set_level_safe(level); | ||
2793 | if (res < 0) | ||
2794 | return res; | ||
2795 | |||
2796 | fan_watchdog_reset(); | ||
2797 | |||
2798 | return count; | ||
2799 | } | ||
2800 | |||
2801 | static struct device_attribute dev_attr_fan_pwm1_enable = | ||
2802 | __ATTR(pwm1_enable, S_IWUSR | S_IRUGO, | ||
2803 | fan_pwm1_enable_show, fan_pwm1_enable_store); | ||
2804 | |||
2805 | /* sysfs fan pwm1 ------------------------------------------------------ */ | ||
2806 | static ssize_t fan_pwm1_show(struct device *dev, | ||
2807 | struct device_attribute *attr, | ||
2808 | char *buf) | ||
2809 | { | ||
2810 | int res; | ||
2811 | u8 status; | ||
2812 | |||
2813 | res = fan_get_status_safe(&status); | ||
2814 | if (res) | ||
2815 | return res; | ||
2816 | |||
2817 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | ||
2818 | if (status != fan_control_initial_status) { | ||
2819 | tp_features.fan_ctrl_status_undef = 0; | ||
2820 | } else { | ||
2821 | status = TP_EC_FAN_AUTO; | ||
2822 | } | ||
2823 | } | ||
2824 | |||
2825 | if ((status & | ||
2826 | (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0) | ||
2827 | status = fan_control_desired_level; | ||
2828 | |||
2829 | if (status > 7) | ||
2830 | status = 7; | ||
2831 | |||
2832 | return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7); | ||
2833 | } | ||
2834 | |||
2835 | static ssize_t fan_pwm1_store(struct device *dev, | ||
2836 | struct device_attribute *attr, | ||
2837 | const char *buf, size_t count) | ||
2838 | { | ||
2839 | unsigned long s; | ||
2840 | int rc; | ||
2841 | u8 status, newlevel; | ||
2842 | |||
2843 | if (parse_strtoul(buf, 255, &s)) | ||
2844 | return -EINVAL; | ||
2845 | |||
2846 | /* scale down from 0-255 to 0-7 */ | ||
2847 | newlevel = (s >> 5) & 0x07; | ||
2848 | |||
2849 | rc = mutex_lock_interruptible(&fan_mutex); | ||
2850 | if (rc < 0) | ||
2851 | return rc; | ||
2852 | |||
2853 | rc = fan_get_status(&status); | ||
2854 | if (!rc && (status & | ||
2855 | (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) { | ||
2856 | rc = fan_set_level(newlevel); | ||
2857 | if (!rc) | ||
2858 | fan_update_desired_level(newlevel); | ||
2859 | fan_watchdog_reset(); | ||
2860 | } | ||
2861 | |||
2862 | mutex_unlock(&fan_mutex); | ||
2863 | return (rc)? rc : count; | ||
2864 | } | ||
2865 | |||
2866 | static struct device_attribute dev_attr_fan_pwm1 = | ||
2867 | __ATTR(pwm1, S_IWUSR | S_IRUGO, | ||
2868 | fan_pwm1_show, fan_pwm1_store); | ||
2869 | |||
2870 | /* sysfs fan fan1_input ------------------------------------------------ */ | ||
2871 | static ssize_t fan_fan1_input_show(struct device *dev, | ||
2872 | struct device_attribute *attr, | ||
2873 | char *buf) | ||
2874 | { | ||
2875 | int res; | ||
2876 | unsigned int speed; | ||
2877 | |||
2878 | res = fan_get_speed(&speed); | ||
2879 | if (res < 0) | ||
2880 | return res; | ||
2881 | |||
2882 | return snprintf(buf, PAGE_SIZE, "%u\n", speed); | ||
2883 | } | ||
2884 | |||
2885 | static struct device_attribute dev_attr_fan_fan1_input = | ||
2886 | __ATTR(fan1_input, S_IRUGO, | ||
2887 | fan_fan1_input_show, NULL); | ||
2888 | |||
2889 | /* sysfs fan fan_watchdog (driver) ------------------------------------- */ | ||
2890 | static ssize_t fan_fan_watchdog_show(struct device_driver *drv, | ||
2891 | char *buf) | ||
2892 | { | ||
2893 | return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval); | ||
2894 | } | ||
2895 | |||
2896 | static ssize_t fan_fan_watchdog_store(struct device_driver *drv, | ||
2897 | const char *buf, size_t count) | ||
2898 | { | ||
2899 | unsigned long t; | ||
2900 | |||
2901 | if (parse_strtoul(buf, 120, &t)) | ||
2902 | return -EINVAL; | ||
2903 | |||
2904 | fan_watchdog_maxinterval = t; | ||
2905 | fan_watchdog_reset(); | ||
2906 | |||
2907 | return count; | ||
2908 | } | ||
2909 | |||
2910 | static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO, | ||
2911 | fan_fan_watchdog_show, fan_fan_watchdog_store); | ||
2912 | |||
2913 | /* --------------------------------------------------------------------- */ | ||
2914 | static struct attribute *fan_attributes[] = { | ||
2915 | &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, | ||
2916 | &dev_attr_fan_fan1_input.attr, | ||
2917 | NULL | ||
2918 | }; | ||
2919 | |||
2920 | static const struct attribute_group fan_attr_group = { | ||
2921 | .attrs = fan_attributes, | ||
2922 | }; | ||
2923 | |||
2711 | static int __init fan_init(struct ibm_init_struct *iibm) | 2924 | static int __init fan_init(struct ibm_init_struct *iibm) |
2712 | { | 2925 | { |
2926 | int rc; | ||
2927 | |||
2713 | vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n"); | 2928 | vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n"); |
2714 | 2929 | ||
2715 | mutex_init(&fan_mutex); | 2930 | mutex_init(&fan_mutex); |
@@ -2718,6 +2933,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
2718 | fan_control_commands = 0; | 2933 | fan_control_commands = 0; |
2719 | fan_watchdog_maxinterval = 0; | 2934 | fan_watchdog_maxinterval = 0; |
2720 | tp_features.fan_ctrl_status_undef = 0; | 2935 | tp_features.fan_ctrl_status_undef = 0; |
2936 | fan_control_desired_level = 7; | ||
2721 | 2937 | ||
2722 | IBM_ACPIHANDLE_INIT(fans); | 2938 | IBM_ACPIHANDLE_INIT(fans); |
2723 | IBM_ACPIHANDLE_INIT(gfan); | 2939 | IBM_ACPIHANDLE_INIT(gfan); |
@@ -2796,9 +3012,36 @@ static int __init fan_init(struct ibm_init_struct *iibm) | |||
2796 | fan_control_access_mode != TPACPI_FAN_WR_NONE), | 3012 | fan_control_access_mode != TPACPI_FAN_WR_NONE), |
2797 | fan_status_access_mode, fan_control_access_mode); | 3013 | fan_status_access_mode, fan_control_access_mode); |
2798 | 3014 | ||
2799 | return (fan_status_access_mode != TPACPI_FAN_NONE || | 3015 | /* update fan_control_desired_level */ |
2800 | fan_control_access_mode != TPACPI_FAN_WR_NONE)? | 3016 | if (fan_status_access_mode != TPACPI_FAN_NONE) |
2801 | 0 : 1; | 3017 | fan_get_status_safe(NULL); |
3018 | |||
3019 | if (fan_status_access_mode != TPACPI_FAN_NONE || | ||
3020 | fan_control_access_mode != TPACPI_FAN_WR_NONE) { | ||
3021 | rc = sysfs_create_group(&tpacpi_pdev->dev.kobj, | ||
3022 | &fan_attr_group); | ||
3023 | if (!(rc < 0)) | ||
3024 | rc = driver_create_file(&tpacpi_pdriver.driver, | ||
3025 | &driver_attr_fan_watchdog); | ||
3026 | if (rc < 0) | ||
3027 | return rc; | ||
3028 | return 0; | ||
3029 | } else | ||
3030 | return 1; | ||
3031 | } | ||
3032 | |||
3033 | /* | ||
3034 | * Call with fan_mutex held | ||
3035 | */ | ||
3036 | static void fan_update_desired_level(u8 status) | ||
3037 | { | ||
3038 | if ((status & | ||
3039 | (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) { | ||
3040 | if (status > 7) | ||
3041 | fan_control_desired_level = 7; | ||
3042 | else | ||
3043 | fan_control_desired_level = status; | ||
3044 | } | ||
2802 | } | 3045 | } |
2803 | 3046 | ||
2804 | static int fan_get_status(u8 *status) | 3047 | static int fan_get_status(u8 *status) |
@@ -2837,9 +3080,33 @@ static int fan_get_status(u8 *status) | |||
2837 | return 0; | 3080 | return 0; |
2838 | } | 3081 | } |
2839 | 3082 | ||
3083 | static int fan_get_status_safe(u8 *status) | ||
3084 | { | ||
3085 | int rc; | ||
3086 | u8 s; | ||
3087 | |||
3088 | rc = mutex_lock_interruptible(&fan_mutex); | ||
3089 | if (rc < 0) | ||
3090 | return rc; | ||
3091 | rc = fan_get_status(&s); | ||
3092 | if (!rc) | ||
3093 | fan_update_desired_level(s); | ||
3094 | mutex_unlock(&fan_mutex); | ||
3095 | |||
3096 | if (status) | ||
3097 | *status = s; | ||
3098 | |||
3099 | return rc; | ||
3100 | } | ||
3101 | |||
2840 | static void fan_exit(void) | 3102 | static void fan_exit(void) |
2841 | { | 3103 | { |
2842 | vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n"); | 3104 | vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n"); |
3105 | |||
3106 | /* FIXME: can we really do this unconditionally? */ | ||
3107 | sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group); | ||
3108 | driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog); | ||
3109 | |||
2843 | cancel_delayed_work(&fan_watchdog_task); | 3110 | cancel_delayed_work(&fan_watchdog_task); |
2844 | flush_scheduled_work(); | 3111 | flush_scheduled_work(); |
2845 | } | 3112 | } |
@@ -2902,17 +3169,10 @@ static void fan_watchdog_reset(void) | |||
2902 | 3169 | ||
2903 | static int fan_set_level(int level) | 3170 | static int fan_set_level(int level) |
2904 | { | 3171 | { |
2905 | int res; | ||
2906 | |||
2907 | switch (fan_control_access_mode) { | 3172 | switch (fan_control_access_mode) { |
2908 | case TPACPI_FAN_WR_ACPI_SFAN: | 3173 | case TPACPI_FAN_WR_ACPI_SFAN: |
2909 | if (level >= 0 && level <= 7) { | 3174 | if (level >= 0 && level <= 7) { |
2910 | res = mutex_lock_interruptible(&fan_mutex); | 3175 | if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) |
2911 | if (res < 0) | ||
2912 | return res; | ||
2913 | res = acpi_evalf(sfan_handle, NULL, NULL, "vd", level); | ||
2914 | mutex_unlock(&fan_mutex); | ||
2915 | if (!res) | ||
2916 | return -EIO; | 3176 | return -EIO; |
2917 | } else | 3177 | } else |
2918 | return -EINVAL; | 3178 | return -EINVAL; |
@@ -2925,12 +3185,7 @@ static int fan_set_level(int level) | |||
2925 | ((level < 0) || (level > 7))) | 3185 | ((level < 0) || (level > 7))) |
2926 | return -EINVAL; | 3186 | return -EINVAL; |
2927 | 3187 | ||
2928 | res = mutex_lock_interruptible(&fan_mutex); | 3188 | if (!acpi_ec_write(fan_status_offset, level)) |
2929 | if (res < 0) | ||
2930 | return res; | ||
2931 | res = acpi_ec_write(fan_status_offset, level); | ||
2932 | mutex_unlock(&fan_mutex); | ||
2933 | if (!res) | ||
2934 | return -EIO; | 3189 | return -EIO; |
2935 | else | 3190 | else |
2936 | tp_features.fan_ctrl_status_undef = 0; | 3191 | tp_features.fan_ctrl_status_undef = 0; |
@@ -2942,6 +3197,25 @@ static int fan_set_level(int level) | |||
2942 | return 0; | 3197 | return 0; |
2943 | } | 3198 | } |
2944 | 3199 | ||
3200 | static int fan_set_level_safe(int level) | ||
3201 | { | ||
3202 | int rc; | ||
3203 | |||
3204 | rc = mutex_lock_interruptible(&fan_mutex); | ||
3205 | if (rc < 0) | ||
3206 | return rc; | ||
3207 | |||
3208 | if (level == TPACPI_FAN_LAST_LEVEL) | ||
3209 | level = fan_control_desired_level; | ||
3210 | |||
3211 | rc = fan_set_level(level); | ||
3212 | if (!rc) | ||
3213 | fan_update_desired_level(level); | ||
3214 | |||
3215 | mutex_unlock(&fan_mutex); | ||
3216 | return rc; | ||
3217 | } | ||
3218 | |||
2945 | static int fan_set_enable(void) | 3219 | static int fan_set_enable(void) |
2946 | { | 3220 | { |
2947 | u8 s; | 3221 | u8 s; |
@@ -3009,19 +3283,24 @@ static int fan_set_disable(void) | |||
3009 | case TPACPI_FAN_WR_TPEC: | 3283 | case TPACPI_FAN_WR_TPEC: |
3010 | if (!acpi_ec_write(fan_status_offset, 0x00)) | 3284 | if (!acpi_ec_write(fan_status_offset, 0x00)) |
3011 | rc = -EIO; | 3285 | rc = -EIO; |
3012 | else | 3286 | else { |
3287 | fan_control_desired_level = 0; | ||
3013 | tp_features.fan_ctrl_status_undef = 0; | 3288 | tp_features.fan_ctrl_status_undef = 0; |
3289 | } | ||
3014 | break; | 3290 | break; |
3015 | 3291 | ||
3016 | case TPACPI_FAN_WR_ACPI_SFAN: | 3292 | case TPACPI_FAN_WR_ACPI_SFAN: |
3017 | if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) | 3293 | if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) |
3018 | rc = -EIO; | 3294 | rc = -EIO; |
3295 | else | ||
3296 | fan_control_desired_level = 0; | ||
3019 | break; | 3297 | break; |
3020 | 3298 | ||
3021 | default: | 3299 | default: |
3022 | rc = -ENXIO; | 3300 | rc = -ENXIO; |
3023 | } | 3301 | } |
3024 | 3302 | ||
3303 | |||
3025 | mutex_unlock(&fan_mutex); | 3304 | mutex_unlock(&fan_mutex); |
3026 | return rc; | 3305 | return rc; |
3027 | } | 3306 | } |
@@ -3063,7 +3342,7 @@ static int fan_read(char *p) | |||
3063 | switch (fan_status_access_mode) { | 3342 | switch (fan_status_access_mode) { |
3064 | case TPACPI_FAN_RD_ACPI_GFAN: | 3343 | case TPACPI_FAN_RD_ACPI_GFAN: |
3065 | /* 570, 600e/x, 770e, 770x */ | 3344 | /* 570, 600e/x, 770e, 770x */ |
3066 | if ((rc = fan_get_status(&status)) < 0) | 3345 | if ((rc = fan_get_status_safe(&status)) < 0) |
3067 | return rc; | 3346 | return rc; |
3068 | 3347 | ||
3069 | len += sprintf(p + len, "status:\t\t%s\n" | 3348 | len += sprintf(p + len, "status:\t\t%s\n" |
@@ -3073,7 +3352,7 @@ static int fan_read(char *p) | |||
3073 | 3352 | ||
3074 | case TPACPI_FAN_RD_TPEC: | 3353 | case TPACPI_FAN_RD_TPEC: |
3075 | /* all except 570, 600e/x, 770e, 770x */ | 3354 | /* all except 570, 600e/x, 770e, 770x */ |
3076 | if ((rc = fan_get_status(&status)) < 0) | 3355 | if ((rc = fan_get_status_safe(&status)) < 0) |
3077 | return rc; | 3356 | return rc; |
3078 | 3357 | ||
3079 | if (unlikely(tp_features.fan_ctrl_status_undef)) { | 3358 | if (unlikely(tp_features.fan_ctrl_status_undef)) { |
@@ -3117,7 +3396,7 @@ static int fan_read(char *p) | |||
3117 | 3396 | ||
3118 | default: | 3397 | default: |
3119 | len += sprintf(p + len, " (<level> is 0-7, " | 3398 | len += sprintf(p + len, " (<level> is 0-7, " |
3120 | "auto, disengaged)\n"); | 3399 | "auto, disengaged, full-speed)\n"); |
3121 | break; | 3400 | break; |
3122 | } | 3401 | } |
3123 | } | 3402 | } |
@@ -3140,12 +3419,13 @@ static int fan_write_cmd_level(const char *cmd, int *rc) | |||
3140 | 3419 | ||
3141 | if (strlencmp(cmd, "level auto") == 0) | 3420 | if (strlencmp(cmd, "level auto") == 0) |
3142 | level = TP_EC_FAN_AUTO; | 3421 | level = TP_EC_FAN_AUTO; |
3143 | else if (strlencmp(cmd, "level disengaged") == 0) | 3422 | else if ((strlencmp(cmd, "level disengaged") == 0) | |
3423 | (strlencmp(cmd, "level full-speed") == 0)) | ||
3144 | level = TP_EC_FAN_FULLSPEED; | 3424 | level = TP_EC_FAN_FULLSPEED; |
3145 | else if (sscanf(cmd, "level %d", &level) != 1) | 3425 | else if (sscanf(cmd, "level %d", &level) != 1) |
3146 | return 0; | 3426 | return 0; |
3147 | 3427 | ||
3148 | if ((*rc = fan_set_level(level)) == -ENXIO) | 3428 | if ((*rc = fan_set_level_safe(level)) == -ENXIO) |
3149 | printk(IBM_ERR "level command accepted for unsupported " | 3429 | printk(IBM_ERR "level command accepted for unsupported " |
3150 | "access mode %d", fan_control_access_mode); | 3430 | "access mode %d", fan_control_access_mode); |
3151 | 3431 | ||
diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h index e833ff3caf39..2fe4d61cc27f 100644 --- a/drivers/misc/thinkpad_acpi.h +++ b/drivers/misc/thinkpad_acpi.h | |||
@@ -349,6 +349,8 @@ enum { /* Fan control constants */ | |||
349 | 349 | ||
350 | TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ | 350 | TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ |
351 | TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ | 351 | TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ |
352 | |||
353 | TPACPI_FAN_LAST_LEVEL = 0x100, /* Use cached last-seen fan level */ | ||
352 | }; | 354 | }; |
353 | 355 | ||
354 | enum fan_status_access_mode { | 356 | enum fan_status_access_mode { |
@@ -375,6 +377,7 @@ static enum fan_status_access_mode fan_status_access_mode; | |||
375 | static enum fan_control_access_mode fan_control_access_mode; | 377 | static enum fan_control_access_mode fan_control_access_mode; |
376 | static enum fan_control_commands fan_control_commands; | 378 | static enum fan_control_commands fan_control_commands; |
377 | static u8 fan_control_initial_status; | 379 | static u8 fan_control_initial_status; |
380 | static u8 fan_control_desired_level; | ||
378 | static int fan_watchdog_maxinterval; | 381 | static int fan_watchdog_maxinterval; |
379 | 382 | ||
380 | struct mutex fan_mutex; | 383 | struct mutex fan_mutex; |
@@ -384,10 +387,13 @@ static acpi_handle fans_handle, gfan_handle, sfan_handle; | |||
384 | static int fan_init(struct ibm_init_struct *iibm); | 387 | static int fan_init(struct ibm_init_struct *iibm); |
385 | static void fan_exit(void); | 388 | static void fan_exit(void); |
386 | static int fan_get_status(u8 *status); | 389 | static int fan_get_status(u8 *status); |
390 | static int fan_get_status_safe(u8 *status); | ||
387 | static int fan_get_speed(unsigned int *speed); | 391 | static int fan_get_speed(unsigned int *speed); |
392 | static void fan_update_desired_level(u8 status); | ||
388 | static void fan_watchdog_fire(struct work_struct *ignored); | 393 | static void fan_watchdog_fire(struct work_struct *ignored); |
389 | static void fan_watchdog_reset(void); | 394 | static void fan_watchdog_reset(void); |
390 | static int fan_set_level(int level); | 395 | static int fan_set_level(int level); |
396 | static int fan_set_level_safe(int level); | ||
391 | static int fan_set_enable(void); | 397 | static int fan_set_enable(void); |
392 | static int fan_set_disable(void); | 398 | static int fan_set_disable(void); |
393 | static int fan_set_speed(int speed); | 399 | static int fan_set_speed(int speed); |