From 7a18afe8097731b8ffb6cb5b2b3b418ded77c105 Mon Sep 17 00:00:00 2001 From: Akshay Bhat Date: Mon, 18 Apr 2016 15:47:53 -0400 Subject: [PATCH 01/35] hwmon: (ads7828) Enable internal reference On ads7828 the internal reference defaults to off upon power up. When using internal reference, it needs to be turned on and the voltage needs to settle before normal conversion cycle can be started. Hence perform a dummy read in the probe to enable the internal reference allowing the voltage to settle before performing a normal read. Without this fix, the first read from the ADC when using internal reference always returns incorrect data. Signed-off-by: Akshay Bhat Cc: stable@vger.kernel.org # v4.1+ Signed-off-by: Guenter Roeck --- drivers/hwmon/ads7828.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 6c99ee7bafa3..ee396ff167d9 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -120,6 +120,7 @@ static int ads7828_probe(struct i2c_client *client, unsigned int vref_mv = ADS7828_INT_VREF_MV; bool diff_input = false; bool ext_vref = false; + unsigned int regval; data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL); if (!data) @@ -154,6 +155,15 @@ static int ads7828_probe(struct i2c_client *client, if (!diff_input) data->cmd_byte |= ADS7828_CMD_SD_SE; + /* + * Datasheet specifies internal reference voltage is disabled by + * default. The internal reference voltage needs to be enabled and + * voltage needs to settle before getting valid ADC data. So perform a + * dummy read to enable the internal reference voltage. + */ + if (!ext_vref) + regmap_read(data->regmap, data->cmd_byte, ®val); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, ads7828_groups); From 04e1e70afec6bb2940aea0ac2e9cbaf7d643d5f4 Mon Sep 17 00:00:00 2001 From: Tiberiu Breana Date: Wed, 30 Mar 2016 19:16:24 +0300 Subject: [PATCH 02/35] hwmon: (max31722) Add support for MAX31722/MAX31723 temperature sensors Add basic support for the Maxim Integrated MAX31722/MAX31723 SPI temperature sensors / thermostats. Includes: - ACPI support; - raw temperature readings; - power management Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf Signed-off-by: Tiberiu Breana Signed-off-by: Guenter Roeck --- Documentation/hwmon/max31722 | 34 ++++++++ drivers/hwmon/Kconfig | 10 +++ drivers/hwmon/Makefile | 1 + drivers/hwmon/max31722.c | 165 +++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 Documentation/hwmon/max31722 create mode 100644 drivers/hwmon/max31722.c diff --git a/Documentation/hwmon/max31722 b/Documentation/hwmon/max31722 new file mode 100644 index 000000000000..090da84538c8 --- /dev/null +++ b/Documentation/hwmon/max31722 @@ -0,0 +1,34 @@ +Kernel driver max31722 +====================== + +Supported chips: + * Maxim Integrated MAX31722 + Prefix: 'max31722' + ACPI ID: MAX31722 + Addresses scanned: - + Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf + * Maxim Integrated MAX31723 + Prefix: 'max31723' + ACPI ID: MAX31723 + Addresses scanned: - + Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf + +Author: Tiberiu Breana + +Description +----------- + +This driver adds support for the Maxim Integrated MAX31722/MAX31723 thermometers +and thermostats running over an SPI interface. + +Usage Notes +----------- + +This driver uses ACPI to auto-detect devices. See ACPI IDs in the above section. + +Sysfs entries +------------- + +The following attribute is supported: + +temp1_input Measured temperature. Read-only. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5c2d13a687aa..bdfa39408996 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -821,6 +821,16 @@ config SENSORS_MAX197 This driver can also be built as a module. If so, the module will be called max197. +config SENSORS_MAX31722 +tristate "MAX31722 temperature sensor" + depends on SPI + help + Support for the Maxim Integrated MAX31722/MAX31723 digital + thermometers/thermostats operating over an SPI interface. + + This driver can also be built as a module. If so, the module + will be called max31722. + config SENSORS_MAX6639 tristate "Maxim MAX6639 sensor chip" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 58cc3acba7e7..2ef5b7c4c54f 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -112,6 +112,7 @@ obj-$(CONFIG_SENSORS_MAX16065) += max16065.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX1668) += max1668.o obj-$(CONFIG_SENSORS_MAX197) += max197.o +obj-$(CONFIG_SENSORS_MAX31722) += max31722.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o diff --git a/drivers/hwmon/max31722.c b/drivers/hwmon/max31722.c new file mode 100644 index 000000000000..30a100e70a0d --- /dev/null +++ b/drivers/hwmon/max31722.c @@ -0,0 +1,165 @@ +/* + * max31722 - hwmon driver for Maxim Integrated MAX31722/MAX31723 SPI + * digital thermometer and thermostats. + * + * Copyright (c) 2016, Intel Corporation. + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define MAX31722_REG_CFG 0x00 +#define MAX31722_REG_TEMP_LSB 0x01 + +#define MAX31722_MODE_CONTINUOUS 0x00 +#define MAX31722_MODE_STANDBY 0x01 +#define MAX31722_MODE_MASK 0xFE +#define MAX31722_RESOLUTION_12BIT 0x06 +#define MAX31722_WRITE_MASK 0x80 + +struct max31722_data { + struct device *hwmon_dev; + struct spi_device *spi_device; + u8 mode; +}; + +static int max31722_set_mode(struct max31722_data *data, u8 mode) +{ + int ret; + struct spi_device *spi = data->spi_device; + u8 buf[2] = { + MAX31722_REG_CFG | MAX31722_WRITE_MASK, + (data->mode & MAX31722_MODE_MASK) | mode + }; + + ret = spi_write(spi, &buf, sizeof(buf)); + if (ret < 0) { + dev_err(&spi->dev, "failed to set sensor mode.\n"); + return ret; + } + data->mode = (data->mode & MAX31722_MODE_MASK) | mode; + + return 0; +} + +static ssize_t max31722_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret; + struct max31722_data *data = dev_get_drvdata(dev); + + ret = spi_w8r16(data->spi_device, MAX31722_REG_TEMP_LSB); + if (ret < 0) + return ret; + /* Keep 12 bits and multiply by the scale of 62.5 millidegrees/bit. */ + return sprintf(buf, "%d\n", (s16)le16_to_cpu(ret) * 125 / 32); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, + max31722_show_temp, NULL, 0); + +static struct attribute *max31722_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(max31722); + +static int max31722_probe(struct spi_device *spi) +{ + int ret; + struct max31722_data *data; + + data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spi_set_drvdata(spi, data); + data->spi_device = spi; + /* + * Set SD bit to 0 so we can have continuous measurements. + * Set resolution to 12 bits for maximum precision. + */ + data->mode = MAX31722_MODE_CONTINUOUS | MAX31722_RESOLUTION_12BIT; + ret = max31722_set_mode(data, MAX31722_MODE_CONTINUOUS); + if (ret < 0) + return ret; + + data->hwmon_dev = hwmon_device_register_with_groups(&spi->dev, + spi->modalias, + data, + max31722_groups); + if (IS_ERR(data->hwmon_dev)) { + max31722_set_mode(data, MAX31722_MODE_STANDBY); + return PTR_ERR(data->hwmon_dev); + } + + return 0; +} + +static int max31722_remove(struct spi_device *spi) +{ + struct max31722_data *data = spi_get_drvdata(spi); + + hwmon_device_unregister(data->hwmon_dev); + + return max31722_set_mode(data, MAX31722_MODE_STANDBY); +} + +static int __maybe_unused max31722_suspend(struct device *dev) +{ + struct spi_device *spi_device = to_spi_device(dev); + struct max31722_data *data = spi_get_drvdata(spi_device); + + return max31722_set_mode(data, MAX31722_MODE_STANDBY); +} + +static int __maybe_unused max31722_resume(struct device *dev) +{ + struct spi_device *spi_device = to_spi_device(dev); + struct max31722_data *data = spi_get_drvdata(spi_device); + + return max31722_set_mode(data, MAX31722_MODE_CONTINUOUS); +} + +static SIMPLE_DEV_PM_OPS(max31722_pm_ops, max31722_suspend, max31722_resume); + +static const struct spi_device_id max31722_spi_id[] = { + {"max31722", 0}, + {"max31723", 0}, + {} +}; + +static const struct acpi_device_id __maybe_unused max31722_acpi_id[] = { + {"MAX31722", 0}, + {"MAX31723", 0}, + {} +}; + +MODULE_DEVICE_TABLE(spi, max31722_spi_id); + +static struct spi_driver max31722_driver = { + .driver = { + .name = "max31722", + .pm = &max31722_pm_ops, + .acpi_match_table = ACPI_PTR(max31722_acpi_id), + }, + .probe = max31722_probe, + .remove = max31722_remove, + .id_table = max31722_spi_id, +}; + +module_spi_driver(max31722_driver); + +MODULE_AUTHOR("Tiberiu Breana "); +MODULE_DESCRIPTION("max31722 sensor driver"); +MODULE_LICENSE("GPL v2"); From 3ba4e3841530b1565f761778bd0e14e242a4ab9a Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Wed, 6 Apr 2016 15:44:10 +0800 Subject: [PATCH 03/35] hwmon: (fam15h_power) Add CPU_SUP_AMD as the dependence This patch adds CONFIG_CPU_SUP_AMD as the dependence of fam15h_power driver. Because the following patch will use the interface from x86/kernel/cpu/amd.c. Otherwise, the below error might be encountered: All errors (new ones prefixed by >>): drivers/built-in.o: In function `fam15h_power_probe': >> fam15h_power.c:(.text+0x26e3a3): undefined reference to >> `amd_get_cores_per_cu' fam15h_power.c:(.text+0x26e41e): undefined reference to `amd_get_cores_per_cu' Reported-by: build test robot Acked-by: Borislav Petkov Signed-off-by: Huang Rui Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index bdfa39408996..1b5b500ede07 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -288,7 +288,7 @@ config SENSORS_K10TEMP config SENSORS_FAM15H_POWER tristate "AMD Family 15h processor power" - depends on X86 && PCI + depends on X86 && PCI && CPU_SUP_AMD help If you say yes here you get support for processor power information of your AMD family 15h CPU. From fa7943449943124e40cabcd453b08c3f8221c454 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Wed, 6 Apr 2016 15:44:11 +0800 Subject: [PATCH 04/35] hwmon: (fam15h_power) Add compute unit accumulated power This patch adds a member in fam15h_power_data which specifies the compute unit accumulated power. It adds do_read_registers_on_cu to do all the read to all MSRs and run it on one of the online cores on each compute unit with smp_call_function_many(). This behavior can decrease IPI numbers. Suggested-by: Borislav Petkov Signed-off-by: Huang Rui Signed-off-by: Guenter Roeck --- drivers/hwmon/fam15h_power.c | 72 +++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 4f695d8fcafa..4edbaf083130 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -44,7 +46,9 @@ MODULE_LICENSE("GPL"); #define FAM15H_MIN_NUM_ATTRS 2 #define FAM15H_NUM_GROUPS 2 +#define MAX_CUS 8 +#define MSR_F15H_CU_PWR_ACCUMULATOR 0xc001007a #define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b #define PCI_DEVICE_ID_AMD_15H_M70H_NB_F4 0x15b4 @@ -59,6 +63,8 @@ struct fam15h_power_data { struct attribute_group group; /* maximum accumulated power of a compute unit */ u64 max_cu_acc_power; + /* accumulated power of the compute units */ + u64 cu_acc_power[MAX_CUS]; }; static ssize_t show_power(struct device *dev, @@ -125,6 +131,70 @@ static ssize_t show_power_crit(struct device *dev, } static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL); +static void do_read_registers_on_cu(void *_data) +{ + struct fam15h_power_data *data = _data; + int cpu, cu; + + cpu = smp_processor_id(); + + /* + * With the new x86 topology modelling, cpu core id actually + * is compute unit id. + */ + cu = cpu_data(cpu).cpu_core_id; + + rdmsrl_safe(MSR_F15H_CU_PWR_ACCUMULATOR, &data->cu_acc_power[cu]); +} + +/* + * This function is only able to be called when CPUID + * Fn8000_0007:EDX[12] is set. + */ +static int read_registers(struct fam15h_power_data *data) +{ + int this_cpu, ret, cpu; + int core, this_core; + cpumask_var_t mask; + + ret = zalloc_cpumask_var(&mask, GFP_KERNEL); + if (!ret) + return -ENOMEM; + + get_online_cpus(); + this_cpu = smp_processor_id(); + + /* + * Choose the first online core of each compute unit, and then + * read their MSR value of power and ptsc in a single IPI, + * because the MSR value of CPU core represent the compute + * unit's. + */ + core = -1; + + for_each_online_cpu(cpu) { + this_core = topology_core_id(cpu); + + if (this_core == core) + continue; + + core = this_core; + + /* get any CPU on this compute unit */ + cpumask_set_cpu(cpumask_any(topology_sibling_cpumask(cpu)), mask); + } + + if (cpumask_test_cpu(this_cpu, mask)) + do_read_registers_on_cu(data); + + smp_call_function_many(mask, do_read_registers_on_cu, data, true); + put_online_cpus(); + + free_cpumask_var(mask); + + return 0; +} + static int fam15h_power_init_attrs(struct pci_dev *pdev, struct fam15h_power_data *data) { @@ -263,7 +333,7 @@ static int fam15h_power_init_data(struct pci_dev *f4, data->max_cu_acc_power = tmp; - return 0; + return read_registers(data); } static int fam15h_power_probe(struct pci_dev *pdev, From cdb9e110b10a08b7e1371356c2c03c73eb4f93d5 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Wed, 6 Apr 2016 15:44:12 +0800 Subject: [PATCH 05/35] hwmon: (fam15h_power) Add ptsc counter value for accumulated power PTSC is the performance timestamp counter value in a cpu core and the cores in one compute unit have the fixed frequency. So it picks up the performance timestamp counter value of the first core per compute unit to measure the interval for average power per compute unit. Signed-off-by: Huang Rui Cc: Borislav Petkov Signed-off-by: Guenter Roeck --- drivers/hwmon/fam15h_power.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 4edbaf083130..336d422fb863 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -50,6 +50,7 @@ MODULE_LICENSE("GPL"); #define MSR_F15H_CU_PWR_ACCUMULATOR 0xc001007a #define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b +#define MSR_F15H_PTSC 0xc0010280 #define PCI_DEVICE_ID_AMD_15H_M70H_NB_F4 0x15b4 @@ -65,6 +66,8 @@ struct fam15h_power_data { u64 max_cu_acc_power; /* accumulated power of the compute units */ u64 cu_acc_power[MAX_CUS]; + /* performance timestamp counter */ + u64 cpu_sw_pwr_ptsc[MAX_CUS]; }; static ssize_t show_power(struct device *dev, @@ -145,6 +148,7 @@ static void do_read_registers_on_cu(void *_data) cu = cpu_data(cpu).cpu_core_id; rdmsrl_safe(MSR_F15H_CU_PWR_ACCUMULATOR, &data->cu_acc_power[cu]); + rdmsrl_safe(MSR_F15H_PTSC, &data->cpu_sw_pwr_ptsc[cu]); } /* From 11bf0d78ccc4b2944aafd22ff05cd7e413ffea57 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Wed, 6 Apr 2016 15:44:13 +0800 Subject: [PATCH 06/35] hwmon: (fam15h_power) Introduce a cpu accumulated power reporting algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces an algorithm that computes the average power by reading a delta value of “core power accumulator” register during measurement interval, and then dividing delta value by the length of the time interval. User is able to use power1_average entry to measure the processor power consumption and power1_average_interval entry to set the interval. A simple example: ray@hr-ub:~/tip$ sensors fam15h_power-pci-00c4 Adapter: PCI adapter power1: 19.58 mW (avg = 2.55 mW, interval = 0.01 s) (crit = 15.00 W) ... The result is current average processor power consumption in 10 millisecond. The unit of the result is uWatt. Suggested-by: Guenter Roeck Signed-off-by: Huang Rui Cc: Borislav Petkov Signed-off-by: Guenter Roeck --- drivers/hwmon/fam15h_power.c | 128 +++++++++++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 336d422fb863..5abbfa89fb18 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -48,6 +50,9 @@ MODULE_LICENSE("GPL"); #define FAM15H_NUM_GROUPS 2 #define MAX_CUS 8 +/* set maximum interval as 1 second */ +#define MAX_INTERVAL 1000 + #define MSR_F15H_CU_PWR_ACCUMULATOR 0xc001007a #define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b #define MSR_F15H_PTSC 0xc0010280 @@ -68,6 +73,9 @@ struct fam15h_power_data { u64 cu_acc_power[MAX_CUS]; /* performance timestamp counter */ u64 cpu_sw_pwr_ptsc[MAX_CUS]; + /* online/offline status of current compute unit */ + int cu_on[MAX_CUS]; + unsigned long power_period; }; static ssize_t show_power(struct device *dev, @@ -149,6 +157,8 @@ static void do_read_registers_on_cu(void *_data) rdmsrl_safe(MSR_F15H_CU_PWR_ACCUMULATOR, &data->cu_acc_power[cu]); rdmsrl_safe(MSR_F15H_PTSC, &data->cpu_sw_pwr_ptsc[cu]); + + data->cu_on[cu] = 1; } /* @@ -165,6 +175,8 @@ static int read_registers(struct fam15h_power_data *data) if (!ret) return -ENOMEM; + memset(data->cu_on, 0, sizeof(int) * MAX_CUS); + get_online_cpus(); this_cpu = smp_processor_id(); @@ -199,6 +211,98 @@ static int read_registers(struct fam15h_power_data *data) return 0; } +static ssize_t acc_show_power(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fam15h_power_data *data = dev_get_drvdata(dev); + u64 prev_cu_acc_power[MAX_CUS], prev_ptsc[MAX_CUS], + jdelta[MAX_CUS]; + u64 tdelta, avg_acc; + int cu, cu_num, ret; + signed long leftover; + + /* + * With the new x86 topology modelling, x86_max_cores is the + * compute unit number. + */ + cu_num = boot_cpu_data.x86_max_cores; + + ret = read_registers(data); + if (ret) + return 0; + + for (cu = 0; cu < cu_num; cu++) { + prev_cu_acc_power[cu] = data->cu_acc_power[cu]; + prev_ptsc[cu] = data->cpu_sw_pwr_ptsc[cu]; + } + + leftover = schedule_timeout_interruptible(msecs_to_jiffies(data->power_period)); + if (leftover) + return 0; + + ret = read_registers(data); + if (ret) + return 0; + + for (cu = 0, avg_acc = 0; cu < cu_num; cu++) { + /* check if current compute unit is online */ + if (data->cu_on[cu] == 0) + continue; + + if (data->cu_acc_power[cu] < prev_cu_acc_power[cu]) { + jdelta[cu] = data->max_cu_acc_power + data->cu_acc_power[cu]; + jdelta[cu] -= prev_cu_acc_power[cu]; + } else { + jdelta[cu] = data->cu_acc_power[cu] - prev_cu_acc_power[cu]; + } + tdelta = data->cpu_sw_pwr_ptsc[cu] - prev_ptsc[cu]; + jdelta[cu] *= data->cpu_pwr_sample_ratio * 1000; + do_div(jdelta[cu], tdelta); + + /* the unit is microWatt */ + avg_acc += jdelta[cu]; + } + + return sprintf(buf, "%llu\n", (unsigned long long)avg_acc); +} +static DEVICE_ATTR(power1_average, S_IRUGO, acc_show_power, NULL); + +static ssize_t acc_show_power_period(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fam15h_power_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%lu\n", data->power_period); +} + +static ssize_t acc_set_power_period(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fam15h_power_data *data = dev_get_drvdata(dev); + unsigned long temp; + int ret; + + ret = kstrtoul(buf, 10, &temp); + if (ret) + return ret; + + if (temp > MAX_INTERVAL) + return -EINVAL; + + /* the interval value should be greater than 0 */ + if (temp <= 0) + return -EINVAL; + + data->power_period = temp; + + return count; +} +static DEVICE_ATTR(power1_average_interval, S_IRUGO | S_IWUSR, + acc_show_power_period, acc_set_power_period); + static int fam15h_power_init_attrs(struct pci_dev *pdev, struct fam15h_power_data *data) { @@ -211,6 +315,10 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev, (c->x86_model >= 0x60 && c->x86_model <= 0x7f))) n += 1; + /* check if processor supports accumulated power */ + if (boot_cpu_has(X86_FEATURE_ACC_POWER)) + n += 2; + fam15h_power_attrs = devm_kcalloc(&pdev->dev, n, sizeof(*fam15h_power_attrs), GFP_KERNEL); @@ -225,6 +333,11 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev, (c->x86_model >= 0x60 && c->x86_model <= 0x7f))) fam15h_power_attrs[n++] = &dev_attr_power1_input.attr; + if (boot_cpu_has(X86_FEATURE_ACC_POWER)) { + fam15h_power_attrs[n++] = &dev_attr_power1_average.attr; + fam15h_power_attrs[n++] = &dev_attr_power1_average_interval.attr; + } + data->group.attrs = fam15h_power_attrs; return 0; @@ -290,7 +403,7 @@ static int fam15h_power_resume(struct pci_dev *pdev) static int fam15h_power_init_data(struct pci_dev *f4, struct fam15h_power_data *data) { - u32 val, eax, ebx, ecx, edx; + u32 val; u64 tmp; int ret; @@ -317,10 +430,9 @@ static int fam15h_power_init_data(struct pci_dev *f4, if (ret) return ret; - cpuid(0x80000007, &eax, &ebx, &ecx, &edx); /* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */ - if (!(edx & BIT(12))) + if (!boot_cpu_has(X86_FEATURE_ACC_POWER)) return 0; /* @@ -328,7 +440,7 @@ static int fam15h_power_init_data(struct pci_dev *f4, * sample period to the PTSC counter period by executing CPUID * Fn8000_0007:ECX */ - data->cpu_pwr_sample_ratio = ecx; + data->cpu_pwr_sample_ratio = cpuid_ecx(0x80000007); if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) { pr_err("Failed to read max compute unit power accumulator MSR\n"); @@ -337,6 +449,14 @@ static int fam15h_power_init_data(struct pci_dev *f4, data->max_cu_acc_power = tmp; + /* + * Milliseconds are a reasonable interval for the measurement. + * But it shouldn't set too long here, because several seconds + * would cause the read function to hang. So set default + * interval as 10 ms. + */ + data->power_period = 10; + return read_registers(data); } From a6e232f78698953e6fd9c2f4c95e16d5f5f9d0c3 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Wed, 6 Apr 2016 15:44:14 +0800 Subject: [PATCH 07/35] hwmon: (fam15h_power) Add documentation for TDP and accumulated power algorithm This patch adds the description to explain the TDP reporting mechanism and accumulated power algorithm. Signed-off-by: Huang Rui Cc: Borislav Petkov Signed-off-by: Guenter Roeck --- Documentation/hwmon/fam15h_power | 65 +++++++++++++++++++++++++++++++- drivers/hwmon/fam15h_power.c | 2 +- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/fam15h_power b/Documentation/hwmon/fam15h_power index e2b1b69eebea..fb594c281c46 100644 --- a/Documentation/hwmon/fam15h_power +++ b/Documentation/hwmon/fam15h_power @@ -10,14 +10,22 @@ Supported chips: Datasheets: BIOS and Kernel Developer's Guide (BKDG) For AMD Family 15h Processors BIOS and Kernel Developer's Guide (BKDG) For AMD Family 16h Processors + AMD64 Architecture Programmer's Manual Volume 2: System Programming Author: Andreas Herrmann Description ----------- +1) Processor TDP (Thermal design power) + +Given a fixed frequency and voltage, the power consumption of a +processor varies based on the workload being executed. Derated power +is the power consumed when running a specific application. Thermal +design power (TDP) is an example of derated power. + This driver permits reading of registers providing power information -of AMD Family 15h and 16h processors. +of AMD Family 15h and 16h processors via TDP algorithm. For AMD Family 15h and 16h processors the following power values can be calculated using different processor northbridge function @@ -37,3 +45,58 @@ This driver provides ProcessorPwrWatts and CurrPwrWatts: On multi-node processors the calculated value is for the entire package and not for a single node. Thus the driver creates sysfs attributes only for internal node0 of a multi-node processor. + +2) Accumulated Power Mechanism + +This driver also introduces an algorithm that should be used to +calculate the average power consumed by a processor during a +measurement interval Tm. The feature of accumulated power mechanism is +indicated by CPUID Fn8000_0007_EDX[12]. + +* Tsample: compute unit power accumulator sample period +* Tref: the PTSC counter period +* PTSC: performance timestamp counter +* N: the ratio of compute unit power accumulator sample period to the + PTSC period +* Jmax: max compute unit accumulated power which is indicated by + MaxCpuSwPwrAcc MSR C001007b +* Jx/Jy: compute unit accumulated power which is indicated by + CpuSwPwrAcc MSR C001007a +* Tx/Ty: the value of performance timestamp counter which is indicated + by CU_PTSC MSR C0010280 +* PwrCPUave: CPU average power + +i. Determine the ratio of Tsample to Tref by executing CPUID Fn8000_0007. + N = value of CPUID Fn8000_0007_ECX[CpuPwrSampleTimeRatio[15:0]]. + +ii. Read the full range of the cumulative energy value from the new +MSR MaxCpuSwPwrAcc. + Jmax = value returned. +iii. At time x, SW reads CpuSwPwrAcc MSR and samples the PTSC. + Jx = value read from CpuSwPwrAcc and Tx = value read from +PTSC. + +iv. At time y, SW reads CpuSwPwrAcc MSR and samples the PTSC. + Jy = value read from CpuSwPwrAcc and Ty = value read from +PTSC. + +v. Calculate the average power consumption for a compute unit over +time period (y-x). Unit of result is uWatt. + if (Jy < Jx) // Rollover has occurred + Jdelta = (Jy + Jmax) - Jx + else + Jdelta = Jy - Jx + PwrCPUave = N * Jdelta * 1000 / (Ty - Tx) + +This driver provides PwrCPUave and interval(default is 10 millisecond +and maximum is 1 second): +* power1_average (PwrCPUave) +* power1_average_interval (Interval) + +The power1_average_interval can be updated at /etc/sensors3.conf file +as below: + +chip "fam15h_power-*" + set power1_average_interval 0.01 + +Then save it with "sensors -s". diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 5abbfa89fb18..7d9d6976a575 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -1,7 +1,7 @@ /* * fam15h_power.c - AMD Family 15h processor power monitoring * - * Copyright (c) 2011 Advanced Micro Devices, Inc. + * Copyright (c) 2011-2016 Advanced Micro Devices, Inc. * Author: Andreas Herrmann * * From 1d28e01628aebab8fe403e7e9d0760f3787763d5 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Wed, 6 Apr 2016 15:44:15 +0800 Subject: [PATCH 08/35] hwmon: (fam15h_power) Add platform check function This patch adds a platform check function to make code more readable. Signed-off-by: Huang Rui Signed-off-by: Guenter Roeck --- drivers/hwmon/fam15h_power.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 7d9d6976a575..eb97a9241d17 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -78,6 +78,11 @@ struct fam15h_power_data { unsigned long power_period; }; +static bool is_carrizo_or_later(void) +{ + return boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model >= 0x60; +} + static ssize_t show_power(struct device *dev, struct device_attribute *attr, char *buf) { @@ -94,7 +99,7 @@ static ssize_t show_power(struct device *dev, * 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) { + if (is_carrizo_or_later()) { running_avg_capture = val >> 4; running_avg_capture = sign_extend32(running_avg_capture, 27); } else { @@ -111,7 +116,7 @@ static ssize_t show_power(struct device *dev, * On Carrizo and later platforms, ApmTdpLimit bit field * is extended to 16:31 from 16:28. */ - if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model >= 0x60) + if (is_carrizo_or_later()) tdp_limit = val >> 16; else tdp_limit = (val >> 16) & 0x1fff; From 11b8360851c22cdc0f939be56e28ce348bc3056f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 18 Jan 2016 00:34:53 -0800 Subject: [PATCH 09/35] hwmon: (ltc2978) Add missing devicetree binding for LTM4675 Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/ltc2978.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/hwmon/ltc2978.txt b/Documentation/devicetree/bindings/hwmon/ltc2978.txt index a7afbf60bb9c..bf2a47bbdc58 100644 --- a/Documentation/devicetree/bindings/hwmon/ltc2978.txt +++ b/Documentation/devicetree/bindings/hwmon/ltc2978.txt @@ -13,6 +13,7 @@ Required properties: * "lltc,ltc3886" * "lltc,ltc3887" * "lltc,ltm2987" + * "lltc,ltm4675" * "lltc,ltm4676" - reg: I2C slave address From 730554059bf26ab7a4f2cab8ed96e840a03d9b40 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 26 Mar 2015 09:16:32 -0700 Subject: [PATCH 10/35] hwmon: (it87) Add feature flag for AVCC3 support AVCC3 is supported on IT8620E, similar to IT8603E. Add feature flag to indicate AVCC3 support. Don't enable it for now on IT8620E since it is unclear if this chip supports it correctly. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 1896e26df634..146f93584cde 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -233,6 +233,8 @@ static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; #define IT87_REG_VIN(nr) (0x20 + (nr)) #define IT87_REG_TEMP(nr) (0x29 + (nr)) +#define IT87_REG_AVCC3 0x2f + #define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2) #define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2) #define IT87_REG_TEMP_HIGH(nr) (0x40 + (nr) * 2) @@ -269,6 +271,7 @@ struct it87_devices { #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) +#define FEAT_AVCC3 (1 << 13) /* Chip supports in9/AVCC3 */ static const struct it87_devices it87_devices[] = { [it87] = { @@ -389,7 +392,8 @@ static const struct it87_devices it87_devices[] = { .name = "it8603", .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL + | FEAT_AVCC3, .peci_mask = 0x07, }, [it8620] = { @@ -419,6 +423,7 @@ static const struct it87_devices it87_devices[] = { #define has_vid(data) ((data)->features & FEAT_VID) #define has_in7_internal(data) ((data)->features & FEAT_IN7_INTERNAL) #define has_six_fans(data) ((data)->features & FEAT_SIX_FANS) +#define has_avcc3(data) ((data)->features & FEAT_AVCC3) struct it87_sio_data { enum chips type; @@ -1548,7 +1553,7 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr, static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0); static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1); static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2); -/* special AVCC3 IT8603E in9 */ +/* AVCC3 */ static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 0); static ssize_t show_name(struct device *dev, struct device_attribute @@ -1944,8 +1949,10 @@ static int __init it87_find(unsigned short *address, /* in8 (Vbat) is always internal */ sio_data->internal |= (1 << 2); - /* Only the IT8603E has in9 */ - if (sio_data->type != it8603) + /* in9 (AVCC3), always internal if supported */ + if (has_avcc3(config)) + sio_data->internal |= (1 << 3); /* in9 is AVCC */ + else sio_data->skip_in |= (1 << 9); if (!has_vid(config)) @@ -2044,8 +2051,6 @@ static int __init it87_find(unsigned short *address, sio_data->skip_in |= (1 << 5); /* No VIN5 */ sio_data->skip_in |= (1 << 6); /* No VIN6 */ - sio_data->internal |= (1 << 3); /* in9 is AVCC */ - sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } else if (sio_data->type == it8620) { int reg; @@ -2689,8 +2694,8 @@ static struct it87_data *it87_update_device(struct device *dev) } /* in8 (battery) has no limit registers */ data->in[8][0] = it87_read_value(data, IT87_REG_VIN(8)); - if (data->type == it8603) - data->in[9][0] = it87_read_value(data, 0x2f); + if (has_avcc3(data)) + data->in[9][0] = it87_read_value(data, IT87_REG_AVCC3); for (i = 0; i < 6; i++) { /* Skip disabled fans */ From 36c4d98a7883d4c51252d0f4ebf2c667fa7f879f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 26 Mar 2015 18:18:19 -0700 Subject: [PATCH 11/35] hwmon: (it87) Add support for all pwm channels on IT8620E IT8620E supports up to 6 pwm channels. Add support for it. Also check if fan tachometers 4..6 are enabled before instantiating the respective sysfs attributes. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 114 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 20 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 146f93584cde..916d73630224 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -168,6 +168,7 @@ static inline void superio_exit(void) #define IT87_SIO_GPIO1_REG 0x25 #define IT87_SIO_GPIO2_REG 0x26 #define IT87_SIO_GPIO3_REG 0x27 +#define IT87_SIO_GPIO4_REG 0x28 #define IT87_SIO_GPIO5_REG 0x29 #define IT87_SIO_PINX1_REG 0x2a /* Pin selection */ #define IT87_SIO_PINX2_REG 0x2c /* Pin selection */ @@ -227,8 +228,8 @@ static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; #define IT87_REG_FAN_MAIN_CTRL 0x13 #define IT87_REG_FAN_CTL 0x14 -#define IT87_REG_PWM(nr) (0x15 + (nr)) -#define IT87_REG_PWM_DUTY(nr) (0x63 + (nr) * 8) +static const u8 IT87_REG_PWM[] = { 0x15, 0x16, 0x17, 0x7f, 0xa7, 0xaf }; +static const u8 IT87_REG_PWM_DUTY[] = { 0x63, 0x6b, 0x73, 0x7b, 0xa3, 0xab }; #define IT87_REG_VIN(nr) (0x20 + (nr)) #define IT87_REG_TEMP(nr) (0x29 + (nr)) @@ -272,6 +273,7 @@ struct it87_devices { #define FEAT_SIX_FANS (1 << 11) /* Supports six fans */ #define FEAT_10_9MV_ADC (1 << 12) #define FEAT_AVCC3 (1 << 13) /* Chip supports in9/AVCC3 */ +#define FEAT_SIX_PWM (1 << 14) /* Chip supports 6 pwm chn */ static const struct it87_devices it87_devices[] = { [it87] = { @@ -401,7 +403,7 @@ static const struct it87_devices it87_devices[] = { .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS - | FEAT_IN7_INTERNAL, + | FEAT_IN7_INTERNAL | FEAT_SIX_PWM, .peci_mask = 0x07, }, }; @@ -424,6 +426,7 @@ static const struct it87_devices it87_devices[] = { #define has_in7_internal(data) ((data)->features & FEAT_IN7_INTERNAL) #define has_six_fans(data) ((data)->features & FEAT_SIX_FANS) #define has_avcc3(data) ((data)->features & FEAT_AVCC3) +#define has_six_pwm(data) ((data)->features & FEAT_SIX_PWM) struct it87_sio_data { enum chips type; @@ -483,9 +486,9 @@ struct it87_data { * is no longer needed, but it is still done to keep the driver * simple. */ - u8 pwm_ctrl[3]; /* Register value */ - u8 pwm_duty[3]; /* Manual PWM value set by user */ - u8 pwm_temp_map[3]; /* PWM to temp. chan. mapping (bits 1-0) */ + u8 pwm_ctrl[6]; /* Register value */ + u8 pwm_duty[6]; /* Manual PWM value set by user */ + u8 pwm_temp_map[6]; /* PWM to temp. chan. mapping (bits 1-0) */ /* Automatic fan speed control registers */ u8 auto_pwm[3][4]; /* [nr][3] is hard-coded */ @@ -1068,7 +1071,7 @@ static ssize_t set_pwm_enable(struct device *dev, data->pwm_duty[nr]; else /* Automatic mode */ data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr]; - it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]); + it87_write_value(data, IT87_REG_PWM[nr], data->pwm_ctrl[nr]); if (data->type != it8603) { /* set SmartGuardian mode */ @@ -1104,7 +1107,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, return -EBUSY; } data->pwm_duty[nr] = pwm_to_reg(data, val); - it87_write_value(data, IT87_REG_PWM_DUTY(nr), + it87_write_value(data, IT87_REG_PWM_DUTY[nr], data->pwm_duty[nr]); } else { data->pwm_duty[nr] = pwm_to_reg(data, val); @@ -1114,7 +1117,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, */ if (!(data->pwm_ctrl[nr] & 0x80)) { data->pwm_ctrl[nr] = data->pwm_duty[nr]; - it87_write_value(data, IT87_REG_PWM(nr), + it87_write_value(data, IT87_REG_PWM[nr], data->pwm_ctrl[nr]); } } @@ -1207,7 +1210,7 @@ static ssize_t set_pwm_temp_map(struct device *dev, */ if (data->pwm_ctrl[nr] & 0x80) { data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr]; - it87_write_value(data, IT87_REG_PWM(nr), data->pwm_ctrl[nr]); + it87_write_value(data, IT87_REG_PWM[nr], data->pwm_ctrl[nr]); } mutex_unlock(&data->update_lock); return count; @@ -1385,6 +1388,27 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, 2, 4); +static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR, + show_pwm_enable, set_pwm_enable, 3); +static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 3); +static DEVICE_ATTR(pwm4_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq); +static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IRUGO | S_IWUSR, + show_pwm_temp_map, set_pwm_temp_map, 3); + +static SENSOR_DEVICE_ATTR(pwm5_enable, S_IRUGO | S_IWUSR, + show_pwm_enable, set_pwm_enable, 4); +static SENSOR_DEVICE_ATTR(pwm5, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 4); +static DEVICE_ATTR(pwm5_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq); +static SENSOR_DEVICE_ATTR(pwm5_auto_channels_temp, S_IRUGO | S_IWUSR, + show_pwm_temp_map, set_pwm_temp_map, 4); + +static SENSOR_DEVICE_ATTR(pwm6_enable, S_IRUGO | S_IWUSR, + show_pwm_enable, set_pwm_enable, 5); +static SENSOR_DEVICE_ATTR(pwm6, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 5); +static DEVICE_ATTR(pwm6_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq); +static SENSOR_DEVICE_ATTR(pwm6_auto_channels_temp, S_IRUGO | S_IWUSR, + show_pwm_temp_map, set_pwm_temp_map, 5); + /* Alarms */ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) @@ -1747,7 +1771,7 @@ static const struct attribute *it87_attributes_fan_div[] = { &sensor_dev_attr_fan3_div.dev_attr.attr, }; -static struct attribute *it87_attributes_pwm[3][4+1] = { { +static struct attribute *it87_attributes_pwm[6][4+1] = { { &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, &dev_attr_pwm1_freq.attr, @@ -1765,12 +1789,33 @@ static struct attribute *it87_attributes_pwm[3][4+1] = { { &dev_attr_pwm3_freq.attr, &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, NULL +}, { + &sensor_dev_attr_pwm4_enable.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + &dev_attr_pwm4_freq.attr, + &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm5_enable.dev_attr.attr, + &sensor_dev_attr_pwm5.dev_attr.attr, + &dev_attr_pwm5_freq.attr, + &sensor_dev_attr_pwm5_auto_channels_temp.dev_attr.attr, + NULL +}, { + &sensor_dev_attr_pwm6_enable.dev_attr.attr, + &sensor_dev_attr_pwm6.dev_attr.attr, + &dev_attr_pwm6_freq.attr, + &sensor_dev_attr_pwm6_auto_channels_temp.dev_attr.attr, + NULL } }; -static const struct attribute_group it87_group_pwm[3] = { +static const struct attribute_group it87_group_pwm[6] = { { .attrs = it87_attributes_pwm[0] }, { .attrs = it87_attributes_pwm[1] }, { .attrs = it87_attributes_pwm[2] }, + { .attrs = it87_attributes_pwm[3] }, + { .attrs = it87_attributes_pwm[4] }, + { .attrs = it87_attributes_pwm[5] }, }; static struct attribute *it87_attributes_autopwm[3][9+1] = { { @@ -1955,6 +2000,9 @@ static int __init it87_find(unsigned short *address, else sio_data->skip_in |= (1 << 9); + if (!has_six_pwm(config)) + sio_data->skip_pwm |= (1 << 3) | (1 << 4) | (1 << 5); + if (!has_vid(config)) sio_data->skip_vid = 1; @@ -2057,6 +2105,11 @@ static int __init it87_find(unsigned short *address, superio_select(GPIO); + /* Check for pwm5 */ + reg = superio_inb(IT87_SIO_GPIO1_REG); + if (reg & (1 << 6)) + sio_data->skip_pwm |= (1 << 4); + /* Check for fan4, fan5 */ reg = superio_inb(IT87_SIO_GPIO2_REG); if (!(reg & (1 << 5))) @@ -2071,12 +2124,22 @@ static int __init it87_find(unsigned short *address, if (reg & (1 << 7)) sio_data->skip_fan |= (1 << 2); + /* Check for pwm4 */ + reg = superio_inb(IT87_SIO_GPIO4_REG); + if (!(reg & (1 << 2))) + sio_data->skip_pwm |= (1 << 3); + /* Check for pwm2, fan2 */ reg = superio_inb(IT87_SIO_GPIO5_REG); if (reg & (1 << 1)) sio_data->skip_pwm |= (1 << 1); if (reg & (1 << 2)) sio_data->skip_fan |= (1 << 1); + /* Check for pwm6, fan6 */ + if (!(reg & (1 << 7))) { + sio_data->skip_pwm |= (1 << 5); + sio_data->skip_fan |= (1 << 5); + } sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } else { @@ -2219,7 +2282,7 @@ static void it87_remove_files(struct device *dev) sysfs_remove_file(&dev->kobj, it87_attributes_fan_div[i]); } - for (i = 0; i < 3; i++) { + for (i = 0; i < 6; i++) { if (sio_data->skip_pwm & (1 << i)) continue; sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]); @@ -2402,7 +2465,7 @@ static int it87_probe(struct platform_device *pdev) } if (enable_pwm_interface) { - for (i = 0; i < 3; i++) { + for (i = 0; i < 6; i++) { if (sio_data->skip_pwm & (1 << i)) continue; err = sysfs_create_group(&dev->kobj, @@ -2505,7 +2568,7 @@ static int it87_check_pwm(struct device *dev) for (i = 0; i < 3; i++) pwm[i] = it87_read_value(data, - IT87_REG_PWM(i)); + IT87_REG_PWM[i]); /* * If any fan is in automatic pwm mode, the polarity @@ -2520,7 +2583,7 @@ static int it87_check_pwm(struct device *dev) tmp | 0x87); for (i = 0; i < 3; i++) it87_write_value(data, - IT87_REG_PWM(i), + IT87_REG_PWM[i], 0x7f & ~pwm[i]); return 1; } @@ -2635,6 +2698,16 @@ static void it87_init_device(struct platform_device *pdev) /* Fan input pins may be used for alternative functions */ data->has_fan &= ~sio_data->skip_fan; + /* Check if pwm5, pwm6 are enabled */ + if (has_six_pwm(data)) { + /* The following code may be IT8620E specific */ + tmp = it87_read_value(data, IT87_REG_FAN_DIV); + if ((tmp & 0xc0) == 0xc0) + sio_data->skip_pwm |= (1 << 4); + if (!(tmp & (1 << 3))) + sio_data->skip_pwm |= (1 << 5); + } + /* Start monitoring */ it87_write_value(data, IT87_REG_CONFIG, (it87_read_value(data, IT87_REG_CONFIG) & 0x3e) @@ -2643,11 +2716,12 @@ static void it87_init_device(struct platform_device *pdev) static void it87_update_pwm_ctrl(struct it87_data *data, int nr) { - data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM(nr)); + data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM[nr]); if (has_newer_autopwm(data)) { - data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; + data->pwm_temp_map[nr] = (data->pwm_ctrl[nr] & 0x03) + + nr < 3 ? 0 : 3; data->pwm_duty[nr] = it87_read_value(data, - IT87_REG_PWM_DUTY(nr)); + IT87_REG_PWM_DUTY[nr]); } else { if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */ data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; @@ -2746,7 +2820,7 @@ static struct it87_data *it87_update_device(struct device *dev) data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL); - for (i = 0; i < 3; i++) + for (i = 0; i < 6; i++) it87_update_pwm_ctrl(data, i); data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); From 60878bcfd3dd2ea146dacf41313f8caa365df9a1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 26 Mar 2015 19:57:42 -0700 Subject: [PATCH 12/35] hwmon: (it87) Add support for second pwm frequency register Recent chips have a separate register to select the pwm2 frequency. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 100 +++++++++++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 31 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 916d73630224..68c8d98e711a 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -274,6 +274,7 @@ struct it87_devices { #define FEAT_10_9MV_ADC (1 << 12) #define FEAT_AVCC3 (1 << 13) /* Chip supports in9/AVCC3 */ #define FEAT_SIX_PWM (1 << 14) /* Chip supports 6 pwm chn */ +#define FEAT_PWM_FREQ2 (1 << 15) /* Separate pwm freq 2 */ static const struct it87_devices it87_devices[] = { [it87] = { @@ -291,20 +292,22 @@ static const struct it87_devices it87_devices[] = { .name = "it8716", .suffix = "F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID - | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS, + | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_PWM_FREQ2, }, [it8718] = { .name = "it8718", .suffix = "F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID - | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS, + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS + | FEAT_PWM_FREQ2, .old_peci_mask = 0x4, }, [it8720] = { .name = "it8720", .suffix = "F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID - | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS, + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS + | FEAT_PWM_FREQ2, .old_peci_mask = 0x4, }, [it8721] = { @@ -312,7 +315,8 @@ static const struct it87_devices it87_devices[] = { .suffix = "F", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI - | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL, + | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL + | FEAT_PWM_FREQ2, .peci_mask = 0x05, .old_peci_mask = 0x02, /* Actually reports PCH */ }, @@ -321,7 +325,7 @@ static const struct it87_devices it87_devices[] = { .suffix = "F", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS - | FEAT_IN7_INTERNAL, + | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2, .peci_mask = 0x07, }, [it8732] = { @@ -337,7 +341,8 @@ static const struct it87_devices it87_devices[] = { .name = "it8771", .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL + | FEAT_PWM_FREQ2, /* PECI: guesswork */ /* 12mV ADC (OHM) */ /* 16 bit fans (OHM) */ @@ -348,7 +353,8 @@ static const struct it87_devices it87_devices[] = { .name = "it8772", .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL + | FEAT_PWM_FREQ2, /* PECI (coreboot) */ /* 12mV ADC (HWSensors4, OHM) */ /* 16 bit fans (HWSensors4, OHM) */ @@ -359,35 +365,37 @@ static const struct it87_devices it87_devices[] = { .name = "it8781", .suffix = "F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG, + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2, .old_peci_mask = 0x4, }, [it8782] = { .name = "it8782", .suffix = "F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG, + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2, .old_peci_mask = 0x4, }, [it8783] = { .name = "it8783", .suffix = "E/F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG, + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2, .old_peci_mask = 0x4, }, [it8786] = { .name = "it8786", .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL + | FEAT_PWM_FREQ2, .peci_mask = 0x07, }, [it8790] = { .name = "it8790", .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL + | FEAT_PWM_FREQ2, .peci_mask = 0x07, }, [it8603] = { @@ -395,7 +403,7 @@ static const struct it87_devices it87_devices[] = { .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL - | FEAT_AVCC3, + | FEAT_AVCC3 | FEAT_PWM_FREQ2, .peci_mask = 0x07, }, [it8620] = { @@ -403,7 +411,7 @@ static const struct it87_devices it87_devices[] = { .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS - | FEAT_IN7_INTERNAL | FEAT_SIX_PWM, + | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2, .peci_mask = 0x07, }, }; @@ -427,6 +435,7 @@ static const struct it87_devices it87_devices[] = { #define has_six_fans(data) ((data)->features & FEAT_SIX_FANS) #define has_avcc3(data) ((data)->features & FEAT_AVCC3) #define has_six_pwm(data) ((data)->features & FEAT_SIX_PWM) +#define has_pwm_freq2(data) ((data)->features & FEAT_PWM_FREQ2) struct it87_sio_data { enum chips type; @@ -906,9 +915,16 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, char *buf) { + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); struct it87_data *data = it87_update_device(dev); - int index = (data->fan_ctl >> 4) & 0x07; + int nr = sensor_attr->index; unsigned int freq; + int index; + + if (has_pwm_freq2(data) && nr == 1) + index = (data->extra >> 4) & 0x07; + else + index = (data->fan_ctl >> 4) & 0x07; freq = pwm_freq[index] / (has_newer_autopwm(data) ? 256 : 128); @@ -1127,7 +1143,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, static ssize_t set_pwm_freq(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); struct it87_data *data = dev_get_drvdata(dev); + int nr = sensor_attr->index; unsigned long val; int i; @@ -1144,9 +1162,15 @@ static ssize_t set_pwm_freq(struct device *dev, } mutex_lock(&data->update_lock); - data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL) & 0x8f; - data->fan_ctl |= i << 4; - it87_write_value(data, IT87_REG_FAN_CTL, data->fan_ctl); + if (nr == 0) { + data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL) & 0x8f; + data->fan_ctl |= i << 4; + it87_write_value(data, IT87_REG_FAN_CTL, data->fan_ctl); + } else { + data->extra = it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x8f; + data->extra |= i << 4; + it87_write_value(data, IT87_REG_TEMP_EXTRA, data->extra); + } mutex_unlock(&data->update_lock); return count; @@ -1316,7 +1340,8 @@ static SENSOR_DEVICE_ATTR_2(fan6_min, S_IRUGO | S_IWUSR, show_fan, set_fan, static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 0); static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0); -static DEVICE_ATTR(pwm1_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq); +static SENSOR_DEVICE_ATTR(pwm1_freq, S_IRUGO | S_IWUSR, show_pwm_freq, + set_pwm_freq, 0); static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO | S_IWUSR, show_pwm_temp_map, set_pwm_temp_map, 0); static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR, @@ -1341,7 +1366,7 @@ static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_temp, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 1); static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 1); -static DEVICE_ATTR(pwm2_freq, S_IRUGO, show_pwm_freq, NULL); +static SENSOR_DEVICE_ATTR(pwm2_freq, S_IRUGO, show_pwm_freq, set_pwm_freq, 1); static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IRUGO | S_IWUSR, show_pwm_temp_map, set_pwm_temp_map, 1); static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR, @@ -1366,7 +1391,7 @@ static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_temp, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 2); static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 2); -static DEVICE_ATTR(pwm3_freq, S_IRUGO, show_pwm_freq, NULL); +static SENSOR_DEVICE_ATTR(pwm3_freq, S_IRUGO, show_pwm_freq, NULL, 2); static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IRUGO | S_IWUSR, show_pwm_temp_map, set_pwm_temp_map, 2); static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, @@ -1391,21 +1416,21 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 3); static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 3); -static DEVICE_ATTR(pwm4_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq); +static SENSOR_DEVICE_ATTR(pwm4_freq, S_IRUGO, show_pwm_freq, NULL, 3); static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IRUGO | S_IWUSR, show_pwm_temp_map, set_pwm_temp_map, 3); static SENSOR_DEVICE_ATTR(pwm5_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 4); static SENSOR_DEVICE_ATTR(pwm5, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 4); -static DEVICE_ATTR(pwm5_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq); +static SENSOR_DEVICE_ATTR(pwm5_freq, S_IRUGO, show_pwm_freq, NULL, 4); static SENSOR_DEVICE_ATTR(pwm5_auto_channels_temp, S_IRUGO | S_IWUSR, show_pwm_temp_map, set_pwm_temp_map, 4); static SENSOR_DEVICE_ATTR(pwm6_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 5); static SENSOR_DEVICE_ATTR(pwm6, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 5); -static DEVICE_ATTR(pwm6_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq); +static SENSOR_DEVICE_ATTR(pwm6_freq, S_IRUGO, show_pwm_freq, NULL, 5); static SENSOR_DEVICE_ATTR(pwm6_auto_channels_temp, S_IRUGO | S_IWUSR, show_pwm_temp_map, set_pwm_temp_map, 5); @@ -1774,44 +1799,57 @@ static const struct attribute *it87_attributes_fan_div[] = { static struct attribute *it87_attributes_pwm[6][4+1] = { { &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, - &dev_attr_pwm1_freq.attr, + &sensor_dev_attr_pwm1_freq.dev_attr.attr, &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, NULL }, { &sensor_dev_attr_pwm2_enable.dev_attr.attr, &sensor_dev_attr_pwm2.dev_attr.attr, - &dev_attr_pwm2_freq.attr, + &sensor_dev_attr_pwm2_freq.dev_attr.attr, &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, NULL }, { &sensor_dev_attr_pwm3_enable.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr, - &dev_attr_pwm3_freq.attr, + &sensor_dev_attr_pwm3_freq.dev_attr.attr, &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, NULL }, { &sensor_dev_attr_pwm4_enable.dev_attr.attr, &sensor_dev_attr_pwm4.dev_attr.attr, - &dev_attr_pwm4_freq.attr, + &sensor_dev_attr_pwm4_freq.dev_attr.attr, &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr, NULL }, { &sensor_dev_attr_pwm5_enable.dev_attr.attr, &sensor_dev_attr_pwm5.dev_attr.attr, - &dev_attr_pwm5_freq.attr, + &sensor_dev_attr_pwm5_freq.dev_attr.attr, &sensor_dev_attr_pwm5_auto_channels_temp.dev_attr.attr, NULL }, { &sensor_dev_attr_pwm6_enable.dev_attr.attr, &sensor_dev_attr_pwm6.dev_attr.attr, - &dev_attr_pwm6_freq.attr, + &sensor_dev_attr_pwm6_freq.dev_attr.attr, &sensor_dev_attr_pwm6_auto_channels_temp.dev_attr.attr, NULL } }; +static umode_t pwm_attribute_mode(struct kobject *kobj, struct attribute *attr, + int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct it87_data *data = dev_get_drvdata(dev); + + if (has_pwm_freq2(data) && index == 2) + return attr->mode | S_IWUSR; + + return attr->mode; +} + static const struct attribute_group it87_group_pwm[6] = { { .attrs = it87_attributes_pwm[0] }, - { .attrs = it87_attributes_pwm[1] }, + { .attrs = it87_attributes_pwm[1], + .is_visible = pwm_attribute_mode, }, { .attrs = it87_attributes_pwm[2] }, { .attrs = it87_attributes_pwm[3] }, { .attrs = it87_attributes_pwm[4] }, From 5cae84a58ee60eb54f636133f4f3ede9af93d476 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 28 Mar 2015 07:44:59 -0700 Subject: [PATCH 13/35] hwmon: (it87) Simplify error return in it87_device_add Return directly on errors if there is no cleanup necessary. Don't create an error message on memory allocation errors. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 68c8d98e711a..9b36987d7949 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -2898,14 +2898,11 @@ static int __init it87_device_add(unsigned short address, err = acpi_check_resource_conflict(&res); if (err) - goto exit; + return err; pdev = platform_device_alloc(DRVNAME, address); - if (!pdev) { - err = -ENOMEM; - pr_err("Device allocation failed\n"); - goto exit; - } + if (!pdev) + return -ENOMEM; err = platform_device_add_resources(pdev, &res, 1); if (err) { @@ -2930,7 +2927,6 @@ static int __init it87_device_add(unsigned short address, exit_device_put: platform_device_put(pdev); -exit: return err; } From 8e50e3c3f60c84b96956d37cbbf109b75569c6ba Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 28 Mar 2015 07:49:14 -0700 Subject: [PATCH 14/35] hwmon: (it87) Don't use pdev as static driver variable Using the same varible name for function names and as static variable invites misuse and prevents us from adding support for a second chip. Rename pdev to it87_pdev and limit its use to where it is needed. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 9b36987d7949..8f28f9b04150 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -78,7 +78,7 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -static struct platform_device *pdev; +static struct platform_device *it87_pdev; #define REG 0x2e /* The register to read/write */ #define DEV 0x07 /* Register: Logical device select */ @@ -2285,7 +2285,7 @@ exit: static void it87_remove_files(struct device *dev) { - struct it87_data *data = platform_get_drvdata(pdev); + struct it87_data *data = dev_get_drvdata(dev); struct it87_sio_data *sio_data = dev_get_platdata(dev); int i; @@ -2888,6 +2888,7 @@ static struct it87_data *it87_update_device(struct device *dev) static int __init it87_device_add(unsigned short address, const struct it87_sio_data *sio_data) { + struct platform_device *pdev; struct resource res = { .start = address + IT87_EC_OFFSET, .end = address + IT87_EC_OFFSET + IT87_EC_EXTENT - 1, @@ -2923,6 +2924,7 @@ static int __init it87_device_add(unsigned short address, goto exit_device_put; } + it87_pdev = pdev; return 0; exit_device_put: @@ -2955,7 +2957,7 @@ static int __init sm_it87_init(void) static void __exit sm_it87_exit(void) { - platform_device_unregister(pdev); + platform_device_unregister(it87_pdev); platform_driver_unregister(&it87_driver); } From 3c2e35126f2821be9b6f11dd5b772c68bcef4475 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 28 Mar 2015 08:03:10 -0700 Subject: [PATCH 15/35] hwmon: (it87) Pass SIO base address as parameter to superio functions This will let us support more than one chip on different SIO addresses with the same driver. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 137 +++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 65 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 8f28f9b04150..4840f2d8c7b1 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -80,9 +80,9 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); static struct platform_device *it87_pdev; -#define REG 0x2e /* The register to read/write */ +#define REG_2E 0x2e /* The register to read/write */ + #define DEV 0x07 /* Register: Logical device select */ -#define VAL 0x2f /* The value to read/write */ #define PME 0x04 /* The device with the fan registers in it */ /* The device with the IT8718F/IT8720F VID value in it */ @@ -91,54 +91,54 @@ static struct platform_device *it87_pdev; #define DEVID 0x20 /* Register: Device ID */ #define DEVREV 0x22 /* Register: Device Revision */ -static inline int superio_inb(int reg) +static inline int superio_inb(int ioreg, int reg) { - outb(reg, REG); - return inb(VAL); + outb(reg, ioreg); + return inb(ioreg + 1); } -static inline void superio_outb(int reg, int val) +static inline void superio_outb(int ioreg, int reg, int val) { - outb(reg, REG); - outb(val, VAL); + outb(reg, ioreg); + outb(val, ioreg + 1); } -static int superio_inw(int reg) +static int superio_inw(int ioreg, int reg) { int val; - outb(reg++, REG); - val = inb(VAL) << 8; - outb(reg, REG); - val |= inb(VAL); + outb(reg++, ioreg); + val = inb(ioreg + 1) << 8; + outb(reg, ioreg); + val |= inb(ioreg + 1); return val; } -static inline void superio_select(int ldn) +static inline void superio_select(int ioreg, int ldn) { - outb(DEV, REG); - outb(ldn, VAL); + outb(DEV, ioreg); + outb(ldn, ioreg + 1); } -static inline int superio_enter(void) +static inline int superio_enter(int ioreg) { /* - * Try to reserve REG and REG + 1 for exclusive access. + * Try to reserve ioreg and ioreg + 1 for exclusive access. */ - if (!request_muxed_region(REG, 2, DRVNAME)) + if (!request_muxed_region(ioreg, 2, DRVNAME)) return -EBUSY; - outb(0x87, REG); - outb(0x01, REG); - outb(0x55, REG); - outb(0x55, REG); + outb(0x87, ioreg); + outb(0x01, ioreg); + outb(0x55, ioreg); + outb(0x55, ioreg); return 0; } -static inline void superio_exit(void) +static inline void superio_exit(int ioreg) { - outb(0x02, REG); - outb(0x02, VAL); - release_region(REG, 2); + outb(0x02, ioreg); + outb(0x02, ioreg + 1); + release_region(ioreg, 2); } /* Logical device 4 registers */ @@ -1929,20 +1929,20 @@ static const struct attribute_group it87_group_label = { }; /* SuperIO detection - will change isa_address if a chip is found */ -static int __init it87_find(unsigned short *address, - struct it87_sio_data *sio_data) +static int __init it87_find(int sioaddr, unsigned short *address, + struct it87_sio_data *sio_data) { int err; u16 chip_type; const char *board_vendor, *board_name; const struct it87_devices *config; - err = superio_enter(); + err = superio_enter(sioaddr); if (err) return err; err = -ENODEV; - chip_type = force_id ? force_id : superio_inw(DEVID); + chip_type = force_id ? force_id : superio_inw(sioaddr, DEVID); switch (chip_type) { case IT8705F_DEVID: @@ -2005,20 +2005,20 @@ static int __init it87_find(unsigned short *address, goto exit; } - superio_select(PME); - if (!(superio_inb(IT87_ACT_REG) & 0x01)) { + superio_select(sioaddr, PME); + if (!(superio_inb(sioaddr, IT87_ACT_REG) & 0x01)) { pr_info("Device not activated, skipping\n"); goto exit; } - *address = superio_inw(IT87_BASE_REG) & ~(IT87_EXTENT - 1); + *address = superio_inw(sioaddr, IT87_BASE_REG) & ~(IT87_EXTENT - 1); if (*address == 0) { pr_info("Base address not set, skipping\n"); goto exit; } err = 0; - sio_data->revision = superio_inb(DEVREV) & 0x0f; + sio_data->revision = superio_inb(sioaddr, DEVREV) & 0x0f; pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type, it87_devices[sio_data->type].suffix, *address, sio_data->revision); @@ -2047,18 +2047,19 @@ static int __init it87_find(unsigned short *address, /* Read GPIO config and VID value from LDN 7 (GPIO) */ if (sio_data->type == it87) { /* The IT8705F has a different LD number for GPIO */ - superio_select(5); - sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + superio_select(sioaddr, 5); + sio_data->beep_pin = superio_inb(sioaddr, + IT87_SIO_BEEP_PIN_REG) & 0x3f; } else if (sio_data->type == it8783) { int reg25, reg27, reg2a, reg2c, regef; - superio_select(GPIO); + superio_select(sioaddr, GPIO); - reg25 = superio_inb(IT87_SIO_GPIO1_REG); - reg27 = superio_inb(IT87_SIO_GPIO3_REG); - reg2a = superio_inb(IT87_SIO_PINX1_REG); - reg2c = superio_inb(IT87_SIO_PINX2_REG); - regef = superio_inb(IT87_SIO_SPI_REG); + reg25 = superio_inb(sioaddr, IT87_SIO_GPIO1_REG); + reg27 = superio_inb(sioaddr, IT87_SIO_GPIO3_REG); + reg2a = superio_inb(sioaddr, IT87_SIO_PINX1_REG); + reg2c = superio_inb(sioaddr, IT87_SIO_PINX2_REG); + regef = superio_inb(sioaddr, IT87_SIO_SPI_REG); /* Check if fan3 is there or not */ if ((reg27 & (1 << 0)) || !(reg2c & (1 << 2))) @@ -2101,7 +2102,8 @@ static int __init it87_find(unsigned short *address, */ if (!(reg2c & (1 << 1))) { reg2c |= (1 << 1); - superio_outb(IT87_SIO_PINX2_REG, reg2c); + superio_outb(sioaddr, IT87_SIO_PINX2_REG, + reg2c); pr_notice("Routing internal VCCH5V to in7.\n"); } pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n"); @@ -2113,13 +2115,14 @@ static int __init it87_find(unsigned short *address, if (reg2c & (1 << 1)) sio_data->internal |= (1 << 1); - sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + sio_data->beep_pin = superio_inb(sioaddr, + IT87_SIO_BEEP_PIN_REG) & 0x3f; } else if (sio_data->type == it8603) { int reg27, reg29; - superio_select(GPIO); + superio_select(sioaddr, GPIO); - reg27 = superio_inb(IT87_SIO_GPIO3_REG); + reg27 = superio_inb(sioaddr, IT87_SIO_GPIO3_REG); /* Check if fan3 is there or not */ if (reg27 & (1 << 6)) @@ -2128,7 +2131,7 @@ static int __init it87_find(unsigned short *address, sio_data->skip_fan |= (1 << 2); /* Check if fan2 is there or not */ - reg29 = superio_inb(IT87_SIO_GPIO5_REG); + reg29 = superio_inb(sioaddr, IT87_SIO_GPIO5_REG); if (reg29 & (1 << 1)) sio_data->skip_pwm |= (1 << 1); if (reg29 & (1 << 2)) @@ -2137,38 +2140,39 @@ static int __init it87_find(unsigned short *address, sio_data->skip_in |= (1 << 5); /* No VIN5 */ sio_data->skip_in |= (1 << 6); /* No VIN6 */ - sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + sio_data->beep_pin = superio_inb(sioaddr, + IT87_SIO_BEEP_PIN_REG) & 0x3f; } else if (sio_data->type == it8620) { int reg; - superio_select(GPIO); + superio_select(sioaddr, GPIO); /* Check for pwm5 */ - reg = superio_inb(IT87_SIO_GPIO1_REG); + reg = superio_inb(sioaddr, IT87_SIO_GPIO1_REG); if (reg & (1 << 6)) sio_data->skip_pwm |= (1 << 4); /* Check for fan4, fan5 */ - reg = superio_inb(IT87_SIO_GPIO2_REG); + reg = superio_inb(sioaddr, IT87_SIO_GPIO2_REG); if (!(reg & (1 << 5))) sio_data->skip_fan |= (1 << 3); if (!(reg & (1 << 4))) sio_data->skip_fan |= (1 << 4); /* Check for pwm3, fan3 */ - reg = superio_inb(IT87_SIO_GPIO3_REG); + reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG); if (reg & (1 << 6)) sio_data->skip_pwm |= (1 << 2); if (reg & (1 << 7)) sio_data->skip_fan |= (1 << 2); /* Check for pwm4 */ - reg = superio_inb(IT87_SIO_GPIO4_REG); + reg = superio_inb(sioaddr, IT87_SIO_GPIO4_REG); if (!(reg & (1 << 2))) sio_data->skip_pwm |= (1 << 3); /* Check for pwm2, fan2 */ - reg = superio_inb(IT87_SIO_GPIO5_REG); + reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG); if (reg & (1 << 1)) sio_data->skip_pwm |= (1 << 1); if (reg & (1 << 2)) @@ -2179,14 +2183,15 @@ static int __init it87_find(unsigned short *address, sio_data->skip_fan |= (1 << 5); } - sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + sio_data->beep_pin = superio_inb(sioaddr, + IT87_SIO_BEEP_PIN_REG) & 0x3f; } else { int reg; bool uart6; - superio_select(GPIO); + superio_select(sioaddr, GPIO); - reg = superio_inb(IT87_SIO_GPIO3_REG); + reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG); if (!sio_data->skip_vid) { /* We need at least 4 VID pins */ if (reg & 0x0f) { @@ -2202,7 +2207,7 @@ static int __init it87_find(unsigned short *address, sio_data->skip_fan |= (1 << 2); /* Check if fan2 is there or not */ - reg = superio_inb(IT87_SIO_GPIO5_REG); + reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG); if (reg & (1 << 1)) sio_data->skip_pwm |= (1 << 1); if (reg & (1 << 2)) @@ -2210,9 +2215,10 @@ static int __init it87_find(unsigned short *address, if ((sio_data->type == it8718 || sio_data->type == it8720) && !(sio_data->skip_vid)) - sio_data->vid_value = superio_inb(IT87_SIO_VID_REG); + sio_data->vid_value = superio_inb(sioaddr, + IT87_SIO_VID_REG); - reg = superio_inb(IT87_SIO_PINX2_REG); + reg = superio_inb(sioaddr, IT87_SIO_PINX2_REG); uart6 = sio_data->type == it8782 && (reg & (1 << 2)); @@ -2232,7 +2238,7 @@ static int __init it87_find(unsigned short *address, */ if ((sio_data->type == it8720 || uart6) && !(reg & (1 << 1))) { reg |= (1 << 1); - superio_outb(IT87_SIO_PINX2_REG, reg); + superio_outb(sioaddr, IT87_SIO_PINX2_REG, reg); pr_notice("Routing internal VCCH to in7\n"); } if (reg & (1 << 0)) @@ -2254,7 +2260,8 @@ static int __init it87_find(unsigned short *address, sio_data->skip_temp |= (1 << 2); } - sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + sio_data->beep_pin = superio_inb(sioaddr, + IT87_SIO_BEEP_PIN_REG) & 0x3f; } if (sio_data->beep_pin) pr_info("Beeping is supported\n"); @@ -2279,7 +2286,7 @@ static int __init it87_find(unsigned short *address, } exit: - superio_exit(); + superio_exit(sioaddr); return err; } @@ -2939,7 +2946,7 @@ static int __init sm_it87_init(void) struct it87_sio_data sio_data; memset(&sio_data, 0, sizeof(struct it87_sio_data)); - err = it87_find(&isa_address, &sio_data); + err = it87_find(REG_2E, &isa_address, &sio_data); if (err) return err; err = platform_driver_register(&it87_driver); From e84bd9535e2bb59624091bc8c1eddae7cc82d260 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 28 Mar 2015 08:24:29 -0700 Subject: [PATCH 16/35] hwmon: (it87) Add support for second Super-IO chip The Super-IO chip can also reside at SIO address 0x4e, and there can be two Super-IO chips in the system. Add support for it. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 49 ++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 4840f2d8c7b1..f877cc98b2fd 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -78,9 +78,10 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -static struct platform_device *it87_pdev; +static struct platform_device *it87_pdev[2]; #define REG_2E 0x2e /* The register to read/write */ +#define REG_4E 0x4e /* Secondary register to read/write */ #define DEV 0x07 /* Register: Logical device select */ #define PME 0x04 /* The device with the fan registers in it */ @@ -130,7 +131,7 @@ static inline int superio_enter(int ioreg) outb(0x87, ioreg); outb(0x01, ioreg); outb(0x55, ioreg); - outb(0x55, ioreg); + outb(ioreg == REG_4E ? 0xaa : 0x55, ioreg); return 0; } @@ -2892,7 +2893,7 @@ static struct it87_data *it87_update_device(struct device *dev) return data; } -static int __init it87_device_add(unsigned short address, +static int __init it87_device_add(int index, unsigned short address, const struct it87_sio_data *sio_data) { struct platform_device *pdev; @@ -2931,7 +2932,7 @@ static int __init it87_device_add(unsigned short address, goto exit_device_put; } - it87_pdev = pdev; + it87_pdev[index] = pdev; return 0; exit_device_put: @@ -2941,30 +2942,48 @@ exit_device_put: static int __init sm_it87_init(void) { - int err; - unsigned short isa_address = 0; + int sioaddr[2] = { REG_2E, REG_4E }; struct it87_sio_data sio_data; + unsigned short isa_address; + bool found = false; + int i, err; - memset(&sio_data, 0, sizeof(struct it87_sio_data)); - err = it87_find(REG_2E, &isa_address, &sio_data); - if (err) - return err; err = platform_driver_register(&it87_driver); if (err) return err; - err = it87_device_add(isa_address, &sio_data); - if (err) { - platform_driver_unregister(&it87_driver); - return err; + for (i = 0; i < ARRAY_SIZE(sioaddr); i++) { + memset(&sio_data, 0, sizeof(struct it87_sio_data)); + isa_address = 0; + err = it87_find(sioaddr[i], &isa_address, &sio_data); + if (err || isa_address == 0) + continue; + + err = it87_device_add(i, isa_address, &sio_data); + if (err) + goto exit_dev_unregister; + found = true; } + if (!found) { + err = -ENODEV; + goto exit_unregister; + } return 0; + +exit_dev_unregister: + /* NULL check handled by platform_device_unregister */ + platform_device_unregister(it87_pdev[0]); +exit_unregister: + platform_driver_unregister(&it87_driver); + return err; } static void __exit sm_it87_exit(void) { - platform_device_unregister(it87_pdev); + /* NULL check handled by platform_device_unregister */ + platform_device_unregister(it87_pdev[1]); + platform_device_unregister(it87_pdev[0]); platform_driver_unregister(&it87_driver); } From c1e7a4ca6d7c58e30185d40e9ba82c7976950f32 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 28 Mar 2015 09:27:27 -0700 Subject: [PATCH 17/35] hwmon: (it87) Rearrange code to avoid forward declarations Cleanup only, no functional change. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 663 +++++++++++++++++++++---------------------- 1 file changed, 327 insertions(+), 336 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index f877cc98b2fd..4b38ecb91959 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -603,23 +603,160 @@ static const unsigned int pwm_freq[8] = { 750000, }; -static int it87_probe(struct platform_device *pdev); -static int it87_remove(struct platform_device *pdev); +/* + * Must be called with data->update_lock held, except during initialization. + * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + * would slow down the IT87 access and should not be necessary. + */ +static int it87_read_value(struct it87_data *data, u8 reg) +{ + outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET); + return inb_p(data->addr + IT87_DATA_REG_OFFSET); +} -static int it87_read_value(struct it87_data *data, u8 reg); -static void it87_write_value(struct it87_data *data, u8 reg, u8 value); -static struct it87_data *it87_update_device(struct device *dev); -static int it87_check_pwm(struct device *dev); -static void it87_init_device(struct platform_device *pdev); +/* + * Must be called with data->update_lock held, except during initialization. + * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + * would slow down the IT87 access and should not be necessary. + */ +static void it87_write_value(struct it87_data *data, u8 reg, u8 value) +{ + outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET); + outb_p(value, data->addr + IT87_DATA_REG_OFFSET); +} +static void it87_update_pwm_ctrl(struct it87_data *data, int nr) +{ + data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM[nr]); + if (has_newer_autopwm(data)) { + data->pwm_temp_map[nr] = (data->pwm_ctrl[nr] & 0x03) + + nr < 3 ? 0 : 3; + data->pwm_duty[nr] = it87_read_value(data, + IT87_REG_PWM_DUTY[nr]); + } else { + if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */ + data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; + else /* Manual mode */ + data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f; + } -static struct platform_driver it87_driver = { - .driver = { - .name = DRVNAME, - }, - .probe = it87_probe, - .remove = it87_remove, -}; + if (has_old_autopwm(data)) { + int i; + + for (i = 0; i < 5 ; i++) + data->auto_temp[nr][i] = it87_read_value(data, + IT87_REG_AUTO_TEMP(nr, i)); + for (i = 0; i < 3 ; i++) + data->auto_pwm[nr][i] = it87_read_value(data, + IT87_REG_AUTO_PWM(nr, i)); + } +} + +static struct it87_data *it87_update_device(struct device *dev) +{ + struct it87_data *data = dev_get_drvdata(dev); + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + if (update_vbat) { + /* + * Cleared after each update, so reenable. Value + * returned by this read will be previous value + */ + it87_write_value(data, IT87_REG_CONFIG, + it87_read_value(data, IT87_REG_CONFIG) | 0x40); + } + for (i = 0; i <= 7; i++) { + data->in[i][0] = + it87_read_value(data, IT87_REG_VIN(i)); + data->in[i][1] = + it87_read_value(data, IT87_REG_VIN_MIN(i)); + data->in[i][2] = + it87_read_value(data, IT87_REG_VIN_MAX(i)); + } + /* in8 (battery) has no limit registers */ + data->in[8][0] = it87_read_value(data, IT87_REG_VIN(8)); + if (has_avcc3(data)) + data->in[9][0] = it87_read_value(data, IT87_REG_AVCC3); + + for (i = 0; i < 6; i++) { + /* Skip disabled fans */ + if (!(data->has_fan & (1 << i))) + continue; + + data->fan[i][1] = + it87_read_value(data, IT87_REG_FAN_MIN[i]); + data->fan[i][0] = it87_read_value(data, + IT87_REG_FAN[i]); + /* Add high byte if in 16-bit mode */ + if (has_16bit_fans(data)) { + data->fan[i][0] |= it87_read_value(data, + IT87_REG_FANX[i]) << 8; + data->fan[i][1] |= it87_read_value(data, + IT87_REG_FANX_MIN[i]) << 8; + } + } + for (i = 0; i < 3; i++) { + if (!(data->has_temp & (1 << i))) + continue; + data->temp[i][0] = + it87_read_value(data, IT87_REG_TEMP(i)); + data->temp[i][1] = + it87_read_value(data, IT87_REG_TEMP_LOW(i)); + data->temp[i][2] = + it87_read_value(data, IT87_REG_TEMP_HIGH(i)); + if (has_temp_offset(data)) + data->temp[i][3] = + it87_read_value(data, + IT87_REG_TEMP_OFFSET[i]); + } + + /* Newer chips don't have clock dividers */ + if ((data->has_fan & 0x07) && !has_16bit_fans(data)) { + i = it87_read_value(data, IT87_REG_FAN_DIV); + data->fan_div[0] = i & 0x07; + data->fan_div[1] = (i >> 3) & 0x07; + data->fan_div[2] = (i & 0x40) ? 3 : 1; + } + + data->alarms = + it87_read_value(data, IT87_REG_ALARM1) | + (it87_read_value(data, IT87_REG_ALARM2) << 8) | + (it87_read_value(data, IT87_REG_ALARM3) << 16); + data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE); + + data->fan_main_ctrl = it87_read_value(data, + IT87_REG_FAN_MAIN_CTRL); + data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL); + for (i = 0; i < 6; i++) + it87_update_pwm_ctrl(data, i); + + data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); + data->extra = it87_read_value(data, IT87_REG_TEMP_EXTRA); + /* + * The IT8705F does not have VID capability. + * The IT8718F and later don't use IT87_REG_VID for the + * same purpose. + */ + if (data->type == it8712 || data->type == it8716) { + data->vid = it87_read_value(data, IT87_REG_VID); + /* + * The older IT8712F revisions had only 5 VID pins, + * but we assume it is always safe to read 6 bits. + */ + data->vid &= 0x3f; + } + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} static ssize_t show_in(struct device *dev, struct device_attribute *attr, char *buf) @@ -2341,6 +2478,175 @@ static void it87_remove_files(struct device *dev) sysfs_remove_group(&dev->kobj, &it87_group_label); } +/* Called when we have found a new IT87. */ +static void it87_init_device(struct platform_device *pdev) +{ + struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev); + struct it87_data *data = platform_get_drvdata(pdev); + int tmp, i; + u8 mask; + + /* + * For each PWM channel: + * - If it is in automatic mode, setting to manual mode should set + * the fan to full speed by default. + * - If it is in manual mode, we need a mapping to temperature + * channels to use when later setting to automatic mode later. + * Use a 1:1 mapping by default (we are clueless.) + * In both cases, the value can (and should) be changed by the user + * prior to switching to a different mode. + * Note that this is no longer needed for the IT8721F and later, as + * these have separate registers for the temperature mapping and the + * manual duty cycle. + */ + for (i = 0; i < 3; i++) { + data->pwm_temp_map[i] = i; + data->pwm_duty[i] = 0x7f; /* Full speed */ + data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */ + } + + /* + * Some chips seem to have default value 0xff for all limit + * registers. For low voltage limits it makes no sense and triggers + * alarms, so change to 0 instead. For high temperature limits, it + * means -1 degree C, which surprisingly doesn't trigger an alarm, + * but is still confusing, so change to 127 degrees C. + */ + for (i = 0; i < 8; i++) { + tmp = it87_read_value(data, IT87_REG_VIN_MIN(i)); + if (tmp == 0xff) + it87_write_value(data, IT87_REG_VIN_MIN(i), 0); + } + for (i = 0; i < 3; i++) { + tmp = it87_read_value(data, IT87_REG_TEMP_HIGH(i)); + if (tmp == 0xff) + it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127); + } + + /* + * Temperature channels are not forcibly enabled, as they can be + * set to two different sensor types and we can't guess which one + * is correct for a given system. These channels can be enabled at + * run-time through the temp{1-3}_type sysfs accessors if needed. + */ + + /* Check if voltage monitors are reset manually or by some reason */ + tmp = it87_read_value(data, IT87_REG_VIN_ENABLE); + if ((tmp & 0xff) == 0) { + /* Enable all voltage monitors */ + it87_write_value(data, IT87_REG_VIN_ENABLE, 0xff); + } + + /* Check if tachometers are reset manually or by some reason */ + mask = 0x70 & ~(sio_data->skip_fan << 4); + data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); + if ((data->fan_main_ctrl & mask) == 0) { + /* Enable all fan tachometers */ + data->fan_main_ctrl |= mask; + it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, + data->fan_main_ctrl); + } + data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; + + tmp = it87_read_value(data, IT87_REG_FAN_16BIT); + + /* Set tachometers to 16-bit mode if needed */ + if (has_fan16_config(data)) { + if (~tmp & 0x07 & data->has_fan) { + dev_dbg(&pdev->dev, + "Setting fan1-3 to 16-bit mode\n"); + it87_write_value(data, IT87_REG_FAN_16BIT, + tmp | 0x07); + } + } + + /* Check for additional fans */ + if (has_five_fans(data)) { + if (tmp & (1 << 4)) + data->has_fan |= (1 << 3); /* fan4 enabled */ + if (tmp & (1 << 5)) + data->has_fan |= (1 << 4); /* fan5 enabled */ + if (has_six_fans(data) && (tmp & (1 << 2))) + data->has_fan |= (1 << 5); /* fan6 enabled */ + } + + /* Fan input pins may be used for alternative functions */ + data->has_fan &= ~sio_data->skip_fan; + + /* Check if pwm5, pwm6 are enabled */ + if (has_six_pwm(data)) { + /* The following code may be IT8620E specific */ + tmp = it87_read_value(data, IT87_REG_FAN_DIV); + if ((tmp & 0xc0) == 0xc0) + sio_data->skip_pwm |= (1 << 4); + if (!(tmp & (1 << 3))) + sio_data->skip_pwm |= (1 << 5); + } + + /* Start monitoring */ + it87_write_value(data, IT87_REG_CONFIG, + (it87_read_value(data, IT87_REG_CONFIG) & 0x3e) + | (update_vbat ? 0x41 : 0x01)); +} + +/* Return 1 if and only if the PWM interface is safe to use */ +static int it87_check_pwm(struct device *dev) +{ + struct it87_data *data = dev_get_drvdata(dev); + /* + * Some BIOSes fail to correctly configure the IT87 fans. All fans off + * and polarity set to active low is sign that this is the case so we + * disable pwm control to protect the user. + */ + int tmp = it87_read_value(data, IT87_REG_FAN_CTL); + + if ((tmp & 0x87) == 0) { + if (fix_pwm_polarity) { + /* + * The user asks us to attempt a chip reconfiguration. + * This means switching to active high polarity and + * inverting all fan speed values. + */ + int i; + u8 pwm[3]; + + for (i = 0; i < 3; i++) + pwm[i] = it87_read_value(data, + IT87_REG_PWM[i]); + + /* + * If any fan is in automatic pwm mode, the polarity + * might be correct, as suspicious as it seems, so we + * better don't change anything (but still disable the + * PWM interface). + */ + if (!((pwm[0] | pwm[1] | pwm[2]) & 0x80)) { + dev_info(dev, + "Reconfiguring PWM to active high polarity\n"); + it87_write_value(data, IT87_REG_FAN_CTL, + tmp | 0x87); + for (i = 0; i < 3; i++) + it87_write_value(data, + IT87_REG_PWM[i], + 0x7f & ~pwm[i]); + return 1; + } + + dev_info(dev, + "PWM configuration is too broken to be fixed\n"); + } + + dev_info(dev, + "Detected broken BIOS defaults, disabling PWM interface\n"); + return 0; + } else if (fix_pwm_polarity) { + dev_info(dev, + "PWM configuration looks sane, won't touch\n"); + } + + return 1; +} + static int it87_probe(struct platform_device *pdev) { struct it87_data *data; @@ -2570,328 +2876,13 @@ static int it87_remove(struct platform_device *pdev) return 0; } -/* - * Must be called with data->update_lock held, except during initialization. - * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, - * would slow down the IT87 access and should not be necessary. - */ -static int it87_read_value(struct it87_data *data, u8 reg) -{ - outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET); - return inb_p(data->addr + IT87_DATA_REG_OFFSET); -} - -/* - * Must be called with data->update_lock held, except during initialization. - * We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, - * would slow down the IT87 access and should not be necessary. - */ -static void it87_write_value(struct it87_data *data, u8 reg, u8 value) -{ - outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET); - outb_p(value, data->addr + IT87_DATA_REG_OFFSET); -} - -/* Return 1 if and only if the PWM interface is safe to use */ -static int it87_check_pwm(struct device *dev) -{ - struct it87_data *data = dev_get_drvdata(dev); - /* - * Some BIOSes fail to correctly configure the IT87 fans. All fans off - * and polarity set to active low is sign that this is the case so we - * disable pwm control to protect the user. - */ - int tmp = it87_read_value(data, IT87_REG_FAN_CTL); - if ((tmp & 0x87) == 0) { - if (fix_pwm_polarity) { - /* - * The user asks us to attempt a chip reconfiguration. - * This means switching to active high polarity and - * inverting all fan speed values. - */ - int i; - u8 pwm[3]; - - for (i = 0; i < 3; i++) - pwm[i] = it87_read_value(data, - IT87_REG_PWM[i]); - - /* - * If any fan is in automatic pwm mode, the polarity - * might be correct, as suspicious as it seems, so we - * better don't change anything (but still disable the - * PWM interface). - */ - if (!((pwm[0] | pwm[1] | pwm[2]) & 0x80)) { - dev_info(dev, - "Reconfiguring PWM to active high polarity\n"); - it87_write_value(data, IT87_REG_FAN_CTL, - tmp | 0x87); - for (i = 0; i < 3; i++) - it87_write_value(data, - IT87_REG_PWM[i], - 0x7f & ~pwm[i]); - return 1; - } - - dev_info(dev, - "PWM configuration is too broken to be fixed\n"); - } - - dev_info(dev, - "Detected broken BIOS defaults, disabling PWM interface\n"); - return 0; - } else if (fix_pwm_polarity) { - dev_info(dev, - "PWM configuration looks sane, won't touch\n"); - } - - return 1; -} - -/* Called when we have found a new IT87. */ -static void it87_init_device(struct platform_device *pdev) -{ - struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev); - struct it87_data *data = platform_get_drvdata(pdev); - int tmp, i; - u8 mask; - - /* - * For each PWM channel: - * - If it is in automatic mode, setting to manual mode should set - * the fan to full speed by default. - * - If it is in manual mode, we need a mapping to temperature - * channels to use when later setting to automatic mode later. - * Use a 1:1 mapping by default (we are clueless.) - * In both cases, the value can (and should) be changed by the user - * prior to switching to a different mode. - * Note that this is no longer needed for the IT8721F and later, as - * these have separate registers for the temperature mapping and the - * manual duty cycle. - */ - for (i = 0; i < 3; i++) { - data->pwm_temp_map[i] = i; - data->pwm_duty[i] = 0x7f; /* Full speed */ - data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */ - } - - /* - * Some chips seem to have default value 0xff for all limit - * registers. For low voltage limits it makes no sense and triggers - * alarms, so change to 0 instead. For high temperature limits, it - * means -1 degree C, which surprisingly doesn't trigger an alarm, - * but is still confusing, so change to 127 degrees C. - */ - for (i = 0; i < 8; i++) { - tmp = it87_read_value(data, IT87_REG_VIN_MIN(i)); - if (tmp == 0xff) - it87_write_value(data, IT87_REG_VIN_MIN(i), 0); - } - for (i = 0; i < 3; i++) { - tmp = it87_read_value(data, IT87_REG_TEMP_HIGH(i)); - if (tmp == 0xff) - it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127); - } - - /* - * Temperature channels are not forcibly enabled, as they can be - * set to two different sensor types and we can't guess which one - * is correct for a given system. These channels can be enabled at - * run-time through the temp{1-3}_type sysfs accessors if needed. - */ - - /* Check if voltage monitors are reset manually or by some reason */ - tmp = it87_read_value(data, IT87_REG_VIN_ENABLE); - if ((tmp & 0xff) == 0) { - /* Enable all voltage monitors */ - it87_write_value(data, IT87_REG_VIN_ENABLE, 0xff); - } - - /* Check if tachometers are reset manually or by some reason */ - mask = 0x70 & ~(sio_data->skip_fan << 4); - data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); - if ((data->fan_main_ctrl & mask) == 0) { - /* Enable all fan tachometers */ - data->fan_main_ctrl |= mask; - it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, - data->fan_main_ctrl); - } - data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; - - tmp = it87_read_value(data, IT87_REG_FAN_16BIT); - - /* Set tachometers to 16-bit mode if needed */ - if (has_fan16_config(data)) { - if (~tmp & 0x07 & data->has_fan) { - dev_dbg(&pdev->dev, - "Setting fan1-3 to 16-bit mode\n"); - it87_write_value(data, IT87_REG_FAN_16BIT, - tmp | 0x07); - } - } - - /* Check for additional fans */ - if (has_five_fans(data)) { - if (tmp & (1 << 4)) - data->has_fan |= (1 << 3); /* fan4 enabled */ - if (tmp & (1 << 5)) - data->has_fan |= (1 << 4); /* fan5 enabled */ - if (has_six_fans(data) && (tmp & (1 << 2))) - data->has_fan |= (1 << 5); /* fan6 enabled */ - } - - /* Fan input pins may be used for alternative functions */ - data->has_fan &= ~sio_data->skip_fan; - - /* Check if pwm5, pwm6 are enabled */ - if (has_six_pwm(data)) { - /* The following code may be IT8620E specific */ - tmp = it87_read_value(data, IT87_REG_FAN_DIV); - if ((tmp & 0xc0) == 0xc0) - sio_data->skip_pwm |= (1 << 4); - if (!(tmp & (1 << 3))) - sio_data->skip_pwm |= (1 << 5); - } - - /* Start monitoring */ - it87_write_value(data, IT87_REG_CONFIG, - (it87_read_value(data, IT87_REG_CONFIG) & 0x3e) - | (update_vbat ? 0x41 : 0x01)); -} - -static void it87_update_pwm_ctrl(struct it87_data *data, int nr) -{ - data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM[nr]); - if (has_newer_autopwm(data)) { - data->pwm_temp_map[nr] = (data->pwm_ctrl[nr] & 0x03) + - nr < 3 ? 0 : 3; - data->pwm_duty[nr] = it87_read_value(data, - IT87_REG_PWM_DUTY[nr]); - } else { - if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */ - data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; - else /* Manual mode */ - data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f; - } - - if (has_old_autopwm(data)) { - int i; - - for (i = 0; i < 5 ; i++) - data->auto_temp[nr][i] = it87_read_value(data, - IT87_REG_AUTO_TEMP(nr, i)); - for (i = 0; i < 3 ; i++) - data->auto_pwm[nr][i] = it87_read_value(data, - IT87_REG_AUTO_PWM(nr, i)); - } -} - -static struct it87_data *it87_update_device(struct device *dev) -{ - struct it87_data *data = dev_get_drvdata(dev); - int i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - if (update_vbat) { - /* - * Cleared after each update, so reenable. Value - * returned by this read will be previous value - */ - it87_write_value(data, IT87_REG_CONFIG, - it87_read_value(data, IT87_REG_CONFIG) | 0x40); - } - for (i = 0; i <= 7; i++) { - data->in[i][0] = - it87_read_value(data, IT87_REG_VIN(i)); - data->in[i][1] = - it87_read_value(data, IT87_REG_VIN_MIN(i)); - data->in[i][2] = - it87_read_value(data, IT87_REG_VIN_MAX(i)); - } - /* in8 (battery) has no limit registers */ - data->in[8][0] = it87_read_value(data, IT87_REG_VIN(8)); - if (has_avcc3(data)) - data->in[9][0] = it87_read_value(data, IT87_REG_AVCC3); - - for (i = 0; i < 6; i++) { - /* Skip disabled fans */ - if (!(data->has_fan & (1 << i))) - continue; - - data->fan[i][1] = - it87_read_value(data, IT87_REG_FAN_MIN[i]); - data->fan[i][0] = it87_read_value(data, - IT87_REG_FAN[i]); - /* Add high byte if in 16-bit mode */ - if (has_16bit_fans(data)) { - data->fan[i][0] |= it87_read_value(data, - IT87_REG_FANX[i]) << 8; - data->fan[i][1] |= it87_read_value(data, - IT87_REG_FANX_MIN[i]) << 8; - } - } - for (i = 0; i < 3; i++) { - if (!(data->has_temp & (1 << i))) - continue; - data->temp[i][0] = - it87_read_value(data, IT87_REG_TEMP(i)); - data->temp[i][1] = - it87_read_value(data, IT87_REG_TEMP_LOW(i)); - data->temp[i][2] = - it87_read_value(data, IT87_REG_TEMP_HIGH(i)); - if (has_temp_offset(data)) - data->temp[i][3] = - it87_read_value(data, - IT87_REG_TEMP_OFFSET[i]); - } - - /* Newer chips don't have clock dividers */ - if ((data->has_fan & 0x07) && !has_16bit_fans(data)) { - i = it87_read_value(data, IT87_REG_FAN_DIV); - data->fan_div[0] = i & 0x07; - data->fan_div[1] = (i >> 3) & 0x07; - data->fan_div[2] = (i & 0x40) ? 3 : 1; - } - - data->alarms = - it87_read_value(data, IT87_REG_ALARM1) | - (it87_read_value(data, IT87_REG_ALARM2) << 8) | - (it87_read_value(data, IT87_REG_ALARM3) << 16); - data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE); - - data->fan_main_ctrl = it87_read_value(data, - IT87_REG_FAN_MAIN_CTRL); - data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL); - for (i = 0; i < 6; i++) - it87_update_pwm_ctrl(data, i); - - data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); - data->extra = it87_read_value(data, IT87_REG_TEMP_EXTRA); - /* - * The IT8705F does not have VID capability. - * The IT8718F and later don't use IT87_REG_VID for the - * same purpose. - */ - if (data->type == it8712 || data->type == it8716) { - data->vid = it87_read_value(data, IT87_REG_VID); - /* - * The older IT8712F revisions had only 5 VID pins, - * but we assume it is always safe to read 6 bits. - */ - data->vid &= 0x3f; - } - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - - return data; -} +static struct platform_driver it87_driver = { + .driver = { + .name = DRVNAME, + }, + .probe = it87_probe, + .remove = it87_remove, +}; static int __init it87_device_add(int index, unsigned short address, const struct it87_sio_data *sio_data) From 52929715634ad36782bd7018ab0bf59a6619c393 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 30 Mar 2013 14:00:08 -0700 Subject: [PATCH 18/35] hwmon: (it87) Use is_visible for voltage sensors Simplify code and reduce object size by more than 300 bytes on x86_64. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 124 +++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 68 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 4b38ecb91959..81c11d1d67e2 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -471,6 +471,7 @@ struct it87_data { unsigned long last_updated; /* In jiffies */ u16 in_scaled; /* Internal voltage sensors are scaled */ + u16 has_in; /* Bitfield, voltage sensors enabled */ u8 in[10][3]; /* [nr][0]=in, [1]=min, [2]=max */ u8 has_fan; /* Bitfield, fans enabled */ u16 fan[6][2]; /* Register values, [nr][0]=fan, [1]=min */ @@ -482,6 +483,7 @@ struct it87_data { u8 vid; /* Register encoding, combined */ u8 vrm; u32 alarms; /* Register encoding, combined */ + bool has_beep; /* true if beep supported */ u8 beeps; /* Register encoding */ u8 fan_main_ctrl; /* Register value */ u8 fan_ctl; /* Register value */ @@ -1751,74 +1753,85 @@ static ssize_t show_name(struct device *dev, struct device_attribute } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static struct attribute *it87_attributes_in[10][5] = { +static umode_t it87_in_is_visible(struct kobject *kobj, + struct attribute *attr, int index) { + struct device *dev = container_of(kobj, struct device, kobj); + struct it87_data *data = dev_get_drvdata(dev); + int i = index / 5; /* voltage index */ + int a = index % 5; /* attribute index */ + + if (index >= 40) { /* in8, in9 only have input attributes */ + i = index - 40 + 8; + a = 0; + } + + if (!(data->has_in & (1 << i))) + return 0; + + if (a == 4 && !data->has_beep) + return 0; + + return attr->mode; +} + +static struct attribute *it87_attributes_in[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_min.dev_attr.attr, &sensor_dev_attr_in0_max.dev_attr.attr, &sensor_dev_attr_in0_alarm.dev_attr.attr, - NULL -}, { + &sensor_dev_attr_in0_beep.dev_attr.attr, /* 4 */ + &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_min.dev_attr.attr, &sensor_dev_attr_in1_max.dev_attr.attr, &sensor_dev_attr_in1_alarm.dev_attr.attr, - NULL -}, { + &sensor_dev_attr_in1_beep.dev_attr.attr, /* 9 */ + &sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_in2_min.dev_attr.attr, &sensor_dev_attr_in2_max.dev_attr.attr, &sensor_dev_attr_in2_alarm.dev_attr.attr, - NULL -}, { + &sensor_dev_attr_in2_beep.dev_attr.attr, /* 14 */ + &sensor_dev_attr_in3_input.dev_attr.attr, &sensor_dev_attr_in3_min.dev_attr.attr, &sensor_dev_attr_in3_max.dev_attr.attr, &sensor_dev_attr_in3_alarm.dev_attr.attr, - NULL -}, { + &sensor_dev_attr_in3_beep.dev_attr.attr, /* 19 */ + &sensor_dev_attr_in4_input.dev_attr.attr, &sensor_dev_attr_in4_min.dev_attr.attr, &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, - NULL -}, { + &sensor_dev_attr_in4_beep.dev_attr.attr, /* 24 */ + &sensor_dev_attr_in5_input.dev_attr.attr, &sensor_dev_attr_in5_min.dev_attr.attr, &sensor_dev_attr_in5_max.dev_attr.attr, &sensor_dev_attr_in5_alarm.dev_attr.attr, - NULL -}, { + &sensor_dev_attr_in5_beep.dev_attr.attr, /* 29 */ + &sensor_dev_attr_in6_input.dev_attr.attr, &sensor_dev_attr_in6_min.dev_attr.attr, &sensor_dev_attr_in6_max.dev_attr.attr, &sensor_dev_attr_in6_alarm.dev_attr.attr, - NULL -}, { + &sensor_dev_attr_in6_beep.dev_attr.attr, /* 34 */ + &sensor_dev_attr_in7_input.dev_attr.attr, &sensor_dev_attr_in7_min.dev_attr.attr, &sensor_dev_attr_in7_max.dev_attr.attr, &sensor_dev_attr_in7_alarm.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_in8_input.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_in9_input.dev_attr.attr, - NULL -} }; + &sensor_dev_attr_in7_beep.dev_attr.attr, /* 39 */ -static const struct attribute_group it87_group_in[10] = { - { .attrs = it87_attributes_in[0] }, - { .attrs = it87_attributes_in[1] }, - { .attrs = it87_attributes_in[2] }, - { .attrs = it87_attributes_in[3] }, - { .attrs = it87_attributes_in[4] }, - { .attrs = it87_attributes_in[5] }, - { .attrs = it87_attributes_in[6] }, - { .attrs = it87_attributes_in[7] }, - { .attrs = it87_attributes_in[8] }, - { .attrs = it87_attributes_in[9] }, + &sensor_dev_attr_in8_input.dev_attr.attr, /* 40 */ + + &sensor_dev_attr_in9_input.dev_attr.attr, /* 41 */ +}; + +static const struct attribute_group it87_group_in = { + .attrs = it87_attributes_in, + .is_visible = it87_in_is_visible, }; static struct attribute *it87_attributes_temp[3][6] = { @@ -1868,19 +1881,6 @@ static const struct attribute_group it87_group = { .attrs = it87_attributes, }; -static struct attribute *it87_attributes_in_beep[] = { - &sensor_dev_attr_in0_beep.dev_attr.attr, - &sensor_dev_attr_in1_beep.dev_attr.attr, - &sensor_dev_attr_in2_beep.dev_attr.attr, - &sensor_dev_attr_in3_beep.dev_attr.attr, - &sensor_dev_attr_in4_beep.dev_attr.attr, - &sensor_dev_attr_in5_beep.dev_attr.attr, - &sensor_dev_attr_in6_beep.dev_attr.attr, - &sensor_dev_attr_in7_beep.dev_attr.attr, - NULL, - NULL, -}; - static struct attribute *it87_attributes_temp_beep[] = { &sensor_dev_attr_temp1_beep.dev_attr.attr, &sensor_dev_attr_temp2_beep.dev_attr.attr, @@ -2435,14 +2435,8 @@ static void it87_remove_files(struct device *dev) int i; sysfs_remove_group(&dev->kobj, &it87_group); - for (i = 0; i < 10; i++) { - if (sio_data->skip_in & (1 << i)) - continue; - sysfs_remove_group(&dev->kobj, &it87_group_in[i]); - if (it87_attributes_in_beep[i]) - sysfs_remove_file(&dev->kobj, - it87_attributes_in_beep[i]); - } + sysfs_remove_group(&dev->kobj, &it87_group_in); + for (i = 0; i < 3; i++) { if (!(data->has_temp & (1 << i))) continue; @@ -2736,6 +2730,10 @@ static int it87_probe(struct platform_device *pdev) data->has_temp &= ~(1 << 2); } + data->has_in = 0x3ff & ~sio_data->skip_in; + + data->has_beep = !!sio_data->beep_pin; + /* Initialize the IT87 chip */ it87_init_device(pdev); @@ -2744,19 +2742,9 @@ static int it87_probe(struct platform_device *pdev) if (err) return err; - for (i = 0; i < 10; i++) { - if (sio_data->skip_in & (1 << i)) - continue; - err = sysfs_create_group(&dev->kobj, &it87_group_in[i]); - if (err) - goto error; - if (sio_data->beep_pin && it87_attributes_in_beep[i]) { - err = sysfs_create_file(&dev->kobj, - it87_attributes_in_beep[i]); - if (err) - goto error; - } - } + err = sysfs_create_group(&dev->kobj, &it87_group_in); + if (err) + goto error; for (i = 0; i < 3; i++) { if (!(data->has_temp & (1 << i))) From 87533770be36ecea55b86fa377ba4232d720df23 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 30 Mar 2013 14:23:20 -0700 Subject: [PATCH 19/35] hwmon: (it87) Use is_visible for temperature sensors Simplify code and reduce object size by more than 200 bytes on x86_64. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 87 ++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 51 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 81c11d1d67e2..c7feea9a6f80 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1834,40 +1834,57 @@ static const struct attribute_group it87_group_in = { .is_visible = it87_in_is_visible, }; -static struct attribute *it87_attributes_temp[3][6] = { +static umode_t it87_temp_is_visible(struct kobject *kobj, + struct attribute *attr, int index) { + struct device *dev = container_of(kobj, struct device, kobj); + struct it87_data *data = dev_get_drvdata(dev); + int i = index / 7; /* temperature index */ + int a = index % 7; /* attribute index */ + + if (!(data->has_temp & (1 << i))) + return 0; + + if (a == 5 && !has_temp_offset(data)) + return 0; + + if (a == 6 && !data->has_beep) + return 0; + + return attr->mode; +} + +static struct attribute *it87_attributes_temp[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_temp1_type.dev_attr.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr, - NULL -} , { + &sensor_dev_attr_temp1_offset.dev_attr.attr, /* 5 */ + &sensor_dev_attr_temp1_beep.dev_attr.attr, /* 6 */ + &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp2_type.dev_attr.attr, &sensor_dev_attr_temp2_alarm.dev_attr.attr, - NULL -} , { + &sensor_dev_attr_temp2_offset.dev_attr.attr, + &sensor_dev_attr_temp2_beep.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp3_type.dev_attr.attr, &sensor_dev_attr_temp3_alarm.dev_attr.attr, - NULL -} }; + &sensor_dev_attr_temp3_offset.dev_attr.attr, + &sensor_dev_attr_temp3_beep.dev_attr.attr, -static const struct attribute_group it87_group_temp[3] = { - { .attrs = it87_attributes_temp[0] }, - { .attrs = it87_attributes_temp[1] }, - { .attrs = it87_attributes_temp[2] }, + NULL }; -static struct attribute *it87_attributes_temp_offset[] = { - &sensor_dev_attr_temp1_offset.dev_attr.attr, - &sensor_dev_attr_temp2_offset.dev_attr.attr, - &sensor_dev_attr_temp3_offset.dev_attr.attr, +static const struct attribute_group it87_group_temp = { + .attrs = it87_attributes_temp, + .is_visible = it87_temp_is_visible, }; static struct attribute *it87_attributes[] = { @@ -1881,12 +1898,6 @@ static const struct attribute_group it87_group = { .attrs = it87_attributes, }; -static struct attribute *it87_attributes_temp_beep[] = { - &sensor_dev_attr_temp1_beep.dev_attr.attr, - &sensor_dev_attr_temp2_beep.dev_attr.attr, - &sensor_dev_attr_temp3_beep.dev_attr.attr, -}; - static struct attribute *it87_attributes_fan[6][3+1] = { { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, @@ -2436,18 +2447,8 @@ static void it87_remove_files(struct device *dev) sysfs_remove_group(&dev->kobj, &it87_group); sysfs_remove_group(&dev->kobj, &it87_group_in); + sysfs_remove_group(&dev->kobj, &it87_group_temp); - for (i = 0; i < 3; i++) { - if (!(data->has_temp & (1 << i))) - continue; - sysfs_remove_group(&dev->kobj, &it87_group_temp[i]); - if (has_temp_offset(data)) - sysfs_remove_file(&dev->kobj, - it87_attributes_temp_offset[i]); - if (sio_data->beep_pin) - sysfs_remove_file(&dev->kobj, - it87_attributes_temp_beep[i]); - } for (i = 0; i < 6; i++) { if (!(data->has_fan & (1 << i))) continue; @@ -2746,25 +2747,9 @@ static int it87_probe(struct platform_device *pdev) if (err) goto error; - for (i = 0; i < 3; i++) { - if (!(data->has_temp & (1 << i))) - continue; - err = sysfs_create_group(&dev->kobj, &it87_group_temp[i]); - if (err) - goto error; - if (has_temp_offset(data)) { - err = sysfs_create_file(&dev->kobj, - it87_attributes_temp_offset[i]); - if (err) - goto error; - } - if (sio_data->beep_pin) { - err = sysfs_create_file(&dev->kobj, - it87_attributes_temp_beep[i]); - if (err) - goto error; - } - } + err = sysfs_create_group(&dev->kobj, &it87_group_temp); + if (err) + goto error; /* Do not create fan files for disabled fans */ fan_beep_need_rw = 1; From 9a70ee814d7093a2806e94b99ce12b8d35c6102e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 30 Mar 2013 14:51:20 -0700 Subject: [PATCH 20/35] hwmon: (it87) Use is_visible for fan attributes Simplify code and reduce object size by almost 500 bytes on x86_64. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 140 +++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 85 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index c7feea9a6f80..e70d227c2bf8 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1898,51 +1898,75 @@ static const struct attribute_group it87_group = { .attrs = it87_attributes, }; -static struct attribute *it87_attributes_fan[6][3+1] = { { +static umode_t it87_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct it87_data *data = dev_get_drvdata(dev); + int i = index / 5; /* fan index */ + int a = index % 5; /* attribute index */ + + if (index >= 15) { /* fan 4..6 don't have divisor attributes */ + i = (index - 15) / 4 + 3; + a = (index - 15) % 4; + } + + if (!(data->has_fan & (1 << i))) + return 0; + + if (a == 3) { /* beep */ + if (!data->has_beep) + return 0; + /* first fan beep attribute is writable */ + if (i == __ffs(data->has_fan)) + return attr->mode | S_IWUSR; + } + + if (a == 4 && has_16bit_fans(data)) /* divisor */ + return 0; + + return attr->mode; +} + +static struct attribute *it87_attributes_fan[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_alarm.dev_attr.attr, - NULL -}, { + &sensor_dev_attr_fan1_beep.dev_attr.attr, /* 3 */ + &sensor_dev_attr_fan1_div.dev_attr.attr, /* 4 */ + &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan2_min.dev_attr.attr, &sensor_dev_attr_fan2_alarm.dev_attr.attr, - NULL -}, { + &sensor_dev_attr_fan2_beep.dev_attr.attr, + &sensor_dev_attr_fan2_div.dev_attr.attr, /* 9 */ + &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan3_min.dev_attr.attr, &sensor_dev_attr_fan3_alarm.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan3_beep.dev_attr.attr, + &sensor_dev_attr_fan3_div.dev_attr.attr, /* 14 */ + + &sensor_dev_attr_fan4_input.dev_attr.attr, /* 15 */ &sensor_dev_attr_fan4_min.dev_attr.attr, &sensor_dev_attr_fan4_alarm.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan4_beep.dev_attr.attr, + + &sensor_dev_attr_fan5_input.dev_attr.attr, /* 19 */ &sensor_dev_attr_fan5_min.dev_attr.attr, &sensor_dev_attr_fan5_alarm.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan5_beep.dev_attr.attr, + + &sensor_dev_attr_fan6_input.dev_attr.attr, /* 23 */ &sensor_dev_attr_fan6_min.dev_attr.attr, &sensor_dev_attr_fan6_alarm.dev_attr.attr, + &sensor_dev_attr_fan6_beep.dev_attr.attr, NULL -} }; - -static const struct attribute_group it87_group_fan[6] = { - { .attrs = it87_attributes_fan[0] }, - { .attrs = it87_attributes_fan[1] }, - { .attrs = it87_attributes_fan[2] }, - { .attrs = it87_attributes_fan[3] }, - { .attrs = it87_attributes_fan[4] }, - { .attrs = it87_attributes_fan[5] }, }; -static const struct attribute *it87_attributes_fan_div[] = { - &sensor_dev_attr_fan1_div.dev_attr.attr, - &sensor_dev_attr_fan2_div.dev_attr.attr, - &sensor_dev_attr_fan3_div.dev_attr.attr, +static const struct attribute_group it87_group_fan = { + .attrs = it87_attributes_fan, + .is_visible = it87_fan_is_visible, }; static struct attribute *it87_attributes_pwm[6][4+1] = { { @@ -2046,15 +2070,6 @@ static const struct attribute_group it87_group_autopwm[3] = { { .attrs = it87_attributes_autopwm[2] }, }; -static struct attribute *it87_attributes_fan_beep[] = { - &sensor_dev_attr_fan1_beep.dev_attr.attr, - &sensor_dev_attr_fan2_beep.dev_attr.attr, - &sensor_dev_attr_fan3_beep.dev_attr.attr, - &sensor_dev_attr_fan4_beep.dev_attr.attr, - &sensor_dev_attr_fan5_beep.dev_attr.attr, - &sensor_dev_attr_fan6_beep.dev_attr.attr, -}; - static struct attribute *it87_attributes_vid[] = { &dev_attr_vrm.attr, &dev_attr_cpu0_vid.attr, @@ -2448,18 +2463,8 @@ static void it87_remove_files(struct device *dev) sysfs_remove_group(&dev->kobj, &it87_group); sysfs_remove_group(&dev->kobj, &it87_group_in); sysfs_remove_group(&dev->kobj, &it87_group_temp); + sysfs_remove_group(&dev->kobj, &it87_group_fan); - for (i = 0; i < 6; i++) { - if (!(data->has_fan & (1 << i))) - continue; - sysfs_remove_group(&dev->kobj, &it87_group_fan[i]); - if (sio_data->beep_pin) - sysfs_remove_file(&dev->kobj, - it87_attributes_fan_beep[i]); - if (i < 3 && !has_16bit_fans(data)) - sysfs_remove_file(&dev->kobj, - it87_attributes_fan_div[i]); - } for (i = 0; i < 6; i++) { if (sio_data->skip_pwm & (1 << i)) continue; @@ -2650,7 +2655,6 @@ static int it87_probe(struct platform_device *pdev) struct it87_sio_data *sio_data = dev_get_platdata(dev); int err = 0, i; int enable_pwm_interface; - int fan_beep_need_rw; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, IT87_EC_EXTENT, @@ -2751,43 +2755,9 @@ static int it87_probe(struct platform_device *pdev) if (err) goto error; - /* Do not create fan files for disabled fans */ - fan_beep_need_rw = 1; - for (i = 0; i < 6; i++) { - if (!(data->has_fan & (1 << i))) - continue; - err = sysfs_create_group(&dev->kobj, &it87_group_fan[i]); - if (err) - goto error; - - if (i < 3 && !has_16bit_fans(data)) { - err = sysfs_create_file(&dev->kobj, - it87_attributes_fan_div[i]); - if (err) - goto error; - } - - if (sio_data->beep_pin) { - err = sysfs_create_file(&dev->kobj, - it87_attributes_fan_beep[i]); - if (err) - goto error; - if (!fan_beep_need_rw) - continue; - - /* - * As we have a single beep enable bit for all fans, - * only the first enabled fan has a writable attribute - * for it. - */ - if (sysfs_chmod_file(&dev->kobj, - it87_attributes_fan_beep[i], - S_IRUGO | S_IWUSR)) - dev_dbg(dev, "chmod +w fan%d_beep failed\n", - i + 1); - fan_beep_need_rw = 0; - } - } + err = sysfs_create_group(&dev->kobj, &it87_group_fan); + if (err) + goto error; if (enable_pwm_interface) { for (i = 0; i < 6; i++) { From 5c3912616d37bcc292ae09a8be7f1a58d54f19f9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 30 Mar 2013 15:02:12 -0700 Subject: [PATCH 21/35] hwmon: (it87) Use is_visible for pwm attributes Simplify code and reduce object size by about 250 bytes on x86_64. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 183 +++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 95 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index e70d227c2bf8..0c13d640bcc7 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -498,6 +498,7 @@ struct it87_data { * is no longer needed, but it is still done to keep the driver * simple. */ + u8 has_pwm; /* Bitfield, pwm control enabled */ u8 pwm_ctrl[6]; /* Register value */ u8 pwm_duty[6]; /* Manual PWM value set by user */ u8 pwm_temp_map[6]; /* PWM to temp. chan. mapping (bits 1-0) */ @@ -1340,15 +1341,6 @@ static ssize_t set_pwm_temp_map(struct device *dev, long val; u8 reg; - /* - * This check can go away if we ever support automatic fan speed - * control on newer chips. - */ - if (!has_old_autopwm(data)) { - dev_notice(dev, "Mapping change disabled for safety reasons\n"); - return -EINVAL; - } - if (kstrtol(buf, 10, &val) < 0) return -EINVAL; @@ -1482,7 +1474,7 @@ static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0); static SENSOR_DEVICE_ATTR(pwm1_freq, S_IRUGO | S_IWUSR, show_pwm_freq, set_pwm_freq, 0); -static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, 0); static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm, 0, 0); @@ -1507,7 +1499,7 @@ static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 1); static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 1); static SENSOR_DEVICE_ATTR(pwm2_freq, S_IRUGO, show_pwm_freq, set_pwm_freq, 1); -static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, 1); static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm, 1, 0); @@ -1532,7 +1524,7 @@ static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 2); static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 2); static SENSOR_DEVICE_ATTR(pwm3_freq, S_IRUGO, show_pwm_freq, NULL, 2); -static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, 2); static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm, 2, 0); @@ -1557,21 +1549,21 @@ static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 3); static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 3); static SENSOR_DEVICE_ATTR(pwm4_freq, S_IRUGO, show_pwm_freq, NULL, 3); -static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, 3); static SENSOR_DEVICE_ATTR(pwm5_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 4); static SENSOR_DEVICE_ATTR(pwm5, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 4); static SENSOR_DEVICE_ATTR(pwm5_freq, S_IRUGO, show_pwm_freq, NULL, 4); -static SENSOR_DEVICE_ATTR(pwm5_auto_channels_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR(pwm5_auto_channels_temp, S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, 4); static SENSOR_DEVICE_ATTR(pwm6_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 5); static SENSOR_DEVICE_ATTR(pwm6, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 5); static SENSOR_DEVICE_ATTR(pwm6_freq, S_IRUGO, show_pwm_freq, NULL, 5); -static SENSOR_DEVICE_ATTR(pwm6_auto_channels_temp, S_IRUGO | S_IWUSR, +static SENSOR_DEVICE_ATTR(pwm6_auto_channels_temp, S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, 5); /* Alarms */ @@ -1969,67 +1961,81 @@ static const struct attribute_group it87_group_fan = { .is_visible = it87_fan_is_visible, }; -static struct attribute *it87_attributes_pwm[6][4+1] = { { - &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm1_freq.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm2_freq.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_pwm3_enable.dev_attr.attr, - &sensor_dev_attr_pwm3.dev_attr.attr, - &sensor_dev_attr_pwm3_freq.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_pwm4_enable.dev_attr.attr, - &sensor_dev_attr_pwm4.dev_attr.attr, - &sensor_dev_attr_pwm4_freq.dev_attr.attr, - &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_pwm5_enable.dev_attr.attr, - &sensor_dev_attr_pwm5.dev_attr.attr, - &sensor_dev_attr_pwm5_freq.dev_attr.attr, - &sensor_dev_attr_pwm5_auto_channels_temp.dev_attr.attr, - NULL -}, { - &sensor_dev_attr_pwm6_enable.dev_attr.attr, - &sensor_dev_attr_pwm6.dev_attr.attr, - &sensor_dev_attr_pwm6_freq.dev_attr.attr, - &sensor_dev_attr_pwm6_auto_channels_temp.dev_attr.attr, - NULL -} }; - -static umode_t pwm_attribute_mode(struct kobject *kobj, struct attribute *attr, - int index) +static umode_t it87_pwm_is_visible(struct kobject *kobj, + struct attribute *attr, int index) { struct device *dev = container_of(kobj, struct device, kobj); struct it87_data *data = dev_get_drvdata(dev); + int i = index / 4; /* pwm index */ + int a = index % 4; /* attribute index */ - if (has_pwm_freq2(data) && index == 2) + if (!(data->has_pwm & (1 << i))) + return 0; + + /* pwmX_auto_channels_temp is only writable for old auto pwm */ + if (a == 3 && has_old_autopwm(data)) + return attr->mode | S_IWUSR; + + /* pwm2_freq is writable if there are two pwm frequency selects */ + if (has_pwm_freq2(data) && i == 1 && a == 2) return attr->mode | S_IWUSR; return attr->mode; } -static const struct attribute_group it87_group_pwm[6] = { - { .attrs = it87_attributes_pwm[0] }, - { .attrs = it87_attributes_pwm[1], - .is_visible = pwm_attribute_mode, }, - { .attrs = it87_attributes_pwm[2] }, - { .attrs = it87_attributes_pwm[3] }, - { .attrs = it87_attributes_pwm[4] }, - { .attrs = it87_attributes_pwm[5] }, +static struct attribute *it87_attributes_pwm[] = { + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_freq.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, + + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_freq.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, + + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm3_freq.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, + + &sensor_dev_attr_pwm4_enable.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + &sensor_dev_attr_pwm4_freq.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr, + + &sensor_dev_attr_pwm5_enable.dev_attr.attr, + &sensor_dev_attr_pwm5.dev_attr.attr, + &sensor_dev_attr_pwm5_freq.dev_attr.attr, + &sensor_dev_attr_pwm5_auto_channels_temp.dev_attr.attr, + + &sensor_dev_attr_pwm6_enable.dev_attr.attr, + &sensor_dev_attr_pwm6.dev_attr.attr, + &sensor_dev_attr_pwm6_freq.dev_attr.attr, + &sensor_dev_attr_pwm6_auto_channels_temp.dev_attr.attr, + + NULL }; -static struct attribute *it87_attributes_autopwm[3][9+1] = { { +static const struct attribute_group it87_group_pwm = { + .attrs = it87_attributes_pwm, + .is_visible = it87_pwm_is_visible, +}; + +static umode_t it87_auto_pwm_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct it87_data *data = dev_get_drvdata(dev); + int i = index / 9; /* pwm index */ + + if (!(data->has_pwm & (1 << i))) + return 0; + + return attr->mode; +} + +static struct attribute *it87_attributes_auto_pwm[] = { &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, @@ -2039,8 +2045,7 @@ static struct attribute *it87_attributes_autopwm[3][9+1] = { { &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, - NULL -}, { + &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, @@ -2050,8 +2055,7 @@ static struct attribute *it87_attributes_autopwm[3][9+1] = { { &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, - NULL -}, { + &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, @@ -2061,13 +2065,13 @@ static struct attribute *it87_attributes_autopwm[3][9+1] = { { &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, - NULL -} }; -static const struct attribute_group it87_group_autopwm[3] = { - { .attrs = it87_attributes_autopwm[0] }, - { .attrs = it87_attributes_autopwm[1] }, - { .attrs = it87_attributes_autopwm[2] }, + NULL, +}; + +static const struct attribute_group it87_group_auto_pwm = { + .attrs = it87_attributes_auto_pwm, + .is_visible = it87_auto_pwm_is_visible, }; static struct attribute *it87_attributes_vid[] = { @@ -2456,23 +2460,15 @@ exit: static void it87_remove_files(struct device *dev) { - struct it87_data *data = dev_get_drvdata(dev); struct it87_sio_data *sio_data = dev_get_platdata(dev); - int i; sysfs_remove_group(&dev->kobj, &it87_group); sysfs_remove_group(&dev->kobj, &it87_group_in); sysfs_remove_group(&dev->kobj, &it87_group_temp); sysfs_remove_group(&dev->kobj, &it87_group_fan); + sysfs_remove_group(&dev->kobj, &it87_group_pwm); + sysfs_remove_group(&dev->kobj, &it87_group_auto_pwm); - for (i = 0; i < 6; i++) { - if (sio_data->skip_pwm & (1 << i)) - continue; - sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]); - if (has_old_autopwm(data)) - sysfs_remove_group(&dev->kobj, - &it87_group_autopwm[i]); - } if (!sio_data->skip_vid) sysfs_remove_group(&dev->kobj, &it87_group_vid); sysfs_remove_group(&dev->kobj, &it87_group_label); @@ -2760,18 +2756,15 @@ static int it87_probe(struct platform_device *pdev) goto error; if (enable_pwm_interface) { - for (i = 0; i < 6; i++) { - if (sio_data->skip_pwm & (1 << i)) - continue; - err = sysfs_create_group(&dev->kobj, - &it87_group_pwm[i]); - if (err) - goto error; + data->has_pwm = (1 << ARRAY_SIZE(IT87_REG_PWM)) - 1; + data->has_pwm &= ~sio_data->skip_pwm; - if (!has_old_autopwm(data)) - continue; + err = sysfs_create_group(&dev->kobj, &it87_group_pwm); + if (err) + goto error; + if (has_old_autopwm(data)) { err = sysfs_create_group(&dev->kobj, - &it87_group_autopwm[i]); + &it87_group_auto_pwm); if (err) goto error; } From d376684880beb603a11fb6593489a21398d2a491 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 30 Mar 2013 15:47:21 -0700 Subject: [PATCH 22/35] hwmon: (it87) Use single group and is_visible for miscellaneous attributes Use is_visible to determine if attributes should be generated or not. This simplifies the code and reduces object size by about 120 bytes on x86_64. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 81 ++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 0c13d640bcc7..e4fb466fd9c3 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -471,6 +471,7 @@ struct it87_data { unsigned long last_updated; /* In jiffies */ u16 in_scaled; /* Internal voltage sensors are scaled */ + u16 in_internal; /* Bitfield, internal sensors (for labels) */ u16 has_in; /* Bitfield, voltage sensors enabled */ u8 in[10][3]; /* [nr][0]=in, [1]=min, [2]=max */ u8 has_fan; /* Bitfield, fans enabled */ @@ -480,6 +481,7 @@ struct it87_data { u8 sensor; /* Register value (IT87_REG_TEMP_ENABLE) */ u8 extra; /* Register value (IT87_REG_TEMP_EXTRA) */ u8 fan_div[3]; /* Register encoding, shifted right */ + bool has_vid; /* True if VID supported */ u8 vid; /* Register encoding, combined */ u8 vrm; u32 alarms; /* Register encoding, combined */ @@ -1879,15 +1881,37 @@ static const struct attribute_group it87_group_temp = { .is_visible = it87_temp_is_visible, }; +static umode_t it87_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct it87_data *data = dev_get_drvdata(dev); + + if ((index == 3 || index == 4) && !data->has_vid) + return 0; + + if (index > 4 && !(data->in_internal & (1 << (index - 5)))) + return 0; + + return attr->mode; +} + static struct attribute *it87_attributes[] = { &dev_attr_alarms.attr, &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, &dev_attr_name.attr, + &dev_attr_vrm.attr, /* 3 */ + &dev_attr_cpu0_vid.attr, /* 4 */ + &sensor_dev_attr_in3_label.dev_attr.attr, /* 5 .. 8 */ + &sensor_dev_attr_in7_label.dev_attr.attr, + &sensor_dev_attr_in8_label.dev_attr.attr, + &sensor_dev_attr_in9_label.dev_attr.attr, NULL }; static const struct attribute_group it87_group = { .attrs = it87_attributes, + .is_visible = it87_is_visible, }; static umode_t it87_fan_is_visible(struct kobject *kobj, @@ -2074,28 +2098,6 @@ static const struct attribute_group it87_group_auto_pwm = { .is_visible = it87_auto_pwm_is_visible, }; -static struct attribute *it87_attributes_vid[] = { - &dev_attr_vrm.attr, - &dev_attr_cpu0_vid.attr, - NULL -}; - -static const struct attribute_group it87_group_vid = { - .attrs = it87_attributes_vid, -}; - -static struct attribute *it87_attributes_label[] = { - &sensor_dev_attr_in3_label.dev_attr.attr, - &sensor_dev_attr_in7_label.dev_attr.attr, - &sensor_dev_attr_in8_label.dev_attr.attr, - &sensor_dev_attr_in9_label.dev_attr.attr, - NULL -}; - -static const struct attribute_group it87_group_label = { - .attrs = it87_attributes_label, -}; - /* SuperIO detection - will change isa_address if a chip is found */ static int __init it87_find(int sioaddr, unsigned short *address, struct it87_sio_data *sio_data) @@ -2460,18 +2462,12 @@ exit: static void it87_remove_files(struct device *dev) { - struct it87_sio_data *sio_data = dev_get_platdata(dev); - sysfs_remove_group(&dev->kobj, &it87_group); sysfs_remove_group(&dev->kobj, &it87_group_in); sysfs_remove_group(&dev->kobj, &it87_group_temp); sysfs_remove_group(&dev->kobj, &it87_group_fan); sysfs_remove_group(&dev->kobj, &it87_group_pwm); sysfs_remove_group(&dev->kobj, &it87_group_auto_pwm); - - if (!sio_data->skip_vid) - sysfs_remove_group(&dev->kobj, &it87_group_vid); - sysfs_remove_group(&dev->kobj, &it87_group_label); } /* Called when we have found a new IT87. */ @@ -2649,8 +2645,8 @@ static int it87_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; struct it87_sio_data *sio_data = dev_get_platdata(dev); - int err = 0, i; int enable_pwm_interface; + int err = 0; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, IT87_EC_EXTENT, @@ -2731,6 +2727,7 @@ static int it87_probe(struct platform_device *pdev) data->has_temp &= ~(1 << 2); } + data->in_internal = sio_data->internal; data->has_in = 0x3ff & ~sio_data->skip_in; data->has_beep = !!sio_data->beep_pin; @@ -2738,6 +2735,13 @@ static int it87_probe(struct platform_device *pdev) /* Initialize the IT87 chip */ it87_init_device(pdev); + if (!sio_data->skip_vid) { + data->has_vid = true; + data->vrm = vid_which_vrm(); + /* VID reading from Super-I/O config space if available */ + data->vid = sio_data->vid_value; + } + /* Register sysfs hooks */ err = sysfs_create_group(&dev->kobj, &it87_group); if (err) @@ -2770,25 +2774,6 @@ static int it87_probe(struct platform_device *pdev) } } - if (!sio_data->skip_vid) { - data->vrm = vid_which_vrm(); - /* VID reading from Super-I/O config space if available */ - data->vid = sio_data->vid_value; - err = sysfs_create_group(&dev->kobj, &it87_group_vid); - if (err) - goto error; - } - - /* Export labels for internal sensors */ - for (i = 0; i < 4; i++) { - if (!(sio_data->internal & (1 << i))) - continue; - err = sysfs_create_file(&dev->kobj, - it87_attributes_label[i]); - if (err) - goto error; - } - data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); From 8638d0afb4df0d3cebcd10c3056dcb4a52432941 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 30 Mar 2015 11:13:49 -0700 Subject: [PATCH 23/35] hwmon: (it87) Convert to use new hwmon API Convert to use devm_hwmon_device_register_with_groups to simplify code and reduce code size. This also attaches sysfs attributes to the hwmon device and no longer to the platform device. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 93 +++++++++----------------------------------- 1 file changed, 19 insertions(+), 74 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index e4fb466fd9c3..e02c972ef81e 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -458,7 +458,7 @@ struct it87_sio_data { * The structure is dynamically allocated. */ struct it87_data { - struct device *hwmon_dev; + const struct attribute_group *groups[7]; enum chips type; u16 features; u8 peci_mask; @@ -1739,14 +1739,6 @@ static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_label, NULL, 2); /* AVCC3 */ static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 0); -static ssize_t show_name(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct it87_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->name); -} -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - static umode_t it87_in_is_visible(struct kobject *kobj, struct attribute *attr, int index) { @@ -1887,10 +1879,10 @@ static umode_t it87_is_visible(struct kobject *kobj, struct device *dev = container_of(kobj, struct device, kobj); struct it87_data *data = dev_get_drvdata(dev); - if ((index == 3 || index == 4) && !data->has_vid) + if ((index == 2 || index == 3) && !data->has_vid) return 0; - if (index > 4 && !(data->in_internal & (1 << (index - 5)))) + if (index > 3 && !(data->in_internal & (1 << (index - 4)))) return 0; return attr->mode; @@ -1899,10 +1891,9 @@ static umode_t it87_is_visible(struct kobject *kobj, static struct attribute *it87_attributes[] = { &dev_attr_alarms.attr, &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, - &dev_attr_name.attr, - &dev_attr_vrm.attr, /* 3 */ - &dev_attr_cpu0_vid.attr, /* 4 */ - &sensor_dev_attr_in3_label.dev_attr.attr, /* 5 .. 8 */ + &dev_attr_vrm.attr, /* 2 */ + &dev_attr_cpu0_vid.attr, /* 3 */ + &sensor_dev_attr_in3_label.dev_attr.attr, /* 4 .. 7 */ &sensor_dev_attr_in7_label.dev_attr.attr, &sensor_dev_attr_in8_label.dev_attr.attr, &sensor_dev_attr_in9_label.dev_attr.attr, @@ -2460,16 +2451,6 @@ exit: return err; } -static void it87_remove_files(struct device *dev) -{ - sysfs_remove_group(&dev->kobj, &it87_group); - sysfs_remove_group(&dev->kobj, &it87_group_in); - sysfs_remove_group(&dev->kobj, &it87_group_temp); - sysfs_remove_group(&dev->kobj, &it87_group_fan); - sysfs_remove_group(&dev->kobj, &it87_group_pwm); - sysfs_remove_group(&dev->kobj, &it87_group_auto_pwm); -} - /* Called when we have found a new IT87. */ static void it87_init_device(struct platform_device *pdev) { @@ -2646,7 +2627,7 @@ static int it87_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct it87_sio_data *sio_data = dev_get_platdata(dev); int enable_pwm_interface; - int err = 0; + struct device *hwmon_dev; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, IT87_EC_EXTENT, @@ -2666,7 +2647,6 @@ static int it87_probe(struct platform_device *pdev) data->features = it87_devices[sio_data->type].features; data->peci_mask = it87_devices[sio_data->type].peci_mask; data->old_peci_mask = it87_devices[sio_data->type].old_peci_mask; - data->name = it87_devices[sio_data->type].name; /* * IT8705F Datasheet 0.4.1, 3h == Version G. * IT8712F Datasheet 0.9.1, section 8.3.5 indicates 8h == Version J. @@ -2742,59 +2722,25 @@ static int it87_probe(struct platform_device *pdev) data->vid = sio_data->vid_value; } - /* Register sysfs hooks */ - err = sysfs_create_group(&dev->kobj, &it87_group); - if (err) - return err; - - err = sysfs_create_group(&dev->kobj, &it87_group_in); - if (err) - goto error; - - err = sysfs_create_group(&dev->kobj, &it87_group_temp); - if (err) - goto error; - - err = sysfs_create_group(&dev->kobj, &it87_group_fan); - if (err) - goto error; + /* Prepare for sysfs hooks */ + data->groups[0] = &it87_group; + data->groups[1] = &it87_group_in; + data->groups[2] = &it87_group_temp; + data->groups[3] = &it87_group_fan; if (enable_pwm_interface) { data->has_pwm = (1 << ARRAY_SIZE(IT87_REG_PWM)) - 1; data->has_pwm &= ~sio_data->skip_pwm; - err = sysfs_create_group(&dev->kobj, &it87_group_pwm); - if (err) - goto error; - if (has_old_autopwm(data)) { - err = sysfs_create_group(&dev->kobj, - &it87_group_auto_pwm); - if (err) - goto error; - } + data->groups[4] = &it87_group_pwm; + if (has_old_autopwm(data)) + data->groups[5] = &it87_group_auto_pwm; } - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error; - } - - return 0; - -error: - it87_remove_files(dev); - return err; -} - -static int it87_remove(struct platform_device *pdev) -{ - struct it87_data *data = platform_get_drvdata(pdev); - - hwmon_device_unregister(data->hwmon_dev); - it87_remove_files(&pdev->dev); - - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + it87_devices[sio_data->type].name, + data, data->groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } static struct platform_driver it87_driver = { @@ -2802,7 +2748,6 @@ static struct platform_driver it87_driver = { .name = DRVNAME, }, .probe = it87_probe, - .remove = it87_remove, }; static int __init it87_device_add(int index, unsigned short address, From cc18da79d9b7faf1ec617e2478d57d25d7b2f93a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 1 Apr 2015 10:03:01 -0700 Subject: [PATCH 24/35] hwmon: (it87) Support up to 6 temperature sensors on IT8620E Add support for the additional temperature sensors on IT8620E. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 48 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index e02c972ef81e..722d6de62d1c 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -219,7 +219,7 @@ static bool fix_pwm_polarity; #define IT87_REG_FAN_DIV 0x0b #define IT87_REG_FAN_16BIT 0x0c -/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ +/* Monitors: 9 voltage (0 to 7, battery), 6 temp (1 to 6), 3 fan (1 to 3) */ static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82, 0x4c }; static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86, 0x4e }; @@ -252,10 +252,12 @@ static const u8 IT87_REG_PWM_DUTY[] = { 0x63, 0x6b, 0x73, 0x7b, 0xa3, 0xab }; #define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i)) #define IT87_REG_AUTO_PWM(nr, i) (0x65 + (nr) * 8 + (i)) +#define IT87_REG_TEMP456_ENABLE 0x77 + struct it87_devices { const char *name; const char * const suffix; - u16 features; + u32 features; u8 peci_mask; u8 old_peci_mask; }; @@ -276,6 +278,7 @@ struct it87_devices { #define FEAT_AVCC3 (1 << 13) /* Chip supports in9/AVCC3 */ #define FEAT_SIX_PWM (1 << 14) /* Chip supports 6 pwm chn */ #define FEAT_PWM_FREQ2 (1 << 15) /* Separate pwm freq 2 */ +#define FEAT_SIX_TEMP (1 << 16) /* Up to 6 temp sensors */ static const struct it87_devices it87_devices[] = { [it87] = { @@ -412,7 +415,8 @@ static const struct it87_devices it87_devices[] = { .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS - | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2, + | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2 + | FEAT_SIX_TEMP, .peci_mask = 0x07, }, }; @@ -437,6 +441,7 @@ static const struct it87_devices it87_devices[] = { #define has_avcc3(data) ((data)->features & FEAT_AVCC3) #define has_six_pwm(data) ((data)->features & FEAT_SIX_PWM) #define has_pwm_freq2(data) ((data)->features & FEAT_PWM_FREQ2) +#define has_six_temp(data) ((data)->features & FEAT_SIX_TEMP) struct it87_sio_data { enum chips type; @@ -477,7 +482,7 @@ struct it87_data { u8 has_fan; /* Bitfield, fans enabled */ u16 fan[6][2]; /* Register values, [nr][0]=fan, [1]=min */ u8 has_temp; /* Bitfield, temp sensors enabled */ - s8 temp[3][4]; /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */ + s8 temp[6][4]; /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */ u8 sensor; /* Register value (IT87_REG_TEMP_ENABLE) */ u8 extra; /* Register value (IT87_REG_TEMP_EXTRA) */ u8 fan_div[3]; /* Register encoding, shifted right */ @@ -704,11 +709,16 @@ static struct it87_data *it87_update_device(struct device *dev) IT87_REG_FANX_MIN[i]) << 8; } } - for (i = 0; i < 3; i++) { + for (i = 0; i < 6; i++) { if (!(data->has_temp & (1 << i))) continue; data->temp[i][0] = it87_read_value(data, IT87_REG_TEMP(i)); + + /* No limits/offset for additional sensors */ + if (i >= 3) + continue; + data->temp[i][1] = it87_read_value(data, IT87_REG_TEMP_LOW(i)); data->temp[i][2] = @@ -848,7 +858,7 @@ static SENSOR_DEVICE_ATTR_2(in7_max, S_IRUGO | S_IWUSR, show_in, set_in, static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 8, 0); static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in, NULL, 9, 0); -/* 3 temperatures */ +/* Up to 6 temperatures */ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) { @@ -921,6 +931,9 @@ static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp, 2, 2); static SENSOR_DEVICE_ATTR_2(temp3_offset, S_IRUGO | S_IWUSR, show_temp, set_temp, 2, 3); +static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, 0); +static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, 0); static ssize_t show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) @@ -1828,6 +1841,11 @@ static umode_t it87_temp_is_visible(struct kobject *kobj, int i = index / 7; /* temperature index */ int a = index % 7; /* attribute index */ + if (index >= 21) { + i = index - 21 + 3; + a = 0; + } + if (!(data->has_temp & (1 << i))) return 0; @@ -1849,7 +1867,7 @@ static struct attribute *it87_attributes_temp[] = { &sensor_dev_attr_temp1_offset.dev_attr.attr, /* 5 */ &sensor_dev_attr_temp1_beep.dev_attr.attr, /* 6 */ - &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, /* 7 */ &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp2_type.dev_attr.attr, @@ -1857,7 +1875,7 @@ static struct attribute *it87_attributes_temp[] = { &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp2_beep.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, /* 14 */ &sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp3_type.dev_attr.attr, @@ -1865,6 +1883,9 @@ static struct attribute *it87_attributes_temp[] = { &sensor_dev_attr_temp3_offset.dev_attr.attr, &sensor_dev_attr_temp3_beep.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, /* 21 */ + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, NULL }; @@ -2710,6 +2731,17 @@ static int it87_probe(struct platform_device *pdev) data->in_internal = sio_data->internal; data->has_in = 0x3ff & ~sio_data->skip_in; + if (has_six_temp(data)) { + u8 reg = it87_read_value(data, IT87_REG_TEMP456_ENABLE); + + if ((reg & 0x03) >= 0x02) + data->has_temp |= (1 << 3); + if (((reg >> 2) & 0x03) >= 0x02) + data->has_temp |= (1 << 4); + if (((reg >> 4) & 0x03) >= 0x02) + data->has_temp |= (1 << 5); + } + data->has_beep = !!sio_data->beep_pin; /* Initialize the IT87 chip */ From 559313c4e9b29f5d0ba246f69d1052ef2fd9b423 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 1 Apr 2015 10:15:38 -0700 Subject: [PATCH 25/35] hwmon: (it87) Simplify reading voltage registers Voltage registers are non-sequential. Use a register array instead of a macro to map sensor index to register to simplify the code and to make it easier to add additional voltage sensors. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 722d6de62d1c..aa3ec50527a3 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -232,10 +232,10 @@ static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; static const u8 IT87_REG_PWM[] = { 0x15, 0x16, 0x17, 0x7f, 0xa7, 0xaf }; static const u8 IT87_REG_PWM_DUTY[] = { 0x63, 0x6b, 0x73, 0x7b, 0xa3, 0xab }; -#define IT87_REG_VIN(nr) (0x20 + (nr)) -#define IT87_REG_TEMP(nr) (0x29 + (nr)) +static const u8 IT87_REG_VIN[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x2f }; -#define IT87_REG_AVCC3 0x2f +#define IT87_REG_TEMP(nr) (0x29 + (nr)) #define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2) #define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2) @@ -679,18 +679,22 @@ static struct it87_data *it87_update_device(struct device *dev) it87_write_value(data, IT87_REG_CONFIG, it87_read_value(data, IT87_REG_CONFIG) | 0x40); } - for (i = 0; i <= 7; i++) { + for (i = 0; i < ARRAY_SIZE(IT87_REG_VIN); i++) { + if (!(data->has_in & (1 << i))) + continue; + data->in[i][0] = - it87_read_value(data, IT87_REG_VIN(i)); + it87_read_value(data, IT87_REG_VIN[i]); + + /* VBAT and AVCC don't have limit registers */ + if (i >= 8) + continue; + data->in[i][1] = it87_read_value(data, IT87_REG_VIN_MIN(i)); data->in[i][2] = it87_read_value(data, IT87_REG_VIN_MAX(i)); } - /* in8 (battery) has no limit registers */ - data->in[8][0] = it87_read_value(data, IT87_REG_VIN(8)); - if (has_avcc3(data)) - data->in[9][0] = it87_read_value(data, IT87_REG_AVCC3); for (i = 0; i < 6; i++) { /* Skip disabled fans */ From f838aa2611045fb1409c461fdd5102a6d8ae79f2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 1 Apr 2015 20:09:36 -0700 Subject: [PATCH 26/35] hwmon: (it87) Add support for VIN7 to VIN10 on IT8620E IT8620E supports three additional voltage sensors. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index aa3ec50527a3..96876755376c 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -219,7 +219,12 @@ static bool fix_pwm_polarity; #define IT87_REG_FAN_DIV 0x0b #define IT87_REG_FAN_16BIT 0x0c -/* Monitors: 9 voltage (0 to 7, battery), 6 temp (1 to 6), 3 fan (1 to 3) */ +/* + * Monitors: + * - up to 13 voltage (0 to 7, battery, avcc, 10 to 12) + * - up to 6 temp (1 to 6) + * - up to 6 fan (1 to 6) + */ static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82, 0x4c }; static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86, 0x4e }; @@ -233,7 +238,7 @@ static const u8 IT87_REG_PWM[] = { 0x15, 0x16, 0x17, 0x7f, 0xa7, 0xaf }; static const u8 IT87_REG_PWM_DUTY[] = { 0x63, 0x6b, 0x73, 0x7b, 0xa3, 0xab }; static const u8 IT87_REG_VIN[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, - 0x27, 0x28, 0x2f }; + 0x27, 0x28, 0x2f, 0x2c, 0x2d, 0x2e }; #define IT87_REG_TEMP(nr) (0x29 + (nr)) @@ -478,7 +483,7 @@ struct it87_data { u16 in_scaled; /* Internal voltage sensors are scaled */ u16 in_internal; /* Bitfield, internal sensors (for labels) */ u16 has_in; /* Bitfield, voltage sensors enabled */ - u8 in[10][3]; /* [nr][0]=in, [1]=min, [2]=max */ + u8 in[13][3]; /* [nr][0]=in, [1]=min, [2]=max */ u8 has_fan; /* Bitfield, fans enabled */ u16 fan[6][2]; /* Register values, [nr][0]=fan, [1]=min */ u8 has_temp; /* Bitfield, temp sensors enabled */ @@ -861,6 +866,9 @@ static SENSOR_DEVICE_ATTR_2(in7_max, S_IRUGO | S_IWUSR, show_in, set_in, static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 8, 0); static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in, NULL, 9, 0); +static SENSOR_DEVICE_ATTR_2(in10_input, S_IRUGO, show_in, NULL, 10, 0); +static SENSOR_DEVICE_ATTR_2(in11_input, S_IRUGO, show_in, NULL, 11, 0); +static SENSOR_DEVICE_ATTR_2(in12_input, S_IRUGO, show_in, NULL, 12, 0); /* Up to 6 temperatures */ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, @@ -1764,7 +1772,7 @@ static umode_t it87_in_is_visible(struct kobject *kobj, int i = index / 5; /* voltage index */ int a = index % 5; /* attribute index */ - if (index >= 40) { /* in8, in9 only have input attributes */ + if (index >= 40) { /* in8 and higher only have input attributes */ i = index - 40 + 8; a = 0; } @@ -1828,8 +1836,10 @@ static struct attribute *it87_attributes_in[] = { &sensor_dev_attr_in7_beep.dev_attr.attr, /* 39 */ &sensor_dev_attr_in8_input.dev_attr.attr, /* 40 */ - &sensor_dev_attr_in9_input.dev_attr.attr, /* 41 */ + &sensor_dev_attr_in10_input.dev_attr.attr, /* 41 */ + &sensor_dev_attr_in11_input.dev_attr.attr, /* 41 */ + &sensor_dev_attr_in12_input.dev_attr.attr, /* 41 */ }; static const struct attribute_group it87_group_in = { @@ -2738,12 +2748,21 @@ static int it87_probe(struct platform_device *pdev) if (has_six_temp(data)) { u8 reg = it87_read_value(data, IT87_REG_TEMP456_ENABLE); + /* Check for additional temperature sensors */ if ((reg & 0x03) >= 0x02) data->has_temp |= (1 << 3); if (((reg >> 2) & 0x03) >= 0x02) data->has_temp |= (1 << 4); if (((reg >> 4) & 0x03) >= 0x02) data->has_temp |= (1 << 5); + + /* Check for additional voltage sensors */ + if ((reg & 0x03) == 0x01) + data->has_in |= (1 << 10); + if (((reg >> 2) & 0x03) == 0x01) + data->has_in |= (1 << 11); + if (((reg >> 4) & 0x03) == 0x01) + data->has_in |= (1 << 12); } data->has_beep = !!sio_data->beep_pin; From 48b2ae7fe9d0eba523a3633ff0f939aba9e962e2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 2 Apr 2015 08:06:12 -0700 Subject: [PATCH 27/35] hwmon: (it87) Use BIT macro Using the BIT macro makes the code a little easier to read and has the added benefit of making checkpatch happy. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 290 ++++++++++++++++++++++--------------------- 1 file changed, 146 insertions(+), 144 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 96876755376c..0d6d106d53a2 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -52,6 +52,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -267,23 +268,23 @@ struct it87_devices { u8 old_peci_mask; }; -#define FEAT_12MV_ADC (1 << 0) -#define FEAT_NEWER_AUTOPWM (1 << 1) -#define FEAT_OLD_AUTOPWM (1 << 2) -#define FEAT_16BIT_FANS (1 << 3) -#define FEAT_TEMP_OFFSET (1 << 4) -#define FEAT_TEMP_PECI (1 << 5) -#define FEAT_TEMP_OLD_PECI (1 << 6) -#define FEAT_FAN16_CONFIG (1 << 7) /* Need to enable 16-bit fans */ -#define FEAT_FIVE_FANS (1 << 8) /* Supports five fans */ -#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) -#define FEAT_AVCC3 (1 << 13) /* Chip supports in9/AVCC3 */ -#define FEAT_SIX_PWM (1 << 14) /* Chip supports 6 pwm chn */ -#define FEAT_PWM_FREQ2 (1 << 15) /* Separate pwm freq 2 */ -#define FEAT_SIX_TEMP (1 << 16) /* Up to 6 temp sensors */ +#define FEAT_12MV_ADC BIT(0) +#define FEAT_NEWER_AUTOPWM BIT(1) +#define FEAT_OLD_AUTOPWM BIT(2) +#define FEAT_16BIT_FANS BIT(3) +#define FEAT_TEMP_OFFSET BIT(4) +#define FEAT_TEMP_PECI BIT(5) +#define FEAT_TEMP_OLD_PECI BIT(6) +#define FEAT_FAN16_CONFIG BIT(7) /* Need to enable 16-bit fans */ +#define FEAT_FIVE_FANS BIT(8) /* Supports five fans */ +#define FEAT_VID BIT(9) /* Set if chip supports VID */ +#define FEAT_IN7_INTERNAL BIT(10) /* Set if in7 is internal */ +#define FEAT_SIX_FANS BIT(11) /* Supports six fans */ +#define FEAT_10_9MV_ADC BIT(12) +#define FEAT_AVCC3 BIT(13) /* Chip supports in9/AVCC3 */ +#define FEAT_SIX_PWM BIT(14) /* Chip supports 6 pwm chn */ +#define FEAT_PWM_FREQ2 BIT(15) /* Separate pwm freq 2 */ +#define FEAT_SIX_TEMP BIT(16) /* Up to 6 temp sensors */ static const struct it87_devices it87_devices[] = { [it87] = { @@ -433,10 +434,10 @@ static const struct it87_devices it87_devices[] = { #define has_old_autopwm(data) ((data)->features & FEAT_OLD_AUTOPWM) #define has_temp_offset(data) ((data)->features & FEAT_TEMP_OFFSET) #define has_temp_peci(data, nr) (((data)->features & FEAT_TEMP_PECI) && \ - ((data)->peci_mask & (1 << nr))) + ((data)->peci_mask & BIT(nr))) #define has_temp_old_peci(data, nr) \ (((data)->features & FEAT_TEMP_OLD_PECI) && \ - ((data)->old_peci_mask & (1 << nr))) + ((data)->old_peci_mask & BIT(nr))) #define has_fan16_config(data) ((data)->features & FEAT_FAN16_CONFIG) #define has_five_fans(data) ((data)->features & (FEAT_FIVE_FANS | \ FEAT_SIX_FANS)) @@ -530,7 +531,7 @@ static int adc_lsb(const struct it87_data *data, int nr) lsb = 109; else lsb = 160; - if (data->in_scaled & (1 << nr)) + if (data->in_scaled & BIT(nr)) lsb <<= 1; return lsb; } @@ -595,7 +596,8 @@ static int DIV_TO_REG(int val) answer++; return answer; } -#define DIV_FROM_REG(val) (1 << (val)) + +#define DIV_FROM_REG(val) BIT(val) /* * PWM base frequencies. The frequency has to be divided by either 128 or 256, @@ -685,7 +687,7 @@ static struct it87_data *it87_update_device(struct device *dev) it87_read_value(data, IT87_REG_CONFIG) | 0x40); } for (i = 0; i < ARRAY_SIZE(IT87_REG_VIN); i++) { - if (!(data->has_in & (1 << i))) + if (!(data->has_in & BIT(i))) continue; data->in[i][0] = @@ -703,7 +705,7 @@ static struct it87_data *it87_update_device(struct device *dev) for (i = 0; i < 6; i++) { /* Skip disabled fans */ - if (!(data->has_fan & (1 << i))) + if (!(data->has_fan & BIT(i))) continue; data->fan[i][1] = @@ -719,7 +721,7 @@ static struct it87_data *it87_update_device(struct device *dev) } } for (i = 0; i < 6; i++) { - if (!(data->has_temp & (1 << i))) + if (!(data->has_temp & BIT(i))) continue; data->temp[i][0] = it87_read_value(data, IT87_REG_TEMP(i)); @@ -1026,7 +1028,7 @@ static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type, static int pwm_mode(const struct it87_data *data, int nr) { - int ctrl = data->fan_main_ctrl & (1 << nr); + int ctrl = data->fan_main_ctrl & BIT(nr); if (ctrl == 0 && data->type != it8603) /* Full speed */ return 0; @@ -1059,7 +1061,7 @@ static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->index; struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); + return sprintf(buf, "%lu\n", DIV_FROM_REG(data->fan_div[nr])); } static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) @@ -1243,9 +1245,9 @@ static ssize_t set_pwm_enable(struct device *dev, int tmp; /* make sure the fan is on when in on/off mode */ tmp = it87_read_value(data, IT87_REG_FAN_CTL); - it87_write_value(data, IT87_REG_FAN_CTL, tmp | (1 << nr)); + it87_write_value(data, IT87_REG_FAN_CTL, tmp | BIT(nr)); /* set on/off mode */ - data->fan_main_ctrl &= ~(1 << nr); + data->fan_main_ctrl &= ~BIT(nr); it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); } else { @@ -1259,7 +1261,7 @@ static ssize_t set_pwm_enable(struct device *dev, if (data->type != it8603) { /* set SmartGuardian mode */ - data->fan_main_ctrl |= (1 << nr); + data->fan_main_ctrl |= BIT(nr); it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); } @@ -1353,7 +1355,7 @@ static ssize_t show_pwm_temp_map(struct device *dev, int map; if (data->pwm_temp_map[nr] < 3) - map = 1 << data->pwm_temp_map[nr]; + map = BIT(data->pwm_temp_map[nr]); else map = 0; /* Should never happen */ return sprintf(buf, "%d\n", map); @@ -1372,13 +1374,13 @@ static ssize_t set_pwm_temp_map(struct device *dev, return -EINVAL; switch (val) { - case (1 << 0): + case BIT(0): reg = 0x00; break; - case (1 << 1): + case BIT(1): reg = 0x01; break; - case (1 << 2): + case BIT(2): reg = 0x02; break; default: @@ -1625,7 +1627,7 @@ static ssize_t clear_intrusion(struct device *dev, struct device_attribute if (config < 0) { count = config; } else { - config |= 1 << 5; + config |= BIT(5); it87_write_value(data, IT87_REG_CONFIG, config); /* Invalidate cache to force re-read */ data->valid = 0; @@ -1676,9 +1678,9 @@ static ssize_t set_beep(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->beeps = it87_read_value(data, IT87_REG_BEEP_ENABLE); if (val) - data->beeps |= (1 << bitnr); + data->beeps |= BIT(bitnr); else - data->beeps &= ~(1 << bitnr); + data->beeps &= ~BIT(bitnr); it87_write_value(data, IT87_REG_BEEP_ENABLE, data->beeps); mutex_unlock(&data->update_lock); return count; @@ -1777,7 +1779,7 @@ static umode_t it87_in_is_visible(struct kobject *kobj, a = 0; } - if (!(data->has_in & (1 << i))) + if (!(data->has_in & BIT(i))) return 0; if (a == 4 && !data->has_beep) @@ -1860,7 +1862,7 @@ static umode_t it87_temp_is_visible(struct kobject *kobj, a = 0; } - if (!(data->has_temp & (1 << i))) + if (!(data->has_temp & BIT(i))) return 0; if (a == 5 && !has_temp_offset(data)) @@ -1917,7 +1919,7 @@ static umode_t it87_is_visible(struct kobject *kobj, if ((index == 2 || index == 3) && !data->has_vid) return 0; - if (index > 3 && !(data->in_internal & (1 << (index - 4)))) + if (index > 3 && !(data->in_internal & BIT(index - 4))) return 0; return attr->mode; @@ -1953,7 +1955,7 @@ static umode_t it87_fan_is_visible(struct kobject *kobj, a = (index - 15) % 4; } - if (!(data->has_fan & (1 << i))) + if (!(data->has_fan & BIT(i))) return 0; if (a == 3) { /* beep */ @@ -2019,7 +2021,7 @@ static umode_t it87_pwm_is_visible(struct kobject *kobj, int i = index / 4; /* pwm index */ int a = index % 4; /* attribute index */ - if (!(data->has_pwm & (1 << i))) + if (!(data->has_pwm & BIT(i))) return 0; /* pwmX_auto_channels_temp is only writable for old auto pwm */ @@ -2079,7 +2081,7 @@ static umode_t it87_auto_pwm_is_visible(struct kobject *kobj, struct it87_data *data = dev_get_drvdata(dev); int i = index / 9; /* pwm index */ - if (!(data->has_pwm & (1 << i))) + if (!(data->has_pwm & BIT(i))) return 0; return attr->mode; @@ -2223,19 +2225,19 @@ static int __init it87_find(int sioaddr, unsigned short *address, /* in7 (VSB or VCCH5V) is always internal on some chips */ if (has_in7_internal(config)) - sio_data->internal |= (1 << 1); + sio_data->internal |= BIT(1); /* in8 (Vbat) is always internal */ - sio_data->internal |= (1 << 2); + sio_data->internal |= BIT(2); /* in9 (AVCC3), always internal if supported */ if (has_avcc3(config)) - sio_data->internal |= (1 << 3); /* in9 is AVCC */ + sio_data->internal |= BIT(3); /* in9 is AVCC */ else - sio_data->skip_in |= (1 << 9); + sio_data->skip_in |= BIT(9); if (!has_six_pwm(config)) - sio_data->skip_pwm |= (1 << 3) | (1 << 4) | (1 << 5); + sio_data->skip_pwm |= BIT(3) | BIT(4) | BIT(5); if (!has_vid(config)) sio_data->skip_vid = 1; @@ -2258,31 +2260,31 @@ static int __init it87_find(int sioaddr, unsigned short *address, regef = superio_inb(sioaddr, IT87_SIO_SPI_REG); /* Check if fan3 is there or not */ - if ((reg27 & (1 << 0)) || !(reg2c & (1 << 2))) - sio_data->skip_fan |= (1 << 2); - if ((reg25 & (1 << 4)) - || (!(reg2a & (1 << 1)) && (regef & (1 << 0)))) - sio_data->skip_pwm |= (1 << 2); + if ((reg27 & BIT(0)) || !(reg2c & BIT(2))) + sio_data->skip_fan |= BIT(2); + if ((reg25 & BIT(4)) + || (!(reg2a & BIT(1)) && (regef & BIT(0)))) + sio_data->skip_pwm |= BIT(2); /* Check if fan2 is there or not */ - if (reg27 & (1 << 7)) - sio_data->skip_fan |= (1 << 1); - if (reg27 & (1 << 3)) - sio_data->skip_pwm |= (1 << 1); + if (reg27 & BIT(7)) + sio_data->skip_fan |= BIT(1); + if (reg27 & BIT(3)) + sio_data->skip_pwm |= BIT(1); /* VIN5 */ - if ((reg27 & (1 << 0)) || (reg2c & (1 << 2))) - sio_data->skip_in |= (1 << 5); /* No VIN5 */ + if ((reg27 & BIT(0)) || (reg2c & BIT(2))) + sio_data->skip_in |= BIT(5); /* No VIN5 */ /* VIN6 */ - if (reg27 & (1 << 1)) - sio_data->skip_in |= (1 << 6); /* No VIN6 */ + if (reg27 & BIT(1)) + sio_data->skip_in |= BIT(6); /* No VIN6 */ /* * VIN7 * Does not depend on bit 2 of Reg2C, contrary to datasheet. */ - if (reg27 & (1 << 2)) { + if (reg27 & BIT(2)) { /* * The data sheet is a bit unclear regarding the * internal voltage divider for VCCH5V. It says @@ -2296,8 +2298,8 @@ static int __init it87_find(int sioaddr, unsigned short *address, * not the case, and ask the user to report if the * resulting voltage is sane. */ - if (!(reg2c & (1 << 1))) { - reg2c |= (1 << 1); + if (!(reg2c & BIT(1))) { + reg2c |= BIT(1); superio_outb(sioaddr, IT87_SIO_PINX2_REG, reg2c); pr_notice("Routing internal VCCH5V to in7.\n"); @@ -2306,10 +2308,10 @@ static int __init it87_find(int sioaddr, unsigned short *address, pr_notice("Please report if it displays a reasonable voltage.\n"); } - if (reg2c & (1 << 0)) - sio_data->internal |= (1 << 0); - if (reg2c & (1 << 1)) - sio_data->internal |= (1 << 1); + if (reg2c & BIT(0)) + sio_data->internal |= BIT(0); + if (reg2c & BIT(1)) + sio_data->internal |= BIT(1); sio_data->beep_pin = superio_inb(sioaddr, IT87_SIO_BEEP_PIN_REG) & 0x3f; @@ -2321,20 +2323,20 @@ static int __init it87_find(int sioaddr, unsigned short *address, reg27 = superio_inb(sioaddr, IT87_SIO_GPIO3_REG); /* Check if fan3 is there or not */ - if (reg27 & (1 << 6)) - sio_data->skip_pwm |= (1 << 2); - if (reg27 & (1 << 7)) - sio_data->skip_fan |= (1 << 2); + if (reg27 & BIT(6)) + sio_data->skip_pwm |= BIT(2); + if (reg27 & BIT(7)) + sio_data->skip_fan |= BIT(2); /* Check if fan2 is there or not */ reg29 = superio_inb(sioaddr, IT87_SIO_GPIO5_REG); - if (reg29 & (1 << 1)) - sio_data->skip_pwm |= (1 << 1); - if (reg29 & (1 << 2)) - sio_data->skip_fan |= (1 << 1); + if (reg29 & BIT(1)) + sio_data->skip_pwm |= BIT(1); + if (reg29 & BIT(2)) + sio_data->skip_fan |= BIT(1); - sio_data->skip_in |= (1 << 5); /* No VIN5 */ - sio_data->skip_in |= (1 << 6); /* No VIN6 */ + sio_data->skip_in |= BIT(5); /* No VIN5 */ + sio_data->skip_in |= BIT(6); /* No VIN6 */ sio_data->beep_pin = superio_inb(sioaddr, IT87_SIO_BEEP_PIN_REG) & 0x3f; @@ -2345,38 +2347,38 @@ static int __init it87_find(int sioaddr, unsigned short *address, /* Check for pwm5 */ reg = superio_inb(sioaddr, IT87_SIO_GPIO1_REG); - if (reg & (1 << 6)) - sio_data->skip_pwm |= (1 << 4); + if (reg & BIT(6)) + sio_data->skip_pwm |= BIT(4); /* Check for fan4, fan5 */ reg = superio_inb(sioaddr, IT87_SIO_GPIO2_REG); - if (!(reg & (1 << 5))) - sio_data->skip_fan |= (1 << 3); - if (!(reg & (1 << 4))) - sio_data->skip_fan |= (1 << 4); + if (!(reg & BIT(5))) + sio_data->skip_fan |= BIT(3); + if (!(reg & BIT(4))) + sio_data->skip_fan |= BIT(4); /* Check for pwm3, fan3 */ reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG); - if (reg & (1 << 6)) - sio_data->skip_pwm |= (1 << 2); - if (reg & (1 << 7)) - sio_data->skip_fan |= (1 << 2); + if (reg & BIT(6)) + sio_data->skip_pwm |= BIT(2); + if (reg & BIT(7)) + sio_data->skip_fan |= BIT(2); /* Check for pwm4 */ reg = superio_inb(sioaddr, IT87_SIO_GPIO4_REG); - if (!(reg & (1 << 2))) - sio_data->skip_pwm |= (1 << 3); + if (!(reg & BIT(2))) + sio_data->skip_pwm |= BIT(3); /* Check for pwm2, fan2 */ reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG); - if (reg & (1 << 1)) - sio_data->skip_pwm |= (1 << 1); - if (reg & (1 << 2)) - sio_data->skip_fan |= (1 << 1); + if (reg & BIT(1)) + sio_data->skip_pwm |= BIT(1); + if (reg & BIT(2)) + sio_data->skip_fan |= BIT(1); /* Check for pwm6, fan6 */ - if (!(reg & (1 << 7))) { - sio_data->skip_pwm |= (1 << 5); - sio_data->skip_fan |= (1 << 5); + if (!(reg & BIT(7))) { + sio_data->skip_pwm |= BIT(5); + sio_data->skip_fan |= BIT(5); } sio_data->beep_pin = superio_inb(sioaddr, @@ -2397,17 +2399,17 @@ static int __init it87_find(int sioaddr, unsigned short *address, } /* Check if fan3 is there or not */ - if (reg & (1 << 6)) - sio_data->skip_pwm |= (1 << 2); - if (reg & (1 << 7)) - sio_data->skip_fan |= (1 << 2); + if (reg & BIT(6)) + sio_data->skip_pwm |= BIT(2); + if (reg & BIT(7)) + sio_data->skip_fan |= BIT(2); /* Check if fan2 is there or not */ reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG); - if (reg & (1 << 1)) - sio_data->skip_pwm |= (1 << 1); - if (reg & (1 << 2)) - sio_data->skip_fan |= (1 << 1); + if (reg & BIT(1)) + sio_data->skip_pwm |= BIT(1); + if (reg & BIT(2)) + sio_data->skip_fan |= BIT(1); if ((sio_data->type == it8718 || sio_data->type == it8720) && !(sio_data->skip_vid)) @@ -2416,7 +2418,7 @@ static int __init it87_find(int sioaddr, unsigned short *address, reg = superio_inb(sioaddr, IT87_SIO_PINX2_REG); - uart6 = sio_data->type == it8782 && (reg & (1 << 2)); + uart6 = sio_data->type == it8782 && (reg & BIT(2)); /* * The IT8720F has no VIN7 pin, so VCCH should always be @@ -2432,15 +2434,15 @@ static int __init it87_find(int sioaddr, unsigned short *address, * If UART6 is enabled, re-route VIN7 to the internal divider * if that is not already the case. */ - if ((sio_data->type == it8720 || uart6) && !(reg & (1 << 1))) { - reg |= (1 << 1); + if ((sio_data->type == it8720 || uart6) && !(reg & BIT(1))) { + reg |= BIT(1); superio_outb(sioaddr, IT87_SIO_PINX2_REG, reg); pr_notice("Routing internal VCCH to in7\n"); } - if (reg & (1 << 0)) - sio_data->internal |= (1 << 0); - if (reg & (1 << 1)) - sio_data->internal |= (1 << 1); + if (reg & BIT(0)) + sio_data->internal |= BIT(0); + if (reg & BIT(1)) + sio_data->internal |= BIT(1); /* * On IT8782F, UART6 pins overlap with VIN5, VIN6, and VIN7. @@ -2452,8 +2454,8 @@ static int __init it87_find(int sioaddr, unsigned short *address, * temperature source here, skip_temp is preliminary. */ if (uart6) { - sio_data->skip_in |= (1 << 5) | (1 << 6); - sio_data->skip_temp |= (1 << 2); + sio_data->skip_in |= BIT(5) | BIT(6); + sio_data->skip_temp |= BIT(2); } sio_data->beep_pin = superio_inb(sioaddr, @@ -2477,7 +2479,7 @@ static int __init it87_find(int sioaddr, unsigned short *address, * the same board is ever used in other systems. */ pr_info("Disabling pwm2 due to hardware constraints\n"); - sio_data->skip_pwm = (1 << 1); + sio_data->skip_pwm = BIT(1); } } @@ -2570,12 +2572,12 @@ static void it87_init_device(struct platform_device *pdev) /* Check for additional fans */ if (has_five_fans(data)) { - if (tmp & (1 << 4)) - data->has_fan |= (1 << 3); /* fan4 enabled */ - if (tmp & (1 << 5)) - data->has_fan |= (1 << 4); /* fan5 enabled */ - if (has_six_fans(data) && (tmp & (1 << 2))) - data->has_fan |= (1 << 5); /* fan6 enabled */ + if (tmp & BIT(4)) + data->has_fan |= BIT(3); /* fan4 enabled */ + if (tmp & BIT(5)) + data->has_fan |= BIT(4); /* fan5 enabled */ + if (has_six_fans(data) && (tmp & BIT(2))) + data->has_fan |= BIT(5); /* fan6 enabled */ } /* Fan input pins may be used for alternative functions */ @@ -2586,9 +2588,9 @@ static void it87_init_device(struct platform_device *pdev) /* The following code may be IT8620E specific */ tmp = it87_read_value(data, IT87_REG_FAN_DIV); if ((tmp & 0xc0) == 0xc0) - sio_data->skip_pwm |= (1 << 4); - if (!(tmp & (1 << 3))) - sio_data->skip_pwm |= (1 << 5); + sio_data->skip_pwm |= BIT(4); + if (!(tmp & BIT(3))) + sio_data->skip_pwm |= BIT(5); } /* Start monitoring */ @@ -2719,27 +2721,27 @@ static int it87_probe(struct platform_device *pdev) /* Starting with IT8721F, we handle scaling of internal voltages */ if (has_12mv_adc(data)) { - if (sio_data->internal & (1 << 0)) - data->in_scaled |= (1 << 3); /* in3 is AVCC */ - if (sio_data->internal & (1 << 1)) - data->in_scaled |= (1 << 7); /* in7 is VSB */ - if (sio_data->internal & (1 << 2)) - data->in_scaled |= (1 << 8); /* in8 is Vbat */ - if (sio_data->internal & (1 << 3)) - data->in_scaled |= (1 << 9); /* in9 is AVCC */ + if (sio_data->internal & BIT(0)) + data->in_scaled |= BIT(3); /* in3 is AVCC */ + if (sio_data->internal & BIT(1)) + data->in_scaled |= BIT(7); /* in7 is VSB */ + if (sio_data->internal & BIT(2)) + data->in_scaled |= BIT(8); /* in8 is Vbat */ + if (sio_data->internal & BIT(3)) + data->in_scaled |= BIT(9); /* in9 is AVCC */ } else if (sio_data->type == it8781 || sio_data->type == it8782 || sio_data->type == it8783) { - if (sio_data->internal & (1 << 0)) - data->in_scaled |= (1 << 3); /* in3 is VCC5V */ - if (sio_data->internal & (1 << 1)) - data->in_scaled |= (1 << 7); /* in7 is VCCH5V */ + if (sio_data->internal & BIT(0)) + data->in_scaled |= BIT(3); /* in3 is VCC5V */ + if (sio_data->internal & BIT(1)) + data->in_scaled |= BIT(7); /* in7 is VCCH5V */ } data->has_temp = 0x07; - if (sio_data->skip_temp & (1 << 2)) { + if (sio_data->skip_temp & BIT(2)) { if (sio_data->type == it8782 && !(it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x80)) - data->has_temp &= ~(1 << 2); + data->has_temp &= ~BIT(2); } data->in_internal = sio_data->internal; @@ -2750,19 +2752,19 @@ static int it87_probe(struct platform_device *pdev) /* Check for additional temperature sensors */ if ((reg & 0x03) >= 0x02) - data->has_temp |= (1 << 3); + data->has_temp |= BIT(3); if (((reg >> 2) & 0x03) >= 0x02) - data->has_temp |= (1 << 4); + data->has_temp |= BIT(4); if (((reg >> 4) & 0x03) >= 0x02) - data->has_temp |= (1 << 5); + data->has_temp |= BIT(5); /* Check for additional voltage sensors */ if ((reg & 0x03) == 0x01) - data->has_in |= (1 << 10); + data->has_in |= BIT(10); if (((reg >> 2) & 0x03) == 0x01) - data->has_in |= (1 << 11); + data->has_in |= BIT(11); if (((reg >> 4) & 0x03) == 0x01) - data->has_in |= (1 << 12); + data->has_in |= BIT(12); } data->has_beep = !!sio_data->beep_pin; @@ -2784,7 +2786,7 @@ static int it87_probe(struct platform_device *pdev) data->groups[3] = &it87_group_fan; if (enable_pwm_interface) { - data->has_pwm = (1 << ARRAY_SIZE(IT87_REG_PWM)) - 1; + data->has_pwm = BIT(ARRAY_SIZE(IT87_REG_PWM)) - 1; data->has_pwm &= ~sio_data->skip_pwm; data->groups[4] = &it87_group_pwm; From 2310048db70a58ae89b37fded0322b6a5ae6443f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 2 Apr 2015 08:23:45 -0700 Subject: [PATCH 28/35] hwmon: (it87) Use defines for array sizes and sensor counts Using array size defines makes it much easier to find errors in index values and loop counts. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 58 ++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 0d6d106d53a2..118d4c756e40 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -260,6 +260,16 @@ static const u8 IT87_REG_VIN[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, #define IT87_REG_TEMP456_ENABLE 0x77 +#define NUM_VIN ARRAY_SIZE(IT87_REG_VIN) +#define NUM_VIN_LIMIT 8 +#define NUM_TEMP 6 +#define NUM_TEMP_OFFSET ARRAY_SIZE(IT87_REG_TEMP_OFFSET) +#define NUM_TEMP_LIMIT 3 +#define NUM_FAN ARRAY_SIZE(IT87_REG_FAN) +#define NUM_FAN_DIV 3 +#define NUM_PWM ARRAY_SIZE(IT87_REG_PWM) +#define NUM_AUTO_PWM ARRAY_SIZE(IT87_REG_PWM) + struct it87_devices { const char *name; const char * const suffix; @@ -484,14 +494,14 @@ struct it87_data { u16 in_scaled; /* Internal voltage sensors are scaled */ u16 in_internal; /* Bitfield, internal sensors (for labels) */ u16 has_in; /* Bitfield, voltage sensors enabled */ - u8 in[13][3]; /* [nr][0]=in, [1]=min, [2]=max */ + u8 in[NUM_VIN][3]; /* [nr][0]=in, [1]=min, [2]=max */ u8 has_fan; /* Bitfield, fans enabled */ - u16 fan[6][2]; /* Register values, [nr][0]=fan, [1]=min */ + u16 fan[NUM_FAN][2]; /* Register values, [nr][0]=fan, [1]=min */ u8 has_temp; /* Bitfield, temp sensors enabled */ - s8 temp[6][4]; /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */ + s8 temp[NUM_TEMP][4]; /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */ u8 sensor; /* Register value (IT87_REG_TEMP_ENABLE) */ u8 extra; /* Register value (IT87_REG_TEMP_EXTRA) */ - u8 fan_div[3]; /* Register encoding, shifted right */ + u8 fan_div[NUM_FAN_DIV];/* Register encoding, shifted right */ bool has_vid; /* True if VID supported */ u8 vid; /* Register encoding, combined */ u8 vrm; @@ -512,13 +522,13 @@ struct it87_data { * simple. */ u8 has_pwm; /* Bitfield, pwm control enabled */ - u8 pwm_ctrl[6]; /* Register value */ - u8 pwm_duty[6]; /* Manual PWM value set by user */ - u8 pwm_temp_map[6]; /* PWM to temp. chan. mapping (bits 1-0) */ + u8 pwm_ctrl[NUM_PWM]; /* Register value */ + u8 pwm_duty[NUM_PWM]; /* Manual PWM value set by user */ + u8 pwm_temp_map[NUM_PWM];/* PWM to temp. chan. mapping (bits 1-0) */ /* Automatic fan speed control registers */ - u8 auto_pwm[3][4]; /* [nr][3] is hard-coded */ - s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ + u8 auto_pwm[NUM_AUTO_PWM][4]; /* [nr][3] is hard-coded */ + s8 auto_temp[NUM_AUTO_PWM][5]; /* [nr][0] is point1_temp_hyst */ }; static int adc_lsb(const struct it87_data *data, int nr) @@ -686,7 +696,7 @@ static struct it87_data *it87_update_device(struct device *dev) it87_write_value(data, IT87_REG_CONFIG, it87_read_value(data, IT87_REG_CONFIG) | 0x40); } - for (i = 0; i < ARRAY_SIZE(IT87_REG_VIN); i++) { + for (i = 0; i < NUM_VIN; i++) { if (!(data->has_in & BIT(i))) continue; @@ -694,7 +704,7 @@ static struct it87_data *it87_update_device(struct device *dev) it87_read_value(data, IT87_REG_VIN[i]); /* VBAT and AVCC don't have limit registers */ - if (i >= 8) + if (i >= NUM_VIN_LIMIT) continue; data->in[i][1] = @@ -703,7 +713,7 @@ static struct it87_data *it87_update_device(struct device *dev) it87_read_value(data, IT87_REG_VIN_MAX(i)); } - for (i = 0; i < 6; i++) { + for (i = 0; i < NUM_FAN; i++) { /* Skip disabled fans */ if (!(data->has_fan & BIT(i))) continue; @@ -720,24 +730,24 @@ static struct it87_data *it87_update_device(struct device *dev) IT87_REG_FANX_MIN[i]) << 8; } } - for (i = 0; i < 6; i++) { + for (i = 0; i < NUM_TEMP; i++) { if (!(data->has_temp & BIT(i))) continue; data->temp[i][0] = it87_read_value(data, IT87_REG_TEMP(i)); - /* No limits/offset for additional sensors */ - if (i >= 3) + if (has_temp_offset(data) && i < NUM_TEMP_OFFSET) + data->temp[i][3] = + it87_read_value(data, + IT87_REG_TEMP_OFFSET[i]); + + if (i >= NUM_TEMP_LIMIT) continue; data->temp[i][1] = it87_read_value(data, IT87_REG_TEMP_LOW(i)); data->temp[i][2] = it87_read_value(data, IT87_REG_TEMP_HIGH(i)); - if (has_temp_offset(data)) - data->temp[i][3] = - it87_read_value(data, - IT87_REG_TEMP_OFFSET[i]); } /* Newer chips don't have clock dividers */ @@ -757,7 +767,7 @@ static struct it87_data *it87_update_device(struct device *dev) data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL); - for (i = 0; i < 6; i++) + for (i = 0; i < NUM_PWM; i++) it87_update_pwm_ctrl(data, i); data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); @@ -2509,7 +2519,7 @@ static void it87_init_device(struct platform_device *pdev) * these have separate registers for the temperature mapping and the * manual duty cycle. */ - for (i = 0; i < 3; i++) { + for (i = 0; i < NUM_AUTO_PWM; i++) { data->pwm_temp_map[i] = i; data->pwm_duty[i] = 0x7f; /* Full speed */ data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */ @@ -2522,12 +2532,12 @@ static void it87_init_device(struct platform_device *pdev) * means -1 degree C, which surprisingly doesn't trigger an alarm, * but is still confusing, so change to 127 degrees C. */ - for (i = 0; i < 8; i++) { + for (i = 0; i < NUM_VIN_LIMIT; i++) { tmp = it87_read_value(data, IT87_REG_VIN_MIN(i)); if (tmp == 0xff) it87_write_value(data, IT87_REG_VIN_MIN(i), 0); } - for (i = 0; i < 3; i++) { + for (i = 0; i < NUM_TEMP_LIMIT; i++) { tmp = it87_read_value(data, IT87_REG_TEMP_HIGH(i)); if (tmp == 0xff) it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127); @@ -2620,7 +2630,7 @@ static int it87_check_pwm(struct device *dev) int i; u8 pwm[3]; - for (i = 0; i < 3; i++) + for (i = 0; i < ARRAY_SIZE(pwm); i++) pwm[i] = it87_read_value(data, IT87_REG_PWM[i]); From c962024e306ed598600853854a067b521bf5b530 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 4 Apr 2015 09:05:57 -0700 Subject: [PATCH 29/35] hwmon: (it87) Formatting cleanup Fix various checkpatch complaints to clean up the code and make it easier to read. CHECK: Do not include the paragraph about writing to the FSF CHECK: Alignment should match open parenthesis CHECK: Logical continuations should be on the previous line CHECK: No space is necessary after a cast CHECK: Please don't use multiple blank lines CHECK: Please use a blank line after function/struct/union/enum declarations CHECK: spaces preferred around that '+' (ctx:VxV) WARNING: Missing a blank line after declarations No functional change. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 157 ++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 76 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 118d4c756e40..515c5cd1890b 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -44,10 +44,6 @@ * 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. - * - * 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. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -108,6 +104,7 @@ static inline void superio_outb(int ioreg, int reg, int val) static int superio_inw(int ioreg, int reg) { int val; + outb(reg++, ioreg); val = inb(ioreg + 1) << 8; outb(reg, ioreg); @@ -598,10 +595,10 @@ static int pwm_from_reg(const struct it87_data *data, u8 reg) return (reg & 0x7f) << 1; } - static int DIV_TO_REG(int val) { int answer = 0; + while (answer < 7 && (val >>= 1)) answer++; return answer; @@ -686,8 +683,8 @@ static struct it87_data *it87_update_device(struct device *dev) mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || + !data->valid) { if (update_vbat) { /* * Cleared after each update, so reenable. Value @@ -798,10 +795,10 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr, char *buf) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; - int index = sattr->index; - struct it87_data *data = it87_update_device(dev); + int index = sattr->index; + int nr = sattr->nr; + return sprintf(buf, "%d\n", in_from_reg(data, nr, data->in[nr][index])); } @@ -809,10 +806,9 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; - int index = sattr->index; - struct it87_data *data = dev_get_drvdata(dev); + int index = sattr->index; + int nr = sattr->nr; unsigned long val; if (kstrtoul(buf, 10, &val) < 0) @@ -968,8 +964,8 @@ static ssize_t show_temp_type(struct device *dev, struct device_attribute *attr, u8 reg = data->sensor; /* In case value is updated while used */ u8 extra = data->extra; - if ((has_temp_peci(data, nr) && (reg >> 6 == nr + 1)) - || (has_temp_old_peci(data, nr) && (extra & 0x80))) + if ((has_temp_peci(data, nr) && (reg >> 6 == nr + 1)) || + (has_temp_old_peci(data, nr) && (extra & 0x80))) return sprintf(buf, "6\n"); /* Intel PECI */ if (reg & (1 << nr)) return sprintf(buf, "3\n"); /* thermal diode */ @@ -1065,35 +1061,38 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *attr, } static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct it87_data *data = it87_update_device(dev); int nr = sensor_attr->index; - struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%lu\n", DIV_FROM_REG(data->fan_div[nr])); } + static ssize_t show_pwm_enable(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct it87_data *data = it87_update_device(dev); int nr = sensor_attr->index; - struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%d\n", pwm_mode(data, nr)); } + static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct it87_data *data = it87_update_device(dev); int nr = sensor_attr->index; - struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%d\n", pwm_from_reg(data, data->pwm_duty[nr])); } + static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); struct it87_data *data = it87_update_device(dev); @@ -1157,12 +1156,11 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr, } static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct it87_data *data = dev_get_drvdata(dev); + int nr = sensor_attr->index; unsigned long val; int min; u8 old; @@ -1227,13 +1225,12 @@ static int check_trip_points(struct device *dev, int nr) return err; } -static ssize_t set_pwm_enable(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct it87_data *data = dev_get_drvdata(dev); + int nr = sensor_attr->index; long val; if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 2) @@ -1280,13 +1277,13 @@ static ssize_t set_pwm_enable(struct device *dev, mutex_unlock(&data->update_lock); return count; } + static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct it87_data *data = dev_get_drvdata(dev); + int nr = sensor_attr->index; long val; if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 255) @@ -1320,8 +1317,9 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, mutex_unlock(&data->update_lock); return count; } -static ssize_t set_pwm_freq(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) + +static ssize_t set_pwm_freq(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); struct it87_data *data = dev_get_drvdata(dev); @@ -1337,7 +1335,7 @@ static ssize_t set_pwm_freq(struct device *dev, /* Search for the nearest available frequency */ for (i = 0; i < 7; i++) { - if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2) + if (val > (pwm_freq[i] + pwm_freq[i + 1]) / 2) break; } @@ -1355,13 +1353,13 @@ static ssize_t set_pwm_freq(struct device *dev, return count; } + static ssize_t show_pwm_temp_map(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct it87_data *data = it87_update_device(dev); + int nr = sensor_attr->index; int map; if (data->pwm_temp_map[nr] < 3) @@ -1370,13 +1368,14 @@ static ssize_t show_pwm_temp_map(struct device *dev, map = 0; /* Should never happen */ return sprintf(buf, "%d\n", map); } + static ssize_t set_pwm_temp_map(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) + struct device_attribute *attr, const char *buf, + size_t count) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct it87_data *data = dev_get_drvdata(dev); + int nr = sensor_attr->index; long val; u8 reg; @@ -1411,8 +1410,8 @@ static ssize_t set_pwm_temp_map(struct device *dev, return count; } -static ssize_t show_auto_pwm(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t show_auto_pwm(struct device *dev, struct device_attribute *attr, + char *buf) { struct it87_data *data = it87_update_device(dev); struct sensor_device_attribute_2 *sensor_attr = @@ -1424,8 +1423,8 @@ static ssize_t show_auto_pwm(struct device *dev, pwm_from_reg(data, data->auto_pwm[nr][point])); } -static ssize_t set_auto_pwm(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_auto_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct it87_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sensor_attr = @@ -1445,8 +1444,8 @@ static ssize_t set_auto_pwm(struct device *dev, return count; } -static ssize_t show_auto_temp(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t show_auto_temp(struct device *dev, struct device_attribute *attr, + char *buf) { struct it87_data *data = it87_update_device(dev); struct sensor_device_attribute_2 *sensor_attr = @@ -1457,8 +1456,8 @@ static ssize_t show_auto_temp(struct device *dev, return sprintf(buf, "%d\n", TEMP_FROM_REG(data->auto_temp[nr][point])); } -static ssize_t set_auto_temp(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_auto_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct it87_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sensor_attr = @@ -1607,27 +1606,30 @@ static SENSOR_DEVICE_ATTR(pwm6_auto_channels_temp, S_IRUGO, /* Alarms */ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); } static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { - int bitnr = to_sensor_dev_attr(attr)->index; struct it87_data *data = it87_update_device(dev); + int bitnr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); } -static ssize_t clear_intrusion(struct device *dev, struct device_attribute - *attr, const char *buf, size_t count) +static ssize_t clear_intrusion(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) { struct it87_data *data = dev_get_drvdata(dev); - long val; int config; + long val; if (kstrtol(buf, 10, &val) < 0 || val != 0) return -EINVAL; @@ -1668,21 +1670,22 @@ static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR, show_alarm, clear_intrusion, 4); static ssize_t show_beep(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { - int bitnr = to_sensor_dev_attr(attr)->index; struct it87_data *data = it87_update_device(dev); + int bitnr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%u\n", (data->beeps >> bitnr) & 1); } + static ssize_t set_beep(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { int bitnr = to_sensor_dev_attr(attr)->index; struct it87_data *data = dev_get_drvdata(dev); long val; - if (kstrtol(buf, 10, &val) < 0 - || (val != 0 && val != 1)) + if (kstrtol(buf, 10, &val) < 0 || (val != 0 && val != 1)) return -EINVAL; mutex_lock(&data->update_lock); @@ -1718,13 +1721,15 @@ static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO, show_beep, NULL, 2); static SENSOR_DEVICE_ATTR(temp3_beep, S_IRUGO, show_beep, NULL, 2); static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct it87_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%u\n", data->vrm); } + static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct it87_data *data = dev_get_drvdata(dev); unsigned long val; @@ -1739,15 +1744,16 @@ static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); + + return sprintf(buf, "%ld\n", (long)vid_from_reg(data->vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); static ssize_t show_label(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { static const char * const labels[] = { "+5V", @@ -2272,8 +2278,8 @@ static int __init it87_find(int sioaddr, unsigned short *address, /* Check if fan3 is there or not */ if ((reg27 & BIT(0)) || !(reg2c & BIT(2))) sio_data->skip_fan |= BIT(2); - if ((reg25 & BIT(4)) - || (!(reg2a & BIT(1)) && (regef & BIT(0)))) + if ((reg25 & BIT(4)) || + (!(reg2a & BIT(1)) && (regef & BIT(0)))) sio_data->skip_pwm |= BIT(2); /* Check if fan2 is there or not */ @@ -2421,8 +2427,8 @@ static int __init it87_find(int sioaddr, unsigned short *address, if (reg & BIT(2)) sio_data->skip_fan |= BIT(1); - if ((sio_data->type == it8718 || sio_data->type == it8720) - && !(sio_data->skip_vid)) + if ((sio_data->type == it8718 || sio_data->type == it8720) && + !(sio_data->skip_vid)) sio_data->vid_value = superio_inb(sioaddr, IT87_SIO_VID_REG); @@ -2478,8 +2484,8 @@ static int __init it87_find(int sioaddr, unsigned short *address, board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); board_name = dmi_get_system_info(DMI_BOARD_NAME); if (board_vendor && board_name) { - if (strcmp(board_vendor, "nVIDIA") == 0 - && strcmp(board_name, "FN68PT") == 0) { + if (strcmp(board_vendor, "nVIDIA") == 0 && + strcmp(board_name, "FN68PT") == 0) { /* * On the Shuttle SN68PT, FAN_CTL2 is apparently not * connected to a fan, but to something else. One user @@ -2718,8 +2724,8 @@ static int it87_probe(struct platform_device *pdev) } /* Now, we do the remaining detection. */ - if ((it87_read_value(data, IT87_REG_CONFIG) & 0x80) - || it87_read_value(data, IT87_REG_CHIPID) != 0x90) + if ((it87_read_value(data, IT87_REG_CONFIG) & 0x80) || + it87_read_value(data, IT87_REG_CHIPID) != 0x90) return -ENODEV; platform_set_drvdata(pdev, data); @@ -2749,8 +2755,8 @@ static int it87_probe(struct platform_device *pdev) data->has_temp = 0x07; if (sio_data->skip_temp & BIT(2)) { - if (sio_data->type == it8782 - && !(it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x80)) + if (sio_data->type == it8782 && + !(it87_read_value(data, IT87_REG_TEMP_EXTRA) & 0x80)) data->has_temp &= ~BIT(2); } @@ -2911,7 +2917,6 @@ static void __exit sm_it87_exit(void) platform_driver_unregister(&it87_driver); } - MODULE_AUTHOR("Chris Gauthron, Jean Delvare "); MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver"); module_param(update_vbat, bool, 0); From f1bbe618604a4aa833a4af0b1ecdfb47a25455c6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 5 Apr 2015 13:16:54 -0700 Subject: [PATCH 30/35] hwmon: (it87) Support disabling fan control for all pwm control and chips On/Off mode is only supported for pwm controls 0-2, and not supported at all for IT8603E/IT8623E. For pwm controls 3-6 and for IT8603E/IT8623E, SmartGuardian mode is always enabled. Use it and set the pwm value to the maximum if fan control is disabled. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 52 ++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 515c5cd1890b..f40d81a0a9fc 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1030,18 +1030,19 @@ static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type, static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type, set_temp_type, 2); -/* 3 Fans */ +/* 6 Fans */ static int pwm_mode(const struct it87_data *data, int nr) { - int ctrl = data->fan_main_ctrl & BIT(nr); + if (data->type != it8603 && nr < 3 && !(data->fan_main_ctrl & BIT(nr))) + return 0; /* Full speed */ + if (data->pwm_ctrl[nr] & 0x80) + return 2; /* Automatic mode */ + if ((data->type == it8603 || nr >= 3) && + data->pwm_duty[nr] == pwm_to_reg(data, 0xff)) + return 0; /* Full speed */ - if (ctrl == 0 && data->type != it8603) /* Full speed */ - return 0; - if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */ - return 2; - else /* Manual mode */ - return 1; + return 1; /* Manual mode */ } static ssize_t show_fan(struct device *dev, struct device_attribute *attr, @@ -1242,21 +1243,30 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, return -EINVAL; } - /* IT8603E does not have on/off mode */ - if (val == 0 && data->type == it8603) - return -EINVAL; - mutex_lock(&data->update_lock); if (val == 0) { - int tmp; - /* make sure the fan is on when in on/off mode */ - tmp = it87_read_value(data, IT87_REG_FAN_CTL); - it87_write_value(data, IT87_REG_FAN_CTL, tmp | BIT(nr)); - /* set on/off mode */ - data->fan_main_ctrl &= ~BIT(nr); - it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, - data->fan_main_ctrl); + if (nr < 3 && data->type != it8603) { + int tmp; + /* make sure the fan is on when in on/off mode */ + tmp = it87_read_value(data, IT87_REG_FAN_CTL); + it87_write_value(data, IT87_REG_FAN_CTL, tmp | BIT(nr)); + /* set on/off mode */ + data->fan_main_ctrl &= ~BIT(nr); + it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, + data->fan_main_ctrl); + } else { + /* No on/off mode, set maximum pwm value */ + data->pwm_duty[nr] = pwm_to_reg(data, 0xff); + it87_write_value(data, IT87_REG_PWM_DUTY[nr], + data->pwm_duty[nr]); + /* and set manual mode */ + data->pwm_ctrl[nr] = has_newer_autopwm(data) ? + data->pwm_temp_map[nr] : + data->pwm_duty[nr]; + it87_write_value(data, IT87_REG_PWM[nr], + data->pwm_ctrl[nr]); + } } else { if (val == 1) /* Manual mode */ data->pwm_ctrl[nr] = has_newer_autopwm(data) ? @@ -1266,7 +1276,7 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, data->pwm_ctrl[nr] = 0x80 | data->pwm_temp_map[nr]; it87_write_value(data, IT87_REG_PWM[nr], data->pwm_ctrl[nr]); - if (data->type != it8603) { + if (data->type != it8603 && nr < 3) { /* set SmartGuardian mode */ data->fan_main_ctrl |= BIT(nr); it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, From a0df926d33232f311d6a4d834391fec21b60db30 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 5 Apr 2015 16:51:36 -0700 Subject: [PATCH 31/35] hwmon: (it87) Enhance validation for fan4 and fan5 Several of the chips supported by this driver have a configuration register to enable fan4 and fan5. Use those registers to determine if fan4 and fan5 tachometers are supported. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index f40d81a0a9fc..5adb269918fb 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -2415,6 +2415,29 @@ static int __init it87_find(int sioaddr, unsigned short *address, superio_select(sioaddr, GPIO); + /* Check for fan4, fan5 */ + if (has_five_fans(config)) { + reg = superio_inb(sioaddr, IT87_SIO_GPIO2_REG); + switch (sio_data->type) { + case it8718: + if (reg & BIT(5)) + sio_data->skip_fan |= BIT(3); + if (reg & BIT(4)) + sio_data->skip_fan |= BIT(4); + break; + case it8720: + case it8721: + case it8728: + if (!(reg & BIT(5))) + sio_data->skip_fan |= BIT(3); + if (!(reg & BIT(4))) + sio_data->skip_fan |= BIT(4); + break; + default: + break; + } + } + reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG); if (!sio_data->skip_vid) { /* We need at least 4 VID pins */ From 2cbb9c370fe32b5de5243f7338dc6ce8747d495b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 5 Apr 2015 11:53:29 -0700 Subject: [PATCH 32/35] hwmon: (it87) Support automatic pwm control on newer chips Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 201 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 187 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 5adb269918fb..e3fbcf49afdb 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -252,8 +252,10 @@ static const u8 IT87_REG_VIN[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, #define IT87_REG_CHIPID 0x58 -#define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i)) -#define IT87_REG_AUTO_PWM(nr, i) (0x65 + (nr) * 8 + (i)) +static const u8 IT87_REG_AUTO_BASE[] = { 0x60, 0x68, 0x70, 0x78, 0xa0, 0xa8 }; + +#define IT87_REG_AUTO_TEMP(nr, i) (IT87_REG_AUTO_BASE[nr] + (i)) +#define IT87_REG_AUTO_PWM(nr, i) (IT87_REG_AUTO_BASE[nr] + 5 + (i)) #define IT87_REG_TEMP456_ENABLE 0x77 @@ -673,6 +675,30 @@ static void it87_update_pwm_ctrl(struct it87_data *data, int nr) for (i = 0; i < 3 ; i++) data->auto_pwm[nr][i] = it87_read_value(data, IT87_REG_AUTO_PWM(nr, i)); + } else if (has_newer_autopwm(data)) { + int i; + + /* + * 0: temperature hysteresis (base + 5) + * 1: fan off temperature (base + 0) + * 2: fan start temperature (base + 1) + * 3: fan max temperature (base + 2) + */ + data->auto_temp[nr][0] = + it87_read_value(data, IT87_REG_AUTO_TEMP(nr, 5)); + + for (i = 0; i < 3 ; i++) + data->auto_temp[nr][i + 1] = + it87_read_value(data, + IT87_REG_AUTO_TEMP(nr, i)); + /* + * 0: start pwm value (base + 3) + * 1: pwm slope (base + 4, 1/8th pwm) + */ + data->auto_pwm[nr][0] = + it87_read_value(data, IT87_REG_AUTO_TEMP(nr, 3)); + data->auto_pwm[nr][1] = + it87_read_value(data, IT87_REG_AUTO_TEMP(nr, 4)); } } @@ -1216,6 +1242,11 @@ static int check_trip_points(struct device *dev, int nr) if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1]) err = -EINVAL; } + } else if (has_newer_autopwm(data)) { + for (i = 1; i < 3; i++) { + if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1]) + err = -EINVAL; + } } if (err) { @@ -1441,6 +1472,7 @@ static ssize_t set_auto_pwm(struct device *dev, struct device_attribute *attr, to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int point = sensor_attr->index; + int regaddr; long val; if (kstrtol(buf, 10, &val) < 0 || val < 0 || val > 255) @@ -1448,8 +1480,41 @@ static ssize_t set_auto_pwm(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->auto_pwm[nr][point] = pwm_to_reg(data, val); - it87_write_value(data, IT87_REG_AUTO_PWM(nr, point), - data->auto_pwm[nr][point]); + if (has_newer_autopwm(data)) + regaddr = IT87_REG_AUTO_TEMP(nr, 3); + else + regaddr = IT87_REG_AUTO_PWM(nr, point); + it87_write_value(data, regaddr, data->auto_pwm[nr][point]); + mutex_unlock(&data->update_lock); + return count; +} + +static ssize_t show_auto_pwm_slope(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct it87_data *data = it87_update_device(dev); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + return sprintf(buf, "%d\n", data->auto_pwm[nr][1] & 0x7f); +} + +static ssize_t set_auto_pwm_slope(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct it87_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + unsigned long val; + + if (kstrtoul(buf, 10, &val) < 0 || val > 127) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->auto_pwm[nr][1] = (data->auto_pwm[nr][1] & 0x80) | val; + it87_write_value(data, IT87_REG_AUTO_TEMP(nr, 4), + data->auto_pwm[nr][1]); mutex_unlock(&data->update_lock); return count; } @@ -1462,8 +1527,14 @@ static ssize_t show_auto_temp(struct device *dev, struct device_attribute *attr, to_sensor_dev_attr_2(attr); int nr = sensor_attr->nr; int point = sensor_attr->index; + int reg; - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->auto_temp[nr][point])); + if (has_old_autopwm(data) || point) + reg = data->auto_temp[nr][point]; + else + reg = data->auto_temp[nr][1] - (data->auto_temp[nr][0] & 0x1f); + + return sprintf(buf, "%d\n", TEMP_FROM_REG(reg)); } static ssize_t set_auto_temp(struct device *dev, struct device_attribute *attr, @@ -1475,14 +1546,24 @@ static ssize_t set_auto_temp(struct device *dev, struct device_attribute *attr, int nr = sensor_attr->nr; int point = sensor_attr->index; long val; + int reg; if (kstrtol(buf, 10, &val) < 0 || val < -128000 || val > 127000) return -EINVAL; mutex_lock(&data->update_lock); - data->auto_temp[nr][point] = TEMP_TO_REG(val); - it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point), - data->auto_temp[nr][point]); + if (has_newer_autopwm(data) && !point) { + reg = data->auto_temp[nr][1] - TEMP_TO_REG(val); + reg = clamp_val(reg, 0, 0x1f) | (data->auto_temp[nr][0] & 0xe0); + data->auto_temp[nr][0] = reg; + it87_write_value(data, IT87_REG_AUTO_TEMP(nr, 5), reg); + } else { + reg = TEMP_TO_REG(val); + data->auto_temp[nr][point] = reg; + if (has_newer_autopwm(data)) + point--; + it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point), reg); + } mutex_unlock(&data->update_lock); return count; } @@ -1542,6 +1623,10 @@ static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, 0, 3); static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_temp, S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, 0, 4); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_start, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 0, 0); +static SENSOR_DEVICE_ATTR(pwm1_auto_slope, S_IRUGO | S_IWUSR, + show_auto_pwm_slope, set_auto_pwm_slope, 0); static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 1); @@ -1567,6 +1652,10 @@ static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, 1, 3); static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_temp, S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, 1, 4); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_start, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 1, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_slope, S_IRUGO | S_IWUSR, + show_auto_pwm_slope, set_auto_pwm_slope, 1); static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 2); @@ -1592,6 +1681,10 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, 2, 3); static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, 2, 4); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_start, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 2, 0); +static SENSOR_DEVICE_ATTR(pwm3_auto_slope, S_IRUGO | S_IWUSR, + show_auto_pwm_slope, set_auto_pwm_slope, 2); static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 3); @@ -1599,6 +1692,18 @@ static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 3); static SENSOR_DEVICE_ATTR(pwm4_freq, S_IRUGO, show_pwm_freq, NULL, 3); static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, 3); +static SENSOR_DEVICE_ATTR_2(pwm4_auto_point1_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 1); +static SENSOR_DEVICE_ATTR_2(pwm4_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 0); +static SENSOR_DEVICE_ATTR_2(pwm4_auto_point2_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 2); +static SENSOR_DEVICE_ATTR_2(pwm4_auto_point3_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 3); +static SENSOR_DEVICE_ATTR_2(pwm4_auto_start, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 3, 0); +static SENSOR_DEVICE_ATTR(pwm4_auto_slope, S_IRUGO | S_IWUSR, + show_auto_pwm_slope, set_auto_pwm_slope, 3); static SENSOR_DEVICE_ATTR(pwm5_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 4); @@ -1606,6 +1711,18 @@ static SENSOR_DEVICE_ATTR(pwm5, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 4); static SENSOR_DEVICE_ATTR(pwm5_freq, S_IRUGO, show_pwm_freq, NULL, 4); static SENSOR_DEVICE_ATTR(pwm5_auto_channels_temp, S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, 4); +static SENSOR_DEVICE_ATTR_2(pwm5_auto_point1_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 1); +static SENSOR_DEVICE_ATTR_2(pwm5_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 0); +static SENSOR_DEVICE_ATTR_2(pwm5_auto_point2_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 2); +static SENSOR_DEVICE_ATTR_2(pwm5_auto_point3_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 3); +static SENSOR_DEVICE_ATTR_2(pwm5_auto_start, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 4, 0); +static SENSOR_DEVICE_ATTR(pwm5_auto_slope, S_IRUGO | S_IWUSR, + show_auto_pwm_slope, set_auto_pwm_slope, 4); static SENSOR_DEVICE_ATTR(pwm6_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 5); @@ -1613,6 +1730,18 @@ static SENSOR_DEVICE_ATTR(pwm6, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 5); static SENSOR_DEVICE_ATTR(pwm6_freq, S_IRUGO, show_pwm_freq, NULL, 5); static SENSOR_DEVICE_ATTR(pwm6_auto_channels_temp, S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, 5); +static SENSOR_DEVICE_ATTR_2(pwm6_auto_point1_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 1); +static SENSOR_DEVICE_ATTR_2(pwm6_auto_point1_temp_hyst, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 0); +static SENSOR_DEVICE_ATTR_2(pwm6_auto_point2_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 2); +static SENSOR_DEVICE_ATTR_2(pwm6_auto_point3_temp, S_IRUGO | S_IWUSR, + show_auto_temp, set_auto_temp, 2, 3); +static SENSOR_DEVICE_ATTR_2(pwm6_auto_start, S_IRUGO | S_IWUSR, + show_auto_pwm, set_auto_pwm, 5, 0); +static SENSOR_DEVICE_ATTR(pwm6_auto_slope, S_IRUGO | S_IWUSR, + show_auto_pwm_slope, set_auto_pwm_slope, 5); /* Alarms */ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, @@ -2050,8 +2179,8 @@ static umode_t it87_pwm_is_visible(struct kobject *kobj, if (!(data->has_pwm & BIT(i))) return 0; - /* pwmX_auto_channels_temp is only writable for old auto pwm */ - if (a == 3 && has_old_autopwm(data)) + /* pwmX_auto_channels_temp is only writable if auto pwm is supported */ + if (a == 3 && (has_old_autopwm(data) || has_newer_autopwm(data))) return attr->mode | S_IWUSR; /* pwm2_freq is writable if there are two pwm frequency selects */ @@ -2105,11 +2234,28 @@ static umode_t it87_auto_pwm_is_visible(struct kobject *kobj, { struct device *dev = container_of(kobj, struct device, kobj); struct it87_data *data = dev_get_drvdata(dev); - int i = index / 9; /* pwm index */ + int i = index / 11; /* pwm index */ + int a = index % 11; /* attribute index */ + + if (index >= 33) { /* pwm 4..6 */ + i = (index - 33) / 6 + 3; + a = (index - 33) % 6 + 4; + } if (!(data->has_pwm & BIT(i))) return 0; + if (has_newer_autopwm(data)) { + if (a < 4) /* no auto point pwm */ + return 0; + if (a == 8) /* no auto_point4 */ + return 0; + } + if (has_old_autopwm(data)) { + if (a >= 9) /* no pwm_auto_start, pwm_auto_slope */ + return 0; + } + return attr->mode; } @@ -2123,8 +2269,10 @@ static struct attribute *it87_attributes_auto_pwm[] = { &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_start.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_slope.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, /* 11 */ &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, @@ -2133,8 +2281,10 @@ static struct attribute *it87_attributes_auto_pwm[] = { &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_start.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_slope.dev_attr.attr, - &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, /* 22 */ &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, @@ -2143,6 +2293,29 @@ static struct attribute *it87_attributes_auto_pwm[] = { &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_start.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_slope.dev_attr.attr, + + &sensor_dev_attr_pwm4_auto_point1_temp.dev_attr.attr, /* 33 */ + &sensor_dev_attr_pwm4_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_start.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_slope.dev_attr.attr, + + &sensor_dev_attr_pwm5_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm5_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm5_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm5_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm5_auto_start.dev_attr.attr, + &sensor_dev_attr_pwm5_auto_slope.dev_attr.attr, + + &sensor_dev_attr_pwm6_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm6_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm6_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm6_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm6_auto_start.dev_attr.attr, + &sensor_dev_attr_pwm6_auto_slope.dev_attr.attr, NULL, }; @@ -2839,7 +3012,7 @@ static int it87_probe(struct platform_device *pdev) data->has_pwm &= ~sio_data->skip_pwm; data->groups[4] = &it87_group_pwm; - if (has_old_autopwm(data)) + if (has_old_autopwm(data) || has_newer_autopwm(data)) data->groups[5] = &it87_group_auto_pwm; } From 0624d861983c2cb1884ea3bafc1c534c7d2348b8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 6 Apr 2015 21:04:18 -0700 Subject: [PATCH 33/35] hwmon: (it87) Fix pwm_temp_map for system with 6 pwm channels Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index e3fbcf49afdb..cde53c1109ea 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -655,8 +655,7 @@ static void it87_update_pwm_ctrl(struct it87_data *data, int nr) { data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM[nr]); if (has_newer_autopwm(data)) { - data->pwm_temp_map[nr] = (data->pwm_ctrl[nr] & 0x03) + - nr < 3 ? 0 : 3; + data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; data->pwm_duty[nr] = it87_read_value(data, IT87_REG_PWM_DUTY[nr]); } else { @@ -790,8 +789,11 @@ static struct it87_data *it87_update_device(struct device *dev) data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); data->fan_ctl = it87_read_value(data, IT87_REG_FAN_CTL); - for (i = 0; i < NUM_PWM; i++) + for (i = 0; i < NUM_PWM; i++) { + if (!(data->has_pwm & BIT(i))) + continue; it87_update_pwm_ctrl(data, i); + } data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); data->extra = it87_read_value(data, IT87_REG_TEMP_EXTRA); @@ -1403,11 +1405,13 @@ static ssize_t show_pwm_temp_map(struct device *dev, int nr = sensor_attr->index; int map; - if (data->pwm_temp_map[nr] < 3) - map = BIT(data->pwm_temp_map[nr]); - else - map = 0; /* Should never happen */ - return sprintf(buf, "%d\n", map); + map = data->pwm_temp_map[nr]; + if (map >= 3) + map = 0; /* Should never happen */ + if (nr >= 3) /* pwm channels 3..6 map to temp4..6 */ + map += 3; + + return sprintf(buf, "%d\n", (int)BIT(map)); } static ssize_t set_pwm_temp_map(struct device *dev, @@ -1423,6 +1427,9 @@ static ssize_t set_pwm_temp_map(struct device *dev, if (kstrtol(buf, 10, &val) < 0) return -EINVAL; + if (nr >= 3) + val -= 3; + switch (val) { case BIT(0): reg = 0x00; From 71a9c23246fe68954f87787a4e6c1aa22565c326 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 18 Jan 2016 00:35:58 -0800 Subject: [PATCH 34/35] hwmon: (it87) Add support for IT8628E IT8628E is functionally identical to IT8620E. Tested-by: Martin Blumenstingl Signed-off-by: Guenter Roeck --- Documentation/hwmon/it87 | 15 +++++++++------ drivers/hwmon/Kconfig | 3 ++- drivers/hwmon/it87.c | 18 ++++++++++++++++-- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 733296d65449..fff6f6bf55bc 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -9,6 +9,9 @@ Supported chips: * IT8620E Prefix: 'it8620' Addresses scanned: from Super I/O config space (8 I/O ports) + * IT8628E + Prefix: 'it8628' + Addresses scanned: from Super I/O config space (8 I/O ports) Datasheet: Not publicly available * IT8705F Prefix: 'it87' @@ -114,8 +117,8 @@ motherboard models. Description ----------- -This driver implements support for the IT8603E, IT8620E, IT8623E, IT8705F, -IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F, +This driver implements support for the IT8603E, IT8620E, IT8623E, IT8628E, +IT8705F, IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8732F, IT8758E, IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E, and SiS950 chips. @@ -158,8 +161,8 @@ The IT8603E/IT8623E is a custom design, hardware monitoring part is similar to IT8728F. It only supports 3 fans, 16-bit fan mode, and the full speed mode of the fan is not supported (value 0 of pwmX_enable). -The IT8620E is another custom design, hardware monitoring part is similar to -IT8728F. It only supports 16-bit fan mode. +The IT8620E and IT8628E are custom designs, hardware monitoring part is similar +to IT8728F. It only supports 16-bit fan mode. Both chips support up to 6 fans. The IT8790E supports up to 3 fans. 16-bit fan mode is always enabled. @@ -187,8 +190,8 @@ of 0.016 volt. IT8603E, IT8721F/IT8758E and IT8728F can measure between 0 and 2.8 volts with a resolution of 0.0109 volt. The battery voltage in8 does not have limit registers. -On the IT8603E, IT8721F/IT8758E, IT8732F, IT8781F, IT8782F, and IT8783E/F, some -voltage inputs are internal and scaled inside the chip: +On the IT8603E, IT8620E, IT8628E, IT8721F/IT8758E, IT8732F, IT8781F, IT8782F, +and IT8783E/F, some voltage inputs are internal and scaled inside the chip: * in3 (optional) * in7 (optional for IT8781F, IT8782F, and IT8783E/F) * in8 (always) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 1b5b500ede07..ff940075bb90 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -621,7 +621,8 @@ config SENSORS_IT87 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. + IT8603E, IT8620E, IT8623E, and IT8628E sensor chips, and the SiS950 + clone. This driver can also be built as a module. If so, the module will be called it87. diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index cde53c1109ea..730d84028260 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -13,6 +13,7 @@ * Supports: IT8603E Super I/O chip w/LPC interface * IT8620E Super I/O chip w/LPC interface * IT8623E Super I/O chip w/LPC interface + * IT8628E Super I/O chip w/LPC interface * IT8705F Super I/O chip w/LPC interface * IT8712F Super I/O chip w/LPC interface * IT8716F Super I/O chip w/LPC interface @@ -69,7 +70,7 @@ enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8732, it8771, it8772, it8781, it8782, it8783, it8786, it8790, it8603, - it8620 }; + it8620, it8628 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -160,6 +161,7 @@ static inline void superio_exit(int ioreg) #define IT8603E_DEVID 0x8603 #define IT8620E_DEVID 0x8620 #define IT8623E_DEVID 0x8623 +#define IT8628E_DEVID 0x8628 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 @@ -434,6 +436,15 @@ static const struct it87_devices it87_devices[] = { | FEAT_SIX_TEMP, .peci_mask = 0x07, }, + [it8628] = { + .name = "it8628", + .suffix = "E", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS + | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2 + | FEAT_SIX_TEMP, + .peci_mask = 0x07, + }, }; #define has_16bit_fans(data) ((data)->features & FEAT_16BIT_FANS) @@ -2402,6 +2413,9 @@ static int __init it87_find(int sioaddr, unsigned short *address, case IT8620E_DEVID: sio_data->type = it8620; break; + case IT8628E_DEVID: + sio_data->type = it8628; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -2546,7 +2560,7 @@ static int __init it87_find(int sioaddr, unsigned short *address, sio_data->beep_pin = superio_inb(sioaddr, IT87_SIO_BEEP_PIN_REG) & 0x3f; - } else if (sio_data->type == it8620) { + } else if (sio_data->type == it8620 || sio_data->type == it8628) { int reg; superio_select(sioaddr, GPIO); From d6a442df63b2f0043c0b4fc05504ac4ded96ae80 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 24 Apr 2016 22:55:47 +0100 Subject: [PATCH 35/35] hwmon: (sch5636) trivial fix of spelling mistake on revision fix spelling mistake, revison -> revision Signed-off-by: Colin Ian King Signed-off-by: Guenter Roeck --- drivers/hwmon/sch5636.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c index 131a2815dbda..d24d7b6047f2 100644 --- a/drivers/hwmon/sch5636.c +++ b/drivers/hwmon/sch5636.c @@ -449,7 +449,7 @@ static int sch5636_probe(struct platform_device *pdev) } revision[i] = val; } - pr_info("Found %s chip at %#hx, revison: %d.%02d\n", DEVNAME, + pr_info("Found %s chip at %#hx, revision: %d.%02d\n", DEVNAME, data->addr, revision[0], revision[1]); /* Read all temp + fan ctrl registers to determine which are active */