This patch adds support for NCT6775F and NCT6776F to the w83627ehf driver. Signed-off-by: Guenter Roeck <guenter.roeck@xxxxxxxxxxxx> Tested-by: Ian Dobson <i.dobson@xxxxxxxxxxxxxx> (for NCT6776F) --- Documentation/hwmon/w83627ehf | 59 +++- drivers/hwmon/w83627ehf.c | 791 +++++++++++++++++++++++++++++++++------- 2 files changed, 694 insertions(+), 156 deletions(-) diff --git a/Documentation/hwmon/w83627ehf b/Documentation/hwmon/w83627ehf index b634e98..76ffef9 100644 --- a/Documentation/hwmon/w83627ehf +++ b/Documentation/hwmon/w83627ehf @@ -22,6 +22,14 @@ Supported chips: Prefix: 'w83667hg' Addresses scanned: ISA address retrieved from Super I/O registers Datasheet: Available from Nuvoton upon request + * Nuvoton NCT6775F/W83667HG-I + Prefix: 'nct6775' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: Available from Nuvoton upon request + * Nuvoton NCT6776F + Prefix: 'nct6776' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: Available from Nuvoton upon request Authors: Jean Delvare <khali@xxxxxxxxxxxx> @@ -34,24 +42,28 @@ Description ----------- This driver implements support for the Winbond W83627EHF, W83627EHG, -W83627DHG, W83627DHG-P, W83667HG and W83667HG-B super I/O chips. -We will refer to them collectively as Winbond chips. - -The chips implement three temperature sensors (up to four for 667HG-B), -five fan rotation speed sensors, ten analog voltage sensors (only nine for the -627DHG), one VID (6 pins for the 627EHF/EHG, 8 pins for the 627DHG and 667HG), -alarms with beep warnings (control unimplemented), and some automatic fan -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. +W83627DHG, W83627DHG-P, W83667HG, W83667HG-B, W83667HG-I (NCT6775F), +and NCT6776F super I/O chips. We will refer to them collectively as +Winbond chips. + +The chips implement three temperature sensors (up to four for 667HG-B, and nine +for NCT6775F and NCT6776F), five fan rotation speed sensors, ten analog voltage +sensors (only nine for the 627DHG), one VID (6 pins for the 627EHF/EHG, 8 pins +for the 627DHG and 667HG), alarms with beep warnings (control unimplemented), +and some automatic fan regulation strategies (plus manual fan control mode). + +The temperature sensor sources on W82677HG-B, NCT6775F, and NCT6776F are +configurable. temp4 and higher attributes are only reported if its temperature +source differs from the temperature sources of the already reported temperature +sensors. The configured source for each of the temperature sensors is provided +in tempX_label. Temperatures are measured in degrees Celsius and measurement resolution is 1 -degC for temp1 and temp4, and 0.5 degC for temp2 and temp3. An alarm is -triggered when the temperature gets higher than high limit; it stays on until -the temperature falls below the hysteresis value. +degC for temp1 and and 0.5 degC for temp2 and temp3. For temp4 and higher, +resolution is 1 degC for W83667HG-B and 0.0 degC for NCT6775F and NCT6776F. +An alarm is triggered when the temperature gets higher than high limit; +it stays on until the temperature falls below the hysteresis value. +Alarms are only supported for temp1, temp2, and temp3. 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 @@ -83,7 +95,8 @@ prog -> pwm4 (not on 667HG and 667HG-B; the programmable setting is not name - this is a standard hwmon device entry. For the W83627EHF and W83627EHG, it is set to "w83627ehf", for the W83627DHG it is set to "w83627dhg", - and for the W83667HG it is set to "w83667hg". + for the W83667HG and W83667HG-B it is set to "w83667hg", for NCT6775F it + is set to "nct6775", and for NCT6776F it is set to "nct6776". pwm[1-4] - this file stores PWM duty cycle or DC value (fan speed) in range: 0 (stop) to 255 (full) @@ -93,6 +106,18 @@ pwm[1-4]_enable - this file controls mode of fan/temperature control: * 2 "Thermal Cruise" mode * 3 "Fan Speed Cruise" mode * 4 "Smart Fan III" mode + * 5 "Smart Fan IV" mode + + SmartFan III mode is not supported on NCT6776F. + + SmartFan IV mode is configurable only if it was configured at system + startup, and is only supported for W83677HG-B, NCT6775F, and NCT6776F. + SmartFan IV operational parameters can not be configured at this time, + and the various pwm attributes are not used in SmartFan IV mode. + The attributes can be written to, which is useful if you plan to + configure the system for a different pwm mode. However, the information + returned when reading pwm attributes is unrelated to SmartFan IV + operation. pwm[1-4]_mode - controls if output is PWM or DC level * 0 DC output (0 - 12v) diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 2f17f99..b3b4f2b 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -6,6 +6,7 @@ Rudolf Marek <r.marek@xxxxxxxxxxxx> David Hubbard <david.c.hubbard@xxxxxxxxx> Daniel J Blueman <daniel.blueman@xxxxxxxxx> + Copyright (C) 2010 Sheng-Yuan Huang (Nuvoton) (PS00) Shamelessly ripped from the w83627hf driver Copyright (C) 2003 Mark Studebaker @@ -40,6 +41,8 @@ w83627dhg-p 9 5 4 3 0xb070 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 + nct6776f 9 5 3 9 0xC330 0xc1 0x5ca3 */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -58,7 +61,8 @@ #include <linux/io.h> #include "lm75.h" -enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b }; +enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83667hg, w83667hg_b, nct6775, + nct6776 }; /* used to set data->name = w83627ehf_device_names[data->sio_kind] */ static const char * const w83627ehf_device_names[] = { @@ -67,6 +71,8 @@ static const char * const w83627ehf_device_names[] = { "w83627dhg", "w83667hg", "w83667hg", + "nct6775", + "nct6776", }; static unsigned short force_id; @@ -96,6 +102,8 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); #define SIO_W83627DHG_P_ID 0xb070 #define SIO_W83667HG_ID 0xa510 #define SIO_W83667HG_B_ID 0xb350 +#define SIO_NCT6775_ID 0xb470 +#define SIO_NCT6776_ID 0xc330 #define SIO_ID_MASK 0xFFF0 static inline void @@ -176,6 +184,10 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252, 0 }; #define W83627EHF_REG_DIODE 0x59 #define W83627EHF_REG_SMI_OVT 0x4C +/* NCT6775F has its own fan divider registers */ +#define NCT6775_REG_FANDIV1 0x506 +#define NCT6775_REG_FANDIV2 0x507 + #define W83627EHF_REG_ALARM1 0x459 #define W83627EHF_REG_ALARM2 0x45A #define W83627EHF_REG_ALARM3 0x45B @@ -214,6 +226,28 @@ static const u16 W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B[] = { 0x67, 0x69, 0x6b }; static const u16 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[] = { 0x68, 0x6a, 0x6c }; +static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301 }; +static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302 }; +static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = { 0x105, 0x205, 0x305 }; +static const u16 NCT6775_REG_FAN_START_OUTPUT[] = { 0x106, 0x206, 0x306 }; +static const u16 NCT6775_REG_FAN_STOP_TIME[] = { 0x107, 0x207, 0x307 }; +static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309 }; +static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a }; +static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b }; +static const u16 NCT6776_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; +static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642}; + +static const u16 NCT6775_REG_TEMP[] + = { 0x27, 0x150, 0x250, 0x73, 0x75, 0x77, 0x62b, 0x62c, 0x62d }; +static const u16 NCT6775_REG_TEMP_CONFIG[] + = { 0, 0x152, 0x252, 0, 0, 0, 0x628, 0x629, 0x62A }; +static const u16 NCT6775_REG_TEMP_HYST[] + = { 0x3a, 0x153, 0x253, 0, 0, 0, 0x673, 0x678, 0x67D }; +static const u16 NCT6775_REG_TEMP_OVER[] + = { 0x39, 0x155, 0x255, 0, 0, 0, 0x672, 0x677, 0x67C }; +static const u16 NCT6775_REG_TEMP_SOURCE[] + = { 0x621, 0x622, 0x623, 0x100, 0x200, 0x300, 0x624, 0x625, 0x626 }; + static const char *const w83667hg_b_temp_label[] = { "SYSTIN", "CPUTIN", @@ -225,15 +259,71 @@ static const char *const w83667hg_b_temp_label[] = { "PECI Agent 4" }; -#define NUM_REG_TEMP 4 +static const char *const nct6775_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN", + "AMD SB-TSI", + "PECI Agent 0", + "PECI Agent 1", + "PECI Agent 2", + "PECI Agent 3", + "PECI Agent 4", + "PECI Agent 5", + "PECI Agent 6", + "PECI Agent 7", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP" +}; + +static const char *const nct6776_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "SMBUSMASTER 2", + "SMBUSMASTER 3", + "SMBUSMASTER 4", + "SMBUSMASTER 5", + "SMBUSMASTER 6", + "SMBUSMASTER 7", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "PCH_DIM0_TEMP", + "PCH_DIM1_TEMP", + "PCH_DIM2_TEMP", + "PCH_DIM3_TEMP", + "BYTE_TEMP" +}; + +#define NUM_REG_TEMP ARRAY_SIZE(NCT6775_REG_TEMP) static inline int is_word_sized(u16 reg) { - return (((reg & 0xff00) == 0x100 + return ((((reg & 0xff00) == 0x100 || (reg & 0xff00) == 0x200) && ((reg & 0x00ff) == 0x50 || (reg & 0x00ff) == 0x53 - || (reg & 0x00ff) == 0x55)); + || (reg & 0x00ff) == 0x55)) + || (reg & 0xfff0) == 0x630 + || reg == 0x640 || reg == 0x642 + || ((reg & 0xfff0) == 0x650 + && (reg & 0x000f) >= 0x06) + || reg == 0x73 || reg == 0x75 || reg == 0x77 + ); } /* @@ -253,11 +343,20 @@ static inline u8 step_time_to_reg(unsigned int msec, u8 mode) } static inline unsigned int -fan_from_reg(u8 reg, unsigned int div) +fan_from_reg(int reg, u16 val, unsigned int div) { - if (reg == 0 || reg == 255) + if (val == 0) return 0; - return 1350000U / (reg * div); + if (is_word_sized(reg)) { + if ((val & 0xff1f) == 0xff1f) + return 0; + val = (val & 0x1f) | ((val & 0xff00) >> 3); + } else { + if (val == 255 || div == 0) + return 0; + val *= div; + } + return 1350000U / val; } static inline unsigned int @@ -274,7 +373,7 @@ temp_from_reg(u16 reg, s16 regval) return regval * 1000; } -static inline s16 +static inline u16 temp_to_reg(u16 reg, long temp) { if (is_word_sized(reg)) @@ -308,6 +407,10 @@ struct w83627ehf_data { struct device *hwmon_dev; struct mutex lock; + u16 reg_temp[NUM_REG_TEMP]; + u16 reg_temp_over[NUM_REG_TEMP]; + u16 reg_temp_hyst[NUM_REG_TEMP]; + u16 reg_temp_config[NUM_REG_TEMP]; u8 temp_src[NUM_REG_TEMP]; const char * const *temp_label; @@ -331,14 +434,15 @@ struct w83627ehf_data { u8 in[10]; /* Register value */ u8 in_max[10]; /* Register value */ u8 in_min[10]; /* Register value */ - u8 fan[5]; - u8 fan_min[5]; + u16 fan[5]; + u16 fan_min[5]; u8 fan_div[5]; u8 has_fan; /* some fan inputs can be disabled */ + u8 has_fan_min; /* some fans don't have min register */ u8 temp_type[3]; - s16 temp[4]; - s16 temp_max[4]; - s16 temp_max_hyst[4]; + s16 temp[9]; + s16 temp_max[9]; + s16 temp_max_hyst[9]; u32 alarms; u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */ @@ -364,7 +468,7 @@ struct w83627ehf_data { u8 vid; u8 vrm; - u8 have_temp; + u16 have_temp; u8 in6_skip; }; @@ -429,6 +533,34 @@ static int w83627ehf_write_value(struct w83627ehf_data *data, u16 reg, } /* This function assumes that the caller holds data->update_lock */ +static void nct6775_write_fan_div(struct w83627ehf_data *data, int nr) +{ + u8 reg; + + switch (nr) { + case 0: + reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV1) & 0x70) + | (data->fan_div[0] & 0x7); + w83627ehf_write_value(data, NCT6775_REG_FANDIV1, reg); + break; + case 1: + reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV1) & 0x7) + | ((data->fan_div[1] << 4) & 0x70); + w83627ehf_write_value(data, NCT6775_REG_FANDIV1, reg); + case 2: + reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV2) & 0x70) + | (data->fan_div[2] & 0x7); + w83627ehf_write_value(data, NCT6775_REG_FANDIV2, reg); + break; + case 3: + reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV2) & 0x7) + | ((data->fan_div[3] << 4) & 0x70); + w83627ehf_write_value(data, NCT6775_REG_FANDIV2, reg); + break; + } +} + +/* This function assumes that the caller holds data->update_lock */ static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr) { u8 reg; @@ -479,6 +611,32 @@ static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr) } } +static void w83627ehf_write_fan_div_common(struct device *dev, + struct w83627ehf_data *data, int nr) +{ + struct w83627ehf_sio_data *sio_data = dev->platform_data; + + if (sio_data->kind == nct6776) + ; /* no dividers, do nothing */ + else if (sio_data->kind == nct6775) + nct6775_write_fan_div(data, nr); + else + w83627ehf_write_fan_div(data, nr); +} + +static void nct6775_update_fan_div(struct w83627ehf_data *data) +{ + u8 i; + + i = w83627ehf_read_value(data, NCT6775_REG_FANDIV1); + data->fan_div[0] = i & 0x7; + data->fan_div[1] = (i & 0x70) >> 4; + i = w83627ehf_read_value(data, NCT6775_REG_FANDIV2); + data->fan_div[2] = i & 0x7; + if (data->has_fan & (1<<3)) + data->fan_div[3] = (i & 0x70) >> 4; +} + static void w83627ehf_update_fan_div(struct w83627ehf_data *data) { int i; @@ -504,10 +662,79 @@ static void w83627ehf_update_fan_div(struct w83627ehf_data *data) } } +static void w83627ehf_update_fan_div_common(struct device *dev, + struct w83627ehf_data *data) +{ + struct w83627ehf_sio_data *sio_data = dev->platform_data; + + if (sio_data->kind == nct6776) + ; /* no dividers, do nothing */ + else if (sio_data->kind == nct6775) + nct6775_update_fan_div(data); + else + w83627ehf_update_fan_div(data); +} + +static void nct6775_update_pwm(struct w83627ehf_data *data) +{ + int i; + int pwmcfg, fanmodecfg; + + for (i = 0; i < data->pwm_num; i++) { + pwmcfg = w83627ehf_read_value(data, + W83627EHF_REG_PWM_ENABLE[i]); + fanmodecfg = w83627ehf_read_value(data, + NCT6775_REG_FAN_MODE[i]); + data->pwm_mode[i] = + ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1; + data->pwm_enable[i] = ((fanmodecfg >> 4) & 7) + 1; + data->tolerance[i] = fanmodecfg & 0x0f; + data->pwm[i] = w83627ehf_read_value(data, data->REG_PWM[i]); + } +} + +static void w83627ehf_update_pwm(struct w83627ehf_data *data) +{ + int i; + int pwmcfg = 0, tolerance = 0; /* shut up the compiler */ + + for (i = 0; i < data->pwm_num; i++) { + if (!(data->has_fan & (1 << i))) + continue; + + /* pwmcfg, tolerance mapped for i=0, i=1 to same reg */ + if (i != 1) { + pwmcfg = w83627ehf_read_value(data, + W83627EHF_REG_PWM_ENABLE[i]); + tolerance = w83627ehf_read_value(data, + W83627EHF_REG_TOLERANCE[i]); + } + data->pwm_mode[i] = + ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1; + data->pwm_enable[i] = ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i]) + & 3) + 1; + data->pwm[i] = w83627ehf_read_value(data, data->REG_PWM[i]); + + data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0)) & 0x0f; + } +} + +static void w83627ehf_update_pwm_common(struct device *dev, + struct w83627ehf_data *data) +{ + struct w83627ehf_sio_data *sio_data = dev->platform_data; + + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) + nct6775_update_pwm(data); + else + w83627ehf_update_pwm(data); +} + static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) { struct w83627ehf_data *data = dev_get_drvdata(dev); - int pwmcfg = 0, tolerance = 0; /* shut up the compiler */ + struct w83627ehf_sio_data *sio_data = dev->platform_data; + int i; mutex_lock(&data->update_lock); @@ -515,7 +742,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ + HZ/2) || !data->valid) { /* Fan clock dividers */ - w83627ehf_update_fan_div(data); + w83627ehf_update_fan_div_common(dev, data); /* Measured voltages and limits */ for (i = 0; i < data->in_num; i++) { @@ -533,23 +760,29 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) continue; data->fan[i] = w83627ehf_read_value(data, - data->REG_FAN[i]); - data->fan_min[i] = w83627ehf_read_value(data, + data->REG_FAN[i]); + + if (data->has_fan_min & (1 << i)) + data->fan_min[i] = w83627ehf_read_value(data, data->REG_FAN_MIN[i]); /* If we failed to measure the fan speed and clock divider can be increased, let's try that for next time */ - if (data->fan[i] == 0xff - && data->fan_div[i] < 0x07) { + if (!is_word_sized(data->REG_FAN[i]) + && (data->fan[i] == 0xff + || (sio_data->kind == nct6775 + && data->fan[i] == 0x00)) + && data->fan_div[i] < 0x07) { dev_dbg(dev, "Increasing fan%d " "clock divider from %u to %u\n", i + 1, div_from_reg(data->fan_div[i]), div_from_reg(data->fan_div[i] + 1)); data->fan_div[i]++; - w83627ehf_write_fan_div(data, i); + w83627ehf_write_fan_div_common(dev, data, i); /* Preserve min limit if possible */ - if (data->fan_min[i] >= 2 + if ((data->has_fan_min & (1 << i)) + && data->fan_min[i] >= 2 && data->fan_min[i] != 255) w83627ehf_write_value(data, data->REG_FAN_MIN[i], @@ -557,64 +790,54 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) } } + w83627ehf_update_pwm_common(dev, data); + for (i = 0; i < data->pwm_num; i++) { if (!(data->has_fan & (1 << i))) continue; - /* pwmcfg, tolerance mapped for i=0, i=1 to same reg */ - if (i != 1) { - pwmcfg = w83627ehf_read_value(data, - W83627EHF_REG_PWM_ENABLE[i]); - tolerance = w83627ehf_read_value(data, - W83627EHF_REG_TOLERANCE[i]); - } - data->pwm_mode[i] = - ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) - ? 0 : 1; - data->pwm_enable[i] = - ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i]) - & 3) + 1; - data->pwm[i] = w83627ehf_read_value(data, - data->REG_PWM[i]); - data->fan_start_output[i] = w83627ehf_read_value(data, - data->REG_FAN_START_OUTPUT[i]); - data->fan_stop_output[i] = w83627ehf_read_value(data, - data->REG_FAN_STOP_OUTPUT[i]); - data->fan_stop_time[i] = w83627ehf_read_value(data, - data->REG_FAN_STOP_TIME[i]); - - if (data->REG_FAN_MAX_OUTPUT[i] != 0xff) + data->fan_start_output[i] = + w83627ehf_read_value(data, + data->REG_FAN_START_OUTPUT[i]); + data->fan_stop_output[i] = + w83627ehf_read_value(data, + data->REG_FAN_STOP_OUTPUT[i]); + data->fan_stop_time[i] = + w83627ehf_read_value(data, + data->REG_FAN_STOP_TIME[i]); + + if (data->REG_FAN_MAX_OUTPUT && + data->REG_FAN_MAX_OUTPUT[i] != 0xff) data->fan_max_output[i] = w83627ehf_read_value(data, - data->REG_FAN_MAX_OUTPUT[i]); + data->REG_FAN_MAX_OUTPUT[i]); - if (data->REG_FAN_STEP_OUTPUT[i] != 0xff) + if (data->REG_FAN_STEP_OUTPUT && + data->REG_FAN_STEP_OUTPUT[i] != 0xff) data->fan_step_output[i] = w83627ehf_read_value(data, - data->REG_FAN_STEP_OUTPUT[i]); + data->REG_FAN_STEP_OUTPUT[i]); data->target_temp[i] = w83627ehf_read_value(data, data->REG_TARGET[i]) & (data->pwm_mode[i] == 1 ? 0x7f : 0xff); - data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0)) - & 0x0f; } /* Measured temperatures and limits */ for (i = 0; i < NUM_REG_TEMP; i++) { if (!(data->have_temp & (1 << i))) continue; - data->temp[i] - = w83627ehf_read_value(data, W83627EHF_REG_TEMP[i]); - 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]); + data->temp[i] = w83627ehf_read_value(data, + data->reg_temp[i]); + if (data->reg_temp_over[i]) + data->temp_max[i] + = w83627ehf_read_value(data, + data->reg_temp_over[i]); + if (data->reg_temp_hyst[i]) + data->temp_max_hyst[i] + = w83627ehf_read_value(data, + data->reg_temp_hyst[i]); } data->alarms = w83627ehf_read_value(data, @@ -736,21 +959,29 @@ static struct sensor_device_attribute sda_in_max[] = { SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9), }; -#define show_fan_reg(reg) \ -static ssize_t \ -show_##reg(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, "%d\n", \ - fan_from_reg(data->reg[nr], \ - div_from_reg(data->fan_div[nr]))); \ +static ssize_t +show_fan(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, "%d\n", + fan_from_reg(data->REG_FAN[nr], + data->fan[nr], + div_from_reg(data->fan_div[nr]))); +} + +static ssize_t +show_fan_min(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, "%d\n", + fan_from_reg(data->REG_FAN_MIN[nr], + data->fan_min[nr], + div_from_reg(data->fan_div[nr]))); } -show_fan_reg(fan); -show_fan_reg(fan_min); static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, @@ -779,6 +1010,18 @@ store_fan_min(struct device *dev, struct device_attribute *attr, return err; mutex_lock(&data->update_lock); + if (is_word_sized(data->REG_FAN_MIN[nr])) { + if (!val) { + val = 0xff1f; + } else { + if (val > 1350000U) + val = 135000U; + val = 1350000U / val; + val = (val & 0x1f) | ((val << 3) & 0xff00); + } + data->fan_min[nr] = val; + goto done; /* Leave fan divider alone */ + } if (!val) { /* No min limit, alarm disabled */ data->fan_min[nr] = 255; @@ -790,14 +1033,16 @@ store_fan_min(struct device *dev, struct device_attribute *attr, data->fan_min[nr] = 254; new_div = 7; /* 128 == (1 << 7) */ dev_warn(dev, "fan%u low limit %lu below minimum %u, set to " - "minimum\n", nr + 1, val, fan_from_reg(254, 128)); + "minimum\n", nr + 1, val, + fan_from_reg(data->REG_FAN_MIN[nr], 254, 128)); } else if (!reg) { /* Speed above this value cannot possibly be represented, even with the lowest divider (1) */ data->fan_min[nr] = 1; new_div = 0; /* 1 == (1 << 0) */ dev_warn(dev, "fan%u low limit %lu above maximum %u, set to " - "maximum\n", nr + 1, val, fan_from_reg(1, 1)); + "maximum\n", nr + 1, val, + fan_from_reg(data->REG_FAN_MIN[nr], 1, 1)); } else { /* Automatically pick the best divider, i.e. the one such that the min limit will correspond to a register value @@ -827,10 +1072,11 @@ store_fan_min(struct device *dev, struct device_attribute *attr, nr + 1, div_from_reg(data->fan_div[nr]), div_from_reg(new_div)); data->fan_div[nr] = new_div; - w83627ehf_write_fan_div(data, nr); + w83627ehf_write_fan_div_common(dev, data, nr); /* Give the chip time to sample a new speed value */ data->last_updated = jiffies; } +done: w83627ehf_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]); mutex_unlock(&data->update_lock); @@ -884,7 +1130,7 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); } -#define show_temp_reg(REG, reg) \ +#define show_temp_reg(addr, reg) \ static ssize_t \ show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ @@ -894,13 +1140,13 @@ show_##reg(struct device *dev, struct device_attribute *attr, \ to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ return sprintf(buf, "%d\n", \ - temp_from_reg(W83627EHF_REG_##REG[nr], data->reg[nr])); \ + temp_from_reg(data->addr[nr], data->reg[nr])); \ } -show_temp_reg(TEMP, temp); -show_temp_reg(TEMP_OVER, temp_max); -show_temp_reg(TEMP_HYST, temp_max_hyst); +show_temp_reg(reg_temp, temp); +show_temp_reg(reg_temp_over, temp_max); +show_temp_reg(reg_temp_hyst, temp_max_hyst); -#define store_temp_reg(REG, reg) \ +#define store_temp_reg(addr, reg) \ static ssize_t \ store_##reg(struct device *dev, struct device_attribute *attr, \ const char *buf, size_t count) \ @@ -915,14 +1161,14 @@ store_##reg(struct device *dev, struct device_attribute *attr, \ if (err < 0) \ return err; \ mutex_lock(&data->update_lock); \ - data->reg[nr] = temp_to_reg(W83627EHF_REG_TEMP_##REG[nr], val); \ - w83627ehf_write_value(data, W83627EHF_REG_TEMP_##REG[nr], \ + data->reg[nr] = temp_to_reg(data->addr[nr], val); \ + w83627ehf_write_value(data, data->addr[nr], \ data->reg[nr]); \ mutex_unlock(&data->update_lock); \ return count; \ } -store_temp_reg(OVER, temp_max); -store_temp_reg(HYST, temp_max_hyst); +store_temp_reg(reg_temp_over, temp_max); +store_temp_reg(reg_temp_hyst, temp_max_hyst); static ssize_t show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) @@ -938,6 +1184,11 @@ static struct sensor_device_attribute sda_temp_input[] = { SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1), SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2), SENSOR_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3), + SENSOR_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4), + SENSOR_ATTR(temp6_input, S_IRUGO, show_temp, NULL, 5), + SENSOR_ATTR(temp7_input, S_IRUGO, show_temp, NULL, 6), + SENSOR_ATTR(temp8_input, S_IRUGO, show_temp, NULL, 7), + SENSOR_ATTR(temp9_input, S_IRUGO, show_temp, NULL, 8), }; static struct sensor_device_attribute sda_temp_label[] = { @@ -945,6 +1196,11 @@ static struct sensor_device_attribute sda_temp_label[] = { 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), + SENSOR_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4), + SENSOR_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5), + SENSOR_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6), + SENSOR_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7), + SENSOR_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8), }; static struct sensor_device_attribute sda_temp_max[] = { @@ -954,6 +1210,18 @@ static struct sensor_device_attribute sda_temp_max[] = { store_temp_max, 1), SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max, store_temp_max, 2), + SENSOR_ATTR(temp4_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 3), + SENSOR_ATTR(temp5_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 4), + SENSOR_ATTR(temp6_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 5), + SENSOR_ATTR(temp7_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 6), + SENSOR_ATTR(temp8_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 7), + SENSOR_ATTR(temp9_max, S_IRUGO | S_IWUSR, show_temp_max, + store_temp_max, 8), }; static struct sensor_device_attribute sda_temp_max_hyst[] = { @@ -963,6 +1231,18 @@ static struct sensor_device_attribute sda_temp_max_hyst[] = { store_temp_max_hyst, 1), SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 2), + SENSOR_ATTR(temp4_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 3), + SENSOR_ATTR(temp5_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 4), + SENSOR_ATTR(temp6_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 5), + SENSOR_ATTR(temp7_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 6), + SENSOR_ATTR(temp8_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 7), + SENSOR_ATTR(temp9_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 8), }; static struct sensor_device_attribute sda_temp_alarm[] = { @@ -1048,6 +1328,7 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83627ehf_data *data = dev_get_drvdata(dev); + struct w83627ehf_sio_data *sio_data = dev->platform_data; struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; unsigned long val; @@ -1060,12 +1341,25 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, if (!val || (val > 4 && val != data->pwm_enable_orig[nr])) return -EINVAL; + /* SmartFan III mode is not supported on NCT6776F */ + if (sio_data->kind == nct6776 && val == 4) + return -EINVAL; + mutex_lock(&data->update_lock); - reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); data->pwm_enable[nr] = val; - reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]); - reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr]; - w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[nr], reg); + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { + reg = w83627ehf_read_value(data, + NCT6775_REG_FAN_MODE[nr]); + reg &= 0x0f; + reg |= (val - 1) << 4; + w83627ehf_write_value(data, + NCT6775_REG_FAN_MODE[nr], reg); + } else { + reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); + reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]); + reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr]; + w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[nr], reg); + } mutex_unlock(&data->update_lock); return count; } @@ -1113,6 +1407,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83627ehf_data *data = dev_get_drvdata(dev); + struct w83627ehf_sio_data *sio_data = dev->platform_data; struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; u16 reg; @@ -1127,13 +1422,22 @@ store_tolerance(struct device *dev, struct device_attribute *attr, val = SENSORS_LIMIT(DIV_ROUND_CLOSEST(val, 1000), 0, 15); mutex_lock(&data->update_lock); - reg = w83627ehf_read_value(data, W83627EHF_REG_TOLERANCE[nr]); - data->tolerance[nr] = val; - if (nr == 1) - reg = (reg & 0x0f) | (val << 4); - else + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { + /* Limit tolerance further for NCT6776F */ + if (sio_data->kind == nct6776 && val > 7) + val = 7; + reg = w83627ehf_read_value(data, NCT6775_REG_FAN_MODE[nr]); reg = (reg & 0xf0) | val; - w83627ehf_write_value(data, W83627EHF_REG_TOLERANCE[nr], reg); + w83627ehf_write_value(data, NCT6775_REG_FAN_MODE[nr], reg); + } else { + reg = w83627ehf_read_value(data, W83627EHF_REG_TOLERANCE[nr]); + if (nr == 1) + reg = (reg & 0x0f) | (val << 4); + else + reg = (reg & 0xf0) | val; + w83627ehf_write_value(data, W83627EHF_REG_TOLERANCE[nr], reg); + } + data->tolerance[nr] = val; mutex_unlock(&data->update_lock); return count; } @@ -1350,7 +1654,8 @@ static void w83627ehf_device_remove_files(struct device *dev) for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) { struct sensor_device_attribute *attr = &sda_sf3_max_step_arrays[i]; - if (data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) + if (data->REG_FAN_STEP_OUTPUT && + data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) device_remove_file(dev, &attr->dev_attr); } for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) @@ -1381,10 +1686,10 @@ 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) - break; device_remove_file(dev, &sda_temp_max[i].dev_attr); device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); + if (i > 2) + continue; device_remove_file(dev, &sda_temp_alarm[i].dev_attr); device_remove_file(dev, &sda_temp_type[i].dev_attr); } @@ -1409,13 +1714,13 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data) for (i = 0; i < NUM_REG_TEMP; i++) { if (!(data->have_temp & (1 << i))) continue; - if (!W83627EHF_REG_TEMP_CONFIG[i]) + if (!data->reg_temp_config[i]) continue; tmp = w83627ehf_read_value(data, - W83627EHF_REG_TEMP_CONFIG[i]); + data->reg_temp_config[i]); if (tmp & 0x01) w83627ehf_write_value(data, - W83627EHF_REG_TEMP_CONFIG[i], + data->reg_temp_config[i], tmp & 0xfe); } @@ -1434,13 +1739,39 @@ static inline void __devinit w83627ehf_init_device(struct w83627ehf_data *data) } } +static void w82627ehf_swap_tempreg(struct w83627ehf_data *data, + int r1, int r2) +{ + u16 tmp; + + tmp = data->temp_src[r1]; + data->temp_src[r1] = data->temp_src[r2]; + data->temp_src[r2] = tmp; + + tmp = data->reg_temp[r1]; + data->reg_temp[r1] = data->reg_temp[r2]; + data->reg_temp[r2] = tmp; + + tmp = data->reg_temp_over[r1]; + data->reg_temp_over[r1] = data->reg_temp_over[r2]; + data->reg_temp_over[r2] = tmp; + + tmp = data->reg_temp_hyst[r1]; + data->reg_temp_hyst[r1] = data->reg_temp_hyst[r2]; + data->reg_temp_hyst[r2] = tmp; + + tmp = data->reg_temp_config[r1]; + data->reg_temp_config[r1] = data->reg_temp_config[r2]; + data->reg_temp_config[r2] = tmp; +} + static int __devinit w83627ehf_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct w83627ehf_sio_data *sio_data = dev->platform_data; struct w83627ehf_data *data; struct resource *res; - u8 fan4pin, fan5pin, en_vrm10; + u8 fan3pin, fan4pin, fan4min, fan5pin, en_vrm10; int i, err = 0; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1466,9 +1797,11 @@ 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 has 3 pwms */ + /* 667HG, NCT6775F, and NCT6776F have 3 pwms */ data->pwm_num = (sio_data->kind == w83667hg - || sio_data->kind == w83667hg_b) ? 3 : 4; + || sio_data->kind == w83667hg_b + || sio_data->kind == nct6775 + || sio_data->kind == nct6776) ? 3 : 4; data->have_temp = 0x07; /* Check temp3 configuration bit for 667HG */ @@ -1479,15 +1812,98 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) if (reg & 0x01) data->have_temp &= ~(1 << 2); else - data->in6_skip = 1; /* Either temp3 or in6 */ + data->in6_skip = 1; /* either temp3 or in6 */ + } + + /* Deal with temperature register setup first. */ + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { + int mask = 0; + + /* + * Display temperature sensor output only if it monitors + * a source other than one already reported. Always display + * first three temperature registers, though. + */ + for (i = 0; i < NUM_REG_TEMP; i++) { + u8 src; + + data->reg_temp[i] = NCT6775_REG_TEMP[i]; + data->reg_temp_over[i] = NCT6775_REG_TEMP_OVER[i]; + data->reg_temp_hyst[i] = NCT6775_REG_TEMP_HYST[i]; + data->reg_temp_config[i] = NCT6775_REG_TEMP_CONFIG[i]; + + src = w83627ehf_read_value(data, + NCT6775_REG_TEMP_SOURCE[i]); + src &= 0x1f; + if (src && !(mask & (1 << src))) { + data->have_temp |= 1 << i; + mask |= 1 << src; + } + + data->temp_src[i] = src; + + /* + * Now do some register swapping if index 0..2 don't + * point to SYSTIN(1), CPUIN(2), and AUXIN(3). + * Idea is to have the first three attributes + * report SYSTIN, CPUIN, and AUXIN if possible + * without overriding the basic system configuration. + */ + if (i > 0 && data->temp_src[0] != 1 + && data->temp_src[i] == 1) + w82627ehf_swap_tempreg(data, 0, i); + if (i > 1 && data->temp_src[1] != 2 + && data->temp_src[i] == 2) + w82627ehf_swap_tempreg(data, 1, i); + if (i > 2 && data->temp_src[2] != 3 + && data->temp_src[i] == 3) + w82627ehf_swap_tempreg(data, 2, i); + } + if (sio_data->kind == nct6776) { + /* + * On NCT6776, AUXTIN and VIN3 pins are shared. + * Only way to detect it is to check if AUXTIN is used + * as a temperature source, and if that source is + * enabled. + * + * If that is the case, disable in6, which reports VIN3. + * Otherwise disable temp3. + */ + if (data->temp_src[2] == 3) { + u8 reg; + + if (data->reg_temp_config[2]) + reg = w83627ehf_read_value(data, + data->reg_temp_config[2]); + else + reg = 0; /* Assume AUXTIN is used */ + + if (reg & 0x01) + data->have_temp &= ~(1 << 2); + else + data->in6_skip = 1; + } + } + + data->temp_label = nct6776_temp_label; } else if (sio_data->kind == w83667hg_b) { u8 reg; + /* + * Temperature sources are selected with bank 0, registers 0x49 + * and 0x4a. + */ + for (i = 0; i < ARRAY_SIZE(W83627EHF_REG_TEMP); i++) { + data->reg_temp[i] = W83627EHF_REG_TEMP[i]; + data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i]; + data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i]; + data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i]; + } 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; + data->temp_src[2] = (reg >> 4) & 0x07; /* * W83667HG-B has another temperature register at 0x7e. @@ -1516,22 +1932,54 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) data->in6_skip = 1; data->temp_label = w83667hg_b_temp_label; + } else { + /* Temperature sources are fixed */ + for (i = 0; i < 3; i++) { + data->reg_temp[i] = W83627EHF_REG_TEMP[i]; + data->reg_temp_over[i] = W83627EHF_REG_TEMP_OVER[i]; + data->reg_temp_hyst[i] = W83627EHF_REG_TEMP_HYST[i]; + data->reg_temp_config[i] = W83627EHF_REG_TEMP_CONFIG[i]; + } } - data->REG_PWM = W83627EHF_REG_PWM; - data->REG_TARGET = W83627EHF_REG_TARGET; - data->REG_FAN = W83627EHF_REG_FAN; - data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; - data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT; - data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT; - data->REG_FAN_STOP_TIME = W83627EHF_REG_FAN_STOP_TIME; - data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT; - if (sio_data->kind == w83667hg_b) { + if (sio_data->kind == nct6775) { + data->REG_PWM = NCT6775_REG_PWM; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = W83627EHF_REG_FAN; + data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; + data->REG_FAN_START_OUTPUT = NCT6775_REG_FAN_START_OUTPUT; + data->REG_FAN_STOP_OUTPUT = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_FAN_STOP_TIME = NCT6775_REG_FAN_STOP_TIME; + data->REG_FAN_MAX_OUTPUT = NCT6775_REG_FAN_MAX_OUTPUT; + data->REG_FAN_STEP_OUTPUT = NCT6775_REG_FAN_STEP_OUTPUT; + } else if (sio_data->kind == nct6776) { + data->REG_PWM = NCT6775_REG_PWM; + data->REG_TARGET = NCT6775_REG_TARGET; + data->REG_FAN = NCT6776_REG_FAN; + data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; + data->REG_FAN_START_OUTPUT = NCT6775_REG_FAN_START_OUTPUT; + data->REG_FAN_STOP_OUTPUT = NCT6775_REG_FAN_STOP_OUTPUT; + data->REG_FAN_STOP_TIME = NCT6775_REG_FAN_STOP_TIME; + } else if (sio_data->kind == w83667hg_b) { + data->REG_PWM = W83627EHF_REG_PWM; + data->REG_TARGET = W83627EHF_REG_TARGET; + data->REG_FAN = W83627EHF_REG_FAN; + data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; + data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT; + data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT; + data->REG_FAN_STOP_TIME = W83627EHF_REG_FAN_STOP_TIME; data->REG_FAN_MAX_OUTPUT = W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B; data->REG_FAN_STEP_OUTPUT = W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B; } else { + data->REG_PWM = W83627EHF_REG_PWM; + data->REG_TARGET = W83627EHF_REG_TARGET; + data->REG_FAN = W83627EHF_REG_FAN; + data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; + data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT; + data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT; + data->REG_FAN_STOP_TIME = W83627EHF_REG_FAN_STOP_TIME; data->REG_FAN_MAX_OUTPUT = W83627EHF_REG_FAN_MAX_OUTPUT_COMMON; data->REG_FAN_STEP_OUTPUT = @@ -1544,7 +1992,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) data->vrm = vid_which_vrm(); superio_enter(sio_data->sioreg); /* Read VID value */ - if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { + if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b || + sio_data->kind == nct6775 || sio_data->kind == nct6776) { /* W83667HG has different pins for VID input and output, so we can get the VID input values directly at logical device D 0xe3. */ @@ -1595,12 +2044,27 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) } /* fan4 and fan5 share some pins with the GPIO and serial flash */ - if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { - fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; + if (sio_data->kind == nct6775) { + /* On NCT6775, fan4 shares pins with the fdc interface */ + fan3pin = 1; + fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); + fan4min = 0; + fan5pin = 0; + } else if (sio_data->kind == nct6776) { + fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); + fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); + fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); + fan4min = fan4pin; + } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { + fan3pin = 1; fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; + fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; + fan4min = fan4pin; } else { - fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); + fan3pin = 1; fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); + fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); + fan4min = fan4pin; } superio_exit(sio_data->sioreg); @@ -1610,15 +2074,36 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) connected fan5 as input unless they are emitting log 1, which is not the default. */ - data->has_fan = 0x07; /* fan1, fan2 and fan3 */ - i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); - if ((i & (1 << 2)) && fan4pin) - data->has_fan |= (1 << 3); - if (!(i & (1 << 1)) && fan5pin) - data->has_fan |= (1 << 4); + data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ + + data->has_fan |= (fan3pin << 2); + data->has_fan_min |= (fan3pin << 2); + + /* + * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1 register + */ + if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { + data->has_fan |= (fan4pin << 3) | (fan5pin << 4); + data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); + } else { + i = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); + if ((i & (1 << 2)) && fan4pin) { + data->has_fan |= (1 << 3); + data->has_fan_min |= (1 << 3); + } + if (!(i & (1 << 1)) && fan5pin) { + data->has_fan |= (1 << 4); + data->has_fan_min |= (1 << 4); + } + } /* Read fan clock dividers immediately */ - w83627ehf_update_fan_div(data); + w83627ehf_update_fan_div_common(dev, data); + + /* Read pwm data to save original values */ + w83627ehf_update_pwm_common(dev, data); + for (i = 0; i < data->pwm_num; i++) + data->pwm_enable_orig[i] = data->pwm_enable[i]; /* Read pwm data to save original values */ w83627ehf_update_pwm_common(dev, data); @@ -1635,7 +2120,8 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) { struct sensor_device_attribute *attr = &sda_sf3_max_step_arrays[i]; - if (data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) { + if (data->REG_FAN_STEP_OUTPUT && + data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) { err = device_create_file(dev, &attr->dev_attr); if (err) goto exit_remove; @@ -1668,12 +2154,20 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) if ((err = device_create_file(dev, &sda_fan_input[i].dev_attr)) || (err = device_create_file(dev, - &sda_fan_alarm[i].dev_attr)) - || (err = device_create_file(dev, - &sda_fan_div[i].dev_attr)) - || (err = device_create_file(dev, - &sda_fan_min[i].dev_attr))) + &sda_fan_alarm[i].dev_attr))) goto exit_remove; + if (sio_data->kind != nct6776) { + err = device_create_file(dev, + &sda_fan_div[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->has_fan_min & (1 << i)) { + err = device_create_file(dev, + &sda_fan_min[i].dev_attr); + if (err) + goto exit_remove; + } if (i < data->pwm_num && ((err = device_create_file(dev, &sda_pwm[i].dev_attr)) @@ -1701,12 +2195,21 @@ static int __devinit w83627ehf_probe(struct platform_device *pdev) if (err) goto exit_remove; } + if (data->reg_temp_over[i]) { + err = device_create_file(dev, + &sda_temp_max[i].dev_attr); + if (err) + goto exit_remove; + } + if (data->reg_temp_hyst[i]) { + err = device_create_file(dev, + &sda_temp_max_hyst[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, - &sda_temp_max_hyst[i].dev_attr)) - || (err = device_create_file(dev, + continue; + if ((err = device_create_file(dev, &sda_temp_alarm[i].dev_attr)) || (err = device_create_file(dev, &sda_temp_type[i].dev_attr))) @@ -1767,6 +2270,8 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, static const char __initdata sio_name_W83627DHG_P[] = "W83627DHG-P"; 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"; + static const char __initdata sio_name_NCT6776[] = "NCT6776F"; u16 val; const char *sio_name; @@ -1803,6 +2308,14 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, sio_data->kind = w83667hg_b; sio_name = sio_name_W83667HG_B; break; + case SIO_NCT6775_ID: + sio_data->kind = nct6775; + sio_name = sio_name_NCT6775; + break; + case SIO_NCT6776_ID: + sio_data->kind = nct6776; + sio_name = sio_name_NCT6776; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); -- 1.7.3.1 _______________________________________________ lm-sensors mailing list lm-sensors@xxxxxxxxxxxxxx http://lists.lm-sensors.org/mailman/listinfo/lm-sensors