aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2007-04-24 10:48:17 -0400
committerLen Brown <len.brown@intel.com>2007-04-25 02:00:27 -0400
commitfe98a52ce7540fb3a19d57488a08864110cf4d5c (patch)
tree8a9ba42e5a4d4ff208297cd18782aa85fef27164 /drivers/misc
parent2c37aa4e22dd55070c608290c5031f2ee93e69ce (diff)
ACPI: thinkpad-acpi: add sysfs support to fan subdriver
Export sysfs attributes to monitor and control the internal thinkpad fan (some thinkpads have more than one fan, but thinkpad-acpi doesn't support the second fan yet). The sysfs interface follows the hwmon design guide for fan devices. Also, fix some stray "thermal" files in the fan procfs description that have been there forever, and officially support "full-speed" as the name for the PWM-disabled state of the fan controller to keep it in line with the hwmon interface. It is much better a name for that mode than the unobvious "disengaged" anyway. Change the procfs interface to also accept full-speed as a fan level, but still report it as disengaged for backwards compatibility. Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/thinkpad_acpi.c326
-rw-r--r--drivers/misc/thinkpad_acpi.h6
2 files changed, 309 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;
2695static enum fan_control_commands fan_control_commands; 2695static enum fan_control_commands fan_control_commands;
2696 2696
2697static u8 fan_control_initial_status; 2697static u8 fan_control_initial_status;
2698static u8 fan_control_desired_level;
2698 2699
2699static void fan_watchdog_fire(struct work_struct *ignored); 2700static void fan_watchdog_fire(struct work_struct *ignored);
2700static int fan_watchdog_maxinterval; 2701static 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 ----------------------------------------------- */
2734static 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
2765static 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
2801static 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 ------------------------------------------------------ */
2806static 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
2835static 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
2866static 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 ------------------------------------------------ */
2871static 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
2885static 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) ------------------------------------- */
2890static 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
2896static 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
2910static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
2911 fan_fan_watchdog_show, fan_fan_watchdog_store);
2912
2913/* --------------------------------------------------------------------- */
2914static 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
2920static const struct attribute_group fan_attr_group = {
2921 .attrs = fan_attributes,
2922};
2923
2711static int __init fan_init(struct ibm_init_struct *iibm) 2924static 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 */
3036static 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
2804static int fan_get_status(u8 *status) 3047static 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
3083static 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
2840static void fan_exit(void) 3102static 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
2903static int fan_set_level(int level) 3170static 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
3200static 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
2945static int fan_set_enable(void) 3219static 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
354enum fan_status_access_mode { 356enum fan_status_access_mode {
@@ -375,6 +377,7 @@ static enum fan_status_access_mode fan_status_access_mode;
375static enum fan_control_access_mode fan_control_access_mode; 377static enum fan_control_access_mode fan_control_access_mode;
376static enum fan_control_commands fan_control_commands; 378static enum fan_control_commands fan_control_commands;
377static u8 fan_control_initial_status; 379static u8 fan_control_initial_status;
380static u8 fan_control_desired_level;
378static int fan_watchdog_maxinterval; 381static int fan_watchdog_maxinterval;
379 382
380struct mutex fan_mutex; 383struct mutex fan_mutex;
@@ -384,10 +387,13 @@ static acpi_handle fans_handle, gfan_handle, sfan_handle;
384static int fan_init(struct ibm_init_struct *iibm); 387static int fan_init(struct ibm_init_struct *iibm);
385static void fan_exit(void); 388static void fan_exit(void);
386static int fan_get_status(u8 *status); 389static int fan_get_status(u8 *status);
390static int fan_get_status_safe(u8 *status);
387static int fan_get_speed(unsigned int *speed); 391static int fan_get_speed(unsigned int *speed);
392static void fan_update_desired_level(u8 status);
388static void fan_watchdog_fire(struct work_struct *ignored); 393static void fan_watchdog_fire(struct work_struct *ignored);
389static void fan_watchdog_reset(void); 394static void fan_watchdog_reset(void);
390static int fan_set_level(int level); 395static int fan_set_level(int level);
396static int fan_set_level_safe(int level);
391static int fan_set_enable(void); 397static int fan_set_enable(void);
392static int fan_set_disable(void); 398static int fan_set_disable(void);
393static int fan_set_speed(int speed); 399static int fan_set_speed(int speed);