diff options
Diffstat (limited to 'drivers/hwmon/pc87360.c')
-rw-r--r-- | drivers/hwmon/pc87360.c | 1461 |
1 files changed, 739 insertions, 722 deletions
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 0828436a1f6c..a4adc8bd531f 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -35,6 +35,18 @@ #include <linux/acpi.h> #include <linux/io.h> +#define DRIVER_NAME "pc87360" + +/* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */ +#define CHAN_CNVRTD 0x80 /* new data ready */ +#define CHAN_ENA 0x01 /* enabled channel (temp or vin) */ +#define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ +#define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ + +#define TEMP_OTS_OE 0x20 /* OTS Output Enable */ +#define VIN_RW1C_MASK (CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN) /* 0x87 */ +#define TEMP_RW1C_MASK (VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */ + static u8 devid; static struct platform_device *pdev; static unsigned short extra_isa[3]; @@ -211,183 +223,181 @@ struct pc87360_data { }; /* - * Functions declaration + * ldi is the logical device index + * bank is for voltages and temperatures only */ - -static int pc87360_probe(struct platform_device *pdev); -static int pc87360_remove(struct platform_device *pdev); - static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg); -static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg, u8 value); -static void pc87360_init_device(struct platform_device *pdev, - int use_thermistors); -static struct pc87360_data *pc87360_update_device(struct device *dev); - -/* - * Driver data - */ - -static struct platform_driver pc87360_driver = { - .driver = { - .name = "pc87360", - }, - .probe = pc87360_probe, - .remove = pc87360_remove, -}; + u8 reg) +{ + int res; -/* - * Sysfs stuff - */ + mutex_lock(&(data->lock)); + if (bank != NO_BANK) + outb_p(bank, data->address[ldi] + PC87365_REG_BANK); + res = inb_p(data->address[ldi] + reg); + mutex_unlock(&(data->lock)); -static ssize_t fan_input_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], - FAN_DIV_FROM_REG(data->fan_status[attr->index]))); -} -static ssize_t fan_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], - FAN_DIV_FROM_REG(data->fan_status[attr->index]))); -} -static ssize_t fan_div_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", - FAN_DIV_FROM_REG(data->fan_status[attr->index])); + return res; } -static ssize_t fan_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) + +static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, + u8 reg, u8 value) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", - FAN_STATUS_FROM_REG(data->fan_status[attr->index])); + mutex_lock(&(data->lock)); + if (bank != NO_BANK) + outb_p(bank, data->address[ldi] + PC87365_REG_BANK); + outb_p(value, data->address[ldi] + reg); + mutex_unlock(&(data->lock)); } -static ssize_t fan_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) + +static void pc87360_autodiv(struct device *dev, int nr) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long fan_min; - int err; - - err = kstrtol(buf, 10, &fan_min); - if (err) - return err; - - mutex_lock(&data->update_lock); - fan_min = FAN_TO_REG(fan_min, - FAN_DIV_FROM_REG(data->fan_status[attr->index])); + u8 old_min = data->fan_min[nr]; - /* If it wouldn't fit, change clock divisor */ - while (fan_min > 255 - && (data->fan_status[attr->index] & 0x60) != 0x60) { - fan_min >>= 1; - data->fan[attr->index] >>= 1; - data->fan_status[attr->index] += 0x20; + /* Increase clock divider if needed and possible */ + if ((data->fan_status[nr] & 0x04) /* overflow flag */ + || (data->fan[nr] >= 224)) { /* next to overflow */ + if ((data->fan_status[nr] & 0x60) != 0x60) { + data->fan_status[nr] += 0x20; + data->fan_min[nr] >>= 1; + data->fan[nr] >>= 1; + dev_dbg(dev, + "Increasing clock divider to %d for fan %d\n", + FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); + } + } else { + /* Decrease clock divider if possible */ + while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */ + && data->fan[nr] < 85 /* bad accuracy */ + && (data->fan_status[nr] & 0x60) != 0x00) { + data->fan_status[nr] -= 0x20; + data->fan_min[nr] <<= 1; + data->fan[nr] <<= 1; + dev_dbg(dev, + "Decreasing clock divider to %d for fan %d\n", + FAN_DIV_FROM_REG(data->fan_status[nr]), + nr + 1); + } } - data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(attr->index), - data->fan_min[attr->index]); - - /* Write new divider, preserve alarm bits */ - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_STATUS(attr->index), - data->fan_status[attr->index] & 0xF9); - mutex_unlock(&data->update_lock); - - return count; -} -static struct sensor_device_attribute fan_input[] = { - SENSOR_ATTR_RO(fan1_input, fan_input, 0), - SENSOR_ATTR_RO(fan2_input, fan_input, 1), - SENSOR_ATTR_RO(fan3_input, fan_input, 2), -}; -static struct sensor_device_attribute fan_status[] = { - SENSOR_ATTR_RO(fan1_status, fan_status, 0), - SENSOR_ATTR_RO(fan2_status, fan_status, 1), - SENSOR_ATTR_RO(fan3_status, fan_status, 2), -}; -static struct sensor_device_attribute fan_div[] = { - SENSOR_ATTR_RO(fan1_div, fan_div, 0), - SENSOR_ATTR_RO(fan2_div, fan_div, 1), - SENSOR_ATTR_RO(fan3_div, fan_div, 2), -}; -static struct sensor_device_attribute fan_min[] = { - SENSOR_ATTR_RW(fan1_min, fan_min, 0), - SENSOR_ATTR_RW(fan2_min, fan_min, 1), - SENSOR_ATTR_RW(fan3_min, fan_min, 2), -}; - -#define FAN_UNIT_ATTRS(X) \ -{ &fan_input[X].dev_attr.attr, \ - &fan_status[X].dev_attr.attr, \ - &fan_div[X].dev_attr.attr, \ - &fan_min[X].dev_attr.attr, \ - NULL \ + /* Write new fan min if it changed */ + if (old_min != data->fan_min[nr]) { + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(nr), + data->fan_min[nr]); + } } -static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", - PWM_FROM_REG(data->pwm[attr->index], - FAN_CONFIG_INVERT(data->fan_conf, - attr->index))); -} -static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static struct pc87360_data *pc87360_update_device(struct device *dev) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return err; + u8 i; mutex_lock(&data->update_lock); - data->pwm[attr->index] = PWM_TO_REG(val, - FAN_CONFIG_INVERT(data->fan_conf, attr->index)); - pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index), - data->pwm[attr->index]); - mutex_unlock(&data->update_lock); - return count; -} -static struct sensor_device_attribute pwm[] = { - SENSOR_ATTR_RW(pwm1, pwm, 0), - SENSOR_ATTR_RW(pwm2, pwm, 1), - SENSOR_ATTR_RW(pwm3, pwm, 2), -}; + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + dev_dbg(dev, "Data update\n"); -static struct attribute *pc8736x_fan_attr[][5] = { - FAN_UNIT_ATTRS(0), - FAN_UNIT_ATTRS(1), - FAN_UNIT_ATTRS(2) -}; + /* Fans */ + for (i = 0; i < data->fannr; i++) { + if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { + data->fan_status[i] = + pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_FAN_STATUS(i)); + data->fan[i] = pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_FAN(i)); + data->fan_min[i] = pc87360_read_value(data, + LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(i)); + /* Change clock divider if needed */ + pc87360_autodiv(dev, i); + /* Clear bits and write new divider */ + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(i), + data->fan_status[i]); + } + if (FAN_CONFIG_CONTROL(data->fan_conf, i)) + data->pwm[i] = pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_PWM(i)); + } -static const struct attribute_group pc8736x_fan_attr_group[] = { - { .attrs = pc8736x_fan_attr[0], }, - { .attrs = pc8736x_fan_attr[1], }, - { .attrs = pc8736x_fan_attr[2], }, -}; + /* Voltages */ + for (i = 0; i < data->innr; i++) { + data->in_status[i] = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + /* Clear bits */ + pc87360_write_value(data, LD_IN, i, + PC87365_REG_IN_STATUS, + data->in_status[i]); + if ((data->in_status[i] & CHAN_READY) == CHAN_READY) { + data->in[i] = pc87360_read_value(data, LD_IN, + i, PC87365_REG_IN); + } + if (data->in_status[i] & CHAN_ENA) { + data->in_min[i] = pc87360_read_value(data, + LD_IN, i, + PC87365_REG_IN_MIN); + data->in_max[i] = pc87360_read_value(data, + LD_IN, i, + PC87365_REG_IN_MAX); + if (i >= 11) + data->in_crit[i-11] = + pc87360_read_value(data, LD_IN, + i, PC87365_REG_TEMP_CRIT); + } + } + if (data->innr) { + data->in_alarms = pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_IN_ALARMS1) + | ((pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_IN_ALARMS2) + & 0x07) << 8); + data->vid = (data->vid_conf & 0xE0) ? + pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_VID) : 0x1F; + } + + /* Temperatures */ + for (i = 0; i < data->tempnr; i++) { + data->temp_status[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_STATUS); + /* Clear bits */ + pc87360_write_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS, + data->temp_status[i]); + if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) { + data->temp[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP); + } + if (data->temp_status[i] & CHAN_ENA) { + data->temp_min[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_MIN); + data->temp_max[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_MAX); + data->temp_crit[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_CRIT); + } + } + if (data->tempnr) { + data->temp_alarms = pc87360_read_value(data, LD_TEMP, + NO_BANK, PC87365_REG_TEMP_ALARMS) + & 0x3F; + } + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} static ssize_t in_input_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -397,29 +407,52 @@ static ssize_t in_input_show(struct device *dev, return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } -static ssize_t in_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) + +static struct sensor_device_attribute in_input[] = { + SENSOR_ATTR_RO(in0_input, in_input, 0), + SENSOR_ATTR_RO(in1_input, in_input, 1), + SENSOR_ATTR_RO(in2_input, in_input, 2), + SENSOR_ATTR_RO(in3_input, in_input, 3), + SENSOR_ATTR_RO(in4_input, in_input, 4), + SENSOR_ATTR_RO(in5_input, in_input, 5), + SENSOR_ATTR_RO(in6_input, in_input, 6), + SENSOR_ATTR_RO(in7_input, in_input, 7), + SENSOR_ATTR_RO(in8_input, in_input, 8), + SENSOR_ATTR_RO(in9_input, in_input, 9), + SENSOR_ATTR_RO(in10_input, in_input, 10), +}; + +static ssize_t in_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], - data->in_vref)); + return sprintf(buf, "%u\n", data->in_status[attr->index]); } -static ssize_t in_max_show(struct device *dev, + +static struct sensor_device_attribute in_status[] = { + SENSOR_ATTR_RO(in0_status, in_status, 0), + SENSOR_ATTR_RO(in1_status, in_status, 1), + SENSOR_ATTR_RO(in2_status, in_status, 2), + SENSOR_ATTR_RO(in3_status, in_status, 3), + SENSOR_ATTR_RO(in4_status, in_status, 4), + SENSOR_ATTR_RO(in5_status, in_status, 5), + SENSOR_ATTR_RO(in6_status, in_status, 6), + SENSOR_ATTR_RO(in7_status, in_status, 7), + SENSOR_ATTR_RO(in8_status, in_status, 8), + SENSOR_ATTR_RO(in9_status, in_status, 9), + SENSOR_ATTR_RO(in10_status, in_status, 10), +}; + +static ssize_t in_min_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], data->in_vref)); } -static ssize_t in_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->in_status[attr->index]); -} + static ssize_t in_min_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -440,6 +473,30 @@ static ssize_t in_min_store(struct device *dev, mutex_unlock(&data->update_lock); return count; } + +static struct sensor_device_attribute in_min[] = { + SENSOR_ATTR_RW(in0_min, in_min, 0), + SENSOR_ATTR_RW(in1_min, in_min, 1), + SENSOR_ATTR_RW(in2_min, in_min, 2), + SENSOR_ATTR_RW(in3_min, in_min, 3), + SENSOR_ATTR_RW(in4_min, in_min, 4), + SENSOR_ATTR_RW(in5_min, in_min, 5), + SENSOR_ATTR_RW(in6_min, in_min, 6), + SENSOR_ATTR_RW(in7_min, in_min, 7), + SENSOR_ATTR_RW(in8_min, in_min, 8), + SENSOR_ATTR_RW(in9_min, in_min, 9), + SENSOR_ATTR_RW(in10_min, in_min, 10), +}; + +static ssize_t in_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], + data->in_vref)); +} + static ssize_t in_max_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -462,45 +519,6 @@ static ssize_t in_max_store(struct device *dev, return count; } -static struct sensor_device_attribute in_input[] = { - SENSOR_ATTR_RO(in0_input, in_input, 0), - SENSOR_ATTR_RO(in1_input, in_input, 1), - SENSOR_ATTR_RO(in2_input, in_input, 2), - SENSOR_ATTR_RO(in3_input, in_input, 3), - SENSOR_ATTR_RO(in4_input, in_input, 4), - SENSOR_ATTR_RO(in5_input, in_input, 5), - SENSOR_ATTR_RO(in6_input, in_input, 6), - SENSOR_ATTR_RO(in7_input, in_input, 7), - SENSOR_ATTR_RO(in8_input, in_input, 8), - SENSOR_ATTR_RO(in9_input, in_input, 9), - SENSOR_ATTR_RO(in10_input, in_input, 10), -}; -static struct sensor_device_attribute in_status[] = { - SENSOR_ATTR_RO(in0_status, in_status, 0), - SENSOR_ATTR_RO(in1_status, in_status, 1), - SENSOR_ATTR_RO(in2_status, in_status, 2), - SENSOR_ATTR_RO(in3_status, in_status, 3), - SENSOR_ATTR_RO(in4_status, in_status, 4), - SENSOR_ATTR_RO(in5_status, in_status, 5), - SENSOR_ATTR_RO(in6_status, in_status, 6), - SENSOR_ATTR_RO(in7_status, in_status, 7), - SENSOR_ATTR_RO(in8_status, in_status, 8), - SENSOR_ATTR_RO(in9_status, in_status, 9), - SENSOR_ATTR_RO(in10_status, in_status, 10), -}; -static struct sensor_device_attribute in_min[] = { - SENSOR_ATTR_RW(in0_min, in_min, 0), - SENSOR_ATTR_RW(in1_min, in_min, 1), - SENSOR_ATTR_RW(in2_min, in_min, 2), - SENSOR_ATTR_RW(in3_min, in_min, 3), - SENSOR_ATTR_RW(in4_min, in_min, 4), - SENSOR_ATTR_RW(in5_min, in_min, 5), - SENSOR_ATTR_RW(in6_min, in_min, 6), - SENSOR_ATTR_RW(in7_min, in_min, 7), - SENSOR_ATTR_RW(in8_min, in_min, 8), - SENSOR_ATTR_RW(in9_min, in_min, 9), - SENSOR_ATTR_RW(in10_min, in_min, 10), -}; static struct sensor_device_attribute in_max[] = { SENSOR_ATTR_RW(in0_max, in_max, 0), SENSOR_ATTR_RW(in1_max, in_max, 1), @@ -534,14 +552,6 @@ static ssize_t in_min_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); } -static ssize_t in_max_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - unsigned nr = to_sensor_dev_attr(devattr)->index; - - return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); -} static struct sensor_device_attribute in_min_alarm[] = { SENSOR_ATTR_RO(in0_min_alarm, in_min_alarm, 0), @@ -556,6 +566,16 @@ static struct sensor_device_attribute in_min_alarm[] = { SENSOR_ATTR_RO(in9_min_alarm, in_min_alarm, 9), SENSOR_ATTR_RO(in10_min_alarm, in_min_alarm, 10), }; + +static ssize_t in_max_alarm_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); +} + static struct sensor_device_attribute in_max_alarm[] = { SENSOR_ATTR_RO(in0_max_alarm, in_max_alarm, 0), SENSOR_ATTR_RO(in1_max_alarm, in_max_alarm, 1), @@ -592,6 +612,7 @@ static ssize_t vrm_show(struct device *dev, struct device_attribute *attr, struct pc87360_data *data = dev_get_drvdata(dev); return sprintf(buf, "%u\n", data->vrm); } + static ssize_t vrm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -648,37 +669,39 @@ static ssize_t therm_input_show(struct device *dev, return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } -static ssize_t therm_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) + +/* + * the +11 term below reflects the fact that VLM units 11,12,13 are + * used in the chip to measure voltage across the thermistors + */ +static struct sensor_device_attribute therm_input[] = { + SENSOR_ATTR_RO(temp4_input, therm_input, 0 + 11), + SENSOR_ATTR_RO(temp5_input, therm_input, 1 + 11), + SENSOR_ATTR_RO(temp6_input, therm_input, 2 + 11), +}; + +static ssize_t therm_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], - data->in_vref)); + return sprintf(buf, "%u\n", data->in_status[attr->index]); } -static ssize_t therm_max_show(struct device *dev, + +static struct sensor_device_attribute therm_status[] = { + SENSOR_ATTR_RO(temp4_status, therm_status, 0 + 11), + SENSOR_ATTR_RO(temp5_status, therm_status, 1 + 11), + SENSOR_ATTR_RO(temp6_status, therm_status, 2 + 11), +}; + +static ssize_t therm_min_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], - data->in_vref)); -} -static ssize_t therm_crit_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11], + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], data->in_vref)); } -static ssize_t therm_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->in_status[attr->index]); -} static ssize_t therm_min_store(struct device *dev, struct device_attribute *devattr, @@ -701,6 +724,21 @@ static ssize_t therm_min_store(struct device *dev, return count; } +static struct sensor_device_attribute therm_min[] = { + SENSOR_ATTR_RW(temp4_min, therm_min, 0 + 11), + SENSOR_ATTR_RW(temp5_min, therm_min, 1 + 11), + SENSOR_ATTR_RW(temp6_min, therm_min, 2 + 11), +}; + +static ssize_t therm_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], + data->in_vref)); +} + static ssize_t therm_max_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -721,6 +759,22 @@ static ssize_t therm_max_store(struct device *dev, mutex_unlock(&data->update_lock); return count; } + +static struct sensor_device_attribute therm_max[] = { + SENSOR_ATTR_RW(temp4_max, therm_max, 0 + 11), + SENSOR_ATTR_RW(temp5_max, therm_max, 1 + 11), + SENSOR_ATTR_RW(temp6_max, therm_max, 2 + 11), +}; + +static ssize_t therm_crit_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11], + data->in_vref)); +} + static ssize_t therm_crit_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -742,30 +796,6 @@ static ssize_t therm_crit_store(struct device *dev, return count; } -/* - * the +11 term below reflects the fact that VLM units 11,12,13 are - * used in the chip to measure voltage across the thermistors - */ -static struct sensor_device_attribute therm_input[] = { - SENSOR_ATTR_RO(temp4_input, therm_input, 0 + 11), - SENSOR_ATTR_RO(temp5_input, therm_input, 1 + 11), - SENSOR_ATTR_RO(temp6_input, therm_input, 2 + 11), -}; -static struct sensor_device_attribute therm_status[] = { - SENSOR_ATTR_RO(temp4_status, therm_status, 0 + 11), - SENSOR_ATTR_RO(temp5_status, therm_status, 1 + 11), - SENSOR_ATTR_RO(temp6_status, therm_status, 2 + 11), -}; -static struct sensor_device_attribute therm_min[] = { - SENSOR_ATTR_RW(temp4_min, therm_min, 0 + 11), - SENSOR_ATTR_RW(temp5_min, therm_min, 1 + 11), - SENSOR_ATTR_RW(temp6_min, therm_min, 2 + 11), -}; -static struct sensor_device_attribute therm_max[] = { - SENSOR_ATTR_RW(temp4_max, therm_max, 0 + 11), - SENSOR_ATTR_RW(temp5_max, therm_max, 1 + 11), - SENSOR_ATTR_RW(temp6_max, therm_max, 2 + 11), -}; static struct sensor_device_attribute therm_crit[] = { SENSOR_ATTR_RW(temp4_crit, therm_crit, 0 + 11), SENSOR_ATTR_RW(temp5_crit, therm_crit, 1 + 11), @@ -776,7 +806,6 @@ static struct sensor_device_attribute therm_crit[] = { * show_therm_min/max_alarm() reads data from the per-channel voltage * status register (sec 11.5.12) */ - static ssize_t therm_min_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -786,6 +815,13 @@ static ssize_t therm_min_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); } + +static struct sensor_device_attribute therm_min_alarm[] = { + SENSOR_ATTR_RO(temp4_min_alarm, therm_min_alarm, 0 + 11), + SENSOR_ATTR_RO(temp5_min_alarm, therm_min_alarm, 1 + 11), + SENSOR_ATTR_RO(temp6_min_alarm, therm_min_alarm, 2 + 11), +}; + static ssize_t therm_max_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -795,6 +831,13 @@ static ssize_t therm_max_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); } + +static struct sensor_device_attribute therm_max_alarm[] = { + SENSOR_ATTR_RO(temp4_max_alarm, therm_max_alarm, 0 + 11), + SENSOR_ATTR_RO(temp5_max_alarm, therm_max_alarm, 1 + 11), + SENSOR_ATTR_RO(temp6_max_alarm, therm_max_alarm, 2 + 11), +}; + static ssize_t therm_crit_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -805,16 +848,6 @@ static ssize_t therm_crit_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & TEMP_ALM_CRIT)); } -static struct sensor_device_attribute therm_min_alarm[] = { - SENSOR_ATTR_RO(temp4_min_alarm, therm_min_alarm, 0 + 11), - SENSOR_ATTR_RO(temp5_min_alarm, therm_min_alarm, 1 + 11), - SENSOR_ATTR_RO(temp6_min_alarm, therm_min_alarm, 2 + 11), -}; -static struct sensor_device_attribute therm_max_alarm[] = { - SENSOR_ATTR_RO(temp4_max_alarm, therm_max_alarm, 0 + 11), - SENSOR_ATTR_RO(temp5_max_alarm, therm_max_alarm, 1 + 11), - SENSOR_ATTR_RO(temp6_max_alarm, therm_max_alarm, 2 + 11), -}; static struct sensor_device_attribute therm_crit_alarm[] = { SENSOR_ATTR_RO(temp4_crit_alarm, therm_crit_alarm, 0 + 11), SENSOR_ATTR_RO(temp5_crit_alarm, therm_crit_alarm, 1 + 11), @@ -849,37 +882,32 @@ static ssize_t temp_input_show(struct device *dev, return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); } -static ssize_t temp_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[attr->index])); -} +static struct sensor_device_attribute temp_input[] = { + SENSOR_ATTR_RO(temp1_input, temp_input, 0), + SENSOR_ATTR_RO(temp2_input, temp_input, 1), + SENSOR_ATTR_RO(temp3_input, temp_input, 2), +}; -static ssize_t temp_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t temp_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[attr->index])); + return sprintf(buf, "%d\n", data->temp_status[attr->index]); } -static ssize_t temp_crit_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", - TEMP_FROM_REG(data->temp_crit[attr->index])); -} +static struct sensor_device_attribute temp_status[] = { + SENSOR_ATTR_RO(temp1_status, temp_status, 0), + SENSOR_ATTR_RO(temp2_status, temp_status, 1), + SENSOR_ATTR_RO(temp3_status, temp_status, 2), +}; -static ssize_t temp_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t temp_min_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", data->temp_status[attr->index]); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[attr->index])); } static ssize_t temp_min_store(struct device *dev, @@ -903,6 +931,20 @@ static ssize_t temp_min_store(struct device *dev, return count; } +static struct sensor_device_attribute temp_min[] = { + SENSOR_ATTR_RW(temp1_min, temp_min, 0), + SENSOR_ATTR_RW(temp2_min, temp_min, 1), + SENSOR_ATTR_RW(temp3_min, temp_min, 2), +}; + +static ssize_t temp_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[attr->index])); +} + static ssize_t temp_max_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -924,6 +966,21 @@ static ssize_t temp_max_store(struct device *dev, return count; } +static struct sensor_device_attribute temp_max[] = { + SENSOR_ATTR_RW(temp1_max, temp_max, 0), + SENSOR_ATTR_RW(temp2_max, temp_max, 1), + SENSOR_ATTR_RW(temp3_max, temp_max, 2), +}; + +static ssize_t temp_crit_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%d\n", + TEMP_FROM_REG(data->temp_crit[attr->index])); +} + static ssize_t temp_crit_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -945,47 +1002,17 @@ static ssize_t temp_crit_store(struct device *dev, return count; } -static struct sensor_device_attribute temp_input[] = { - SENSOR_ATTR_RO(temp1_input, temp_input, 0), - SENSOR_ATTR_RO(temp2_input, temp_input, 1), - SENSOR_ATTR_RO(temp3_input, temp_input, 2), -}; -static struct sensor_device_attribute temp_status[] = { - SENSOR_ATTR_RO(temp1_status, temp_status, 0), - SENSOR_ATTR_RO(temp2_status, temp_status, 1), - SENSOR_ATTR_RO(temp3_status, temp_status, 2), -}; -static struct sensor_device_attribute temp_min[] = { - SENSOR_ATTR_RW(temp1_min, temp_min, 0), - SENSOR_ATTR_RW(temp2_min, temp_min, 1), - SENSOR_ATTR_RW(temp3_min, temp_min, 2), -}; -static struct sensor_device_attribute temp_max[] = { - SENSOR_ATTR_RW(temp1_max, temp_max, 0), - SENSOR_ATTR_RW(temp2_max, temp_max, 1), - SENSOR_ATTR_RW(temp3_max, temp_max, 2), -}; static struct sensor_device_attribute temp_crit[] = { SENSOR_ATTR_RW(temp1_crit, temp_crit, 0), SENSOR_ATTR_RW(temp2_crit, temp_crit, 1), SENSOR_ATTR_RW(temp3_crit, temp_crit, 2), }; -static ssize_t alarms_temp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->temp_alarms); -} - -static DEVICE_ATTR_RO(alarms_temp); - /* - * show_temp_min/max_alarm() reads data from the per-channel status + * temp_min/max_alarm_show() reads data from the per-channel status * register (sec 12.3.7), not the temp event status registers (sec * 12.3.2) that show_temp_alarm() reads (via data->temp_alarms) */ - static ssize_t temp_min_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -996,6 +1023,12 @@ static ssize_t temp_min_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MIN)); } +static struct sensor_device_attribute temp_min_alarm[] = { + SENSOR_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0), + SENSOR_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1), + SENSOR_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2), +}; + static ssize_t temp_max_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1006,6 +1039,12 @@ static ssize_t temp_max_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MAX)); } +static struct sensor_device_attribute temp_max_alarm[] = { + SENSOR_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0), + SENSOR_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1), + SENSOR_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2), +}; + static ssize_t temp_crit_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1016,18 +1055,6 @@ static ssize_t temp_crit_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_ALM_CRIT)); } -static struct sensor_device_attribute temp_min_alarm[] = { - SENSOR_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0), - SENSOR_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1), - SENSOR_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2), -}; - -static struct sensor_device_attribute temp_max_alarm[] = { - SENSOR_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0), - SENSOR_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1), - SENSOR_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2), -}; - static struct sensor_device_attribute temp_crit_alarm[] = { SENSOR_ATTR_RO(temp1_crit_alarm, temp_crit_alarm, 0), SENSOR_ATTR_RO(temp2_crit_alarm, temp_crit_alarm, 1), @@ -1043,6 +1070,7 @@ static ssize_t temp_fault_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_FAULT)); } + static struct sensor_device_attribute temp_fault[] = { SENSOR_ATTR_RO(temp1_fault, temp_fault, 0), SENSOR_ATTR_RO(temp2_fault, temp_fault, 1), @@ -1074,106 +1102,180 @@ static const struct attribute_group pc8736x_temp_attr_group[] = { { .attrs = pc8736x_temp_attr[2] } }; -static ssize_t name_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t alarms_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct pc87360_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->name); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", data->temp_alarms); } -static DEVICE_ATTR_RO(name); +static DEVICE_ATTR_RO(alarms_temp); -/* - * Device detection, registration and update - */ +static ssize_t fan_input_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], + FAN_DIV_FROM_REG(data->fan_status[attr->index]))); +} -static int __init pc87360_find(int sioaddr, u8 *devid, - unsigned short *addresses) +static struct sensor_device_attribute fan_input[] = { + SENSOR_ATTR_RO(fan1_input, fan_input, 0), + SENSOR_ATTR_RO(fan2_input, fan_input, 1), + SENSOR_ATTR_RO(fan3_input, fan_input, 2), +}; + +static ssize_t fan_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { - u16 val; - int i; - int nrdev; /* logical device count */ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", + FAN_STATUS_FROM_REG(data->fan_status[attr->index])); +} - /* No superio_enter */ +static struct sensor_device_attribute fan_status[] = { + SENSOR_ATTR_RO(fan1_status, fan_status, 0), + SENSOR_ATTR_RO(fan2_status, fan_status, 1), + SENSOR_ATTR_RO(fan3_status, fan_status, 2), +}; - /* Identify device */ - val = force_id ? force_id : superio_inb(sioaddr, DEVID); - switch (val) { - case 0xE1: /* PC87360 */ - case 0xE8: /* PC87363 */ - case 0xE4: /* PC87364 */ - nrdev = 1; - break; - case 0xE5: /* PC87365 */ - case 0xE9: /* PC87366 */ - nrdev = 3; - break; - default: - superio_exit(sioaddr); - return -ENODEV; +static ssize_t fan_div_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", + FAN_DIV_FROM_REG(data->fan_status[attr->index])); +} + +static struct sensor_device_attribute fan_div[] = { + SENSOR_ATTR_RO(fan1_div, fan_div, 0), + SENSOR_ATTR_RO(fan2_div, fan_div, 1), + SENSOR_ATTR_RO(fan3_div, fan_div, 2), +}; + +static ssize_t fan_min_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], + FAN_DIV_FROM_REG(data->fan_status[attr->index]))); +} + +static ssize_t fan_min_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = dev_get_drvdata(dev); + long fan_min; + int err; + + err = kstrtol(buf, 10, &fan_min); + if (err) + return err; + + mutex_lock(&data->update_lock); + fan_min = FAN_TO_REG(fan_min, + FAN_DIV_FROM_REG(data->fan_status[attr->index])); + + /* If it wouldn't fit, change clock divisor */ + while (fan_min > 255 + && (data->fan_status[attr->index] & 0x60) != 0x60) { + fan_min >>= 1; + data->fan[attr->index] >>= 1; + data->fan_status[attr->index] += 0x20; } - /* Remember the device id */ - *devid = val; + data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(attr->index), + data->fan_min[attr->index]); - for (i = 0; i < nrdev; i++) { - /* select logical device */ - superio_outb(sioaddr, DEV, logdev[i]); + /* Write new divider, preserve alarm bits */ + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(attr->index), + data->fan_status[attr->index] & 0xF9); + mutex_unlock(&data->update_lock); - val = superio_inb(sioaddr, ACT); - if (!(val & 0x01)) { - pr_info("Device 0x%02x not activated\n", logdev[i]); - continue; - } + return count; +} - val = (superio_inb(sioaddr, BASE) << 8) - | superio_inb(sioaddr, BASE + 1); - if (!val) { - pr_info("Base address not set for device 0x%02x\n", - logdev[i]); - continue; - } +static struct sensor_device_attribute fan_min[] = { + SENSOR_ATTR_RW(fan1_min, fan_min, 0), + SENSOR_ATTR_RW(fan2_min, fan_min, 1), + SENSOR_ATTR_RW(fan3_min, fan_min, 2), +}; - addresses[i] = val; +#define FAN_UNIT_ATTRS(X) \ +{ &fan_input[X].dev_attr.attr, \ + &fan_status[X].dev_attr.attr, \ + &fan_div[X].dev_attr.attr, \ + &fan_min[X].dev_attr.attr, \ + NULL \ +} - if (i == 0) { /* Fans */ - confreg[0] = superio_inb(sioaddr, 0xF0); - confreg[1] = superio_inb(sioaddr, 0xF1); +static struct attribute *pc8736x_fan_attr[][5] = { + FAN_UNIT_ATTRS(0), + FAN_UNIT_ATTRS(1), + FAN_UNIT_ATTRS(2) +}; - pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1, - (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1, - (confreg[0] >> 4) & 1); - pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2, - (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1, - (confreg[0] >> 7) & 1); - pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3, - confreg[1] & 1, (confreg[1] >> 1) & 1, - (confreg[1] >> 2) & 1); - } else if (i == 1) { /* Voltages */ - /* Are we using thermistors? */ - if (*devid == 0xE9) { /* PC87366 */ - /* - * These registers are not logical-device - * specific, just that we won't need them if - * we don't use the VLM device - */ - confreg[2] = superio_inb(sioaddr, 0x2B); - confreg[3] = superio_inb(sioaddr, 0x25); +static const struct attribute_group pc8736x_fan_attr_group[] = { + { .attrs = pc8736x_fan_attr[0], }, + { .attrs = pc8736x_fan_attr[1], }, + { .attrs = pc8736x_fan_attr[2], }, +}; - if (confreg[2] & 0x40) { - pr_info("Using thermistors for temperature monitoring\n"); - } - if (confreg[3] & 0xE0) { - pr_info("VID inputs routed (mode %u)\n", - confreg[3] >> 5); - } - } - } - } +static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", + PWM_FROM_REG(data->pwm[attr->index], + FAN_CONFIG_INVERT(data->fan_conf, + attr->index))); +} - superio_exit(sioaddr); - return 0; +static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = dev_get_drvdata(dev); + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->pwm[attr->index] = PWM_TO_REG(val, + FAN_CONFIG_INVERT(data->fan_conf, attr->index)); + pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index), + data->pwm[attr->index]); + mutex_unlock(&data->update_lock); + return count; } +static struct sensor_device_attribute pwm[] = { + SENSOR_ATTR_RW(pwm1, pwm, 0), + SENSOR_ATTR_RW(pwm2, pwm, 1), + SENSOR_ATTR_RW(pwm3, pwm, 2), +}; + +static ssize_t name_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->name); +} + +static DEVICE_ATTR_RO(name); + static void pc87360_remove_files(struct device *dev) { int i; @@ -1190,6 +1292,146 @@ static void pc87360_remove_files(struct device *dev) sysfs_remove_group(&dev->kobj, &pc8736x_vin_group); } +static void pc87360_init_device(struct platform_device *pdev, + int use_thermistors) +{ + struct pc87360_data *data = platform_get_drvdata(pdev); + int i, nr; + const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 }; + const u8 init_temp[3] = { 2, 2, 1 }; + u8 reg; + + if (init >= 2 && data->innr) { + reg = pc87360_read_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONVRATE); + dev_info(&pdev->dev, + "VLM conversion set to 1s period, 160us delay\n"); + pc87360_write_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONVRATE, + (reg & 0xC0) | 0x11); + } + + nr = data->innr < 11 ? data->innr : 11; + for (i = 0; i < nr; i++) { + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + dev_dbg(&pdev->dev, "bios in%d status:0x%02x\n", i, reg); + if (init >= init_in[i]) { + /* Forcibly enable voltage channel */ + if (!(reg & CHAN_ENA)) { + dev_dbg(&pdev->dev, "Forcibly enabling in%d\n", + i); + pc87360_write_value(data, LD_IN, i, + PC87365_REG_IN_STATUS, + (reg & 0x68) | 0x87); + } + } + } + + /* + * We can't blindly trust the Super-I/O space configuration bit, + * most BIOS won't set it properly + */ + dev_dbg(&pdev->dev, "bios thermistors:%d\n", use_thermistors); + for (i = 11; i < data->innr; i++) { + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_TEMP_STATUS); + use_thermistors = use_thermistors || (reg & CHAN_ENA); + /* thermistors are temp[4-6], measured on vin[11-14] */ + dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i-7, reg); + } + dev_dbg(&pdev->dev, "using thermistors:%d\n", use_thermistors); + + i = use_thermistors ? 2 : 0; + for (; i < data->tempnr; i++) { + reg = pc87360_read_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS); + dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i + 1, reg); + if (init >= init_temp[i]) { + /* Forcibly enable temperature channel */ + if (!(reg & CHAN_ENA)) { + dev_dbg(&pdev->dev, + "Forcibly enabling temp%d\n", i + 1); + pc87360_write_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS, + 0xCF); + } + } + } + + if (use_thermistors) { + for (i = 11; i < data->innr; i++) { + if (init >= init_in[i]) { + /* + * The pin may already be used by thermal + * diodes + */ + reg = pc87360_read_value(data, LD_TEMP, + (i - 11) / 2, PC87365_REG_TEMP_STATUS); + if (reg & CHAN_ENA) { + dev_dbg(&pdev->dev, + "Skipping temp%d, pin already in use by temp%d\n", + i - 7, (i - 11) / 2); + continue; + } + + /* Forcibly enable thermistor channel */ + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + if (!(reg & CHAN_ENA)) { + dev_dbg(&pdev->dev, + "Forcibly enabling temp%d\n", + i - 7); + pc87360_write_value(data, LD_IN, i, + PC87365_REG_TEMP_STATUS, + (reg & 0x60) | 0x8F); + } + } + } + } + + if (data->innr) { + reg = pc87360_read_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONFIG); + dev_dbg(&pdev->dev, "bios vin-cfg:0x%02x\n", reg); + if (reg & CHAN_ENA) { + dev_dbg(&pdev->dev, + "Forcibly enabling monitoring (VLM)\n"); + pc87360_write_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONFIG, + reg & 0xFE); + } + } + + if (data->tempnr) { + reg = pc87360_read_value(data, LD_TEMP, NO_BANK, + PC87365_REG_TEMP_CONFIG); + dev_dbg(&pdev->dev, "bios temp-cfg:0x%02x\n", reg); + if (reg & CHAN_ENA) { + dev_dbg(&pdev->dev, + "Forcibly enabling monitoring (TMS)\n"); + pc87360_write_value(data, LD_TEMP, NO_BANK, + PC87365_REG_TEMP_CONFIG, + reg & 0xFE); + } + + if (init >= 2) { + /* Chip config as documented by National Semi. */ + pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08); + /* + * We voluntarily omit the bank here, in case the + * sequence itself matters. It shouldn't be a problem, + * since nobody else is supposed to access the + * device at that point. + */ + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04); + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35); + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05); + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05); + } + } +} + static int pc87360_probe(struct platform_device *pdev) { int i; @@ -1239,7 +1481,7 @@ static int pc87360_probe(struct platform_device *pdev) data->address[i] = extra_isa[i]; if (data->address[i] && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT, - pc87360_driver.driver.name)) { + DRIVER_NAME)) { dev_err(dev, "Region 0x%x-0x%x already in use!\n", extra_isa[i], extra_isa[i]+PC87360_EXTENT-1); @@ -1355,330 +1597,105 @@ static int pc87360_remove(struct platform_device *pdev) } /* - * ldi is the logical device index - * bank is for voltages and temperatures only + * Driver data */ -static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg) -{ - int res; - - mutex_lock(&(data->lock)); - if (bank != NO_BANK) - outb_p(bank, data->address[ldi] + PC87365_REG_BANK); - res = inb_p(data->address[ldi] + reg); - mutex_unlock(&(data->lock)); +static struct platform_driver pc87360_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = pc87360_probe, + .remove = pc87360_remove, +}; - return res; -} +/* + * Device detection, registration and update + */ -static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg, u8 value) +static int __init pc87360_find(int sioaddr, u8 *devid, + unsigned short *addresses) { - mutex_lock(&(data->lock)); - if (bank != NO_BANK) - outb_p(bank, data->address[ldi] + PC87365_REG_BANK); - outb_p(value, data->address[ldi] + reg); - mutex_unlock(&(data->lock)); -} + u16 val; + int i; + int nrdev; /* logical device count */ -/* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */ -#define CHAN_CNVRTD 0x80 /* new data ready */ -#define CHAN_ENA 0x01 /* enabled channel (temp or vin) */ -#define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ -#define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ + /* No superio_enter */ -#define TEMP_OTS_OE 0x20 /* OTS Output Enable */ -#define VIN_RW1C_MASK (CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN) /* 0x87 */ -#define TEMP_RW1C_MASK (VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */ + /* Identify device */ + val = force_id ? force_id : superio_inb(sioaddr, DEVID); + switch (val) { + case 0xE1: /* PC87360 */ + case 0xE8: /* PC87363 */ + case 0xE4: /* PC87364 */ + nrdev = 1; + break; + case 0xE5: /* PC87365 */ + case 0xE9: /* PC87366 */ + nrdev = 3; + break; + default: + superio_exit(sioaddr); + return -ENODEV; + } + /* Remember the device id */ + *devid = val; -static void pc87360_init_device(struct platform_device *pdev, - int use_thermistors) -{ - struct pc87360_data *data = platform_get_drvdata(pdev); - int i, nr; - const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 }; - const u8 init_temp[3] = { 2, 2, 1 }; - u8 reg; + for (i = 0; i < nrdev; i++) { + /* select logical device */ + superio_outb(sioaddr, DEV, logdev[i]); - if (init >= 2 && data->innr) { - reg = pc87360_read_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONVRATE); - dev_info(&pdev->dev, - "VLM conversion set to 1s period, 160us delay\n"); - pc87360_write_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONVRATE, - (reg & 0xC0) | 0x11); - } + val = superio_inb(sioaddr, ACT); + if (!(val & 0x01)) { + pr_info("Device 0x%02x not activated\n", logdev[i]); + continue; + } - nr = data->innr < 11 ? data->innr : 11; - for (i = 0; i < nr; i++) { - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - dev_dbg(&pdev->dev, "bios in%d status:0x%02x\n", i, reg); - if (init >= init_in[i]) { - /* Forcibly enable voltage channel */ - if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, "Forcibly enabling in%d\n", - i); - pc87360_write_value(data, LD_IN, i, - PC87365_REG_IN_STATUS, - (reg & 0x68) | 0x87); - } + val = (superio_inb(sioaddr, BASE) << 8) + | superio_inb(sioaddr, BASE + 1); + if (!val) { + pr_info("Base address not set for device 0x%02x\n", + logdev[i]); + continue; } - } - /* - * We can't blindly trust the Super-I/O space configuration bit, - * most BIOS won't set it properly - */ - dev_dbg(&pdev->dev, "bios thermistors:%d\n", use_thermistors); - for (i = 11; i < data->innr; i++) { - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_TEMP_STATUS); - use_thermistors = use_thermistors || (reg & CHAN_ENA); - /* thermistors are temp[4-6], measured on vin[11-14] */ - dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i-7, reg); - } - dev_dbg(&pdev->dev, "using thermistors:%d\n", use_thermistors); + addresses[i] = val; - i = use_thermistors ? 2 : 0; - for (; i < data->tempnr; i++) { - reg = pc87360_read_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS); - dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i + 1, reg); - if (init >= init_temp[i]) { - /* Forcibly enable temperature channel */ - if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, - "Forcibly enabling temp%d\n", i + 1); - pc87360_write_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS, - 0xCF); - } - } - } + if (i == 0) { /* Fans */ + confreg[0] = superio_inb(sioaddr, 0xF0); + confreg[1] = superio_inb(sioaddr, 0xF1); - if (use_thermistors) { - for (i = 11; i < data->innr; i++) { - if (init >= init_in[i]) { + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1, + (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1, + (confreg[0] >> 4) & 1); + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2, + (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1, + (confreg[0] >> 7) & 1); + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3, + confreg[1] & 1, (confreg[1] >> 1) & 1, + (confreg[1] >> 2) & 1); + } else if (i == 1) { /* Voltages */ + /* Are we using thermistors? */ + if (*devid == 0xE9) { /* PC87366 */ /* - * The pin may already be used by thermal - * diodes + * These registers are not logical-device + * specific, just that we won't need them if + * we don't use the VLM device */ - reg = pc87360_read_value(data, LD_TEMP, - (i - 11) / 2, PC87365_REG_TEMP_STATUS); - if (reg & CHAN_ENA) { - dev_dbg(&pdev->dev, - "Skipping temp%d, pin already in use by temp%d\n", - i - 7, (i - 11) / 2); - continue; - } + confreg[2] = superio_inb(sioaddr, 0x2B); + confreg[3] = superio_inb(sioaddr, 0x25); - /* Forcibly enable thermistor channel */ - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - if (!(reg & CHAN_ENA)) { - dev_dbg(&pdev->dev, - "Forcibly enabling temp%d\n", - i - 7); - pc87360_write_value(data, LD_IN, i, - PC87365_REG_TEMP_STATUS, - (reg & 0x60) | 0x8F); + if (confreg[2] & 0x40) { + pr_info("Using thermistors for temperature monitoring\n"); + } + if (confreg[3] & 0xE0) { + pr_info("VID inputs routed (mode %u)\n", + confreg[3] >> 5); } } } } - if (data->innr) { - reg = pc87360_read_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONFIG); - dev_dbg(&pdev->dev, "bios vin-cfg:0x%02x\n", reg); - if (reg & CHAN_ENA) { - dev_dbg(&pdev->dev, - "Forcibly enabling monitoring (VLM)\n"); - pc87360_write_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONFIG, - reg & 0xFE); - } - } - - if (data->tempnr) { - reg = pc87360_read_value(data, LD_TEMP, NO_BANK, - PC87365_REG_TEMP_CONFIG); - dev_dbg(&pdev->dev, "bios temp-cfg:0x%02x\n", reg); - if (reg & CHAN_ENA) { - dev_dbg(&pdev->dev, - "Forcibly enabling monitoring (TMS)\n"); - pc87360_write_value(data, LD_TEMP, NO_BANK, - PC87365_REG_TEMP_CONFIG, - reg & 0xFE); - } - - if (init >= 2) { - /* Chip config as documented by National Semi. */ - pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08); - /* - * We voluntarily omit the bank here, in case the - * sequence itself matters. It shouldn't be a problem, - * since nobody else is supposed to access the - * device at that point. - */ - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04); - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35); - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05); - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05); - } - } -} - -static void pc87360_autodiv(struct device *dev, int nr) -{ - struct pc87360_data *data = dev_get_drvdata(dev); - u8 old_min = data->fan_min[nr]; - - /* Increase clock divider if needed and possible */ - if ((data->fan_status[nr] & 0x04) /* overflow flag */ - || (data->fan[nr] >= 224)) { /* next to overflow */ - if ((data->fan_status[nr] & 0x60) != 0x60) { - data->fan_status[nr] += 0x20; - data->fan_min[nr] >>= 1; - data->fan[nr] >>= 1; - dev_dbg(dev, - "Increasing clock divider to %d for fan %d\n", - FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); - } - } else { - /* Decrease clock divider if possible */ - while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */ - && data->fan[nr] < 85 /* bad accuracy */ - && (data->fan_status[nr] & 0x60) != 0x00) { - data->fan_status[nr] -= 0x20; - data->fan_min[nr] <<= 1; - data->fan[nr] <<= 1; - dev_dbg(dev, - "Decreasing clock divider to %d for fan %d\n", - FAN_DIV_FROM_REG(data->fan_status[nr]), - nr + 1); - } - } - - /* Write new fan min if it changed */ - if (old_min != data->fan_min[nr]) { - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(nr), - data->fan_min[nr]); - } -} - -static struct pc87360_data *pc87360_update_device(struct device *dev) -{ - struct pc87360_data *data = dev_get_drvdata(dev); - u8 i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - dev_dbg(dev, "Data update\n"); - - /* Fans */ - for (i = 0; i < data->fannr; i++) { - if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { - data->fan_status[i] = - pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_FAN_STATUS(i)); - data->fan[i] = pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_FAN(i)); - data->fan_min[i] = pc87360_read_value(data, - LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(i)); - /* Change clock divider if needed */ - pc87360_autodiv(dev, i); - /* Clear bits and write new divider */ - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_STATUS(i), - data->fan_status[i]); - } - if (FAN_CONFIG_CONTROL(data->fan_conf, i)) - data->pwm[i] = pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_PWM(i)); - } - - /* Voltages */ - for (i = 0; i < data->innr; i++) { - data->in_status[i] = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - /* Clear bits */ - pc87360_write_value(data, LD_IN, i, - PC87365_REG_IN_STATUS, - data->in_status[i]); - if ((data->in_status[i] & CHAN_READY) == CHAN_READY) { - data->in[i] = pc87360_read_value(data, LD_IN, - i, PC87365_REG_IN); - } - if (data->in_status[i] & CHAN_ENA) { - data->in_min[i] = pc87360_read_value(data, - LD_IN, i, - PC87365_REG_IN_MIN); - data->in_max[i] = pc87360_read_value(data, - LD_IN, i, - PC87365_REG_IN_MAX); - if (i >= 11) - data->in_crit[i-11] = - pc87360_read_value(data, LD_IN, - i, PC87365_REG_TEMP_CRIT); - } - } - if (data->innr) { - data->in_alarms = pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_IN_ALARMS1) - | ((pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_IN_ALARMS2) - & 0x07) << 8); - data->vid = (data->vid_conf & 0xE0) ? - pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_VID) : 0x1F; - } - - /* Temperatures */ - for (i = 0; i < data->tempnr; i++) { - data->temp_status[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_STATUS); - /* Clear bits */ - pc87360_write_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS, - data->temp_status[i]); - if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) { - data->temp[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP); - } - if (data->temp_status[i] & CHAN_ENA) { - data->temp_min[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_MIN); - data->temp_max[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_MAX); - data->temp_crit[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_CRIT); - } - } - if (data->tempnr) { - data->temp_alarms = pc87360_read_value(data, LD_TEMP, - NO_BANK, PC87365_REG_TEMP_ALARMS) - & 0x3F; - } - - data->last_updated = jiffies; - data->valid = true; - } - - mutex_unlock(&data->update_lock); - - return data; + superio_exit(sioaddr); + return 0; } static int __init pc87360_device_add(unsigned short address) @@ -1777,10 +1794,10 @@ static void __exit pc87360_exit(void) platform_driver_unregister(&pc87360_driver); } - MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("PC8736x hardware monitor"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); module_init(pc87360_init); module_exit(pc87360_exit); |