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); |
