hwmon: (w83627ehf) Improve support for W83667HG-B

Add support for 4th temperature sensor on W83677HG-B.
Display temperature labels on W83677HG-B to report temperature sources.

Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
Acked-by: Ian Dobson <i.dobson@planet-ian.com>
This commit is contained in:
Guenter Roeck 2011-02-07 15:08:54 -08:00
parent 83cc8985b8
commit d36cf32c9a
2 changed files with 136 additions and 38 deletions

View file

@ -39,16 +39,21 @@ This driver implements support for the Winbond W83627EHF, W83627EHG,
W83627DHG, W83627DHG-P, W83667HG and W83667HG-B super I/O chips. W83627DHG, W83627DHG-P, W83667HG and W83667HG-B super I/O chips.
We will refer to them collectively as Winbond chips. We will refer to them collectively as Winbond chips.
The chips implement three temperature sensors, five fan rotation The chips implement three temperature sensors (up to four for 667HG-B),
speed sensors, ten analog voltage sensors (only nine for the 627DHG), one five fan rotation speed sensors, ten analog voltage sensors (only nine for the
VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG and 667HG), alarms 627DHG), one VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG and 667HG),
with beep warnings (control unimplemented), and some automatic fan alarms with beep warnings (control unimplemented), and some automatic fan
regulation strategies (plus manual fan control mode). regulation strategies (plus manual fan control mode).
The temperature sensor sources on W82677HG-B are configurable. temp4 is only
reported if its temperature source differs from the temperature sources of the
other three temperature sensors. The configured source for each of the
temperature sensors is reported in tempX_label.
Temperatures are measured in degrees Celsius and measurement resolution is 1 Temperatures are measured in degrees Celsius and measurement resolution is 1
degC for temp1 and 0.5 degC for temp2 and temp3. An alarm is triggered when degC for temp1 and temp4, and 0.5 degC for temp2 and temp3. An alarm is
the temperature gets higher than high limit; it stays on until the temperature triggered when the temperature gets higher than high limit; it stays on until
falls below the hysteresis value. the temperature falls below the hysteresis value.
Fan rotation speeds are reported in RPM (rotations per minute). An alarm is Fan rotation speeds are reported in RPM (rotations per minute). An alarm is
triggered if the rotation speed has dropped below a programmable limit. Fan triggered if the rotation speed has dropped below a programmable limit. Fan

View file

@ -39,7 +39,7 @@
w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3
w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3 w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3
w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3
w83667hg-b 9 5 3 3 0xb350 0xc1 0x5ca3 w83667hg-b 9 5 3 4 0xb350 0xc1 0x5ca3
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@ -164,10 +164,10 @@ static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c };
#define W83627EHF_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ #define W83627EHF_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \
(0x550 + (nr) - 7)) (0x550 + (nr) - 7))
static const u16 W83627EHF_REG_TEMP[] = { 0x27, 0x150, 0x250 }; static const u16 W83627EHF_REG_TEMP[] = { 0x27, 0x150, 0x250, 0x7e };
static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x3a, 0x153, 0x253 }; static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x3a, 0x153, 0x253, 0 };
static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x39, 0x155, 0x255 }; static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x39, 0x155, 0x255, 0 };
static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252 }; static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252, 0 };
/* Fan clock dividers are spread over the following five registers */ /* Fan clock dividers are spread over the following five registers */
#define W83627EHF_REG_FANDIV1 0x47 #define W83627EHF_REG_FANDIV1 0x47
@ -213,6 +213,19 @@ static const u8 W83627EHF_REG_FAN_STEP_OUTPUT_COMMON[]
static const u8 W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B[] = { 0x67, 0x69, 0x6b }; static const u8 W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B[] = { 0x67, 0x69, 0x6b };
static const u8 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[] = { 0x68, 0x6a, 0x6c }; static const u8 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[] = { 0x68, 0x6a, 0x6c };
static const char *const w83667hg_b_temp_label[] = {
"SYSTIN",
"CPUTIN",
"AUXTIN",
"AMDTSI",
"PECI Agent 1",
"PECI Agent 2",
"PECI Agent 3",
"PECI Agent 4"
};
#define NUM_REG_TEMP 4
static inline int is_word_sized(u16 reg) static inline int is_word_sized(u16 reg)
{ {
return (((reg & 0xff00) == 0x100 return (((reg & 0xff00) == 0x100
@ -294,6 +307,9 @@ struct w83627ehf_data {
struct device *hwmon_dev; struct device *hwmon_dev;
struct mutex lock; struct mutex lock;
u8 temp_src[NUM_REG_TEMP];
const char * const *temp_label;
const u8 *REG_FAN_START_OUTPUT; const u8 *REG_FAN_START_OUTPUT;
const u8 *REG_FAN_STOP_OUTPUT; const u8 *REG_FAN_STOP_OUTPUT;
const u8 *REG_FAN_MAX_OUTPUT; const u8 *REG_FAN_MAX_OUTPUT;
@ -314,9 +330,9 @@ struct w83627ehf_data {
u8 fan_div[5]; u8 fan_div[5];
u8 has_fan; /* some fan inputs can be disabled */ u8 has_fan; /* some fan inputs can be disabled */
u8 temp_type[3]; u8 temp_type[3];
s16 temp[3]; s16 temp[4];
s16 temp_max[3]; s16 temp_max[4];
s16 temp_max_hyst[3]; s16 temp_max_hyst[4];
u32 alarms; u32 alarms;
u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */ u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
@ -339,7 +355,7 @@ struct w83627ehf_data {
u8 vid; u8 vid;
u8 vrm; u8 vrm;
u8 temp3_disable; u8 have_temp;
u8 in6_skip; u8 in6_skip;
}; };
@ -577,12 +593,18 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
} }
/* Measured temperatures and limits */ /* Measured temperatures and limits */
for (i = 0; i < 3; i++) { for (i = 0; i < NUM_REG_TEMP; i++) {
data->temp[i] = w83627ehf_read_value(data, if (!(data->have_temp & (1 << i)))
W83627EHF_REG_TEMP[i]); continue;
data->temp_max[i] = w83627ehf_read_value(data, data->temp[i]
W83627EHF_REG_TEMP_OVER[i]); = w83627ehf_read_value(data, W83627EHF_REG_TEMP[i]);
data->temp_max_hyst[i] = w83627ehf_read_value(data, if (i > 2)
break;
data->temp_max[i]
= w83627ehf_read_value(data,
W83627EHF_REG_TEMP_OVER[i]);
data->temp_max_hyst[i]
= w83627ehf_read_value(data,
W83627EHF_REG_TEMP_HYST[i]); W83627EHF_REG_TEMP_HYST[i]);
} }
@ -844,6 +866,15 @@ static struct sensor_device_attribute sda_fan_div[] = {
SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4), SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
}; };
static ssize_t
show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83627ehf_data *data = w83627ehf_update_device(dev);
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
}
#define show_temp_reg(REG, reg) \ #define show_temp_reg(REG, reg) \
static ssize_t \ static ssize_t \
show_##reg(struct device *dev, struct device_attribute *attr, \ show_##reg(struct device *dev, struct device_attribute *attr, \
@ -897,6 +928,14 @@ static struct sensor_device_attribute sda_temp_input[] = {
SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0), SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0),
SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1), SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1),
SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2), SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2),
SENSOR_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3),
};
static struct sensor_device_attribute sda_temp_label[] = {
SENSOR_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0),
SENSOR_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1),
SENSOR_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2),
SENSOR_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3),
}; };
static struct sensor_device_attribute sda_temp_max[] = { static struct sensor_device_attribute sda_temp_max[] = {
@ -1328,10 +1367,13 @@ static void w83627ehf_device_remove_files(struct device *dev)
device_remove_file(dev, &sda_target_temp[i].dev_attr); device_remove_file(dev, &sda_target_temp[i].dev_attr);
device_remove_file(dev, &sda_tolerance[i].dev_attr); device_remove_file(dev, &sda_tolerance[i].dev_attr);
} }
for (i = 0; i < 3; i++) { for (i = 0; i < NUM_REG_TEMP; i++) {
if ((i == 2) && data->temp3_disable) if (!(data->have_temp & (1 << i)))
continue; continue;
device_remove_file(dev, &sda_temp_input[i].dev_attr); device_remove_file(dev, &sda_temp_input[i].dev_attr);
device_remove_file(dev, &sda_temp_label[i].dev_attr);
if (i > 2)
break;
device_remove_file(dev, &sda_temp_max[i].dev_attr); device_remove_file(dev, &sda_temp_max[i].dev_attr);
device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr);
device_remove_file(dev, &sda_temp_alarm[i].dev_attr); device_remove_file(dev, &sda_temp_alarm[i].dev_attr);
@ -1354,12 +1396,14 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data)
w83627ehf_write_value(data, W83627EHF_REG_CONFIG, w83627ehf_write_value(data, W83627EHF_REG_CONFIG,
tmp | 0x01); tmp | 0x01);
/* Enable temp2 and temp3 if needed */ /* Enable temperature sensors if needed */
for (i = 1; i < 3; i++) { for (i = 0; i < NUM_REG_TEMP; i++) {
if (!(data->have_temp & (1 << i)))
continue;
if (!W83627EHF_REG_TEMP_CONFIG[i])
continue;
tmp = w83627ehf_read_value(data, tmp = w83627ehf_read_value(data,
W83627EHF_REG_TEMP_CONFIG[i]); W83627EHF_REG_TEMP_CONFIG[i]);
if ((i == 2) && data->temp3_disable)
continue;
if (tmp & 0x01) if (tmp & 0x01)
w83627ehf_write_value(data, w83627ehf_write_value(data,
W83627EHF_REG_TEMP_CONFIG[i], W83627EHF_REG_TEMP_CONFIG[i],
@ -1417,11 +1461,52 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
data->pwm_num = (sio_data->kind == w83667hg data->pwm_num = (sio_data->kind == w83667hg
|| sio_data->kind == w83667hg_b) ? 3 : 4; || sio_data->kind == w83667hg_b) ? 3 : 4;
data->have_temp = 0x07;
/* Check temp3 configuration bit for 667HG */ /* Check temp3 configuration bit for 667HG */
if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { if (sio_data->kind == w83667hg) {
data->temp3_disable = w83627ehf_read_value(data, u8 reg;
W83627EHF_REG_TEMP_CONFIG[2]) & 0x01;
data->in6_skip = !data->temp3_disable; reg = w83627ehf_read_value(data, W83627EHF_REG_TEMP_CONFIG[2]);
if (reg & 0x01)
data->have_temp &= ~(1 << 2);
else
data->in6_skip = 1; /* Either temp3 or in6 */
} else if (sio_data->kind == w83667hg_b) {
u8 reg;
reg = w83627ehf_read_value(data, 0x4a);
data->temp_src[0] = reg >> 5;
reg = w83627ehf_read_value(data, 0x49);
data->temp_src[1] = reg & 0x07;
data->temp_src[2] = (reg >> 4) & 0x07;
/*
* W83667HG-B has another temperature register at 0x7e.
* The temperature source is selected with register 0x7d.
* Support it if the source differs from already reported
* sources.
*/
reg = w83627ehf_read_value(data, 0x7d);
reg &= 0x07;
if (reg != data->temp_src[0] && reg != data->temp_src[1]
&& reg != data->temp_src[2]) {
data->temp_src[3] = reg;
data->have_temp |= 1 << 3;
}
/*
* Chip supports either AUXTIN or VIN3. Try to find out which
* one.
*/
reg = w83627ehf_read_value(data, W83627EHF_REG_TEMP_CONFIG[2]);
if (data->temp_src[2] == 2 && (reg & 0x01))
data->have_temp &= ~(1 << 2);
if ((data->temp_src[2] == 2 && (data->have_temp & (1 << 2)))
|| (data->temp_src[3] == 2 && (data->have_temp & (1 << 3))))
data->in6_skip = 1;
data->temp_label = w83667hg_b_temp_label;
} }
data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT; data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT;
@ -1584,13 +1669,21 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev)
} }
} }
for (i = 0; i < 3; i++) { for (i = 0; i < NUM_REG_TEMP; i++) {
if ((i == 2) && data->temp3_disable) if (!(data->have_temp & (1 << i)))
continue; continue;
if ((err = device_create_file(dev, err = device_create_file(dev, &sda_temp_input[i].dev_attr);
&sda_temp_input[i].dev_attr)) if (err)
|| (err = device_create_file(dev, goto exit_remove;
&sda_temp_max[i].dev_attr)) if (data->temp_label) {
err = device_create_file(dev,
&sda_temp_label[i].dev_attr);
if (err)
goto exit_remove;
}
if (i > 2)
break;
if ((err = device_create_file(dev, &sda_temp_max[i].dev_attr))
|| (err = device_create_file(dev, || (err = device_create_file(dev,
&sda_temp_max_hyst[i].dev_attr)) &sda_temp_max_hyst[i].dev_attr))
|| (err = device_create_file(dev, || (err = device_create_file(dev,