#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/acpi.h>
+#include <linux/bitops.h>
#include <linux/dmi.h>
#include <linux/io.h>
+#include <linux/debugfs.h>
#include "lm75.h"
#include "compat.h"
[15] = 0x664,
};
-static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1]
- = { 0, 0, 0, 0, 0xa00, 0xa01, 0xa02, 0xa03, 0xa04, 0xa05, 0xa06,
- 0xa07 };
+static const u16 NCT6775_REG_TEMP_CRIT[32] = {
+ [4] = 0xa00,
+ [5] = 0xa01,
+ [6] = 0xa02,
+ [7] = 0xa03,
+ [8] = 0xa04,
+ [9] = 0xa05,
+ [10] = 0xa06,
+ [11] = 0xa07
+};
/* NCT6776 specific data */
static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0, 0, 0, 0 };
static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0, 0, 0, 0 };
-static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 };
-static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 };
+static const u16 NCT6776_REG_FAN_MIN[] = {
+ 0x63a, 0x63c, 0x63e, 0x640, 0x642, 0x64a };
+static const u16 NCT6776_REG_FAN_PULSES[] = {
+ 0x644, 0x645, 0x646, 0x647, 0x648, 0x649 };
static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = {
0x13e, 0x23e, 0x33e, 0x83e, 0x93e, 0xa3e };
static inline unsigned int
div_from_reg(u8 reg)
{
- return 1 << reg;
+ return BIT(reg);
}
/*
u8 temp_src[NUM_TEMP];
u16 reg_temp_config[NUM_TEMP];
const char * const *temp_label;
- int temp_mask;
+ u32 temp_mask;
u16 REG_CONFIG;
u16 REG_VBAT;
enum kinds kind;
};
+#ifdef CONFIG_DEBUG_FS
+
+static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg);
+static u16 nct6775_read_value(struct nct6775_data *data, u16 reg);
+
+static const char *temp_attr_names[5] = {
+ "input",
+ "max",
+ "hyst",
+ "crit",
+ "lcrit",
+};
+
+static int nct6775_seq_show(struct seq_file *s, void *v)
+{
+ struct device *dev = (struct device *)s->private;
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ int i, j;
+
+ seq_printf(s, "Temperatures:\n");
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & BIT(i)))
+ continue;
+ seq_printf(s, " temp%d [source %d, %s]:\n", i + 1,
+ data->temp_src[i],
+ data->temp_label[data->temp_src[i]]);
+ for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) {
+ if (data->reg_temp[j][i]) {
+ seq_printf(s, " %s: reg=0x%x val=0x%x cached 0x%x\n",
+ temp_attr_names[j],
+ data->reg_temp[j][i],
+ nct6775_read_temp(data, data->reg_temp[j][i]),
+ (u16)data->temp[j][i]);
+ }
+ }
+ }
+ seq_printf(s, "Temperature sources:\n");
+ for (i = 0; i < data->num_temp_alarms; i++) {
+ seq_printf(s, " index %d register 0x%x: val=0x%x\n",
+ i, data->REG_TEMP_SOURCE[i],
+ nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f);
+ }
+ return 0;
+}
+
+static int nct6775_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, nct6775_seq_show, inode->i_private);
+}
+
+static const struct file_operations nct6775_debug_operations = {
+ .open = nct6775_debug_open,
+ .llseek = seq_lseek,
+ .read = seq_read,
+ .release = single_release,
+};
+
+static void nct6775_debugfs_exit(void *data)
+{
+ debugfs_remove_recursive(data);
+}
+
+static int nct6775_debugfs_init(struct device *dev)
+{
+ struct dentry *rootdir;
+
+ rootdir = debugfs_create_dir(dev_name(dev), NULL);
+ if (!rootdir)
+ return -ENOMEM;
+
+ devm_add_action(dev, nct6775_debugfs_exit, rootdir);
+
+ debugfs_create_file("registers", S_IFREG | 0444, rootdir,
+ dev, &nct6775_debug_operations);
+
+ return 0;
+}
+
+#else
+static int nct6775_debugfs_init(struct device *dev) { return 0; }
+#endif
+
struct sensor_device_template {
struct device_attribute dev_attr;
union {
((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) ||
reg == 0x402 ||
reg == 0x63a || reg == 0x63c || reg == 0x63e ||
- reg == 0x640 || reg == 0x642 ||
+ reg == 0x640 || reg == 0x642 || reg == 0x64a ||
reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 ||
reg == 0x7b || reg == 0x7d;
}
data->fan_div[1] = (i & 0x70) >> 4;
i = nct6775_read_value(data, NCT6775_REG_FANDIV2);
data->fan_div[2] = i & 0x7;
- if (data->has_fan & (1 << 3))
+ if (data->has_fan & BIT(3))
data->fan_div[3] = (i & 0x70) >> 4;
}
* We'll compute a better divider later on.
*/
for (i = 0; i < ARRAY_SIZE(data->fan_div); i++) {
- if (!(data->has_fan & (1 << i)))
+ if (!(data->has_fan & BIT(i)))
continue;
if (data->fan_div[i] == 0) {
data->fan_div[i] = 7;
* prevents the unnecessary warning when fanX_min is reported as 0.
*/
for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
- if (data->has_fan_min & (1 << i)) {
+ if (data->has_fan_min & BIT(i)) {
reg = nct6775_read_value(data, data->REG_FAN_MIN[i]);
if (!reg)
nct6775_write_value(data, data->REG_FAN_MIN[i],
div_from_reg(fan_div));
/* Preserve min limit if possible */
- if (data->has_fan_min & (1 << nr)) {
+ if (data->has_fan_min & BIT(nr)) {
fan_min = data->fan_min[nr];
if (fan_div > data->fan_div[nr]) {
if (fan_min != 255 && fan_min > 1)
bool duty_is_dc;
for (i = 0; i < data->pwm_num; i++) {
- if (!(data->has_pwm & (1 << i)))
+ if (!(data->has_pwm & BIT(i)))
continue;
duty_is_dc = data->REG_PWM_MODE[i] &&
u16 reg_t;
for (i = 0; i < data->pwm_num; i++) {
- if (!(data->has_pwm & (1 << i)))
+ if (!(data->has_pwm & BIT(i)))
continue;
for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) {
/* Measured voltages and limits */
for (i = 0; i < data->in_num; i++) {
- if (!(data->have_in & (1 << i)))
+ if (!(data->have_in & BIT(i)))
continue;
data->in[i][0] = nct6775_read_value(data,
for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
u16 reg;
- if (!(data->has_fan & (1 << i)))
+ if (!(data->has_fan & BIT(i)))
continue;
reg = nct6775_read_value(data, data->REG_FAN[i]);
data->rpm[i] = data->fan_from_reg(reg,
data->fan_div[i]);
- if (data->has_fan_min & (1 << i))
+ if (data->has_fan_min & BIT(i))
data->fan_min[i] = nct6775_read_value(data,
data->REG_FAN_MIN[i]);
data->fan_pulses[i] =
/* Measured temperatures and limits */
for (i = 0; i < NUM_TEMP; i++) {
- if (!(data->have_temp & (1 << i)))
+ if (!(data->have_temp & BIT(i)))
continue;
for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) {
if (data->reg_temp[j][i])
data->reg_temp[j][i]);
}
if (i >= NUM_TEMP_FIXED ||
- !(data->have_temp_fixed & (1 << i)))
+ !(data->have_temp_fixed & BIT(i)))
continue;
data->temp_offset[i]
= nct6775_read_value(data, data->REG_TEMP_OFFSET[i]);
struct nct6775_data *data = dev_get_drvdata(dev);
int in = index / 5; /* voltage index */
- if (!(data->have_in & (1 << in)))
+ if (!(data->have_in & BIT(in)))
return 0;
return attr->mode;
* even with the highest divider (128)
*/
data->fan_min[nr] = 254;
- new_div = 7; /* 128 == (1 << 7) */
+ new_div = 7; /* 128 == BIT(7) */
dev_warn(dev,
"fan%u low limit %lu below minimum %u, set to minimum\n",
nr + 1, val, data->fan_from_reg_min(254, 7));
* even with the lowest divider (1)
*/
data->fan_min[nr] = 1;
- new_div = 0; /* 1 == (1 << 0) */
+ new_div = 0; /* 1 == BIT(0) */
dev_warn(dev,
"fan%u low limit %lu above maximum %u, set to maximum\n",
nr + 1, val, data->fan_from_reg_min(1, 0));
int fan = index / 6; /* fan index */
int nr = index % 6; /* attribute index */
- if (!(data->has_fan & (1 << fan)))
+ if (!(data->has_fan & BIT(fan)))
return 0;
if (nr == 1 && data->ALARM_BITS[FAN_ALARM_BASE + fan] == -1)
return 0;
if (nr == 2 && data->BEEP_BITS[FAN_ALARM_BASE + fan] == -1)
return 0;
- if (nr == 4 && !(data->has_fan_min & (1 << fan)))
+ if (nr == 4 && !(data->has_fan_min & BIT(fan)))
return 0;
if (nr == 5 && data->kind != nct6775)
return 0;
int temp = index / 10; /* temp index */
int nr = index % 10; /* attribute index */
- if (!(data->have_temp & (1 << temp)))
+ if (!(data->have_temp & BIT(temp)))
return 0;
if (nr == 1 && !data->temp_label)
return 0;
/* offset and type only apply to fixed sensors */
- if (nr > 7 && !(data->have_temp_fixed & (1 << temp)))
+ if (nr > 7 && !(data->have_temp_fixed & BIT(temp)))
return 0;
return attr->mode;
int i, sel = 0;
for (i = 0; i < NUM_TEMP; i++) {
- if (!(data->have_temp & (1 << i)))
+ if (!(data->have_temp & BIT(i)))
continue;
if (src == data->temp_src[i]) {
sel = i + 1;
return err;
if (val == 0 || val > NUM_TEMP)
return -EINVAL;
- if (!(data->have_temp & (1 << (val - 1))) || !data->temp_src[val - 1])
+ if (!(data->have_temp & BIT(val - 1)) || !data->temp_src[val - 1])
return -EINVAL;
mutex_lock(&data->update_lock);
return err;
if (val > NUM_TEMP)
return -EINVAL;
- if (val && (!(data->have_temp & (1 << (val - 1))) ||
+ if (val && (!(data->have_temp & BIT(val - 1)) ||
!data->temp_src[val - 1]))
return -EINVAL;
int pwm = index / 36; /* pwm index */
int nr = index % 36; /* attribute index */
- if (!(data->has_pwm & (1 << pwm)))
+ if (!(data->has_pwm & BIT(pwm)))
return 0;
if ((nr >= 14 && nr <= 18) || nr == 21) /* weight */
/* Enable temperature sensors if needed */
for (i = 0; i < NUM_TEMP; i++) {
- if (!(data->have_temp & (1 << i)))
+ if (!(data->have_temp & BIT(i)))
continue;
if (!data->reg_temp_config[i])
continue;
diode = nct6775_read_value(data, data->REG_DIODE);
for (i = 0; i < data->temp_fixed_num; i++) {
- if (!(data->have_temp_fixed & (1 << i)))
+ if (!(data->have_temp_fixed & BIT(i)))
continue;
if ((tmp & (data->DIODE_MASK << i))) /* diode */
data->temp_type[i]
if (data->kind == nct6775) {
regval = superio_inb(sioreg, 0x2c);
- fan3pin = regval & (1 << 6);
- pwm3pin = regval & (1 << 7);
+ fan3pin = regval & BIT(6);
+ pwm3pin = regval & BIT(7);
/* On NCT6775, fan4 shares pins with the fdc interface */
fan4pin = !(superio_inb(sioreg, 0x2A) & 0x80);
pwm4pin = false;
pwm5pin = false;
pwm6pin = false;
- } else { /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D */
- int regval_1b, regval_2a, regval_eb;
+ } else { /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D */
+ int regval_1b, regval_2a, regval_2f, regval_eb;
+ bool dsw_en;
regval = superio_inb(sioreg, 0x1c);
- fan3pin = !(regval & (1 << 5));
- fan4pin = !(regval & (1 << 6));
- fan5pin = !(regval & (1 << 7));
+ fan3pin = !(regval & BIT(5));
+ fan4pin = !(regval & BIT(6));
+ fan5pin = !(regval & BIT(7));
- pwm3pin = !(regval & (1 << 0));
- pwm4pin = !(regval & (1 << 1));
- pwm5pin = !(regval & (1 << 2));
+ pwm3pin = !(regval & BIT(0));
+ pwm4pin = !(regval & BIT(1));
+ pwm5pin = !(regval & BIT(2));
regval = superio_inb(sioreg, 0x2d);
switch (data->kind) {
case nct6791:
case nct6792:
- fan6pin = regval & (1 << 1);
- pwm6pin = regval & (1 << 0);
+ fan6pin = regval & BIT(1);
+ pwm6pin = regval & BIT(0);
break;
case nct6793:
case nct6795:
regval_1b = superio_inb(sioreg, 0x1b);
regval_2a = superio_inb(sioreg, 0x2a);
+ regval_2f = superio_inb(sioreg, 0x2f);
+ dsw_en = regval_2f & BIT(3);
if (!pwm5pin)
- pwm5pin = regval & (1 << 7);
- fan6pin = regval & (1 << 1);
- pwm6pin = regval & (1 << 0);
+ pwm5pin = regval & BIT(7);
+
if (!fan5pin)
- fan5pin = regval_1b & (1 << 5);
+ fan5pin = regval_1b & BIT(5);
+
+ fan6pin = false;
+ pwm6pin = false;
+ if (!dsw_en) {
+ fan6pin = regval & BIT(1);
+ pwm6pin = regval & BIT(0);
+ }
superio_select(sioreg, NCT6775_LD_12);
regval_eb = superio_inb(sioreg, 0xeb);
if (!fan5pin)
- fan5pin = regval_eb & (1 << 5);
+ fan5pin = regval_eb & BIT(5);
if (!pwm5pin)
- pwm5pin = (regval_eb & (1 << 4)) &&
- !(regval_2a & (1 << 0));
+ pwm5pin = (regval_eb & BIT(4)) &&
+ !(regval_2a & BIT(0));
if (!fan6pin)
- fan6pin = regval_eb & (1 << 3);
+ fan6pin = regval_eb & BIT(3);
if (!pwm6pin)
- pwm6pin = regval_eb & (1 << 2);
+ pwm6pin = regval_eb & BIT(2);
+
+ if (data->kind == nct6795) {
+ int regval_ed = superio_inb(sioreg, 0xed);
+
+ if (!fan6pin)
+ fan6pin = (regval_2a & BIT(4)) &&
+ (!dsw_en ||
+ (dsw_en && (regval_ed & BIT(4))));
+ if (!pwm6pin)
+ pwm6pin = (regval_2a & BIT(3)) &&
+ (regval_ed & BIT(2));
+ }
break;
default: /* NCT6779D */
fan6pin = false;
data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) |
(fan5pin << 4) | (fan6pin << 5);
data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) |
- (fan5pin << 4);
+ (fan5pin << 4) | (fan6pin << 5);
data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) |
(pwm5pin << 4) | (pwm6pin << 5);
}
continue;
src = nct6775_read_value(data, regp[i]);
src &= 0x1f;
- if (!src || (*mask & (1 << src)))
+ if (!src || (*mask & BIT(src)))
continue;
if (!(data->temp_mask & BIT(src)))
continue;
index = __ffs(*available);
nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src);
- *available &= ~(1 << index);
- *mask |= 1 << src;
+ *available &= ~BIT(index);
+ *mask |= BIT(src);
}
}
default:
return -ENODEV;
}
- data->have_in = (1 << data->in_num) - 1;
+ data->have_in = BIT(data->in_num) - 1;
data->have_temp = 0;
/*
continue;
src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
- if (!src || (mask & (1 << src)))
- available |= 1 << i;
+ if (!src || (mask & BIT(src)))
+ available |= BIT(i);
- mask |= 1 << src;
+ mask |= BIT(src);
}
/*
continue;
src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
- if (!src || (mask & (1 << src)))
+ if (!src || (mask & BIT(src)))
continue;
if (!(data->temp_mask & BIT(src))) {
continue;
}
- mask |= 1 << src;
+ mask |= BIT(src);
/* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
if (src <= data->temp_fixed_num) {
- data->have_temp |= 1 << (src - 1);
- data->have_temp_fixed |= 1 << (src - 1);
+ data->have_temp |= BIT(src - 1);
+ data->have_temp_fixed |= BIT(src - 1);
data->reg_temp[0][src - 1] = reg_temp[i];
data->reg_temp[1][src - 1] = reg_temp_over[i];
data->reg_temp[2][src - 1] = reg_temp_hyst[i];
continue;
/* Use dynamic index for other sources */
- data->have_temp |= 1 << s;
+ data->have_temp |= BIT(s);
data->reg_temp[0][s] = reg_temp[i];
data->reg_temp[1][s] = reg_temp_over[i];
data->reg_temp[2][s] = reg_temp_hyst[i];
* are no duplicates.
*/
if (src != TEMP_SOURCE_VIRTUAL) {
- if (mask & (1 << src))
+ if (mask & BIT(src))
continue;
- mask |= 1 << src;
+ mask |= BIT(src);
}
/* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
if (src <= data->temp_fixed_num) {
- if (data->have_temp & (1 << (src - 1)))
+ if (data->have_temp & BIT(src - 1))
continue;
- data->have_temp |= 1 << (src - 1);
- data->have_temp_fixed |= 1 << (src - 1);
+ data->have_temp |= BIT(src - 1);
+ data->have_temp_fixed |= BIT(src - 1);
data->reg_temp[0][src - 1] = reg_temp_mon[i];
data->temp_src[src - 1] = src;
continue;
continue;
/* Use dynamic index for other sources */
- data->have_temp |= 1 << s;
+ data->have_temp |= BIT(s);
data->reg_temp[0][s] = reg_temp_mon[i];
data->temp_src[s] = src;
s++;
* is set.
*/
for (i = 0; i < 32; i++) {
- if (!(data->temp_mask & BIT(i)))
+ if (!(data->temp_mask & BIT(i + 1)))
continue;
if (!reg_temp_alternate[i])
continue;
- if (mask & (1 << (i + 1)))
+ if (mask & BIT(i + 1))
continue;
if (i < data->temp_fixed_num) {
- if (data->have_temp & (1 << i))
+ if (data->have_temp & BIT(i))
continue;
- data->have_temp |= 1 << i;
- data->have_temp_fixed |= 1 << i;
+ data->have_temp |= BIT(i);
+ data->have_temp_fixed |= BIT(i);
data->reg_temp[0][i] = reg_temp_alternate[i];
if (i < num_reg_temp) {
data->reg_temp[1][i] = reg_temp_over[i];
if (s >= NUM_TEMP) /* Abort if no more space */
break;
- data->have_temp |= 1 << s;
+ data->have_temp |= BIT(s);
data->reg_temp[0][s] = reg_temp_alternate[i];
data->temp_src[s] = i + 1;
s++;
#else
hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name,
data, data->groups);
+ if (!IS_ERR(hwmon_dev))
+ nct6775_debugfs_init(hwmon_dev);
#endif
return PTR_ERR_OR_ZERO(hwmon_dev);
}
/* Restore limits */
for (i = 0; i < data->in_num; i++) {
- if (!(data->have_in & (1 << i)))
+ if (!(data->have_in & BIT(i)))
continue;
nct6775_write_value(data, data->REG_IN_MINMAX[0][i],
}
for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
- if (!(data->has_fan_min & (1 << i)))
+ if (!(data->has_fan_min & BIT(i)))
continue;
nct6775_write_value(data, data->REG_FAN_MIN[i],
}
for (i = 0; i < NUM_TEMP; i++) {
- if (!(data->have_temp & (1 << i)))
+ if (!(data->have_temp & BIT(i)))
continue;
for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)