aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorPali Rohár <pali.rohar@gmail.com>2015-01-12 08:32:03 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-01-25 12:17:59 -0500
commit8f21d8e939b8be096d6f8b6f4386a1f616e307a9 (patch)
tree90a4558a4f8715303482ee1037d9f700f6388dfe /drivers/char
parent7f69fb033b38022554fc43f991aef030021b2d91 (diff)
i8k: Autodetect fan RPM multiplier
This patch adds new function i8k_get_fan_nominal_speed() for doing SMM call which will return nominal fan RPM for specified fan speed. It returns nominal RPM value at which fan operate when speed (0, 1, 2, 3) is set. It looks like RPM value is not accurate, but still provides very useful information. New function i8k_get_fan_nominal_speed() is used for determinate if fan multiplier is 1 or 30. If function for maximal fan value success and returned RPM value too high (above 30000) then fan multiplier is set to 1. Otherwise multiplier is not changed and default value 30 is used. Signed-off-by: Pali Rohár <pali.rohar@gmail.com> Tested-by: Pali Rohár <pali.rohar@gmail.com> Tested-by: Gabriele Mazzotta <gabriele.mzt@gmail.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/i8k.c60
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);
64static char bios_version[4]; 67static char bios_version[4];
65static struct device *i8k_hwmon_dev; 68static struct device *i8k_hwmon_dev;
66static u32 i8k_hwmon_flags; 69static u32 i8k_hwmon_flags;
67static uint i8k_fan_mult; 70static uint i8k_fan_mult = I8K_FAN_MULT;
68static uint i8k_pwm_mult; 71static uint i8k_pwm_mult;
69static uint i8k_fan_max = I8K_FAN_HIGH; 72static uint i8k_fan_max = I8K_FAN_HIGH;
70 73
@@ -95,13 +98,13 @@ static bool power_status;
95module_param(power_status, bool, 0600); 98module_param(power_status, bool, 0600);
96MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k"); 99MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");
97 100
98static uint fan_mult = I8K_FAN_MULT; 101static uint fan_mult;
99module_param(fan_mult, uint, 0); 102module_param(fan_mult, uint, 0);
100MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with"); 103MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)");
101 104
102static uint fan_max = I8K_FAN_HIGH; 105static uint fan_max;
103module_param(fan_max, uint, 0); 106module_param(fan_max, uint, 0);
104MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed"); 107MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)");
105 108
106static int i8k_open_fs(struct inode *inode, struct file *file); 109static int i8k_open_fs(struct inode *inode, struct file *file);
107static long i8k_ioctl(struct file *, unsigned int, unsigned long); 110static 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 */
284static 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(&regs) ? : (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 */
281static int i8k_set_fan(int fan, int speed) 295static int i8k_set_fan(int fan, int speed)
@@ -863,6 +877,7 @@ MODULE_DEVICE_TABLE(dmi, i8k_dmi_table);
863static int __init i8k_probe(void) 877static 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