summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
commite5fd91f1ef340da553f7a79da9540c3db711c937 (patch)
treeb11842027dc6641da63f4bcc524f8678263304a3 /drivers/hwmon
parent2a9b0348e685a63d97486f6749622b61e9e3292f (diff)
Linux-libre 4.2-gnu
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig29
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/atxp1.c58
-rw-r--r--drivers/hwmon/coretemp.c3
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c1043
-rw-r--r--drivers/hwmon/g762.c1
-rw-r--r--drivers/hwmon/max197.c2
-rw-r--r--drivers/hwmon/ntc_thermistor.c82
-rw-r--r--drivers/hwmon/sht15.c2
-rw-r--r--drivers/hwmon/tc74.c177
-rw-r--r--drivers/hwmon/w83627ehf.c26
-rw-r--r--drivers/hwmon/w83792d.c27
12 files changed, 1349 insertions, 103 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 25d9e7262..7c65b7334 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -371,6 +371,17 @@ config SENSORS_DS1621
This driver can also be built as a module. If so, the module
will be called ds1621.
+config SENSORS_DELL_SMM
+ tristate "Dell laptop SMM BIOS hwmon driver"
+ depends on X86
+ help
+ This hwmon driver adds support for reporting temperature of different
+ sensors and controls the fans on Dell laptops via System Management
+ Mode provided by Dell BIOS.
+
+ When option I8K is also enabled this driver provides legacy /proc/i8k
+ userspace interface for i8kutils package.
+
config SENSORS_DA9052_ADC
tristate "Dialog DA9052/DA9053 ADC"
depends on PMIC_DA9052
@@ -509,7 +520,7 @@ config SENSORS_G762
config SENSORS_GPIO_FAN
tristate "GPIO fan"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
depends on THERMAL || THERMAL=n
help
If you say yes here you get support for fans connected to GPIO lines.
@@ -1106,8 +1117,8 @@ config SENSORS_NTC_THERMISTOR
send notifications about the temperature.
Currently, this driver supports
- NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, and NCP15WL333
- from Murata and B57330V2103 from EPCOS.
+ NCP15WB473, NCP18WB473, NCP21WB473, NCP03WB473, NCP15WL333,
+ and NCP03WF104 from Murata and B57330V2103 from EPCOS.
This driver can also be built as a module. If so, the module
will be called ntc-thermistor.
@@ -1186,7 +1197,7 @@ config SENSORS_PWM_FAN
config SENSORS_SHT15
tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
If you say yes here you get support for the Sensiron SHT10, SHT11,
SHT15, SHT71, SHT75 humidity and temperature sensors.
@@ -1452,6 +1463,16 @@ config SENSORS_INA2XX
This driver can also be built as a module. If so, the module
will be called ina2xx.
+config SENSORS_TC74
+ tristate "Microchip TC74"
+ depends on I2C
+ help
+ If you say yes here you get support for Microchip TC74 single
+ input temperature sensor chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called tc74.
+
config SENSORS_THMC50
tristate "Texas Instruments THMC50 / Analog Devices ADM1022"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b4a40f17e..9e0f3dd28 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o
obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o
+obj-$(CONFIG_SENSORS_DELL_SMM) += dell-smm-hwmon.o
obj-$(CONFIG_SENSORS_DME1737) += dme1737.o
obj-$(CONFIG_SENSORS_DS620) += ds620.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
@@ -140,6 +141,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
+obj-$(CONFIG_SENSORS_TC74) += tc74.o
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
obj-$(CONFIG_SENSORS_TMP102) += tmp102.o
obj-$(CONFIG_SENSORS_TMP103) += tmp103.o
diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c
index 4c829bb2f..f2f2f2fc7 100644
--- a/drivers/hwmon/atxp1.c
+++ b/drivers/hwmon/atxp1.c
@@ -12,10 +12,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
+ * The ATXP1 can reside on I2C addresses 0x37 or 0x4e. The chip is
+ * not auto-detected by the driver and must be instantiated explicitly.
+ * See Documentation/i2c/instantiating-devices for more information.
*/
#include <linux/kernel.h>
@@ -43,8 +42,6 @@ MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>");
#define ATXP1_VIDMASK 0x1f
#define ATXP1_GPIO1MASK 0x0f
-static const unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END };
-
struct atxp1_data {
struct i2c_client *client;
struct mutex update_lock;
@@ -259,48 +256,6 @@ static struct attribute *atxp1_attrs[] = {
};
ATTRIBUTE_GROUPS(atxp1);
-/* Return 0 if detection is successful, -ENODEV otherwise */
-static int atxp1_detect(struct i2c_client *new_client,
- struct i2c_board_info *info)
-{
- struct i2c_adapter *adapter = new_client->adapter;
-
- u8 temp;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -ENODEV;
-
- /* Detect ATXP1, checking if vendor ID registers are all zero */
- if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) &&
- (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) &&
- (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) &&
- (i2c_smbus_read_byte_data(new_client, 0xff) == 0)))
- return -ENODEV;
-
- /*
- * No vendor ID, now checking if registers 0x10,0x11 (non-existent)
- * showing the same as register 0x00
- */
- temp = i2c_smbus_read_byte_data(new_client, 0x00);
-
- if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) &&
- (i2c_smbus_read_byte_data(new_client, 0x11) == temp)))
- return -ENODEV;
-
- /* Get VRM */
- temp = vid_which_vrm();
-
- if ((temp != 90) && (temp != 91)) {
- dev_err(&adapter->dev, "atxp1: Not supporting VRM %d.%d\n",
- temp / 10, temp % 10);
- return -ENODEV;
- }
-
- strlcpy(info->type, "atxp1", I2C_NAME_SIZE);
-
- return 0;
-}
-
static int atxp1_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -314,6 +269,11 @@ static int atxp1_probe(struct i2c_client *client,
/* Get VRM */
data->vrm = vid_which_vrm();
+ if (data->vrm != 90 && data->vrm != 91) {
+ dev_err(dev, "atxp1: Not supporting VRM %d.%d\n",
+ data->vrm / 10, data->vrm % 10);
+ return -ENODEV;
+ }
data->client = client;
mutex_init(&data->update_lock);
@@ -342,8 +302,6 @@ static struct i2c_driver atxp1_driver = {
},
.probe = atxp1_probe,
.id_table = atxp1_id,
- .detect = atxp1_detect,
- .address_list = normal_i2c,
};
module_i2c_driver(atxp1_driver);
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index ed303ba3a..3e03379e7 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -63,7 +63,8 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO)
#ifdef CONFIG_SMP
-#define for_each_sibling(i, cpu) for_each_cpu(i, cpu_sibling_mask(cpu))
+#define for_each_sibling(i, cpu) \
+ for_each_cpu(i, topology_sibling_cpumask(cpu))
#else
#define for_each_sibling(i, cpu) for (i = 0; false; )
#endif
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
new file mode 100644
index 000000000..c8487894b
--- /dev/null
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -0,0 +1,1043 @@
+/*
+ * dell-smm-hwmon.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
+ *
+ * Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org>
+ *
+ * Hwmon integration:
+ * Copyright (C) 2011 Jean Delvare <jdelvare@suse.de>
+ * Copyright (C) 2013, 2014 Guenter Roeck <linux@roeck-us.net>
+ * Copyright (C) 2014, 2015 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
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/dmi.h>
+#include <linux/capability.h>
+#include <linux/mutex.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+
+#include <linux/i8k.h>
+
+#define I8K_SMM_FN_STATUS 0x0025
+#define I8K_SMM_POWER_STATUS 0x0069
+#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
+#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
+#define I8K_FN_UP 0x01
+#define I8K_FN_DOWN 0x02
+#define I8K_FN_MUTE 0x04
+#define I8K_FN_MASK 0x07
+#define I8K_FN_SHIFT 8
+
+#define I8K_POWER_AC 0x05
+#define I8K_POWER_BATTERY 0x01
+
+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 = 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)
+#define I8K_HWMON_HAVE_TEMP3 (1 << 2)
+#define I8K_HWMON_HAVE_TEMP4 (1 << 3)
+#define I8K_HWMON_HAVE_FAN1 (1 << 4)
+#define I8K_HWMON_HAVE_FAN2 (1 << 5)
+
+MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("i8k");
+
+static bool force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Force loading without checking for supported models");
+
+static bool ignore_dmi;
+module_param(ignore_dmi, bool, 0);
+MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match");
+
+#if IS_ENABLED(CONFIG_I8K)
+static bool restricted;
+module_param(restricted, bool, 0);
+MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set");
+
+static bool power_status;
+module_param(power_status, bool, 0600);
+MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");
+#endif
+
+static uint fan_mult;
+module_param(fan_mult, uint, 0);
+MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with (default: autodetect)");
+
+static uint fan_max;
+module_param(fan_max, uint, 0);
+MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed (default: autodetect)");
+
+struct smm_regs {
+ unsigned int eax;
+ unsigned int ebx __packed;
+ unsigned int ecx __packed;
+ unsigned int edx __packed;
+ unsigned int esi __packed;
+ unsigned int edi __packed;
+};
+
+static inline const char *i8k_get_dmi_data(int field)
+{
+ const char *dmi_data = dmi_get_system_info(field);
+
+ return dmi_data && *dmi_data ? dmi_data : "?";
+}
+
+/*
+ * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
+ */
+static int i8k_smm(struct smm_regs *regs)
+{
+ int rc;
+ int eax = regs->eax;
+ cpumask_var_t old_mask;
+
+ /* SMM requires CPU 0 */
+ if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
+ return -ENOMEM;
+ cpumask_copy(old_mask, &current->cpus_allowed);
+ rc = set_cpus_allowed_ptr(current, cpumask_of(0));
+ if (rc)
+ goto out;
+ if (smp_processor_id() != 0) {
+ rc = -EBUSY;
+ goto out;
+ }
+
+#if defined(CONFIG_X86_64)
+ asm volatile("pushq %%rax\n\t"
+ "movl 0(%%rax),%%edx\n\t"
+ "pushq %%rdx\n\t"
+ "movl 4(%%rax),%%ebx\n\t"
+ "movl 8(%%rax),%%ecx\n\t"
+ "movl 12(%%rax),%%edx\n\t"
+ "movl 16(%%rax),%%esi\n\t"
+ "movl 20(%%rax),%%edi\n\t"
+ "popq %%rax\n\t"
+ "out %%al,$0xb2\n\t"
+ "out %%al,$0x84\n\t"
+ "xchgq %%rax,(%%rsp)\n\t"
+ "movl %%ebx,4(%%rax)\n\t"
+ "movl %%ecx,8(%%rax)\n\t"
+ "movl %%edx,12(%%rax)\n\t"
+ "movl %%esi,16(%%rax)\n\t"
+ "movl %%edi,20(%%rax)\n\t"
+ "popq %%rdx\n\t"
+ "movl %%edx,0(%%rax)\n\t"
+ "pushfq\n\t"
+ "popq %%rax\n\t"
+ "andl $1,%%eax\n"
+ : "=a"(rc)
+ : "a"(regs)
+ : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
+#else
+ asm volatile("pushl %%eax\n\t"
+ "movl 0(%%eax),%%edx\n\t"
+ "push %%edx\n\t"
+ "movl 4(%%eax),%%ebx\n\t"
+ "movl 8(%%eax),%%ecx\n\t"
+ "movl 12(%%eax),%%edx\n\t"
+ "movl 16(%%eax),%%esi\n\t"
+ "movl 20(%%eax),%%edi\n\t"
+ "popl %%eax\n\t"
+ "out %%al,$0xb2\n\t"
+ "out %%al,$0x84\n\t"
+ "xchgl %%eax,(%%esp)\n\t"
+ "movl %%ebx,4(%%eax)\n\t"
+ "movl %%ecx,8(%%eax)\n\t"
+ "movl %%edx,12(%%eax)\n\t"
+ "movl %%esi,16(%%eax)\n\t"
+ "movl %%edi,20(%%eax)\n\t"
+ "popl %%edx\n\t"
+ "movl %%edx,0(%%eax)\n\t"
+ "lahf\n\t"
+ "shrl $8,%%eax\n\t"
+ "andl $1,%%eax\n"
+ : "=a"(rc)
+ : "a"(regs)
+ : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
+#endif
+ if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
+ rc = -EINVAL;
+
+out:
+ set_cpus_allowed_ptr(current, old_mask);
+ free_cpumask_var(old_mask);
+ return rc;
+}
+
+/*
+ * Read the fan status.
+ */
+static int i8k_get_fan_status(int fan)
+{
+ struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, };
+
+ regs.ebx = fan & 0xff;
+ return i8k_smm(&regs) ? : regs.eax & 0xff;
+}
+
+/*
+ * Read the fan speed in RPM.
+ */
+static int i8k_get_fan_speed(int fan)
+{
+ struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
+
+ regs.ebx = fan & 0xff;
+ return i8k_smm(&regs) ? : (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(&regs) ? : regs.eax & 0xff;
+}
+
+/*
+ * 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)
+{
+ struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
+
+ speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed);
+ regs.ebx = (fan & 0xff) | (speed << 8);
+
+ return i8k_smm(&regs) ? : 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(&regs) ? : regs.eax & 0xff;
+}
+
+/*
+ * Read the cpu temperature.
+ */
+static int _i8k_get_temp(int sensor)
+{
+ struct smm_regs regs = {
+ .eax = I8K_SMM_GET_TEMP,
+ .ebx = sensor & 0xff,
+ };
+
+ return i8k_smm(&regs) ? : regs.eax & 0xff;
+}
+
+static int i8k_get_temp(int sensor)
+{
+ int temp = _i8k_get_temp(sensor);
+
+ /*
+ * Sometimes the temperature sensor returns 0x99, which is out of range.
+ * In this case we retry (once) before returning an error.
+ # 1003655137 00000058 00005a4b
+ # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
+ # 1003655139 00000054 00005c52
+ */
+ 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;
+
+ return temp;
+}
+
+static int i8k_get_dell_signature(int req_fn)
+{
+ struct smm_regs regs = { .eax = req_fn, };
+ int rc;
+
+ rc = i8k_smm(&regs);
+ if (rc < 0)
+ return rc;
+
+ return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
+}
+
+#if IS_ENABLED(CONFIG_I8K)
+
+/*
+ * Read the Fn key status.
+ */
+static int i8k_get_fn_status(void)
+{
+ struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
+ int rc;
+
+ rc = i8k_smm(&regs);
+ if (rc < 0)
+ return rc;
+
+ switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
+ case I8K_FN_UP:
+ return I8K_VOL_UP;
+ case I8K_FN_DOWN:
+ return I8K_VOL_DOWN;
+ case I8K_FN_MUTE:
+ return I8K_VOL_MUTE;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Read the power status.
+ */
+static int i8k_get_power_status(void)
+{
+ struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
+ int rc;
+
+ rc = i8k_smm(&regs);
+ if (rc < 0)
+ return rc;
+
+ return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
+}
+
+/*
+ * Procfs interface
+ */
+
+static int
+i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ int val = 0;
+ int speed;
+ unsigned char buff[16];
+ int __user *argp = (int __user *)arg;
+
+ if (!argp)
+ return -EINVAL;
+
+ switch (cmd) {
+ case I8K_BIOS_VERSION:
+ val = (bios_version[0] << 16) |
+ (bios_version[1] << 8) | bios_version[2];
+ break;
+
+ case I8K_MACHINE_ID:
+ memset(buff, 0, 16);
+ strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
+ sizeof(buff));
+ break;
+
+ case I8K_FN_STATUS:
+ val = i8k_get_fn_status();
+ break;
+
+ case I8K_POWER_STATUS:
+ val = i8k_get_power_status();
+ break;
+
+ case I8K_GET_TEMP:
+ val = i8k_get_temp(0);
+ break;
+
+ case I8K_GET_SPEED:
+ if (copy_from_user(&val, argp, sizeof(int)))
+ return -EFAULT;
+
+ val = i8k_get_fan_speed(val);
+ break;
+
+ case I8K_GET_FAN:
+ if (copy_from_user(&val, argp, sizeof(int)))
+ return -EFAULT;
+
+ val = i8k_get_fan_status(val);
+ break;
+
+ case I8K_SET_FAN:
+ if (restricted && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&val, argp, sizeof(int)))
+ return -EFAULT;
+
+ if (copy_from_user(&speed, argp + 1, sizeof(int)))
+ return -EFAULT;
+
+ val = i8k_set_fan(val, speed);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (val < 0)
+ return val;
+
+ switch (cmd) {
+ case I8K_BIOS_VERSION:
+ if (copy_to_user(argp, &val, 4))
+ return -EFAULT;
+
+ break;
+ case I8K_MACHINE_ID:
+ if (copy_to_user(argp, buff, 16))
+ return -EFAULT;
+
+ break;
+ default:
+ if (copy_to_user(argp, &val, sizeof(int)))
+ return -EFAULT;
+
+ break;
+ }
+
+ return 0;
+}
+
+static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ mutex_lock(&i8k_mutex);
+ ret = i8k_ioctl_unlocked(fp, cmd, arg);
+ mutex_unlock(&i8k_mutex);
+
+ return ret;
+}
+
+/*
+ * Print the information for /proc/i8k.
+ */
+static int i8k_proc_show(struct seq_file *seq, void *offset)
+{
+ int fn_key, cpu_temp, ac_power;
+ int left_fan, right_fan, left_speed, right_speed;
+
+ cpu_temp = i8k_get_temp(0); /* 11100 µs */
+ left_fan = i8k_get_fan_status(I8K_FAN_LEFT); /* 580 µs */
+ right_fan = i8k_get_fan_status(I8K_FAN_RIGHT); /* 580 µs */
+ left_speed = i8k_get_fan_speed(I8K_FAN_LEFT); /* 580 µs */
+ right_speed = i8k_get_fan_speed(I8K_FAN_RIGHT); /* 580 µs */
+ fn_key = i8k_get_fn_status(); /* 750 µs */
+ if (power_status)
+ ac_power = i8k_get_power_status(); /* 14700 µs */
+ else
+ ac_power = -1;
+
+ /*
+ * Info:
+ *
+ * 1) Format version (this will change if format changes)
+ * 2) BIOS version
+ * 3) BIOS machine ID
+ * 4) Cpu temperature
+ * 5) Left fan status
+ * 6) Right fan status
+ * 7) Left fan speed
+ * 8) Right fan speed
+ * 9) AC power
+ * 10) Fn Key status
+ */
+ seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
+ I8K_PROC_FMT,
+ bios_version,
+ i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
+ cpu_temp,
+ left_fan, right_fan, left_speed, right_speed,
+ ac_power, fn_key);
+
+ return 0;
+}
+
+static int i8k_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, i8k_proc_show, NULL);
+}
+
+static const struct file_operations i8k_fops = {
+ .owner = THIS_MODULE,
+ .open = i8k_open_fs,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .unlocked_ioctl = i8k_ioctl,
+};
+
+static void __init i8k_init_procfs(void)
+{
+ /* Register the proc entry */
+ proc_create("i8k", 0, NULL, &i8k_fops);
+}
+
+static void __exit i8k_exit_procfs(void)
+{
+ remove_proc_entry("i8k", NULL);
+}
+
+#else
+
+static inline void __init i8k_init_procfs(void)
+{
+}
+
+static inline void __exit i8k_exit_procfs(void)
+{
+}
+
+#endif
+
+/*
+ * 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)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ int temp;
+
+ temp = i8k_get_temp(index);
+ if (temp < 0)
+ return temp;
+ 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)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ int fan_speed;
+
+ fan_speed = i8k_get_fan_speed(index);
+ if (fan_speed < 0)
+ return fan_speed;
+ return sprintf(buf, "%d\n", fan_speed);
+}
+
+static ssize_t i8k_hwmon_show_pwm(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ int index = to_sensor_dev_attr(devattr)->index;
+ int status;
+
+ status = i8k_get_fan_status(index);
+ if (status < 0)
+ return -EIO;
+ return sprintf(buf, "%d\n", clamp_val(status * i8k_pwm_mult, 0, 255));
+}
+
+static ssize_t i8k_hwmon_set_pwm(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ val = clamp_val(DIV_ROUND_CLOSEST(val, i8k_pwm_mult), 0, i8k_fan_max);
+
+ mutex_lock(&i8k_mutex);
+ err = i8k_set_fan(index, val);
+ mutex_unlock(&i8k_mutex);
+
+ return err < 0 ? -EIO : count;
+}
+
+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, 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, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
+ 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, 1);
+
+static struct attribute *i8k_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */
+ &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_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
+};
+
+static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
+ int index)
+{
+ if (index >= 0 && index <= 1 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
+ return 0;
+ if (index >= 2 && index <= 3 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
+ return 0;
+ if (index >= 4 && index <= 5 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
+ return 0;
+ if (index >= 6 && index <= 7 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
+ return 0;
+ if (index >= 8 && index <= 10 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
+ return 0;
+ if (index >= 11 && index <= 13 &&
+ !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group i8k_group = {
+ .attrs = i8k_attrs,
+ .is_visible = i8k_is_visible,
+};
+__ATTRIBUTE_GROUPS(i8k);
+
+static int __init i8k_init_hwmon(void)
+{
+ int err;
+
+ i8k_hwmon_flags = 0;
+
+ /* 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_type(1);
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2;
+ err = i8k_get_temp_type(2);
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3;
+ err = i8k_get_temp_type(3);
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4;
+
+ /* First fan attributes, if fan type is OK */
+ err = i8k_get_fan_type(0);
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1;
+
+ /* Second fan attributes, if fan type is OK */
+ err = i8k_get_fan_type(1);
+ if (err >= 0)
+ i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
+
+ i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "dell_smm",
+ NULL, i8k_groups);
+ if (IS_ERR(i8k_hwmon_dev)) {
+ err = PTR_ERR(i8k_hwmon_dev);
+ i8k_hwmon_dev = NULL;
+ pr_err("hwmon registration failed (%d)\n", err);
+ return err;
+ }
+ return 0;
+}
+
+struct i8k_config_data {
+ uint fan_mult;
+ uint fan_max;
+};
+
+enum i8k_configs {
+ DELL_LATITUDE_D520,
+ DELL_PRECISION_490,
+ DELL_STUDIO,
+ DELL_XPS,
+};
+
+static const struct i8k_config_data i8k_config_data[] = {
+ [DELL_LATITUDE_D520] = {
+ .fan_mult = 1,
+ .fan_max = I8K_FAN_TURBO,
+ },
+ [DELL_PRECISION_490] = {
+ .fan_mult = 1,
+ .fan_max = I8K_FAN_TURBO,
+ },
+ [DELL_STUDIO] = {
+ .fan_mult = 1,
+ .fan_max = I8K_FAN_HIGH,
+ },
+ [DELL_XPS] = {
+ .fan_mult = 1,
+ .fan_max = I8K_FAN_HIGH,
+ },
+};
+
+static struct dmi_system_id i8k_dmi_table[] __initdata = {
+ {
+ .ident = "Dell Inspiron",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
+ },
+ },
+ {
+ .ident = "Dell Latitude",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
+ },
+ },
+ {
+ .ident = "Dell Inspiron 2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
+ },
+ },
+ {
+ .ident = "Dell Latitude D520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"),
+ },
+ .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520],
+ },
+ {
+ .ident = "Dell Latitude 2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"),
+ },
+ },
+ { /* UK Inspiron 6400 */
+ .ident = "Dell Inspiron 3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MM061"),
+ },
+ },
+ {
+ .ident = "Dell Inspiron 3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
+ },
+ },
+ {
+ .ident = "Dell Precision 490",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME,
+ "Precision WorkStation 490"),
+ },
+ .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490],
+ },
+ {
+ .ident = "Dell Precision",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Precision"),
+ },
+ },
+ {
+ .ident = "Dell Vostro",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"),
+ },
+ },
+ {
+ .ident = "Dell XPS421",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"),
+ },
+ },
+ {
+ .ident = "Dell Studio",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
+ },
+ .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],
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(dmi, i8k_dmi_table);
+
+static struct dmi_system_id i8k_blacklist_dmi_table[] __initdata = {
+ {
+ /*
+ * CPU fan speed going up and down on Dell Studio XPS 8100
+ * for unknown reasons.
+ */
+ .ident = "Dell Studio XPS 8100",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8100"),
+ },
+ },
+ { }
+};
+
+/*
+ * Probe for the presence of a supported laptop.
+ */
+static int __init i8k_probe(void)
+{
+ const struct dmi_system_id *id;
+ int fan, ret;
+
+ /*
+ * Get DMI information
+ */
+ if (!dmi_check_system(i8k_dmi_table) ||
+ dmi_check_system(i8k_blacklist_dmi_table)) {
+ if (!ignore_dmi && !force)
+ return -ENODEV;
+
+ pr_info("not running on a supported Dell system.\n");
+ pr_info("vendor=%s, model=%s, version=%s\n",
+ i8k_get_dmi_data(DMI_SYS_VENDOR),
+ i8k_get_dmi_data(DMI_PRODUCT_NAME),
+ i8k_get_dmi_data(DMI_BIOS_VERSION));
+ }
+
+ strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
+ sizeof(bios_version));
+
+ /*
+ * Get SMM Dell signature
+ */
+ if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
+ i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
+ pr_err("unable to get SMM Dell signature\n");
+ if (!force)
+ return -ENODEV;
+ }
+
+ /*
+ * 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 && 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;
+}
+
+static int __init i8k_init(void)
+{
+ int err;
+
+ /* Are we running on an supported laptop? */
+ if (i8k_probe())
+ return -ENODEV;
+
+ err = i8k_init_hwmon();
+ if (err)
+ return err;
+
+ i8k_init_procfs();
+ return 0;
+}
+
+static void __exit i8k_exit(void)
+{
+ hwmon_device_unregister(i8k_hwmon_dev);
+ i8k_exit_procfs();
+}
+
+module_init(i8k_init);
+module_exit(i8k_exit);
diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c
index 9b55e673b..85d106fe3 100644
--- a/drivers/hwmon/g762.c
+++ b/drivers/hwmon/g762.c
@@ -582,6 +582,7 @@ static const struct of_device_id g762_dt_match[] = {
{ .compatible = "gmt,g763" },
{ },
};
+MODULE_DEVICE_TABLE(of, g762_dt_match);
/*
* Grab clock (a required property), enable it, get (fixed) clock frequency
diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c
index cb0dcfda9..076285695 100644
--- a/drivers/hwmon/max197.c
+++ b/drivers/hwmon/max197.c
@@ -324,7 +324,7 @@ static int max197_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id max197_device_ids[] = {
+static const struct platform_device_id max197_device_ids[] = {
{ "max197", max197 },
{ "max199", max199 },
{ }
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index 688001158..dc0b76c5e 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -53,6 +53,7 @@ static const struct platform_device_id ntc_thermistor_id[] = {
{ "ncp03wb473", TYPE_NCPXXWB473 },
{ "ncp15wl333", TYPE_NCPXXWL333 },
{ "b57330v2103", TYPE_B57330V2103},
+ { "ncp03wf104", TYPE_NCPXXWF104 },
{ },
};
@@ -135,6 +136,43 @@ static const struct ntc_compensation ncpXXwl333[] = {
{ .temp_c = 125, .ohm = 707 },
};
+static const struct ntc_compensation ncpXXwf104[] = {
+ { .temp_c = -40, .ohm = 4397119 },
+ { .temp_c = -35, .ohm = 3088599 },
+ { .temp_c = -30, .ohm = 2197225 },
+ { .temp_c = -25, .ohm = 1581881 },
+ { .temp_c = -20, .ohm = 1151037 },
+ { .temp_c = -15, .ohm = 846579 },
+ { .temp_c = -10, .ohm = 628988 },
+ { .temp_c = -5, .ohm = 471632 },
+ { .temp_c = 0, .ohm = 357012 },
+ { .temp_c = 5, .ohm = 272500 },
+ { .temp_c = 10, .ohm = 209710 },
+ { .temp_c = 15, .ohm = 162651 },
+ { .temp_c = 20, .ohm = 127080 },
+ { .temp_c = 25, .ohm = 100000 },
+ { .temp_c = 30, .ohm = 79222 },
+ { .temp_c = 35, .ohm = 63167 },
+ { .temp_c = 40, .ohm = 50677 },
+ { .temp_c = 45, .ohm = 40904 },
+ { .temp_c = 50, .ohm = 33195 },
+ { .temp_c = 55, .ohm = 27091 },
+ { .temp_c = 60, .ohm = 22224 },
+ { .temp_c = 65, .ohm = 18323 },
+ { .temp_c = 70, .ohm = 15184 },
+ { .temp_c = 75, .ohm = 12635 },
+ { .temp_c = 80, .ohm = 10566 },
+ { .temp_c = 85, .ohm = 8873 },
+ { .temp_c = 90, .ohm = 7481 },
+ { .temp_c = 95, .ohm = 6337 },
+ { .temp_c = 100, .ohm = 5384 },
+ { .temp_c = 105, .ohm = 4594 },
+ { .temp_c = 110, .ohm = 3934 },
+ { .temp_c = 115, .ohm = 3380 },
+ { .temp_c = 120, .ohm = 2916 },
+ { .temp_c = 125, .ohm = 2522 },
+};
+
/*
* The following compensation table is from the specification of EPCOS NTC
* Thermistors Datasheet
@@ -190,20 +228,21 @@ struct ntc_data {
static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata)
{
struct iio_channel *channel = pdata->chan;
- s64 result;
- int val, ret;
+ int raw, uv, ret;
- ret = iio_read_channel_raw(channel, &val);
+ ret = iio_read_channel_raw(channel, &raw);
if (ret < 0) {
pr_err("read channel() error: %d\n", ret);
return ret;
}
- /* unit: mV */
- result = pdata->pullup_uv * (s64) val;
- result >>= 12;
+ ret = iio_convert_raw_to_processed(channel, raw, &uv, 1000);
+ if (ret < 0) {
+ /* Assume 12 bit ADC with vref at pullup_uv */
+ uv = (pdata->pullup_uv * (s64)raw) >> 12;
+ }
- return (int)result;
+ return uv;
}
static const struct of_device_id ntc_match[] = {
@@ -219,6 +258,8 @@ static const struct of_device_id ntc_match[] = {
.data = &ntc_thermistor_id[4] },
{ .compatible = "epcos,b57330v2103",
.data = &ntc_thermistor_id[5]},
+ { .compatible = "murata,ncp03wf104",
+ .data = &ntc_thermistor_id[6] },
/* Usage of vendor name "ntc" is deprecated */
{ .compatible = "ntc,ncp15wb473",
@@ -309,30 +350,27 @@ static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
{
struct ntc_thermistor_platform_data *pdata = data->pdata;
- u64 mv = uv / 1000;
- u64 pmv = pdata->pullup_uv / 1000;
+ u32 puv = pdata->pullup_uv;
u64 n, puo, pdo;
puo = pdata->pullup_ohm;
pdo = pdata->pulldown_ohm;
- if (mv == 0) {
- if (pdata->connect == NTC_CONNECTED_POSITIVE)
- return INT_MAX;
- return 0;
- }
- if (mv >= pmv)
+ if (uv == 0)
+ return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
+ INT_MAX : 0;
+ if (uv >= puv)
return (pdata->connect == NTC_CONNECTED_POSITIVE) ?
0 : INT_MAX;
if (pdata->connect == NTC_CONNECTED_POSITIVE && puo == 0)
- n = div64_u64_safe(pdo * (pmv - mv), mv);
+ n = div_u64(pdo * (puv - uv), uv);
else if (pdata->connect == NTC_CONNECTED_GROUND && pdo == 0)
- n = div64_u64_safe(puo * mv, pmv - mv);
+ n = div_u64(puo * uv, puv - uv);
else if (pdata->connect == NTC_CONNECTED_POSITIVE)
- n = div64_u64_safe(pdo * puo * (pmv - mv),
- puo * mv - pdo * (pmv - mv));
+ n = div64_u64_safe(pdo * puo * (puv - uv),
+ puo * uv - pdo * (puv - uv));
else
- n = div64_u64_safe(pdo * puo * mv, pdo * (pmv - mv) - puo * mv);
+ n = div64_u64_safe(pdo * puo * uv, pdo * (puv - uv) - puo * uv);
if (n > INT_MAX)
n = INT_MAX;
@@ -567,6 +605,10 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
data->comp = b57330v2103;
data->n_comp = ARRAY_SIZE(b57330v2103);
break;
+ case TYPE_NCPXXWF104:
+ data->comp = ncpXXwf104;
+ data->n_comp = ARRAY_SIZE(ncpXXwf104);
+ break;
default:
dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
pdev_id->driver_data, pdev_id->name);
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index d4f0935da..497a7f822 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -1074,7 +1074,7 @@ static int sht15_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id sht15_device_ids[] = {
+static const struct platform_device_id sht15_device_ids[] = {
{ "sht10", sht10 },
{ "sht11", sht11 },
{ "sht15", sht15 },
diff --git a/drivers/hwmon/tc74.c b/drivers/hwmon/tc74.c
new file mode 100644
index 000000000..d95165158
--- /dev/null
+++ b/drivers/hwmon/tc74.c
@@ -0,0 +1,177 @@
+/*
+ * An hwmon driver for the Microchip TC74
+ *
+ * Copyright 2015 Maciej Szmigiero <mail@maciej.szmigiero.name>
+ *
+ * Based on ad7414.c:
+ * Copyright 2006 Stefan Roese, DENX Software Engineering
+ * Copyright 2008 Sean MacLennan, PIKA Technologies
+ * Copyright 2008 Frank Edelhaeuser, Spansion Inc.
+ *
+ * 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 Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+/* TC74 registers */
+#define TC74_REG_TEMP 0x00
+#define TC74_REG_CONFIG 0x01
+
+struct tc74_data {
+ struct i2c_client *client;
+ struct mutex lock; /* atomic read data updates */
+ bool valid; /* validity of fields below */
+ unsigned long next_update; /* In jiffies */
+ s8 temp_input; /* Temp value in dC */
+};
+
+static int tc74_update_device(struct device *dev)
+{
+ struct tc74_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = mutex_lock_interruptible(&data->lock);
+ if (ret)
+ return ret;
+
+ if (time_after(jiffies, data->next_update) || !data->valid) {
+ s32 value;
+
+ value = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
+ if (value < 0) {
+ dev_dbg(&client->dev, "TC74_REG_CONFIG read err %d\n",
+ (int)value);
+
+ ret = value;
+ goto ret_unlock;
+ }
+
+ if (!(value & BIT(6))) {
+ /* not ready yet */
+
+ ret = -EAGAIN;
+ goto ret_unlock;
+ }
+
+ value = i2c_smbus_read_byte_data(client, TC74_REG_TEMP);
+ if (value < 0) {
+ dev_dbg(&client->dev, "TC74_REG_TEMP read err %d\n",
+ (int)value);
+
+ ret = value;
+ goto ret_unlock;
+ }
+
+ data->temp_input = value;
+ data->next_update = jiffies + HZ / 4;
+ data->valid = true;
+ }
+
+ret_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static ssize_t show_temp_input(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tc74_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = tc74_update_device(dev);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n", data->temp_input * 1000);
+}
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
+
+static struct attribute *tc74_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(tc74);
+
+static int tc74_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ struct device *dev = &client->dev;
+ struct tc74_data *data;
+ struct device *hwmon_dev;
+ s32 conf;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EOPNOTSUPP;
+
+ data = devm_kzalloc(dev, sizeof(struct tc74_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ mutex_init(&data->lock);
+
+ /* Make sure the chip is powered up. */
+ conf = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
+ if (conf < 0) {
+ dev_err(dev, "unable to read config register\n");
+
+ return conf;
+ }
+
+ if (conf & 0x3f) {
+ dev_err(dev, "invalid config register value\n");
+
+ return -ENODEV;
+ }
+
+ if (conf & BIT(7)) {
+ s32 ret;
+
+ conf &= ~BIT(7);
+
+ ret = i2c_smbus_write_byte_data(client, TC74_REG_CONFIG, conf);
+ if (ret)
+ dev_warn(dev, "unable to disable STANDBY\n");
+ }
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ client->name,
+ data, tc74_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id tc74_id[] = {
+ { "tc74", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tc74_id);
+
+static struct i2c_driver tc74_driver = {
+ .driver = {
+ .name = "tc74",
+ },
+ .probe = tc74_probe,
+ .id_table = tc74_id,
+};
+
+module_i2c_driver(tc74_driver);
+
+MODULE_AUTHOR("Maciej Szmigiero <mail@maciej.szmigiero.name>");
+
+MODULE_DESCRIPTION("TC74 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index b10353b31..697007afb 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -1937,27 +1937,11 @@ static inline void w83627ehf_init_device(struct w83627ehf_data *data,
static void w82627ehf_swap_tempreg(struct w83627ehf_data *data,
int r1, int r2)
{
- u16 tmp;
-
- tmp = data->temp_src[r1];
- data->temp_src[r1] = data->temp_src[r2];
- data->temp_src[r2] = tmp;
-
- tmp = data->reg_temp[r1];
- data->reg_temp[r1] = data->reg_temp[r2];
- data->reg_temp[r2] = tmp;
-
- tmp = data->reg_temp_over[r1];
- data->reg_temp_over[r1] = data->reg_temp_over[r2];
- data->reg_temp_over[r2] = tmp;
-
- tmp = data->reg_temp_hyst[r1];
- data->reg_temp_hyst[r1] = data->reg_temp_hyst[r2];
- data->reg_temp_hyst[r2] = tmp;
-
- tmp = data->reg_temp_config[r1];
- data->reg_temp_config[r1] = data->reg_temp_config[r2];
- data->reg_temp_config[r2] = tmp;
+ swap(data->temp_src[r1], data->temp_src[r2]);
+ swap(data->reg_temp[r1], data->reg_temp[r2]);
+ swap(data->reg_temp_over[r1], data->reg_temp_over[r2]);
+ swap(data->reg_temp_hyst[r1], data->reg_temp_hyst[r2]);
+ swap(data->reg_temp_config[r1], data->reg_temp_config[r2]);
}
static void
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 4068db4d9..0a8bce726 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -289,10 +289,7 @@ struct w83792d_data {
u8 temp1[3]; /* current, over, thyst */
u8 temp_add[2][6]; /* Register value */
u8 fan_div[7]; /* Register encoding, shifted right */
- u8 pwm[7]; /*
- * We only consider the first 3 set of pwm,
- * although 792 chip has 7 set of pwm.
- */
+ u8 pwm[7]; /* The 7 PWM outputs */
u8 pwmenable[3];
u32 alarms; /* realtime status register encoding,combined */
u8 chassis; /* Chassis status */
@@ -1075,6 +1072,10 @@ static DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR,
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0);
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3);
+static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4);
+static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 5);
+static SENSOR_DEVICE_ATTR(pwm7, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 6);
static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
show_pwmenable, store_pwmenable, 1);
static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
@@ -1087,6 +1088,14 @@ static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO,
show_pwm_mode, store_pwm_mode, 1);
static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO,
show_pwm_mode, store_pwm_mode, 2);
+static SENSOR_DEVICE_ATTR(pwm4_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 3);
+static SENSOR_DEVICE_ATTR(pwm5_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 4);
+static SENSOR_DEVICE_ATTR(pwm6_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 5);
+static SENSOR_DEVICE_ATTR(pwm7_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 6);
static SENSOR_DEVICE_ATTR(tolerance1, S_IWUSR | S_IRUGO,
show_tolerance, store_tolerance, 1);
static SENSOR_DEVICE_ATTR(tolerance2, S_IWUSR | S_IRUGO,
@@ -1177,30 +1186,38 @@ static SENSOR_DEVICE_ATTR(fan6_div, S_IWUSR | S_IRUGO,
static SENSOR_DEVICE_ATTR(fan7_div, S_IWUSR | S_IRUGO,
show_fan_div, store_fan_div, 7);
-static struct attribute *w83792d_attributes_fan[4][5] = {
+static struct attribute *w83792d_attributes_fan[4][7] = {
{
&sensor_dev_attr_fan4_input.dev_attr.attr,
&sensor_dev_attr_fan4_min.dev_attr.attr,
&sensor_dev_attr_fan4_div.dev_attr.attr,
&sensor_dev_attr_fan4_alarm.dev_attr.attr,
+ &sensor_dev_attr_pwm4.dev_attr.attr,
+ &sensor_dev_attr_pwm4_mode.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_fan5_input.dev_attr.attr,
&sensor_dev_attr_fan5_min.dev_attr.attr,
&sensor_dev_attr_fan5_div.dev_attr.attr,
&sensor_dev_attr_fan5_alarm.dev_attr.attr,
+ &sensor_dev_attr_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm5_mode.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_fan6_input.dev_attr.attr,
&sensor_dev_attr_fan6_min.dev_attr.attr,
&sensor_dev_attr_fan6_div.dev_attr.attr,
&sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ &sensor_dev_attr_pwm6.dev_attr.attr,
+ &sensor_dev_attr_pwm6_mode.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_fan7_input.dev_attr.attr,
&sensor_dev_attr_fan7_min.dev_attr.attr,
&sensor_dev_attr_fan7_div.dev_attr.attr,
&sensor_dev_attr_fan7_alarm.dev_attr.attr,
+ &sensor_dev_attr_pwm7.dev_attr.attr,
+ &sensor_dev_attr_pwm7_mode.dev_attr.attr,
NULL
}
};