diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/i8k.c | 60 |
1 files changed, 48 insertions, 12 deletions
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index d6e8a267d3a6..6ad087240cef 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Hwmon integration: | 6 | * Hwmon integration: |
7 | * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de> | 7 | * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de> |
8 | * Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net> | 8 | * Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net> |
9 | * Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com> | ||
9 | * | 10 | * |
10 | * This program is free software; you can redistribute it and/or modify it | 11 | * This program is free software; you can redistribute it and/or modify it |
11 | * under the terms of the GNU General Public License as published by the | 12 | * under the terms of the GNU General Public License as published by the |
@@ -42,12 +43,14 @@ | |||
42 | #define I8K_SMM_SET_FAN 0x01a3 | 43 | #define I8K_SMM_SET_FAN 0x01a3 |
43 | #define I8K_SMM_GET_FAN 0x00a3 | 44 | #define I8K_SMM_GET_FAN 0x00a3 |
44 | #define I8K_SMM_GET_SPEED 0x02a3 | 45 | #define I8K_SMM_GET_SPEED 0x02a3 |
46 | #define I8K_SMM_GET_NOM_SPEED 0x04a3 | ||
45 | #define I8K_SMM_GET_TEMP 0x10a3 | 47 | #define I8K_SMM_GET_TEMP 0x10a3 |
46 | #define I8K_SMM_GET_TEMP_TYPE 0x11a3 | 48 | #define I8K_SMM_GET_TEMP_TYPE 0x11a3 |
47 | #define I8K_SMM_GET_DELL_SIG1 0xfea3 | 49 | #define I8K_SMM_GET_DELL_SIG1 0xfea3 |
48 | #define I8K_SMM_GET_DELL_SIG2 0xffa3 | 50 | #define I8K_SMM_GET_DELL_SIG2 0xffa3 |
49 | 51 | ||
50 | #define I8K_FAN_MULT 30 | 52 | #define I8K_FAN_MULT 30 |
53 | #define I8K_FAN_MAX_RPM 30000 | ||
51 | #define I8K_MAX_TEMP 127 | 54 | #define I8K_MAX_TEMP 127 |
52 | 55 | ||
53 | #define I8K_FN_NONE 0x00 | 56 | #define I8K_FN_NONE 0x00 |
@@ -64,7 +67,7 @@ static DEFINE_MUTEX(i8k_mutex); | |||
64 | static char bios_version[4]; | 67 | static char bios_version[4]; |
65 | static struct device *i8k_hwmon_dev; | 68 | static struct device *i8k_hwmon_dev; |
66 | static u32 i8k_hwmon_flags; | 69 | static u32 i8k_hwmon_flags; |
67 | static uint i8k_fan_mult; | 70 | static uint i8k_fan_mult = I8K_FAN_MULT; |
68 | static uint i8k_pwm_mult; | 71 | static uint i8k_pwm_mult; |
69 | static uint i8k_fan_max = I8K_FAN_HIGH; | 72 | static uint i8k_fan_max = I8K_FAN_HIGH; |
70 | 73 | ||
@@ -95,13 +98,13 @@ static bool power_status; | |||
95 | module_param(power_status, bool, 0600); | 98 | module_param(power_status, bool, 0600); |
96 | MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k"); | 99 | MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k"); |
97 | 100 | ||
98 | static uint fan_mult = I8K_FAN_MULT; | 101 | static uint fan_mult; |
99 | module_param(fan_mult, uint, 0); | 102 | module_param(fan_mult, uint, 0); |
100 | MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with"); | 103 | MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)"); |
101 | 104 | ||
102 | static uint fan_max = I8K_FAN_HIGH; | 105 | static uint fan_max; |
103 | module_param(fan_max, uint, 0); | 106 | module_param(fan_max, uint, 0); |
104 | MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed"); | 107 | MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)"); |
105 | 108 | ||
106 | static int i8k_open_fs(struct inode *inode, struct file *file); | 109 | static int i8k_open_fs(struct inode *inode, struct file *file); |
107 | static long i8k_ioctl(struct file *, unsigned int, unsigned long); | 110 | static long i8k_ioctl(struct file *, unsigned int, unsigned long); |
@@ -276,6 +279,17 @@ static int i8k_get_fan_speed(int fan) | |||
276 | } | 279 | } |
277 | 280 | ||
278 | /* | 281 | /* |
282 | * Read the fan nominal rpm for specific fan speed. | ||
283 | */ | ||
284 | static int i8k_get_fan_nominal_speed(int fan, int speed) | ||
285 | { | ||
286 | struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, }; | ||
287 | |||
288 | regs.ebx = (fan & 0xff) | (speed << 8); | ||
289 | return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; | ||
290 | } | ||
291 | |||
292 | /* | ||
279 | * Set the fan speed (off, low, high). Returns the new fan status. | 293 | * Set the fan speed (off, low, high). Returns the new fan status. |
280 | */ | 294 | */ |
281 | static int i8k_set_fan(int fan, int speed) | 295 | static int i8k_set_fan(int fan, int speed) |
@@ -863,6 +877,7 @@ MODULE_DEVICE_TABLE(dmi, i8k_dmi_table); | |||
863 | static int __init i8k_probe(void) | 877 | static int __init i8k_probe(void) |
864 | { | 878 | { |
865 | const struct dmi_system_id *id; | 879 | const struct dmi_system_id *id; |
880 | int fan, ret; | ||
866 | 881 | ||
867 | /* | 882 | /* |
868 | * Get DMI information | 883 | * Get DMI information |
@@ -891,19 +906,40 @@ static int __init i8k_probe(void) | |||
891 | return -ENODEV; | 906 | return -ENODEV; |
892 | } | 907 | } |
893 | 908 | ||
894 | i8k_fan_mult = fan_mult; | 909 | /* |
895 | i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ | 910 | * Set fan multiplier and maximal fan speed from dmi config |
911 | * Values specified in module parameters override values from dmi | ||
912 | */ | ||
896 | id = dmi_first_match(i8k_dmi_table); | 913 | id = dmi_first_match(i8k_dmi_table); |
897 | if (id && id->driver_data) { | 914 | if (id && id->driver_data) { |
898 | const struct i8k_config_data *conf = id->driver_data; | 915 | const struct i8k_config_data *conf = id->driver_data; |
899 | 916 | if (!fan_mult && conf->fan_mult) | |
900 | if (fan_mult == I8K_FAN_MULT && conf->fan_mult) | 917 | fan_mult = conf->fan_mult; |
901 | i8k_fan_mult = conf->fan_mult; | 918 | if (!fan_max && conf->fan_max) |
902 | if (fan_max == I8K_FAN_HIGH && conf->fan_max) | 919 | fan_max = conf->fan_max; |
903 | i8k_fan_max = conf->fan_max; | ||
904 | } | 920 | } |
921 | |||
922 | i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ | ||
905 | i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max); | 923 | i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max); |
906 | 924 | ||
925 | if (!fan_mult) { | ||
926 | /* | ||
927 | * Autodetect fan multiplier based on nominal rpm | ||
928 | * If fan reports rpm value too high then set multiplier to 1 | ||
929 | */ | ||
930 | for (fan = 0; fan < 2; ++fan) { | ||
931 | ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max); | ||
932 | if (ret < 0) | ||
933 | continue; | ||
934 | if (ret > I8K_FAN_MAX_RPM) | ||
935 | i8k_fan_mult = 1; | ||
936 | break; | ||
937 | } | ||
938 | } else { | ||
939 | /* Fan multiplier was specified in module param or in dmi */ | ||
940 | i8k_fan_mult = fan_mult; | ||
941 | } | ||
942 | |||
907 | return 0; | 943 | return 0; |
908 | } | 944 | } |
909 | 945 | ||