diff options
author | Jean Delvare <khali@linux-fr.org> | 2011-11-04 12:00:48 +0100 |
---|---|---|
committer | Jean Delvare <khali@endymion.delvare> | 2011-11-04 12:00:48 +0100 |
commit | eff7687d473c31cba3876c13e97eebc708eb8582 (patch) | |
tree | 59f2bcc34e01a7c125a335a45d42010bc66b2dc1 /drivers/hwmon/w83627ehf.c | |
parent | 6ba71de5f81bbf639b5dcea2bc2c33cdb87ed782 (diff) | |
download | linux-eff7687d473c31cba3876c13e97eebc708eb8582.tar.bz2 |
hwmon: (w83627ehf) Add support for the W83627UHG
This is essentially a stripped down version of the W83627DHG. Noticeable
difference is that it is still powered with +5V, as older models, even
though the ADC resolution is 8 mV as newer models have.
Thanks to Ulf Bruman (Saab Group) for doing all the testing.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Acked-by: Guenter Roeck <guenter.roeck@ericsson.com>
Diffstat (limited to 'drivers/hwmon/w83627ehf.c')
-rw-r--r-- | drivers/hwmon/w83627ehf.c | 148 |
1 files changed, 122 insertions, 26 deletions
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 81534cbd0aff..483cb268781e 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1,7 +1,7 @@ /* w83627ehf - Driver for the hardware monitoring functionality of the Winbond W83627EHF Super-I/O chip - Copyright (C) 2005 Jean Delvare <khali@linux-fr.org> + Copyright (C) 2005-2011 Jean Delvare <khali@linux-fr.org> Copyright (C) 2006 Yuan Mu (Winbond), Rudolf Marek <r.marek@assembler.cz> David Hubbard <david.c.hubbard@gmail.com> @@ -39,6 +39,7 @@ 0x8860 0xa1 w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 w83627dhg-p 9 5 4 3 0xb070 0xc1 0x5ca3 + w83627uhg 8 2 2 2 0xa230 0xc1 0x5ca3 w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 w83667hg-b 9 5 3 4 0xb350 0xc1 0x5ca3 nct6775f 9 4 3 9 0xb470 0xc1 0x5ca3 @@ -61,14 +62,17 @@ #include <linux/io.h> #include "lm75.h" -enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b, nct6775, - nct6776 }; +enum kinds { + w83627ehf, w83627dhg, w83627dhg_p, w83627uhg, + w83667hg, w83667hg_b, nct6775, nct6776, +}; /* used to set data->name = w83627ehf_device_names[data->sio_kind] */ static const char * const w83627ehf_device_names[] = { "w83627ehf", "w83627dhg", "w83627dhg", + "w83627uhg", "w83667hg", "w83667hg", "nct6775", @@ -104,6 +108,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_W83627EHG_ID 0x8860 #define SIO_W83627DHG_ID 0xa020 #define SIO_W83627DHG_P_ID 0xb070 +#define SIO_W83627UHG_ID 0xa230 #define SIO_W83667HG_ID 0xa510 #define SIO_W83667HG_B_ID 0xb350 #define SIO_NCT6775_ID 0xb470 @@ -388,18 +393,23 @@ div_from_reg(u8 reg) return 1 << reg; } -/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */ - -static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 }; +/* Some of the voltage inputs have internal scaling, the tables below + * contain 8 (the ADC LSB in mV) * scaling factor * 100 */ +static const u16 scale_in_common[10] = { + 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800 +}; +static const u16 scale_in_w83627uhg[9] = { + 800, 800, 3328, 3424, 800, 800, 0, 3328, 3400 +}; -static inline long in_from_reg(u8 reg, u8 nr) +static inline long in_from_reg(u8 reg, u8 nr, const u16 *scale_in) { - return reg * scale_in[nr]; + return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100); } -static inline u8 in_to_reg(u32 val, u8 nr) +static inline u8 in_to_reg(u32 val, u8 nr, const u16 *scale_in) { - return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0, + return SENSORS_LIMIT(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255); } @@ -430,6 +440,7 @@ struct w83627ehf_data { const u16 *REG_FAN_STOP_TIME; const u16 *REG_FAN_MAX_OUTPUT; const u16 *REG_FAN_STEP_OUTPUT; + const u16 *scale_in; unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); @@ -481,7 +492,8 @@ struct w83627ehf_data { u8 vrm; u16 have_temp; - u8 in6_skip; + u8 in6_skip:1; + u8 temp3_val_only:1; }; struct w83627ehf_sio_data { @@ -907,7 +919,8 @@ show_##reg(struct device *dev, struct device_attribute *attr, \ struct sensor_device_attribute *sensor_attr = \ to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ - return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \ + return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr, \ + data->scale_in)); \ } show_in_reg(in) show_in_reg(in_min) @@ -928,7 +941,7 @@ store_in_##reg(struct device *dev, struct device_attribute *attr, \ if (err < 0) \ return err; \ mutex_lock(&data->update_lock); \ - data->in_##reg[nr] = in_to_reg(val, nr); \ + data->in_##reg[nr] = in_to_reg(val, nr, data->scale_in); \ w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(nr), \ data->in_##reg[nr]); \ mutex_unlock(&data->update_lock); \ @@ -1617,25 +1630,28 @@ static struct sensor_device_attribute sda_sf3_arrays_fan4[] = { store_fan_step_output, 3), }; +static struct sensor_device_attribute sda_sf3_arrays_fan3[] = { + SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, + store_fan_stop_time, 2), + SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, + store_fan_start_output, 2), + SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, + store_fan_stop_output, 2), +}; + static struct sensor_device_attribute sda_sf3_arrays[] = { SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, store_fan_stop_time, 0), SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, store_fan_stop_time, 1), - SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, - store_fan_stop_time, 2), SENSOR_ATTR(pwm1_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, store_fan_start_output, 0), SENSOR_ATTR(pwm2_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, store_fan_start_output, 1), - SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, - store_fan_start_output, 2), SENSOR_ATTR(pwm1_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, store_fan_stop_output, 0), SENSOR_ATTR(pwm2_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, store_fan_stop_output, 1), - SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, - store_fan_stop_output, 2), }; @@ -1728,6 +1744,8 @@ static void w83627ehf_device_remove_files(struct device *dev) data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) device_remove_file(dev, &attr->dev_attr); } + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) + device_remove_file(dev, &sda_sf3_arrays_fan3[i].dev_attr); for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr); for (i = 0; i < data->in_num; i++) { @@ -1756,6 +1774,8 @@ static void w83627ehf_device_remove_files(struct device *dev) continue; device_remove_file(dev, &sda_temp_input[i].dev_attr); device_remove_file(dev, &sda_temp_label[i].dev_attr); + if (i == 2 && data->temp3_val_only) + continue; device_remove_file(dev, &sda_temp_max[i].dev_attr); device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); if (i > 2) @@ -1808,6 +1828,9 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data, case w83627ehf: diode = w83627ehf_read_value(data, W83627EHF_REG_DIODE); break; + case w83627uhg: + diode = 0x00; + break; default: diode = 0x70; } @@ -1871,6 +1894,13 @@ w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data, { int fan3pin, fan4pin, fan4min, fan5pin, regval; + /* The W83627UHG is simple, only two fan inputs, no config */ + if (sio_data->kind == w83627uhg) { + data->has_fan = 0x03; /* fan1 and fan2 */ + data->has_fan_min = 0x03; + return; + } + superio_enter(sio_data->sioreg); /* fan4 and fan5 share some pins with the GPIO and serial flash */ @@ -1962,11 +1992,21 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */ data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9; - /* 667HG, NCT6775F, and NCT6776F have 3 pwms */ - data->pwm_num = (sio_data->kind == w83667hg - || sio_data->kind == w83667hg_b - || sio_data->kind == nct6775 - || sio_data->kind == nct6776) ? 3 : 4; + /* 667HG, NCT6775F, and NCT6776F have 3 pwms, and 627UHG has only 2 */ + switch (sio_data->kind) { + default: + data->pwm_num = 4; + break; + case w83667hg: + case w83667hg_b: + case nct6775: + case nct6776: + data->pwm_num = 3; + break; + case w83627uhg: + data->pwm_num = 2; + break; + } /* Default to 3 temperature inputs, code below will adjust as needed */ data->have_temp = 0x07; @@ -2085,6 +2125,42 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) data->in6_skip = 1; data->temp_label = w83667hg_b_temp_label; + } else if (sio_data->kind == w83627uhg) { + u8 reg; + + w83627ehf_set_temp_reg_ehf(data, 3); + + /* + * Temperature sources for temp1 and temp2 are selected with + * bank 0, registers 0x49 and 0x4a. + */ + data->temp_src[0] = 0; /* SYSTIN */ + reg = w83627ehf_read_value(data, 0x49) & 0x07; + /* Adjust to have the same mapping as other source registers */ + if (reg == 0) + data->temp_src[1]++; + else if (reg >= 2 && reg <= 5) + data->temp_src[1] += 2; + else /* should never happen */ + data->have_temp &= ~(1 << 1); + reg = w83627ehf_read_value(data, 0x4a); + data->temp_src[2] = reg >> 5; + + /* + * Skip temp3 if source is invalid or the same as temp1 + * or temp2. + */ + if (data->temp_src[2] == 2 || data->temp_src[2] == 3 || + data->temp_src[2] == data->temp_src[0] || + ((data->have_temp & (1 << 1)) && + data->temp_src[2] == data->temp_src[1])) + data->have_temp &= ~(1 << 2); + else + data->temp3_val_only = 1; /* No limit regs */ + + data->in6_skip = 1; /* No VIN3 */ + + data->temp_label = w83667hg_b_temp_label; } else { w83627ehf_set_temp_reg_ehf(data, 3); @@ -2162,6 +2238,12 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) W83627EHF_REG_FAN_STEP_OUTPUT_COMMON; } + /* Setup input voltage scaling factors */ + if (sio_data->kind == w83627uhg) + data->scale_in = scale_in_w83627uhg; + else + data->scale_in = scale_in_common; + /* Initialize the chip */ w83627ehf_init_device(data, sio_data->kind); @@ -2178,7 +2260,7 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) err = device_create_file(dev, &dev_attr_cpu0_vid); if (err) goto exit_release; - } else { + } else if (sio_data->kind != w83627uhg) { superio_select(sio_data->sioreg, W83627EHF_LD_HWM); if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { /* Set VID input sensibility if needed. In theory the @@ -2268,7 +2350,14 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) goto exit_remove; } } - /* if fan4 is enabled create the sf3 files for it */ + /* if fan3 and fan4 are enabled create the sf3 files for them */ + if ((data->has_fan & (1 << 2)) && data->pwm_num >= 3) + for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) { + err = device_create_file(dev, + &sda_sf3_arrays_fan3[i].dev_attr); + if (err) + goto exit_remove; + } if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4) for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) { err = device_create_file(dev, @@ -2336,6 +2425,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) if (err) goto exit_remove; } + if (i == 2 && data->temp3_val_only) + continue; if (data->reg_temp_over[i]) { err = device_create_file(dev, &sda_temp_max[i].dev_attr); @@ -2419,6 +2510,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, static const char __initdata sio_name_W83627EHG[] = "W83627EHG"; static const char __initdata sio_name_W83627DHG[] = "W83627DHG"; static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P"; + static const char __initdata sio_name_W83627UHG[] = "W83627UHG"; static const char __initdata sio_name_W83667HG[] = "W83667HG"; static const char __initdata sio_name_W83667HG_B[] = "W83667HG-B"; static const char __initdata sio_name_NCT6775[] = "NCT6775F"; @@ -2451,6 +2543,10 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, sio_data->kind = w83627dhg_p; sio_name = sio_name_W83627DHG_P; break; + case SIO_W83627UHG_ID: + sio_data->kind = w83627uhg; + sio_name = sio_name_W83627UHG; + break; case SIO_W83667HG_ID: sio_data->kind = w83667hg; sio_name = sio_name_W83667HG; |