* IT8622E Super I/O chip w/LPC interface
* IT8623E Super I/O chip w/LPC interface
* IT8628E Super I/O chip w/LPC interface
+ * IT8665E Super I/O chip w/LPC interface
+ * IT8686E Super I/O chip w/LPC interface
* IT8705F Super I/O chip w/LPC interface
* IT8712F Super I/O chip w/LPC interface
* IT8716F Super I/O chip w/LPC interface
enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8732,
it8771, it8772, it8781, it8782, it8783, it8786, it8790,
- it8792, it8603, it8607, it8620, it8622, it8628 };
+ it8792, it8603, it8607, it8620, it8622, it8628, it8665,
+ it8686 };
static unsigned short force_id;
module_param(force_id, ushort, 0);
#define IT8622E_DEVID 0x8622
#define IT8623E_DEVID 0x8623
#define IT8628E_DEVID 0x8628
+#define IT8665E_DEVID 0x8665
+#define IT8686E_DEVID 0x8686
#define IT87_ACT_REG 0x30
#define IT87_BASE_REG 0x60
#define IT87_SIO_GPIO3_REG 0x27
#define IT87_SIO_GPIO4_REG 0x28
#define IT87_SIO_GPIO5_REG 0x29
+#define IT87_SIO_GPIO9_REG 0xd3
#define IT87_SIO_PINX1_REG 0x2a /* Pin selection */
#define IT87_SIO_PINX2_REG 0x2c /* Pin selection */
+#define IT87_SIO_PINX4_REG 0x2d /* Pin selection */
#define IT87_SIO_SPI_REG 0xef /* SPI function pin select */
#define IT87_SIO_VID_REG 0xfc /* VID value */
#define IT87_SIO_BEEP_PIN_REG 0xf6 /* Beep pin mapping */
#define IT87_REG_ALARM2 0x02
#define IT87_REG_ALARM3 0x03
+#define IT87_REG_BANK 0x06
+
/*
* The IT8718F and IT8720F have the VID value in a different register, in
* Super-I/O configuration space.
#define FEAT_PWM_FREQ2 BIT(16) /* Separate pwm freq 2 */
#define FEAT_SIX_TEMP BIT(17) /* Up to 6 temp sensors */
#define FEAT_VIN3_5V BIT(18) /* VIN3 connected to +5V */
+#define FEAT_FOUR_FANS BIT(19) /* Supports four fans */
+#define FEAT_FOUR_PWM BIT(20) /* Supports four fan controls */
+#define FEAT_BANK_SEL BIT(21) /* Chip has multi-bank support */
+#define FEAT_SCALING BIT(22) /* Internal voltage scaling */
static const struct it87_devices it87_devices[] = {
[it87] = {
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
| FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL
- | FEAT_PWM_FREQ2,
+ | FEAT_PWM_FREQ2 | FEAT_SCALING,
.peci_mask = 0x05,
.old_peci_mask = 0x02, /* Actually reports PCH */
},
.suffix = "F",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
- | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2,
+ | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2 | FEAT_SCALING,
.peci_mask = 0x07,
},
[it8732] = {
.suffix = "F",
.features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
- | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL,
+ | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_FOUR_FANS
+ | FEAT_FOUR_PWM,
.peci_mask = 0x07,
.old_peci_mask = 0x02, /* Actually reports PCH */
},
.suffix = "E",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
- | FEAT_PWM_FREQ2,
+ | FEAT_PWM_FREQ2 | FEAT_SCALING,
/* PECI: guesswork */
/* 12mV ADC (OHM) */
/* 16 bit fans (OHM) */
.suffix = "E",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
- | FEAT_PWM_FREQ2,
+ | FEAT_PWM_FREQ2 | FEAT_SCALING,
/* PECI (coreboot) */
/* 12mV ADC (HWSensors4, OHM) */
/* 16 bit fans (HWSensors4, OHM) */
[it8790] = {
.name = "it8790",
.suffix = "E",
- .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
- | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
- | FEAT_PWM_FREQ2,
+ .features = FEAT_NEWER_AUTOPWM | FEAT_10_9MV_ADC | FEAT_SCALING
+ | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI
+ | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2,
.peci_mask = 0x07,
},
[it8792] = {
.name = "it8792",
.suffix = "E",
- .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
- | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
- | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL,
+ .features = FEAT_NEWER_AUTOPWM | FEAT_10_9MV_ADC | FEAT_SCALING
+ | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI
+ | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2,
.peci_mask = 0x07,
- .old_peci_mask = 0x02, /* Actually reports PCH */
},
[it8603] = {
.name = "it8603",
.suffix = "E",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
- | FEAT_AVCC3 | FEAT_PWM_FREQ2,
+ | FEAT_AVCC3 | FEAT_PWM_FREQ2 | FEAT_SCALING,
.peci_mask = 0x07,
},
[it8607] = {
.suffix = "E",
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
- | FEAT_AVCC3 | FEAT_PWM_FREQ2,
+ | FEAT_AVCC3 | FEAT_PWM_FREQ2 | FEAT_SCALING,
.peci_mask = 0x07,
},
[it8620] = {
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
| FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
- | FEAT_SIX_TEMP | FEAT_VIN3_5V,
+ | FEAT_SIX_TEMP | FEAT_VIN3_5V | FEAT_SCALING,
.peci_mask = 0x07,
},
[it8622] = {
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
| FEAT_FIVE_PWM | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2
- | FEAT_AVCC3 | FEAT_VIN3_5V,
+ | FEAT_AVCC3 | FEAT_VIN3_5V | FEAT_SCALING,
.peci_mask = 0x07,
},
[it8628] = {
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
| FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
- | FEAT_SIX_TEMP | FEAT_VIN3_5V,
+ | FEAT_SIX_TEMP | FEAT_SCALING,
+ .peci_mask = 0x07,
+ },
+ [it8665] = {
+ .name = "it8665",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
+ | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_SIX_FANS
+ | FEAT_SIX_PWM | FEAT_BANK_SEL,
+ .peci_mask = 0x07,
+ },
+ [it8686] = {
+ .name = "it8686",
+ .suffix = "E",
+ .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
+ | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
+ | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
+ | FEAT_SIX_TEMP | FEAT_BANK_SEL | FEAT_SCALING,
.peci_mask = 0x07,
},
};
#define has_pwm_freq2(data) ((data)->features & FEAT_PWM_FREQ2)
#define has_six_temp(data) ((data)->features & FEAT_SIX_TEMP)
#define has_vin3_5v(data) ((data)->features & FEAT_VIN3_5V)
+#define has_four_fans(data) ((data)->features & (FEAT_FOUR_FANS | \
+ FEAT_FIVE_FANS | \
+ FEAT_SIX_FANS))
+#define has_four_pwm(data) ((data)->features & (FEAT_FOUR_PWM | \
+ FEAT_FIVE_PWM \
+ | FEAT_SIX_PWM))
+#define has_bank_sel(data) ((data)->features & FEAT_BANK_SEL)
+#define has_scaling(data) ((data)->features & FEAT_SCALING)
struct it87_sio_data {
enum chips type;
const struct attribute_group *groups[7];
enum chips type;
u32 features;
+ u8 bank;
u8 peci_mask;
u8 old_peci_mask;
750000,
};
+static int _it87_read_value(struct it87_data *data, u8 reg)
+{
+ outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET);
+ return inb_p(data->addr + IT87_DATA_REG_OFFSET);
+}
+
+static void _it87_write_value(struct it87_data *data, u8 reg, u8 value)
+{
+ outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET);
+ outb_p(value, data->addr + IT87_DATA_REG_OFFSET);
+}
+
+static void it87_set_bank(struct it87_data *data, u8 bank)
+{
+ if (has_bank_sel(data) && bank != data->bank) {
+ u8 breg = _it87_read_value(data, IT87_REG_BANK);
+
+ breg &= 0x1f;
+ breg |= (bank << 5);
+ data->bank = bank;
+ _it87_write_value(data, IT87_REG_BANK, breg);
+ }
+}
+
/*
* Must be called with data->update_lock held, except during initialization.
* We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
* would slow down the IT87 access and should not be necessary.
*/
-static int it87_read_value(struct it87_data *data, u8 reg)
+static int it87_read_value(struct it87_data *data, u16 reg)
{
- outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET);
- return inb_p(data->addr + IT87_DATA_REG_OFFSET);
+ it87_set_bank(data, reg >> 8);
+ return _it87_read_value(data, reg & 0xff);
}
/*
* We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks,
* would slow down the IT87 access and should not be necessary.
*/
-static void it87_write_value(struct it87_data *data, u8 reg, u8 value)
+static void it87_write_value(struct it87_data *data, u16 reg, u8 value)
{
- outb_p(reg, data->addr + IT87_ADDR_REG_OFFSET);
- outb_p(value, data->addr + IT87_DATA_REG_OFFSET);
+ it87_set_bank(data, reg >> 8);
+ _it87_write_value(data, reg & 0xff, value);
}
static void it87_update_pwm_ctrl(struct it87_data *data, int nr)
return err;
err = -ENODEV;
- chip_type = force_id ? force_id : superio_inw(sioaddr, DEVID);
+ chip_type = superio_inw(sioaddr, DEVID);
+ if (chip_type == 0xffff)
+ goto exit;
+
+ if (force_id)
+ chip_type = force_id;
switch (chip_type) {
case IT8705F_DEVID:
case IT8628E_DEVID:
sio_data->type = it8628;
break;
+ case IT8665E_DEVID:
+ sio_data->type = it8665;
+ break;
+ case IT8686E_DEVID:
+ sio_data->type = it8686;
+ break;
case 0xffff: /* No device at all */
goto exit;
default:
else
sio_data->skip_in |= BIT(9);
- if (!has_five_pwm(config))
+ if (!has_four_pwm(config))
sio_data->skip_pwm |= BIT(3) | BIT(4) | BIT(5);
+ else if (!has_five_pwm(config))
+ sio_data->skip_pwm |= BIT(4) | BIT(5);
else if (!has_six_pwm(config))
sio_data->skip_pwm |= BIT(5);
sio_data->beep_pin = superio_inb(sioaddr,
IT87_SIO_BEEP_PIN_REG) & 0x3f;
- } else if (sio_data->type == it8620 || sio_data->type == it8628) {
+ } else if (sio_data->type == it8620 || sio_data->type == it8628 ||
+ sio_data->type == it8686) {
int reg;
superio_select(sioaddr, GPIO);
if (!(reg & BIT(0)))
sio_data->skip_in |= BIT(9);
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8732) {
+ int reg;
+
+ superio_select(sioaddr, GPIO);
+
+ /* Check for pwm2, fan2 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG);
+ if (reg & BIT(1))
+ sio_data->skip_pwm |= BIT(1);
+ if (reg & BIT(2))
+ sio_data->skip_fan |= BIT(1);
+
+ /* Check for pwm3, fan3, fan4 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
+ if (reg & BIT(6))
+ sio_data->skip_pwm |= BIT(2);
+ if (reg & BIT(7))
+ sio_data->skip_fan |= BIT(2);
+ if (reg & BIT(5))
+ sio_data->skip_fan |= BIT(3);
+
+ /* Check if AVCC is on VIN3 */
+ reg = superio_inb(sioaddr, IT87_SIO_PINX2_REG);
+ if (reg & BIT(0))
+ sio_data->internal |= BIT(0);
+
+ sio_data->beep_pin = superio_inb(sioaddr,
+ IT87_SIO_BEEP_PIN_REG) & 0x3f;
+ } else if (sio_data->type == it8665) {
+ int reg;
+
+ superio_select(sioaddr, GPIO);
+
+ /* Check for pwm2 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO5_REG);
+ if (reg & BIT(1))
+ sio_data->skip_pwm |= BIT(1);
+
+ /* Check for fan2 */
+ reg = superio_inb(sioaddr, IT87_SIO_PINX4_REG);
+ if (reg & BIT(4))
+ sio_data->skip_fan |= BIT(1);
+
+ /* Check for pwm3, fan3 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
+ if (reg & BIT(6))
+ sio_data->skip_pwm |= BIT(2);
+ if (reg & BIT(7))
+ sio_data->skip_fan |= BIT(2);
+
+ /* Check for pwm5, fan5 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO2_REG);
+ if (reg & BIT(5))
+ sio_data->skip_pwm |= BIT(4);
+ if (!(reg & BIT(4)))
+ sio_data->skip_fan |= BIT(4);
+
+ /* Check for pwm4, fan4, pwm6, fan6 */
+ reg = superio_inb(sioaddr, IT87_SIO_GPIO9_REG);
+ if (reg & BIT(2))
+ sio_data->skip_pwm |= BIT(3);
+ if (reg & BIT(3))
+ sio_data->skip_fan |= BIT(3);
+ if (reg & BIT(0))
+ sio_data->skip_pwm |= BIT(5);
+ if (reg & BIT(1))
+ sio_data->skip_fan |= BIT(5);
+
sio_data->beep_pin = superio_inb(sioaddr,
IT87_SIO_BEEP_PIN_REG) & 0x3f;
} else {
}
/* Check for additional fans */
- if (has_five_fans(data)) {
- if (tmp & BIT(4))
- data->has_fan |= BIT(3); /* fan4 enabled */
- if (tmp & BIT(5))
- data->has_fan |= BIT(4); /* fan5 enabled */
- if (has_six_fans(data) && (tmp & BIT(2)))
- data->has_fan |= BIT(5); /* fan6 enabled */
+ if (has_four_fans(data) && (tmp & BIT(4)))
+ data->has_fan |= BIT(3); /* fan4 enabled */
+ if (has_five_fans(data) && (tmp & BIT(5)))
+ data->has_fan |= BIT(4); /* fan5 enabled */
+ if (has_six_fans(data)) {
+ switch (data->type) {
+ case it8620:
+ case it8628:
+ case it8686:
+ if (tmp & BIT(2))
+ data->has_fan |= BIT(5); /* fan6 enabled */
+ break;
+ case it8665:
+ tmp = it87_read_value(data, IT87_REG_FAN_DIV);
+ if (tmp & BIT(3))
+ data->has_fan |= BIT(5); /* fan6 enabled */
+ break;
+ default:
+ break;
+ }
}
/* Fan input pins may be used for alternative functions */
data->has_fan &= ~sio_data->skip_fan;
- /* Check if pwm5, pwm6 are enabled */
+ /* Check if pwm6 is enabled */
if (has_six_pwm(data)) {
- /* The following code may be IT8620E specific */
- tmp = it87_read_value(data, IT87_REG_FAN_DIV);
- if ((tmp & 0xc0) == 0xc0)
- sio_data->skip_pwm |= BIT(4);
- if (!(tmp & BIT(3)))
- sio_data->skip_pwm |= BIT(5);
+ switch (data->type) {
+ case it8620:
+ case it8686:
+ tmp = it87_read_value(data, IT87_REG_FAN_DIV);
+ if (!(tmp & BIT(3)))
+ sio_data->skip_pwm |= BIT(5);
+ break;
+ default:
+ break;
+ }
}
/* Start monitoring */
data->features = it87_devices[sio_data->type].features;
data->peci_mask = it87_devices[sio_data->type].peci_mask;
data->old_peci_mask = it87_devices[sio_data->type].old_peci_mask;
+ data->bank = 0xff;
+
/*
* IT8705F Datasheet 0.4.1, 3h == Version G.
* IT8712F Datasheet 0.9.1, section 8.3.5 indicates 8h == Version J.
enable_pwm_interface = it87_check_pwm(dev);
/* Starting with IT8721F, we handle scaling of internal voltages */
- if (has_12mv_adc(data)) {
+ if (has_scaling(data)) {
if (sio_data->internal & BIT(0))
data->in_scaled |= BIT(3); /* in3 is AVCC */
if (sio_data->internal & BIT(1))