#define VENDOR_ID_REG 0x7A /* Any bank */ #define NUVOTON_ID 0x50 #define CHIP_ID_REG 0x7B /* Any bank */ #define NCT7904_ID 0xC5 #define DEVICE_ID_REG 0x7C /* Any bank */
#define VSEN1_HV_LL_REG 0x02 /* Bank 1; 2 regs (HV/LV) per sensor */ #define VSEN1_LV_LL_REG 0x03 /* Bank 1; 2 regs (HV/LV) per sensor */ #define VSEN1_HV_HL_REG 0x00 /* Bank 1; 2 regs (HV/LV) per sensor */ #define VSEN1_LV_HL_REG 0x01 /* Bank 1; 2 regs (HV/LV) per sensor */ #define SMI_STS1_REG 0xC1 /* Bank 0; SMI Status Register */ #define SMI_STS3_REG 0xC3 /* Bank 0; SMI Status Register */ #define SMI_STS5_REG 0xC5 /* Bank 0; SMI Status Register */ #define SMI_STS7_REG 0xC7 /* Bank 0; SMI Status Register */ #define SMI_STS8_REG 0xC8 /* Bank 0; SMI Status Register */
#define VSEN1_HV_REG 0x40 /* Bank 0; 2 regs (HV/LV) per sensor */ #define TEMP_CH1_HV_REG 0x42 /* Bank 0; same as VSEN2_HV */ #define LTD_HV_REG 0x62 /* Bank 0; 2 regs in VSEN range */ #define LTD_HV_HL_REG 0x44 /* Bank 1; 1 reg for LTD */ #define LTD_LV_HL_REG 0x45 /* Bank 1; 1 reg for LTD */ #define LTD_HV_LL_REG 0x46 /* Bank 1; 1 reg for LTD */ #define LTD_LV_LL_REG 0x47 /* Bank 1; 1 reg for LTD */ #define TEMP_CH1_CH_REG 0x05 /* Bank 1; 1 reg for LTD */ #define TEMP_CH1_W_REG 0x06 /* Bank 1; 1 reg for LTD */ #define TEMP_CH1_WH_REG 0x07 /* Bank 1; 1 reg for LTD */ #define TEMP_CH1_C_REG 0x04 /* Bank 1; 1 reg per sensor */ #define DTS_T_CPU1_C_REG 0x90 /* Bank 1; 1 reg per sensor */ #define DTS_T_CPU1_CH_REG 0x91 /* Bank 1; 1 reg per sensor */ #define DTS_T_CPU1_W_REG 0x92 /* Bank 1; 1 reg per sensor */ #define DTS_T_CPU1_WH_REG 0x93 /* Bank 1; 1 reg per sensor */ #define FANIN1_HV_REG 0x80 /* Bank 0; 2 regs (HV/LV) per sensor */ #define FANIN1_HV_HL_REG 0x60 /* Bank 1; 2 regs (HV/LV) per sensor */ #define FANIN1_LV_HL_REG 0x61 /* Bank 1; 2 regs (HV/LV) per sensor */ #define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */
#define PRTS_REG 0x03 /* Bank 2 */ #define PFE_REG 0x00 /* Bank 2; PECI Function Enable */ #define TSI_CTRL_REG 0x50 /* Bank 2; TSI Control Register */ #define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */ #define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */
/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */ staticint nct7904_read_reg(struct nct7904_data *data, unsignedint bank, unsignedint reg)
{ struct i2c_client *client = data->client; int ret;
ret = nct7904_bank_lock(data, bank); if (ret == 0)
ret = i2c_smbus_read_byte_data(client, reg);
nct7904_bank_release(data); return ret;
}
/* * Read 2-byte register. Returns register in big-endian format or * -ERRNO on error.
*/ staticint nct7904_read_reg16(struct nct7904_data *data, unsignedint bank, unsignedint reg)
{ struct i2c_client *client = data->client; int ret, hi;
ret = nct7904_bank_lock(data, bank); if (ret == 0) {
ret = i2c_smbus_read_byte_data(client, reg); if (ret >= 0) {
hi = ret;
ret = i2c_smbus_read_byte_data(client, reg + 1); if (ret >= 0)
ret |= hi << 8;
}
}
nct7904_bank_release(data); return ret;
}
/* Write 1-byte register. Returns 0 or -ERRNO on error. */ staticint nct7904_write_reg(struct nct7904_data *data, unsignedint bank, unsignedint reg, u8 val)
{ struct i2c_client *client = data->client; int ret;
ret = nct7904_bank_lock(data, bank); if (ret == 0)
ret = i2c_smbus_write_byte_data(client, reg, val);
nct7904_bank_release(data); return ret;
}
staticint nct7904_read_fan(struct device *dev, u32 attr, int channel, long *val)
{ struct nct7904_data *data = dev_get_drvdata(dev); unsignedint cnt, rpm; int ret;
switch (attr) { case hwmon_fan_input:
ret = nct7904_read_reg16(data, BANK_0,
FANIN1_HV_REG + channel * 2); if (ret < 0) return ret;
cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); if (cnt == 0 || cnt == 0x1fff)
rpm = 0; else
rpm = 1350000 / cnt;
*val = rpm; return 0; case hwmon_fan_min:
ret = nct7904_read_reg16(data, BANK_1,
FANIN1_HV_HL_REG + channel * 2); if (ret < 0) return ret;
cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); if (cnt == 0 || cnt == 0x1fff)
rpm = 0; else
rpm = 1350000 / cnt;
*val = rpm; return 0; case hwmon_fan_alarm:
ret = nct7904_read_reg(data, BANK_0,
SMI_STS5_REG + (channel >> 3)); if (ret < 0) return ret; if (!data->fan_alarm[channel >> 3])
data->fan_alarm[channel >> 3] = ret & 0xff; else /* If there is new alarm showing up */
data->fan_alarm[channel >> 3] |= (ret & 0xff);
*val = (data->fan_alarm[channel >> 3] >> (channel & 0x07)) & 1; /* Needs to clean the alarm if alarm existing */ if (*val)
data->fan_alarm[channel >> 3] ^= 1 << (channel & 0x07); return 0; default: return -EOPNOTSUPP;
}
}
staticint nct7904_read_in(struct device *dev, u32 attr, int channel, long *val)
{ struct nct7904_data *data = dev_get_drvdata(dev); int ret, volt, index;
index = nct7904_chan_to_index[channel];
switch (attr) { case hwmon_in_input:
ret = nct7904_read_reg16(data, BANK_0,
VSEN1_HV_REG + index * 2); if (ret < 0) return ret;
volt = ((ret & 0xff00) >> 5) | (ret & 0x7); if (index < 14)
volt *= 2; /* 0.002V scale */ else
volt *= 6; /* 0.006V scale */
*val = volt; return 0; case hwmon_in_min:
ret = nct7904_read_reg16(data, BANK_1,
VSEN1_HV_LL_REG + index * 4); if (ret < 0) return ret;
volt = ((ret & 0xff00) >> 5) | (ret & 0x7); if (index < 14)
volt *= 2; /* 0.002V scale */ else
volt *= 6; /* 0.006V scale */
*val = volt; return 0; case hwmon_in_max:
ret = nct7904_read_reg16(data, BANK_1,
VSEN1_HV_HL_REG + index * 4); if (ret < 0) return ret;
volt = ((ret & 0xff00) >> 5) | (ret & 0x7); if (index < 14)
volt *= 2; /* 0.002V scale */ else
volt *= 6; /* 0.006V scale */
*val = volt; return 0; case hwmon_in_alarm:
ret = nct7904_read_reg(data, BANK_0,
SMI_STS1_REG + (index >> 3)); if (ret < 0) return ret; if (!data->vsen_alarm[index >> 3])
data->vsen_alarm[index >> 3] = ret & 0xff; else /* If there is new alarm showing up */
data->vsen_alarm[index >> 3] |= (ret & 0xff);
*val = (data->vsen_alarm[index >> 3] >> (index & 0x07)) & 1; /* Needs to clean the alarm if alarm existing */ if (*val)
data->vsen_alarm[index >> 3] ^= 1 << (index & 0x07); return 0; default: return -EOPNOTSUPP;
}
}
static umode_t nct7904_in_is_visible(constvoid *_data, u32 attr, int channel)
{ conststruct nct7904_data *data = _data; int index = nct7904_chan_to_index[channel];
switch (attr) { case hwmon_in_input: case hwmon_in_alarm: if (channel > 0 && (data->vsen_mask & BIT(index))) return 0444; break; case hwmon_in_min: case hwmon_in_max: if (channel > 0 && (data->vsen_mask & BIT(index))) return 0644; break; default: break;
}
return 0;
}
staticint nct7904_read_temp(struct device *dev, u32 attr, int channel, long *val)
{ struct nct7904_data *data = dev_get_drvdata(dev); int ret, temp; unsignedint reg1, reg2, reg3;
s8 temps;
staticint nct7904_wdt_set_timeout(struct watchdog_device *wdt, unsignedint timeout)
{ struct nct7904_data *data = watchdog_get_drvdata(wdt); /* * The NCT7904 is very special in watchdog function. * Its minimum unit is minutes. And wdt->timeout needs * to match the actual timeout selected. So, this needs * to be: wdt->timeout = timeout / 60 * 60. * For example, if the user configures a timeout of * 119 seconds, the actual timeout will be 60 seconds. * So, wdt->timeout must then be set to 60 seconds.
*/
wdt->timeout = timeout / 60 * 60;
staticint nct7904_wdt_ping(struct watchdog_device *wdt)
{ /* * Note: * NCT7904 does not support refreshing WDT_TIMER_REG register when * the watchdog is active. Please disable watchdog before feeding * the watchdog and enable it again.
*/ struct nct7904_data *data = watchdog_get_drvdata(wdt); int ret;
/* Disable soft watchdog timer */
ret = nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_DIS); if (ret < 0) return ret;
/* feed watchdog */
ret = nct7904_write_reg(data, BANK_0, WDT_TIMER_REG, wdt->timeout / 60); if (ret < 0) return ret;
/* * VSEN attributes * * Note: voltage sensors overlap with external temperature * sensors. So, if we ever decide to support the latter * we will have to adjust 'vsen_mask' accordingly.
*/
mask = 0;
ret = nct7904_read_reg16(data, BANK_0, VT_ADC_CTRL0_REG); if (ret >= 0)
mask = (ret >> 8) | ((ret & 0xff) << 8);
ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG); if (ret >= 0)
mask |= (ret << 16);
data->vsen_mask = mask;
/* CPU_TEMP attributes */
ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL0_REG); if (ret < 0) return ret;
/* LTD */
ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG); if (ret < 0) return ret; if ((ret & 0x02) == 0x02)
data->tcpu_mask |= 0x10;
/* Multi-Function detecting for Volt and TR/TD */
ret = nct7904_read_reg(data, BANK_0, VT_ADC_MD_REG); if (ret < 0) return ret;
data->temp_mode = 0; for (i = 0; i < 4; i++) {
val = (ret >> (i * 2)) & 0x03;
bit = (1 << i); if (val == VOLT_MONITOR_MODE) {
data->tcpu_mask &= ~bit;
} elseif (val == THERMAL_DIODE_MODE && i < 2) {
data->temp_mode |= bit;
data->vsen_mask &= ~(0x06 << (i * 2));
} elseif (val == THERMISTOR_MODE) {
data->vsen_mask &= ~(0x02 << (i * 2));
} else { /* Reserved */
data->tcpu_mask &= ~bit;
data->vsen_mask &= ~(0x06 << (i * 2));
}
}
/* PECI */
ret = nct7904_read_reg(data, BANK_2, PFE_REG); if (ret < 0) return ret; if (ret & 0x80) {
data->enable_dts = 1; /* Enable DTS & PECI */
} else {
ret = nct7904_read_reg(data, BANK_2, TSI_CTRL_REG); if (ret < 0) return ret; if (ret & 0x80)
data->enable_dts = 0x3; /* Enable DTS & TSI */
}
/* Check DTS enable status */ if (data->enable_dts) {
ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL0_REG); if (ret < 0) return ret;
data->has_dts = ret & 0xF; if (data->enable_dts & ENABLE_TSI) {
ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG); if (ret < 0) return ret;
data->has_dts |= (ret & 0xF) << 4;
}
}
for (i = 0; i < FANCTL_MAX; i++) {
ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i); if (ret < 0) return ret;
data->fan_mode[i] = ret;
}
/* Read all of SMI status register to clear alarms */ for (i = 0; i < SMI_STS_MAX; i++) {
ret = nct7904_read_reg(data, BANK_0, SMI_STS1_REG + i); if (ret < 0) return ret;
}
hwmon_dev =
devm_hwmon_device_register_with_info(dev, client->name, data,
&nct7904_chip_info, NULL);
ret = PTR_ERR_OR_ZERO(hwmon_dev); if (ret) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.