From 73f0718e74e25ac7381450a7a21257b8f26f43f0 Mon Sep 17 00:00:00 2001 From: Rob Ward Date: Sun, 7 Dec 2014 15:40:33 +0000 Subject: drivers: char: mem: Make /dev/mem an optional device Adds Kconfig option CONFIG_DEVMEM that allows the /dev/mem device to be disabled. Option defaults to /dev/mem enabled. Signed-off-by: Rob Ward Acked-by: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/Kconfig | 9 +++++++++ drivers/char/mem.c | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index efefd12a0f7b..a4af8221751e 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -6,6 +6,15 @@ menu "Character devices" source "drivers/tty/Kconfig" +config DEVMEM + bool "/dev/mem virtual device support" + default y + help + Say Y here if you want to support the /dev/mem device. + The /dev/mem device is used to access areas of physical + memory. + When in doubt, say "Y". + config DEVKMEM bool "/dev/kmem virtual device support" default y diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 4c58333b4257..7d6778437be0 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -715,7 +715,7 @@ static int open_port(struct inode *inode, struct file *filp) #define open_mem open_port #define open_kmem open_mem -static const struct file_operations mem_fops = { +static const struct file_operations __maybe_unused mem_fops = { .llseek = memory_lseek, .read = read_mem, .write = write_mem, @@ -785,7 +785,9 @@ static const struct memdev { const struct file_operations *fops; struct backing_dev_info *dev_info; } devlist[] = { +#ifdef CONFIG_DEVMEM [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi }, +#endif #ifdef CONFIG_DEVKMEM [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi }, #endif -- cgit v1.2.3 From a8c912522b547b3512cd25ccb8d507cd75264c84 Mon Sep 17 00:00:00 2001 From: Rob Ward Date: Sun, 7 Dec 2014 15:40:34 +0000 Subject: drivers: char: mem: Simplify DEVKMEM configuration Simplify the use of CONFIG_DEVKMEM by making the kmem_fops so that it is __maybe_unused. This enabled the multiple #ifdef's used for this structure to be removed and brings it in line with the use of CONFIG_DEVMEM This change should introduce no functional changes. Signed-off-by: Rob Ward Acked-by: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 7d6778437be0..17b21396e7f9 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -341,7 +341,6 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) return 0; } -#ifdef CONFIG_DEVKMEM static int mmap_kmem(struct file *file, struct vm_area_struct *vma) { unsigned long pfn; @@ -362,9 +361,7 @@ static int mmap_kmem(struct file *file, struct vm_area_struct *vma) vma->vm_pgoff = pfn; return mmap_mem(file, vma); } -#endif -#ifdef CONFIG_DEVKMEM /* * This function reads the *virtual* memory as seen by the kernel. */ @@ -544,7 +541,6 @@ static ssize_t write_kmem(struct file *file, const char __user *buf, *ppos = p; return virtr + wrote ? : err; } -#endif #ifdef CONFIG_DEVPORT static ssize_t read_port(struct file *file, char __user *buf, @@ -724,8 +720,7 @@ static const struct file_operations __maybe_unused mem_fops = { .get_unmapped_area = get_unmapped_area_mem, }; -#ifdef CONFIG_DEVKMEM -static const struct file_operations kmem_fops = { +static const struct file_operations __maybe_unused kmem_fops = { .llseek = memory_lseek, .read = read_kmem, .write = write_kmem, @@ -733,7 +728,6 @@ static const struct file_operations kmem_fops = { .open = open_kmem, .get_unmapped_area = get_unmapped_area_mem, }; -#endif static const struct file_operations null_fops = { .llseek = null_lseek, -- cgit v1.2.3 From 3a4bc2fb80fb1fc943b640aae87dc078ba2aad78 Mon Sep 17 00:00:00 2001 From: Rob Ward Date: Sun, 7 Dec 2014 15:40:35 +0000 Subject: drivers: char: mem: Simplify DEVPORT configuration Simplify the use of CONFIG_DEVPORT by making the port_fops so that it includes __maybe_unused. This enabled the multiple #ifdef's used for this structure to be removed and brings it in line with the use of CONFIG_DEVMEM This change should introduce no functional changes. Signed-off-by: Rob Ward Acked-by: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 17b21396e7f9..40c1580ecd47 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -542,7 +542,6 @@ static ssize_t write_kmem(struct file *file, const char __user *buf, return virtr + wrote ? : err; } -#ifdef CONFIG_DEVPORT static ssize_t read_port(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -583,7 +582,6 @@ static ssize_t write_port(struct file *file, const char __user *buf, *ppos = i; return tmp-buf; } -#endif static ssize_t read_null(struct file *file, char __user *buf, size_t count, loff_t *ppos) @@ -738,14 +736,12 @@ static const struct file_operations null_fops = { .splice_write = splice_write_null, }; -#ifdef CONFIG_DEVPORT -static const struct file_operations port_fops = { +static const struct file_operations __maybe_unused port_fops = { .llseek = memory_lseek, .read = read_port, .write = write_port, .open = open_port, }; -#endif static const struct file_operations zero_fops = { .llseek = zero_lseek, -- cgit v1.2.3 From 35b6c7e4a536f9a6110023431533b738650d6457 Mon Sep 17 00:00:00 2001 From: Rob Ward Date: Sat, 20 Dec 2014 18:28:35 +0000 Subject: drivers: char: mem: Replace usage of asm include Replaces the use of asm/uaccess.h with linux/uaccess.h. Signed-off-by: Rob Ward Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 40c1580ecd47..da73aeaa7008 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -28,7 +28,7 @@ #include #include -#include +#include #ifdef CONFIG_IA64 # include -- cgit v1.2.3 From 6a0061badd2e894edb5a3a5d7704095766a343fd Mon Sep 17 00:00:00 2001 From: Rob Ward Date: Sat, 20 Dec 2014 18:28:36 +0000 Subject: drivers: char: mem: Fix Missing blank line issues Fixes "Missing a blank line after declarations" reported by checkpatch. This patch introduces no functional changes. Signed-off-by: Rob Ward Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index da73aeaa7008..080273287c48 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -570,6 +570,7 @@ static ssize_t write_port(struct file *file, const char __user *buf, return -EFAULT; while (count-- > 0 && i < 65536) { char c; + if (__get_user(c, tmp)) { if (tmp > buf) break; @@ -625,6 +626,7 @@ static ssize_t read_iter_zero(struct kiocb *iocb, struct iov_iter *iter) while (iov_iter_count(iter)) { size_t chunk = iov_iter_count(iter), n; + if (chunk > PAGE_SIZE) chunk = PAGE_SIZE; /* Just for latency reasons */ n = iov_iter_zero(chunk, iter); -- cgit v1.2.3 From 5114b474cbb464e7a5da4ba5cdc551e798395151 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 12 Jan 2015 14:31:57 +0100 Subject: i8k: Add support for temperature sensor labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds labels for temperature sensors if SMM function with EAX register 0x11a3 reports it. This information was taken from DOS binary NBSVC.MDM. Signed-off-by: Pali Rohár Reviewed-by: Guenter Roeck Tested-by: Guenter Roeck Tested-by: Pali Rohár Tested-by: Steven Honeyman Tested-by: Gabriele Mazzotta Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 74 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 13 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index e34a019eb930..663868b2adc4 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -42,6 +42,7 @@ #define I8K_SMM_GET_FAN 0x00a3 #define I8K_SMM_GET_SPEED 0x02a3 #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 @@ -288,6 +289,14 @@ static int i8k_set_fan(int fan, int speed) return i8k_smm(®s) ? : i8k_get_fan_status(fan); } +static int i8k_get_temp_type(int sensor) +{ + struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, }; + + regs.ebx = sensor & 0xff; + return i8k_smm(®s) ? : regs.eax & 0xff; +} + /* * Read the cpu temperature. */ @@ -493,6 +502,29 @@ static int i8k_open_fs(struct inode *inode, struct file *file) * Hwmon interface */ +static ssize_t i8k_hwmon_show_temp_label(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + static const char * const labels[] = { + "CPU", + "GPU", + "SODIMM", + "Other", + "Ambient", + "Other", + }; + int index = to_sensor_dev_attr(devattr)->index; + int type; + + type = i8k_get_temp_type(index); + if (type < 0) + return type; + if (type >= ARRAY_SIZE(labels)) + type = ARRAY_SIZE(labels) - 1; + return sprintf(buf, "%s\n", labels[type]); +} + static ssize_t i8k_hwmon_show_temp(struct device *dev, struct device_attribute *devattr, char *buf) @@ -555,9 +587,17 @@ static ssize_t i8k_hwmon_set_pwm(struct device *dev, } static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, + 0); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, + 1); static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, + 2); static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, + 3); static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, I8K_FAN_LEFT); static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, @@ -569,31 +609,39 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, static struct attribute *i8k_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */ - &sensor_dev_attr_temp2_input.dev_attr.attr, /* 1 */ - &sensor_dev_attr_temp3_input.dev_attr.attr, /* 2 */ - &sensor_dev_attr_temp4_input.dev_attr.attr, /* 3 */ - &sensor_dev_attr_fan1_input.dev_attr.attr, /* 4 */ - &sensor_dev_attr_pwm1.dev_attr.attr, /* 5 */ - &sensor_dev_attr_fan2_input.dev_attr.attr, /* 6 */ - &sensor_dev_attr_pwm2.dev_attr.attr, /* 7 */ + &sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */ + &sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */ + &sensor_dev_attr_temp2_label.dev_attr.attr, /* 3 */ + &sensor_dev_attr_temp3_input.dev_attr.attr, /* 4 */ + &sensor_dev_attr_temp3_label.dev_attr.attr, /* 5 */ + &sensor_dev_attr_temp4_input.dev_attr.attr, /* 6 */ + &sensor_dev_attr_temp4_label.dev_attr.attr, /* 7 */ + &sensor_dev_attr_fan1_input.dev_attr.attr, /* 8 */ + &sensor_dev_attr_pwm1.dev_attr.attr, /* 9 */ + &sensor_dev_attr_fan2_input.dev_attr.attr, /* 10 */ + &sensor_dev_attr_pwm2.dev_attr.attr, /* 11 */ NULL }; static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - if (index == 0 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) + if (index >= 0 && index <= 1 && + !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) return 0; - if (index == 1 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2)) + if (index >= 2 && index <= 3 && + !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2)) return 0; - if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3)) + if (index >= 4 && index <= 5 && + !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3)) return 0; - if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) + if (index >= 6 && index <= 7 && + !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) return 0; - if (index >= 4 && index <= 5 && + if (index >= 8 && index <= 9 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) return 0; - if (index >= 6 && index <= 7 && + if (index >= 10 && index <= 11 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) return 0; -- cgit v1.2.3 From 672af223305791c3a814ec8c4fb841dc75277f48 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 12 Jan 2015 14:31:58 +0100 Subject: i8k: Register only temperature sensors which have labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detect presense of sensor by calling type function instead trying to read temperature value. Type function is working also for sensors which are temporary turned off (e.g on GPU which is turned off). Dell DOS binary NBSVC.MDM is doing similar checks, so we should do that too. Signed-off-by: Pali Rohár Reviewed-by: Guenter Roeck Tested-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 663868b2adc4..afcc9fe084dc 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -660,19 +660,19 @@ static int __init i8k_init_hwmon(void) i8k_hwmon_flags = 0; - /* CPU temperature attributes, if temperature reading is OK */ - err = i8k_get_temp(0); - if (err >= 0 || err == -ERANGE) + /* CPU temperature attributes, if temperature type is OK */ + err = i8k_get_temp_type(0); + if (err >= 0) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1; /* check for additional temperature sensors */ - err = i8k_get_temp(1); - if (err >= 0 || err == -ERANGE) + err = i8k_get_temp_type(1); + if (err >= 0) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2; - err = i8k_get_temp(2); - if (err >= 0 || err == -ERANGE) + err = i8k_get_temp_type(2); + if (err >= 0) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3; - err = i8k_get_temp(3); - if (err >= 0 || err == -ERANGE) + err = i8k_get_temp_type(3); + if (err >= 0) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4; /* Left fan attributes, if left fan is present */ -- cgit v1.2.3 From 83d514d76278e85cc74c472dd51687c8eb4faff0 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 12 Jan 2015 14:31:59 +0100 Subject: i8k: Return -ENODATA for invalid temperature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guenter Roeck suggested to return -ENODATA instead -ERANGE or -EINVAL when BIOS reports invalid temperature value. Signed-off-by: Pali Rohár Reviewed-by: Guenter Roeck Tested-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index afcc9fe084dc..1854faba8ae6 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -331,7 +331,7 @@ static int i8k_get_temp(int sensor) prev[sensor] = temp; } if (temp > I8K_MAX_TEMP) - return -ERANGE; + return -ENODATA; #endif return temp; @@ -533,8 +533,6 @@ static ssize_t i8k_hwmon_show_temp(struct device *dev, int temp; temp = i8k_get_temp(index); - if (temp == -ERANGE) - return -EINVAL; if (temp < 0) return temp; return sprintf(buf, "%d\n", temp * 1000); -- cgit v1.2.3 From 564132d9d62b3151dc6573da794378b5bf0cea17 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 12 Jan 2015 14:32:00 +0100 Subject: i8k: Rework error retries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of returning a previous value if the SMM code returns an error when trying to read a temperature, retry once. If that fails again, return -ENODATA. Also return -ENODATA if an attempt is made to read the GPU temperature but the GPU is currently turned off. Drop the I8K_TEMPERATURE_BUG definition and handle the related bug unconditionally. Signed-off-by: Guenter Roeck Tested-by: Pali Rohár Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 1854faba8ae6..0e332fcd8fc3 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -5,7 +5,7 @@ * * Hwmon integration: * Copyright (C) 2011 Jean Delvare - * Copyright (C) 2013 Guenter Roeck + * Copyright (C) 2013, 2014 Guenter Roeck * * 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 @@ -20,6 +20,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -59,8 +60,6 @@ #define I8K_POWER_AC 0x05 #define I8K_POWER_BATTERY 0x01 -#define I8K_TEMPERATURE_BUG 1 - static DEFINE_MUTEX(i8k_mutex); static char bios_version[4]; static struct device *i8k_hwmon_dev; @@ -300,39 +299,41 @@ static int i8k_get_temp_type(int sensor) /* * Read the cpu temperature. */ -static int i8k_get_temp(int sensor) +static int _i8k_get_temp(int sensor) { - struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, }; - int rc; - int temp; + struct smm_regs regs = { + .eax = I8K_SMM_GET_TEMP, + .ebx = sensor & 0xff, + }; -#ifdef I8K_TEMPERATURE_BUG - static int prev[4] = { I8K_MAX_TEMP+1, I8K_MAX_TEMP+1, I8K_MAX_TEMP+1, I8K_MAX_TEMP+1 }; -#endif - regs.ebx = sensor & 0xff; - rc = i8k_smm(®s); - if (rc < 0) - return rc; + return i8k_smm(®s) ? : regs.eax & 0xff; +} - temp = regs.eax & 0xff; +static int i8k_get_temp(int sensor) +{ + int temp = _i8k_get_temp(sensor); -#ifdef I8K_TEMPERATURE_BUG /* * Sometimes the temperature sensor returns 0x99, which is out of range. - * In this case we return (once) the previous cached value. For example: + * In this case we retry (once) before returning an error. # 1003655137 00000058 00005a4b # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees # 1003655139 00000054 00005c52 */ - if (temp > I8K_MAX_TEMP) { - temp = prev[sensor]; - prev[sensor] = I8K_MAX_TEMP+1; - } else { - prev[sensor] = temp; + if (temp == 0x99) { + msleep(100); + temp = _i8k_get_temp(sensor); } + /* + * Return -ENODATA for all invalid temperatures. + * + * Known instances are the 0x99 value as seen above as well as + * 0xc1 (193), which may be returned when trying to read the GPU + * temperature if the system supports a GPU and it is currently + * turned off. + */ if (temp > I8K_MAX_TEMP) return -ENODATA; -#endif return temp; } -- cgit v1.2.3 From 34ae40f6c91c2267af1dd3c0495082e8b9f9969e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 12 Jan 2015 14:32:01 +0100 Subject: i8k: Add support for Dell XPS 13 XPS 13 does not support turbo speed, so its initialization data matches that of XPS M140. Make XPS initialization data generic, and add support for XPS 13. Signed-off-by: Guenter Roeck Reviewed-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 0e332fcd8fc3..8ec4c371dc50 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -705,7 +705,7 @@ enum i8k_configs { DELL_LATITUDE_E6540, DELL_PRECISION_490, DELL_STUDIO, - DELL_XPS_M140, + DELL_XPS, }; static const struct i8k_config_data i8k_config_data[] = { @@ -725,7 +725,7 @@ static const struct i8k_config_data i8k_config_data[] = { .fan_mult = 1, .fan_max = I8K_FAN_HIGH, }, - [DELL_XPS_M140] = { + [DELL_XPS] = { .fan_mult = 1, .fan_max = I8K_FAN_HIGH, }, @@ -836,13 +836,21 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { }, .driver_data = (void *)&i8k_config_data[DELL_STUDIO], }, + { + .ident = "Dell XPS 13", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS13"), + }, + .driver_data = (void *)&i8k_config_data[DELL_XPS], + }, { .ident = "Dell XPS M140", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"), }, - .driver_data = (void *)&i8k_config_data[DELL_XPS_M140], + .driver_data = (void *)&i8k_config_data[DELL_XPS], }, { } }; -- cgit v1.2.3 From 7f69fb033b38022554fc43f991aef030021b2d91 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 12 Jan 2015 14:32:02 +0100 Subject: i8k: Make fan module parameters an unsigned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting negative fan multiplier or maximal fan speed does make any sense and can cause problems. So ensure that negative values will not be accepted. Signed-off-by: Pali Rohár Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 8ec4c371dc50..d6e8a267d3a6 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -64,9 +64,9 @@ static DEFINE_MUTEX(i8k_mutex); static char bios_version[4]; static struct device *i8k_hwmon_dev; static u32 i8k_hwmon_flags; -static int i8k_fan_mult; -static int i8k_pwm_mult; -static int i8k_fan_max = I8K_FAN_HIGH; +static uint i8k_fan_mult; +static uint i8k_pwm_mult; +static uint i8k_fan_max = I8K_FAN_HIGH; #define I8K_HWMON_HAVE_TEMP1 (1 << 0) #define I8K_HWMON_HAVE_TEMP2 (1 << 1) @@ -95,12 +95,12 @@ static bool power_status; module_param(power_status, bool, 0600); MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k"); -static int fan_mult = I8K_FAN_MULT; -module_param(fan_mult, int, 0); +static uint fan_mult = I8K_FAN_MULT; +module_param(fan_mult, uint, 0); MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with"); -static int fan_max = I8K_FAN_HIGH; -module_param(fan_max, int, 0); +static uint fan_max = I8K_FAN_HIGH; +module_param(fan_max, uint, 0); MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed"); static int i8k_open_fs(struct inode *inode, struct file *file); @@ -696,8 +696,8 @@ static int __init i8k_init_hwmon(void) } struct i8k_config_data { - int fan_mult; - int fan_max; + uint fan_mult; + uint fan_max; }; enum i8k_configs { -- cgit v1.2.3 From 8f21d8e939b8be096d6f8b6f4386a1f616e307a9 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 12 Jan 2015 14:32:03 +0100 Subject: i8k: Autodetect fan RPM multiplier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Tested-by: Pali Rohár Tested-by: Gabriele Mazzotta Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 60 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 12 deletions(-) (limited to 'drivers/char') 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 @@ * Hwmon integration: * Copyright (C) 2011 Jean Delvare * Copyright (C) 2013, 2014 Guenter Roeck + * Copyright (C) 2014 Pali Rohár * * 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); @@ -275,6 +278,17 @@ static int i8k_get_fan_speed(int fan) return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; } +/* + * 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(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; +} + /* * Set the fan speed (off, low, high). Returns the new fan status. */ @@ -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,40 @@ 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 + */ + for (fan = 0; fan < 2; ++fan) { + ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max); + 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; } -- cgit v1.2.3 From 1a131ca1de7a84cf3827c418ee5971b493c6f23f Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 12 Jan 2015 14:32:04 +0100 Subject: i8k: Remove DMI config data for Latitude E6440 and E6540 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both Dell Latitude machines were tested with new fan autodetection code and they are working fine. We already have DMI_MATCH data for generic Latitude machines which match also E6440 and E6540 models. So we do not need to maintain DMI data for those specific machines anymore. Signed-off-by: Pali Rohár Tested-by: Pali Rohár Tested-by: Steven Honeyman Acked-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 6ad087240cef..09ebe06764e3 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -716,7 +716,6 @@ struct i8k_config_data { enum i8k_configs { DELL_LATITUDE_D520, - DELL_LATITUDE_E6540, DELL_PRECISION_490, DELL_STUDIO, DELL_XPS, @@ -727,10 +726,6 @@ static const struct i8k_config_data i8k_config_data[] = { .fan_mult = 1, .fan_max = I8K_FAN_TURBO, }, - [DELL_LATITUDE_E6540] = { - .fan_mult = 1, - .fan_max = I8K_FAN_HIGH, - }, [DELL_PRECISION_490] = { .fan_mult = 1, .fan_max = I8K_FAN_TURBO, @@ -775,22 +770,6 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { }, .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520], }, - { - .ident = "Dell Latitude E6440", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6440"), - }, - .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_E6540], - }, - { - .ident = "Dell Latitude E6540", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6540"), - }, - .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_E6540], - }, { .ident = "Dell Latitude 2", .matches = { -- cgit v1.2.3 From f989e55452c74b4f7b22c889b8ec9f1192aaeec4 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Mon, 12 Jan 2015 14:32:05 +0100 Subject: i8k: Add support for fan labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds labels support for fans if SMM function with EAX register 0x03a3 reports it. This information was taken from DOS binary NBSVC.MDM. Additionally this patch change detection of fan presece. Instead reading fan status now detection is based on new label SMM function. Dell DOS binary NBSVC.MDM is doing similar checks, so we should do that too. This patch also remove I8K_FAN_LEFT and I8K_FAN_RIGHT usage from hwmon driver part because that names does not make sense anymore. So numeric constants are used instead. Original /proc/i8k ioctl part was not changed for compatibility reasons. Signed-off-by: Pali Rohár Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/char/i8k.c | 76 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 14 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 09ebe06764e3..24cc4ed9a780 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -43,6 +43,7 @@ #define I8K_SMM_SET_FAN 0x01a3 #define I8K_SMM_GET_FAN 0x00a3 #define I8K_SMM_GET_SPEED 0x02a3 +#define I8K_SMM_GET_FAN_TYPE 0x03a3 #define I8K_SMM_GET_NOM_SPEED 0x04a3 #define I8K_SMM_GET_TEMP 0x10a3 #define I8K_SMM_GET_TEMP_TYPE 0x11a3 @@ -278,6 +279,17 @@ static int i8k_get_fan_speed(int fan) return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; } +/* + * Read the fan type. + */ +static int i8k_get_fan_type(int fan) +{ + struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; + + regs.ebx = fan & 0xff; + return i8k_smm(®s) ? : regs.eax & 0xff; +} + /* * Read the fan nominal rpm for specific fan speed. */ @@ -553,6 +565,37 @@ static ssize_t i8k_hwmon_show_temp(struct device *dev, return sprintf(buf, "%d\n", temp * 1000); } +static ssize_t i8k_hwmon_show_fan_label(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + static const char * const labels[] = { + "Processor Fan", + "Motherboard Fan", + "Video Fan", + "Power Supply Fan", + "Chipset Fan", + "Other Fan", + }; + int index = to_sensor_dev_attr(devattr)->index; + bool dock = false; + int type; + + type = i8k_get_fan_type(index); + if (type < 0) + return type; + + if (type & 0x10) { + dock = true; + type &= 0x0F; + } + + if (type >= ARRAY_SIZE(labels)) + type = (ARRAY_SIZE(labels) - 1); + + return sprintf(buf, "%s%s\n", (dock ? "Docking " : ""), labels[type]); +} + static ssize_t i8k_hwmon_show_fan(struct device *dev, struct device_attribute *devattr, char *buf) @@ -611,14 +654,17 @@ static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 3); static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, i8k_hwmon_show_temp_label, NULL, 3); -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, - I8K_FAN_LEFT); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_fan_label, NULL, + 0); static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, - i8k_hwmon_set_pwm, I8K_FAN_LEFT); + i8k_hwmon_set_pwm, 0); static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL, - I8K_FAN_RIGHT); + 1); +static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_fan_label, NULL, + 1); static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, - i8k_hwmon_set_pwm, I8K_FAN_RIGHT); + i8k_hwmon_set_pwm, 1); static struct attribute *i8k_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */ @@ -630,9 +676,11 @@ static struct attribute *i8k_attrs[] = { &sensor_dev_attr_temp4_input.dev_attr.attr, /* 6 */ &sensor_dev_attr_temp4_label.dev_attr.attr, /* 7 */ &sensor_dev_attr_fan1_input.dev_attr.attr, /* 8 */ - &sensor_dev_attr_pwm1.dev_attr.attr, /* 9 */ - &sensor_dev_attr_fan2_input.dev_attr.attr, /* 10 */ - &sensor_dev_attr_pwm2.dev_attr.attr, /* 11 */ + &sensor_dev_attr_fan1_label.dev_attr.attr, /* 9 */ + &sensor_dev_attr_pwm1.dev_attr.attr, /* 10 */ + &sensor_dev_attr_fan2_input.dev_attr.attr, /* 11 */ + &sensor_dev_attr_fan2_label.dev_attr.attr, /* 12 */ + &sensor_dev_attr_pwm2.dev_attr.attr, /* 13 */ NULL }; @@ -651,10 +699,10 @@ static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, if (index >= 6 && index <= 7 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) return 0; - if (index >= 8 && index <= 9 && + if (index >= 8 && index <= 10 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) return 0; - if (index >= 10 && index <= 11 && + if (index >= 11 && index <= 13 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) return 0; @@ -688,13 +736,13 @@ static int __init i8k_init_hwmon(void) if (err >= 0) i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4; - /* Left fan attributes, if left fan is present */ - err = i8k_get_fan_status(I8K_FAN_LEFT); + /* First fan attributes, if fan type is OK */ + err = i8k_get_fan_type(0); if (err >= 0) i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1; - /* Right fan attributes, if right fan is present */ - err = i8k_get_fan_status(I8K_FAN_RIGHT); + /* Second fan attributes, if fan type is OK */ + err = i8k_get_fan_type(1); if (err >= 0) i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2; -- cgit v1.2.3 From d909f4315b99e5743041eeb01a2552fcbb125c89 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 13 Jan 2015 16:43:18 +0200 Subject: virtio/console: verify device has config space Some devices might not implement config space access (e.g. remoteproc used not to - before 3.9). virtio/console needs config space access so make it fail gracefully if not there. Signed-off-by: Michael S. Tsirkin Signed-off-by: Greg Kroah-Hartman --- drivers/char/virtio_console.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index de03df9dd7c9..26afb56a8073 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1986,6 +1986,12 @@ static int virtcons_probe(struct virtio_device *vdev) bool multiport; bool early = early_put_chars != NULL; + if (!vdev->config->get) { + dev_err(&vdev->dev, "%s failure: config access disabled\n", + __func__); + return -EINVAL; + } + /* Ensure to read early_put_chars now */ barrier(); -- cgit v1.2.3