diff options
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/Kconfig | 26 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 2 | ||||
-rw-r--r-- | drivers/hwmon/adm1025.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/adm1026.c | 26 | ||||
-rw-r--r-- | drivers/hwmon/adm9240.c | 9 | ||||
-rw-r--r-- | drivers/hwmon/adt7411.c | 301 | ||||
-rw-r--r-- | drivers/hwmon/adt7462.c | 12 | ||||
-rw-r--r-- | drivers/hwmon/adt7470.c | 6 | ||||
-rw-r--r-- | drivers/hwmon/amc6821.c | 4 | ||||
-rw-r--r-- | drivers/hwmon/coretemp.c | 321 | ||||
-rw-r--r-- | drivers/hwmon/ds620.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/emc2103.c | 4 | ||||
-rw-r--r-- | drivers/hwmon/emc6w201.c | 8 | ||||
-rw-r--r-- | drivers/hwmon/g762.c | 11 | ||||
-rw-r--r-- | drivers/hwmon/hwmon.c | 79 | ||||
-rw-r--r-- | drivers/hwmon/lm85.c | 3 | ||||
-rw-r--r-- | drivers/hwmon/lm87.c | 136 | ||||
-rw-r--r-- | drivers/hwmon/lm90.c | 2 | ||||
-rw-r--r-- | drivers/hwmon/mcp3021.c | 50 | ||||
-rw-r--r-- | drivers/hwmon/nct7802.c | 8 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/adm1275.c | 20 | ||||
-rw-r--r-- | drivers/hwmon/scpi-hwmon.c | 1 | ||||
-rw-r--r-- | drivers/hwmon/smsc47m192.c | 5 | ||||
-rw-r--r-- | drivers/hwmon/tc654.c | 514 | ||||
-rw-r--r-- | drivers/hwmon/tmp108.c | 469 | ||||
-rw-r--r-- | drivers/hwmon/via-cputemp.c | 77 |
26 files changed, 1543 insertions, 555 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 45cef3d2c75c..190d270b20a2 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -907,6 +907,17 @@ config SENSORS_MCP3021 This driver can also be built as a module. If so, the module will be called mcp3021. +config SENSORS_TC654 + tristate "Microchip TC654/TC655 and compatibles" + depends on I2C + help + If you say yes here you get support for TC654 and TC655. + The TC654 and TC655 are PWM mode fan speed controllers with + FanSense technology for use with brushless DC fans. + + This driver can also be built as a module. If so, the module + will be called tc654. + config SENSORS_MENF21BMC_HWMON tristate "MEN 14F021P00 BMC Hardware Monitoring" depends on MFD_MENF21BMC @@ -1068,8 +1079,8 @@ config SENSORS_LM90 LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A, Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, - Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, and GMT G781 - sensor chips. + Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, GMT G781, and + Texas Instruments TMP451 sensor chips. This driver can also be built as a module. If so, the module will be called lm90. @@ -1591,6 +1602,17 @@ config SENSORS_TMP103 This driver can also be built as a module. If so, the module will be called tmp103. +config SENSORS_TMP108 + tristate "Texas Instruments TMP108" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments TMP108 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called tmp108. + config SENSORS_TMP401 tristate "Texas Instruments TMP401 and compatibles" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index aecf4ba17460..d2cb7e804a0f 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -122,6 +122,7 @@ 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_TC654) += tc654.o obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o @@ -152,6 +153,7 @@ obj-$(CONFIG_SENSORS_TC74) += tc74.o obj-$(CONFIG_SENSORS_THMC50) += thmc50.o obj-$(CONFIG_SENSORS_TMP102) += tmp102.o obj-$(CONFIG_SENSORS_TMP103) += tmp103.o +obj-$(CONFIG_SENSORS_TMP108) += tmp108.o obj-$(CONFIG_SENSORS_TMP401) += tmp401.o obj-$(CONFIG_SENSORS_TMP421) += tmp421.o obj-$(CONFIG_SENSORS_TWL4030_MADC)+= twl4030-madc-hwmon.o diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index d6c767ace916..1abb4609b412 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -93,7 +93,7 @@ static const int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 }; #define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192) #define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \ - (val) * 192 >= (scale) * 255 ? 255 : \ + (val) >= (scale) * 255 / 192 ? 255 : \ ((val) * 192 + (scale) / 2) / (scale)) #define TEMP_FROM_REG(reg) ((reg) * 1000) diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index e67b9a50ac7c..b2a5d9e5c590 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -197,8 +197,9 @@ static int adm1026_scaling[] = { /* .001 Volts */ }; #define NEG12_OFFSET 16000 #define SCALE(val, from, to) (((val)*(to) + ((from)/2))/(from)) -#define INS_TO_REG(n, val) (clamp_val(SCALE(val, adm1026_scaling[n], 192),\ - 0, 255)) +#define INS_TO_REG(n, val) \ + SCALE(clamp_val(val, 0, 255 * adm1026_scaling[n] / 192), \ + adm1026_scaling[n], 192) #define INS_FROM_REG(n, val) (SCALE(val, 192, adm1026_scaling[n])) /* @@ -215,11 +216,11 @@ static int adm1026_scaling[] = { /* .001 Volts */ #define DIV_TO_REG(val) ((val) >= 8 ? 3 : (val) >= 4 ? 2 : (val) >= 2 ? 1 : 0) /* Temperature is reported in 1 degC increments */ -#define TEMP_TO_REG(val) (clamp_val(((val) + ((val) < 0 ? -500 : 500)) \ - / 1000, -127, 127)) +#define TEMP_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), \ + 1000) #define TEMP_FROM_REG(val) ((val) * 1000) -#define OFFSET_TO_REG(val) (clamp_val(((val) + ((val) < 0 ? -500 : 500)) \ - / 1000, -127, 127)) +#define OFFSET_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), \ + 1000) #define OFFSET_FROM_REG(val) ((val) * 1000) #define PWM_TO_REG(val) (clamp_val(val, 0, 255)) @@ -233,7 +234,8 @@ static int adm1026_scaling[] = { /* .001 Volts */ * indicates that the DAC could be used to drive the fans, but in our * example board (Arima HDAMA) it isn't connected to the fans at all. */ -#define DAC_TO_REG(val) (clamp_val(((((val) * 255) + 500) / 2500), 0, 255)) +#define DAC_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val(val, 0, 2500) * 255, \ + 2500) #define DAC_FROM_REG(val) (((val) * 2500) / 255) /* @@ -593,7 +595,10 @@ static ssize_t set_in16_min(struct device *dev, struct device_attribute *attr, return err; mutex_lock(&data->update_lock); - data->in_min[16] = INS_TO_REG(16, val + NEG12_OFFSET); + data->in_min[16] = INS_TO_REG(16, + clamp_val(val, INT_MIN, + INT_MAX - NEG12_OFFSET) + + NEG12_OFFSET); adm1026_write_value(client, ADM1026_REG_IN_MIN[16], data->in_min[16]); mutex_unlock(&data->update_lock); return count; @@ -618,7 +623,10 @@ static ssize_t set_in16_max(struct device *dev, struct device_attribute *attr, return err; mutex_lock(&data->update_lock); - data->in_max[16] = INS_TO_REG(16, val+NEG12_OFFSET); + data->in_max[16] = INS_TO_REG(16, + clamp_val(val, INT_MIN, + INT_MAX - NEG12_OFFSET) + + NEG12_OFFSET); adm1026_write_value(client, ADM1026_REG_IN_MAX[16], data->in_max[16]); mutex_unlock(&data->update_lock); return count; diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 2fe1828bd10b..72bf2489511e 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -98,13 +98,15 @@ static inline unsigned int IN_FROM_REG(u8 reg, int n) static inline u8 IN_TO_REG(unsigned long val, int n) { - return clamp_val(SCALE(val, 192, nom_mv[n]), 0, 255); + val = clamp_val(val, 0, nom_mv[n] * 255 / 192); + return SCALE(val, 192, nom_mv[n]); } /* temperature range: -40..125, 127 disables temperature alarm */ static inline s8 TEMP_TO_REG(long val) { - return clamp_val(SCALE(val, 1, 1000), -40, 127); + val = clamp_val(val, -40000, 127000); + return SCALE(val, 1, 1000); } /* two fans, each with low fan speed limit */ @@ -122,7 +124,8 @@ static inline unsigned int FAN_FROM_REG(u8 reg, u8 div) /* analog out 0..1250mV */ static inline u8 AOUT_TO_REG(unsigned long val) { - return clamp_val(SCALE(val, 255, 1250), 0, 255); + val = clamp_val(val, 0, 1250); + return SCALE(val, 255, 1250); } static inline unsigned int AOUT_FROM_REG(u8 reg) diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index 812fbc00f693..bdeaece9641d 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -55,7 +55,7 @@ struct adt7411_data { struct mutex device_lock; /* for "atomic" device accesses */ struct mutex update_lock; unsigned long next_update; - int vref_cached; + long vref_cached; struct i2c_client *client; bool use_ext_temp; }; @@ -114,85 +114,6 @@ static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit, return ret; } -static ssize_t adt7411_show_vdd(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct adt7411_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, - ADT7411_REG_VDD_MSB, 2); - - return ret < 0 ? ret : sprintf(buf, "%u\n", ret * 7000 / 1024); -} - -static ssize_t adt7411_show_temp(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int nr = to_sensor_dev_attr(attr)->index; - struct adt7411_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int val; - struct { - u8 low; - u8 high; - } reg[2] = { - { ADT7411_REG_INT_TEMP_VDD_LSB, ADT7411_REG_INT_TEMP_MSB }, - { ADT7411_REG_EXT_TEMP_AIN14_LSB, - ADT7411_REG_EXT_TEMP_AIN1_MSB }, - }; - - val = adt7411_read_10_bit(client, reg[nr].low, reg[nr].high, 0); - if (val < 0) - return val; - - val = val & 0x200 ? val - 0x400 : val; /* 10 bit signed */ - - return sprintf(buf, "%d\n", val * 250); -} - -static ssize_t adt7411_show_input(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int nr = to_sensor_dev_attr(attr)->index; - struct adt7411_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int val; - u8 lsb_reg, lsb_shift; - - mutex_lock(&data->update_lock); - if (time_after_eq(jiffies, data->next_update)) { - val = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3); - if (val < 0) - goto exit_unlock; - - if (val & ADT7411_CFG3_REF_VDD) { - val = adt7411_read_10_bit(client, - ADT7411_REG_INT_TEMP_VDD_LSB, - ADT7411_REG_VDD_MSB, 2); - if (val < 0) - goto exit_unlock; - - data->vref_cached = val * 7000 / 1024; - } else { - data->vref_cached = 2250; - } - - data->next_update = jiffies + HZ; - } - - lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2); - lsb_shift = 2 * (nr & 0x03); - val = adt7411_read_10_bit(client, lsb_reg, - ADT7411_REG_EXT_TEMP_AIN1_MSB + nr, lsb_shift); - if (val < 0) - goto exit_unlock; - - val = sprintf(buf, "%u\n", val * data->vref_cached / 1024); - exit_unlock: - mutex_unlock(&data->update_lock); - return val; -} - static ssize_t adt7411_show_bit(struct device *dev, struct device_attribute *attr, char *buf) { @@ -228,65 +149,157 @@ static ssize_t adt7411_set_bit(struct device *dev, return ret < 0 ? ret : count; } - #define ADT7411_BIT_ATTR(__name, __reg, __bit) \ SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \ adt7411_set_bit, __bit, __reg) -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, adt7411_show_temp, NULL, 1); -static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL); -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1); -static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, adt7411_show_input, NULL, 2); -static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, adt7411_show_input, NULL, 3); -static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, adt7411_show_input, NULL, 4); -static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, adt7411_show_input, NULL, 5); -static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, adt7411_show_input, NULL, 6); -static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, adt7411_show_input, NULL, 7); static ADT7411_BIT_ATTR(no_average, ADT7411_REG_CFG2, ADT7411_CFG2_DISABLE_AVG); static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_225); static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD); static struct attribute *adt7411_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &dev_attr_in0_input.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - &sensor_dev_attr_in7_input.dev_attr.attr, - &sensor_dev_attr_in8_input.dev_attr.attr, &sensor_dev_attr_no_average.dev_attr.attr, &sensor_dev_attr_fast_sampling.dev_attr.attr, &sensor_dev_attr_adc_ref_vdd.dev_attr.attr, NULL }; +ATTRIBUTE_GROUPS(adt7411); -static umode_t adt7411_attrs_visible(struct kobject *kobj, - struct attribute *attr, int index) +static int adt7411_read_in_vdd(struct device *dev, u32 attr, long *val) { - struct device *dev = container_of(kobj, struct device, kobj); struct adt7411_data *data = dev_get_drvdata(dev); - bool visible = true; + struct i2c_client *client = data->client; + int ret; - if (attr == &sensor_dev_attr_temp2_input.dev_attr.attr) - visible = data->use_ext_temp; - else if (attr == &sensor_dev_attr_in1_input.dev_attr.attr || - attr == &sensor_dev_attr_in2_input.dev_attr.attr) - visible = !data->use_ext_temp; + switch (attr) { + case hwmon_in_input: + ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, + ADT7411_REG_VDD_MSB, 2); + if (ret < 0) + return ret; + *val = ret * 7000 / 1024; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel, + long *val) +{ + struct adt7411_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; - return visible ? attr->mode : 0; + int ret; + int lsb_reg, lsb_shift; + int nr = channel - 1; + + mutex_lock(&data->update_lock); + if (time_after_eq(jiffies, data->next_update)) { + ret = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3); + if (ret < 0) + goto exit_unlock; + + if (ret & ADT7411_CFG3_REF_VDD) { + ret = adt7411_read_in_vdd(dev, hwmon_in_input, + &data->vref_cached); + if (ret < 0) + goto exit_unlock; + } else { + data->vref_cached = 2250; + } + + data->next_update = jiffies + HZ; + } + + switch (attr) { + case hwmon_in_input: + lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2); + lsb_shift = 2 * (nr & 0x03); + ret = adt7411_read_10_bit(client, lsb_reg, + ADT7411_REG_EXT_TEMP_AIN1_MSB + nr, + lsb_shift); + if (ret < 0) + goto exit_unlock; + *val = ret * data->vref_cached / 1024; + ret = 0; + break; + default: + ret = -EOPNOTSUPP; + break; + } + exit_unlock: + mutex_unlock(&data->update_lock); + return ret; } -static const struct attribute_group adt7411_group = { - .attrs = adt7411_attrs, - .is_visible = adt7411_attrs_visible, -}; -__ATTRIBUTE_GROUPS(adt7411); +static int adt7411_read_in(struct device *dev, u32 attr, int channel, + long *val) +{ + if (channel == 0) + return adt7411_read_in_vdd(dev, attr, val); + else + return adt7411_read_in_chan(dev, attr, channel, val); +} + +static int adt7411_read_temp(struct device *dev, u32 attr, int channel, + long *val) +{ + struct adt7411_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int ret, regl, regh; + + switch (attr) { + case hwmon_temp_input: + regl = channel ? ADT7411_REG_EXT_TEMP_AIN14_LSB : + ADT7411_REG_INT_TEMP_VDD_LSB; + regh = channel ? ADT7411_REG_EXT_TEMP_AIN1_MSB : + ADT7411_REG_INT_TEMP_MSB; + ret = adt7411_read_10_bit(client, regl, regh, 0); + if (ret < 0) + return ret; + ret = ret & 0x200 ? ret - 0x400 : ret; /* 10 bit signed */ + *val = ret * 250; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int adt7411_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_in: + return adt7411_read_in(dev, attr, channel, val); + case hwmon_temp: + return adt7411_read_temp(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t adt7411_is_visible(const void *_data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct adt7411_data *data = _data; + + switch (type) { + case hwmon_in: + if (channel > 0 && channel < 3) + return data->use_ext_temp ? 0 : S_IRUGO; + else + return S_IRUGO; + case hwmon_temp: + if (channel == 1) + return data->use_ext_temp ? S_IRUGO : 0; + else + return S_IRUGO; + default: + return 0; + } +} static int adt7411_detect(struct i2c_client *client, struct i2c_board_info *info) @@ -358,6 +371,51 @@ static int adt7411_init_device(struct adt7411_data *data) return i2c_smbus_write_byte_data(data->client, ADT7411_REG_CFG1, val); } +static const u32 adt7411_in_config[] = { + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + 0 +}; + +static const struct hwmon_channel_info adt7411_in = { + .type = hwmon_in, + .config = adt7411_in_config, +}; + +static const u32 adt7411_temp_config[] = { + HWMON_T_INPUT, + HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info adt7411_temp = { + .type = hwmon_temp, + .config = adt7411_temp_config, +}; + +static const struct hwmon_channel_info *adt7411_info[] = { + &adt7411_in, + &adt7411_temp, + NULL +}; + +static const struct hwmon_ops adt7411_hwmon_ops = { + .is_visible = adt7411_is_visible, + .read = adt7411_read, +}; + +static const struct hwmon_chip_info adt7411_chip_info = { + .ops = &adt7411_hwmon_ops, + .info = adt7411_info, +}; + static int adt7411_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -382,9 +440,10 @@ static int adt7411_probe(struct i2c_client *client, /* force update on first occasion */ data->next_update = jiffies; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - adt7411_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &adt7411_chip_info, + adt7411_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 5929e126da63..19f2a6d48bac 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -810,8 +810,8 @@ static ssize_t set_temp_min(struct device *dev, if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) return -EINVAL; + temp = clamp_val(temp, -64000, 191000); temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; - temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->temp_min[attr->index] = temp; @@ -848,8 +848,8 @@ static ssize_t set_temp_max(struct device *dev, if (kstrtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) return -EINVAL; + temp = clamp_val(temp, -64000, 191000); temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; - temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->temp_max[attr->index] = temp; @@ -912,9 +912,9 @@ static ssize_t set_volt_max(struct device *dev, if (kstrtol(buf, 10, &temp) || !x) return -EINVAL; + temp = clamp_val(temp, 0, 255 * x / 1000); temp *= 1000; /* convert mV to uV */ temp = DIV_ROUND_CLOSEST(temp, x); - temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->volt_max[attr->index] = temp; @@ -954,9 +954,9 @@ static ssize_t set_volt_min(struct device *dev, if (kstrtol(buf, 10, &temp) || !x) return -EINVAL; + temp = clamp_val(temp, 0, 255 * x / 1000); temp *= 1000; /* convert mV to uV */ temp = DIV_ROUND_CLOSEST(temp, x); - temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->volt_min[attr->index] = temp; @@ -1220,8 +1220,8 @@ static ssize_t set_pwm_hyst(struct device *dev, if (kstrtol(buf, 10, &temp)) return -EINVAL; + temp = clamp_val(temp, 0, 15000); temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = clamp_val(temp, 0, 15); /* package things up */ temp &= ADT7462_PWM_HYST_MASK; @@ -1306,8 +1306,8 @@ static ssize_t set_pwm_tmin(struct device *dev, if (kstrtol(buf, 10, &temp)) return -EINVAL; + temp = clamp_val(temp, -64000, 191000); temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; - temp = clamp_val(temp, 0, 255); mutex_lock(&data->lock); data->pwm_tmin[attr->index] = temp; diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 6e60ca53406e..c9a1d9c25572 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -483,8 +483,8 @@ static ssize_t set_temp_min(struct device *dev, if (kstrtol(buf, 10, &temp)) return -EINVAL; + temp = clamp_val(temp, -128000, 127000); temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = clamp_val(temp, -128, 127); mutex_lock(&data->lock); data->temp_min[attr->index] = temp; @@ -517,8 +517,8 @@ static ssize_t set_temp_max(struct device *dev, if (kstrtol(buf, 10, &temp)) return -EINVAL; + temp = clamp_val(temp, -128000, 127000); temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = clamp_val(temp, -128, 127); mutex_lock(&data->lock); data->temp_max[attr->index] = temp; @@ -880,8 +880,8 @@ static ssize_t set_pwm_tmin(struct device *dev, if (kstrtol(buf, 10, &temp)) return -EINVAL; + temp = clamp_val(temp, -128000, 127000); temp = DIV_ROUND_CLOSEST(temp, 1000); - temp = clamp_val(temp, -128, 127); mutex_lock(&data->lock); data->pwm_tmin[attr->index] = temp; diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 12e851a5af48..46b4e35fd555 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -188,8 +188,8 @@ static struct amc6821_data *amc6821_update_device(struct device *dev) !data->valid) { for (i = 0; i < TEMP_IDX_LEN; i++) - data->temp[i] = i2c_smbus_read_byte_data(client, - temp_reg[i]); + data->temp[i] = (int8_t)i2c_smbus_read_byte_data( + client, temp_reg[i]); data->stat1 = i2c_smbus_read_byte_data(client, AMC6821_REG_STAT1); diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 6a27eb2fed17..3ac4c03ba77b 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -51,6 +51,7 @@ static int force_tjmax; module_param_named(tjmax, force_tjmax, int, 0444); MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); +#define PKG_SYSFS_ATTR_NO 1 /* Sysfs attribute for package temp */ #define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */ #define NUM_REAL_CORES 128 /* Number of Real cores per cpu */ #define CORETEMP_NAME_LENGTH 19 /* String Length of attrs */ @@ -58,7 +59,6 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius"); #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) -#define TO_PHYS_ID(cpu) (cpu_data(cpu).phys_proc_id) #define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id) #define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) @@ -102,20 +102,17 @@ struct temp_data { /* Platform Data per Physical CPU */ struct platform_data { - struct device *hwmon_dev; - u16 phys_proc_id; - struct temp_data *core_data[MAX_CORE_DATA]; + struct device *hwmon_dev; + u16 pkg_id; + struct cpumask cpumask; + struct temp_data *core_data[MAX_CORE_DATA]; struct device_attribute name_attr; }; -struct pdev_entry { - struct list_head list; - struct platform_device *pdev; - u16 phys_proc_id; -}; - -static LIST_HEAD(pdev_list); -static DEFINE_MUTEX(pdev_list_mutex); +/* Keep track of how many package pointers we allocated in init() */ +static int max_packages __read_mostly; +/* Array of package pointers. Serialized by cpu hotplug lock */ +static struct platform_device **pkg_devices; static ssize_t show_label(struct device *dev, struct device_attribute *devattr, char *buf) @@ -125,7 +122,7 @@ static ssize_t show_label(struct device *dev, struct temp_data *tdata = pdata->core_data[attr->index]; if (tdata->is_pkg_data) - return sprintf(buf, "Physical id %u\n", pdata->phys_proc_id); + return sprintf(buf, "Package id %u\n", pdata->pkg_id); return sprintf(buf, "Core %u\n", tdata->cpu_core_id); } @@ -138,7 +135,9 @@ static ssize_t show_crit_alarm(struct device *dev, struct platform_data *pdata = dev_get_drvdata(dev); struct temp_data *tdata = pdata->core_data[attr->index]; + mutex_lock(&tdata->update_lock); rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx); + mutex_unlock(&tdata->update_lock); return sprintf(buf, "%d\n", (eax >> 5) & 1); } @@ -435,18 +434,10 @@ static int chk_ucode_version(unsigned int cpu) static struct platform_device *coretemp_get_pdev(unsigned int cpu) { - u16 phys_proc_id = TO_PHYS_ID(cpu); - struct pdev_entry *p; - - mutex_lock(&pdev_list_mutex); + int pkgid = topology_logical_package_id(cpu); - list_for_each_entry(p, &pdev_list, list) - if (p->phys_proc_id == phys_proc_id) { - mutex_unlock(&pdev_list_mutex); - return p->pdev; - } - - mutex_unlock(&pdev_list_mutex); + if (pkgid >= 0 && pkgid < max_packages) + return pkg_devices[pkgid]; return NULL; } @@ -483,21 +474,11 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, * The attr number is always core id + 2 * The Pkgtemp will always show up as temp1_*, if available */ - attr_no = pkg_flag ? 1 : TO_ATTR_NO(cpu); + attr_no = pkg_flag ? PKG_SYSFS_ATTR_NO : TO_ATTR_NO(cpu); if (attr_no > MAX_CORE_DATA - 1) return -ERANGE; - /* - * Provide a single set of attributes for all HT siblings of a core - * to avoid duplicate sensors (the processor ID and core ID of all - * HT siblings of a core are the same). - * Skip if a HT sibling of this core is already registered. - * This is not an error. - */ - if (pdata->core_data[attr_no] != NULL) - return 0; - tdata = init_temp_data(cpu, pkg_flag); if (!tdata) return -ENOMEM; @@ -539,21 +520,14 @@ exit_free: return err; } -static void coretemp_add_core(unsigned int cpu, int pkg_flag) +static void +coretemp_add_core(struct platform_device *pdev, unsigned int cpu, int pkg_flag) { - struct platform_device *pdev = coretemp_get_pdev(cpu); - int err; - - if (!pdev) - return; - - err = create_core_data(pdev, cpu, pkg_flag); - if (err) + if (create_core_data(pdev, cpu, pkg_flag)) dev_err(&pdev->dev, "Adding Core %u failed\n", cpu); } -static void coretemp_remove_core(struct platform_data *pdata, - int indx) +static void coretemp_remove_core(struct platform_data *pdata, int indx) { struct temp_data *tdata = pdata->core_data[indx]; @@ -574,7 +548,7 @@ static int coretemp_probe(struct platform_device *pdev) if (!pdata) return -ENOMEM; - pdata->phys_proc_id = pdev->id; + pdata->pkg_id = pdev->id; platform_set_drvdata(pdev, pdata); pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, @@ -602,85 +576,33 @@ static struct platform_driver coretemp_driver = { .remove = coretemp_remove, }; -static int coretemp_device_add(unsigned int cpu) +static struct platform_device *coretemp_device_add(unsigned int cpu) { - int err; + int err, pkgid = topology_logical_package_id(cpu); struct platform_device *pdev; - struct pdev_entry *pdev_entry; - mutex_lock(&pdev_list_mutex); + if (pkgid < 0) + return ERR_PTR(-ENOMEM); - pdev = platform_device_alloc(DRVNAME, TO_PHYS_ID(cpu)); - if (!pdev) { - err = -ENOMEM; - pr_err("Device allocation failed\n"); - goto exit; - } - - pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL); - if (!pdev_entry) { - err = -ENOMEM; - goto exit_device_put; - } + pdev = platform_device_alloc(DRVNAME, pkgid); + if (!pdev) + return ERR_PTR(-ENOMEM); err = platform_device_add(pdev); if (err) { - pr_err("Device addition failed (%d)\n", err); - goto exit_device_free; - } - - pdev_entry->pdev = pdev; - pdev_entry->phys_proc_id = pdev->id; - - list_add_tail(&pdev_entry->list, &pdev_list); - mutex_unlock(&pdev_list_mutex); - - return 0; - -exit_device_free: - kfree(pdev_entry); -exit_device_put: - platform_device_put(pdev); -exit: - mutex_unlock(&pdev_list_mutex); - return err; -} - -static void coretemp_device_remove(unsigned int cpu) -{ - struct pdev_entry *p, *n; - u16 phys_proc_id = TO_PHYS_ID(cpu); - - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - if (p->phys_proc_id != phys_proc_id) - continue; - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); + platform_device_put(pdev); + return ERR_PTR(err); } - mutex_unlock(&pdev_list_mutex); -} - -static bool is_any_core_online(struct platform_data *pdata) -{ - int i; - /* Find online cores, except pkgtemp data */ - for (i = MAX_CORE_DATA - 1; i >= 0; --i) { - if (pdata->core_data[i] && - !pdata->core_data[i]->is_pkg_data) { - return true; - } - } - return false; + pkg_devices[pkgid] = pdev; + return pdev; } -static void get_core_online(unsigned int cpu) +static int coretemp_cpu_online(unsigned int cpu) { - struct cpuinfo_x86 *c = &cpu_data(cpu); struct platform_device *pdev = coretemp_get_pdev(cpu); - int err; + struct cpuinfo_x86 *c = &cpu_data(cpu); + struct platform_data *pdata; /* * CPUID.06H.EAX[0] indicates whether the CPU has thermal @@ -688,12 +610,12 @@ static void get_core_online(unsigned int cpu) * without thermal sensors will be filtered out. */ if (!cpu_has(c, X86_FEATURE_DTHERM)) - return; + return -ENODEV; if (!pdev) { /* Check the microcode version of the CPU */ if (chk_ucode_version(cpu)) - return; + return -EINVAL; /* * Alright, we have DTS support. @@ -701,101 +623,100 @@ static void get_core_online(unsigned int cpu) * online. So, initialize per-pkg data structures and * then bring this core online. */ - err = coretemp_device_add(cpu); - if (err) - return; + pdev = coretemp_device_add(cpu); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + /* * Check whether pkgtemp support is available. * If so, add interfaces for pkgtemp. */ if (cpu_has(c, X86_FEATURE_PTS)) - coretemp_add_core(cpu, 1); + coretemp_add_core(pdev, cpu, 1); } + + pdata = platform_get_drvdata(pdev); /* - * Physical CPU device already exists. - * So, just add interfaces for this core. + * Check whether a thread sibling is already online. If not add the + * interface for this CPU core. */ - coretemp_add_core(cpu, 0); + if (!cpumask_intersects(&pdata->cpumask, topology_sibling_cpumask(cpu))) + coretemp_add_core(pdev, cpu, 0); + + cpumask_set_cpu(cpu, &pdata->cpumask); + return 0; } -static void put_core_offline(unsigned int cpu) +static int coretemp_cpu_offline(unsigned int cpu) { - int i, indx; - struct platform_data *pdata; struct platform_device *pdev = coretemp_get_pdev(cpu); + struct platform_data *pd; + struct temp_data *tdata; + int indx, target; /* If the physical CPU device does not exist, just return */ if (!pdev) - return; - - pdata = platform_get_drvdata(pdev); - - indx = TO_ATTR_NO(cpu); + return 0; /* The core id is too big, just return */ + indx = TO_ATTR_NO(cpu); if (indx > MAX_CORE_DATA - 1) - return; + return 0; - if (pdata->core_data[indx] && pdata->core_data[indx]->cpu == cpu) - coretemp_remove_core(pdata, indx); + pd = platform_get_drvdata(pdev); + tdata = pd->core_data[indx]; + + cpumask_clear_cpu(cpu, &pd->cpumask); /* - * If a HT sibling of a core is taken offline, but another HT sibling - * of the same core is still online, register the alternate sibling. - * This ensures that exactly one set of attributes is provided as long - * as at least one HT sibling of a core is online. + * If this is the last thread sibling, remove the CPU core + * interface, If there is still a sibling online, transfer the + * target cpu of that core interface to it. */ - for_each_sibling(i, cpu) { - if (i != cpu) { - get_core_online(i); - /* - * Display temperature sensor data for one HT sibling - * per core only, so abort the loop after one such - * sibling has been found. - */ - break; - } + target = cpumask_any_and(&pd->cpumask, topology_sibling_cpumask(cpu)); + if (target >= nr_cpu_ids) { + coretemp_remove_core(pd, indx); + } else if (tdata && tdata->cpu == cpu) { + mutex_lock(&tdata->update_lock); + tdata->cpu = target; + mutex_unlock(&tdata->update_lock); } + /* - * If all cores in this pkg are offline, remove the device. - * coretemp_device_remove calls unregister_platform_device, - * which in turn calls coretemp_remove. This removes the - * pkgtemp entry and does other clean ups. + * If all cores in this pkg are offline, remove the device. This + * will invoke the platform driver remove function, which cleans up + * the rest. */ - if (!is_any_core_online(pdata)) - coretemp_device_remove(cpu); -} + if (cpumask_empty(&pd->cpumask)) { + pkg_devices[topology_logical_package_id(cpu)] = NULL; + platform_device_unregister(pdev); + return 0; + } -static int coretemp_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - unsigned int cpu = (unsigned long) hcpu; - - switch (action) { - case CPU_ONLINE: - case CPU_DOWN_FAILED: - get_core_online(cpu); - break; - case CPU_DOWN_PREPARE: - put_core_offline(cpu); - break; + /* + * Check whether this core is the target for the package + * interface. We need to assign it to some other cpu. + */ + tdata = pd->core_data[PKG_SYSFS_ATTR_NO]; + if (tdata && tdata->cpu == cpu) { + target = cpumask_first(&pd->cpumask); + mutex_lock(&tdata->update_lock); + tdata->cpu = target; + mutex_unlock(&tdata->update_lock); } - return NOTIFY_OK; + return 0; } - -static struct notifier_block coretemp_cpu_notifier __refdata = { - .notifier_call = coretemp_cpu_callback, -}; - static const struct x86_cpu_id __initconst coretemp_ids[] = { { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, {} }; MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); +static enum cpuhp_state coretemp_hp_online; + static int __init coretemp_init(void) { - int i, err; + int err; /* * CPUID.06H.EAX[0] indicates whether the CPU has thermal @@ -805,54 +726,38 @@ static int __init coretemp_init(void) if (!x86_match_cpu(coretemp_ids)) return -ENODEV; + max_packages = topology_max_packages(); + pkg_devices = kzalloc(max_packages * sizeof(struct platform_device *), + GFP_KERNEL); + if (!pkg_devices) + return -ENOMEM; + err = platform_driver_register(&coretemp_driver); if (err) - goto exit; + return err; - cpu_notifier_register_begin(); - for_each_online_cpu(i) - get_core_online(i); - -#ifndef CONFIG_HOTPLUG_CPU - if (list_empty(&pdev_list)) { - cpu_notifier_register_done(); - err = -ENODEV; - goto exit_driver_unreg; - } -#endif - - __register_hotcpu_notifier(&coretemp_cpu_notifier); - cpu_notifier_register_done(); + err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online", + coretemp_cpu_online, coretemp_cpu_offline); + if (err < 0) + goto outdrv; + coretemp_hp_online = err; return 0; -#ifndef CONFIG_HOTPLUG_CPU -exit_driver_unreg: +outdrv: platform_driver_unregister(&coretemp_driver); -#endif -exit: + kfree(pkg_devices); return err; } +module_init(coretemp_init) static void __exit coretemp_exit(void) { - struct pdev_entry *p, *n; - - cpu_notifier_register_begin(); - __unregister_hotcpu_notifier(&coretemp_cpu_notifier); - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } - mutex_unlock(&pdev_list_mutex); - cpu_notifier_register_done(); + cpuhp_remove_state(coretemp_hp_online); platform_driver_unregister(&coretemp_driver); + kfree(pkg_devices); } +module_exit(coretemp_exit) MODULE_AUTHOR("Rudolf Marek <r.marek@assembler.cz>"); MODULE_DESCRIPTION("Intel Core temperature monitor"); MODULE_LICENSE("GPL"); - -module_init(coretemp_init) -module_exit(coretemp_exit) diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c index edf550fc4eef..0043a4c02b85 100644 --- a/drivers/hwmon/ds620.c +++ b/drivers/hwmon/ds620.c @@ -166,7 +166,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, if (res) return res; - val = (val * 10 / 625) * 8; + val = (clamp_val(val, -128000, 128000) * 10 / 625) * 8; mutex_lock(&data->update_lock); data->temp[attr->index] = val; diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c index 24e395c5907d..4b870ee9b0d3 100644 --- a/drivers/hwmon/emc2103.c +++ b/drivers/hwmon/emc2103.c @@ -251,7 +251,7 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *da, if (result < 0) return result; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -63, 127); + val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000); mutex_lock(&data->update_lock); data->temp_min[nr] = val; @@ -273,7 +273,7 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *da, if (result < 0) return result; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -63, 127); + val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000); mutex_lock(&data->update_lock); data->temp_max[nr] = val; diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c index f37fe2011640..4aee5adf9ef2 100644 --- a/drivers/hwmon/emc6w201.c +++ b/drivers/hwmon/emc6w201.c @@ -215,12 +215,13 @@ static ssize_t set_in(struct device *dev, struct device_attribute *devattr, if (err < 0) return err; - val = DIV_ROUND_CLOSEST(val * 0xC0, nominal_mv[nr]); + val = clamp_val(val, 0, 255 * nominal_mv[nr] / 192); + val = DIV_ROUND_CLOSEST(val * 192, nominal_mv[nr]); reg = (sf == min) ? EMC6W201_REG_IN_LOW(nr) : EMC6W201_REG_IN_HIGH(nr); mutex_lock(&data->update_lock); - data->in[sf][nr] = clamp_val(val, 0, 255); + data->in[sf][nr] = val; err = emc6w201_write8(client, reg, data->in[sf][nr]); mutex_unlock(&data->update_lock); @@ -252,12 +253,13 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, if (err < 0) return err; + val = clamp_val(val, -127000, 127000); val = DIV_ROUND_CLOSEST(val, 1000); reg = (sf == min) ? EMC6W201_REG_TEMP_LOW(nr) : EMC6W201_REG_TEMP_HIGH(nr); mutex_lock(&data->update_lock); - data->temp[sf][nr] = clamp_val(val, -127, 127); + data->temp[sf][nr] = val; err = emc6w201_write8(client, reg, data->temp[sf][nr]); mutex_unlock(&data->update_lock); diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index b96a2a9e4df7..628be9c95ff9 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -193,14 +193,17 @@ static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p, * Convert fan RPM value from sysfs into count value for fan controller * register (FAN_SET_CNT). */ -static inline unsigned char cnt_from_rpm(u32 rpm, u32 clk_freq, u16 p, +static inline unsigned char cnt_from_rpm(unsigned long rpm, u32 clk_freq, u16 p, u8 clk_div, u8 gear_mult) { - if (!rpm) /* to stop the fan, set cnt to 255 */ + unsigned long f1 = clk_freq * 30 * gear_mult; + unsigned long f2 = p * clk_div; + + if (!rpm) /* to stop the fan, set cnt to 255 */ return 0xff; - return clamp_val(((clk_freq * 30 * gear_mult) / (rpm * p * clk_div)), - 0, 255); + rpm = clamp_val(rpm, f1 / (255 * f2), ULONG_MAX / f2); + return DIV_ROUND_CLOSEST(f1, rpm * f2); } /* helper to grab and cache data, at most one time per second */ diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index a74c075a30ec..3932f9276c07 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -38,12 +38,15 @@ struct hwmon_device { #define to_hwmon_device(d) container_of(d, struct hwmon_device, dev) +#define MAX_SYSFS_ATTR_NAME_LENGTH 32 + struct hwmon_device_attribute { struct device_attribute dev_attr; const struct hwmon_ops *ops; enum hwmon_sensor_types type; u32 attr; int index; + char name[MAX_SYSFS_ATTR_NAME_LENGTH]; }; #define to_hwmon_attr(d) \ @@ -178,6 +181,22 @@ static ssize_t hwmon_attr_show(struct device *dev, return sprintf(buf, "%ld\n", val); } +static ssize_t hwmon_attr_show_string(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + char *s; + int ret; + + ret = hattr->ops->read_string(dev, hattr->type, hattr->attr, + hattr->index, &s); + if (ret < 0) + return ret; + + return sprintf(buf, "%s\n", s); +} + static ssize_t hwmon_attr_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -205,6 +224,17 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) return 1; } +static bool is_string_attr(enum hwmon_sensor_types type, u32 attr) +{ + return (type == hwmon_temp && attr == hwmon_temp_label) || + (type == hwmon_in && attr == hwmon_in_label) || + (type == hwmon_curr && attr == hwmon_curr_label) || + (type == hwmon_power && attr == hwmon_power_label) || + (type == hwmon_energy && attr == hwmon_energy_label) || + (type == hwmon_humidity && attr == hwmon_humidity_label) || + (type == hwmon_fan && attr == hwmon_fan_label); +} + static struct attribute *hwmon_genattr(struct device *dev, const void *drvdata, enum hwmon_sensor_types type, @@ -218,6 +248,7 @@ static struct attribute *hwmon_genattr(struct device *dev, struct attribute *a; umode_t mode; char *name; + bool is_string = is_string_attr(type, attr); /* The attribute is invisible if there is no template string */ if (!template) @@ -227,32 +258,31 @@ static struct attribute *hwmon_genattr(struct device *dev, if (!mode) return ERR_PTR(-ENOENT); - if ((mode & S_IRUGO) && !ops->read) + if ((mode & S_IRUGO) && ((is_string && !ops->read_string) || + (!is_string && !ops->read))) return ERR_PTR(-EINVAL); if ((mode & S_IWUGO) && !ops->write) return ERR_PTR(-EINVAL); + hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL); + if (!hattr) + return ERR_PTR(-ENOMEM); + if (type == hwmon_chip) { name = (char *)template; } else { - name = devm_kzalloc(dev, strlen(template) + 16, GFP_KERNEL); - if (!name) - return ERR_PTR(-ENOMEM); - scnprintf(name, strlen(template) + 16, template, + scnprintf(hattr->name, sizeof(hattr->name), template, index + hwmon_attr_base(type)); + name = hattr->name; } - hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL); - if (!hattr) - return ERR_PTR(-ENOMEM); - hattr->type = type; hattr->attr = attr; hattr->index = index; hattr->ops = ops; dattr = &hattr->dev_attr; - dattr->show = hwmon_attr_show; + dattr->show = is_string ? hwmon_attr_show_string : hwmon_attr_show; dattr->store = hwmon_attr_store; a = &dattr->attr; @@ -263,7 +293,11 @@ static struct attribute *hwmon_genattr(struct device *dev, return a; } -static const char * const hwmon_chip_attr_templates[] = { +/* + * Chip attributes are not attribute templates but actual sysfs attributes. + * See hwmon_genattr() for special handling. + */ +static const char * const hwmon_chip_attrs[] = { [hwmon_chip_temp_reset_history] = "temp_reset_history", [hwmon_chip_in_reset_history] = "in_reset_history", [hwmon_chip_curr_reset_history] = "curr_reset_history", @@ -400,7 +434,7 @@ static const char * const hwmon_pwm_attr_templates[] = { }; static const char * const *__templates[] = { - [hwmon_chip] = hwmon_chip_attr_templates, + [hwmon_chip] = hwmon_chip_attrs, [hwmon_temp] = hwmon_temp_attr_templates, [hwmon_in] = hwmon_in_attr_templates, [hwmon_curr] = hwmon_curr_attr_templates, @@ -412,7 +446,7 @@ static const char * const *__templates[] = { }; static const int __templates_size[] = { - [hwmon_chip] = ARRAY_SIZE(hwmon_chip_attr_templates), + [hwmon_chip] = ARRAY_SIZE(hwmon_chip_attrs), [hwmon_temp] = ARRAY_SIZE(hwmon_temp_attr_templates), [hwmon_in] = ARRAY_SIZE(hwmon_in_attr_templates), [hwmon_curr] = ARRAY_SIZE(hwmon_curr_attr_templates), @@ -526,9 +560,9 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, hdev = &hwdev->dev; - if (chip && chip->ops->is_visible) { + if (chip) { struct attribute **attrs; - int ngroups = 2; + int ngroups = 2; /* terminating NULL plus &hwdev->groups */ if (groups) for (i = 0; groups[i]; i++) @@ -572,7 +606,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, if (err) goto free_hwmon; - if (chip && chip->ops->is_visible && chip->ops->read && + if (chip && chip->ops->read && chip->info[0]->type == hwmon_chip && (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) { const struct hwmon_channel_info **info = chip->info; @@ -626,8 +660,8 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups); * @dev: the parent device * @name: hwmon name attribute * @drvdata: driver data to attach to created device - * @info: Pointer to hwmon chip information - * @groups - pointer to list of driver specific attribute groups + * @info: pointer to hwmon chip information + * @extra_groups: pointer to list of additional non-standard attribute groups * * hwmon_device_unregister() must be called when the device is no * longer needed. @@ -638,12 +672,12 @@ struct device * hwmon_device_register_with_info(struct device *dev, const char *name, void *drvdata, const struct hwmon_chip_info *chip, - const struct attribute_group **groups) + const struct attribute_group **extra_groups) { - if (chip && (!chip->ops || !chip->info)) + if (chip && (!chip->ops || !chip->ops->is_visible || !chip->info)) return ERR_PTR(-EINVAL); - return __hwmon_device_register(dev, name, drvdata, chip, groups); + return __hwmon_device_register(dev, name, drvdata, chip, extra_groups); } EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); @@ -658,6 +692,9 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_info); */ struct device *hwmon_device_register(struct device *dev) { + dev_warn(dev, + "hwmon_device_register() is deprecated. Please convert the driver to use hwmon_device_register_with_info().\n"); + return hwmon_device_register_with_groups(dev, NULL, NULL, NULL); } EXPORT_SYMBOL_GPL(hwmon_device_register); diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 6ff773fcaefb..29c8136ce9c5 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -136,7 +136,8 @@ static const int lm85_scaling[] = { /* .001 Volts */ #define SCALE(val, from, to) (((val) * (to) + ((from) / 2)) / (from)) #define INS_TO_REG(n, val) \ - clamp_val(SCALE(val, lm85_scaling[n], 192), 0, 255) + SCALE(clamp_val(val, 0, 255 * lm85_scaling[n] / 192), \ + lm85_scaling[n], 192) #define INSEXT_FROM_REG(n, val, ext) \ SCALE(((val) << 4) + (ext), 192 << 4, lm85_scaling[n]) diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index a5e295826aea..13cca3606e06 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -121,7 +121,7 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; #define IN_FROM_REG(reg, scale) (((reg) * (scale) + 96) / 192) #define IN_TO_REG(val, scale) ((val) <= 0 ? 0 : \ - (val) * 192 >= (scale) * 255 ? 255 : \ + (val) >= (scale) * 255 / 192 ? 255 : \ ((val) * 192 + (scale) / 2) / (scale)) #define TEMP_FROM_REG(reg) ((reg) * 1000) @@ -154,7 +154,6 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; */ struct lm87_data { - struct device *hwmon_dev; struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -181,6 +180,8 @@ struct lm87_data { u16 alarms; /* register values, combined */ u8 vid; /* register values, combined */ u8 vrm; + + const struct attribute_group *attr_groups[6]; }; static inline int lm87_read_value(struct i2c_client *client, u8 reg) @@ -195,7 +196,7 @@ static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value) static struct lm87_data *lm87_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = dev_get_drvdata(dev); struct lm87_data *data = i2c_get_clientdata(client); mutex_lock(&data->update_lock); @@ -309,7 +310,7 @@ static ssize_t show_in_max(struct device *dev, static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = dev_get_drvdata(dev); struct lm87_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; long val; @@ -330,7 +331,7 @@ static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = dev_get_drvdata(dev); struct lm87_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; long val; @@ -396,7 +397,7 @@ static ssize_t show_temp_high(struct device *dev, static ssize_t set_temp_low(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = dev_get_drvdata(dev); struct lm87_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; long val; @@ -416,7 +417,7 @@ static ssize_t set_temp_low(struct device *dev, struct device_attribute *attr, static ssize_t set_temp_high(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = dev_get_drvdata(dev); struct lm87_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; long val; @@ -495,7 +496,7 @@ static ssize_t show_fan_div(struct device *dev, static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = dev_get_drvdata(dev); struct lm87_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; long val; @@ -522,7 +523,7 @@ static ssize_t set_fan_min(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) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = dev_get_drvdata(dev); struct lm87_data *data = i2c_get_clientdata(client); int nr = to_sensor_dev_attr(attr)->index; long val; @@ -635,7 +636,7 @@ static ssize_t show_aout(struct device *dev, struct device_attribute *attr, static ssize_t set_aout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); + struct i2c_client *client = dev_get_drvdata(dev); struct lm87_data *data = i2c_get_clientdata(client); long val; int err; @@ -841,23 +842,18 @@ static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info) return 0; } -static void lm87_remove_files(struct i2c_client *client) +static void lm87_restore_config(void *arg) { - struct device *dev = &client->dev; - - sysfs_remove_group(&dev->kobj, &lm87_group); - sysfs_remove_group(&dev->kobj, &lm87_group_in6); - sysfs_remove_group(&dev->kobj, &lm87_group_fan1); - sysfs_remove_group(&dev->kobj, &lm87_group_in7); - sysfs_remove_group(&dev->kobj, &lm87_group_fan2); - sysfs_remove_group(&dev->kobj, &lm87_group_temp3); - sysfs_remove_group(&dev->kobj, &lm87_group_in0_5); - sysfs_remove_group(&dev->kobj, &lm87_group_vid); + struct i2c_client *client = arg; + struct lm87_data *data = i2c_get_clientdata(client); + + lm87_write_value(client, LM87_REG_CONFIG, data->config); } -static void lm87_init_client(struct i2c_client *client) +static int lm87_init_client(struct i2c_client *client) { struct lm87_data *data = i2c_get_clientdata(client); + int rc; if (dev_get_platdata(&client->dev)) { data->channel = *(u8 *)dev_get_platdata(&client->dev); @@ -868,6 +864,10 @@ static void lm87_init_client(struct i2c_client *client) } data->config = lm87_read_value(client, LM87_REG_CONFIG) & 0x6F; + rc = devm_add_action(&client->dev, lm87_restore_config, client); + if (rc) + return rc; + if (!(data->config & 0x01)) { int i; @@ -895,12 +895,15 @@ static void lm87_init_client(struct i2c_client *client) if ((data->config & 0x09) != 0x01) lm87_write_value(client, LM87_REG_CONFIG, (data->config & 0x77) | 0x01); + return 0; } static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lm87_data *data; + struct device *hwmon_dev; int err; + unsigned int group_tail = 0; data = devm_kzalloc(&client->dev, sizeof(struct lm87_data), GFP_KERNEL); if (!data) @@ -910,7 +913,9 @@ static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id) mutex_init(&data->update_lock); /* Initialize the LM87 chip */ - lm87_init_client(client); + err = lm87_init_client(client); + if (err) + return err; data->in_scale[0] = 2500; data->in_scale[1] = 2700; @@ -921,72 +926,34 @@ static int lm87_probe(struct i2c_client *client, const struct i2c_device_id *id) data->in_scale[6] = 1875; data->in_scale[7] = 1875; - /* Register sysfs hooks */ - err = sysfs_create_group(&client->dev.kobj, &lm87_group); - if (err) - goto exit_stop; - - if (data->channel & CHAN_NO_FAN(0)) { - err = sysfs_create_group(&client->dev.kobj, &lm87_group_in6); - if (err) - goto exit_remove; - } else { - err = sysfs_create_group(&client->dev.kobj, &lm87_group_fan1); - if (err) - goto exit_remove; - } - - if (data->channel & CHAN_NO_FAN(1)) { - err = sysfs_create_group(&client->dev.kobj, &lm87_group_in7); - if (err) - goto exit_remove; - } else { - err = sysfs_create_group(&client->dev.kobj, &lm87_group_fan2); - if (err) - goto exit_remove; - } - - if (data->channel & CHAN_TEMP3) { - err = sysfs_create_group(&client->dev.kobj, &lm87_group_temp3); - if (err) - goto exit_remove; - } else { - err = sysfs_create_group(&client->dev.kobj, &lm87_group_in0_5); - if (err) - goto exit_remove; - } + /* + * Construct the list of attributes, the list depends on the + * configuration of the chip + */ + data->attr_groups[group_tail++] = &lm87_group; + if (data->channel & CHAN_NO_FAN(0)) + data->attr_groups[group_tail++] = &lm87_group_in6; + else + data->attr_groups[group_tail++] = &lm87_group_fan1; + + if (data->channel & CHAN_NO_FAN(1)) + data->attr_groups[group_tail++] = &lm87_group_in7; + else + data->attr_groups[group_tail++] = &lm87_group_fan2; + + if (data->channel & CHAN_TEMP3) + data->attr_groups[group_tail++] = &lm87_group_temp3; + else + data->attr_groups[group_tail++] = &lm87_group_in0_5; if (!(data->channel & CHAN_NO_VID)) { data->vrm = vid_which_vrm(); - err = sysfs_create_group(&client->dev.kobj, &lm87_group_vid); - if (err) - goto exit_remove; - } - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; + data->attr_groups[group_tail++] = &lm87_group_vid; } - return 0; - -exit_remove: - lm87_remove_files(client); -exit_stop: - lm87_write_value(client, LM87_REG_CONFIG, data->config); - return err; -} - -static int lm87_remove(struct i2c_client *client) -{ - struct lm87_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - lm87_remove_files(client); - - lm87_write_value(client, LM87_REG_CONFIG, data->config); - return 0; + hwmon_dev = devm_hwmon_device_register_with_groups( + &client->dev, client->name, client, data->attr_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); } /* @@ -1006,7 +973,6 @@ static struct i2c_driver lm87_driver = { .name = "lm87", }, .probe = lm87_probe, - .remove = lm87_remove, .id_table = lm87_id, .detect = lm87_detect, .address_list = normal_i2c, diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 322ed9272811..841f2428e84a 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1036,7 +1036,7 @@ static const u8 lm90_temp_emerg_index[3] = { }; static const u8 lm90_min_alarm_bits[3] = { 5, 3, 11 }; -static const u8 lm90_max_alarm_bits[3] = { 0, 4, 12 }; +static const u8 lm90_max_alarm_bits[3] = { 6, 4, 12 }; static const u8 lm90_crit_alarm_bits[3] = { 0, 1, 9 }; static const u8 lm90_emergency_alarm_bits[3] = { 15, 13, 14 }; static const u8 lm90_fault_bits[3] = { 0, 2, 10 }; diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c index 972444a14cca..1929734c3b1d 100644 --- a/drivers/hwmon/mcp3021.c +++ b/drivers/hwmon/mcp3021.c @@ -4,6 +4,7 @@ * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc. * Author: Mingkai Hu <Mingkai.hu@freescale.com> * Reworked by Sven Schuchmann <schuchmann@schleissheimer.de> + * DT support added by Clemens Gruber <clemens.gruber@pqgruber.com> * * This driver export the value of analog input voltage to sysfs, the * voltage unit is mV. Through the sysfs interface, lm-sensors tool @@ -22,11 +23,13 @@ #include <linux/i2c.h> #include <linux/err.h> #include <linux/device.h> +#include <linux/of.h> +#include <linux/of_device.h> -/* Vdd info */ -#define MCP3021_VDD_MAX 5500 -#define MCP3021_VDD_MIN 2700 -#define MCP3021_VDD_REF 3300 +/* Vdd / reference voltage in millivolt */ +#define MCP3021_VDD_REF_MAX 5500 +#define MCP3021_VDD_REF_MIN 2700 +#define MCP3021_VDD_REF_DEFAULT 3300 /* output format */ #define MCP3021_SAR_SHIFT 2 @@ -47,7 +50,7 @@ enum chips { */ struct mcp3021_data { struct device *hwmon_dev; - u32 vdd; /* device power supply */ + u32 vdd; /* supply and reference voltage in millivolt */ u16 sar_shift; u16 sar_mask; u8 output_res; @@ -99,13 +102,14 @@ static ssize_t show_in_input(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", in_input); } -static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL); +static DEVICE_ATTR(in0_input, 0444, show_in_input, NULL); static int mcp3021_probe(struct i2c_client *client, const struct i2c_device_id *id) { int err; struct mcp3021_data *data = NULL; + struct device_node *np = client->dev.of_node; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; @@ -117,6 +121,21 @@ static int mcp3021_probe(struct i2c_client *client, i2c_set_clientdata(client, data); + if (np) { + if (!of_property_read_u32(np, "reference-voltage-microvolt", + &data->vdd)) + data->vdd /= 1000; + else + data->vdd = MCP3021_VDD_REF_DEFAULT; + } else { + u32 *pdata = dev_get_platdata(&client->dev); + + if (pdata) + data->vdd = *pdata; + else + data->vdd = MCP3021_VDD_REF_DEFAULT; + } + switch (id->driver_data) { case mcp3021: data->sar_shift = MCP3021_SAR_SHIFT; @@ -131,13 +150,8 @@ static int mcp3021_probe(struct i2c_client *client, break; } - if (dev_get_platdata(&client->dev)) { - data->vdd = *(u32 *)dev_get_platdata(&client->dev); - if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN) - return -EINVAL; - } else { - data->vdd = MCP3021_VDD_REF; - } + if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN) + return -EINVAL; err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr); if (err) @@ -173,9 +187,19 @@ static const struct i2c_device_id mcp3021_id[] = { }; MODULE_DEVICE_TABLE(i2c, mcp3021_id); +#ifdef CONFIG_OF +static const struct of_device_id of_mcp3021_match[] = { + { .compatible = "microchip,mcp3021", .data = (void *)mcp3021 }, + { .compatible = "microchip,mcp3221", .data = (void *)mcp3221 }, + { } +}; +MODULE_DEVICE_TABLE(of, of_mcp3021_match); +#endif + static struct i2c_driver mcp3021_driver = { .driver = { .name = "mcp3021", + .of_match_table = of_match_ptr(of_mcp3021_match), }, .probe = mcp3021_probe, .remove = mcp3021_remove, diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 3ce33d244cc0..12b94b094c0d 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -259,13 +259,15 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, ret = 0; else if (ret) ret = DIV_ROUND_CLOSEST(1350000U, ret); + else + ret = 1350000U; abort: mutex_unlock(&data->access_lock); return ret; } static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low, - u8 reg_fan_high, unsigned int limit) + u8 reg_fan_high, unsigned long limit) { int err; @@ -326,8 +328,8 @@ static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index, int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; int err; + voltage = clamp_val(voltage, 0, 0x3ff * nct7802_vmul[nr]); voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]); - voltage = clamp_val(voltage, 0, 0x3ff); mutex_lock(&data->access_lock); err = regmap_write(data->regmap, @@ -402,7 +404,7 @@ static ssize_t store_temp(struct device *dev, struct device_attribute *attr, if (err < 0) return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); err = regmap_write(data->regmap, nr, val & 0xff); return err ? : count; diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 3baa4f4a8c5e..4ab5293c7bf0 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -499,15 +499,27 @@ static int adm1275_probe(struct i2c_client *client, pindex = 2; tindex = 3; - info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT; + info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + + /* Enable VOUT if not enabled (it is disabled by default) */ + if (!(config & ADM1278_VOUT_EN)) { + config |= ADM1278_VOUT_EN; + ret = i2c_smbus_write_byte_data(client, + ADM1275_PMON_CONFIG, + config); + if (ret < 0) { + dev_err(&client->dev, + "Failed to enable VOUT monitoring\n"); + return -ENODEV; + } + } + if (config & ADM1278_TEMP1_EN) info->func[0] |= PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; if (config & ADM1278_VIN_EN) info->func[0] |= PMBUS_HAVE_VIN; - if (config & ADM1278_VOUT_EN) - info->func[0] |= - PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; break; case adm1293: case adm1294: diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c index 559a3dcd64d8..094f948f99ff 100644 --- a/drivers/hwmon/scpi-hwmon.c +++ b/drivers/hwmon/scpi-hwmon.c @@ -251,6 +251,7 @@ static const struct of_device_id scpi_of_match[] = { {.compatible = "arm,scpi-sensors"}, {}, }; +MODULE_DEVICE_TABLE(of, scpi_of_match); static struct platform_driver scpi_hwmon_platdrv = { .driver = { diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index 6ac7cda72d4c..15650f247679 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -77,14 +77,15 @@ static inline unsigned int IN_FROM_REG(u8 reg, int n) static inline u8 IN_TO_REG(unsigned long val, int n) { - return clamp_val(SCALE(val, 192, nom_mv[n]), 0, 255); + val = clamp_val(val, 0, nom_mv[n] * 255 / 192); + return SCALE(val, 192, nom_mv[n]); } /* * TEMP: 0.001 degC units (-128C to +127C) * REG: 1C/bit, two's complement */ -static inline s8 TEMP_TO_REG(int val) +static inline s8 TEMP_TO_REG(long val) { return SCALE(clamp_val(val, -128000, 127000), 1, 1000); } diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c new file mode 100644 index 000000000000..18136e1f95fd --- /dev/null +++ b/drivers/hwmon/tc654.c @@ -0,0 +1,514 @@ +/* + * tc654.c - Linux kernel modules for fan speed controller + * + * Copyright (C) 2016 Allied Telesis Labs NZ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/util_macros.h> + +enum tc654_regs { + TC654_REG_RPM1 = 0x00, /* RPM Output 1 */ + TC654_REG_RPM2 = 0x01, /* RPM Output 2 */ + TC654_REG_FAN_FAULT1 = 0x02, /* Fan Fault 1 Threshold */ + TC654_REG_FAN_FAULT2 = 0x03, /* Fan Fault 2 Threshold */ + TC654_REG_CONFIG = 0x04, /* Configuration */ + TC654_REG_STATUS = 0x05, /* Status */ + TC654_REG_DUTY_CYCLE = 0x06, /* Fan Speed Duty Cycle */ + TC654_REG_MFR_ID = 0x07, /* Manufacturer Identification */ + TC654_REG_VER_ID = 0x08, /* Version Identification */ +}; + +/* Macros to easily index the registers */ +#define TC654_REG_RPM(idx) (TC654_REG_RPM1 + (idx)) +#define TC654_REG_FAN_FAULT(idx) (TC654_REG_FAN_FAULT1 + (idx)) + +/* Config register bits */ +#define TC654_REG_CONFIG_RES BIT(6) /* Resolution Selection */ +#define TC654_REG_CONFIG_DUTYC BIT(5) /* Duty Cycle Control */ +#define TC654_REG_CONFIG_SDM BIT(0) /* Shutdown Mode */ + +/* Status register bits */ +#define TC654_REG_STATUS_F2F BIT(1) /* Fan 2 Fault */ +#define TC654_REG_STATUS_F1F BIT(0) /* Fan 1 Fault */ + +/* RPM resolution for RPM Output registers */ +#define TC654_HIGH_RPM_RESOLUTION 25 /* 25 RPM resolution */ +#define TC654_LOW_RPM_RESOLUTION 50 /* 50 RPM resolution */ + +/* Convert to the fan fault RPM threshold from register value */ +#define TC654_FAN_FAULT_FROM_REG(val) ((val) * 50) /* 50 RPM resolution */ + +/* Convert to register value from the fan fault RPM threshold */ +#define TC654_FAN_FAULT_TO_REG(val) (((val) / 50) & 0xff) + +/* Register data is read (and cached) at most once per second. */ +#define TC654_UPDATE_INTERVAL HZ + +struct tc654_data { + struct i2c_client *client; + + /* update mutex */ + struct mutex update_lock; + + /* tc654 register cache */ + bool valid; + unsigned long last_updated; /* in jiffies */ + + u8 rpm_output[2]; /* The fan RPM data for fans 1 and 2 is then + * written to registers RPM1 and RPM2 + */ + u8 fan_fault[2]; /* The Fan Fault Threshold Registers are used to + * set the fan fault threshold levels for fan 1 + * and fan 2 + */ + u8 config; /* The Configuration Register is an 8-bit read/ + * writable multi-function control register + * 7: Fan Fault Clear + * 1 = Clear Fan Fault + * 0 = Normal Operation (default) + * 6: Resolution Selection for RPM Output Registers + * RPM Output Registers (RPM1 and RPM2) will be + * set for + * 1 = 25 RPM (9-bit) resolution + * 0 = 50 RPM (8-bit) resolution (default) + * 5: Duty Cycle Control Method + * The V OUT duty cycle will be controlled via + * 1 = the SMBus interface. + * 0 = via the V IN analog input pin. (default) + * 4,3: Fan 2 Pulses Per Rotation + * 00 = 1 + * 01 = 2 (default) + * 10 = 4 + * 11 = 8 + * 2,1: Fan 1 Pulses Per Rotation + * 00 = 1 + * 01 = 2 (default) + * 10 = 4 + * 11 = 8 + * 0: Shutdown Mode + * 1 = Shutdown mode. + * 0 = Normal operation. (default) + */ + u8 status; /* The Status register provides all the information + * about what is going on within the TC654/TC655 + * devices. + * 7,6: Unimplemented, Read as '0' + * 5: Over-Temperature Fault Condition + * 1 = Over-Temperature condition has occurred + * 0 = Normal operation. V IN is less than 2.6V + * 4: RPM2 Counter Overflow + * 1 = Fault condition + * 0 = Normal operation + * 3: RPM1 Counter Overflow + * 1 = Fault condition + * 0 = Normal operation + * 2: V IN Input Status + * 1 = V IN is open + * 0 = Normal operation. voltage present at V IN + * 1: Fan 2 Fault + * 1 = Fault condition + * 0 = Normal operation + * 0: Fan 1 Fault + * 1 = Fault condition + * 0 = Normal operation + */ + u8 duty_cycle; /* The DUTY_CYCLE register is a 4-bit read/ + * writable register used to control the duty + * cycle of the V OUT output. + */ +}; + +/* helper to grab and cache data, at most one time per second */ +static struct tc654_data *tc654_update_client(struct device *dev) +{ + struct tc654_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int ret = 0; + + mutex_lock(&data->update_lock); + if (time_before(jiffies, data->last_updated + TC654_UPDATE_INTERVAL) && + likely(data->valid)) + goto out; + + ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(0)); + if (ret < 0) + goto out; + data->rpm_output[0] = ret; + + ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(1)); + if (ret < 0) + goto out; + data->rpm_output[1] = ret; + + ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(0)); + if (ret < 0) + goto out; + data->fan_fault[0] = ret; + + ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(1)); + if (ret < 0) + goto out; + data->fan_fault[1] = ret; + + ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG); + if (ret < 0) + goto out; + data->config = ret; + + ret = i2c_smbus_read_byte_data(client, TC654_REG_STATUS); + if (ret < 0) + goto out; + data->status = ret; + + ret = i2c_smbus_read_byte_data(client, TC654_REG_DUTY_CYCLE); + if (ret < 0) + goto out; + data->duty_cycle = ret & 0x0f; + + data->last_updated = jiffies; + data->valid = true; +out: + mutex_unlock(&data->update_lock); + + if (ret < 0) /* upon error, encode it in return value */ + data = ERR_PTR(ret); + + return data; +} + +/* + * sysfs attributes + */ + +static ssize_t show_fan(struct device *dev, struct device_attribute *da, + char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct tc654_data *data = tc654_update_client(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (data->config & TC654_REG_CONFIG_RES) + val = data->rpm_output[nr] * TC654_HIGH_RPM_RESOLUTION; + else + val = data->rpm_output[nr] * TC654_LOW_RPM_RESOLUTION; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t show_fan_min(struct device *dev, struct device_attribute *da, + char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct tc654_data *data = tc654_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", + TC654_FAN_FAULT_FROM_REG(data->fan_fault[nr])); +} + +static ssize_t set_fan_min(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(da)->index; + struct tc654_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + val = clamp_val(val, 0, 12750); + + mutex_lock(&data->update_lock); + + data->fan_fault[nr] = TC654_FAN_FAULT_TO_REG(val); + ret = i2c_smbus_write_byte_data(client, TC654_REG_FAN_FAULT(nr), + data->fan_fault[nr]); + + mutex_unlock(&data->update_lock); + return ret < 0 ? ret : count; +} + +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da, + char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct tc654_data *data = tc654_update_client(dev); + int val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (nr == 0) + val = !!(data->status & TC654_REG_STATUS_F1F); + else + val = !!(data->status & TC654_REG_STATUS_F2F); + + return sprintf(buf, "%d\n", val); +} + +static const u8 TC654_FAN_PULSE_SHIFT[] = { 1, 3 }; + +static ssize_t show_fan_pulses(struct device *dev, struct device_attribute *da, + char *buf) +{ + int nr = to_sensor_dev_attr(da)->index; + struct tc654_data *data = tc654_update_client(dev); + u8 val; + + if (IS_ERR(data)) + return PTR_ERR(data); + + val = BIT((data->config >> TC654_FAN_PULSE_SHIFT[nr]) & 0x03); + return sprintf(buf, "%d\n", val); +} + +static ssize_t set_fan_pulses(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(da)->index; + struct tc654_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + u8 config; + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + switch (val) { + case 1: + config = 0; + break; + case 2: + config = 1; + break; + case 4: + config = 2; + break; + case 8: + config = 3; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->update_lock); + + data->config &= ~(0x03 << TC654_FAN_PULSE_SHIFT[nr]); + data->config |= (config << TC654_FAN_PULSE_SHIFT[nr]); + ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config); + + mutex_unlock(&data->update_lock); + return ret < 0 ? ret : count; +} + +static ssize_t show_pwm_mode(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct tc654_data *data = tc654_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", !!(data->config & TC654_REG_CONFIG_DUTYC)); +} + +static ssize_t set_pwm_mode(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct tc654_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + + if (val) + data->config |= TC654_REG_CONFIG_DUTYC; + else + data->config &= ~TC654_REG_CONFIG_DUTYC; + + ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config); + + mutex_unlock(&data->update_lock); + return ret < 0 ? ret : count; +} + +static const int tc654_pwm_map[16] = { 77, 88, 102, 112, 124, 136, 148, 160, + 172, 184, 196, 207, 219, 231, 243, 255}; + +static ssize_t show_pwm(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct tc654_data *data = tc654_update_client(dev); + int pwm; + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (data->config & TC654_REG_CONFIG_SDM) + pwm = 0; + else + pwm = tc654_pwm_map[data->duty_cycle]; + + return sprintf(buf, "%d\n", pwm); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct tc654_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + if (val > 255) + return -EINVAL; + + mutex_lock(&data->update_lock); + + if (val == 0) + data->config |= TC654_REG_CONFIG_SDM; + else + data->config &= ~TC654_REG_CONFIG_SDM; + + data->duty_cycle = find_closest(val, tc654_pwm_map, + ARRAY_SIZE(tc654_pwm_map)); + + ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config); + if (ret < 0) + goto out; + + ret = i2c_smbus_write_byte_data(client, TC654_REG_DUTY_CYCLE, + data->duty_cycle); + +out: + mutex_unlock(&data->update_lock); + return ret < 0 ? ret : count; +} + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 0); +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 1); +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, + set_fan_pulses, 0); +static SENSOR_DEVICE_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, + set_fan_pulses, 1); +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, + show_pwm_mode, set_pwm_mode, 0); +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, + set_pwm, 0); + +/* Driver data */ +static struct attribute *tc654_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan1_pulses.dev_attr.attr, + &sensor_dev_attr_fan2_pulses.dev_attr.attr, + &sensor_dev_attr_pwm1_mode.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + NULL +}; + +ATTRIBUTE_GROUPS(tc654); + +/* + * device probe and removal + */ + +static int tc654_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct tc654_data *data; + struct device *hwmon_dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct tc654_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->update_lock); + + ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG); + if (ret < 0) + return ret; + + data->config = ret; + + hwmon_dev = + devm_hwmon_device_register_with_groups(dev, client->name, data, + tc654_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id tc654_id[] = { + {"tc654", 0}, + {"tc655", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tc654_id); + +static struct i2c_driver tc654_driver = { + .driver = { + .name = "tc654", + }, + .probe = tc654_probe, + .id_table = tc654_id, +}; + +module_i2c_driver(tc654_driver); + +MODULE_AUTHOR("Allied Telesis Labs"); +MODULE_DESCRIPTION("Microchip TC654/TC655 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c new file mode 100644 index 000000000000..91bb94639286 --- /dev/null +++ b/drivers/hwmon/tmp108.c @@ -0,0 +1,469 @@ +/* Texas Instruments TMP108 SMBus temperature sensor driver + * + * Copyright (C) 2016 John Muir <john@jmuir.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define DRIVER_NAME "tmp108" + +#define TMP108_REG_TEMP 0x00 +#define TMP108_REG_CONF 0x01 +#define TMP108_REG_TLOW 0x02 +#define TMP108_REG_THIGH 0x03 + +#define TMP108_TEMP_MIN_MC -50000 /* Minimum millicelcius. */ +#define TMP108_TEMP_MAX_MC 127937 /* Maximum millicelcius. */ + +/* Configuration register bits. + * Note: these bit definitions are byte swapped. + */ +#define TMP108_CONF_M0 0x0100 /* Sensor mode. */ +#define TMP108_CONF_M1 0x0200 +#define TMP108_CONF_TM 0x0400 /* Thermostat mode. */ +#define TMP108_CONF_FL 0x0800 /* Watchdog flag - TLOW */ +#define TMP108_CONF_FH 0x1000 /* Watchdog flag - THIGH */ +#define TMP108_CONF_CR0 0x2000 /* Conversion rate. */ +#define TMP108_CONF_CR1 0x4000 +#define TMP108_CONF_ID 0x8000 +#define TMP108_CONF_HYS0 0x0010 /* Hysteresis. */ +#define TMP108_CONF_HYS1 0x0020 +#define TMP108_CONF_POL 0x0080 /* Polarity of alert. */ + +/* Defaults set by the hardware upon reset. */ +#define TMP108_CONF_DEFAULTS (TMP108_CONF_CR0 | TMP108_CONF_TM |\ + TMP108_CONF_HYS0 | TMP108_CONF_M1) +/* These bits are read-only. */ +#define TMP108_CONF_READ_ONLY (TMP108_CONF_FL | TMP108_CONF_FH |\ + TMP108_CONF_ID) + +#define TMP108_CONF_MODE_MASK (TMP108_CONF_M0|TMP108_CONF_M1) +#define TMP108_MODE_SHUTDOWN 0x0000 +#define TMP108_MODE_ONE_SHOT TMP108_CONF_M0 +#define TMP108_MODE_CONTINUOUS TMP108_CONF_M1 /* Default */ + /* When M1 is set, M0 is ignored. */ + +#define TMP108_CONF_CONVRATE_MASK (TMP108_CONF_CR0|TMP108_CONF_CR1) +#define TMP108_CONVRATE_0P25HZ 0x0000 +#define TMP108_CONVRATE_1HZ TMP108_CONF_CR0 /* Default */ +#define TMP108_CONVRATE_4HZ TMP108_CONF_CR1 +#define TMP108_CONVRATE_16HZ (TMP108_CONF_CR0|TMP108_CONF_CR1) + +#define TMP108_CONF_HYSTERESIS_MASK (TMP108_CONF_HYS0|TMP108_CONF_HYS1) +#define TMP108_HYSTERESIS_0C 0x0000 +#define TMP108_HYSTERESIS_1C TMP108_CONF_HYS0 /* Default */ +#define TMP108_HYSTERESIS_2C TMP108_CONF_HYS1 +#define TMP108_HYSTERESIS_4C (TMP108_CONF_HYS0|TMP108_CONF_HYS1) + +#define TMP108_CONVERSION_TIME_MS 30 /* in milli-seconds */ + +struct tmp108 { + struct regmap *regmap; + u16 orig_config; + unsigned long ready_time; +}; + +/* convert 12-bit TMP108 register value to milliCelsius */ +static inline int tmp108_temp_reg_to_mC(s16 val) +{ + return (val & ~0x0f) * 1000 / 256; +} + +/* convert milliCelsius to left adjusted 12-bit TMP108 register value */ +static inline u16 tmp108_mC_to_temp_reg(int val) +{ + return (val * 256) / 1000; +} + +static int tmp108_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + struct tmp108 *tmp108 = dev_get_drvdata(dev); + unsigned int regval; + int err, hyst; + + if (type == hwmon_chip) { + if (attr == hwmon_chip_update_interval) { + err = regmap_read(tmp108->regmap, TMP108_REG_CONF, + ®val); + if (err < 0) + return err; + switch (regval & TMP108_CONF_CONVRATE_MASK) { + case TMP108_CONVRATE_0P25HZ: + default: + *temp = 4000; + break; + case TMP108_CONVRATE_1HZ: + *temp = 1000; + break; + case TMP108_CONVRATE_4HZ: + *temp = 250; + break; + case TMP108_CONVRATE_16HZ: + *temp = 63; + break; + } + return 0; + } + return -EOPNOTSUPP; + } + + switch (attr) { + case hwmon_temp_input: + /* Is it too early to return a conversion ? */ + if (time_before(jiffies, tmp108->ready_time)) { + dev_dbg(dev, "%s: Conversion not ready yet..\n", + __func__); + return -EAGAIN; + } + err = regmap_read(tmp108->regmap, TMP108_REG_TEMP, ®val); + if (err < 0) + return err; + *temp = tmp108_temp_reg_to_mC(regval); + break; + case hwmon_temp_min: + case hwmon_temp_max: + err = regmap_read(tmp108->regmap, attr == hwmon_temp_min ? + TMP108_REG_TLOW : TMP108_REG_THIGH, ®val); + if (err < 0) + return err; + *temp = tmp108_temp_reg_to_mC(regval); + break; + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + err = regmap_read(tmp108->regmap, TMP108_REG_CONF, ®val); + if (err < 0) + return err; + *temp = !!(regval & (attr == hwmon_temp_min_alarm ? + TMP108_CONF_FL : TMP108_CONF_FH)); + break; + case hwmon_temp_min_hyst: + case hwmon_temp_max_hyst: + err = regmap_read(tmp108->regmap, TMP108_REG_CONF, ®val); + if (err < 0) + return err; + switch (regval & TMP108_CONF_HYSTERESIS_MASK) { + case TMP108_HYSTERESIS_0C: + default: + hyst = 0; + break; + case TMP108_HYSTERESIS_1C: + hyst = 1000; + break; + case TMP108_HYSTERESIS_2C: + hyst = 2000; + break; + case TMP108_HYSTERESIS_4C: + hyst = 4000; + break; + } + err = regmap_read(tmp108->regmap, attr == hwmon_temp_min_hyst ? + TMP108_REG_TLOW : TMP108_REG_THIGH, ®val); + if (err < 0) + return err; + *temp = tmp108_temp_reg_to_mC(regval); + if (attr == hwmon_temp_min_hyst) + *temp += hyst; + else + *temp -= hyst; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int tmp108_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long temp) +{ + struct tmp108 *tmp108 = dev_get_drvdata(dev); + u32 regval, mask; + int err; + + if (type == hwmon_chip) { + if (attr == hwmon_chip_update_interval) { + if (temp < 156) + mask = TMP108_CONVRATE_16HZ; + else if (temp < 625) + mask = TMP108_CONVRATE_4HZ; + else if (temp < 2500) + mask = TMP108_CONVRATE_1HZ; + else + mask = TMP108_CONVRATE_0P25HZ; + return regmap_update_bits(tmp108->regmap, + TMP108_REG_CONF, + TMP108_CONF_CONVRATE_MASK, + mask); + } + return -EOPNOTSUPP; + } + + switch (attr) { + case hwmon_temp_min: + case hwmon_temp_max: + temp = clamp_val(temp, TMP108_TEMP_MIN_MC, TMP108_TEMP_MAX_MC); + return regmap_write(tmp108->regmap, + attr == hwmon_temp_min ? + TMP108_REG_TLOW : TMP108_REG_THIGH, + tmp108_mC_to_temp_reg(temp)); + case hwmon_temp_min_hyst: + case hwmon_temp_max_hyst: + temp = clamp_val(temp, TMP108_TEMP_MIN_MC, TMP108_TEMP_MAX_MC); + err = regmap_read(tmp108->regmap, + attr == hwmon_temp_min_hyst ? + TMP108_REG_TLOW : TMP108_REG_THIGH, + ®val); + if (err < 0) + return err; + if (attr == hwmon_temp_min_hyst) + temp -= tmp108_temp_reg_to_mC(regval); + else + temp = tmp108_temp_reg_to_mC(regval) - temp; + if (temp < 500) + mask = TMP108_HYSTERESIS_0C; + else if (temp < 1500) + mask = TMP108_HYSTERESIS_1C; + else if (temp < 3000) + mask = TMP108_HYSTERESIS_2C; + else + mask = TMP108_HYSTERESIS_4C; + return regmap_update_bits(tmp108->regmap, TMP108_REG_CONF, + TMP108_CONF_HYSTERESIS_MASK, mask); + default: + return -EOPNOTSUPP; + } +} + +static umode_t tmp108_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type == hwmon_chip && attr == hwmon_chip_update_interval) + return 0644; + + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + return 0444; + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_min_hyst: + case hwmon_temp_max_hyst: + return 0644; + default: + return 0; + } +} + +static u32 tmp108_chip_config[] = { + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL, + 0 +}; + +static const struct hwmon_channel_info tmp108_chip = { + .type = hwmon_chip, + .config = tmp108_chip_config, +}; + +static u32 tmp108_temp_config[] = { + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_MIN_HYST + | HWMON_T_MAX_HYST | HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM, + 0 +}; + +static const struct hwmon_channel_info tmp108_temp = { + .type = hwmon_temp, + .config = tmp108_temp_config, +}; + +static const struct hwmon_channel_info *tmp108_info[] = { + &tmp108_chip, + &tmp108_temp, + NULL +}; + +static const struct hwmon_ops tmp108_hwmon_ops = { + .is_visible = tmp108_is_visible, + .read = tmp108_read, + .write = tmp108_write, +}; + +static const struct hwmon_chip_info tmp108_chip_info = { + .ops = &tmp108_hwmon_ops, + .info = tmp108_info, +}; + +static void tmp108_restore_config(void *data) +{ + struct tmp108 *tmp108 = data; + + regmap_write(tmp108->regmap, TMP108_REG_CONF, tmp108->orig_config); +} + +static bool tmp108_is_writeable_reg(struct device *dev, unsigned int reg) +{ + return reg != TMP108_REG_TEMP; +} + +static bool tmp108_is_volatile_reg(struct device *dev, unsigned int reg) +{ + /* Configuration register must be volatile to enable FL and FH. */ + return reg == TMP108_REG_TEMP || reg == TMP108_REG_CONF; +} + +static const struct regmap_config tmp108_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = TMP108_REG_THIGH, + .writeable_reg = tmp108_is_writeable_reg, + .volatile_reg = tmp108_is_volatile_reg, + .val_format_endian = REGMAP_ENDIAN_BIG, + .cache_type = REGCACHE_RBTREE, + .use_single_rw = true, +}; + +static int tmp108_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct tmp108 *tmp108; + int err; + u32 config; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(dev, + "adapter doesn't support SMBus word transactions\n"); + return -ENODEV; + } + + tmp108 = devm_kzalloc(dev, sizeof(*tmp108), GFP_KERNEL); + if (!tmp108) + return -ENOMEM; + + dev_set_drvdata(dev, tmp108); + + tmp108->regmap = devm_regmap_init_i2c(client, &tmp108_regmap_config); + if (IS_ERR(tmp108->regmap)) { + err = PTR_ERR(tmp108->regmap); + dev_err(dev, "regmap init failed: %d", err); + return err; + } + + err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &config); + if (err < 0) { + dev_err(dev, "error reading config register: %d", err); + return err; + } + tmp108->orig_config = config; + + /* Only continuous mode is supported. */ + config &= ~TMP108_CONF_MODE_MASK; + config |= TMP108_MODE_CONTINUOUS; + + /* Only comparator mode is supported. */ + config &= ~TMP108_CONF_TM; + + err = regmap_write(tmp108->regmap, TMP108_REG_CONF, config); + if (err < 0) { + dev_err(dev, "error writing config register: %d", err); + return err; + } + + tmp108->ready_time = jiffies; + if ((tmp108->orig_config & TMP108_CONF_MODE_MASK) == + TMP108_MODE_SHUTDOWN) + tmp108->ready_time += + msecs_to_jiffies(TMP108_CONVERSION_TIME_MS); + + err = devm_add_action_or_reset(dev, tmp108_restore_config, tmp108); + if (err) { + dev_err(dev, "add action or reset failed: %d", err); + return err; + } + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + tmp108, + &tmp108_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static int __maybe_unused tmp108_suspend(struct device *dev) +{ + struct tmp108 *tmp108 = dev_get_drvdata(dev); + + return regmap_update_bits(tmp108->regmap, TMP108_REG_CONF, + TMP108_CONF_MODE_MASK, TMP108_MODE_SHUTDOWN); +} + +static int __maybe_unused tmp108_resume(struct device *dev) +{ + struct tmp108 *tmp108 = dev_get_drvdata(dev); + int err; + + err = regmap_update_bits(tmp108->regmap, TMP108_REG_CONF, + TMP108_CONF_MODE_MASK, TMP108_MODE_CONTINUOUS); + tmp108->ready_time = jiffies + + msecs_to_jiffies(TMP108_CONVERSION_TIME_MS); + return err; +} + +static SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); + +static const struct i2c_device_id tmp108_i2c_ids[] = { + { "tmp108", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id tmp108_of_ids[] = { + { .compatible = "ti,tmp108", }, + {} +}; +MODULE_DEVICE_TABLE(of, tmp108_of_ids); +#endif + +static struct i2c_driver tmp108_driver = { + .driver = { + .name = DRIVER_NAME, + .pm = &tmp108_dev_pm_ops, + .of_match_table = of_match_ptr(tmp108_of_ids), + }, + .probe = tmp108_probe, + .id_table = tmp108_i2c_ids, +}; + +module_i2c_driver(tmp108_driver); + +MODULE_AUTHOR("John Muir <john@jmuir.com>"); +MODULE_DESCRIPTION("Texas Instruments TMP108 temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index ac91c07e3f90..d1f209a5feac 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -220,7 +220,7 @@ struct pdev_entry { static LIST_HEAD(pdev_list); static DEFINE_MUTEX(pdev_list_mutex); -static int via_cputemp_device_add(unsigned int cpu) +static int via_cputemp_online(unsigned int cpu) { int err; struct platform_device *pdev; @@ -261,7 +261,7 @@ exit: return err; } -static void via_cputemp_device_remove(unsigned int cpu) +static int via_cputemp_down_prep(unsigned int cpu) { struct pdev_entry *p; @@ -272,33 +272,13 @@ static void via_cputemp_device_remove(unsigned int cpu) list_del(&p->list); mutex_unlock(&pdev_list_mutex); kfree(p); - return; + return 0; } } mutex_unlock(&pdev_list_mutex); + return 0; } -static int via_cputemp_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - unsigned int cpu = (unsigned long) hcpu; - - switch (action) { - case CPU_ONLINE: - case CPU_DOWN_FAILED: - via_cputemp_device_add(cpu); - break; - case CPU_DOWN_PREPARE: - via_cputemp_device_remove(cpu); - break; - } - return NOTIFY_OK; -} - -static struct notifier_block via_cputemp_cpu_notifier __refdata = { - .notifier_call = via_cputemp_cpu_callback, -}; - static const struct x86_cpu_id __initconst cputemp_ids[] = { { X86_VENDOR_CENTAUR, 6, 0xa, }, /* C7 A */ { X86_VENDOR_CENTAUR, 6, 0xd, }, /* C7 D */ @@ -307,9 +287,11 @@ static const struct x86_cpu_id __initconst cputemp_ids[] = { }; MODULE_DEVICE_TABLE(x86cpu, cputemp_ids); +static enum cpuhp_state via_temp_online; + static int __init via_cputemp_init(void) { - int i, err; + int err; if (!x86_match_cpu(cputemp_ids)) return -ENODEV; @@ -318,58 +300,33 @@ static int __init via_cputemp_init(void) if (err) goto exit; - cpu_notifier_register_begin(); - for_each_online_cpu(i) { - struct cpuinfo_x86 *c = &cpu_data(i); - - if (c->x86 != 6) - continue; - - if (c->x86_model < 0x0a) - continue; - - if (c->x86_model > 0x0f) { - pr_warn("Unknown CPU model 0x%x\n", c->x86_model); - continue; - } - - via_cputemp_device_add(i); - } + err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/via:online", + via_cputemp_online, via_cputemp_down_prep); + if (err < 0) + goto exit_driver_unreg; + via_temp_online = err; #ifndef CONFIG_HOTPLUG_CPU if (list_empty(&pdev_list)) { - cpu_notifier_register_done(); err = -ENODEV; - goto exit_driver_unreg; + goto exit_hp_unreg; } #endif - - __register_hotcpu_notifier(&via_cputemp_cpu_notifier); - cpu_notifier_register_done(); return 0; #ifndef CONFIG_HOTPLUG_CPU +exit_hp_unreg: + cpuhp_remove_state_nocalls(via_temp_online); +#endif exit_driver_unreg: platform_driver_unregister(&via_cputemp_driver); -#endif exit: return err; } static void __exit via_cputemp_exit(void) { - struct pdev_entry *p, *n; - - cpu_notifier_register_begin(); - __unregister_hotcpu_notifier(&via_cputemp_cpu_notifier); - mutex_lock(&pdev_list_mutex); - list_for_each_entry_safe(p, n, &pdev_list, list) { - platform_device_unregister(p->pdev); - list_del(&p->list); - kfree(p); - } - mutex_unlock(&pdev_list_mutex); - cpu_notifier_register_done(); + cpuhp_remove_state(via_temp_online); platform_driver_unregister(&via_cputemp_driver); } |