diff options
author | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-11 10:41:07 +0300 |
---|---|---|
committer | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-13 08:17:18 +0300 |
commit | e09b41010ba33a20a87472ee821fa407a5b8da36 (patch) | |
tree | d10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/hwmon | |
parent | f93b97fd65072de626c074dbe099a1fff05ce060 (diff) |
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page.
During the rebasing, the following patch collided:
Force tick interrupt and get rid of softirq magic(I70131fb85).
Collisions have been removed because its logic was found on the
source already.
Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/hwmon')
47 files changed, 4159 insertions, 867 deletions
diff --git a/kernel/drivers/hwmon/Kconfig b/kernel/drivers/hwmon/Kconfig index 25d9e7262..80a73bfc1 100644 --- a/kernel/drivers/hwmon/Kconfig +++ b/kernel/drivers/hwmon/Kconfig @@ -321,6 +321,15 @@ config SENSORS_APPLESMC Say Y here if you have an applicable laptop and want to experience the awesome power of applesmc. +config SENSORS_ARM_SCPI + tristate "ARM SCPI Sensors" + depends on ARM_SCPI_PROTOCOL + depends on THERMAL || !THERMAL_OF + help + This driver provides support for temperature, voltage, current + and power sensors available on ARM Ltd's SCP based platforms. The + actual number and type of sensors exported depend on the platform. + config SENSORS_ASB100 tristate "Asus ASB100 Bach" depends on X86 && I2C @@ -371,6 +380,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 +529,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. @@ -598,8 +618,8 @@ config SENSORS_IT87 depends on !PPC select HWMON_VID help - If you say yes here you get support for ITE IT8705F, IT8712F, - IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, + If you say yes here you get support for ITE IT8705F, IT8712F, IT8716F, + IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F, IT8758E, IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E, IT8603E, IT8620E, and IT8623E sensor chips, and the SiS950 clone. @@ -829,6 +849,16 @@ config SENSORS_MAX6697 This driver can also be built as a module. If so, the module will be called max6697. +config SENSORS_MAX31790 + tristate "Maxim MAX31790 sensor chip" + depends on I2C + help + If you say yes here you get support for 6-Channel PWM-Output + Fan RPM Controller. + + This driver can also be built as a module. If so, the module + will be called max31790. + config SENSORS_HTU21 tristate "Measurement Specialties HTU21D humidity/temperature sensors" depends on I2C @@ -1106,8 +1136,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. @@ -1129,8 +1159,8 @@ config SENSORS_NCT6775 help If you say yes here you get support for the hardware monitoring functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D, - NCT6791D, NCT6792D and compatible Super-I/O chips. This driver - replaces the w83627ehf driver for NCT6775F and NCT6776F. + NCT6791D, NCT6792D, NCT6793D, and compatible Super-I/O chips. This + driver replaces the w83627ehf driver for NCT6775F and NCT6776F. This driver can also be built as a module. If so, the module will be called nct6775. @@ -1186,7 +1216,8 @@ config SENSORS_PWM_FAN config SENSORS_SHT15 tristate "Sensiron humidity and temperature sensors. SHT15 and compat." - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST + select BITREVERSE help If you say yes here you get support for the Sensiron SHT10, SHT11, SHT15, SHT71, SHT75 humidity and temperature sensors. @@ -1442,6 +1473,7 @@ config SENSORS_INA209 config SENSORS_INA2XX tristate "Texas Instruments INA219 and compatibles" depends on I2C + select REGMAP_I2C help If you say yes here you get support for INA219, INA220, INA226, INA230, and INA231 power monitor chips. @@ -1452,6 +1484,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/kernel/drivers/hwmon/Makefile b/kernel/drivers/hwmon/Makefile index b4a40f17e..12a32398f 100644 --- a/kernel/drivers/hwmon/Makefile +++ b/kernel/drivers/hwmon/Makefile @@ -44,11 +44,13 @@ obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o +obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o 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 @@ -114,6 +116,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MAX6697) += max6697.o +obj-$(CONFIG_SENSORS_MAX31790) += max31790.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o @@ -140,6 +143,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/kernel/drivers/hwmon/abx500.c b/kernel/drivers/hwmon/abx500.c index 6cb89c0eb..d87cae8c6 100644 --- a/kernel/drivers/hwmon/abx500.c +++ b/kernel/drivers/hwmon/abx500.c @@ -377,7 +377,7 @@ static int setup_irqs(struct platform_device *pdev) } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - abx500_temp_irq_handler, IRQF_NO_SUSPEND, "abx500-temp", pdev); + abx500_temp_irq_handler, 0, "abx500-temp", pdev); if (ret < 0) dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); @@ -470,6 +470,7 @@ static const struct of_device_id abx500_temp_match[] = { { .compatible = "stericsson,abx500-temp" }, {}, }; +MODULE_DEVICE_TABLE(of, abx500_temp_match); #endif static struct platform_driver abx500_temp_driver = { diff --git a/kernel/drivers/hwmon/ad7314.c b/kernel/drivers/hwmon/ad7314.c index 11955467f..202c1fbb3 100644 --- a/kernel/drivers/hwmon/ad7314.c +++ b/kernel/drivers/hwmon/ad7314.c @@ -157,7 +157,6 @@ MODULE_DEVICE_TABLE(spi, ad7314_id); static struct spi_driver ad7314_driver = { .driver = { .name = "ad7314", - .owner = THIS_MODULE, }, .probe = ad7314_probe, .remove = ad7314_remove, diff --git a/kernel/drivers/hwmon/adcxx.c b/kernel/drivers/hwmon/adcxx.c index 04c08c2f7..69e0bb97e 100644 --- a/kernel/drivers/hwmon/adcxx.c +++ b/kernel/drivers/hwmon/adcxx.c @@ -234,7 +234,6 @@ MODULE_DEVICE_TABLE(spi, adcxx_ids); static struct spi_driver adcxx_driver = { .driver = { .name = "adcxx", - .owner = THIS_MODULE, }, .id_table = adcxx_ids, .probe = adcxx_probe, diff --git a/kernel/drivers/hwmon/ads1015.c b/kernel/drivers/hwmon/ads1015.c index f155b8380..2b3105c8a 100644 --- a/kernel/drivers/hwmon/ads1015.c +++ b/kernel/drivers/hwmon/ads1015.c @@ -126,7 +126,7 @@ static int ads1015_reg_to_mv(struct i2c_client *client, unsigned int channel, struct ads1015_data *data = i2c_get_clientdata(client); unsigned int pga = data->channel_data[channel].pga; int fullscale = fullscale_table[pga]; - const unsigned mask = data->id == ads1115 ? 0x7fff : 0x7ff0; + const int mask = data->id == ads1115 ? 0x7fff : 0x7ff0; return DIV_ROUND_CLOSEST(reg * fullscale, mask); } diff --git a/kernel/drivers/hwmon/ads7871.c b/kernel/drivers/hwmon/ads7871.c index 3eff73b62..4fd9e4de1 100644 --- a/kernel/drivers/hwmon/ads7871.c +++ b/kernel/drivers/hwmon/ads7871.c @@ -237,7 +237,6 @@ static int ads7871_remove(struct spi_device *spi) static struct spi_driver ads7871_driver = { .driver = { .name = DEVICE_NAME, - .owner = THIS_MODULE, }, .probe = ads7871_probe, diff --git a/kernel/drivers/hwmon/adt7310.c b/kernel/drivers/hwmon/adt7310.c index 5994cf68e..ec02f4f0d 100644 --- a/kernel/drivers/hwmon/adt7310.c +++ b/kernel/drivers/hwmon/adt7310.c @@ -104,7 +104,6 @@ MODULE_DEVICE_TABLE(spi, adt7310_id); static struct spi_driver adt7310_driver = { .driver = { .name = "adt7310", - .owner = THIS_MODULE, .pm = ADT7X10_DEV_PM_OPS, }, .probe = adt7310_spi_probe, diff --git a/kernel/drivers/hwmon/applesmc.c b/kernel/drivers/hwmon/applesmc.c index 0af63da6b..0af7fd311 100644 --- a/kernel/drivers/hwmon/applesmc.c +++ b/kernel/drivers/hwmon/applesmc.c @@ -537,7 +537,7 @@ static int applesmc_init_index(struct applesmc_registers *s) static int applesmc_init_smcreg_try(void) { struct applesmc_registers *s = &smcreg; - bool left_light_sensor, right_light_sensor; + bool left_light_sensor = 0, right_light_sensor = 0; unsigned int count; u8 tmp[1]; int ret; @@ -1138,7 +1138,7 @@ out: return ret; } -/* Create accelerometer ressources */ +/* Create accelerometer resources */ static int applesmc_create_accelerometer(void) { struct input_dev *idev; @@ -1191,7 +1191,7 @@ out: return ret; } -/* Release all ressources used by the accelerometer */ +/* Release all resources used by the accelerometer */ static void applesmc_release_accelerometer(void) { if (!smcreg.has_accelerometer) diff --git a/kernel/drivers/hwmon/atxp1.c b/kernel/drivers/hwmon/atxp1.c index 4c829bb2f..f2f2f2fc7 100644 --- a/kernel/drivers/hwmon/atxp1.c +++ b/kernel/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/kernel/drivers/hwmon/coretemp.c b/kernel/drivers/hwmon/coretemp.c index ed303ba3a..6a27eb2fe 100644 --- a/kernel/drivers/hwmon/coretemp.c +++ b/kernel/drivers/hwmon/coretemp.c @@ -52,7 +52,7 @@ module_param_named(tjmax, force_tjmax, int, 0444); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ -#define NUM_REAL_CORES 32 /* Number of Real cores per cpu */ +#define NUM_REAL_CORES 128 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */ #define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) @@ -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/kernel/drivers/hwmon/dell-smm-hwmon.c b/kernel/drivers/hwmon/dell-smm-hwmon.c new file mode 100644 index 000000000..c43318d34 --- /dev/null +++ b/kernel/drivers/hwmon/dell-smm-hwmon.c @@ -0,0 +1,1054 @@ +/* + * 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, ¤t->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(®s) ? : 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(®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. + */ +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. + */ +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(®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. + */ +static int _i8k_get_temp(int sensor) +{ + struct smm_regs regs = { + .eax = I8K_SMM_GET_TEMP, + .ebx = sensor & 0xff, + }; + + return i8k_smm(®s) ? : 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(®s); + 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(®s); + 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(®s); + 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 8000 + * for unknown reasons. + */ + .ident = "Dell Studio XPS 8000", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Studio XPS 8000"), + }, + }, + { + /* + * 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/kernel/drivers/hwmon/f71882fg.c b/kernel/drivers/hwmon/f71882fg.c index 2e5c6f46e..cb28e4b4f 100644 --- a/kernel/drivers/hwmon/f71882fg.c +++ b/kernel/drivers/hwmon/f71882fg.c @@ -51,6 +51,7 @@ #define SIO_F71808A_ID 0x1001 /* Chipset ID */ #define SIO_F71858_ID 0x0507 /* Chipset ID */ #define SIO_F71862_ID 0x0601 /* Chipset ID */ +#define SIO_F71868_ID 0x1106 /* Chipset ID */ #define SIO_F71869_ID 0x0814 /* Chipset ID */ #define SIO_F71869A_ID 0x1007 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ @@ -58,7 +59,9 @@ #define SIO_F71889E_ID 0x0909 /* Chipset ID */ #define SIO_F71889A_ID 0x1005 /* Chipset ID */ #define SIO_F8000_ID 0x0581 /* Chipset ID */ +#define SIO_F81768D_ID 0x1210 /* Chipset ID */ #define SIO_F81865_ID 0x0704 /* Chipset ID */ +#define SIO_F81866_ID 0x1010 /* Chipset ID */ #define REGION_LENGTH 8 #define ADDR_REG_OFFSET 5 @@ -69,6 +72,10 @@ #define F71882FG_REG_IN(nr) (0x20 + (nr)) #define F71882FG_REG_IN1_HIGH 0x32 /* f7188x only */ +#define F81866_REG_IN_STATUS 0x16 /* F81866 only */ +#define F81866_REG_IN_BEEP 0x17 /* F81866 only */ +#define F81866_REG_IN1_HIGH 0x3a /* F81866 only */ + #define F71882FG_REG_FAN(nr) (0xA0 + (16 * (nr))) #define F71882FG_REG_FAN_TARGET(nr) (0xA2 + (16 * (nr))) #define F71882FG_REG_FAN_FULL_SPEED(nr) (0xA4 + (16 * (nr))) @@ -101,7 +108,7 @@ #define F71882FG_REG_START 0x01 -#define F71882FG_MAX_INS 9 +#define F71882FG_MAX_INS 11 #define FAN_MIN_DETECT 366 /* Lowest detectable fanspeed */ @@ -109,14 +116,16 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -enum chips { f71808e, f71808a, f71858fg, f71862fg, f71869, f71869a, f71882fg, - f71889fg, f71889ed, f71889a, f8000, f81865f }; +enum chips { f71808e, f71808a, f71858fg, f71862fg, f71868a, f71869, f71869a, + f71882fg, f71889fg, f71889ed, f71889a, f8000, f81768d, f81865f, + f81866a}; static const char *const f71882fg_names[] = { "f71808e", "f71808a", "f71858fg", "f71862fg", + "f71868a", "f71869", /* Both f71869f and f71869e, reg. compatible and same id */ "f71869a", "f71882fg", @@ -124,22 +133,27 @@ static const char *const f71882fg_names[] = { "f71889ed", "f71889a", "f8000", + "f81768d", "f81865f", + "f81866a", }; static const char f71882fg_has_in[][F71882FG_MAX_INS] = { - [f71808e] = { 1, 1, 1, 1, 1, 1, 0, 1, 1 }, - [f71808a] = { 1, 1, 1, 1, 0, 0, 0, 1, 1 }, - [f71858fg] = { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, - [f71862fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - [f71869] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - [f71869a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - [f71882fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - [f71889fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - [f71889ed] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - [f71889a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 }, - [f8000] = { 1, 1, 1, 0, 0, 0, 0, 0, 0 }, - [f81865f] = { 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + [f71808e] = { 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0 }, + [f71808a] = { 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0 }, + [f71858fg] = { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, + [f71862fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + [f71868a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }, + [f71869] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + [f71869a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + [f71882fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + [f71889fg] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + [f71889ed] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + [f71889a] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + [f8000] = { 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, + [f81768d] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, + [f81865f] = { 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, + [f81866a] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }, }; static const char f71882fg_has_in1_alarm[] = { @@ -147,6 +161,7 @@ static const char f71882fg_has_in1_alarm[] = { [f71808a] = 0, [f71858fg] = 0, [f71862fg] = 0, + [f71868a] = 0, [f71869] = 0, [f71869a] = 0, [f71882fg] = 1, @@ -154,7 +169,9 @@ static const char f71882fg_has_in1_alarm[] = { [f71889ed] = 1, [f71889a] = 1, [f8000] = 0, + [f81768d] = 1, [f81865f] = 1, + [f81866a] = 1, }; static const char f71882fg_fan_has_beep[] = { @@ -162,6 +179,7 @@ static const char f71882fg_fan_has_beep[] = { [f71808a] = 0, [f71858fg] = 0, [f71862fg] = 1, + [f71868a] = 1, [f71869] = 1, [f71869a] = 1, [f71882fg] = 1, @@ -169,7 +187,9 @@ static const char f71882fg_fan_has_beep[] = { [f71889ed] = 1, [f71889a] = 1, [f8000] = 0, + [f81768d] = 1, [f81865f] = 1, + [f81866a] = 1, }; static const char f71882fg_nr_fans[] = { @@ -177,6 +197,7 @@ static const char f71882fg_nr_fans[] = { [f71808a] = 2, /* +1 fan which is monitor + simple pwm only */ [f71858fg] = 3, [f71862fg] = 3, + [f71868a] = 3, [f71869] = 3, [f71869a] = 3, [f71882fg] = 4, @@ -184,7 +205,9 @@ static const char f71882fg_nr_fans[] = { [f71889ed] = 3, [f71889a] = 3, [f8000] = 3, /* +1 fan which is monitor only */ + [f81768d] = 3, [f81865f] = 2, + [f81866a] = 3, }; static const char f71882fg_temp_has_beep[] = { @@ -192,6 +215,7 @@ static const char f71882fg_temp_has_beep[] = { [f71808a] = 1, [f71858fg] = 0, [f71862fg] = 1, + [f71868a] = 1, [f71869] = 1, [f71869a] = 1, [f71882fg] = 1, @@ -199,7 +223,9 @@ static const char f71882fg_temp_has_beep[] = { [f71889ed] = 1, [f71889a] = 1, [f8000] = 0, + [f81768d] = 1, [f81865f] = 1, + [f81866a] = 1, }; static const char f71882fg_nr_temps[] = { @@ -207,6 +233,7 @@ static const char f71882fg_nr_temps[] = { [f71808a] = 2, [f71858fg] = 3, [f71862fg] = 3, + [f71868a] = 3, [f71869] = 3, [f71869a] = 3, [f71882fg] = 3, @@ -214,7 +241,9 @@ static const char f71882fg_nr_temps[] = { [f71889ed] = 3, [f71889a] = 3, [f8000] = 3, + [f81768d] = 3, [f81865f] = 2, + [f81866a] = 3, }; static struct platform_device *f71882fg_pdev; @@ -490,6 +519,23 @@ static struct sensor_device_attribute_2 fxxxx_temp_beep_attr[3][2] = { { store_temp_beep, 0, 7), } }; +static struct sensor_device_attribute_2 f81866_temp_beep_attr[3][2] = { { + SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 0), + SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 4), +}, { + SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 1), + SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 5), +}, { + SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 2), + SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 6), +} }; + /* * Temp attr for the f8000 * Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max) @@ -531,6 +577,8 @@ static struct sensor_device_attribute_2 fxxxx_in_attr[] = { SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6), SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7), SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8), + SENSOR_ATTR_2(in9_input, S_IRUGO, show_in, NULL, 0, 9), + SENSOR_ATTR_2(in10_input, S_IRUGO, show_in, NULL, 0, 10), }; /* For models with in1 alarm capability */ @@ -1170,10 +1218,21 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) if (time_after(jiffies, data->last_limits + 60 * HZ) || !data->valid) { if (f71882fg_has_in1_alarm[data->type]) { - data->in1_max = - f71882fg_read8(data, F71882FG_REG_IN1_HIGH); - data->in_beep = - f71882fg_read8(data, F71882FG_REG_IN_BEEP); + if (data->type == f81866a) { + data->in1_max = + f71882fg_read8(data, + F81866_REG_IN1_HIGH); + data->in_beep = + f71882fg_read8(data, + F81866_REG_IN_BEEP); + } else { + data->in1_max = + f71882fg_read8(data, + F71882FG_REG_IN1_HIGH); + data->in_beep = + f71882fg_read8(data, + F71882FG_REG_IN_BEEP); + } } /* Get High & boundary temps*/ @@ -1297,9 +1356,16 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->fan[3] = f71882fg_read16(data, F71882FG_REG_FAN(3)); - if (f71882fg_has_in1_alarm[data->type]) - data->in_status = f71882fg_read8(data, + if (f71882fg_has_in1_alarm[data->type]) { + if (data->type == f81866a) + data->in_status = f71882fg_read8(data, + F81866_REG_IN_STATUS); + + else + data->in_status = f71882fg_read8(data, F71882FG_REG_IN_STATUS); + } + for (nr = 0; nr < F71882FG_MAX_INS; nr++) if (f71882fg_has_in[data->type][nr]) data->in[nr] = f71882fg_read8(data, @@ -1440,7 +1506,10 @@ static ssize_t store_in_max(struct device *dev, struct device_attribute val = clamp_val(val, 0, 255); mutex_lock(&data->update_lock); - f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val); + if (data->type == f81866a) + f71882fg_write8(data, F81866_REG_IN1_HIGH, val); + else + f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val); data->in1_max = val; mutex_unlock(&data->update_lock); @@ -1471,13 +1540,20 @@ static ssize_t store_in_beep(struct device *dev, struct device_attribute return err; mutex_lock(&data->update_lock); - data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); + if (data->type == f81866a) + data->in_beep = f71882fg_read8(data, F81866_REG_IN_BEEP); + else + data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); + if (val) data->in_beep |= 1 << nr; else data->in_beep &= ~(1 << nr); - f71882fg_write8(data, F71882FG_REG_IN_BEEP, data->in_beep); + if (data->type == f81866a) + f71882fg_write8(data, F81866_REG_IN_BEEP, data->in_beep); + else + f71882fg_write8(data, F71882FG_REG_IN_BEEP, data->in_beep); mutex_unlock(&data->update_lock); return count; @@ -2270,6 +2346,7 @@ static int f71882fg_probe(struct platform_device *pdev) int nr_fans = f71882fg_nr_fans[sio_data->type]; int nr_temps = f71882fg_nr_temps[sio_data->type]; int err, i; + int size; u8 start_reg, reg; data = devm_kzalloc(&pdev->dev, sizeof(struct f71882fg_data), @@ -2280,7 +2357,8 @@ static int f71882fg_probe(struct platform_device *pdev) data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; data->type = sio_data->type; data->temp_start = - (data->type == f71858fg || data->type == f8000) ? 0 : 1; + (data->type == f71858fg || data->type == f8000 || + data->type == f81866a) ? 0 : 1; mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); @@ -2322,6 +2400,11 @@ static int f71882fg_probe(struct platform_device *pdev) f8000_temp_attr, ARRAY_SIZE(f8000_temp_attr)); break; + case f81866a: + err = f71882fg_create_sysfs_files(pdev, + f71858fg_temp_attr, + ARRAY_SIZE(f71858fg_temp_attr)); + break; default: err = f71882fg_create_sysfs_files(pdev, &fxxxx_temp_attr[0][0], @@ -2331,10 +2414,18 @@ static int f71882fg_probe(struct platform_device *pdev) goto exit_unregister_sysfs; if (f71882fg_temp_has_beep[data->type]) { - err = f71882fg_create_sysfs_files(pdev, - &fxxxx_temp_beep_attr[0][0], - ARRAY_SIZE(fxxxx_temp_beep_attr[0]) - * nr_temps); + if (data->type == f81866a) { + size = ARRAY_SIZE(f81866_temp_beep_attr[0]); + err = f71882fg_create_sysfs_files(pdev, + &f81866_temp_beep_attr[0][0], + size * nr_temps); + + } else { + size = ARRAY_SIZE(fxxxx_temp_beep_attr[0]); + err = f71882fg_create_sysfs_files(pdev, + &fxxxx_temp_beep_attr[0][0], + size * nr_temps); + } if (err) goto exit_unregister_sysfs; } @@ -2451,15 +2542,27 @@ static int f71882fg_remove(struct platform_device *pdev) f8000_temp_attr, ARRAY_SIZE(f8000_temp_attr)); break; + case f81866a: + f71882fg_remove_sysfs_files(pdev, + f71858fg_temp_attr, + ARRAY_SIZE(f71858fg_temp_attr)); + break; default: f71882fg_remove_sysfs_files(pdev, &fxxxx_temp_attr[0][0], ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps); } if (f71882fg_temp_has_beep[data->type]) { - f71882fg_remove_sysfs_files(pdev, - &fxxxx_temp_beep_attr[0][0], - ARRAY_SIZE(fxxxx_temp_beep_attr[0]) * nr_temps); + if (data->type == f81866a) + f71882fg_remove_sysfs_files(pdev, + &f81866_temp_beep_attr[0][0], + ARRAY_SIZE(f81866_temp_beep_attr[0]) + * nr_temps); + else + f71882fg_remove_sysfs_files(pdev, + &fxxxx_temp_beep_attr[0][0], + ARRAY_SIZE(fxxxx_temp_beep_attr[0]) + * nr_temps); } for (i = 0; i < F71882FG_MAX_INS; i++) { @@ -2551,6 +2654,9 @@ static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data) case SIO_F71862_ID: sio_data->type = f71862fg; break; + case SIO_F71868_ID: + sio_data->type = f71868a; + break; case SIO_F71869_ID: sio_data->type = f71869; break; @@ -2572,9 +2678,15 @@ static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data) case SIO_F8000_ID: sio_data->type = f8000; break; + case SIO_F81768D_ID: + sio_data->type = f81768d; + break; case SIO_F81865_ID: sio_data->type = f81865f; break; + case SIO_F81866_ID: + sio_data->type = f81866a; + break; default: pr_info("Unsupported Fintek device: %04x\n", (unsigned int)devid); diff --git a/kernel/drivers/hwmon/fam15h_power.c b/kernel/drivers/hwmon/fam15h_power.c index 3057dfc7e..5f7067d7b 100644 --- a/kernel/drivers/hwmon/fam15h_power.c +++ b/kernel/drivers/hwmon/fam15h_power.c @@ -26,6 +26,7 @@ #include <linux/pci.h> #include <linux/bitops.h> #include <asm/processor.h> +#include <asm/msr.h> MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor"); MODULE_AUTHOR("Andreas Herrmann <herrmann.der.user@googlemail.com>"); @@ -41,11 +42,21 @@ MODULE_LICENSE("GPL"); #define REG_TDP_RUNNING_AVERAGE 0xe0 #define REG_TDP_LIMIT3 0xe8 +#define FAM15H_MIN_NUM_ATTRS 2 +#define FAM15H_NUM_GROUPS 2 + +#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b + struct fam15h_power_data { struct pci_dev *pdev; unsigned int tdp_to_watts; unsigned int base_tdp; unsigned int processor_pwr_watts; + unsigned int cpu_pwr_sample_ratio; + const struct attribute_group *groups[FAM15H_NUM_GROUPS]; + struct attribute_group group; + /* maximum accumulated power of a compute unit */ + u64 max_cu_acc_power; }; static ssize_t show_power(struct device *dev, @@ -59,8 +70,19 @@ static ssize_t show_power(struct device *dev, pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), REG_TDP_RUNNING_AVERAGE, &val); - running_avg_capture = (val >> 4) & 0x3fffff; - running_avg_capture = sign_extend32(running_avg_capture, 21); + + /* + * On Carrizo and later platforms, TdpRunAvgAccCap bit field + * is extended to 4:31 from 4:25. + */ + if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model >= 0x60) { + running_avg_capture = val >> 4; + running_avg_capture = sign_extend32(running_avg_capture, 27); + } else { + running_avg_capture = (val >> 4) & 0x3fffff; + running_avg_capture = sign_extend32(running_avg_capture, 21); + } + running_avg_range = (val & 0xf) + 1; pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5), @@ -93,31 +115,38 @@ static ssize_t show_power_crit(struct device *dev, } static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL); -static umode_t fam15h_power_is_visible(struct kobject *kobj, - struct attribute *attr, - int index) +static int fam15h_power_init_attrs(struct pci_dev *pdev, + struct fam15h_power_data *data) { - /* power1_input is only reported for Fam15h, Models 00h-0fh */ - if (attr == &dev_attr_power1_input.attr && - (boot_cpu_data.x86 != 0x15 || boot_cpu_data.x86_model > 0xf)) - return 0; + int n = FAM15H_MIN_NUM_ATTRS; + struct attribute **fam15h_power_attrs; + struct cpuinfo_x86 *c = &boot_cpu_data; - return attr->mode; -} + if (c->x86 == 0x15 && + (c->x86_model <= 0xf || + (c->x86_model >= 0x60 && c->x86_model <= 0x6f))) + n += 1; -static struct attribute *fam15h_power_attrs[] = { - &dev_attr_power1_input.attr, - &dev_attr_power1_crit.attr, - NULL -}; + fam15h_power_attrs = devm_kcalloc(&pdev->dev, n, + sizeof(*fam15h_power_attrs), + GFP_KERNEL); -static const struct attribute_group fam15h_power_group = { - .attrs = fam15h_power_attrs, - .is_visible = fam15h_power_is_visible, -}; -__ATTRIBUTE_GROUPS(fam15h_power); + if (!fam15h_power_attrs) + return -ENOMEM; + + n = 0; + fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr; + if (c->x86 == 0x15 && + (c->x86_model <= 0xf || + (c->x86_model >= 0x60 && c->x86_model <= 0x6f))) + fam15h_power_attrs[n++] = &dev_attr_power1_input.attr; + + data->group.attrs = fam15h_power_attrs; + + return 0; +} -static bool fam15h_power_is_internal_node0(struct pci_dev *f4) +static bool should_load_on_this_node(struct pci_dev *f4) { u32 val; @@ -174,11 +203,12 @@ static int fam15h_power_resume(struct pci_dev *pdev) #define fam15h_power_resume NULL #endif -static void fam15h_power_init_data(struct pci_dev *f4, - struct fam15h_power_data *data) +static int fam15h_power_init_data(struct pci_dev *f4, + struct fam15h_power_data *data) { - u32 val; + u32 val, eax, ebx, ecx, edx; u64 tmp; + int ret; pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val); data->base_tdp = val >> 16; @@ -198,14 +228,41 @@ static void fam15h_power_init_data(struct pci_dev *f4, /* convert to microWatt */ data->processor_pwr_watts = (tmp * 15625) >> 10; + + ret = fam15h_power_init_attrs(f4, data); + if (ret) + return ret; + + cpuid(0x80000007, &eax, &ebx, &ecx, &edx); + + /* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */ + if (!(edx & BIT(12))) + return 0; + + /* + * determine the ratio of the compute unit power accumulator + * sample period to the PTSC counter period by executing CPUID + * Fn8000_0007:ECX + */ + data->cpu_pwr_sample_ratio = ecx; + + if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) { + pr_err("Failed to read max compute unit power accumulator MSR\n"); + return -ENODEV; + } + + data->max_cu_acc_power = tmp; + + return 0; } static int fam15h_power_probe(struct pci_dev *pdev, - const struct pci_device_id *id) + const struct pci_device_id *id) { struct fam15h_power_data *data; struct device *dev = &pdev->dev; struct device *hwmon_dev; + int ret; /* * though we ignore every other northbridge, we still have to @@ -214,25 +271,31 @@ static int fam15h_power_probe(struct pci_dev *pdev, */ tweak_runavg_range(pdev); - if (!fam15h_power_is_internal_node0(pdev)) + if (!should_load_on_this_node(pdev)) return -ENODEV; data = devm_kzalloc(dev, sizeof(struct fam15h_power_data), GFP_KERNEL); if (!data) return -ENOMEM; - fam15h_power_init_data(pdev, data); + ret = fam15h_power_init_data(pdev, data); + if (ret) + return ret; + data->pdev = pdev; + data->groups[0] = &data->group; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power", data, - fam15h_power_groups); + &data->groups[0]); return PTR_ERR_OR_ZERO(hwmon_dev); } static const struct pci_device_id fam15h_power_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) }, {} diff --git a/kernel/drivers/hwmon/g762.c b/kernel/drivers/hwmon/g762.c index 9b55e673b..b96a2a9e4 100644 --- a/kernel/drivers/hwmon/g762.c +++ b/kernel/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 @@ -1112,7 +1113,6 @@ static int g762_remove(struct i2c_client *client) static struct i2c_driver g762_driver = { .driver = { .name = DRVNAME, - .owner = THIS_MODULE, .of_match_table = of_match_ptr(g762_dt_match), }, .probe = g762_probe, diff --git a/kernel/drivers/hwmon/gpio-fan.c b/kernel/drivers/hwmon/gpio-fan.c index a3dae6d00..685568b12 100644 --- a/kernel/drivers/hwmon/gpio-fan.c +++ b/kernel/drivers/hwmon/gpio-fan.c @@ -406,16 +406,11 @@ static int gpio_fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) { struct gpio_fan_data *fan_data = cdev->devdata; - int r; if (!fan_data) return -EINVAL; - r = get_fan_speed_index(fan_data); - if (r < 0) - return r; - - *state = r; + *state = fan_data->speed_index; return 0; } @@ -539,6 +534,7 @@ static const struct of_device_id of_gpio_fan_match[] = { { .compatible = "gpio-fan", }, {}, }; +MODULE_DEVICE_TABLE(of, of_gpio_fan_match); #endif /* CONFIG_OF_GPIO */ static int gpio_fan_probe(struct platform_device *pdev) diff --git a/kernel/drivers/hwmon/ibmpowernv.c b/kernel/drivers/hwmon/ibmpowernv.c index 4255514b2..55b5a8ff1 100644 --- a/kernel/drivers/hwmon/ibmpowernv.c +++ b/kernel/drivers/hwmon/ibmpowernv.c @@ -474,11 +474,18 @@ static const struct platform_device_id opal_sensor_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids); +static const struct of_device_id opal_sensor_match[] = { + { .compatible = "ibm,opal-sensor" }, + { }, +}; +MODULE_DEVICE_TABLE(of, opal_sensor_match); + static struct platform_driver ibmpowernv_driver = { .probe = ibmpowernv_probe, .id_table = opal_sensor_driver_ids, .driver = { .name = DRVNAME, + .of_match_table = opal_sensor_match, }, }; diff --git a/kernel/drivers/hwmon/ina2xx.c b/kernel/drivers/hwmon/ina2xx.c index 4d2815079..b24f1d304 100644 --- a/kernel/drivers/hwmon/ina2xx.c +++ b/kernel/drivers/hwmon/ina2xx.c @@ -37,6 +37,7 @@ #include <linux/of.h> #include <linux/delay.h> #include <linux/util_macros.h> +#include <linux/regmap.h> #include <linux/platform_data/ina2xx.h> @@ -84,6 +85,11 @@ */ #define INA226_TOTAL_CONV_TIME_DEFAULT 2200 +static struct regmap_config ina2xx_regmap_config = { + .reg_bits = 8, + .val_bits = 16, +}; + enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { @@ -97,20 +103,13 @@ struct ina2xx_config { }; struct ina2xx_data { - struct i2c_client *client; const struct ina2xx_config *config; long rshunt; - u16 curr_config; - - struct mutex update_lock; - bool valid; - unsigned long last_updated; - int update_interval; /* in jiffies */ + struct mutex config_lock; + struct regmap *regmap; - int kind; const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS]; - u16 regs[INA2XX_MAX_REGISTERS]; }; static const struct ina2xx_config ina2xx_config[] = { @@ -153,7 +152,11 @@ static int ina226_reg_to_interval(u16 config) return DIV_ROUND_CLOSEST(avg * INA226_TOTAL_CONV_TIME_DEFAULT, 1000); } -static u16 ina226_interval_to_reg(int interval, u16 config) +/* + * Return the new, shifted AVG field value of CONFIG register, + * to use with regmap_update_bits + */ +static u16 ina226_interval_to_reg(int interval) { int avg, avg_bits; @@ -162,15 +165,7 @@ static u16 ina226_interval_to_reg(int interval, u16 config) avg_bits = find_closest(avg, ina226_avg_tab, ARRAY_SIZE(ina226_avg_tab)); - return (config & ~INA226_AVG_RD_MASK) | INA226_SHIFT_AVG(avg_bits); -} - -static void ina226_set_update_interval(struct ina2xx_data *data) -{ - int ms; - - ms = ina226_reg_to_interval(data->curr_config); - data->update_interval = msecs_to_jiffies(ms); + return INA226_SHIFT_AVG(avg_bits); } static int ina2xx_calibrate(struct ina2xx_data *data) @@ -178,8 +173,7 @@ static int ina2xx_calibrate(struct ina2xx_data *data) u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor, data->rshunt); - return i2c_smbus_write_word_swapped(data->client, - INA2XX_CALIBRATION, val); + return regmap_write(data->regmap, INA2XX_CALIBRATION, val); } /* @@ -187,12 +181,8 @@ static int ina2xx_calibrate(struct ina2xx_data *data) */ static int ina2xx_init(struct ina2xx_data *data) { - struct i2c_client *client = data->client; - int ret; - - /* device configuration */ - ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, - data->curr_config); + int ret = regmap_write(data->regmap, INA2XX_CONFIG, + data->config->config_default); if (ret < 0) return ret; @@ -203,47 +193,52 @@ static int ina2xx_init(struct ina2xx_data *data) return ina2xx_calibrate(data); } -static int ina2xx_do_update(struct device *dev) +static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) { struct ina2xx_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int i, rv, retry; + int ret, retry; - dev_dbg(&client->dev, "Starting ina2xx update\n"); + dev_dbg(dev, "Starting register %d read\n", reg); for (retry = 5; retry; retry--) { - /* Read all registers */ - for (i = 0; i < data->config->registers; i++) { - rv = i2c_smbus_read_word_swapped(client, i); - if (rv < 0) - return rv; - data->regs[i] = rv; - } + + ret = regmap_read(data->regmap, reg, regval); + if (ret < 0) + return ret; + + dev_dbg(dev, "read %d, val = 0x%04x\n", reg, *regval); /* * If the current value in the calibration register is 0, the * power and current registers will also remain at 0. In case * the chip has been reset let's check the calibration * register and reinitialize if needed. + * We do that extra read of the calibration register if there + * is some hint of a chip reset. */ - if (data->regs[INA2XX_CALIBRATION] == 0) { - dev_warn(dev, "chip not calibrated, reinitializing\n"); - - rv = ina2xx_init(data); - if (rv < 0) - return rv; - - /* - * Let's make sure the power and current registers - * have been updated before trying again. - */ - msleep(INA2XX_MAX_DELAY); - continue; + if (*regval == 0) { + unsigned int cal; + + ret = regmap_read(data->regmap, INA2XX_CALIBRATION, + &cal); + if (ret < 0) + return ret; + + if (cal == 0) { + dev_warn(dev, "chip not calibrated, reinitializing\n"); + + ret = ina2xx_init(data); + if (ret < 0) + return ret; + /* + * Let's make sure the power and current + * registers have been updated before trying + * again. + */ + msleep(INA2XX_MAX_DELAY); + continue; + } } - - data->last_updated = jiffies; - data->valid = 1; - return 0; } @@ -256,51 +251,31 @@ static int ina2xx_do_update(struct device *dev) return -ENODEV; } -static struct ina2xx_data *ina2xx_update_device(struct device *dev) -{ - struct ina2xx_data *data = dev_get_drvdata(dev); - struct ina2xx_data *ret = data; - unsigned long after; - int rv; - - mutex_lock(&data->update_lock); - - after = data->last_updated + data->update_interval; - if (time_after(jiffies, after) || !data->valid) { - rv = ina2xx_do_update(dev); - if (rv < 0) - ret = ERR_PTR(rv); - } - - mutex_unlock(&data->update_lock); - return ret; -} - -static int ina2xx_get_value(struct ina2xx_data *data, u8 reg) +static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, + unsigned int regval) { int val; switch (reg) { case INA2XX_SHUNT_VOLTAGE: /* signed register */ - val = DIV_ROUND_CLOSEST((s16)data->regs[reg], - data->config->shunt_div); + val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div); break; case INA2XX_BUS_VOLTAGE: - val = (data->regs[reg] >> data->config->bus_voltage_shift) + val = (regval >> data->config->bus_voltage_shift) * data->config->bus_voltage_lsb; val = DIV_ROUND_CLOSEST(val, 1000); break; case INA2XX_POWER: - val = data->regs[reg] * data->config->power_lsb; + val = regval * data->config->power_lsb; break; case INA2XX_CURRENT: /* signed register, LSB=1mA (selected), in mA */ - val = (s16)data->regs[reg]; + val = (s16)regval; break; case INA2XX_CALIBRATION: val = DIV_ROUND_CLOSEST(data->config->calibration_factor, - data->regs[reg]); + regval); break; default: /* programmer goofed */ @@ -316,25 +291,25 @@ static ssize_t ina2xx_show_value(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct ina2xx_data *data = ina2xx_update_device(dev); + struct ina2xx_data *data = dev_get_drvdata(dev); + unsigned int regval; + + int err = ina2xx_read_reg(dev, attr->index, ®val); - if (IS_ERR(data)) - return PTR_ERR(data); + if (err < 0) + return err; return snprintf(buf, PAGE_SIZE, "%d\n", - ina2xx_get_value(data, attr->index)); + ina2xx_get_value(data, attr->index, regval)); } static ssize_t ina2xx_set_shunt(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { - struct ina2xx_data *data = ina2xx_update_device(dev); unsigned long val; int status; - - if (IS_ERR(data)) - return PTR_ERR(data); + struct ina2xx_data *data = dev_get_drvdata(dev); status = kstrtoul(buf, 10, &val); if (status < 0) @@ -345,10 +320,10 @@ static ssize_t ina2xx_set_shunt(struct device *dev, val > data->config->calibration_factor) return -EINVAL; - mutex_lock(&data->update_lock); + mutex_lock(&data->config_lock); data->rshunt = val; status = ina2xx_calibrate(data); - mutex_unlock(&data->update_lock); + mutex_unlock(&data->config_lock); if (status < 0) return status; @@ -370,17 +345,9 @@ static ssize_t ina226_set_interval(struct device *dev, if (val > INT_MAX || val == 0) return -EINVAL; - mutex_lock(&data->update_lock); - data->curr_config = ina226_interval_to_reg(val, - data->regs[INA2XX_CONFIG]); - status = i2c_smbus_write_word_swapped(data->client, - INA2XX_CONFIG, - data->curr_config); - - ina226_set_update_interval(data); - /* Make sure the next access re-reads all registers. */ - data->valid = 0; - mutex_unlock(&data->update_lock); + status = regmap_update_bits(data->regmap, INA2XX_CONFIG, + INA226_AVG_RD_MASK, + ina226_interval_to_reg(val)); if (status < 0) return status; @@ -390,18 +357,15 @@ static ssize_t ina226_set_interval(struct device *dev, static ssize_t ina226_show_interval(struct device *dev, struct device_attribute *da, char *buf) { - struct ina2xx_data *data = ina2xx_update_device(dev); + struct ina2xx_data *data = dev_get_drvdata(dev); + int status; + unsigned int regval; - if (IS_ERR(data)) - return PTR_ERR(data); + status = regmap_read(data->regmap, INA2XX_CONFIG, ®val); + if (status) + return status; - /* - * We don't use data->update_interval here as we want to display - * the actual interval used by the chip and jiffies_to_msecs() - * doesn't seem to be accurate enough. - */ - return snprintf(buf, PAGE_SIZE, "%d\n", - ina226_reg_to_interval(data->regs[INA2XX_CONFIG])); + return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval)); } /* shunt voltage */ @@ -455,60 +419,51 @@ static const struct attribute_group ina226_group = { static int ina2xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct i2c_adapter *adapter = client->adapter; - struct ina2xx_platform_data *pdata; struct device *dev = &client->dev; struct ina2xx_data *data; struct device *hwmon_dev; u32 val; int ret, group = 0; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - if (dev_get_platdata(dev)) { - pdata = dev_get_platdata(dev); - data->rshunt = pdata->shunt_uohms; - } else if (!of_property_read_u32(dev->of_node, - "shunt-resistor", &val)) { - data->rshunt = val; - } else { - data->rshunt = INA2XX_RSHUNT_DEFAULT; - } - /* set the device type */ - data->kind = id->driver_data; - data->config = &ina2xx_config[data->kind]; - data->curr_config = data->config->config_default; - data->client = client; + data->config = &ina2xx_config[id->driver_data]; - /* - * Ina226 has a variable update_interval. For ina219 we - * use a constant value. - */ - if (data->kind == ina226) - ina226_set_update_interval(data); - else - data->update_interval = HZ / INA2XX_CONVERSION_RATE; + if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) { + struct ina2xx_platform_data *pdata = dev_get_platdata(dev); + + if (pdata) + val = pdata->shunt_uohms; + else + val = INA2XX_RSHUNT_DEFAULT; + } - if (data->rshunt <= 0 || - data->rshunt > data->config->calibration_factor) + if (val <= 0 || val > data->config->calibration_factor) return -ENODEV; + data->rshunt = val; + + ina2xx_regmap_config.max_register = data->config->registers; + + data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + ret = ina2xx_init(data); if (ret < 0) { dev_err(dev, "error configuring the device: %d\n", ret); return -ENODEV; } - mutex_init(&data->update_lock); + mutex_init(&data->config_lock); data->groups[group++] = &ina2xx_group; - if (data->kind == ina226) + if (id->driver_data == ina226) data->groups[group++] = &ina226_group; hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, diff --git a/kernel/drivers/hwmon/it87.c b/kernel/drivers/hwmon/it87.c index d0ee556e8..1896e26df 100644 --- a/kernel/drivers/hwmon/it87.c +++ b/kernel/drivers/hwmon/it87.c @@ -21,6 +21,7 @@ * IT8721F Super I/O chip w/LPC interface * IT8726F Super I/O chip w/LPC interface * IT8728F Super I/O chip w/LPC interface + * IT8732F Super I/O chip w/LPC interface * IT8758E Super I/O chip w/LPC interface * IT8771E Super I/O chip w/LPC interface * IT8772E Super I/O chip w/LPC interface @@ -69,8 +70,9 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8771, - it8772, it8781, it8782, it8783, it8786, it8790, it8603, it8620 }; +enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8732, + it8771, it8772, it8781, it8782, it8783, it8786, it8790, it8603, + it8620 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -148,6 +150,7 @@ static inline void superio_exit(void) #define IT8721F_DEVID 0x8721 #define IT8726F_DEVID 0x8726 #define IT8728F_DEVID 0x8728 +#define IT8732F_DEVID 0x8732 #define IT8771E_DEVID 0x8771 #define IT8772E_DEVID 0x8772 #define IT8781F_DEVID 0x8781 @@ -265,6 +268,7 @@ struct it87_devices { #define FEAT_VID (1 << 9) /* Set if chip supports VID */ #define FEAT_IN7_INTERNAL (1 << 10) /* Set if in7 is internal */ #define FEAT_SIX_FANS (1 << 11) /* Supports six fans */ +#define FEAT_10_9MV_ADC (1 << 12) static const struct it87_devices it87_devices[] = { [it87] = { @@ -315,6 +319,15 @@ static const struct it87_devices it87_devices[] = { | FEAT_IN7_INTERNAL, .peci_mask = 0x07, }, + [it8732] = { + .name = "it8732", + .suffix = "F", + .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI + | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL, + .peci_mask = 0x07, + .old_peci_mask = 0x02, /* Actually reports PCH */ + }, [it8771] = { .name = "it8771", .suffix = "E", @@ -391,6 +404,7 @@ static const struct it87_devices it87_devices[] = { #define has_16bit_fans(data) ((data)->features & FEAT_16BIT_FANS) #define has_12mv_adc(data) ((data)->features & FEAT_12MV_ADC) +#define has_10_9mv_adc(data) ((data)->features & FEAT_10_9MV_ADC) #define has_newer_autopwm(data) ((data)->features & FEAT_NEWER_AUTOPWM) #define has_old_autopwm(data) ((data)->features & FEAT_OLD_AUTOPWM) #define has_temp_offset(data) ((data)->features & FEAT_TEMP_OFFSET) @@ -475,7 +489,14 @@ struct it87_data { static int adc_lsb(const struct it87_data *data, int nr) { - int lsb = has_12mv_adc(data) ? 12 : 16; + int lsb; + + if (has_12mv_adc(data)) + lsb = 120; + else if (has_10_9mv_adc(data)) + lsb = 109; + else + lsb = 160; if (data->in_scaled & (1 << nr)) lsb <<= 1; return lsb; @@ -483,13 +504,13 @@ static int adc_lsb(const struct it87_data *data, int nr) static u8 in_to_reg(const struct it87_data *data, int nr, long val) { - val = DIV_ROUND_CLOSEST(val, adc_lsb(data, nr)); + val = DIV_ROUND_CLOSEST(val * 10, adc_lsb(data, nr)); return clamp_val(val, 0, 255); } static int in_from_reg(const struct it87_data *data, int nr, int val) { - return val * adc_lsb(data, nr); + return DIV_ROUND_CLOSEST(val * adc_lsb(data, nr), 10); } static inline u8 FAN_TO_REG(long rpm, int div) @@ -1515,9 +1536,14 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr, }; struct it87_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr(attr)->index; + const char *label; - return sprintf(buf, "%s\n", has_12mv_adc(data) ? labels_it8721[nr] - : labels[nr]); + if (has_12mv_adc(data) || has_10_9mv_adc(data)) + label = labels_it8721[nr]; + else + label = labels[nr]; + + return sprintf(buf, "%s\n", label); } static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0); static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1); @@ -1853,6 +1879,9 @@ static int __init it87_find(unsigned short *address, case IT8728F_DEVID: sio_data->type = it8728; break; + case IT8732F_DEVID: + sio_data->type = it8732; + break; case IT8771E_DEVID: sio_data->type = it8771; break; diff --git a/kernel/drivers/hwmon/k10temp.c b/kernel/drivers/hwmon/k10temp.c index 1e7bdcdcb..9cdfde651 100644 --- a/kernel/drivers/hwmon/k10temp.c +++ b/kernel/drivers/hwmon/k10temp.c @@ -60,7 +60,6 @@ static DEFINE_MUTEX(nb_smu_ind_mutex); * Control] */ #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4 -#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F3 0x1573 static void amd_nb_smu_index_read(struct pci_dev *pdev, unsigned int devfn, int offset, u32 *val) diff --git a/kernel/drivers/hwmon/lm70.c b/kernel/drivers/hwmon/lm70.c index 97204dce1..583f883a4 100644 --- a/kernel/drivers/hwmon/lm70.c +++ b/kernel/drivers/hwmon/lm70.c @@ -37,6 +37,7 @@ #include <linux/mod_devicetable.h> #include <linux/spi/spi.h> #include <linux/slab.h> +#include <linux/of_device.h> #define DRVNAME "lm70" @@ -130,11 +131,41 @@ ATTRIBUTE_GROUPS(lm70); /*----------------------------------------------------------------------*/ +#ifdef CONFIG_OF +static const struct of_device_id lm70_of_ids[] = { + { + .compatible = "ti,lm70", + .data = (void *) LM70_CHIP_LM70, + }, + { + .compatible = "ti,tmp121", + .data = (void *) LM70_CHIP_TMP121, + }, + { + .compatible = "ti,lm71", + .data = (void *) LM70_CHIP_LM71, + }, + { + .compatible = "ti,lm74", + .data = (void *) LM70_CHIP_LM74, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, lm70_of_ids); +#endif + static int lm70_probe(struct spi_device *spi) { - int chip = spi_get_device_id(spi)->driver_data; + const struct of_device_id *match; struct device *hwmon_dev; struct lm70 *p_lm70; + int chip; + + match = of_match_device(lm70_of_ids, &spi->dev); + if (match) + chip = (int)(uintptr_t)match->data; + else + chip = spi_get_device_id(spi)->driver_data; /* signaling is SPI_MODE_0 */ if (spi->mode & (SPI_CPOL | SPI_CPHA)) @@ -168,7 +199,7 @@ MODULE_DEVICE_TABLE(spi, lm70_ids); static struct spi_driver lm70_driver = { .driver = { .name = "lm70", - .owner = THIS_MODULE, + .of_match_table = of_match_ptr(lm70_of_ids), }, .id_table = lm70_ids, .probe = lm70_probe, diff --git a/kernel/drivers/hwmon/lm75.c b/kernel/drivers/hwmon/lm75.c index fe41d5ae7..0addc84ba 100644 --- a/kernel/drivers/hwmon/lm75.c +++ b/kernel/drivers/hwmon/lm75.c @@ -57,6 +57,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ tmp175, tmp275, tmp75, + tmp75c, }; /* Addresses scanned */ @@ -104,7 +105,7 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution) /* sysfs attributes for hwmon */ -static int lm75_read_temp(void *dev, long *temp) +static int lm75_read_temp(void *dev, int *temp) { struct lm75_data *data = lm75_update_device(dev); @@ -280,6 +281,11 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) data->resolution = 12; data->sample_time = HZ / 2; break; + case tmp75c: + clr_mask |= 1 << 5; /* not one-shot mode */ + data->resolution = 12; + data->sample_time = HZ / 4; + break; } /* configure as specified */ @@ -343,6 +349,7 @@ static const struct i2c_device_id lm75_ids[] = { { "tmp175", tmp175, }, { "tmp275", tmp275, }, { "tmp75", tmp75, }, + { "tmp75c", tmp75c, }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm75_ids); diff --git a/kernel/drivers/hwmon/max1111.c b/kernel/drivers/hwmon/max1111.c index f67d71ee8..36544c4f6 100644 --- a/kernel/drivers/hwmon/max1111.c +++ b/kernel/drivers/hwmon/max1111.c @@ -277,7 +277,6 @@ MODULE_DEVICE_TABLE(spi, max1111_ids); static struct spi_driver max1111_driver = { .driver = { .name = "max1111", - .owner = THIS_MODULE, }, .id_table = max1111_ids, .probe = max1111_probe, diff --git a/kernel/drivers/hwmon/max197.c b/kernel/drivers/hwmon/max197.c index cb0dcfda9..076285695 100644 --- a/kernel/drivers/hwmon/max197.c +++ b/kernel/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/kernel/drivers/hwmon/max31790.c b/kernel/drivers/hwmon/max31790.c new file mode 100644 index 000000000..69c0ac80a --- /dev/null +++ b/kernel/drivers/hwmon/max31790.c @@ -0,0 +1,603 @@ +/* + * max31790.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring. + * + * (C) 2015 by Il Han <corone.il.han@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 of the License, 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. + */ + +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/slab.h> + +/* MAX31790 registers */ +#define MAX31790_REG_GLOBAL_CONFIG 0x00 +#define MAX31790_REG_FAN_CONFIG(ch) (0x02 + (ch)) +#define MAX31790_REG_FAN_DYNAMICS(ch) (0x08 + (ch)) +#define MAX31790_REG_FAN_FAULT_STATUS2 0x10 +#define MAX31790_REG_FAN_FAULT_STATUS1 0x11 +#define MAX31790_REG_TACH_COUNT(ch) (0x18 + (ch) * 2) +#define MAX31790_REG_PWM_DUTY_CYCLE(ch) (0x30 + (ch) * 2) +#define MAX31790_REG_PWMOUT(ch) (0x40 + (ch) * 2) +#define MAX31790_REG_TARGET_COUNT(ch) (0x50 + (ch) * 2) + +/* Fan Config register bits */ +#define MAX31790_FAN_CFG_RPM_MODE 0x80 +#define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08 +#define MAX31790_FAN_CFG_TACH_INPUT 0x01 + +/* Fan Dynamics register bits */ +#define MAX31790_FAN_DYN_SR_SHIFT 5 +#define MAX31790_FAN_DYN_SR_MASK 0xE0 +#define SR_FROM_REG(reg) (((reg) & MAX31790_FAN_DYN_SR_MASK) \ + >> MAX31790_FAN_DYN_SR_SHIFT) + +#define FAN_RPM_MIN 120 +#define FAN_RPM_MAX 7864320 + +#define RPM_FROM_REG(reg, sr) (((reg) >> 4) ? \ + ((60 * (sr) * 8192) / ((reg) >> 4)) : \ + FAN_RPM_MAX) +#define RPM_TO_REG(rpm, sr) ((60 * (sr) * 8192) / ((rpm) * 2)) + +#define NR_CHANNEL 6 + +/* + * Client data (each client gets its own) + */ +struct max31790_data { + struct i2c_client *client; + struct mutex update_lock; + bool valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* register values */ + u8 fan_config[NR_CHANNEL]; + u8 fan_dynamics[NR_CHANNEL]; + u16 fault_status; + u16 tach[NR_CHANNEL * 2]; + u16 pwm[NR_CHANNEL]; + u16 target_count[NR_CHANNEL]; +}; + +static struct max31790_data *max31790_update_device(struct device *dev) +{ + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct max31790_data *ret = data; + int i; + int rv; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_FAULT_STATUS1); + if (rv < 0) + goto abort; + data->fault_status = rv & 0x3F; + + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_FAULT_STATUS2); + if (rv < 0) + goto abort; + data->fault_status |= (rv & 0x3F) << 6; + + for (i = 0; i < NR_CHANNEL; i++) { + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_TACH_COUNT(i)); + if (rv < 0) + goto abort; + data->tach[i] = rv; + + if (data->fan_config[i] + & MAX31790_FAN_CFG_TACH_INPUT) { + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_TACH_COUNT(NR_CHANNEL + + i)); + if (rv < 0) + goto abort; + data->tach[NR_CHANNEL + i] = rv; + } else { + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_PWMOUT(i)); + if (rv < 0) + goto abort; + data->pwm[i] = rv; + + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_TARGET_COUNT(i)); + if (rv < 0) + goto abort; + data->target_count[i] = rv; + } + } + + data->last_updated = jiffies; + data->valid = true; + } + goto done; + +abort: + data->valid = false; + ret = ERR_PTR(rv); + +done: + mutex_unlock(&data->update_lock); + + return ret; +} + +static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 }; + +static u8 get_tach_period(u8 fan_dynamics) +{ + return tach_period[SR_FROM_REG(fan_dynamics)]; +} + +static u8 bits_for_tach_period(int rpm) +{ + u8 bits; + + if (rpm < 500) + bits = 0x0; + else if (rpm < 1000) + bits = 0x1; + else if (rpm < 2000) + bits = 0x2; + else if (rpm < 4000) + bits = 0x3; + else if (rpm < 8000) + bits = 0x4; + else + bits = 0x5; + + return bits; +} + +static ssize_t get_fan(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int sr, rpm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + sr = get_tach_period(data->fan_dynamics[attr->index]); + rpm = RPM_FROM_REG(data->tach[attr->index], sr); + + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t get_fan_target(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int sr, rpm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + sr = get_tach_period(data->fan_dynamics[attr->index]); + rpm = RPM_FROM_REG(data->target_count[attr->index], sr); + + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t set_fan_target(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + u8 bits; + int sr; + int target_count; + unsigned long rpm; + int err; + + err = kstrtoul(buf, 10, &rpm); + if (err) + return err; + + mutex_lock(&data->update_lock); + + rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX); + bits = bits_for_tach_period(rpm); + data->fan_dynamics[attr->index] = + ((data->fan_dynamics[attr->index] + & ~MAX31790_FAN_DYN_SR_MASK) + | (bits << MAX31790_FAN_DYN_SR_SHIFT)); + err = i2c_smbus_write_byte_data(client, + MAX31790_REG_FAN_DYNAMICS(attr->index), + data->fan_dynamics[attr->index]); + + if (err < 0) { + mutex_unlock(&data->update_lock); + return err; + } + + sr = get_tach_period(data->fan_dynamics[attr->index]); + target_count = RPM_TO_REG(rpm, sr); + target_count = clamp_val(target_count, 0x1, 0x7FF); + + data->target_count[attr->index] = target_count << 5; + + err = i2c_smbus_write_word_swapped(client, + MAX31790_REG_TARGET_COUNT(attr->index), + data->target_count[attr->index]); + + mutex_unlock(&data->update_lock); + + if (err < 0) + return err; + + return count; +} + +static ssize_t get_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int pwm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + pwm = data->pwm[attr->index] >> 8; + + return sprintf(buf, "%d\n", pwm); +} + +static ssize_t set_pwm(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long pwm; + int err; + + err = kstrtoul(buf, 10, &pwm); + if (err) + return err; + + if (pwm > 255) + return -EINVAL; + + mutex_lock(&data->update_lock); + + data->pwm[attr->index] = pwm << 8; + err = i2c_smbus_write_word_swapped(client, + MAX31790_REG_PWMOUT(attr->index), + data->pwm[attr->index]); + + mutex_unlock(&data->update_lock); + + if (err < 0) + return err; + + return count; +} + +static ssize_t get_pwm_enable(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int mode; + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (data->fan_config[attr->index] & MAX31790_FAN_CFG_RPM_MODE) + mode = 2; + else if (data->fan_config[attr->index] & MAX31790_FAN_CFG_TACH_INPUT_EN) + mode = 1; + else + mode = 0; + + return sprintf(buf, "%d\n", mode); +} + +static ssize_t set_pwm_enable(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long mode; + int err; + + err = kstrtoul(buf, 10, &mode); + if (err) + return err; + + switch (mode) { + case 0: + data->fan_config[attr->index] = + data->fan_config[attr->index] + & ~(MAX31790_FAN_CFG_TACH_INPUT_EN + | MAX31790_FAN_CFG_RPM_MODE); + break; + case 1: + data->fan_config[attr->index] = + (data->fan_config[attr->index] + | MAX31790_FAN_CFG_TACH_INPUT_EN) + & ~MAX31790_FAN_CFG_RPM_MODE; + break; + case 2: + data->fan_config[attr->index] = + data->fan_config[attr->index] + | MAX31790_FAN_CFG_TACH_INPUT_EN + | MAX31790_FAN_CFG_RPM_MODE; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->update_lock); + + err = i2c_smbus_write_byte_data(client, + MAX31790_REG_FAN_CONFIG(attr->index), + data->fan_config[attr->index]); + + mutex_unlock(&data->update_lock); + + if (err < 0) + return err; + + return count; +} + +static ssize_t get_fan_fault(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int fault; + + if (IS_ERR(data)) + return PTR_ERR(data); + + fault = !!(data->fault_status & (1 << attr->index)); + + return sprintf(buf, "%d\n", fault); +} + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5); + +static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5); + +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, get_fan, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, get_fan, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, get_fan, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, get_fan, NULL, 9); +static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, get_fan, NULL, 10); +static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, get_fan, NULL, 11); + +static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, get_fan_fault, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, get_fan_fault, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, get_fan_fault, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, get_fan_fault, NULL, 9); +static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, get_fan_fault, NULL, 10); +static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, get_fan_fault, NULL, 11); + +static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 0); +static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 1); +static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 2); +static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 3); +static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 4); +static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 5); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0); +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2); +static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3); +static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4); +static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5); + +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 0); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 1); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 2); +static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 3); +static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 4); +static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 5); + +static struct attribute *max31790_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + + &sensor_dev_attr_fan1_fault.dev_attr.attr, + &sensor_dev_attr_fan2_fault.dev_attr.attr, + &sensor_dev_attr_fan3_fault.dev_attr.attr, + &sensor_dev_attr_fan4_fault.dev_attr.attr, + &sensor_dev_attr_fan5_fault.dev_attr.attr, + &sensor_dev_attr_fan6_fault.dev_attr.attr, + + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + &sensor_dev_attr_fan9_input.dev_attr.attr, + &sensor_dev_attr_fan10_input.dev_attr.attr, + &sensor_dev_attr_fan11_input.dev_attr.attr, + &sensor_dev_attr_fan12_input.dev_attr.attr, + + &sensor_dev_attr_fan7_fault.dev_attr.attr, + &sensor_dev_attr_fan8_fault.dev_attr.attr, + &sensor_dev_attr_fan9_fault.dev_attr.attr, + &sensor_dev_attr_fan10_fault.dev_attr.attr, + &sensor_dev_attr_fan11_fault.dev_attr.attr, + &sensor_dev_attr_fan12_fault.dev_attr.attr, + + &sensor_dev_attr_fan1_target.dev_attr.attr, + &sensor_dev_attr_fan2_target.dev_attr.attr, + &sensor_dev_attr_fan3_target.dev_attr.attr, + &sensor_dev_attr_fan4_target.dev_attr.attr, + &sensor_dev_attr_fan5_target.dev_attr.attr, + &sensor_dev_attr_fan6_target.dev_attr.attr, + + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + &sensor_dev_attr_pwm5.dev_attr.attr, + &sensor_dev_attr_pwm6.dev_attr.attr, + + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm4_enable.dev_attr.attr, + &sensor_dev_attr_pwm5_enable.dev_attr.attr, + &sensor_dev_attr_pwm6_enable.dev_attr.attr, + NULL +}; + +static umode_t max31790_attrs_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct max31790_data *data = dev_get_drvdata(dev); + struct device_attribute *devattr = + container_of(a, struct device_attribute, attr); + int index = to_sensor_dev_attr(devattr)->index % NR_CHANNEL; + u8 fan_config; + + fan_config = data->fan_config[index]; + + if (n >= NR_CHANNEL * 2 && n < NR_CHANNEL * 4 && + !(fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return 0; + if (n >= NR_CHANNEL * 4 && (fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return 0; + + return a->mode; +} + +static const struct attribute_group max31790_group = { + .attrs = max31790_attrs, + .is_visible = max31790_attrs_visible, +}; +__ATTRIBUTE_GROUPS(max31790); + +static int max31790_init_client(struct i2c_client *client, + struct max31790_data *data) +{ + int i, rv; + + for (i = 0; i < NR_CHANNEL; i++) { + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_CONFIG(i)); + if (rv < 0) + return rv; + data->fan_config[i] = rv; + + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_DYNAMICS(i)); + if (rv < 0) + return rv; + data->fan_dynamics[i] = rv; + } + + return 0; +} + +static int max31790_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct max31790_data *data; + struct device *hwmon_dev; + int err; + + if (!i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct max31790_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + /* + * Initialize the max31790 chip + */ + err = max31790_init_client(client, data); + if (err) + return err; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, data, max31790_groups); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id max31790_id[] = { + { "max31790", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max31790_id); + +static struct i2c_driver max31790_driver = { + .class = I2C_CLASS_HWMON, + .probe = max31790_probe, + .driver = { + .name = "max31790", + }, + .id_table = max31790_id, +}; + +module_i2c_driver(max31790_driver); + +MODULE_AUTHOR("Il Han <corone.il.han@gmail.com>"); +MODULE_DESCRIPTION("MAX31790 sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/hwmon/nct6775.c b/kernel/drivers/hwmon/nct6775.c index bd1c99dea..d7ebdf865 100644 --- a/kernel/drivers/hwmon/nct6775.c +++ b/kernel/drivers/hwmon/nct6775.c @@ -39,6 +39,7 @@ * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 * nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3 * nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3 + * nct6793d 15 6 6 2+6 0xd120 0xc1 0x5ca3 * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value). @@ -63,7 +64,7 @@ #define USE_ALTERNATE -enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792 }; +enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { @@ -73,6 +74,17 @@ static const char * const nct6775_device_names[] = { "nct6779", "nct6791", "nct6792", + "nct6793", +}; + +static const char * const nct6775_sio_names[] __initconst = { + "NCT6106D", + "NCT6775F", + "NCT6776D/F", + "NCT6779D", + "NCT6791D", + "NCT6792D", + "NCT6793D", }; static unsigned short force_id; @@ -104,6 +116,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_NCT6779_ID 0xc560 #define SIO_NCT6791_ID 0xc800 #define SIO_NCT6792_ID 0xc910 +#define SIO_NCT6793_ID 0xd120 #define SIO_ID_MASK 0xFFF0 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; @@ -354,6 +367,10 @@ static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1] /* NCT6776 specific data */ +/* STEP_UP_TIME and STEP_DOWN_TIME regs are swapped for all chips but NCT6775 */ +#define NCT6776_REG_FAN_STEP_UP_TIME NCT6775_REG_FAN_STEP_DOWN_TIME +#define NCT6776_REG_FAN_STEP_DOWN_TIME NCT6775_REG_FAN_STEP_UP_TIME + static const s8 NCT6776_ALARM_BITS[] = { 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ @@ -498,16 +515,24 @@ static const char *const nct6779_temp_label[] = { "PCH_DIM1_TEMP", "PCH_DIM2_TEMP", "PCH_DIM3_TEMP", - "BYTE_TEMP" + "BYTE_TEMP", + "", + "", + "", + "", + "Virtual_TEMP" }; -static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1] +#define NCT6779_NUM_LABELS (ARRAY_SIZE(nct6779_temp_label) - 5) +#define NCT6791_NUM_LABELS ARRAY_SIZE(nct6779_temp_label) + +static const u16 NCT6779_REG_TEMP_ALTERNATE[NCT6791_NUM_LABELS - 1] = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407, 0x408, 0 }; -static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] +static const u16 NCT6779_REG_TEMP_CRIT[NCT6791_NUM_LABELS - 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; /* NCT6791 specific data */ @@ -533,13 +558,83 @@ static const s8 NCT6791_ALARM_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, 9 }; /* intrusion0, intrusion1 */ -/* NCT6792 specific data */ +/* NCT6792/NCT6793 specific data */ static const u16 NCT6792_REG_TEMP_MON[] = { 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d }; static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5, 0xbf }; +static const char *const nct6792_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN0", + "AUXTIN1", + "AUXTIN2", + "AUXTIN3", + "", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "SMBUSMASTER 2", + "SMBUSMASTER 3", + "SMBUSMASTER 4", + "SMBUSMASTER 5", + "SMBUSMASTER 6", + "SMBUSMASTER 7", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP", + "BYTE_TEMP", + "PECI Agent 0 Calibration", + "PECI Agent 1 Calibration", + "", + "", + "Virtual_TEMP" +}; + +static const char *const nct6793_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN0", + "AUXTIN1", + "AUXTIN2", + "AUXTIN3", + "", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "", + "", + "", + "", + "", + "", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "Agent0 Dimm0 ", + "Agent0 Dimm1", + "Agent1 Dimm0", + "Agent1 Dimm1", + "BYTE_TEMP0", + "BYTE_TEMP1", + "PECI Agent 0 Calibration", + "PECI Agent 1 Calibration", + "", + "Virtual_TEMP" +}; + /* NCT6102D/NCT6106D specific data */ #define NCT6106_REG_VBAT 0x318 @@ -1056,6 +1151,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) case nct6779: case nct6791: case nct6792: + case nct6793: return reg == 0x150 || reg == 0x153 || reg == 0x155 || ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) || reg == 0x402 || @@ -1407,6 +1503,7 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6779: case nct6791: case nct6792: + case nct6793: reg = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i]); if (reg & data->CRITICAL_PWM_ENABLE_MASK) @@ -2822,6 +2919,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6779: case nct6791: case nct6792: + case nct6793: nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); reg = nct6775_read_value(data, @@ -3256,7 +3354,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm4pin = false; pwm5pin = false; pwm6pin = false; - } else { /* NCT6779D, NCT6791D, or NCT6792D */ + } else { /* NCT6779D, NCT6791D, NCT6792D, or NCT6793D */ regval = superio_inb(sioreg, 0x1c); fan3pin = !(regval & (1 << 5)); @@ -3269,7 +3367,8 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan4min = fan4pin; - if (data->kind == nct6791 || data->kind == nct6792) { + if (data->kind == nct6791 || data->kind == nct6792 || + data->kind == nct6793) { regval = superio_inb(sioreg, 0x2d); fan6pin = (regval & (1 << 1)); pwm6pin = (regval & (1 << 0)); @@ -3528,8 +3627,8 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; - data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; data->REG_PWM[0] = NCT6775_REG_PWM; data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; @@ -3584,7 +3683,7 @@ static int nct6775_probe(struct platform_device *pdev) data->speed_tolerance_limit = 63; data->temp_label = nct6779_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + data->temp_label_num = NCT6779_NUM_LABELS; data->REG_CONFIG = NCT6775_REG_CONFIG; data->REG_VBAT = NCT6775_REG_VBAT; @@ -3600,8 +3699,8 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; - data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; data->REG_PWM[0] = NCT6775_REG_PWM; data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; @@ -3643,6 +3742,7 @@ static int nct6775_probe(struct platform_device *pdev) break; case nct6791: case nct6792: + case nct6793: data->in_num = 15; data->pwm_num = 6; data->auto_pwm_num = 4; @@ -3660,8 +3760,19 @@ static int nct6775_probe(struct platform_device *pdev) data->tolerance_mask = 0x07; data->speed_tolerance_limit = 63; - data->temp_label = nct6779_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + switch (data->kind) { + default: + case nct6791: + data->temp_label = nct6779_temp_label; + break; + case nct6792: + data->temp_label = nct6792_temp_label; + break; + case nct6793: + data->temp_label = nct6793_temp_label; + break; + } + data->temp_label_num = NCT6791_NUM_LABELS; data->REG_CONFIG = NCT6775_REG_CONFIG; data->REG_VBAT = NCT6775_REG_VBAT; @@ -3677,8 +3788,8 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; - data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; data->REG_PWM[0] = NCT6775_REG_PWM; data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; @@ -3918,6 +4029,7 @@ static int nct6775_probe(struct platform_device *pdev) case nct6779: case nct6791: case nct6792: + case nct6793: break; } @@ -3950,6 +4062,7 @@ static int nct6775_probe(struct platform_device *pdev) break; case nct6791: case nct6792: + case nct6793: tmp |= 0x7e; break; } @@ -4047,7 +4160,8 @@ static int __maybe_unused nct6775_resume(struct device *dev) if (reg != data->sio_reg_enable) superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable); - if (data->kind == nct6791 || data->kind == nct6792) + if (data->kind == nct6791 || data->kind == nct6792 || + data->kind == nct6793) nct6791_enable_io_mapping(sioreg); superio_exit(sioreg); @@ -4106,15 +4220,6 @@ static struct platform_driver nct6775_driver = { .probe = nct6775_probe, }; -static const char * const nct6775_sio_names[] __initconst = { - "NCT6106D", - "NCT6775F", - "NCT6776D/F", - "NCT6779D", - "NCT6791D", - "NCT6792D", -}; - /* nct6775_find() looks for a '627 in the Super-I/O config space */ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) { @@ -4150,6 +4255,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6792_ID: sio_data->kind = nct6792; break; + case SIO_NCT6793_ID: + sio_data->kind = nct6793; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); @@ -4175,7 +4283,8 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); } - if (sio_data->kind == nct6791 || sio_data->kind == nct6792) + if (sio_data->kind == nct6791 || sio_data->kind == nct6792 || + sio_data->kind == nct6793) nct6791_enable_io_mapping(sioaddr); superio_exit(sioaddr); @@ -4285,7 +4394,7 @@ static void __exit sensors_nct6775_exit(void) } MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); -MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D/NCT6792D driver"); +MODULE_DESCRIPTION("Driver for NCT6775F and compatible chips"); MODULE_LICENSE("GPL"); module_init(sensors_nct6775_init); diff --git a/kernel/drivers/hwmon/nct7802.c b/kernel/drivers/hwmon/nct7802.c index fbfc02bb2..3ce33d244 100644 --- a/kernel/drivers/hwmon/nct7802.c +++ b/kernel/drivers/hwmon/nct7802.c @@ -49,10 +49,13 @@ static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = { #define REG_VOLTAGE_LOW 0x0f #define REG_FANCOUNT_LOW 0x13 #define REG_START 0x21 -#define REG_MODE 0x22 +#define REG_MODE 0x22 /* 7.2.32 Mode Selection Register */ #define REG_PECI_ENABLE 0x23 #define REG_FAN_ENABLE 0x24 #define REG_VMON_ENABLE 0x25 +#define REG_PWM(x) (0x60 + (x)) +#define REG_SMARTFAN_EN(x) (0x64 + (x) / 2) +#define SMARTFAN_EN_SHIFT(x) ((x) % 2 * 4) #define REG_VENDOR_ID 0xfd #define REG_CHIP_ID 0xfe #define REG_VERSION_ID 0xff @@ -66,6 +69,129 @@ struct nct7802_data { struct mutex access_lock; /* for multi-byte read and write operations */ }; +static ssize_t show_temp_type(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + unsigned int mode; + int ret; + + ret = regmap_read(data->regmap, REG_MODE, &mode); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", (mode >> (2 * sattr->index) & 3) + 2); +} + +static ssize_t store_temp_type(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + unsigned int type; + int err; + + err = kstrtouint(buf, 0, &type); + if (err < 0) + return err; + if (sattr->index == 2 && type != 4) /* RD3 */ + return -EINVAL; + if (type < 3 || type > 4) + return -EINVAL; + err = regmap_update_bits(data->regmap, REG_MODE, + 3 << 2 * sattr->index, (type - 2) << 2 * sattr->index); + return err ? : count; +} + +static ssize_t show_pwm_mode(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int regval; + int ret; + + if (sattr->index > 1) + return sprintf(buf, "1\n"); + + ret = regmap_read(data->regmap, 0x5E, ®val); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", !(regval & (1 << sattr->index))); +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int val; + int ret; + + if (!attr->index) + return sprintf(buf, "255\n"); + + ret = regmap_read(data->regmap, attr->index, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct nct7802_data *data = dev_get_drvdata(dev); + int err; + u8 val; + + err = kstrtou8(buf, 0, &val); + if (err < 0) + return err; + + err = regmap_write(data->regmap, attr->index, val); + return err ? : count; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + unsigned int reg, enabled; + int ret; + + ret = regmap_read(data->regmap, REG_SMARTFAN_EN(sattr->index), ®); + if (ret < 0) + return ret; + enabled = reg >> SMARTFAN_EN_SHIFT(sattr->index) & 1; + return sprintf(buf, "%u\n", enabled + 1); +} + +static ssize_t store_pwm_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + u8 val; + int ret; + + ret = kstrtou8(buf, 0, &val); + if (ret < 0) + return ret; + if (val < 1 || val > 2) + return -EINVAL; + ret = regmap_update_bits(data->regmap, REG_SMARTFAN_EN(sattr->index), + 1 << SMARTFAN_EN_SHIFT(sattr->index), + (val - 1) << SMARTFAN_EN_SHIFT(sattr->index)); + return ret ? : count; +} + static int nct7802_read_temp(struct nct7802_data *data, u8 reg_temp, u8 reg_temp_low, int *temp) { @@ -377,6 +503,8 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf, return err ? : count; } +static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO | S_IWUSR, + show_temp_type, store_temp_type, 0); static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0x01, REG_TEMP_LSB); static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, @@ -386,6 +514,8 @@ static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, 0x3a, 0); +static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, + show_temp_type, store_temp_type, 1); static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0x02, REG_TEMP_LSB); static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, @@ -395,6 +525,8 @@ static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, 0x3b, 0); +static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, + show_temp_type, store_temp_type, 2); static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0x03, REG_TEMP_LSB); static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, @@ -475,6 +607,7 @@ static SENSOR_DEVICE_ATTR_2(temp6_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, 0x5c, 5); static struct attribute *nct7802_temp_attrs[] = { + &sensor_dev_attr_temp1_type.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, @@ -485,7 +618,8 @@ static struct attribute *nct7802_temp_attrs[] = { &sensor_dev_attr_temp1_fault.dev_attr.attr, &sensor_dev_attr_temp1_beep.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, /* 9 */ + &sensor_dev_attr_temp2_type.dev_attr.attr, /* 10 */ + &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp2_crit.dev_attr.attr, @@ -495,7 +629,8 @@ static struct attribute *nct7802_temp_attrs[] = { &sensor_dev_attr_temp2_fault.dev_attr.attr, &sensor_dev_attr_temp2_beep.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, /* 18 */ + &sensor_dev_attr_temp3_type.dev_attr.attr, /* 20 */ + &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_temp3_crit.dev_attr.attr, @@ -505,7 +640,7 @@ static struct attribute *nct7802_temp_attrs[] = { &sensor_dev_attr_temp3_fault.dev_attr.attr, &sensor_dev_attr_temp3_beep.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, /* 27 */ + &sensor_dev_attr_temp4_input.dev_attr.attr, /* 30 */ &sensor_dev_attr_temp4_min.dev_attr.attr, &sensor_dev_attr_temp4_max.dev_attr.attr, &sensor_dev_attr_temp4_crit.dev_attr.attr, @@ -514,7 +649,7 @@ static struct attribute *nct7802_temp_attrs[] = { &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp4_beep.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, /* 35 */ + &sensor_dev_attr_temp5_input.dev_attr.attr, /* 38 */ &sensor_dev_attr_temp5_min.dev_attr.attr, &sensor_dev_attr_temp5_max.dev_attr.attr, &sensor_dev_attr_temp5_crit.dev_attr.attr, @@ -523,7 +658,7 @@ static struct attribute *nct7802_temp_attrs[] = { &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp5_beep.dev_attr.attr, - &sensor_dev_attr_temp6_input.dev_attr.attr, /* 43 */ + &sensor_dev_attr_temp6_input.dev_attr.attr, /* 46 */ &sensor_dev_attr_temp6_beep.dev_attr.attr, NULL @@ -541,25 +676,27 @@ static umode_t nct7802_temp_is_visible(struct kobject *kobj, if (err < 0) return 0; - if (index < 9 && + if (index < 10 && (reg & 03) != 0x01 && (reg & 0x03) != 0x02) /* RD1 */ return 0; - if (index >= 9 && index < 18 && + + if (index >= 10 && index < 20 && (reg & 0x0c) != 0x04 && (reg & 0x0c) != 0x08) /* RD2 */ return 0; - if (index >= 18 && index < 27 && (reg & 0x30) != 0x20) /* RD3 */ + if (index >= 20 && index < 30 && (reg & 0x30) != 0x20) /* RD3 */ return 0; - if (index >= 27 && index < 35) /* local */ + + if (index >= 30 && index < 38) /* local */ return attr->mode; err = regmap_read(data->regmap, REG_PECI_ENABLE, ®); if (err < 0) return 0; - if (index >= 35 && index < 43 && !(reg & 0x01)) /* PECI 0 */ + if (index >= 38 && index < 46 && !(reg & 0x01)) /* PECI 0 */ return 0; - if (index >= 0x43 && (!(reg & 0x02))) /* PECI 1 */ + if (index >= 0x46 && (!(reg & 0x02))) /* PECI 1 */ return 0; return attr->mode; @@ -687,6 +824,27 @@ static SENSOR_DEVICE_ATTR_2(fan3_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 2); static SENSOR_DEVICE_ATTR_2(fan3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, 0x5b, 2); +/* 7.2.89 Fan Control Output Type */ +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL, 0); +static SENSOR_DEVICE_ATTR(pwm2_mode, S_IRUGO, show_pwm_mode, NULL, 1); +static SENSOR_DEVICE_ATTR(pwm3_mode, S_IRUGO, show_pwm_mode, NULL, 2); + +/* 7.2.91... Fan Control Output Value */ +static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, store_pwm, + REG_PWM(0)); +static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm, store_pwm, + REG_PWM(1)); +static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, store_pwm, + REG_PWM(2)); + +/* 7.2.95... Temperature to Fan mapping Relationships Register */ +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + store_pwm_enable, 0); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + store_pwm_enable, 1); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + store_pwm_enable, 2); + static struct attribute *nct7802_fan_attrs[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, @@ -725,10 +883,142 @@ static struct attribute_group nct7802_fan_group = { .is_visible = nct7802_fan_is_visible, }; +static struct attribute *nct7802_pwm_attrs[] = { + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_mode.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_mode.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_mode.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + NULL +}; + +static struct attribute_group nct7802_pwm_group = { + .attrs = nct7802_pwm_attrs, +}; + +/* 7.2.115... 0x80-0x83, 0x84 Temperature (X-axis) transition */ +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x80, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x81, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x82, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x83, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point5_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x84, 0); + +/* 7.2.120... 0x85-0x88 PWM (Y-axis) transition */ +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x85); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x86); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x87); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x88); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm, NULL, 0); + +/* 7.2.124 Table 2 X-axis Transition Point 1 Register */ +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x90, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x91, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x92, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x93, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point5_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x94, 0); + +/* 7.2.129 Table 2 Y-axis Transition Point 1 Register */ +static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x95); +static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x96); +static SENSOR_DEVICE_ATTR(pwm2_auto_point3_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x97); +static SENSOR_DEVICE_ATTR(pwm2_auto_point4_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x98); +static SENSOR_DEVICE_ATTR(pwm2_auto_point5_pwm, S_IRUGO, show_pwm, NULL, 0); + +/* 7.2.133 Table 3 X-axis Transition Point 1 Register */ +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA0, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA1, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA2, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA3, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point5_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA4, 0); + +/* 7.2.138 Table 3 Y-axis Transition Point 1 Register */ +static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0xA5); +static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0xA6); +static SENSOR_DEVICE_ATTR(pwm3_auto_point3_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0xA7); +static SENSOR_DEVICE_ATTR(pwm3_auto_point4_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0xA8); +static SENSOR_DEVICE_ATTR(pwm3_auto_point5_pwm, S_IRUGO, show_pwm, NULL, 0); + +static struct attribute *nct7802_auto_point_attrs[] = { + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, + + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, + + &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point5_temp.dev_attr.attr, + + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point5_pwm.dev_attr.attr, + + &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point5_temp.dev_attr.attr, + + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point5_pwm.dev_attr.attr, + + NULL +}; + +static struct attribute_group nct7802_auto_point_group = { + .attrs = nct7802_auto_point_attrs, +}; + static const struct attribute_group *nct7802_groups[] = { &nct7802_temp_group, &nct7802_in_group, &nct7802_fan_group, + &nct7802_pwm_group, + &nct7802_auto_point_group, NULL }; @@ -776,7 +1066,8 @@ static int nct7802_detect(struct i2c_client *client, static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg) { - return reg != REG_BANK && reg <= 0x20; + return (reg != REG_BANK && reg <= 0x20) || + (reg >= REG_PWM(0) && reg <= REG_PWM(2)); } static const struct regmap_config nct7802_regmap_config = { diff --git a/kernel/drivers/hwmon/ntc_thermistor.c b/kernel/drivers/hwmon/ntc_thermistor.c index 688001158..feed30646 100644 --- a/kernel/drivers/hwmon/ntc_thermistor.c +++ b/kernel/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; @@ -439,7 +477,7 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data) return -EINVAL; } -static int ntc_read_temp(void *dev, long *temp) +static int ntc_read_temp(void *dev, int *temp) { struct ntc_data *data = dev_get_drvdata(dev); int ohm; @@ -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/kernel/drivers/hwmon/pmbus/Kconfig b/kernel/drivers/hwmon/pmbus/Kconfig index 9f7dbd189..df6ebb2b8 100644 --- a/kernel/drivers/hwmon/pmbus/Kconfig +++ b/kernel/drivers/hwmon/pmbus/Kconfig @@ -20,7 +20,8 @@ config SENSORS_PMBUS help If you say yes here you get hardware monitoring support for generic PMBus devices, including but not limited to ADP4000, BMR453, BMR454, - MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, UDT020, and TPS40400. + MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, TPS40400, TPS544B20, + TPS544B25, TPS544C20, TPS544C25, and UDT020. This driver can also be built as a module. If so, the module will be called pmbus. @@ -30,8 +31,8 @@ config SENSORS_ADM1275 default n help If you say yes here you get hardware monitoring support for Analog - Devices ADM1075, ADM1275, and ADM1276 Hot-Swap Controller and Digital - Power Monitors. + Devices ADM1075, ADM1275, ADM1276, ADM1293, and ADM1294 Hot-Swap + Controller and Digital Power Monitors. This driver can also be built as a module. If so, the module will be called adm1275. @@ -51,7 +52,8 @@ config SENSORS_LTC2978 default n help If you say yes here you get hardware monitoring support for Linear - Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676. + Technology LTC2974, LTC2975, LTC2977, LTC2978, LTC2980, LTC3880, + LTC3883, LTC3886, LTC3887, LTCM2987, LTM4675, and LTM4676. This driver can also be built as a module. If so, the module will be called ltc2978. @@ -73,6 +75,16 @@ config SENSORS_MAX16064 This driver can also be built as a module. If so, the module will be called max16064. +config SENSORS_MAX20751 + tristate "Maxim MAX20751" + default n + help + If you say yes here you get hardware monitoring support for Maxim + MAX20751. + + This driver can also be built as a module. If so, the module will + be called max20751. + config SENSORS_MAX34440 tristate "Maxim MAX34440 and compatibles" default n diff --git a/kernel/drivers/hwmon/pmbus/Makefile b/kernel/drivers/hwmon/pmbus/Makefile index 1454293e9..bce046d37 100644 --- a/kernel/drivers/hwmon/pmbus/Makefile +++ b/kernel/drivers/hwmon/pmbus/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o +obj-$(CONFIG_SENSORS_MAX20751) += max20751.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o diff --git a/kernel/drivers/hwmon/pmbus/adm1275.c b/kernel/drivers/hwmon/pmbus/adm1275.c index 60aad9570..188af4c89 100644 --- a/kernel/drivers/hwmon/pmbus/adm1275.c +++ b/kernel/drivers/hwmon/pmbus/adm1275.c @@ -21,46 +21,120 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/bitops.h> #include "pmbus.h" -enum chips { adm1075, adm1275, adm1276 }; +enum chips { adm1075, adm1275, adm1276, adm1293, adm1294 }; + +#define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0) +#define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5) +#define ADM1293_MFR_STATUS_VAUX_OV_WARN BIT(6) #define ADM1275_PEAK_IOUT 0xd0 #define ADM1275_PEAK_VIN 0xd1 #define ADM1275_PEAK_VOUT 0xd2 #define ADM1275_PMON_CONFIG 0xd4 -#define ADM1275_VIN_VOUT_SELECT (1 << 6) -#define ADM1275_VRANGE (1 << 5) -#define ADM1075_IRANGE_50 (1 << 4) -#define ADM1075_IRANGE_25 (1 << 3) -#define ADM1075_IRANGE_MASK ((1 << 3) | (1 << 4)) +#define ADM1275_VIN_VOUT_SELECT BIT(6) +#define ADM1275_VRANGE BIT(5) +#define ADM1075_IRANGE_50 BIT(4) +#define ADM1075_IRANGE_25 BIT(3) +#define ADM1075_IRANGE_MASK (BIT(3) | BIT(4)) + +#define ADM1293_IRANGE_25 0 +#define ADM1293_IRANGE_50 BIT(6) +#define ADM1293_IRANGE_100 BIT(7) +#define ADM1293_IRANGE_200 (BIT(6) | BIT(7)) +#define ADM1293_IRANGE_MASK (BIT(6) | BIT(7)) + +#define ADM1293_VIN_SEL_012 BIT(2) +#define ADM1293_VIN_SEL_074 BIT(3) +#define ADM1293_VIN_SEL_210 (BIT(2) | BIT(3)) +#define ADM1293_VIN_SEL_MASK (BIT(2) | BIT(3)) + +#define ADM1293_VAUX_EN BIT(1) #define ADM1275_IOUT_WARN2_LIMIT 0xd7 #define ADM1275_DEVICE_CONFIG 0xd8 -#define ADM1275_IOUT_WARN2_SELECT (1 << 4) +#define ADM1275_IOUT_WARN2_SELECT BIT(4) #define ADM1276_PEAK_PIN 0xda - -#define ADM1275_MFR_STATUS_IOUT_WARN2 (1 << 0) - #define ADM1075_READ_VAUX 0xdd #define ADM1075_VAUX_OV_WARN_LIMIT 0xde #define ADM1075_VAUX_UV_WARN_LIMIT 0xdf +#define ADM1293_IOUT_MIN 0xe3 +#define ADM1293_PIN_MIN 0xe4 #define ADM1075_VAUX_STATUS 0xf6 -#define ADM1075_VAUX_OV_WARN (1<<7) -#define ADM1075_VAUX_UV_WARN (1<<6) +#define ADM1075_VAUX_OV_WARN BIT(7) +#define ADM1075_VAUX_UV_WARN BIT(6) struct adm1275_data { int id; bool have_oc_fault; + bool have_uc_fault; + bool have_vout; + bool have_vaux_status; + bool have_mfr_vaux_status; + bool have_iout_min; + bool have_pin_min; + bool have_pin_max; struct pmbus_driver_info info; }; #define to_adm1275_data(x) container_of(x, struct adm1275_data, info) +struct coefficients { + s16 m; + s16 b; + s16 R; +}; + +static const struct coefficients adm1075_coefficients[] = { + [0] = { 27169, 0, -1 }, /* voltage */ + [1] = { 806, 20475, -1 }, /* current, irange25 */ + [2] = { 404, 20475, -1 }, /* current, irange50 */ + [3] = { 0, -1, 8549 }, /* power, irange25 */ + [4] = { 0, -1, 4279 }, /* power, irange50 */ +}; + +static const struct coefficients adm1275_coefficients[] = { + [0] = { 19199, 0, -2 }, /* voltage, vrange set */ + [1] = { 6720, 0, -1 }, /* voltage, vrange not set */ + [2] = { 807, 20475, -1 }, /* current */ +}; + +static const struct coefficients adm1276_coefficients[] = { + [0] = { 19199, 0, -2 }, /* voltage, vrange set */ + [1] = { 6720, 0, -1 }, /* voltage, vrange not set */ + [2] = { 807, 20475, -1 }, /* current */ + [3] = { 6043, 0, -2 }, /* power, vrange set */ + [4] = { 2115, 0, -1 }, /* power, vrange not set */ +}; + +static const struct coefficients adm1293_coefficients[] = { + [0] = { 3333, -1, 0 }, /* voltage, vrange 1.2V */ + [1] = { 5552, -5, -1 }, /* voltage, vrange 7.4V */ + [2] = { 19604, -50, -2 }, /* voltage, vrange 21V */ + [3] = { 8000, -100, -2 }, /* current, irange25 */ + [4] = { 4000, -100, -2 }, /* current, irange50 */ + [5] = { 20000, -1000, -3 }, /* current, irange100 */ + [6] = { 10000, -1000, -3 }, /* current, irange200 */ + [7] = { 10417, 0, -1 }, /* power, 1.2V, irange25 */ + [8] = { 5208, 0, -1 }, /* power, 1.2V, irange50 */ + [9] = { 26042, 0, -2 }, /* power, 1.2V, irange100 */ + [10] = { 13021, 0, -2 }, /* power, 1.2V, irange200 */ + [11] = { 17351, 0, -2 }, /* power, 7.4V, irange25 */ + [12] = { 8676, 0, -2 }, /* power, 7.4V, irange50 */ + [13] = { 4338, 0, -2 }, /* power, 7.4V, irange100 */ + [14] = { 21689, 0, -3 }, /* power, 7.4V, irange200 */ + [15] = { 6126, 0, -2 }, /* power, 21V, irange25 */ + [16] = { 30631, 0, -3 }, /* power, 21V, irange50 */ + [17] = { 15316, 0, -3 }, /* power, 21V, irange100 */ + [18] = { 7658, 0, -3 }, /* power, 21V, irange200 */ +}; + static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); @@ -72,42 +146,37 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_IOUT_UC_FAULT_LIMIT: - if (data->have_oc_fault) { - ret = -ENXIO; - break; - } + if (!data->have_uc_fault) + return -ENXIO; ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); break; case PMBUS_IOUT_OC_FAULT_LIMIT: - if (!data->have_oc_fault) { - ret = -ENXIO; - break; - } + if (!data->have_oc_fault) + return -ENXIO; ret = pmbus_read_word_data(client, 0, ADM1275_IOUT_WARN2_LIMIT); break; case PMBUS_VOUT_OV_WARN_LIMIT: - if (data->id != adm1075) { - ret = -ENODATA; - break; - } + if (data->have_vout) + return -ENODATA; ret = pmbus_read_word_data(client, 0, ADM1075_VAUX_OV_WARN_LIMIT); break; case PMBUS_VOUT_UV_WARN_LIMIT: - if (data->id != adm1075) { - ret = -ENODATA; - break; - } + if (data->have_vout) + return -ENODATA; ret = pmbus_read_word_data(client, 0, ADM1075_VAUX_UV_WARN_LIMIT); break; case PMBUS_READ_VOUT: - if (data->id != adm1075) { - ret = -ENODATA; - break; - } + if (data->have_vout) + return -ENODATA; ret = pmbus_read_word_data(client, 0, ADM1075_READ_VAUX); break; + case PMBUS_VIRT_READ_IOUT_MIN: + if (!data->have_iout_min) + return -ENXIO; + ret = pmbus_read_word_data(client, 0, ADM1293_IOUT_MIN); + break; case PMBUS_VIRT_READ_IOUT_MAX: ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_IOUT); break; @@ -117,11 +186,14 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) case PMBUS_VIRT_READ_VIN_MAX: ret = pmbus_read_word_data(client, 0, ADM1275_PEAK_VIN); break; + case PMBUS_VIRT_READ_PIN_MIN: + if (!data->have_pin_min) + return -ENXIO; + ret = pmbus_read_word_data(client, 0, ADM1293_PIN_MIN); + break; case PMBUS_VIRT_READ_PIN_MAX: - if (data->id == adm1275) { - ret = -ENXIO; - break; - } + if (!data->have_pin_max) + return -ENXIO; ret = pmbus_read_word_data(client, 0, ADM1276_PEAK_PIN); break; case PMBUS_VIRT_RESET_IOUT_HISTORY: @@ -129,8 +201,8 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) case PMBUS_VIRT_RESET_VIN_HISTORY: break; case PMBUS_VIRT_RESET_PIN_HISTORY: - if (data->id == adm1275) - ret = -ENXIO; + if (!data->have_pin_max) + return -ENXIO; break; default: ret = -ENODATA; @@ -142,6 +214,8 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, u16 word) { + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct adm1275_data *data = to_adm1275_data(info); int ret; if (page) @@ -155,6 +229,9 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, break; case PMBUS_VIRT_RESET_IOUT_HISTORY: ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_IOUT, 0); + if (!ret && data->have_iout_min) + ret = pmbus_write_word_data(client, 0, + ADM1293_IOUT_MIN, 0); break; case PMBUS_VIRT_RESET_VOUT_HISTORY: ret = pmbus_write_word_data(client, 0, ADM1275_PEAK_VOUT, 0); @@ -164,6 +241,9 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, break; case PMBUS_VIRT_RESET_PIN_HISTORY: ret = pmbus_write_word_data(client, 0, ADM1276_PEAK_PIN, 0); + if (!ret && data->have_pin_min) + ret = pmbus_write_word_data(client, 0, + ADM1293_PIN_MIN, 0); break; default: ret = -ENODATA; @@ -186,29 +266,40 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_IOUT); if (ret < 0) break; + if (!data->have_oc_fault && !data->have_uc_fault) + break; mfr_status = pmbus_read_byte_data(client, page, PMBUS_STATUS_MFR_SPECIFIC); - if (mfr_status < 0) { - ret = mfr_status; - break; - } + if (mfr_status < 0) + return mfr_status; if (mfr_status & ADM1275_MFR_STATUS_IOUT_WARN2) { ret |= data->have_oc_fault ? PB_IOUT_OC_FAULT : PB_IOUT_UC_FAULT; } break; case PMBUS_STATUS_VOUT: - if (data->id != adm1075) { - ret = -ENODATA; - break; - } + if (data->have_vout) + return -ENODATA; ret = 0; - mfr_status = pmbus_read_byte_data(client, 0, - ADM1075_VAUX_STATUS); - if (mfr_status & ADM1075_VAUX_OV_WARN) - ret |= PB_VOLTAGE_OV_WARNING; - if (mfr_status & ADM1075_VAUX_UV_WARN) - ret |= PB_VOLTAGE_UV_WARNING; + if (data->have_vaux_status) { + mfr_status = pmbus_read_byte_data(client, 0, + ADM1075_VAUX_STATUS); + if (mfr_status < 0) + return mfr_status; + if (mfr_status & ADM1075_VAUX_OV_WARN) + ret |= PB_VOLTAGE_OV_WARNING; + if (mfr_status & ADM1075_VAUX_UV_WARN) + ret |= PB_VOLTAGE_UV_WARNING; + } else if (data->have_mfr_vaux_status) { + mfr_status = pmbus_read_byte_data(client, page, + PMBUS_STATUS_MFR_SPECIFIC); + if (mfr_status < 0) + return mfr_status; + if (mfr_status & ADM1293_MFR_STATUS_VAUX_OV_WARN) + ret |= PB_VOLTAGE_OV_WARNING; + if (mfr_status & ADM1293_MFR_STATUS_VAUX_UV_WARN) + ret |= PB_VOLTAGE_UV_WARNING; + } break; default: ret = -ENODATA; @@ -221,6 +312,8 @@ static const struct i2c_device_id adm1275_id[] = { { "adm1075", adm1075 }, { "adm1275", adm1275 }, { "adm1276", adm1276 }, + { "adm1293", adm1293 }, + { "adm1294", adm1294 }, { } }; MODULE_DEVICE_TABLE(i2c, adm1275_id); @@ -234,6 +327,8 @@ static int adm1275_probe(struct i2c_client *client, struct pmbus_driver_info *info; struct adm1275_data *data; const struct i2c_device_id *mid; + const struct coefficients *coefficients; + int vindex = -1, voindex = -1, cindex = -1, pindex = -1; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA @@ -290,61 +385,38 @@ static int adm1275_probe(struct i2c_client *client, info->format[PSC_VOLTAGE_IN] = direct; info->format[PSC_VOLTAGE_OUT] = direct; info->format[PSC_CURRENT_OUT] = direct; - info->m[PSC_CURRENT_OUT] = 807; - info->b[PSC_CURRENT_OUT] = 20475; - info->R[PSC_CURRENT_OUT] = -1; + info->format[PSC_POWER] = direct; info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; info->read_word_data = adm1275_read_word_data; info->read_byte_data = adm1275_read_byte_data; info->write_word_data = adm1275_write_word_data; - if (data->id == adm1075) { - info->m[PSC_VOLTAGE_IN] = 27169; - info->b[PSC_VOLTAGE_IN] = 0; - info->R[PSC_VOLTAGE_IN] = -1; - info->m[PSC_VOLTAGE_OUT] = 27169; - info->b[PSC_VOLTAGE_OUT] = 0; - info->R[PSC_VOLTAGE_OUT] = -1; - } else if (config & ADM1275_VRANGE) { - info->m[PSC_VOLTAGE_IN] = 19199; - info->b[PSC_VOLTAGE_IN] = 0; - info->R[PSC_VOLTAGE_IN] = -2; - info->m[PSC_VOLTAGE_OUT] = 19199; - info->b[PSC_VOLTAGE_OUT] = 0; - info->R[PSC_VOLTAGE_OUT] = -2; - } else { - info->m[PSC_VOLTAGE_IN] = 6720; - info->b[PSC_VOLTAGE_IN] = 0; - info->R[PSC_VOLTAGE_IN] = -1; - info->m[PSC_VOLTAGE_OUT] = 6720; - info->b[PSC_VOLTAGE_OUT] = 0; - info->R[PSC_VOLTAGE_OUT] = -1; - } - - if (device_config & ADM1275_IOUT_WARN2_SELECT) - data->have_oc_fault = true; - switch (data->id) { case adm1075: - info->format[PSC_POWER] = direct; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -1; + if (device_config & ADM1275_IOUT_WARN2_SELECT) + data->have_oc_fault = true; + else + data->have_uc_fault = true; + data->have_pin_max = true; + data->have_vaux_status = true; + + coefficients = adm1075_coefficients; + vindex = 0; switch (config & ADM1075_IRANGE_MASK) { case ADM1075_IRANGE_25: - info->m[PSC_POWER] = 8549; - info->m[PSC_CURRENT_OUT] = 806; + cindex = 1; + pindex = 3; break; case ADM1075_IRANGE_50: - info->m[PSC_POWER] = 4279; - info->m[PSC_CURRENT_OUT] = 404; + cindex = 2; + pindex = 4; break; default: dev_err(&client->dev, "Invalid input current range"); - info->m[PSC_POWER] = 0; - info->m[PSC_CURRENT_OUT] = 0; break; } + info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT; if (config & ADM1275_VIN_VOUT_SELECT) @@ -352,6 +424,16 @@ static int adm1275_probe(struct i2c_client *client, PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; break; case adm1275: + if (device_config & ADM1275_IOUT_WARN2_SELECT) + data->have_oc_fault = true; + else + data->have_uc_fault = true; + data->have_vout = true; + + coefficients = adm1275_coefficients; + vindex = (config & ADM1275_VRANGE) ? 0 : 1; + cindex = 2; + if (config & ADM1275_VIN_VOUT_SELECT) info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; @@ -360,22 +442,100 @@ static int adm1275_probe(struct i2c_client *client, PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; break; case adm1276: - info->format[PSC_POWER] = direct; + if (device_config & ADM1275_IOUT_WARN2_SELECT) + data->have_oc_fault = true; + else + data->have_uc_fault = true; + data->have_vout = true; + data->have_pin_max = true; + + coefficients = adm1276_coefficients; + vindex = (config & ADM1275_VRANGE) ? 0 : 1; + cindex = 2; + pindex = (config & ADM1275_VRANGE) ? 3 : 4; + info->func[0] |= PMBUS_HAVE_VIN | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT; if (config & ADM1275_VIN_VOUT_SELECT) info->func[0] |= PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; - if (config & ADM1275_VRANGE) { - info->m[PSC_POWER] = 6043; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -2; - } else { - info->m[PSC_POWER] = 2115; - info->b[PSC_POWER] = 0; - info->R[PSC_POWER] = -1; + break; + case adm1293: + case adm1294: + data->have_iout_min = true; + data->have_pin_min = true; + data->have_pin_max = true; + data->have_mfr_vaux_status = true; + + coefficients = adm1293_coefficients; + + voindex = 0; + switch (config & ADM1293_VIN_SEL_MASK) { + case ADM1293_VIN_SEL_012: /* 1.2V */ + vindex = 0; + break; + case ADM1293_VIN_SEL_074: /* 7.4V */ + vindex = 1; + break; + case ADM1293_VIN_SEL_210: /* 21V */ + vindex = 2; + break; + default: /* disabled */ + break; } + + switch (config & ADM1293_IRANGE_MASK) { + case ADM1293_IRANGE_25: + cindex = 3; + break; + case ADM1293_IRANGE_50: + cindex = 4; + break; + case ADM1293_IRANGE_100: + cindex = 5; + break; + case ADM1293_IRANGE_200: + cindex = 6; + break; + } + + if (vindex >= 0) + pindex = 7 + vindex * 4 + (cindex - 3); + + if (config & ADM1293_VAUX_EN) + info->func[0] |= + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + + info->func[0] |= PMBUS_HAVE_PIN | + PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT; + break; + default: + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + if (voindex < 0) + voindex = vindex; + if (vindex >= 0) { + info->m[PSC_VOLTAGE_IN] = coefficients[vindex].m; + info->b[PSC_VOLTAGE_IN] = coefficients[vindex].b; + info->R[PSC_VOLTAGE_IN] = coefficients[vindex].R; + } + if (voindex >= 0) { + info->m[PSC_VOLTAGE_OUT] = coefficients[voindex].m; + info->b[PSC_VOLTAGE_OUT] = coefficients[voindex].b; + info->R[PSC_VOLTAGE_OUT] = coefficients[voindex].R; + } + if (cindex >= 0) { + info->m[PSC_CURRENT_OUT] = coefficients[cindex].m; + info->b[PSC_CURRENT_OUT] = coefficients[cindex].b; + info->R[PSC_CURRENT_OUT] = coefficients[cindex].R; + } + if (pindex >= 0) { + info->m[PSC_POWER] = coefficients[pindex].m; + info->b[PSC_POWER] = coefficients[pindex].b; + info->R[PSC_POWER] = coefficients[pindex].R; } return pmbus_do_probe(client, id, info); diff --git a/kernel/drivers/hwmon/pmbus/lm25066.c b/kernel/drivers/hwmon/pmbus/lm25066.c index a26b1d1d9..a3d912cd3 100644 --- a/kernel/drivers/hwmon/pmbus/lm25066.c +++ b/kernel/drivers/hwmon/pmbus/lm25066.c @@ -19,6 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -42,15 +43,15 @@ enum chips { lm25056, lm25063, lm25066, lm5064, lm5066 }; #define LM25066_READ_AVG_IIN 0xde #define LM25066_READ_AVG_PIN 0xdf -#define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */ +#define LM25066_DEV_SETUP_CL BIT(4) /* Current limit */ /* LM25056 only */ #define LM25056_VAUX_OV_WARN_LIMIT 0xe3 #define LM25056_VAUX_UV_WARN_LIMIT 0xe4 -#define LM25056_MFR_STS_VAUX_OV_WARN (1 << 1) -#define LM25056_MFR_STS_VAUX_UV_WARN (1 << 0) +#define LM25056_MFR_STS_VAUX_OV_WARN BIT(1) +#define LM25056_MFR_STS_VAUX_UV_WARN BIT(0) /* LM25063 only */ diff --git a/kernel/drivers/hwmon/pmbus/ltc2978.c b/kernel/drivers/hwmon/pmbus/ltc2978.c index 0835050ec..58b789c28 100644 --- a/kernel/drivers/hwmon/pmbus/ltc2978.c +++ b/kernel/drivers/hwmon/pmbus/ltc2978.c @@ -1,9 +1,9 @@ /* - * Hardware monitoring driver for LTC2974, LTC2977, LTC2978, LTC3880, - * LTC3883, and LTM4676 + * Hardware monitoring driver for LTC2978 and compatible chips. * * Copyright (c) 2011 Ericsson AB. - * Copyright (c) 2013, 2014 Guenter Roeck + * Copyright (c) 2013, 2014, 2015 Guenter Roeck + * Copyright (c) 2015 Linear Technology * * 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 @@ -16,6 +16,8 @@ * GNU General Public License for more details. */ +#include <linux/delay.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -25,49 +27,71 @@ #include <linux/regulator/driver.h> #include "pmbus.h" -enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 }; +enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, + ltc3883, ltc3886, ltc3887, ltm2987, ltm4675, ltm4676 }; /* Common for all chips */ #define LTC2978_MFR_VOUT_PEAK 0xdd #define LTC2978_MFR_VIN_PEAK 0xde #define LTC2978_MFR_TEMPERATURE_PEAK 0xdf -#define LTC2978_MFR_SPECIAL_ID 0xe7 +#define LTC2978_MFR_SPECIAL_ID 0xe7 /* Undocumented on LTC3882 */ +#define LTC2978_MFR_COMMON 0xef -/* LTC2974, LCT2977, and LTC2978 */ +/* LTC2974, LTC2975, LCT2977, LTC2980, LTC2978, and LTM2987 */ #define LTC2978_MFR_VOUT_MIN 0xfb #define LTC2978_MFR_VIN_MIN 0xfc #define LTC2978_MFR_TEMPERATURE_MIN 0xfd -/* LTC2974 only */ +/* LTC2974, LTC2975 */ #define LTC2974_MFR_IOUT_PEAK 0xd7 #define LTC2974_MFR_IOUT_MIN 0xd8 -/* LTC3880, LTC3883, and LTM4676 */ +/* LTC3880, LTC3882, LTC3883, LTC3887, LTM4675, and LTM4676 */ #define LTC3880_MFR_IOUT_PEAK 0xd7 #define LTC3880_MFR_CLEAR_PEAKS 0xe3 #define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 -/* LTC3883 only */ +/* LTC3883 and LTC3886 only */ #define LTC3883_MFR_IIN_PEAK 0xe1 -#define LTC2974_ID_REV1 0x0212 -#define LTC2974_ID_REV2 0x0213 +/* LTC2975 only */ +#define LTC2975_MFR_IIN_PEAK 0xc4 +#define LTC2975_MFR_IIN_MIN 0xc5 +#define LTC2975_MFR_PIN_PEAK 0xc6 +#define LTC2975_MFR_PIN_MIN 0xc7 + +#define LTC2978_ID_MASK 0xfff0 + +#define LTC2974_ID 0x0210 +#define LTC2975_ID 0x0220 #define LTC2977_ID 0x0130 -#define LTC2978_ID_REV1 0x0121 -#define LTC2978_ID_REV2 0x0122 -#define LTC2978A_ID 0x0124 -#define LTC3880_ID 0x4000 -#define LTC3880_ID_MASK 0xff00 +#define LTC2978_ID_REV1 0x0110 /* Early revision */ +#define LTC2978_ID_REV2 0x0120 +#define LTC2980_ID_A 0x8030 /* A/B for two die IDs */ +#define LTC2980_ID_B 0x8040 +#define LTC3880_ID 0x4020 +#define LTC3882_ID 0x4200 +#define LTC3882_ID_D1 0x4240 /* Dash 1 */ #define LTC3883_ID 0x4300 -#define LTC3883_ID_MASK 0xff00 -#define LTM4676_ID 0x4480 /* datasheet claims 0x440X */ -#define LTM4676_ID_MASK 0xfff0 +#define LTC3886_ID 0x4600 +#define LTC3887_ID 0x4700 +#define LTM2987_ID_A 0x8010 /* A/B for two die IDs */ +#define LTM2987_ID_B 0x8020 +#define LTM4675_ID 0x47a0 +#define LTM4676_ID_REV1 0x4400 +#define LTM4676_ID_REV2 0x4480 +#define LTM4676A_ID 0x47e0 #define LTC2974_NUM_PAGES 4 #define LTC2978_NUM_PAGES 8 #define LTC3880_NUM_PAGES 2 #define LTC3883_NUM_PAGES 1 +#define LTC_POLL_TIMEOUT 100 /* in milli-seconds */ + +#define LTC_NOT_BUSY BIT(5) +#define LTC_NOT_PENDING BIT(4) + /* * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which * happens pretty much each time chip data is updated. Raw peak data therefore @@ -82,13 +106,91 @@ struct ltc2978_data { u16 temp_min[LTC2974_NUM_PAGES], temp_max[LTC2974_NUM_PAGES]; u16 vout_min[LTC2978_NUM_PAGES], vout_max[LTC2978_NUM_PAGES]; u16 iout_min[LTC2974_NUM_PAGES], iout_max[LTC2974_NUM_PAGES]; - u16 iin_max; + u16 iin_min, iin_max; + u16 pin_min, pin_max; u16 temp2_max; struct pmbus_driver_info info; + u32 features; }; - #define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info) +#define FEAT_CLEAR_PEAKS BIT(0) +#define FEAT_NEEDS_POLLING BIT(1) + +#define has_clear_peaks(d) ((d)->features & FEAT_CLEAR_PEAKS) +#define needs_polling(d) ((d)->features & FEAT_NEEDS_POLLING) + +static int ltc_wait_ready(struct i2c_client *client) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(LTC_POLL_TIMEOUT); + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int status; + u8 mask; + + if (!needs_polling(data)) + return 0; + + /* + * LTC3883 does not support LTC_NOT_PENDING, even though + * the datasheet claims that it does. + */ + mask = LTC_NOT_BUSY; + if (data->id != ltc3883) + mask |= LTC_NOT_PENDING; + + do { + status = pmbus_read_byte_data(client, 0, LTC2978_MFR_COMMON); + if (status == -EBADMSG || status == -ENXIO) { + /* PEC error or NACK: chip may be busy, try again */ + usleep_range(50, 100); + continue; + } + if (status < 0) + return status; + + if ((status & mask) == mask) + return 0; + + usleep_range(50, 100); + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static int ltc_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + ret = ltc_wait_ready(client); + if (ret < 0) + return ret; + + return pmbus_read_word_data(client, page, reg); +} + +static int ltc_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + ret = ltc_wait_ready(client); + if (ret < 0) + return ret; + + return pmbus_read_byte_data(client, page, reg); +} + +static int ltc_write_byte(struct i2c_client *client, int page, u8 byte) +{ + int ret; + + ret = ltc_wait_ready(client); + if (ret < 0) + return ret; + + return pmbus_write_byte(client, page, byte); +} + static inline int lin11_to_val(int data) { s16 e = ((s16)data) >> 11; @@ -102,6 +204,34 @@ static inline int lin11_to_val(int data) return (e < 0 ? m >> -e : m << e); } +static int ltc_get_max(struct ltc2978_data *data, struct i2c_client *client, + int page, int reg, u16 *pmax) +{ + int ret; + + ret = ltc_read_word_data(client, page, reg); + if (ret >= 0) { + if (lin11_to_val(ret) > lin11_to_val(*pmax)) + *pmax = ret; + ret = *pmax; + } + return ret; +} + +static int ltc_get_min(struct ltc2978_data *data, struct i2c_client *client, + int page, int reg, u16 *pmin) +{ + int ret; + + ret = ltc_read_word_data(client, page, reg); + if (ret >= 0) { + if (lin11_to_val(ret) < lin11_to_val(*pmin)) + *pmin = ret; + ret = *pmin; + } + return ret; +} + static int ltc2978_read_word_data_common(struct i2c_client *client, int page, int reg) { @@ -111,15 +241,11 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page, switch (reg) { case PMBUS_VIRT_READ_VIN_MAX: - ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_PEAK); - if (ret >= 0) { - if (lin11_to_val(ret) > lin11_to_val(data->vin_max)) - data->vin_max = ret; - ret = data->vin_max; - } + ret = ltc_get_max(data, client, page, LTC2978_MFR_VIN_PEAK, + &data->vin_max); break; case PMBUS_VIRT_READ_VOUT_MAX: - ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK); + ret = ltc_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK); if (ret >= 0) { /* * VOUT is 16 bit unsigned with fixed exponent, @@ -131,14 +257,9 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page, } break; case PMBUS_VIRT_READ_TEMP_MAX: - ret = pmbus_read_word_data(client, page, - LTC2978_MFR_TEMPERATURE_PEAK); - if (ret >= 0) { - if (lin11_to_val(ret) - > lin11_to_val(data->temp_max[page])) - data->temp_max[page] = ret; - ret = data->temp_max[page]; - } + ret = ltc_get_max(data, client, page, + LTC2978_MFR_TEMPERATURE_PEAK, + &data->temp_max[page]); break; case PMBUS_VIRT_RESET_VOUT_HISTORY: case PMBUS_VIRT_RESET_VIN_HISTORY: @@ -146,6 +267,9 @@ static int ltc2978_read_word_data_common(struct i2c_client *client, int page, ret = 0; break; default: + ret = ltc_wait_ready(client); + if (ret < 0) + return ret; ret = -ENODATA; break; } @@ -160,15 +284,11 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VIRT_READ_VIN_MIN: - ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_MIN); - if (ret >= 0) { - if (lin11_to_val(ret) < lin11_to_val(data->vin_min)) - data->vin_min = ret; - ret = data->vin_min; - } + ret = ltc_get_min(data, client, page, LTC2978_MFR_VIN_MIN, + &data->vin_min); break; case PMBUS_VIRT_READ_VOUT_MIN: - ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN); + ret = ltc_read_word_data(client, page, LTC2978_MFR_VOUT_MIN); if (ret >= 0) { /* * VOUT_MIN is known to not be supported on some lots @@ -184,14 +304,9 @@ static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) } break; case PMBUS_VIRT_READ_TEMP_MIN: - ret = pmbus_read_word_data(client, page, - LTC2978_MFR_TEMPERATURE_MIN); - if (ret >= 0) { - if (lin11_to_val(ret) - < lin11_to_val(data->temp_min[page])) - data->temp_min[page] = ret; - ret = data->temp_min[page]; - } + ret = ltc_get_min(data, client, page, + LTC2978_MFR_TEMPERATURE_MIN, + &data->temp_min[page]); break; case PMBUS_VIRT_READ_IOUT_MAX: case PMBUS_VIRT_RESET_IOUT_HISTORY: @@ -214,22 +329,12 @@ static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_PEAK); - if (ret >= 0) { - if (lin11_to_val(ret) - > lin11_to_val(data->iout_max[page])) - data->iout_max[page] = ret; - ret = data->iout_max[page]; - } + ret = ltc_get_max(data, client, page, LTC2974_MFR_IOUT_PEAK, + &data->iout_max[page]); break; case PMBUS_VIRT_READ_IOUT_MIN: - ret = pmbus_read_word_data(client, page, LTC2974_MFR_IOUT_MIN); - if (ret >= 0) { - if (lin11_to_val(ret) - < lin11_to_val(data->iout_min[page])) - data->iout_min[page] = ret; - ret = data->iout_min[page]; - } + ret = ltc_get_min(data, client, page, LTC2974_MFR_IOUT_MIN, + &data->iout_min[page]); break; case PMBUS_VIRT_RESET_IOUT_HISTORY: ret = 0; @@ -241,6 +346,40 @@ static int ltc2974_read_word_data(struct i2c_client *client, int page, int reg) return ret; } +static int ltc2975_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct ltc2978_data *data = to_ltc2978_data(info); + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_IIN_MAX: + ret = ltc_get_max(data, client, page, LTC2975_MFR_IIN_PEAK, + &data->iin_max); + break; + case PMBUS_VIRT_READ_IIN_MIN: + ret = ltc_get_min(data, client, page, LTC2975_MFR_IIN_MIN, + &data->iin_min); + break; + case PMBUS_VIRT_READ_PIN_MAX: + ret = ltc_get_max(data, client, page, LTC2975_MFR_PIN_PEAK, + &data->pin_max); + break; + case PMBUS_VIRT_READ_PIN_MIN: + ret = ltc_get_min(data, client, page, LTC2975_MFR_PIN_MIN, + &data->pin_min); + break; + case PMBUS_VIRT_RESET_IIN_HISTORY: + case PMBUS_VIRT_RESET_PIN_HISTORY: + ret = 0; + break; + default: + ret = ltc2978_read_word_data(client, page, reg); + break; + } + return ret; +} + static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); @@ -249,22 +388,13 @@ static int ltc3880_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VIRT_READ_IOUT_MAX: - ret = pmbus_read_word_data(client, page, LTC3880_MFR_IOUT_PEAK); - if (ret >= 0) { - if (lin11_to_val(ret) - > lin11_to_val(data->iout_max[page])) - data->iout_max[page] = ret; - ret = data->iout_max[page]; - } + ret = ltc_get_max(data, client, page, LTC3880_MFR_IOUT_PEAK, + &data->iout_max[page]); break; case PMBUS_VIRT_READ_TEMP2_MAX: - ret = pmbus_read_word_data(client, page, - LTC3880_MFR_TEMPERATURE2_PEAK); - if (ret >= 0) { - if (lin11_to_val(ret) > lin11_to_val(data->temp2_max)) - data->temp2_max = ret; - ret = data->temp2_max; - } + ret = ltc_get_max(data, client, page, + LTC3880_MFR_TEMPERATURE2_PEAK, + &data->temp2_max); break; case PMBUS_VIRT_READ_VIN_MIN: case PMBUS_VIRT_READ_VOUT_MIN: @@ -290,13 +420,8 @@ static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg) switch (reg) { case PMBUS_VIRT_READ_IIN_MAX: - ret = pmbus_read_word_data(client, page, LTC3883_MFR_IIN_PEAK); - if (ret >= 0) { - if (lin11_to_val(ret) - > lin11_to_val(data->iin_max)) - data->iin_max = ret; - ret = data->iin_max; - } + ret = ltc_get_max(data, client, page, LTC3883_MFR_IIN_PEAK, + &data->iin_max); break; case PMBUS_VIRT_RESET_IIN_HISTORY: ret = 0; @@ -308,15 +433,15 @@ static int ltc3883_read_word_data(struct i2c_client *client, int page, int reg) return ret; } -static int ltc2978_clear_peaks(struct i2c_client *client, int page, - enum chips id) +static int ltc2978_clear_peaks(struct ltc2978_data *data, + struct i2c_client *client, int page) { int ret; - if (id == ltc3880 || id == ltc3883) - ret = pmbus_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS); + if (has_clear_peaks(data)) + ret = ltc_write_byte(client, 0, LTC3880_MFR_CLEAR_PEAKS); else - ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); + ret = ltc_write_byte(client, page, PMBUS_CLEAR_FAULTS); return ret; } @@ -331,33 +456,42 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, switch (reg) { case PMBUS_VIRT_RESET_IIN_HISTORY: data->iin_max = 0x7c00; - ret = ltc2978_clear_peaks(client, page, data->id); + data->iin_min = 0x7bff; + ret = ltc2978_clear_peaks(data, client, 0); + break; + case PMBUS_VIRT_RESET_PIN_HISTORY: + data->pin_max = 0x7c00; + data->pin_min = 0x7bff; + ret = ltc2978_clear_peaks(data, client, 0); break; case PMBUS_VIRT_RESET_IOUT_HISTORY: data->iout_max[page] = 0x7c00; data->iout_min[page] = 0xfbff; - ret = ltc2978_clear_peaks(client, page, data->id); + ret = ltc2978_clear_peaks(data, client, page); break; case PMBUS_VIRT_RESET_TEMP2_HISTORY: data->temp2_max = 0x7c00; - ret = ltc2978_clear_peaks(client, page, data->id); + ret = ltc2978_clear_peaks(data, client, page); break; case PMBUS_VIRT_RESET_VOUT_HISTORY: data->vout_min[page] = 0xffff; data->vout_max[page] = 0; - ret = ltc2978_clear_peaks(client, page, data->id); + ret = ltc2978_clear_peaks(data, client, page); break; case PMBUS_VIRT_RESET_VIN_HISTORY: data->vin_min = 0x7bff; data->vin_max = 0x7c00; - ret = ltc2978_clear_peaks(client, page, data->id); + ret = ltc2978_clear_peaks(data, client, page); break; case PMBUS_VIRT_RESET_TEMP_HISTORY: data->temp_min[page] = 0x7bff; data->temp_max[page] = 0x7c00; - ret = ltc2978_clear_peaks(client, page, data->id); + ret = ltc2978_clear_peaks(data, client, page); break; default: + ret = ltc_wait_ready(client); + if (ret < 0) + return ret; ret = -ENODATA; break; } @@ -366,10 +500,17 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, static const struct i2c_device_id ltc2978_id[] = { {"ltc2974", ltc2974}, + {"ltc2975", ltc2975}, {"ltc2977", ltc2977}, {"ltc2978", ltc2978}, + {"ltc2980", ltc2980}, {"ltc3880", ltc3880}, + {"ltc3882", ltc3882}, {"ltc3883", ltc3883}, + {"ltc3886", ltc3886}, + {"ltc3887", ltc3887}, + {"ltm2987", ltm2987}, + {"ltm4675", ltm4675}, {"ltm4676", ltm4676}, {} }; @@ -388,10 +529,74 @@ static const struct regulator_desc ltc2978_reg_desc[] = { }; #endif /* CONFIG_SENSORS_LTC2978_REGULATOR */ +static int ltc2978_get_id(struct i2c_client *client) +{ + int chip_id; + + chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID); + if (chip_id < 0) { + const struct i2c_device_id *id; + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) + return ret; + if (ret < 3 || strncmp(buf, "LTC", 3)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) + return ret; + for (id = <c2978_id[0]; strlen(id->name); id++) { + if (!strncasecmp(id->name, buf, strlen(id->name))) + return (int)id->driver_data; + } + return -ENODEV; + } + + chip_id &= LTC2978_ID_MASK; + + if (chip_id == LTC2974_ID) + return ltc2974; + else if (chip_id == LTC2975_ID) + return ltc2975; + else if (chip_id == LTC2977_ID) + return ltc2977; + else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) + return ltc2978; + else if (chip_id == LTC2980_ID_A || chip_id == LTC2980_ID_B) + return ltc2980; + else if (chip_id == LTC3880_ID) + return ltc3880; + else if (chip_id == LTC3882_ID || chip_id == LTC3882_ID_D1) + return ltc3882; + else if (chip_id == LTC3883_ID) + return ltc3883; + else if (chip_id == LTC3886_ID) + return ltc3886; + else if (chip_id == LTC3887_ID) + return ltc3887; + else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B) + return ltm2987; + else if (chip_id == LTM4675_ID) + return ltm4675; + else if (chip_id == LTM4676_ID_REV1 || chip_id == LTM4676_ID_REV2 || + chip_id == LTM4676A_ID) + return ltm4676; + + dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); + return -ENODEV; +} + static int ltc2978_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int chip_id, i; + int i, chip_id; struct ltc2978_data *data; struct pmbus_driver_info *info; @@ -404,27 +609,11 @@ static int ltc2978_probe(struct i2c_client *client, if (!data) return -ENOMEM; - chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID); + chip_id = ltc2978_get_id(client); if (chip_id < 0) return chip_id; - if (chip_id == LTC2974_ID_REV1 || chip_id == LTC2974_ID_REV2) { - data->id = ltc2974; - } else if (chip_id == LTC2977_ID) { - data->id = ltc2977; - } else if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2 || - chip_id == LTC2978A_ID) { - data->id = ltc2978; - } else if ((chip_id & LTC3880_ID_MASK) == LTC3880_ID) { - data->id = ltc3880; - } else if ((chip_id & LTC3883_ID_MASK) == LTC3883_ID) { - data->id = ltc3883; - } else if ((chip_id & LTM4676_ID_MASK) == LTM4676_ID) { - data->id = ltm4676; - } else { - dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); - return -ENODEV; - } + data->id = chip_id; if (data->id != id->driver_data) dev_warn(&client->dev, "Device mismatch: Configured %s, detected %s\n", @@ -433,6 +622,9 @@ static int ltc2978_probe(struct i2c_client *client, info = &data->info; info->write_word_data = ltc2978_write_word_data; + info->write_byte = ltc_write_byte; + info->read_word_data = ltc_read_word_data; + info->read_byte_data = ltc_read_byte_data; data->vin_min = 0x7bff; data->vin_max = 0x7c00; @@ -461,8 +653,23 @@ static int ltc2978_probe(struct i2c_client *client, | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; } break; + case ltc2975: + info->read_word_data = ltc2975_read_word_data; + info->pages = LTC2974_NUM_PAGES; + info->func[0] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN + | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP2; + for (i = 0; i < info->pages; i++) { + info->func[i] |= PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + } + break; case ltc2977: case ltc2978: + case ltc2980: + case ltm2987: info->read_word_data = ltc2978_read_word_data; info->pages = LTC2978_NUM_PAGES; info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT @@ -474,7 +681,10 @@ static int ltc2978_probe(struct i2c_client *client, } break; case ltc3880: + case ltc3887: + case ltm4675: case ltm4676: + data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; info->read_word_data = ltc3880_read_word_data; info->pages = LTC3880_NUM_PAGES; info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN @@ -488,7 +698,23 @@ static int ltc2978_probe(struct i2c_client *client, | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; break; + case ltc3882: + data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; + info->read_word_data = ltc3880_read_word_data; + info->pages = LTC3880_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + break; case ltc3883: + data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; info->read_word_data = ltc3883_read_word_data; info->pages = LTC3883_NUM_PAGES; info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN @@ -498,6 +724,21 @@ static int ltc2978_probe(struct i2c_client *client, | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; break; + case ltc3886: + data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; + info->read_word_data = ltc3883_read_word_data; + info->pages = LTC3880_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP; + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + break; default: return -ENODEV; } @@ -517,10 +758,17 @@ static int ltc2978_probe(struct i2c_client *client, #ifdef CONFIG_OF static const struct of_device_id ltc2978_of_match[] = { { .compatible = "lltc,ltc2974" }, + { .compatible = "lltc,ltc2975" }, { .compatible = "lltc,ltc2977" }, { .compatible = "lltc,ltc2978" }, + { .compatible = "lltc,ltc2980" }, { .compatible = "lltc,ltc3880" }, + { .compatible = "lltc,ltc3882" }, { .compatible = "lltc,ltc3883" }, + { .compatible = "lltc,ltc3886" }, + { .compatible = "lltc,ltc3887" }, + { .compatible = "lltc,ltm2987" }, + { .compatible = "lltc,ltm4675" }, { .compatible = "lltc,ltm4676" }, { } }; @@ -540,5 +788,5 @@ static struct i2c_driver ltc2978_driver = { module_i2c_driver(ltc2978_driver); MODULE_AUTHOR("Guenter Roeck"); -MODULE_DESCRIPTION("PMBus driver for LTC2974, LTC2978, LTC3880, LTC3883, and LTM4676"); +MODULE_DESCRIPTION("PMBus driver for LTC2978 and comppatible chips"); MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/hwmon/pmbus/max20751.c b/kernel/drivers/hwmon/pmbus/max20751.c new file mode 100644 index 000000000..ab74aeae8 --- /dev/null +++ b/kernel/drivers/hwmon/pmbus/max20751.c @@ -0,0 +1,64 @@ +/* + * Hardware monitoring driver for Maxim MAX20751 + * + * Copyright (c) 2015 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 Free Software Foundation; either version 2 of the License, 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include "pmbus.h" + +static struct pmbus_driver_info max20751_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = vid, + .vrm_version = vr12, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_POUT, +}; + +static int max20751_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return pmbus_do_probe(client, id, &max20751_info); +} + +static const struct i2c_device_id max20751_id[] = { + {"max20751", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, max20751_id); + +static struct i2c_driver max20751_driver = { + .driver = { + .name = "max20751", + }, + .probe = max20751_probe, + .remove = pmbus_do_remove, + .id_table = max20751_id, +}; + +module_i2c_driver(max20751_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("PMBus driver for Maxim MAX20751"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/hwmon/pmbus/max34440.c b/kernel/drivers/hwmon/pmbus/max34440.c index 7e930c3ce..74a1f6f68 100644 --- a/kernel/drivers/hwmon/pmbus/max34440.c +++ b/kernel/drivers/hwmon/pmbus/max34440.c @@ -19,6 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -38,10 +39,10 @@ enum chips { max34440, max34441, max34446, max34460, max34461 }; #define MAX34446_MFR_IOUT_AVG 0xe2 #define MAX34446_MFR_TEMPERATURE_AVG 0xe3 -#define MAX34440_STATUS_OC_WARN (1 << 0) -#define MAX34440_STATUS_OC_FAULT (1 << 1) -#define MAX34440_STATUS_OT_FAULT (1 << 5) -#define MAX34440_STATUS_OT_WARN (1 << 6) +#define MAX34440_STATUS_OC_WARN BIT(0) +#define MAX34440_STATUS_OC_FAULT BIT(1) +#define MAX34440_STATUS_OT_FAULT BIT(5) +#define MAX34440_STATUS_OT_WARN BIT(6) struct max34440_data { int id; diff --git a/kernel/drivers/hwmon/pmbus/max8688.c b/kernel/drivers/hwmon/pmbus/max8688.c index f04454a42..dd4883a19 100644 --- a/kernel/drivers/hwmon/pmbus/max8688.c +++ b/kernel/drivers/hwmon/pmbus/max8688.c @@ -18,6 +18,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -30,15 +31,15 @@ #define MAX8688_MFR_TEMPERATURE_PEAK 0xd6 #define MAX8688_MFG_STATUS 0xd8 -#define MAX8688_STATUS_OC_FAULT (1 << 4) -#define MAX8688_STATUS_OV_FAULT (1 << 5) -#define MAX8688_STATUS_OV_WARNING (1 << 8) -#define MAX8688_STATUS_UV_FAULT (1 << 9) -#define MAX8688_STATUS_UV_WARNING (1 << 10) -#define MAX8688_STATUS_UC_FAULT (1 << 11) -#define MAX8688_STATUS_OC_WARNING (1 << 12) -#define MAX8688_STATUS_OT_FAULT (1 << 13) -#define MAX8688_STATUS_OT_WARNING (1 << 14) +#define MAX8688_STATUS_OC_FAULT BIT(4) +#define MAX8688_STATUS_OV_FAULT BIT(5) +#define MAX8688_STATUS_OV_WARNING BIT(8) +#define MAX8688_STATUS_UV_FAULT BIT(9) +#define MAX8688_STATUS_UV_WARNING BIT(10) +#define MAX8688_STATUS_UC_FAULT BIT(11) +#define MAX8688_STATUS_OC_WARNING BIT(12) +#define MAX8688_STATUS_OT_FAULT BIT(13) +#define MAX8688_STATUS_OT_WARNING BIT(14) static int max8688_read_word_data(struct i2c_client *client, int page, int reg) { diff --git a/kernel/drivers/hwmon/pmbus/pmbus.c b/kernel/drivers/hwmon/pmbus/pmbus.c index 554d0249d..0a74991a6 100644 --- a/kernel/drivers/hwmon/pmbus/pmbus.c +++ b/kernel/drivers/hwmon/pmbus/pmbus.c @@ -129,6 +129,7 @@ static int pmbus_identify(struct i2c_client *client, break; case 1: info->format[PSC_VOLTAGE_OUT] = vid; + info->vrm_version = vr11; break; case 2: info->format[PSC_VOLTAGE_OUT] = direct; @@ -193,6 +194,10 @@ static const struct i2c_device_id pmbus_id[] = { {"pdt012", 1}, {"pmbus", 0}, {"tps40400", 1}, + {"tps544b20", 1}, + {"tps544b25", 1}, + {"tps544c20", 1}, + {"tps544c25", 1}, {"udt020", 1}, {} }; diff --git a/kernel/drivers/hwmon/pmbus/pmbus.h b/kernel/drivers/hwmon/pmbus/pmbus.h index 89a23ff83..bfcb13bae 100644 --- a/kernel/drivers/hwmon/pmbus/pmbus.h +++ b/kernel/drivers/hwmon/pmbus/pmbus.h @@ -19,114 +19,116 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/regulator/driver.h> - #ifndef PMBUS_H #define PMBUS_H +#include <linux/bitops.h> +#include <linux/regulator/driver.h> + /* * Registers */ -#define PMBUS_PAGE 0x00 -#define PMBUS_OPERATION 0x01 -#define PMBUS_ON_OFF_CONFIG 0x02 -#define PMBUS_CLEAR_FAULTS 0x03 -#define PMBUS_PHASE 0x04 - -#define PMBUS_CAPABILITY 0x19 -#define PMBUS_QUERY 0x1A - -#define PMBUS_VOUT_MODE 0x20 -#define PMBUS_VOUT_COMMAND 0x21 -#define PMBUS_VOUT_TRIM 0x22 -#define PMBUS_VOUT_CAL_OFFSET 0x23 -#define PMBUS_VOUT_MAX 0x24 -#define PMBUS_VOUT_MARGIN_HIGH 0x25 -#define PMBUS_VOUT_MARGIN_LOW 0x26 -#define PMBUS_VOUT_TRANSITION_RATE 0x27 -#define PMBUS_VOUT_DROOP 0x28 -#define PMBUS_VOUT_SCALE_LOOP 0x29 -#define PMBUS_VOUT_SCALE_MONITOR 0x2A - -#define PMBUS_COEFFICIENTS 0x30 -#define PMBUS_POUT_MAX 0x31 - -#define PMBUS_FAN_CONFIG_12 0x3A -#define PMBUS_FAN_COMMAND_1 0x3B -#define PMBUS_FAN_COMMAND_2 0x3C -#define PMBUS_FAN_CONFIG_34 0x3D -#define PMBUS_FAN_COMMAND_3 0x3E -#define PMBUS_FAN_COMMAND_4 0x3F - -#define PMBUS_VOUT_OV_FAULT_LIMIT 0x40 -#define PMBUS_VOUT_OV_FAULT_RESPONSE 0x41 -#define PMBUS_VOUT_OV_WARN_LIMIT 0x42 -#define PMBUS_VOUT_UV_WARN_LIMIT 0x43 -#define PMBUS_VOUT_UV_FAULT_LIMIT 0x44 -#define PMBUS_VOUT_UV_FAULT_RESPONSE 0x45 -#define PMBUS_IOUT_OC_FAULT_LIMIT 0x46 -#define PMBUS_IOUT_OC_FAULT_RESPONSE 0x47 -#define PMBUS_IOUT_OC_LV_FAULT_LIMIT 0x48 -#define PMBUS_IOUT_OC_LV_FAULT_RESPONSE 0x49 -#define PMBUS_IOUT_OC_WARN_LIMIT 0x4A -#define PMBUS_IOUT_UC_FAULT_LIMIT 0x4B -#define PMBUS_IOUT_UC_FAULT_RESPONSE 0x4C - -#define PMBUS_OT_FAULT_LIMIT 0x4F -#define PMBUS_OT_FAULT_RESPONSE 0x50 -#define PMBUS_OT_WARN_LIMIT 0x51 -#define PMBUS_UT_WARN_LIMIT 0x52 -#define PMBUS_UT_FAULT_LIMIT 0x53 -#define PMBUS_UT_FAULT_RESPONSE 0x54 -#define PMBUS_VIN_OV_FAULT_LIMIT 0x55 -#define PMBUS_VIN_OV_FAULT_RESPONSE 0x56 -#define PMBUS_VIN_OV_WARN_LIMIT 0x57 -#define PMBUS_VIN_UV_WARN_LIMIT 0x58 -#define PMBUS_VIN_UV_FAULT_LIMIT 0x59 - -#define PMBUS_IIN_OC_FAULT_LIMIT 0x5B -#define PMBUS_IIN_OC_WARN_LIMIT 0x5D - -#define PMBUS_POUT_OP_FAULT_LIMIT 0x68 -#define PMBUS_POUT_OP_WARN_LIMIT 0x6A -#define PMBUS_PIN_OP_WARN_LIMIT 0x6B - -#define PMBUS_STATUS_BYTE 0x78 -#define PMBUS_STATUS_WORD 0x79 -#define PMBUS_STATUS_VOUT 0x7A -#define PMBUS_STATUS_IOUT 0x7B -#define PMBUS_STATUS_INPUT 0x7C -#define PMBUS_STATUS_TEMPERATURE 0x7D -#define PMBUS_STATUS_CML 0x7E -#define PMBUS_STATUS_OTHER 0x7F -#define PMBUS_STATUS_MFR_SPECIFIC 0x80 -#define PMBUS_STATUS_FAN_12 0x81 -#define PMBUS_STATUS_FAN_34 0x82 - -#define PMBUS_READ_VIN 0x88 -#define PMBUS_READ_IIN 0x89 -#define PMBUS_READ_VCAP 0x8A -#define PMBUS_READ_VOUT 0x8B -#define PMBUS_READ_IOUT 0x8C -#define PMBUS_READ_TEMPERATURE_1 0x8D -#define PMBUS_READ_TEMPERATURE_2 0x8E -#define PMBUS_READ_TEMPERATURE_3 0x8F -#define PMBUS_READ_FAN_SPEED_1 0x90 -#define PMBUS_READ_FAN_SPEED_2 0x91 -#define PMBUS_READ_FAN_SPEED_3 0x92 -#define PMBUS_READ_FAN_SPEED_4 0x93 -#define PMBUS_READ_DUTY_CYCLE 0x94 -#define PMBUS_READ_FREQUENCY 0x95 -#define PMBUS_READ_POUT 0x96 -#define PMBUS_READ_PIN 0x97 - -#define PMBUS_REVISION 0x98 -#define PMBUS_MFR_ID 0x99 -#define PMBUS_MFR_MODEL 0x9A -#define PMBUS_MFR_REVISION 0x9B -#define PMBUS_MFR_LOCATION 0x9C -#define PMBUS_MFR_DATE 0x9D -#define PMBUS_MFR_SERIAL 0x9E +enum pmbus_regs { + PMBUS_PAGE = 0x00, + PMBUS_OPERATION = 0x01, + PMBUS_ON_OFF_CONFIG = 0x02, + PMBUS_CLEAR_FAULTS = 0x03, + PMBUS_PHASE = 0x04, + + PMBUS_CAPABILITY = 0x19, + PMBUS_QUERY = 0x1A, + + PMBUS_VOUT_MODE = 0x20, + PMBUS_VOUT_COMMAND = 0x21, + PMBUS_VOUT_TRIM = 0x22, + PMBUS_VOUT_CAL_OFFSET = 0x23, + PMBUS_VOUT_MAX = 0x24, + PMBUS_VOUT_MARGIN_HIGH = 0x25, + PMBUS_VOUT_MARGIN_LOW = 0x26, + PMBUS_VOUT_TRANSITION_RATE = 0x27, + PMBUS_VOUT_DROOP = 0x28, + PMBUS_VOUT_SCALE_LOOP = 0x29, + PMBUS_VOUT_SCALE_MONITOR = 0x2A, + + PMBUS_COEFFICIENTS = 0x30, + PMBUS_POUT_MAX = 0x31, + + PMBUS_FAN_CONFIG_12 = 0x3A, + PMBUS_FAN_COMMAND_1 = 0x3B, + PMBUS_FAN_COMMAND_2 = 0x3C, + PMBUS_FAN_CONFIG_34 = 0x3D, + PMBUS_FAN_COMMAND_3 = 0x3E, + PMBUS_FAN_COMMAND_4 = 0x3F, + + PMBUS_VOUT_OV_FAULT_LIMIT = 0x40, + PMBUS_VOUT_OV_FAULT_RESPONSE = 0x41, + PMBUS_VOUT_OV_WARN_LIMIT = 0x42, + PMBUS_VOUT_UV_WARN_LIMIT = 0x43, + PMBUS_VOUT_UV_FAULT_LIMIT = 0x44, + PMBUS_VOUT_UV_FAULT_RESPONSE = 0x45, + PMBUS_IOUT_OC_FAULT_LIMIT = 0x46, + PMBUS_IOUT_OC_FAULT_RESPONSE = 0x47, + PMBUS_IOUT_OC_LV_FAULT_LIMIT = 0x48, + PMBUS_IOUT_OC_LV_FAULT_RESPONSE = 0x49, + PMBUS_IOUT_OC_WARN_LIMIT = 0x4A, + PMBUS_IOUT_UC_FAULT_LIMIT = 0x4B, + PMBUS_IOUT_UC_FAULT_RESPONSE = 0x4C, + + PMBUS_OT_FAULT_LIMIT = 0x4F, + PMBUS_OT_FAULT_RESPONSE = 0x50, + PMBUS_OT_WARN_LIMIT = 0x51, + PMBUS_UT_WARN_LIMIT = 0x52, + PMBUS_UT_FAULT_LIMIT = 0x53, + PMBUS_UT_FAULT_RESPONSE = 0x54, + PMBUS_VIN_OV_FAULT_LIMIT = 0x55, + PMBUS_VIN_OV_FAULT_RESPONSE = 0x56, + PMBUS_VIN_OV_WARN_LIMIT = 0x57, + PMBUS_VIN_UV_WARN_LIMIT = 0x58, + PMBUS_VIN_UV_FAULT_LIMIT = 0x59, + + PMBUS_IIN_OC_FAULT_LIMIT = 0x5B, + PMBUS_IIN_OC_WARN_LIMIT = 0x5D, + + PMBUS_POUT_OP_FAULT_LIMIT = 0x68, + PMBUS_POUT_OP_WARN_LIMIT = 0x6A, + PMBUS_PIN_OP_WARN_LIMIT = 0x6B, + + PMBUS_STATUS_BYTE = 0x78, + PMBUS_STATUS_WORD = 0x79, + PMBUS_STATUS_VOUT = 0x7A, + PMBUS_STATUS_IOUT = 0x7B, + PMBUS_STATUS_INPUT = 0x7C, + PMBUS_STATUS_TEMPERATURE = 0x7D, + PMBUS_STATUS_CML = 0x7E, + PMBUS_STATUS_OTHER = 0x7F, + PMBUS_STATUS_MFR_SPECIFIC = 0x80, + PMBUS_STATUS_FAN_12 = 0x81, + PMBUS_STATUS_FAN_34 = 0x82, + + PMBUS_READ_VIN = 0x88, + PMBUS_READ_IIN = 0x89, + PMBUS_READ_VCAP = 0x8A, + PMBUS_READ_VOUT = 0x8B, + PMBUS_READ_IOUT = 0x8C, + PMBUS_READ_TEMPERATURE_1 = 0x8D, + PMBUS_READ_TEMPERATURE_2 = 0x8E, + PMBUS_READ_TEMPERATURE_3 = 0x8F, + PMBUS_READ_FAN_SPEED_1 = 0x90, + PMBUS_READ_FAN_SPEED_2 = 0x91, + PMBUS_READ_FAN_SPEED_3 = 0x92, + PMBUS_READ_FAN_SPEED_4 = 0x93, + PMBUS_READ_DUTY_CYCLE = 0x94, + PMBUS_READ_FREQUENCY = 0x95, + PMBUS_READ_POUT = 0x96, + PMBUS_READ_PIN = 0x97, + + PMBUS_REVISION = 0x98, + PMBUS_MFR_ID = 0x99, + PMBUS_MFR_MODEL = 0x9A, + PMBUS_MFR_REVISION = 0x9B, + PMBUS_MFR_LOCATION = 0x9C, + PMBUS_MFR_DATE = 0x9D, + PMBUS_MFR_SERIAL = 0x9E, /* * Virtual registers. @@ -148,55 +150,58 @@ * the calling PMBus core code will abort if the chip driver returns an error * code when reading or writing virtual registers. */ -#define PMBUS_VIRT_BASE 0x100 -#define PMBUS_VIRT_READ_TEMP_AVG (PMBUS_VIRT_BASE + 0) -#define PMBUS_VIRT_READ_TEMP_MIN (PMBUS_VIRT_BASE + 1) -#define PMBUS_VIRT_READ_TEMP_MAX (PMBUS_VIRT_BASE + 2) -#define PMBUS_VIRT_RESET_TEMP_HISTORY (PMBUS_VIRT_BASE + 3) -#define PMBUS_VIRT_READ_VIN_AVG (PMBUS_VIRT_BASE + 4) -#define PMBUS_VIRT_READ_VIN_MIN (PMBUS_VIRT_BASE + 5) -#define PMBUS_VIRT_READ_VIN_MAX (PMBUS_VIRT_BASE + 6) -#define PMBUS_VIRT_RESET_VIN_HISTORY (PMBUS_VIRT_BASE + 7) -#define PMBUS_VIRT_READ_IIN_AVG (PMBUS_VIRT_BASE + 8) -#define PMBUS_VIRT_READ_IIN_MIN (PMBUS_VIRT_BASE + 9) -#define PMBUS_VIRT_READ_IIN_MAX (PMBUS_VIRT_BASE + 10) -#define PMBUS_VIRT_RESET_IIN_HISTORY (PMBUS_VIRT_BASE + 11) -#define PMBUS_VIRT_READ_PIN_AVG (PMBUS_VIRT_BASE + 12) -#define PMBUS_VIRT_READ_PIN_MAX (PMBUS_VIRT_BASE + 13) -#define PMBUS_VIRT_RESET_PIN_HISTORY (PMBUS_VIRT_BASE + 14) -#define PMBUS_VIRT_READ_POUT_AVG (PMBUS_VIRT_BASE + 15) -#define PMBUS_VIRT_READ_POUT_MAX (PMBUS_VIRT_BASE + 16) -#define PMBUS_VIRT_RESET_POUT_HISTORY (PMBUS_VIRT_BASE + 17) -#define PMBUS_VIRT_READ_VOUT_AVG (PMBUS_VIRT_BASE + 18) -#define PMBUS_VIRT_READ_VOUT_MIN (PMBUS_VIRT_BASE + 19) -#define PMBUS_VIRT_READ_VOUT_MAX (PMBUS_VIRT_BASE + 20) -#define PMBUS_VIRT_RESET_VOUT_HISTORY (PMBUS_VIRT_BASE + 21) -#define PMBUS_VIRT_READ_IOUT_AVG (PMBUS_VIRT_BASE + 22) -#define PMBUS_VIRT_READ_IOUT_MIN (PMBUS_VIRT_BASE + 23) -#define PMBUS_VIRT_READ_IOUT_MAX (PMBUS_VIRT_BASE + 24) -#define PMBUS_VIRT_RESET_IOUT_HISTORY (PMBUS_VIRT_BASE + 25) -#define PMBUS_VIRT_READ_TEMP2_AVG (PMBUS_VIRT_BASE + 26) -#define PMBUS_VIRT_READ_TEMP2_MIN (PMBUS_VIRT_BASE + 27) -#define PMBUS_VIRT_READ_TEMP2_MAX (PMBUS_VIRT_BASE + 28) -#define PMBUS_VIRT_RESET_TEMP2_HISTORY (PMBUS_VIRT_BASE + 29) - -#define PMBUS_VIRT_READ_VMON (PMBUS_VIRT_BASE + 30) -#define PMBUS_VIRT_VMON_UV_WARN_LIMIT (PMBUS_VIRT_BASE + 31) -#define PMBUS_VIRT_VMON_OV_WARN_LIMIT (PMBUS_VIRT_BASE + 32) -#define PMBUS_VIRT_VMON_UV_FAULT_LIMIT (PMBUS_VIRT_BASE + 33) -#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34) -#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35) + PMBUS_VIRT_BASE = 0x100, + PMBUS_VIRT_READ_TEMP_AVG, + PMBUS_VIRT_READ_TEMP_MIN, + PMBUS_VIRT_READ_TEMP_MAX, + PMBUS_VIRT_RESET_TEMP_HISTORY, + PMBUS_VIRT_READ_VIN_AVG, + PMBUS_VIRT_READ_VIN_MIN, + PMBUS_VIRT_READ_VIN_MAX, + PMBUS_VIRT_RESET_VIN_HISTORY, + PMBUS_VIRT_READ_IIN_AVG, + PMBUS_VIRT_READ_IIN_MIN, + PMBUS_VIRT_READ_IIN_MAX, + PMBUS_VIRT_RESET_IIN_HISTORY, + PMBUS_VIRT_READ_PIN_AVG, + PMBUS_VIRT_READ_PIN_MIN, + PMBUS_VIRT_READ_PIN_MAX, + PMBUS_VIRT_RESET_PIN_HISTORY, + PMBUS_VIRT_READ_POUT_AVG, + PMBUS_VIRT_READ_POUT_MIN, + PMBUS_VIRT_READ_POUT_MAX, + PMBUS_VIRT_RESET_POUT_HISTORY, + PMBUS_VIRT_READ_VOUT_AVG, + PMBUS_VIRT_READ_VOUT_MIN, + PMBUS_VIRT_READ_VOUT_MAX, + PMBUS_VIRT_RESET_VOUT_HISTORY, + PMBUS_VIRT_READ_IOUT_AVG, + PMBUS_VIRT_READ_IOUT_MIN, + PMBUS_VIRT_READ_IOUT_MAX, + PMBUS_VIRT_RESET_IOUT_HISTORY, + PMBUS_VIRT_READ_TEMP2_AVG, + PMBUS_VIRT_READ_TEMP2_MIN, + PMBUS_VIRT_READ_TEMP2_MAX, + PMBUS_VIRT_RESET_TEMP2_HISTORY, + + PMBUS_VIRT_READ_VMON, + PMBUS_VIRT_VMON_UV_WARN_LIMIT, + PMBUS_VIRT_VMON_OV_WARN_LIMIT, + PMBUS_VIRT_VMON_UV_FAULT_LIMIT, + PMBUS_VIRT_VMON_OV_FAULT_LIMIT, + PMBUS_VIRT_STATUS_VMON, +}; /* * OPERATION */ -#define PB_OPERATION_CONTROL_ON (1<<7) +#define PB_OPERATION_CONTROL_ON BIT(7) /* * CAPABILITY */ -#define PB_CAPABILITY_SMBALERT (1<<4) -#define PB_CAPABILITY_ERROR_CHECK (1<<7) +#define PB_CAPABILITY_SMBALERT BIT(4) +#define PB_CAPABILITY_ERROR_CHECK BIT(7) /* * VOUT_MODE @@ -211,94 +216,94 @@ /* * Fan configuration */ -#define PB_FAN_2_PULSE_MASK ((1 << 0) | (1 << 1)) -#define PB_FAN_2_RPM (1 << 2) -#define PB_FAN_2_INSTALLED (1 << 3) -#define PB_FAN_1_PULSE_MASK ((1 << 4) | (1 << 5)) -#define PB_FAN_1_RPM (1 << 6) -#define PB_FAN_1_INSTALLED (1 << 7) +#define PB_FAN_2_PULSE_MASK (BIT(0) | BIT(1)) +#define PB_FAN_2_RPM BIT(2) +#define PB_FAN_2_INSTALLED BIT(3) +#define PB_FAN_1_PULSE_MASK (BIT(4) | BIT(5)) +#define PB_FAN_1_RPM BIT(6) +#define PB_FAN_1_INSTALLED BIT(7) /* * STATUS_BYTE, STATUS_WORD (lower) */ -#define PB_STATUS_NONE_ABOVE (1<<0) -#define PB_STATUS_CML (1<<1) -#define PB_STATUS_TEMPERATURE (1<<2) -#define PB_STATUS_VIN_UV (1<<3) -#define PB_STATUS_IOUT_OC (1<<4) -#define PB_STATUS_VOUT_OV (1<<5) -#define PB_STATUS_OFF (1<<6) -#define PB_STATUS_BUSY (1<<7) +#define PB_STATUS_NONE_ABOVE BIT(0) +#define PB_STATUS_CML BIT(1) +#define PB_STATUS_TEMPERATURE BIT(2) +#define PB_STATUS_VIN_UV BIT(3) +#define PB_STATUS_IOUT_OC BIT(4) +#define PB_STATUS_VOUT_OV BIT(5) +#define PB_STATUS_OFF BIT(6) +#define PB_STATUS_BUSY BIT(7) /* * STATUS_WORD (upper) */ -#define PB_STATUS_UNKNOWN (1<<8) -#define PB_STATUS_OTHER (1<<9) -#define PB_STATUS_FANS (1<<10) -#define PB_STATUS_POWER_GOOD_N (1<<11) -#define PB_STATUS_WORD_MFR (1<<12) -#define PB_STATUS_INPUT (1<<13) -#define PB_STATUS_IOUT_POUT (1<<14) -#define PB_STATUS_VOUT (1<<15) +#define PB_STATUS_UNKNOWN BIT(8) +#define PB_STATUS_OTHER BIT(9) +#define PB_STATUS_FANS BIT(10) +#define PB_STATUS_POWER_GOOD_N BIT(11) +#define PB_STATUS_WORD_MFR BIT(12) +#define PB_STATUS_INPUT BIT(13) +#define PB_STATUS_IOUT_POUT BIT(14) +#define PB_STATUS_VOUT BIT(15) /* * STATUS_IOUT */ -#define PB_POUT_OP_WARNING (1<<0) -#define PB_POUT_OP_FAULT (1<<1) -#define PB_POWER_LIMITING (1<<2) -#define PB_CURRENT_SHARE_FAULT (1<<3) -#define PB_IOUT_UC_FAULT (1<<4) -#define PB_IOUT_OC_WARNING (1<<5) -#define PB_IOUT_OC_LV_FAULT (1<<6) -#define PB_IOUT_OC_FAULT (1<<7) +#define PB_POUT_OP_WARNING BIT(0) +#define PB_POUT_OP_FAULT BIT(1) +#define PB_POWER_LIMITING BIT(2) +#define PB_CURRENT_SHARE_FAULT BIT(3) +#define PB_IOUT_UC_FAULT BIT(4) +#define PB_IOUT_OC_WARNING BIT(5) +#define PB_IOUT_OC_LV_FAULT BIT(6) +#define PB_IOUT_OC_FAULT BIT(7) /* * STATUS_VOUT, STATUS_INPUT */ -#define PB_VOLTAGE_UV_FAULT (1<<4) -#define PB_VOLTAGE_UV_WARNING (1<<5) -#define PB_VOLTAGE_OV_WARNING (1<<6) -#define PB_VOLTAGE_OV_FAULT (1<<7) +#define PB_VOLTAGE_UV_FAULT BIT(4) +#define PB_VOLTAGE_UV_WARNING BIT(5) +#define PB_VOLTAGE_OV_WARNING BIT(6) +#define PB_VOLTAGE_OV_FAULT BIT(7) /* * STATUS_INPUT */ -#define PB_PIN_OP_WARNING (1<<0) -#define PB_IIN_OC_WARNING (1<<1) -#define PB_IIN_OC_FAULT (1<<2) +#define PB_PIN_OP_WARNING BIT(0) +#define PB_IIN_OC_WARNING BIT(1) +#define PB_IIN_OC_FAULT BIT(2) /* * STATUS_TEMPERATURE */ -#define PB_TEMP_UT_FAULT (1<<4) -#define PB_TEMP_UT_WARNING (1<<5) -#define PB_TEMP_OT_WARNING (1<<6) -#define PB_TEMP_OT_FAULT (1<<7) +#define PB_TEMP_UT_FAULT BIT(4) +#define PB_TEMP_UT_WARNING BIT(5) +#define PB_TEMP_OT_WARNING BIT(6) +#define PB_TEMP_OT_FAULT BIT(7) /* * STATUS_FAN */ -#define PB_FAN_AIRFLOW_WARNING (1<<0) -#define PB_FAN_AIRFLOW_FAULT (1<<1) -#define PB_FAN_FAN2_SPEED_OVERRIDE (1<<2) -#define PB_FAN_FAN1_SPEED_OVERRIDE (1<<3) -#define PB_FAN_FAN2_WARNING (1<<4) -#define PB_FAN_FAN1_WARNING (1<<5) -#define PB_FAN_FAN2_FAULT (1<<6) -#define PB_FAN_FAN1_FAULT (1<<7) +#define PB_FAN_AIRFLOW_WARNING BIT(0) +#define PB_FAN_AIRFLOW_FAULT BIT(1) +#define PB_FAN_FAN2_SPEED_OVERRIDE BIT(2) +#define PB_FAN_FAN1_SPEED_OVERRIDE BIT(3) +#define PB_FAN_FAN2_WARNING BIT(4) +#define PB_FAN_FAN1_WARNING BIT(5) +#define PB_FAN_FAN2_FAULT BIT(6) +#define PB_FAN_FAN1_FAULT BIT(7) /* * CML_FAULT_STATUS */ -#define PB_CML_FAULT_OTHER_MEM_LOGIC (1<<0) -#define PB_CML_FAULT_OTHER_COMM (1<<1) -#define PB_CML_FAULT_PROCESSOR (1<<3) -#define PB_CML_FAULT_MEMORY (1<<4) -#define PB_CML_FAULT_PACKET_ERROR (1<<5) -#define PB_CML_FAULT_INVALID_DATA (1<<6) -#define PB_CML_FAULT_INVALID_COMMAND (1<<7) +#define PB_CML_FAULT_OTHER_MEM_LOGIC BIT(0) +#define PB_CML_FAULT_OTHER_COMM BIT(1) +#define PB_CML_FAULT_PROCESSOR BIT(3) +#define PB_CML_FAULT_MEMORY BIT(4) +#define PB_CML_FAULT_PACKET_ERROR BIT(5) +#define PB_CML_FAULT_INVALID_DATA BIT(6) +#define PB_CML_FAULT_INVALID_COMMAND BIT(7) enum pmbus_sensor_classes { PSC_VOLTAGE_IN = 0, @@ -314,32 +319,34 @@ enum pmbus_sensor_classes { #define PMBUS_PAGES 32 /* Per PMBus specification */ /* Functionality bit mask */ -#define PMBUS_HAVE_VIN (1 << 0) -#define PMBUS_HAVE_VCAP (1 << 1) -#define PMBUS_HAVE_VOUT (1 << 2) -#define PMBUS_HAVE_IIN (1 << 3) -#define PMBUS_HAVE_IOUT (1 << 4) -#define PMBUS_HAVE_PIN (1 << 5) -#define PMBUS_HAVE_POUT (1 << 6) -#define PMBUS_HAVE_FAN12 (1 << 7) -#define PMBUS_HAVE_FAN34 (1 << 8) -#define PMBUS_HAVE_TEMP (1 << 9) -#define PMBUS_HAVE_TEMP2 (1 << 10) -#define PMBUS_HAVE_TEMP3 (1 << 11) -#define PMBUS_HAVE_STATUS_VOUT (1 << 12) -#define PMBUS_HAVE_STATUS_IOUT (1 << 13) -#define PMBUS_HAVE_STATUS_INPUT (1 << 14) -#define PMBUS_HAVE_STATUS_TEMP (1 << 15) -#define PMBUS_HAVE_STATUS_FAN12 (1 << 16) -#define PMBUS_HAVE_STATUS_FAN34 (1 << 17) -#define PMBUS_HAVE_VMON (1 << 18) -#define PMBUS_HAVE_STATUS_VMON (1 << 19) +#define PMBUS_HAVE_VIN BIT(0) +#define PMBUS_HAVE_VCAP BIT(1) +#define PMBUS_HAVE_VOUT BIT(2) +#define PMBUS_HAVE_IIN BIT(3) +#define PMBUS_HAVE_IOUT BIT(4) +#define PMBUS_HAVE_PIN BIT(5) +#define PMBUS_HAVE_POUT BIT(6) +#define PMBUS_HAVE_FAN12 BIT(7) +#define PMBUS_HAVE_FAN34 BIT(8) +#define PMBUS_HAVE_TEMP BIT(9) +#define PMBUS_HAVE_TEMP2 BIT(10) +#define PMBUS_HAVE_TEMP3 BIT(11) +#define PMBUS_HAVE_STATUS_VOUT BIT(12) +#define PMBUS_HAVE_STATUS_IOUT BIT(13) +#define PMBUS_HAVE_STATUS_INPUT BIT(14) +#define PMBUS_HAVE_STATUS_TEMP BIT(15) +#define PMBUS_HAVE_STATUS_FAN12 BIT(16) +#define PMBUS_HAVE_STATUS_FAN34 BIT(17) +#define PMBUS_HAVE_VMON BIT(18) +#define PMBUS_HAVE_STATUS_VMON BIT(19) enum pmbus_data_format { linear = 0, direct, vid }; +enum vrm_version { vr11 = 0, vr12 }; struct pmbus_driver_info { int pages; /* Total number of pages */ enum pmbus_data_format format[PSC_NUM_CLASSES]; + enum vrm_version vrm_version; /* * Support one set of coefficients for each sensor type * Used for chips providing data in direct mode. @@ -380,7 +387,7 @@ struct pmbus_driver_info { /* Regulator ops */ -extern struct regulator_ops pmbus_regulator_ops; +extern const struct regulator_ops pmbus_regulator_ops; /* Macro for filling in array of struct regulator_desc */ #define PMBUS_REGULATOR(_name, _id) \ @@ -390,6 +397,7 @@ extern struct regulator_ops pmbus_regulator_ops; .of_match = of_match_ptr(_name # _id), \ .regulators_node = of_match_ptr("regulators"), \ .ops = &pmbus_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ } diff --git a/kernel/drivers/hwmon/pmbus/pmbus_core.c b/kernel/drivers/hwmon/pmbus/pmbus_core.c index f2e47c7dd..ba59eaef2 100644 --- a/kernel/drivers/hwmon/pmbus/pmbus_core.c +++ b/kernel/drivers/hwmon/pmbus/pmbus_core.c @@ -515,16 +515,24 @@ static long pmbus_reg2data_direct(struct pmbus_data *data, /* * Convert VID sensor values to milli- or micro-units * depending on sensor type. - * We currently only support VR11. */ static long pmbus_reg2data_vid(struct pmbus_data *data, struct pmbus_sensor *sensor) { long val = sensor->data; + long rv = 0; - if (val < 0x02 || val > 0xb2) - return 0; - return DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); + switch (data->info->vrm_version) { + case vr11: + if (val >= 0x02 && val <= 0xb2) + rv = DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); + break; + case vr12: + if (val >= 0x01) + rv = 250 + (val - 1) * 5; + break; + } + return rv; } static long pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) @@ -1329,6 +1337,10 @@ static const struct pmbus_limit_attr pin_limit_attrs[] = { .update = true, .attr = "average", }, { + .reg = PMBUS_VIRT_READ_PIN_MIN, + .update = true, + .attr = "input_lowest", + }, { .reg = PMBUS_VIRT_READ_PIN_MAX, .update = true, .attr = "input_highest", @@ -1359,6 +1371,10 @@ static const struct pmbus_limit_attr pout_limit_attrs[] = { .update = true, .attr = "average", }, { + .reg = PMBUS_VIRT_READ_POUT_MIN, + .update = true, + .attr = "input_lowest", + }, { .reg = PMBUS_VIRT_READ_POUT_MAX, .update = true, .attr = "input_highest", @@ -1735,6 +1751,11 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, } } + /* Enable PEC if the controller supports it */ + ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); + if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) + client->flags |= I2C_CLIENT_PEC; + pmbus_clear_faults(client); if (info->identify) { @@ -1796,7 +1817,7 @@ static int pmbus_regulator_disable(struct regulator_dev *rdev) return _pmbus_regulator_on_off(rdev, 0); } -struct regulator_ops pmbus_regulator_ops = { +const struct regulator_ops pmbus_regulator_ops = { .enable = pmbus_regulator_enable, .disable = pmbus_regulator_disable, .is_enabled = pmbus_regulator_is_enabled, diff --git a/kernel/drivers/hwmon/pmbus/zl6100.c b/kernel/drivers/hwmon/pmbus/zl6100.c index 819644121..771802d7e 100644 --- a/kernel/drivers/hwmon/pmbus/zl6100.c +++ b/kernel/drivers/hwmon/pmbus/zl6100.c @@ -19,6 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/bitops.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -44,16 +45,16 @@ struct zl6100_data { #define ZL6100_MFR_CONFIG 0xd0 #define ZL6100_DEVICE_ID 0xe4 -#define ZL6100_MFR_XTEMP_ENABLE (1 << 7) +#define ZL6100_MFR_XTEMP_ENABLE BIT(7) #define MFR_VMON_OV_FAULT_LIMIT 0xf5 #define MFR_VMON_UV_FAULT_LIMIT 0xf6 #define MFR_READ_VMON 0xf7 -#define VMON_UV_WARNING (1 << 5) -#define VMON_OV_WARNING (1 << 4) -#define VMON_UV_FAULT (1 << 1) -#define VMON_OV_FAULT (1 << 0) +#define VMON_UV_WARNING BIT(5) +#define VMON_OV_WARNING BIT(4) +#define VMON_UV_FAULT BIT(1) +#define VMON_OV_FAULT BIT(0) #define ZL6100_WAIT_TIME 1000 /* uS */ diff --git a/kernel/drivers/hwmon/pwm-fan.c b/kernel/drivers/hwmon/pwm-fan.c index 2d9a71269..3e23003f7 100644 --- a/kernel/drivers/hwmon/pwm-fan.c +++ b/kernel/drivers/hwmon/pwm-fan.c @@ -323,6 +323,7 @@ static const struct of_device_id of_pwm_fan_match[] = { { .compatible = "pwm-fan", }, {}, }; +MODULE_DEVICE_TABLE(of, of_pwm_fan_match); static struct platform_driver pwm_fan_driver = { .probe = pwm_fan_probe, diff --git a/kernel/drivers/hwmon/scpi-hwmon.c b/kernel/drivers/hwmon/scpi-hwmon.c new file mode 100644 index 000000000..7e20567bc --- /dev/null +++ b/kernel/drivers/hwmon/scpi-hwmon.c @@ -0,0 +1,289 @@ +/* + * System Control and Power Interface(SCPI) based hwmon sensor driver + * + * Copyright (C) 2015 ARM Ltd. + * Punit Agrawal <punit.agrawal@arm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/hwmon.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/scpi_protocol.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/thermal.h> + +struct sensor_data { + struct scpi_sensor_info info; + struct device_attribute dev_attr_input; + struct device_attribute dev_attr_label; + char input[20]; + char label[20]; +}; + +struct scpi_thermal_zone { + struct list_head list; + int sensor_id; + struct scpi_sensors *scpi_sensors; + struct thermal_zone_device *tzd; +}; + +struct scpi_sensors { + struct scpi_ops *scpi_ops; + struct sensor_data *data; + struct list_head thermal_zones; + struct attribute **attrs; + struct attribute_group group; + const struct attribute_group *groups[2]; +}; + +static int scpi_read_temp(void *dev, int *temp) +{ + struct scpi_thermal_zone *zone = dev; + struct scpi_sensors *scpi_sensors = zone->scpi_sensors; + struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops; + struct sensor_data *sensor = &scpi_sensors->data[zone->sensor_id]; + u32 value; + int ret; + + ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value); + if (ret) + return ret; + + *temp = value; + return 0; +} + +/* hwmon callback functions */ +static ssize_t +scpi_show_sensor(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct scpi_sensors *scpi_sensors = dev_get_drvdata(dev); + struct scpi_ops *scpi_ops = scpi_sensors->scpi_ops; + struct sensor_data *sensor; + u32 value; + int ret; + + sensor = container_of(attr, struct sensor_data, dev_attr_input); + + ret = scpi_ops->sensor_get_value(sensor->info.sensor_id, &value); + if (ret) + return ret; + + return sprintf(buf, "%u\n", value); +} + +static ssize_t +scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_data *sensor; + + sensor = container_of(attr, struct sensor_data, dev_attr_label); + + return sprintf(buf, "%s\n", sensor->info.name); +} + +static void +unregister_thermal_zones(struct platform_device *pdev, + struct scpi_sensors *scpi_sensors) +{ + struct list_head *pos; + + list_for_each(pos, &scpi_sensors->thermal_zones) { + struct scpi_thermal_zone *zone; + + zone = list_entry(pos, struct scpi_thermal_zone, list); + thermal_zone_of_sensor_unregister(&pdev->dev, zone->tzd); + } +} + +static struct thermal_zone_of_device_ops scpi_sensor_ops = { + .get_temp = scpi_read_temp, +}; + +static int scpi_hwmon_probe(struct platform_device *pdev) +{ + u16 nr_sensors, i; + int num_temp = 0, num_volt = 0, num_current = 0, num_power = 0; + struct scpi_ops *scpi_ops; + struct device *hwdev, *dev = &pdev->dev; + struct scpi_sensors *scpi_sensors; + int ret, idx; + + scpi_ops = get_scpi_ops(); + if (!scpi_ops) + return -EPROBE_DEFER; + + ret = scpi_ops->sensor_get_capability(&nr_sensors); + if (ret) + return ret; + + if (!nr_sensors) + return -ENODEV; + + scpi_sensors = devm_kzalloc(dev, sizeof(*scpi_sensors), GFP_KERNEL); + if (!scpi_sensors) + return -ENOMEM; + + scpi_sensors->data = devm_kcalloc(dev, nr_sensors, + sizeof(*scpi_sensors->data), GFP_KERNEL); + if (!scpi_sensors->data) + return -ENOMEM; + + scpi_sensors->attrs = devm_kcalloc(dev, (nr_sensors * 2) + 1, + sizeof(*scpi_sensors->attrs), GFP_KERNEL); + if (!scpi_sensors->attrs) + return -ENOMEM; + + scpi_sensors->scpi_ops = scpi_ops; + + for (i = 0, idx = 0; i < nr_sensors; i++) { + struct sensor_data *sensor = &scpi_sensors->data[idx]; + + ret = scpi_ops->sensor_get_info(i, &sensor->info); + if (ret) + return ret; + + switch (sensor->info.class) { + case TEMPERATURE: + snprintf(sensor->input, sizeof(sensor->input), + "temp%d_input", num_temp + 1); + snprintf(sensor->label, sizeof(sensor->input), + "temp%d_label", num_temp + 1); + num_temp++; + break; + case VOLTAGE: + snprintf(sensor->input, sizeof(sensor->input), + "in%d_input", num_volt); + snprintf(sensor->label, sizeof(sensor->input), + "in%d_label", num_volt); + num_volt++; + break; + case CURRENT: + snprintf(sensor->input, sizeof(sensor->input), + "curr%d_input", num_current + 1); + snprintf(sensor->label, sizeof(sensor->input), + "curr%d_label", num_current + 1); + num_current++; + break; + case POWER: + snprintf(sensor->input, sizeof(sensor->input), + "power%d_input", num_power + 1); + snprintf(sensor->label, sizeof(sensor->input), + "power%d_label", num_power + 1); + num_power++; + break; + default: + continue; + } + + sensor->dev_attr_input.attr.mode = S_IRUGO; + sensor->dev_attr_input.show = scpi_show_sensor; + sensor->dev_attr_input.attr.name = sensor->input; + + sensor->dev_attr_label.attr.mode = S_IRUGO; + sensor->dev_attr_label.show = scpi_show_label; + sensor->dev_attr_label.attr.name = sensor->label; + + scpi_sensors->attrs[idx << 1] = &sensor->dev_attr_input.attr; + scpi_sensors->attrs[(idx << 1) + 1] = &sensor->dev_attr_label.attr; + + sysfs_attr_init(scpi_sensors->attrs[idx << 1]); + sysfs_attr_init(scpi_sensors->attrs[(idx << 1) + 1]); + idx++; + } + + scpi_sensors->group.attrs = scpi_sensors->attrs; + scpi_sensors->groups[0] = &scpi_sensors->group; + + platform_set_drvdata(pdev, scpi_sensors); + + hwdev = devm_hwmon_device_register_with_groups(dev, + "scpi_sensors", scpi_sensors, scpi_sensors->groups); + + if (IS_ERR(hwdev)) + return PTR_ERR(hwdev); + + /* + * Register the temperature sensors with the thermal framework + * to allow their usage in setting up the thermal zones from + * device tree. + * + * NOTE: Not all temperature sensors maybe used for thermal + * control + */ + INIT_LIST_HEAD(&scpi_sensors->thermal_zones); + for (i = 0; i < nr_sensors; i++) { + struct sensor_data *sensor = &scpi_sensors->data[i]; + struct scpi_thermal_zone *zone; + + if (sensor->info.class != TEMPERATURE) + continue; + + zone = devm_kzalloc(dev, sizeof(*zone), GFP_KERNEL); + if (!zone) { + ret = -ENOMEM; + goto unregister_tzd; + } + + zone->sensor_id = i; + zone->scpi_sensors = scpi_sensors; + zone->tzd = thermal_zone_of_sensor_register(dev, + sensor->info.sensor_id, zone, &scpi_sensor_ops); + /* + * The call to thermal_zone_of_sensor_register returns + * an error for sensors that are not associated with + * any thermal zones or if the thermal subsystem is + * not configured. + */ + if (IS_ERR(zone->tzd)) { + devm_kfree(dev, zone); + continue; + } + list_add(&zone->list, &scpi_sensors->thermal_zones); + } + + return 0; + +unregister_tzd: + unregister_thermal_zones(pdev, scpi_sensors); + return ret; +} + +static int scpi_hwmon_remove(struct platform_device *pdev) +{ + struct scpi_sensors *scpi_sensors = platform_get_drvdata(pdev); + + unregister_thermal_zones(pdev, scpi_sensors); + + return 0; +} + +static const struct of_device_id scpi_of_match[] = { + {.compatible = "arm,scpi-sensors"}, + {}, +}; + +static struct platform_driver scpi_hwmon_platdrv = { + .driver = { + .name = "scpi-hwmon", + .owner = THIS_MODULE, + .of_match_table = scpi_of_match, + }, + .probe = scpi_hwmon_probe, + .remove = scpi_hwmon_remove, +}; +module_platform_driver(scpi_hwmon_platdrv); + +MODULE_AUTHOR("Punit Agrawal <punit.agrawal@arm.com>"); +MODULE_DESCRIPTION("ARM SCPI HWMON interface driver"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/hwmon/sht15.c b/kernel/drivers/hwmon/sht15.c index d4f0935da..a2fdbb7d2 100644 --- a/kernel/drivers/hwmon/sht15.c +++ b/kernel/drivers/hwmon/sht15.c @@ -33,6 +33,7 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/atomic.h> +#include <linux/bitrev.h> /* Commands */ #define SHT15_MEASURE_TEMP 0x03 @@ -173,19 +174,6 @@ struct sht15_data { }; /** - * sht15_reverse() - reverse a byte - * @byte: byte to reverse. - */ -static u8 sht15_reverse(u8 byte) -{ - u8 i, c; - - for (c = 0, i = 0; i < 8; i++) - c |= (!!(byte & (1 << i))) << (7 - i); - return c; -} - -/** * sht15_crc8() - compute crc8 * @data: sht15 specific data. * @value: sht15 retrieved data. @@ -196,7 +184,7 @@ static u8 sht15_crc8(struct sht15_data *data, const u8 *value, int len) { - u8 crc = sht15_reverse(data->val_status & 0x0F); + u8 crc = bitrev8(data->val_status & 0x0F); while (len--) { crc = sht15_crc8_table[*value ^ crc]; @@ -477,7 +465,7 @@ static int sht15_update_status(struct sht15_data *data) if (data->checksumming) { sht15_ack(data); - dev_checksum = sht15_reverse(sht15_read_byte(data)); + dev_checksum = bitrev8(sht15_read_byte(data)); checksum_vals[0] = SHT15_READ_STATUS; checksum_vals[1] = status; data->checksum_ok = (sht15_crc8(data, checksum_vals, 2) @@ -864,7 +852,7 @@ static void sht15_bh_read_data(struct work_struct *work_s) */ if (sht15_ack(data)) goto wakeup; - dev_checksum = sht15_reverse(sht15_read_byte(data)); + dev_checksum = bitrev8(sht15_read_byte(data)); checksum_vals[0] = (data->state == SHT15_READING_TEMP) ? SHT15_MEASURE_TEMP : SHT15_MEASURE_RH; checksum_vals[1] = (u8) (val >> 8); @@ -1074,7 +1062,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/kernel/drivers/hwmon/tc74.c b/kernel/drivers/hwmon/tc74.c new file mode 100644 index 000000000..d95165158 --- /dev/null +++ b/kernel/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/kernel/drivers/hwmon/tmp102.c b/kernel/drivers/hwmon/tmp102.c index 9da2735f1..5289aa098 100644 --- a/kernel/drivers/hwmon/tmp102.c +++ b/kernel/drivers/hwmon/tmp102.c @@ -58,6 +58,7 @@ struct tmp102 { u16 config_orig; unsigned long last_update; int temp[3]; + bool first_time; }; /* convert left adjusted 13-bit TMP102 register value to milliCelsius */ @@ -93,15 +94,22 @@ static struct tmp102 *tmp102_update_device(struct device *dev) tmp102->temp[i] = tmp102_reg_to_mC(status); } tmp102->last_update = jiffies; + tmp102->first_time = false; } mutex_unlock(&tmp102->lock); return tmp102; } -static int tmp102_read_temp(void *dev, long *temp) +static int tmp102_read_temp(void *dev, int *temp) { struct tmp102 *tmp102 = tmp102_update_device(dev); + /* Is it too early even to return a conversion? */ + if (tmp102->first_time) { + dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__); + return -EAGAIN; + } + *temp = tmp102->temp[0]; return 0; @@ -114,6 +122,10 @@ static ssize_t tmp102_show_temp(struct device *dev, struct sensor_device_attribute *sda = to_sensor_dev_attr(attr); struct tmp102 *tmp102 = tmp102_update_device(dev); + /* Is it too early even to return a read? */ + if (tmp102->first_time) + return -EAGAIN; + return sprintf(buf, "%d\n", tmp102->temp[sda->index]); } @@ -207,7 +219,9 @@ static int tmp102_probe(struct i2c_client *client, status = -ENODEV; goto fail_restore_config; } - tmp102->last_update = jiffies - HZ; + tmp102->last_update = jiffies; + /* Mark that we are not ready with data until conversion is complete */ + tmp102->first_time = true; mutex_init(&tmp102->lock); hwmon_dev = hwmon_device_register_with_groups(dev, client->name, diff --git a/kernel/drivers/hwmon/w83627ehf.c b/kernel/drivers/hwmon/w83627ehf.c index b10353b31..697007afb 100644 --- a/kernel/drivers/hwmon/w83627ehf.c +++ b/kernel/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/kernel/drivers/hwmon/w83792d.c b/kernel/drivers/hwmon/w83792d.c index 4068db4d9..0a8bce726 100644 --- a/kernel/drivers/hwmon/w83792d.c +++ b/kernel/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 } }; |