X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=nct6775.c;h=c0c03249b5ec9473bbab2f3d00c7246cfdf0aa40;hb=cf0541c0b9557045ec4b0e6b4314caac28f28fc5;hp=64a2362a0e65b6f4c787b0f622875c6c5a7cf538;hpb=069a44e1a13e9b8a25186a95c37ceaf18357f1c2;p=groeck-nct6775 diff --git a/nct6775.c b/nct6775.c index 64a2362..c0c0324 100644 --- a/nct6775.c +++ b/nct6775.c @@ -5,7 +5,7 @@ * Copyright (C) 2012 Guenter Roeck * * Derived from w83627ehf driver - * Copyright (C) 2005-2012 Jean Delvare + * Copyright (C) 2005-2012 Jean Delvare * Copyright (C) 2006 Yuan Mu (Winbond), * Rudolf Marek * David Hubbard @@ -38,6 +38,8 @@ * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 * nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3 + * nct6792d 15 6 6 2+6 0xc911 0xc1 0x5ca3 + * nct6793d 15 6 6 2+6 0xd120 0xc1 0x5ca3 * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value). @@ -56,13 +58,14 @@ #include #include #include +#include #include #include "lm75.h" #include "compat.h" #define USE_ALTERNATE -enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 }; +enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { @@ -71,6 +74,8 @@ static const char * const nct6775_device_names[] = { "nct6776", "nct6779", "nct6791", + "nct6792", + "nct6793", }; static unsigned short force_id; @@ -101,6 +106,8 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_NCT6776_ID 0xc330 #define SIO_NCT6779_ID 0xc560 #define SIO_NCT6791_ID 0xc800 +#define SIO_NCT6792_ID 0xc910 +#define SIO_NCT6793_ID 0xd120 #define SIO_ID_MASK 0xFFF0 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; @@ -249,6 +256,7 @@ static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301, 0x801, 0x901, 0xa01 }; static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302, 0x802, 0x902, 0xa02 }; +/* STEP_DOWN_TIME and STEP_UP_TIME regs are swapped for all chips but NCT6775 */ static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = { 0x103, 0x203, 0x303, 0x803, 0x903, 0xa03 }; static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = { @@ -495,22 +503,37 @@ static const char *const nct6779_temp_label[] = { "PCH_DIM1_TEMP", "PCH_DIM2_TEMP", "PCH_DIM3_TEMP", - "BYTE_TEMP" + "BYTE_TEMP", + "", + "", + "", + "", + "Virtual_TEMP" }; -static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1] +#define NCT6779_NUM_LABELS (ARRAY_SIZE(nct6779_temp_label) - 5) +#define NCT6791_NUM_LABELS ARRAY_SIZE(nct6779_temp_label) + +static const u16 NCT6779_REG_TEMP_ALTERNATE[NCT6791_NUM_LABELS - 1] = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407, 0x408, 0 }; -static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] +static const u16 NCT6779_REG_TEMP_CRIT[NCT6791_NUM_LABELS - 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; /* NCT6791 specific data */ #define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28 +static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[6] = { 0, 0x239 }; +static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[6] = { 0, 0x23a }; +static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[6] = { 0, 0x23b }; +static const u16 NCT6791_REG_WEIGHT_DUTY_STEP[6] = { 0, 0x23c }; +static const u16 NCT6791_REG_WEIGHT_TEMP_BASE[6] = { 0, 0x23d }; +static const u16 NCT6791_REG_WEIGHT_DUTY_BASE[6] = { 0, 0x23e }; + static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B, 0x568, 0x45D }; @@ -523,6 +546,12 @@ static const s8 NCT6791_ALARM_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, 9 }; /* intrusion0, intrusion1 */ +/* NCT6792/NCT6793 specific data */ + +static const u16 NCT6792_REG_TEMP_MON[] = { + 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d }; +static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = { + 0xb2, 0xb3, 0xb4, 0xb5, 0xbf }; /* NCT6102D/NCT6106D specific data */ @@ -729,11 +758,11 @@ struct nct6775_data { enum kinds kind; const char *name; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) struct device *hwmon_dev; - struct attribute_group *group_in; - struct attribute_group *group_fan; - struct attribute_group *group_temp; - struct attribute_group *group_pwm; +#endif + + const struct attribute_group *groups[6]; u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, * 3=temp_crit, 4=temp_lcrit @@ -869,12 +898,12 @@ struct nct6775_data { u16 have_temp; u16 have_temp_fixed; u16 have_in; -#ifdef CONFIG_PM + /* Remember extra register values over suspend/resume */ u8 vbat; u8 fandiv1; u8 fandiv2; -#endif + u8 sio_reg_enable; }; struct nct6775_sio_data { @@ -947,7 +976,7 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, struct sensor_device_attribute_2 *a2; struct attribute **attrs; struct sensor_device_template **t; - int err, i, j, count; + int i, count; if (repeat <= 0) return ERR_PTR(-EINVAL); @@ -978,7 +1007,7 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, for (i = 0; i < repeat; i++) { t = tg->templates; - for (j = 0; *t != NULL; j++) { + while (*t != NULL) { snprintf(su->name, sizeof(su->name), (*t)->dev_attr.attr.name, tg->base + i); if ((*t)->s2) { @@ -1007,10 +1036,6 @@ nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, } } - err = sysfs_create_group(&dev->kobj, group); - if (err) - return ERR_PTR(-ENOMEM); - return group; } @@ -1045,13 +1070,15 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) reg == 0x73 || reg == 0x75 || reg == 0x77; case nct6779: case nct6791: + case nct6792: + case nct6793: return reg == 0x150 || reg == 0x153 || reg == 0x155 || ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) || reg == 0x402 || reg == 0x63a || reg == 0x63c || reg == 0x63e || reg == 0x640 || reg == 0x642 || reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 || - reg == 0x7b; + reg == 0x7b || reg == 0x7d; } return false; } @@ -1065,6 +1092,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) { u8 bank = reg >> 8; + if (data->bank != bank) { outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET); outb_p(bank, data->addr + DATA_REG_OFFSET); @@ -1302,6 +1330,7 @@ static void nct6775_update_pwm(struct device *dev) if (!data->target_speed_tolerance[i] || data->pwm_enable[i] == speed_cruise) { u8 t = fanmodecfg & 0x0f; + if (data->REG_TOLERANCE_H) { t |= (nct6775_read_value(data, data->REG_TOLERANCE_H[i]) & 0x70) >> 1; @@ -1319,6 +1348,9 @@ static void nct6775_update_pwm(struct device *dev) if (reg & 0x80) data->pwm[2][i] = 0; + if (!data->REG_WEIGHT_TEMP_SEL[i]) + continue; + reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]); data->pwm_weight_temp_sel[i] = reg & 0x1f; /* If weight is disabled, report weight source as 0 */ @@ -1390,6 +1422,8 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6106: case nct6779: case nct6791: + case nct6792: + case nct6793: reg = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i]); if (reg & data->CRITICAL_PWM_ENABLE_MASK) @@ -1462,7 +1496,8 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) = nct6775_read_temp(data, data->reg_temp[j][i]); } - if (!(data->have_temp_fixed & (1 << i))) + if (i >= NUM_TEMP_FIXED || + !(data->have_temp_fixed & (1 << i))) continue; data->temp_offset[i] = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]); @@ -1471,6 +1506,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->alarms = 0; for (i = 0; i < NUM_REG_ALARM; i++) { u8 alarm; + if (!data->REG_ALARM[i]) continue; alarm = nct6775_read_value(data, data->REG_ALARM[i]); @@ -1480,6 +1516,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->beeps = 0; for (i = 0; i < NUM_REG_BEEP; i++) { u8 beep; + if (!data->REG_BEEP[i]) continue; beep = nct6775_read_value(data, data->REG_BEEP[i]); @@ -1502,8 +1539,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; int index = sattr->index; + int nr = sattr->nr; + return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr)); } @@ -1513,10 +1551,12 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, { struct nct6775_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; int index = sattr->index; + int nr = sattr->nr; unsigned long val; - int err = kstrtoul(buf, 10, &val); + int err; + + err = kstrtoul(buf, 10, &val); if (err < 0) return err; mutex_lock(&data->update_lock); @@ -1533,6 +1573,7 @@ show_alarm(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = data->ALARM_BITS[sattr->index]; + return sprintf(buf, "%u\n", (unsigned int)((data->alarms >> nr) & 0x01)); } @@ -1550,7 +1591,7 @@ static int find_temp_source(struct nct6775_data *data, int index, int count) if (src == source) return nr; } - return -1; + return -ENODEV; } static ssize_t @@ -1568,6 +1609,7 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf) nr = find_temp_source(data, sattr->index, data->num_temp_alarms); if (nr >= 0) { int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE]; + alarm = (data->alarms >> bit) & 0x01; } return sprintf(buf, "%u\n", alarm); @@ -1593,8 +1635,9 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf, int nr = data->BEEP_BITS[sattr->index]; int regindex = nr >> 3; unsigned long val; + int err; - int err = kstrtoul(buf, 10, &val); + err = kstrtoul(buf, 10, &val); if (err < 0) return err; if (val > 1) @@ -1627,6 +1670,7 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf) nr = find_temp_source(data, sattr->index, data->num_temp_beeps); if (nr >= 0) { int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; + beep = (data->beeps >> bit) & 0x01; } return sprintf(buf, "%u\n", beep); @@ -1640,8 +1684,9 @@ store_temp_beep(struct device *dev, struct device_attribute *attr, struct nct6775_data *data = dev_get_drvdata(dev); int nr, bit, regindex; unsigned long val; + int err; - int err = kstrtoul(buf, 10, &val); + err = kstrtoul(buf, 10, &val); if (err < 0) return err; if (val > 1) @@ -1649,7 +1694,7 @@ store_temp_beep(struct device *dev, struct device_attribute *attr, nr = find_temp_source(data, sattr->index, data->num_temp_beeps); if (nr < 0) - return -ENODEV; + return nr; bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; regindex = bit >> 3; @@ -1713,6 +1758,7 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", data->rpm[nr]); } @@ -1722,6 +1768,7 @@ show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", data->fan_from_reg_min(data->fan_min[nr], data->fan_div[nr])); @@ -1733,6 +1780,7 @@ show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); } @@ -1744,9 +1792,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; unsigned long val; - int err; unsigned int reg; u8 new_div; + int err; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -1930,6 +1978,7 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); } @@ -2006,6 +2055,7 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", (int)data->temp_type[nr]); } @@ -2730,6 +2780,7 @@ store_fan_time(struct device *dev, struct device_attribute *attr, return count; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2739,6 +2790,7 @@ show_name(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +#endif static ssize_t show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf) @@ -2798,6 +2850,8 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6106: case nct6779: case nct6791: + case nct6792: + case nct6793: nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); reg = nct6775_read_value(data, @@ -2873,6 +2927,9 @@ static umode_t nct6775_pwm_is_visible(struct kobject *kobj, if (!(data->has_pwm & (1 << pwm))) return 0; + if ((nr >= 14 && nr <= 18) || nr == 21) /* weight */ + if (!data->REG_WEIGHT_TEMP_SEL[pwm]) + return 0; if (nr == 19 && data->REG_PWM[3] == NULL) /* pwm_max */ return 0; if (nr == 20 && data->REG_PWM[4] == NULL) /* pwm_step */ @@ -2966,11 +3023,11 @@ static struct sensor_device_template *nct6775_attributes_pwm_template[] = { &sensor_dev_template_pwm_step_down_time, &sensor_dev_template_pwm_start, &sensor_dev_template_pwm_floor, - &sensor_dev_template_pwm_weight_temp_sel, + &sensor_dev_template_pwm_weight_temp_sel, /* 14 */ &sensor_dev_template_pwm_weight_temp_step, &sensor_dev_template_pwm_weight_temp_step_tol, &sensor_dev_template_pwm_weight_temp_step_base, - &sensor_dev_template_pwm_weight_duty_step, + &sensor_dev_template_pwm_weight_duty_step, /* 18 */ &sensor_dev_template_pwm_max, /* 19 */ &sensor_dev_template_pwm_step, /* 20 */ &sensor_dev_template_pwm_weight_duty_base, /* 21 */ @@ -3002,6 +3059,7 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } @@ -3066,16 +3124,16 @@ static umode_t nct6775_other_is_visible(struct kobject *kobj, struct device *dev = container_of(kobj, struct device, kobj); struct nct6775_data *data = dev_get_drvdata(dev); - if (index == 1 && !data->have_vid) + if (index == 0 && !data->have_vid) return 0; - if (index == 2 || index == 3) { - if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 2] < 0) + if (index == 1 || index == 2) { + if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0) return 0; } - if (index == 4 || index == 5) { - if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 4] < 0) + if (index == 3 || index == 4) { + if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0) return 0; } @@ -3088,14 +3146,15 @@ static umode_t nct6775_other_is_visible(struct kobject *kobj, * Any change in order or content must be matched. */ static struct attribute *nct6775_attributes_other[] = { + &dev_attr_cpu0_vid.attr, /* 0 */ + &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */ + &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */ + &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */ + &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */ + &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) &dev_attr_name.attr, - &dev_attr_cpu0_vid.attr, /* 1 */ - &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 2 */ - &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 3 */ - &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 4 */ - &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 5 */ - &sensor_dev_attr_beep_enable.dev_attr.attr, /* 6 */ - +#endif NULL }; @@ -3104,27 +3163,6 @@ static const struct attribute_group nct6775_group_other = { .is_visible = nct6775_other_is_visible, }; -/* - * Driver and device management - */ - -static void nct6775_device_remove_files(struct device *dev) -{ - struct nct6775_data *data = dev_get_drvdata(dev); - - if (data->group_pwm) - sysfs_remove_group(&dev->kobj, data->group_pwm); - if (data->group_in) - sysfs_remove_group(&dev->kobj, data->group_in); - if (data->group_fan) - sysfs_remove_group(&dev->kobj, data->group_fan); - if (data->group_temp) - sysfs_remove_group(&dev->kobj, data->group_temp); - - sysfs_remove_group(&dev->kobj, &nct6775_group_other); -} - -/* Get the monitoring functions started */ static inline void nct6775_init_device(struct nct6775_data *data) { int i; @@ -3175,6 +3213,10 @@ nct6775_check_fan_inputs(struct nct6775_data *data) int sioreg = data->sioreg; int regval; + /* Store SIO_REG_ENABLE for use during resume */ + superio_select(sioreg, NCT6775_LD_HWM); + data->sio_reg_enable = superio_inb(sioreg, SIO_REG_ENABLE); + /* fan4 and fan5 share some pins with the GPIO and serial flash */ if (data->kind == nct6775) { regval = superio_inb(sioreg, 0x2c); @@ -3192,21 +3234,38 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm6pin = false; } else if (data->kind == nct6776) { bool gpok = superio_inb(sioreg, 0x27) & 0x80; + const char *board_vendor, *board_name; + + board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); + board_name = dmi_get_system_info(DMI_BOARD_NAME); - superio_select(sioreg, NCT6775_LD_HWM); - regval = superio_inb(sioreg, SIO_REG_ENABLE); + if (board_name && board_vendor && + !strcmp(board_vendor, "ASRock")) { + /* + * Auxiliary fan monitoring is not enabled on ASRock + * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode. + * Observed with BIOS version 2.00. + */ + if (!strcmp(board_name, "Z77 Pro4-M")) { + if ((data->sio_reg_enable & 0xe0) != 0xe0) { + data->sio_reg_enable |= 0xe0; + superio_outb(sioreg, SIO_REG_ENABLE, + data->sio_reg_enable); + } + } + } - if (regval & 0x80) + if (data->sio_reg_enable & 0x80) fan3pin = gpok; else fan3pin = !(superio_inb(sioreg, 0x24) & 0x40); - if (regval & 0x40) + if (data->sio_reg_enable & 0x40) fan4pin = gpok; else fan4pin = superio_inb(sioreg, 0x1C) & 0x01; - if (regval & 0x20) + if (data->sio_reg_enable & 0x20) fan5pin = gpok; else fan5pin = superio_inb(sioreg, 0x1C) & 0x02; @@ -3229,7 +3288,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm4pin = false; pwm5pin = false; pwm6pin = false; - } else { /* NCT6779D or NCT6791D */ + } else { /* NCT6779D, NCT6791D, NCT6792D, or NCT6793D */ regval = superio_inb(sioreg, 0x1c); fan3pin = !(regval & (1 << 5)); @@ -3242,7 +3301,8 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan4min = fan4pin; - if (data->kind == nct6791) { + if (data->kind == nct6791 || data->kind == nct6792 || + data->kind == nct6793) { regval = superio_inb(sioreg, 0x2d); fan6pin = (regval & (1 << 1)); pwm6pin = (regval & (1 << 0)); @@ -3290,7 +3350,7 @@ static void add_temp_sensors(struct nct6775_data *data, const u16 *regp, static int nct6775_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct nct6775_sio_data *sio_data = dev->platform_data; + struct nct6775_sio_data *sio_data = dev_get_platdata(dev); struct nct6775_data *data; struct resource *res; int i, s, err = 0; @@ -3301,6 +3361,8 @@ static int nct6775_probe(struct platform_device *pdev) int num_reg_temp, num_reg_temp_mon; u8 cr2a; struct attribute_group *group; + struct device *hwmon_dev; + int num_attr_groups = 0; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, @@ -3499,8 +3561,8 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; - data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_UP_TIME; data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; data->REG_PWM[0] = NCT6775_REG_PWM; data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; @@ -3555,7 +3617,7 @@ static int nct6775_probe(struct platform_device *pdev) data->speed_tolerance_limit = 63; data->temp_label = nct6779_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + data->temp_label_num = NCT6779_NUM_LABELS; data->REG_CONFIG = NCT6775_REG_CONFIG; data->REG_VBAT = NCT6775_REG_VBAT; @@ -3571,8 +3633,8 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; - data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_UP_TIME; data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; data->REG_PWM[0] = NCT6775_REG_PWM; data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; @@ -3613,6 +3675,8 @@ static int nct6775_probe(struct platform_device *pdev) break; case nct6791: + case nct6792: + case nct6793: data->in_num = 15; data->pwm_num = 6; data->auto_pwm_num = 4; @@ -3631,7 +3695,7 @@ static int nct6775_probe(struct platform_device *pdev) data->speed_tolerance_limit = 63; data->temp_label = nct6779_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); + data->temp_label_num = NCT6791_NUM_LABELS; data->REG_CONFIG = NCT6775_REG_CONFIG; data->REG_VBAT = NCT6775_REG_VBAT; @@ -3647,14 +3711,14 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; - data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_DOWN_TIME; + data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_UP_TIME; data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; data->REG_PWM[0] = NCT6775_REG_PWM; data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; - data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; - data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE; + data->REG_PWM[5] = NCT6791_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6791_REG_WEIGHT_DUTY_BASE; data->REG_PWM_READ = NCT6775_REG_PWM_READ; data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; @@ -3670,17 +3734,25 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; - data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; - data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; - data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; - data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; + data->REG_WEIGHT_TEMP_SEL = NCT6791_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6791_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6791_REG_ALARM; - data->REG_BEEP = NCT6776_REG_BEEP; + if (data->kind == nct6791) + data->REG_BEEP = NCT6776_REG_BEEP; + else + data->REG_BEEP = NCT6792_REG_BEEP; reg_temp = NCT6779_REG_TEMP; - reg_temp_mon = NCT6779_REG_TEMP_MON; num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); - num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + if (data->kind == nct6791) { + reg_temp_mon = NCT6779_REG_TEMP_MON; + num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + } else { + reg_temp_mon = NCT6792_REG_TEMP_MON; + num_reg_temp_mon = ARRAY_SIZE(NCT6792_REG_TEMP_MON); + } reg_temp_over = NCT6779_REG_TEMP_OVER; reg_temp_hyst = NCT6779_REG_TEMP_HYST; reg_temp_config = NCT6779_REG_TEMP_CONFIG; @@ -3797,7 +3869,8 @@ static int nct6775_probe(struct platform_device *pdev) !strlen(data->temp_label[src])) { dev_info(dev, "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n", - src, i, data->REG_TEMP_SEL[i], reg_temp_mon[i]); + src, i, data->REG_TEMP_SEL[i], + reg_temp_mon[i]); continue; } @@ -3878,6 +3951,8 @@ static int nct6775_probe(struct platform_device *pdev) case nct6106: case nct6779: case nct6791: + case nct6792: + case nct6793: break; } @@ -3909,6 +3984,8 @@ static int nct6775_probe(struct platform_device *pdev) tmp |= 0x3e; break; case nct6791: + case nct6792: + case nct6793: tmp |= 0x7e; break; } @@ -3928,65 +4005,74 @@ static int nct6775_probe(struct platform_device *pdev) /* Register sysfs hooks */ group = nct6775_create_attr_group(dev, &nct6775_pwm_template_group, data->pwm_num); - if (IS_ERR(group)) { - err = PTR_ERR(group); - goto exit_remove; - } - data->group_pwm = group; + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[num_attr_groups++] = group; group = nct6775_create_attr_group(dev, &nct6775_in_template_group, fls(data->have_in)); - if (IS_ERR(group)) { - err = PTR_ERR(group); - goto exit_remove; - } - data->group_in = group; + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[num_attr_groups++] = group; group = nct6775_create_attr_group(dev, &nct6775_fan_template_group, fls(data->has_fan)); - if (IS_ERR(group)) { - err = PTR_ERR(group); - goto exit_remove; - } - data->group_fan = group; + if (IS_ERR(group)) + return PTR_ERR(group); + + data->groups[num_attr_groups++] = group; group = nct6775_create_attr_group(dev, &nct6775_temp_template_group, fls(data->have_temp)); - if (IS_ERR(group)) { - err = PTR_ERR(group); - goto exit_remove; - } - data->group_temp = group; + if (IS_ERR(group)) + return PTR_ERR(group); - err = sysfs_create_group(&dev->kobj, &nct6775_group_other); - if (err) - goto exit_remove; + data->groups[num_attr_groups++] = group; + data->groups[num_attr_groups++] = &nct6775_group_other; - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + err = sysfs_create_groups(&dev->kobj, data->groups); + if (err < 0) + return err; + hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(hwmon_dev)) { + sysfs_remove_groups(&dev->kobj, data->groups); + return PTR_ERR(hwmon_dev); } - - return 0; - -exit_remove: - nct6775_device_remove_files(dev); - return err; + data->hwmon_dev = hwmon_dev; +#else + hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name, + data, data->groups); +#endif + return PTR_ERR_OR_ZERO(hwmon_dev); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) static int nct6775_remove(struct platform_device *pdev) { struct nct6775_data *data = platform_get_drvdata(pdev); hwmon_device_unregister(data->hwmon_dev); - nct6775_device_remove_files(&pdev->dev); - + sysfs_remove_groups(&pdev->dev.kobj, data->groups); return 0; } +#endif + +static void nct6791_enable_io_mapping(int sioaddr) +{ + int val; + + val = superio_inb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE); + if (val & 0x10) { + pr_info("Enabling hardware monitor logical device mappings.\n"); + superio_outb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE, + val & ~0x10); + } +} -#ifdef CONFIG_PM -static int nct6775_suspend(struct device *dev) +static int __maybe_unused nct6775_suspend(struct device *dev) { struct nct6775_data *data = nct6775_update_device(dev); @@ -4001,14 +4087,31 @@ static int nct6775_suspend(struct device *dev) return 0; } -static int nct6775_resume(struct device *dev) +static int __maybe_unused nct6775_resume(struct device *dev) { struct nct6775_data *data = dev_get_drvdata(dev); - int i, j; + int sioreg = data->sioreg; + int i, j, err = 0; + u8 reg; mutex_lock(&data->update_lock); data->bank = 0xff; /* Force initial bank selection */ + err = superio_enter(sioreg); + if (err) + goto abort; + + superio_select(sioreg, NCT6775_LD_HWM); + reg = superio_inb(sioreg, SIO_REG_ENABLE); + if (reg != data->sio_reg_enable) + superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable); + + if (data->kind == nct6791 || data->kind == nct6792 || + data->kind == nct6793) + nct6791_enable_io_mapping(sioreg); + + superio_exit(sioreg); + /* Restore limits */ for (i = 0; i < data->in_num; i++) { if (!(data->have_in & (1 << i))) @@ -4045,31 +4148,26 @@ static int nct6775_resume(struct device *dev) nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2); } +abort: /* Force re-reading all values */ data->valid = false; mutex_unlock(&data->update_lock); - return 0; + return err; } -static const struct dev_pm_ops nct6775_dev_pm_ops = { - .suspend = nct6775_suspend, - .resume = nct6775_resume, -}; - -#define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops) -#else -#define NCT6775_DEV_PM_OPS NULL -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume); static struct platform_driver nct6775_driver = { .driver = { .owner = THIS_MODULE, .name = DRVNAME, - .pm = NCT6775_DEV_PM_OPS, + .pm = &nct6775_dev_pm_ops, }, .probe = nct6775_probe, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) .remove = nct6775_remove, +#endif }; static const char * const nct6775_sio_names[] __initconst = { @@ -4078,6 +4176,7 @@ static const char * const nct6775_sio_names[] __initconst = { "NCT6776D/F", "NCT6779D", "NCT6791D", + "NCT6792D", }; /* nct6775_find() looks for a '627 in the Super-I/O config space */ @@ -4112,6 +4211,12 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6791_ID: sio_data->kind = nct6791; break; + case SIO_NCT6792_ID: + sio_data->kind = nct6792; + break; + case SIO_NCT6793_ID: + sio_data->kind = nct6793; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); @@ -4136,15 +4241,10 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); } - if (sio_data->kind == nct6791) { - val = superio_inb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE); - if (val & 0x10) { - pr_info("Enabling hardware monitor logical device mappings.\n"); - superio_outb(sioaddr, - NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE, - val & ~0x10); - } - } + + if (sio_data->kind == nct6791 || sio_data->kind == nct6792 || + sio_data->kind == nct6793) + nct6791_enable_io_mapping(sioaddr); superio_exit(sioaddr); pr_info("Found %s or compatible chip at %#x:%#x\n", @@ -4157,7 +4257,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) /* * when Super-I/O functions move to a separate file, the Super-I/O * bus will manage the lifetime of the device and this module will only keep - * track of the nct6775 driver. But since we platform_device_alloc(), we + * track of the nct6775 driver. But since we use platform_device_alloc(), we * must keep track of the device */ static struct platform_device *pdev[2]; @@ -4192,7 +4292,7 @@ static int __init sensors_nct6775_init(void) pdev[i] = platform_device_alloc(DRVNAME, address); if (!pdev[i]) { err = -ENOMEM; - goto exit_device_put; + goto exit_device_unregister; } err = platform_device_add_data(pdev[i], &sio_data, @@ -4230,9 +4330,11 @@ static int __init sensors_nct6775_init(void) return 0; exit_device_put: - for (i = 0; i < ARRAY_SIZE(pdev); i++) { + platform_device_put(pdev[i]); +exit_device_unregister: + while (--i >= 0) { if (pdev[i]) - platform_device_put(pdev[i]); + platform_device_unregister(pdev[i]); } exit_unregister: platform_driver_unregister(&nct6775_driver); @@ -4251,7 +4353,7 @@ static void __exit sensors_nct6775_exit(void) } MODULE_AUTHOR("Guenter Roeck "); -MODULE_DESCRIPTION("NCT6775F/NCT6776F/NCT6779D driver"); +MODULE_DESCRIPTION("Driver for NCT6775F and compatible chips"); MODULE_LICENSE("GPL"); module_init(sensors_nct6775_init);