Re: [PATCH v4] i8k: Autodetect maximal fan speed and fan RPM multiplier

Pali Rohár
2014-12-23T19:11:21Z

[-- Attachment #1: Type: Text/Plain, Size: 7496 bytes --]

On Tuesday 23 December 2014 14:52:10 Guenter Roeck wrote:
> On Mon, Dec 22, 2014 at 04:07:34PM +0100, Pali Rohár wrote:
> > On Sunday 21 December 2014 20:51:14 Guenter Roeck wrote:
> > > On 12/21/2014 09:23 AM, Pali Rohár wrote:
> > > > 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.
> > > >
> > > > First it can be used to validate if certain fan speed
> > > > could be accepted by SMM for setting fan speed and we
> > > > can use this routine to detect maximal fan speed.
> > > >
> > > > Second it returns RPM value, so we can check if value
> > > > looks correct with multiplier 30 or multiplier 1 (until
> > > > now only these two multiplier were used). If RPM value
> > > > with multiplier 30 is too high, then multiplier 1 is
> > > > used.
> > > >
> > > > In case when SMM reports that new function is not
> > > > supported we will fallback to old hardcoded values.
> > > > Maximal fan speed would be 2 and RPM multiplier 30.
> > > >
> > > > Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
> > > > Tested-by: Pali Rohár <pali.rohar@gmail.com>
> > >
> > > Auto-detection of both multiplier and maximum speed tested
> > > working on M140 (after removing its configuration entry).
> > >
> > > On Studio 1555, multiplier auto-detection works, but
> > > fan_max auto-detection fails. A speed value of '3' is
> > > accepted, but it does not set the fan speed to its
> > > maximum. Also, after setting the speed value to '3',
> > > reading it back returns to old value. No idea what it
> > > does or is expected to do. Reading the nominal speed does
> > > return a valid value.
> > >
> > > Given that, I think we should not try to auto-detect
> > > fan_max, but keep the current code (meaning either use 2
> > > or 3 depending on the configuration data, with 2 as
> > > default if nothing else is known).
> > >
> > > Thanks,
> > > Guenter
> >
> > Ok. In this case I will remove max fan speed detection code
> > and we will use config data for those some machines which
> > support TURBO speed. If multiplier detection on Studio is
> > working fine, I can send another patch which remove config
> > data for Studio (because default multiplier is 2).
> >
> > Can you check which nominal value Studio returns for speed
> > 3? Maybe it is some special?
>
> It was a bit higher than the value returned for speed 2.
> Nothing special as far as I could see, only that setting a
> value of 3 did not modify the fan speed.
>

Ok. I removed detection of maximal fan speed. Patch is below.

> Note that I won't be able to test further in the next two
> weeks; I am about 6,000 miles away from my Dell laptops right
> now.
>
> Guenter

Ok. When you will be back you can test new version:

diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c
index d6e8a26..f0e21a0 100644
--- a/drivers/char/i8k.c
+++ b/drivers/char/i8k.c
@@ -6,6 +6,7 @@
* Hwmon integration:
* Copyright (C) 2011 Jean Delvare <jdelvare@suse.de>
* Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net>
+ * Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -42,12 +43,14 @@
#define I8K_SMM_SET_FAN 0x01a3
#define I8K_SMM_GET_FAN 0x00a3
#define I8K_SMM_GET_SPEED 0x02a3
+#define I8K_SMM_GET_NOM_SPEED 0x04a3
#define I8K_SMM_GET_TEMP 0x10a3
#define I8K_SMM_GET_TEMP_TYPE 0x11a3
#define I8K_SMM_GET_DELL_SIG1 0xfea3
#define I8K_SMM_GET_DELL_SIG2 0xffa3

#define I8K_FAN_MULT 30
+#define I8K_FAN_MAX_RPM 30000
#define I8K_MAX_TEMP 127

#define I8K_FN_NONE 0x00
@@ -64,7 +67,7 @@ static DEFINE_MUTEX(i8k_mutex);
static char bios_version[4];
static struct device *i8k_hwmon_dev;
static u32 i8k_hwmon_flags;
-static uint i8k_fan_mult;
+static uint i8k_fan_mult = I8K_FAN_MULT;
static uint i8k_pwm_mult;
static uint i8k_fan_max = I8K_FAN_HIGH;

@@ -95,13 +98,13 @@ static bool power_status;
module_param(power_status, bool, 0600);
MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");

-static uint fan_mult = I8K_FAN_MULT;
+static uint fan_mult;
module_param(fan_mult, uint, 0);
-MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with");
+MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)");

-static uint fan_max = I8K_FAN_HIGH;
+static uint fan_max;
module_param(fan_max, uint, 0);
-MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed");
+MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)");

static int i8k_open_fs(struct inode *inode, struct file *file);
static long i8k_ioctl(struct file *, unsigned int, unsigned long);
@@ -276,6 +279,17 @@ static int i8k_get_fan_speed(int fan)
}

/*
+ * Read the fan nominal rpm for specific fan speed.
+ */
+static int i8k_get_fan_nominal_speed(int fan, int speed)
+{
+ struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, };
+
+ regs.ebx = (fan & 0xff) | (speed << 8);
+ return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult;
+}
+
+/*
* Set the fan speed (off, low, high). Returns the new fan status.
*/
static int i8k_set_fan(int fan, int speed)
@@ -863,6 +877,7 @@ MODULE_DEVICE_TABLE(dmi, i8k_dmi_table);
static int __init i8k_probe(void)
{
const struct dmi_system_id *id;
+ int fan, ret;

/*
* Get DMI information
@@ -891,19 +906,47 @@ static int __init i8k_probe(void)
return -ENODEV;
}

- i8k_fan_mult = fan_mult;
- i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
+ /*
+ * Set fan multiplier and maximal fan speed from dmi config
+ * Values specified in module parameters override values from dmi
+ */
id = dmi_first_match(i8k_dmi_table);
if (id && id->driver_data) {
const struct i8k_config_data *conf = id->driver_data;
-
- if (fan_mult == I8K_FAN_MULT && conf->fan_mult)
- i8k_fan_mult = conf->fan_mult;
- if (fan_max == I8K_FAN_HIGH && conf->fan_max)
- i8k_fan_max = conf->fan_max;
+ if (!fan_mult && conf->fan_mult)
+ fan_mult = conf->fan_mult;
+ if (!fan_max && conf->fan_max)
+ fan_max = conf->fan_max;
}
+
+ i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max);

+ if (!fan_mult) {
+ /*
+ * Autodetect fan multiplier based on nominal rpm
+ * If fan reports rpm value too high then set multiplier to 1
+ *
+ * Try also setting multiplier from current rpm, but this will
+ * work only in case when fan is not turned off. It is better
+ * then nothing for machines which does not support nominal rpm
+ * SMM function.
+ */
+ for (fan = 0; fan < 2; ++fan) {
+ ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max);
+ if (ret < 0)
+ ret = i8k_get_fan_speed(fan);
+ if (ret < 0)
+ continue;
+ if (ret > I8K_FAN_MAX_RPM)
+ i8k_fan_mult = 1;
+ break;
+ }
+ } else {
+ /* Fan multiplier was specified in module param or in dmi */
+ i8k_fan_mult = fan_mult;
+ }
+
return 0;
}

--
Pali Rohár
pali.rohar@gmail.com

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]