From 195a4b4298a7951c845dac2b326585c66add3435 Mon Sep 17 00:00:00 2001 From: Il Han Date: Sun, 30 Aug 2015 20:44:26 +0900 Subject: hwmon: Driver for Maxim MAX31790 The driver supports the Maxim MAX31790. Signed-off-by: Il Han Signed-off-by: Guenter Roeck --- Documentation/hwmon/max31790 | 37 +++ drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/max31790.c | 603 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 651 insertions(+) create mode 100644 Documentation/hwmon/max31790 create mode 100644 drivers/hwmon/max31790.c diff --git a/Documentation/hwmon/max31790 b/Documentation/hwmon/max31790 new file mode 100644 index 000000000000..855e62430da9 --- /dev/null +++ b/Documentation/hwmon/max31790 @@ -0,0 +1,37 @@ +Kernel driver max31790 +====================== + +Supported chips: + * Maxim MAX31790 + Prefix: 'max31790' + Addresses scanned: - + Datasheet: http://pdfserv.maximintegrated.com/en/ds/MAX31790.pdf + +Author: Il Han + + +Description +----------- + +This driver implements support for the Maxim MAX31790 chip. + +The MAX31790 controls the speeds of up to six fans using six independent +PWM outputs. The desired fan speeds (or PWM duty cycles) are written +through the I2C interface. The outputs drive "4-wire" fans directly, +or can be used to modulate the fan's power terminals using an external +pass transistor. + +Tachometer inputs monitor fan tachometer logic outputs for precise (+/-1%) +monitoring and control of fan RPM as well as detection of fan failure. +Six pins are dedicated tachometer inputs. Any of the six PWM outputs can +also be configured to serve as tachometer inputs. + + +Sysfs entries +------------- + +fan[1-12]_input RO fan tachometer speed in RPM +fan[1-12]_fault RO fan experienced fault +fan[1-6]_target RW desired fan speed in RPM +pwm[1-6]_enable RW regulator mode, 0=disabled, 1=manual mode, 2=rpm mode +pwm[1-6] RW fan target duty cycle (0-255) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e13c902e8966..796569eeaf1d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -840,6 +840,16 @@ config SENSORS_MAX6697 This driver can also be built as a module. If so, the module will be called max6697. +config SENSORS_MAX31790 + tristate "Maxim MAX31790 sensor chip" + depends on I2C + help + If you say yes here you get support for 6-Channel PWM-Output + Fan RPM Controller. + + This driver can also be built as a module. If so, the module + will be called max31790. + config SENSORS_HTU21 tristate "Measurement Specialties HTU21D humidity/temperature sensors" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 9e0f3dd2841d..01855ee641d1 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -115,6 +115,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MAX6697) += max6697.o +obj-$(CONFIG_SENSORS_MAX31790) += max31790.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c new file mode 100644 index 000000000000..f1296680833a --- /dev/null +++ b/drivers/hwmon/max31790.c @@ -0,0 +1,603 @@ +/* + * max31790.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring. + * + * (C) 2015 by Il Han + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* MAX31790 registers */ +#define MAX31790_REG_GLOBAL_CONFIG 0x00 +#define MAX31790_REG_FAN_CONFIG(ch) (0x02 + (ch)) +#define MAX31790_REG_FAN_DYNAMICS(ch) (0x08 + (ch)) +#define MAX31790_REG_FAN_FAULT_STATUS2 0x10 +#define MAX31790_REG_FAN_FAULT_STATUS1 0x11 +#define MAX31790_REG_TACH_COUNT(ch) (0x18 + (ch) * 2) +#define MAX31790_REG_PWM_DUTY_CYCLE(ch) (0x30 + (ch) * 2) +#define MAX31790_REG_PWMOUT(ch) (0x40 + (ch) * 2) +#define MAX31790_REG_TARGET_COUNT(ch) (0x50 + (ch) * 2) + +/* Fan Config register bits */ +#define MAX31790_FAN_CFG_RPM_MODE 0x80 +#define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08 +#define MAX31790_FAN_CFG_TACH_INPUT 0x01 + +/* Fan Dynamics register bits */ +#define MAX31790_FAN_DYN_SR_SHIFT 5 +#define MAX31790_FAN_DYN_SR_MASK 0xE0 +#define SR_FROM_REG(reg) (((reg) & MAX31790_FAN_DYN_SR_MASK) \ + >> MAX31790_FAN_DYN_SR_SHIFT) + +#define FAN_RPM_MIN 120 +#define FAN_RPM_MAX 7864320 + +#define RPM_FROM_REG(reg, sr) (((reg) >> 4) ? \ + ((60 * (sr) * 8192) / ((reg) >> 4)) : \ + FAN_RPM_MAX) +#define RPM_TO_REG(rpm, sr) ((60 * (sr) * 8192) / ((rpm) * 2)) + +#define NR_CHANNEL 6 + +/* + * Client data (each client gets its own) + */ +struct max31790_data { + struct i2c_client *client; + struct mutex update_lock; + bool valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* register values */ + u8 fan_config[NR_CHANNEL]; + u8 fan_dynamics[NR_CHANNEL]; + u16 fault_status; + u16 tach[NR_CHANNEL * 2]; + u16 pwm[NR_CHANNEL]; + u16 target_count[NR_CHANNEL]; +}; + +static struct max31790_data *max31790_update_device(struct device *dev) +{ + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + struct max31790_data *ret = data; + int i; + int rv; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_FAULT_STATUS1); + if (rv < 0) + goto abort; + data->fault_status = rv & 0x3F; + + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_FAULT_STATUS2); + if (rv < 0) + goto abort; + data->fault_status |= (rv & 0x3F) << 6; + + for (i = 0; i < NR_CHANNEL; i++) { + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_TACH_COUNT(i)); + if (rv < 0) + goto abort; + data->tach[i] = rv; + + if (data->fan_config[i] + & MAX31790_FAN_CFG_TACH_INPUT) { + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_TACH_COUNT(NR_CHANNEL + + i)); + if (rv < 0) + goto abort; + data->tach[NR_CHANNEL + i] = rv; + } else { + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_PWMOUT(i)); + if (rv < 0) + goto abort; + data->pwm[i] = rv; + + rv = i2c_smbus_read_word_swapped(client, + MAX31790_REG_TARGET_COUNT(i)); + if (rv < 0) + goto abort; + data->target_count[i] = rv; + } + } + + data->last_updated = jiffies; + data->valid = true; + } + goto done; + +abort: + data->valid = false; + ret = ERR_PTR(rv); + +done: + mutex_unlock(&data->update_lock); + + return ret; +} + +static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 }; + +static u8 get_tach_period(u8 fan_dynamics) +{ + return tach_period[SR_FROM_REG(fan_dynamics)]; +} + +static u8 bits_for_tach_period(int rpm) +{ + u8 bits; + + if (rpm < 500) + bits = 0x0; + else if (rpm < 1000) + bits = 0x1; + else if (rpm < 2000) + bits = 0x2; + else if (rpm < 4000) + bits = 0x3; + else if (rpm < 8000) + bits = 0x4; + else + bits = 0x5; + + return bits; +} + +static ssize_t get_fan(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int sr = get_tach_period(data->fan_dynamics[attr->index]); + int rpm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + rpm = RPM_FROM_REG(data->tach[attr->index], sr); + + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t get_fan_target(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int sr = get_tach_period(data->fan_dynamics[attr->index]); + int rpm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + rpm = RPM_FROM_REG(data->target_count[attr->index], sr); + + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t set_fan_target(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + u8 bits; + int sr; + int target_count; + unsigned long rpm; + int err; + + err = kstrtoul(buf, 10, &rpm); + if (err) + return err; + + mutex_lock(&data->update_lock); + + rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX); + bits = bits_for_tach_period(rpm); + data->fan_dynamics[attr->index] = + ((data->fan_dynamics[attr->index] + & ~MAX31790_FAN_DYN_SR_MASK) + | (bits << MAX31790_FAN_DYN_SR_SHIFT)); + err = i2c_smbus_write_byte_data(client, + MAX31790_REG_FAN_DYNAMICS(attr->index), + data->fan_dynamics[attr->index]); + + if (err < 0) { + mutex_unlock(&data->update_lock); + return err; + } + + sr = get_tach_period(data->fan_dynamics[attr->index]); + target_count = RPM_TO_REG(rpm, sr); + target_count = clamp_val(target_count, 0x1, 0x7FF); + + data->target_count[attr->index] = target_count << 5; + + err = i2c_smbus_write_word_swapped(client, + MAX31790_REG_TARGET_COUNT(attr->index), + data->target_count[attr->index]); + + mutex_unlock(&data->update_lock); + + if (err < 0) + return err; + + return count; +} + +static ssize_t get_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int pwm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + pwm = data->pwm[attr->index] >> 8; + + return sprintf(buf, "%d\n", pwm); +} + +static ssize_t set_pwm(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long pwm; + int err; + + err = kstrtoul(buf, 10, &pwm); + if (err) + return err; + + if (pwm > 255) + return -EINVAL; + + mutex_lock(&data->update_lock); + + data->pwm[attr->index] = pwm << 8; + err = i2c_smbus_write_word_swapped(client, + MAX31790_REG_PWMOUT(attr->index), + data->pwm[attr->index]); + + mutex_unlock(&data->update_lock); + + if (err < 0) + return err; + + return count; +} + +static ssize_t get_pwm_enable(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int mode; + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (data->fan_config[attr->index] & MAX31790_FAN_CFG_RPM_MODE) + mode = 2; + else if (data->fan_config[attr->index] & MAX31790_FAN_CFG_TACH_INPUT_EN) + mode = 1; + else + mode = 0; + + return sprintf(buf, "%d\n", mode); +} + +static ssize_t set_pwm_enable(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long mode; + int err; + + err = kstrtoul(buf, 10, &mode); + if (err) + return err; + + switch (mode) { + case 0: + data->fan_config[attr->index] = + data->fan_config[attr->index] + & ~(MAX31790_FAN_CFG_TACH_INPUT_EN + | MAX31790_FAN_CFG_RPM_MODE); + break; + case 1: + data->fan_config[attr->index] = + (data->fan_config[attr->index] + | MAX31790_FAN_CFG_TACH_INPUT_EN) + & ~MAX31790_FAN_CFG_RPM_MODE; + break; + case 2: + data->fan_config[attr->index] = + data->fan_config[attr->index] + | MAX31790_FAN_CFG_TACH_INPUT_EN + | MAX31790_FAN_CFG_RPM_MODE; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->update_lock); + + err = i2c_smbus_write_byte_data(client, + MAX31790_REG_FAN_CONFIG(attr->index), + data->fan_config[attr->index]); + + mutex_unlock(&data->update_lock); + + if (err < 0) + return err; + + return count; +} + +static ssize_t get_fan_fault(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max31790_data *data = max31790_update_device(dev); + int fault; + + if (IS_ERR(data)) + return PTR_ERR(data); + + fault = !!(data->fault_status & (1 << attr->index)); + + return sprintf(buf, "%d\n", fault); +} + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5); + +static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5); + +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, get_fan, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, get_fan, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, get_fan, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, get_fan, NULL, 9); +static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, get_fan, NULL, 10); +static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, get_fan, NULL, 11); + +static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, get_fan_fault, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, get_fan_fault, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, get_fan_fault, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, get_fan_fault, NULL, 9); +static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, get_fan_fault, NULL, 10); +static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, get_fan_fault, NULL, 11); + +static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 0); +static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 1); +static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 2); +static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 3); +static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 4); +static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO, + get_fan_target, set_fan_target, 5); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0); +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2); +static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3); +static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4); +static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5); + +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 0); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 1); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 2); +static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 3); +static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 4); +static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO, + get_pwm_enable, set_pwm_enable, 5); + +static struct attribute *max31790_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + + &sensor_dev_attr_fan1_fault.dev_attr.attr, + &sensor_dev_attr_fan2_fault.dev_attr.attr, + &sensor_dev_attr_fan3_fault.dev_attr.attr, + &sensor_dev_attr_fan4_fault.dev_attr.attr, + &sensor_dev_attr_fan5_fault.dev_attr.attr, + &sensor_dev_attr_fan6_fault.dev_attr.attr, + + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + &sensor_dev_attr_fan9_input.dev_attr.attr, + &sensor_dev_attr_fan10_input.dev_attr.attr, + &sensor_dev_attr_fan11_input.dev_attr.attr, + &sensor_dev_attr_fan12_input.dev_attr.attr, + + &sensor_dev_attr_fan7_fault.dev_attr.attr, + &sensor_dev_attr_fan8_fault.dev_attr.attr, + &sensor_dev_attr_fan9_fault.dev_attr.attr, + &sensor_dev_attr_fan10_fault.dev_attr.attr, + &sensor_dev_attr_fan11_fault.dev_attr.attr, + &sensor_dev_attr_fan12_fault.dev_attr.attr, + + &sensor_dev_attr_fan1_target.dev_attr.attr, + &sensor_dev_attr_fan2_target.dev_attr.attr, + &sensor_dev_attr_fan3_target.dev_attr.attr, + &sensor_dev_attr_fan4_target.dev_attr.attr, + &sensor_dev_attr_fan5_target.dev_attr.attr, + &sensor_dev_attr_fan6_target.dev_attr.attr, + + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + &sensor_dev_attr_pwm5.dev_attr.attr, + &sensor_dev_attr_pwm6.dev_attr.attr, + + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm4_enable.dev_attr.attr, + &sensor_dev_attr_pwm5_enable.dev_attr.attr, + &sensor_dev_attr_pwm6_enable.dev_attr.attr, + NULL +}; + +static umode_t max31790_attrs_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct max31790_data *data = dev_get_drvdata(dev); + struct device_attribute *devattr = + container_of(a, struct device_attribute, attr); + int index = to_sensor_dev_attr(devattr)->index % NR_CHANNEL; + u8 fan_config; + + fan_config = data->fan_config[index]; + + if (n >= NR_CHANNEL * 2 && n < NR_CHANNEL * 4 && + !(fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return 0; + if (n >= NR_CHANNEL * 4 && (fan_config & MAX31790_FAN_CFG_TACH_INPUT)) + return 0; + + return a->mode; +} + +static const struct attribute_group max31790_group = { + .attrs = max31790_attrs, + .is_visible = max31790_attrs_visible, +}; +__ATTRIBUTE_GROUPS(max31790); + +static int max31790_init_client(struct i2c_client *client, + struct max31790_data *data) +{ + int i, rv; + + for (i = 0; i < NR_CHANNEL; i++) { + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_CONFIG(i)); + if (rv < 0) + return rv; + data->fan_config[i] = rv; + + rv = i2c_smbus_read_byte_data(client, + MAX31790_REG_FAN_DYNAMICS(i)); + if (rv < 0) + return rv; + data->fan_dynamics[i] = rv; + } + + return 0; +} + +static int max31790_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct max31790_data *data; + struct device *hwmon_dev; + int err; + + if (!i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct max31790_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + /* + * Initialize the max31790 chip + */ + err = max31790_init_client(client, data); + if (err) + return err; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, data, max31790_groups); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id max31790_id[] = { + { "max31790", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max31790_id); + +static struct i2c_driver max31790_driver = { + .class = I2C_CLASS_HWMON, + .probe = max31790_probe, + .driver = { + .name = "max31790", + }, + .id_table = max31790_id, +}; + +module_i2c_driver(max31790_driver); + +MODULE_AUTHOR("Il Han "); +MODULE_DESCRIPTION("MAX31790 sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 791432cfd95bea3515f8cdfaf72ec263e5d3da80 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 16 Sep 2015 19:32:59 +0530 Subject: hwmon: (max31790) Fix dereference of ERR_PTR max31790_update_device() return the error code in ERR_PTR. We were checking if it has returned error or not but before checking we have dereferenced it. Signed-off-by: Sudip Mukherjee Signed-off-by: Guenter Roeck --- drivers/hwmon/max31790.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index f1296680833a..69c0ac80a946 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -174,12 +174,12 @@ static ssize_t get_fan(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max31790_data *data = max31790_update_device(dev); - int sr = get_tach_period(data->fan_dynamics[attr->index]); - int rpm; + int sr, rpm; if (IS_ERR(data)) return PTR_ERR(data); + sr = get_tach_period(data->fan_dynamics[attr->index]); rpm = RPM_FROM_REG(data->tach[attr->index], sr); return sprintf(buf, "%d\n", rpm); @@ -190,12 +190,12 @@ static ssize_t get_fan_target(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max31790_data *data = max31790_update_device(dev); - int sr = get_tach_period(data->fan_dynamics[attr->index]); - int rpm; + int sr, rpm; if (IS_ERR(data)) return PTR_ERR(data); + sr = get_tach_period(data->fan_dynamics[attr->index]); rpm = RPM_FROM_REG(data->target_count[attr->index], sr); return sprintf(buf, "%d\n", rpm); -- cgit v1.2.3 From 47e4b5e152dd78476ed28ddeca3fd1ffe00cc3be Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 21 Sep 2015 16:47:04 +0100 Subject: hwmon: (abx500) drop the use of IRQF_NO_SUSPEND The description in the driver states: "ABX500 does not provide auto ADC, so to monitor the required temperatures, a periodic work is used. It is more important to not wake up the CPU... If the chip gets too hot during a sleep state it's most likely due to external factors, such as the surrounding temperature and nothing can be done in S/W." So it makes no sense to keep IRQs enabled as it need not be wakeup source. This patch removes the use of IRQF_NO_SUSPEND flag Signed-off-by: Sudeep Holla Signed-off-by: Guenter Roeck --- drivers/hwmon/abx500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c index 1fd46859ed29..d87cae8c635f 100644 --- a/drivers/hwmon/abx500.c +++ b/drivers/hwmon/abx500.c @@ -377,7 +377,7 @@ static int setup_irqs(struct platform_device *pdev) } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - abx500_temp_irq_handler, IRQF_NO_SUSPEND, "abx500-temp", pdev); + abx500_temp_irq_handler, 0, "abx500-temp", pdev); if (ret < 0) dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); -- cgit v1.2.3 From 0b056b29f28e45adaf756e579c1bf291584a87cd Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Wed, 23 Sep 2015 14:44:48 +0200 Subject: hwmon: (ibmpowernv) Add OF compatibility table entry Fix module autoload for IBM and Open power platforms. Signed-off-by: Cédric Le Goater Signed-off-by: Guenter Roeck --- drivers/hwmon/ibmpowernv.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 4255514b2c72..55b5a8ff1cfe 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -474,11 +474,18 @@ static const struct platform_device_id opal_sensor_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids); +static const struct of_device_id opal_sensor_match[] = { + { .compatible = "ibm,opal-sensor" }, + { }, +}; +MODULE_DEVICE_TABLE(of, opal_sensor_match); + static struct platform_driver ibmpowernv_driver = { .probe = ibmpowernv_probe, .id_table = opal_sensor_driver_ids, .driver = { .name = DRVNAME, + .of_match_table = opal_sensor_match, }, }; -- cgit v1.2.3 From 9c32e815cf9e29d5cfed738ad1cb3d07ea1bb67c Mon Sep 17 00:00:00 2001 From: Ben Gardner Date: Wed, 7 Oct 2015 21:55:20 -0500 Subject: hwmon: (lm75) Add support for TMP75C The TMP75C has a different control register layout and only supports 12-bit temperature samples (0.0625 deg C). The continuous sample rate is ~12 Hz. Signed-off-by: Ben Gardner Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm75 | 5 +++-- drivers/hwmon/lm75.c | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/lm75 b/Documentation/hwmon/lm75 index 67691a0aa41d..ac95edfcd907 100644 --- a/Documentation/hwmon/lm75 +++ b/Documentation/hwmon/lm75 @@ -42,8 +42,8 @@ Supported chips: Addresses scanned: none Datasheet: Publicly available at the ST website http://www.st.com/internet/analog/product/121769.jsp - * Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP175, TMP275 - Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp275' + * Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP75C, TMP175, TMP275 + Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp75c', 'tmp275' Addresses scanned: none Datasheet: Publicly available at the Texas Instruments website http://www.ti.com/product/tmp100 @@ -51,6 +51,7 @@ Supported chips: http://www.ti.com/product/tmp105 http://www.ti.com/product/tmp112 http://www.ti.com/product/tmp75 + http://www.ti.com/product/tmp75c http://www.ti.com/product/tmp175 http://www.ti.com/product/tmp275 * NXP LM75B diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index e4e57bbafb10..0addc84ba948 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -57,6 +57,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ tmp175, tmp275, tmp75, + tmp75c, }; /* Addresses scanned */ @@ -280,6 +281,11 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) data->resolution = 12; data->sample_time = HZ / 2; break; + case tmp75c: + clr_mask |= 1 << 5; /* not one-shot mode */ + data->resolution = 12; + data->sample_time = HZ / 4; + break; } /* configure as specified */ @@ -343,6 +349,7 @@ static const struct i2c_device_id lm75_ids[] = { { "tmp175", tmp175, }, { "tmp275", tmp275, }, { "tmp75", tmp75, }, + { "tmp75c", tmp75c, }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm75_ids); -- cgit v1.2.3 From cc904f9cf26dc455cffbdf6e7eb86717e047d8ff Mon Sep 17 00:00:00 2001 From: Lukasz Odzioba Date: Mon, 12 Oct 2015 13:53:32 +0200 Subject: hwmon: (coretemp) Increase limit of maximum core ID from 32 to 128. A new limit selected arbitrarily as power of two greater than required minimum for Xeon Phi processor (72 for Knights Landing). Currently driver is not able to handle cores with core ID greater than 32. Such attempt ends up with the following error in dmesg: coretemp coretemp.0: Adding Core XXX failed Signed-off-by: Lukasz Odzioba Signed-off-by: Guenter Roeck --- drivers/hwmon/coretemp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 3e03379e7c5d..6a27eb2fed17 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -52,7 +52,7 @@ module_param_named(tjmax, force_tjmax, int, 0444); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ -#define NUM_REAL_CORES 32 /* Number of Real cores per cpu */ +#define NUM_REAL_CORES 128 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */ #define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) -- cgit v1.2.3 From a0de56c81fcf9f1a691e22e519b0dff21c48c645 Mon Sep 17 00:00:00 2001 From: Marc Titinger Date: Wed, 28 Oct 2015 12:04:53 +0100 Subject: hwmon: (ina2xx) convert driver to using regmap Any sysfs "show" read access from the client app will result in reading all registers (8 with ina226). Depending on the host this can limit the best achievable read rate. This changeset allows for individual register accesses through regmap. Tested with BeagleBone Black (Baylibre-ACME) and ina226. Signed-off-by: Marc Titinger Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 214 ++++++++++++++++++++----------------------------- 1 file changed, 86 insertions(+), 128 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 4d2815079fc2..96862c6eae74 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -84,6 +85,11 @@ */ #define INA226_TOTAL_CONV_TIME_DEFAULT 2200 +static struct regmap_config ina2xx_regmap_config = { + .reg_bits = 8, + .val_bits = 16, +}; + enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { @@ -97,20 +103,14 @@ struct ina2xx_config { }; struct ina2xx_data { - struct i2c_client *client; const struct ina2xx_config *config; long rshunt; - u16 curr_config; - - struct mutex update_lock; - bool valid; - unsigned long last_updated; - int update_interval; /* in jiffies */ + struct mutex config_lock; + struct regmap *regmap; int kind; const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS]; - u16 regs[INA2XX_MAX_REGISTERS]; }; static const struct ina2xx_config ina2xx_config[] = { @@ -153,7 +153,11 @@ static int ina226_reg_to_interval(u16 config) return DIV_ROUND_CLOSEST(avg * INA226_TOTAL_CONV_TIME_DEFAULT, 1000); } -static u16 ina226_interval_to_reg(int interval, u16 config) +/* + * Return the new, shifted AVG field value of CONFIG register, + * to use with regmap_update_bits + */ +static u16 ina226_interval_to_reg(int interval) { int avg, avg_bits; @@ -162,15 +166,7 @@ static u16 ina226_interval_to_reg(int interval, u16 config) avg_bits = find_closest(avg, ina226_avg_tab, ARRAY_SIZE(ina226_avg_tab)); - return (config & ~INA226_AVG_RD_MASK) | INA226_SHIFT_AVG(avg_bits); -} - -static void ina226_set_update_interval(struct ina2xx_data *data) -{ - int ms; - - ms = ina226_reg_to_interval(data->curr_config); - data->update_interval = msecs_to_jiffies(ms); + return INA226_SHIFT_AVG(avg_bits); } static int ina2xx_calibrate(struct ina2xx_data *data) @@ -178,8 +174,7 @@ static int ina2xx_calibrate(struct ina2xx_data *data) u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor, data->rshunt); - return i2c_smbus_write_word_swapped(data->client, - INA2XX_CALIBRATION, val); + return regmap_write(data->regmap, INA2XX_CALIBRATION, val); } /* @@ -187,12 +182,8 @@ static int ina2xx_calibrate(struct ina2xx_data *data) */ static int ina2xx_init(struct ina2xx_data *data) { - struct i2c_client *client = data->client; - int ret; - - /* device configuration */ - ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, - data->curr_config); + int ret = regmap_write(data->regmap, INA2XX_CONFIG, + data->config->config_default); if (ret < 0) return ret; @@ -203,47 +194,52 @@ static int ina2xx_init(struct ina2xx_data *data) return ina2xx_calibrate(data); } -static int ina2xx_do_update(struct device *dev) +static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval) { struct ina2xx_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int i, rv, retry; + int ret, retry; - dev_dbg(&client->dev, "Starting ina2xx update\n"); + dev_dbg(dev, "Starting register %d read\n", reg); for (retry = 5; retry; retry--) { - /* Read all registers */ - for (i = 0; i < data->config->registers; i++) { - rv = i2c_smbus_read_word_swapped(client, i); - if (rv < 0) - return rv; - data->regs[i] = rv; - } + + ret = regmap_read(data->regmap, reg, regval); + if (ret < 0) + return ret; + + dev_dbg(dev, "read %d, val = 0x%04x\n", reg, *regval); /* * If the current value in the calibration register is 0, the * power and current registers will also remain at 0. In case * the chip has been reset let's check the calibration * register and reinitialize if needed. + * We do that extra read of the calibration register if there + * is some hint of a chip reset. */ - if (data->regs[INA2XX_CALIBRATION] == 0) { - dev_warn(dev, "chip not calibrated, reinitializing\n"); - - rv = ina2xx_init(data); - if (rv < 0) - return rv; - - /* - * Let's make sure the power and current registers - * have been updated before trying again. - */ - msleep(INA2XX_MAX_DELAY); - continue; + if (*regval == 0) { + unsigned int cal; + + ret = regmap_read(data->regmap, INA2XX_CALIBRATION, + &cal); + if (ret < 0) + return ret; + + if (cal == 0) { + dev_warn(dev, "chip not calibrated, reinitializing\n"); + + ret = ina2xx_init(data); + if (ret < 0) + return ret; + /* + * Let's make sure the power and current + * registers have been updated before trying + * again. + */ + msleep(INA2XX_MAX_DELAY); + continue; + } } - - data->last_updated = jiffies; - data->valid = 1; - return 0; } @@ -256,51 +252,31 @@ static int ina2xx_do_update(struct device *dev) return -ENODEV; } -static struct ina2xx_data *ina2xx_update_device(struct device *dev) -{ - struct ina2xx_data *data = dev_get_drvdata(dev); - struct ina2xx_data *ret = data; - unsigned long after; - int rv; - - mutex_lock(&data->update_lock); - - after = data->last_updated + data->update_interval; - if (time_after(jiffies, after) || !data->valid) { - rv = ina2xx_do_update(dev); - if (rv < 0) - ret = ERR_PTR(rv); - } - - mutex_unlock(&data->update_lock); - return ret; -} - -static int ina2xx_get_value(struct ina2xx_data *data, u8 reg) +static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, + unsigned int regval) { int val; switch (reg) { case INA2XX_SHUNT_VOLTAGE: /* signed register */ - val = DIV_ROUND_CLOSEST((s16)data->regs[reg], - data->config->shunt_div); + val = DIV_ROUND_CLOSEST((s16)regval, data->config->shunt_div); break; case INA2XX_BUS_VOLTAGE: - val = (data->regs[reg] >> data->config->bus_voltage_shift) + val = (regval >> data->config->bus_voltage_shift) * data->config->bus_voltage_lsb; val = DIV_ROUND_CLOSEST(val, 1000); break; case INA2XX_POWER: - val = data->regs[reg] * data->config->power_lsb; + val = regval * data->config->power_lsb; break; case INA2XX_CURRENT: /* signed register, LSB=1mA (selected), in mA */ - val = (s16)data->regs[reg]; + val = (s16)regval; break; case INA2XX_CALIBRATION: val = DIV_ROUND_CLOSEST(data->config->calibration_factor, - data->regs[reg]); + regval); break; default: /* programmer goofed */ @@ -316,25 +292,25 @@ static ssize_t ina2xx_show_value(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct ina2xx_data *data = ina2xx_update_device(dev); + struct ina2xx_data *data = dev_get_drvdata(dev); + unsigned int regval; - if (IS_ERR(data)) - return PTR_ERR(data); + int err = ina2xx_read_reg(dev, attr->index, ®val); + + if (err < 0) + return err; return snprintf(buf, PAGE_SIZE, "%d\n", - ina2xx_get_value(data, attr->index)); + ina2xx_get_value(data, attr->index, regval)); } static ssize_t ina2xx_set_shunt(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { - struct ina2xx_data *data = ina2xx_update_device(dev); unsigned long val; int status; - - if (IS_ERR(data)) - return PTR_ERR(data); + struct ina2xx_data *data = dev_get_drvdata(dev); status = kstrtoul(buf, 10, &val); if (status < 0) @@ -345,10 +321,10 @@ static ssize_t ina2xx_set_shunt(struct device *dev, val > data->config->calibration_factor) return -EINVAL; - mutex_lock(&data->update_lock); + mutex_lock(&data->config_lock); data->rshunt = val; status = ina2xx_calibrate(data); - mutex_unlock(&data->update_lock); + mutex_unlock(&data->config_lock); if (status < 0) return status; @@ -370,17 +346,9 @@ static ssize_t ina226_set_interval(struct device *dev, if (val > INT_MAX || val == 0) return -EINVAL; - mutex_lock(&data->update_lock); - data->curr_config = ina226_interval_to_reg(val, - data->regs[INA2XX_CONFIG]); - status = i2c_smbus_write_word_swapped(data->client, - INA2XX_CONFIG, - data->curr_config); - - ina226_set_update_interval(data); - /* Make sure the next access re-reads all registers. */ - data->valid = 0; - mutex_unlock(&data->update_lock); + status = regmap_update_bits(data->regmap, INA2XX_CONFIG, + INA226_AVG_RD_MASK, + ina226_interval_to_reg(val)); if (status < 0) return status; @@ -390,18 +358,15 @@ static ssize_t ina226_set_interval(struct device *dev, static ssize_t ina226_show_interval(struct device *dev, struct device_attribute *da, char *buf) { - struct ina2xx_data *data = ina2xx_update_device(dev); + struct ina2xx_data *data = dev_get_drvdata(dev); + int status; + unsigned int regval; - if (IS_ERR(data)) - return PTR_ERR(data); + status = regmap_read(data->regmap, INA2XX_CONFIG, ®val); + if (status) + return status; - /* - * We don't use data->update_interval here as we want to display - * the actual interval used by the chip and jiffies_to_msecs() - * doesn't seem to be accurate enough. - */ - return snprintf(buf, PAGE_SIZE, "%d\n", - ina226_reg_to_interval(data->regs[INA2XX_CONFIG])); + return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval)); } /* shunt voltage */ @@ -455,7 +420,6 @@ static const struct attribute_group ina226_group = { static int ina2xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct i2c_adapter *adapter = client->adapter; struct ina2xx_platform_data *pdata; struct device *dev = &client->dev; struct ina2xx_data *data; @@ -463,9 +427,6 @@ static int ina2xx_probe(struct i2c_client *client, u32 val; int ret, group = 0; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -483,29 +444,26 @@ static int ina2xx_probe(struct i2c_client *client, /* set the device type */ data->kind = id->driver_data; data->config = &ina2xx_config[data->kind]; - data->curr_config = data->config->config_default; - data->client = client; - - /* - * Ina226 has a variable update_interval. For ina219 we - * use a constant value. - */ - if (data->kind == ina226) - ina226_set_update_interval(data); - else - data->update_interval = HZ / INA2XX_CONVERSION_RATE; if (data->rshunt <= 0 || data->rshunt > data->config->calibration_factor) return -ENODEV; + ina2xx_regmap_config.max_register = data->config->registers; + + data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + ret = ina2xx_init(data); if (ret < 0) { dev_err(dev, "error configuring the device: %d\n", ret); return -ENODEV; } - mutex_init(&data->update_lock); + mutex_init(&data->config_lock); data->groups[group++] = &ina2xx_group; if (data->kind == ina226) -- cgit v1.2.3 From 001e2e730ce4e6dc2cd97fcb169097febfc7b200 Mon Sep 17 00:00:00 2001 From: Marc Titinger Date: Tue, 27 Oct 2015 10:51:08 +0100 Subject: hwmon: (ina2xx) give precedence to DT over checking for platform data. when checking for the value of the shunt resistor. Signed-off-by: Marc Titinger Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 96862c6eae74..1ba0c72c2b8f 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -420,7 +420,6 @@ static const struct attribute_group ina226_group = { static int ina2xx_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct ina2xx_platform_data *pdata; struct device *dev = &client->dev; struct ina2xx_data *data; struct device *hwmon_dev; @@ -431,24 +430,24 @@ static int ina2xx_probe(struct i2c_client *client, if (!data) return -ENOMEM; - if (dev_get_platdata(dev)) { - pdata = dev_get_platdata(dev); - data->rshunt = pdata->shunt_uohms; - } else if (!of_property_read_u32(dev->of_node, - "shunt-resistor", &val)) { - data->rshunt = val; - } else { - data->rshunt = INA2XX_RSHUNT_DEFAULT; - } - /* set the device type */ data->kind = id->driver_data; data->config = &ina2xx_config[data->kind]; - if (data->rshunt <= 0 || - data->rshunt > data->config->calibration_factor) + if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) { + struct ina2xx_platform_data *pdata = dev_get_platdata(dev); + + if (pdata) + val = pdata->shunt_uohms; + else + val = INA2XX_RSHUNT_DEFAULT; + } + + if (val <= 0 || val > data->config->calibration_factor) return -ENODEV; + data->rshunt = val; + ina2xx_regmap_config.max_register = data->config->registers; data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); -- cgit v1.2.3 From 9a38371a8cda366400e592d10bc062deea09b695 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 29 Aug 2015 15:29:25 -0700 Subject: hwmon: (nct6775) NCT6791D and NCT6792D have an additional temperature source Both NCT6791D and NCT6792D permit selection of a 'virtual' temperature register as temperature source. The virtual temperature registers are registers 0xea to 0xef in bank 0 and can be written by software. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 8b4fa55e46c6..724401a368f7 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -515,16 +515,24 @@ static const char *const nct6779_temp_label[] = { "PCH_DIM1_TEMP", "PCH_DIM2_TEMP", "PCH_DIM3_TEMP", - "BYTE_TEMP" + "BYTE_TEMP", + "", + "", + "", + "", + "Virtual_TEMP" }; -static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1] +#define NCT6779_NUM_LABELS (ARRAY_SIZE(nct6779_temp_label) - 5) +#define NCT6791_NUM_LABELS ARRAY_SIZE(nct6779_temp_label) + +static const u16 NCT6779_REG_TEMP_ALTERNATE[NCT6791_NUM_LABELS - 1] = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407, 0x408, 0 }; -static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] +static const u16 NCT6779_REG_TEMP_CRIT[NCT6791_NUM_LABELS - 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; /* NCT6791 specific data */ @@ -3605,7 +3613,7 @@ static int nct6775_probe(struct platform_device *pdev) data->speed_tolerance_limit = 63; data->temp_label = nct6779_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + data->temp_label_num = NCT6779_NUM_LABELS; data->REG_CONFIG = NCT6775_REG_CONFIG; data->REG_VBAT = NCT6775_REG_VBAT; @@ -3683,7 +3691,7 @@ static int nct6775_probe(struct platform_device *pdev) data->speed_tolerance_limit = 63; data->temp_label = nct6779_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + data->temp_label_num = NCT6791_NUM_LABELS; data->REG_CONFIG = NCT6775_REG_CONFIG; data->REG_VBAT = NCT6775_REG_VBAT; -- cgit v1.2.3 From 50224f4d0959981ed03c407af1f35ed7917ae097 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 30 Oct 2015 07:52:39 -0700 Subject: hwmon: (nct6775) Introduce separate temperature labels for NCT6792 and NCT6793 NCT6792 and NCT6793 are mostly register compatible to NCT6791, but temperature sources are different and difficult to manage with a single temperature label array. Introduce separate temperature label arrays for those chips to reflect the differences. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 724401a368f7..d7ebdf8651f5 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -565,6 +565,76 @@ static const u16 NCT6792_REG_TEMP_MON[] = { static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5, 0xbf }; +static const char *const nct6792_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN0", + "AUXTIN1", + "AUXTIN2", + "AUXTIN3", + "", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "SMBUSMASTER 2", + "SMBUSMASTER 3", + "SMBUSMASTER 4", + "SMBUSMASTER 5", + "SMBUSMASTER 6", + "SMBUSMASTER 7", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP", + "BYTE_TEMP", + "PECI Agent 0 Calibration", + "PECI Agent 1 Calibration", + "", + "", + "Virtual_TEMP" +}; + +static const char *const nct6793_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN0", + "AUXTIN1", + "AUXTIN2", + "AUXTIN3", + "", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "", + "", + "", + "", + "", + "", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "Agent0 Dimm0 ", + "Agent0 Dimm1", + "Agent1 Dimm0", + "Agent1 Dimm1", + "BYTE_TEMP0", + "BYTE_TEMP1", + "PECI Agent 0 Calibration", + "PECI Agent 1 Calibration", + "", + "Virtual_TEMP" +}; + /* NCT6102D/NCT6106D specific data */ #define NCT6106_REG_VBAT 0x318 @@ -3690,7 +3760,18 @@ static int nct6775_probe(struct platform_device *pdev) data->tolerance_mask = 0x07; data->speed_tolerance_limit = 63; - data->temp_label = nct6779_temp_label; + switch (data->kind) { + default: + case nct6791: + data->temp_label = nct6779_temp_label; + break; + case nct6792: + data->temp_label = nct6792_temp_label; + break; + case nct6793: + data->temp_label = nct6793_temp_label; + break; + } data->temp_label_num = NCT6791_NUM_LABELS; data->REG_CONFIG = NCT6775_REG_CONFIG; -- cgit v1.2.3 From 5aa4e83dd54a3b33d0f2fbab0a1f06a3614a5949 Mon Sep 17 00:00:00 2001 From: Marc Titinger Date: Thu, 29 Oct 2015 10:07:17 +0100 Subject: hwmon: (ina2xx) remove no longer used variable 'kind' Signed-off-by: Marc Titinger Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 1ba0c72c2b8f..b24f1d3045f0 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -109,7 +109,6 @@ struct ina2xx_data { struct mutex config_lock; struct regmap *regmap; - int kind; const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS]; }; @@ -431,8 +430,7 @@ static int ina2xx_probe(struct i2c_client *client, return -ENOMEM; /* set the device type */ - data->kind = id->driver_data; - data->config = &ina2xx_config[data->kind]; + data->config = &ina2xx_config[id->driver_data]; if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) { struct ina2xx_platform_data *pdata = dev_get_platdata(dev); @@ -465,7 +463,7 @@ static int ina2xx_probe(struct i2c_client *client, mutex_init(&data->config_lock); data->groups[group++] = &ina2xx_group; - if (data->kind == ina226) + if (id->driver_data == ina226) data->groups[group++] = &ina226_group; hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, -- cgit v1.2.3 From 7deb14b1316def5d4c85f99718d9e530d53cef4e Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Fri, 30 Oct 2015 17:56:55 +0800 Subject: hwmon: (fam15h_power) Refactor attributes for dynamically added Attributes depend on the CPU model the driver gets loaded on. Therefore, add those attributes dynamically at init time. This is more flexible to control the different attributes on different platforms. Suggested-by: Borislav Petkov Signed-off-by: Huang Rui Cc: Guenter Roeck Cc: Peter Zijlstra Cc: Ingo Molnar Signed-off-by: Guenter Roeck --- drivers/hwmon/fam15h_power.c | 70 ++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index e80ee23b62d3..2d899fd75f2a 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -41,12 +41,17 @@ MODULE_LICENSE("GPL"); #define REG_TDP_RUNNING_AVERAGE 0xe0 #define REG_TDP_LIMIT3 0xe8 +#define FAM15H_MIN_NUM_ATTRS 2 +#define FAM15H_NUM_GROUPS 2 + struct fam15h_power_data { struct pci_dev *pdev; unsigned int tdp_to_watts; unsigned int base_tdp; unsigned int processor_pwr_watts; unsigned int cpu_pwr_sample_ratio; + const struct attribute_group *groups[FAM15H_NUM_GROUPS]; + struct attribute_group group; }; static ssize_t show_power(struct device *dev, @@ -105,29 +110,31 @@ static ssize_t show_power_crit(struct device *dev, } static DEVICE_ATTR(power1_crit, S_IRUGO, show_power_crit, NULL); -static umode_t fam15h_power_is_visible(struct kobject *kobj, - struct attribute *attr, - int index) +static int fam15h_power_init_attrs(struct pci_dev *pdev, + struct fam15h_power_data *data) { - /* power1_input is only reported for Fam15h, Models 00h-0fh */ - if (attr == &dev_attr_power1_input.attr && - (boot_cpu_data.x86 != 0x15 || boot_cpu_data.x86_model > 0xf)) - return 0; + int n = FAM15H_MIN_NUM_ATTRS; + struct attribute **fam15h_power_attrs; - return attr->mode; -} + if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model <= 0xf) + n += 1; -static struct attribute *fam15h_power_attrs[] = { - &dev_attr_power1_input.attr, - &dev_attr_power1_crit.attr, - NULL -}; + fam15h_power_attrs = devm_kcalloc(&pdev->dev, n, + sizeof(*fam15h_power_attrs), + GFP_KERNEL); -static const struct attribute_group fam15h_power_group = { - .attrs = fam15h_power_attrs, - .is_visible = fam15h_power_is_visible, -}; -__ATTRIBUTE_GROUPS(fam15h_power); + if (!fam15h_power_attrs) + return -ENOMEM; + + n = 0; + fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr; + if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model <= 0xf) + fam15h_power_attrs[n++] = &dev_attr_power1_input.attr; + + data->group.attrs = fam15h_power_attrs; + + return 0; +} static bool should_load_on_this_node(struct pci_dev *f4) { @@ -186,11 +193,12 @@ static int fam15h_power_resume(struct pci_dev *pdev) #define fam15h_power_resume NULL #endif -static void fam15h_power_init_data(struct pci_dev *f4, - struct fam15h_power_data *data) +static int fam15h_power_init_data(struct pci_dev *f4, + struct fam15h_power_data *data) { u32 val, eax, ebx, ecx, edx; u64 tmp; + int ret; pci_read_config_dword(f4, REG_PROCESSOR_TDP, &val); data->base_tdp = val >> 16; @@ -211,11 +219,15 @@ static void fam15h_power_init_data(struct pci_dev *f4, /* convert to microWatt */ data->processor_pwr_watts = (tmp * 15625) >> 10; + ret = fam15h_power_init_attrs(f4, data); + if (ret) + return ret; + cpuid(0x80000007, &eax, &ebx, &ecx, &edx); /* CPUID Fn8000_0007:EDX[12] indicates to support accumulated power */ if (!(edx & BIT(12))) - return; + return 0; /* * determine the ratio of the compute unit power accumulator @@ -223,14 +235,17 @@ static void fam15h_power_init_data(struct pci_dev *f4, * Fn8000_0007:ECX */ data->cpu_pwr_sample_ratio = ecx; + + return 0; } static int fam15h_power_probe(struct pci_dev *pdev, - const struct pci_device_id *id) + const struct pci_device_id *id) { struct fam15h_power_data *data; struct device *dev = &pdev->dev; struct device *hwmon_dev; + int ret; /* * though we ignore every other northbridge, we still have to @@ -246,12 +261,17 @@ static int fam15h_power_probe(struct pci_dev *pdev, if (!data) return -ENOMEM; - fam15h_power_init_data(pdev, data); + ret = fam15h_power_init_data(pdev, data); + if (ret) + return ret; + data->pdev = pdev; + data->groups[0] = &data->group; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, "fam15h_power", data, - fam15h_power_groups); + &data->groups[0]); return PTR_ERR_OR_ZERO(hwmon_dev); } -- cgit v1.2.3 From 46f29c2b494600cd326b84f87f5765e80fffface Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Fri, 30 Oct 2015 17:56:56 +0800 Subject: hwmon: (fam15h_power) Enable power1_input on AMD Carrizo This patch enables power1_input attribute for Carrizo platform. Signed-off-by: Huang Rui Cc: Borislav Petkov Cc: Guenter Roeck Cc: Peter Zijlstra Cc: Ingo Molnar 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 2d899fd75f2a..a46e166cdd7e 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -115,8 +115,11 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev, { int n = FAM15H_MIN_NUM_ATTRS; struct attribute **fam15h_power_attrs; + struct cpuinfo_x86 *c = &boot_cpu_data; - if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model <= 0xf) + if (c->x86 == 0x15 && + (c->x86_model <= 0xf || + (c->x86_model >= 0x60 && c->x86_model <= 0x6f))) n += 1; fam15h_power_attrs = devm_kcalloc(&pdev->dev, n, @@ -128,7 +131,9 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev, n = 0; fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr; - if (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model <= 0xf) + if (c->x86 == 0x15 && + (c->x86_model <= 0xf || + (c->x86_model >= 0x60 && c->x86_model <= 0x6f))) fam15h_power_attrs[n++] = &dev_attr_power1_input.attr; data->group.attrs = fam15h_power_attrs; -- cgit v1.2.3 From 3b5ea47dbff0c934b7b979bcc772427a2404ed3d Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Fri, 30 Oct 2015 17:56:57 +0800 Subject: hwmon: (fam15h_power) Add max compute unit accumulated power This patch adds a member in fam15h_power_data which specifies the maximum accumulated power in a compute unit. Signed-off-by: Huang Rui Cc: Borislav Petkov Cc: Guenter Roeck Cc: Peter Zijlstra Cc: Ingo Molnar Signed-off-by: Guenter Roeck --- drivers/hwmon/fam15h_power.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index a46e166cdd7e..5f7067d7b625 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -26,6 +26,7 @@ #include #include #include +#include MODULE_DESCRIPTION("AMD Family 15h CPU processor power monitor"); MODULE_AUTHOR("Andreas Herrmann "); @@ -44,6 +45,8 @@ MODULE_LICENSE("GPL"); #define FAM15H_MIN_NUM_ATTRS 2 #define FAM15H_NUM_GROUPS 2 +#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b + struct fam15h_power_data { struct pci_dev *pdev; unsigned int tdp_to_watts; @@ -52,6 +55,8 @@ struct fam15h_power_data { unsigned int cpu_pwr_sample_ratio; const struct attribute_group *groups[FAM15H_NUM_GROUPS]; struct attribute_group group; + /* maximum accumulated power of a compute unit */ + u64 max_cu_acc_power; }; static ssize_t show_power(struct device *dev, @@ -241,6 +246,13 @@ static int fam15h_power_init_data(struct pci_dev *f4, */ data->cpu_pwr_sample_ratio = ecx; + if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) { + pr_err("Failed to read max compute unit power accumulator MSR\n"); + return -ENODEV; + } + + data->max_cu_acc_power = tmp; + return 0; } -- cgit v1.2.3