diff options
Diffstat (limited to 'drivers/misc/thinkpad_acpi.c')
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 326 |
1 files changed, 303 insertions, 23 deletions
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 | ||