diff options
author | Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net> | 2012-05-06 22:48:01 +0000 |
---|---|---|
committer | Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net> | 2012-05-06 22:48:01 +0000 |
commit | f5e62cb151b4f109a319b58baedcd83976c9ac35 (patch) | |
tree | 57c496013e2629ec46ec4d35262cd5aefd0a2965 | |
parent | 5e695ab4d7555952f0b020f235d868955251e3ae (diff) | |
download | flashrom-f5e62cb151b4f109a319b58baedcd83976c9ac35.tar.gz flashrom-f5e62cb151b4f109a319b58baedcd83976c9ac35.tar.bz2 flashrom-f5e62cb151b4f109a319b58baedcd83976c9ac35.zip |
Add Winbond W836xx SuperI/O detection
Add ITE IT8707F/IT8710F detection.
Note that we autodetect those chips, but we don't handle their flash
translation features automatically yet.
Corresponding to flashrom svn r1533.
Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
Acked-by: Michael Karcher <flashrom@mkarcher.dialup.fu-berlin.de>
-rw-r--r-- | board_enable.c | 168 | ||||
-rw-r--r-- | internal.c | 12 | ||||
-rw-r--r-- | programmer.h | 2 |
3 files changed, 162 insertions, 20 deletions
diff --git a/board_enable.c b/board_enable.c index 440a77a9..56d3081f 100644 --- a/board_enable.c +++ b/board_enable.c @@ -67,6 +67,17 @@ void sio_mask(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask) OUTB(tmp | (data & mask), port + 1); } +/* Winbond W83697 documentation indicates that the index register has to be written for each access. */ +void sio_mask_alzheimer(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask) +{ + uint8_t tmp; + + OUTB(reg, port); + tmp = INB(port + 1) & ~mask; + OUTB(reg, port); + OUTB(tmp | (data & mask), port + 1); +} + /* Not used yet. */ #if 0 static int enable_flash_decode_superio(void) @@ -163,6 +174,7 @@ enum winbond_id { WINBOND_W83627HF_ID = 0x52, WINBOND_W83627EHF_ID = 0x88, WINBOND_W83627THF_ID = 0x82, + WINBOND_W83697HF_ID = 0x60, }; static const struct winbond_mux w83627hf_port2_mux[8] = { @@ -227,28 +239,154 @@ static const struct winbond_chip winbond_chips[] = { {WINBOND_W83627THF_ID, ARRAY_SIZE(w83627thf), w83627thf}, }; -/* - * Detects which Winbond Super I/O is responding at the given base address, - * but takes no effort to make sure the chip is really a Winbond Super I/O. +#define WINBOND_SUPERIO_PORT1 0x2e +#define WINBOND_SUPERIO_PORT2 0x4e + +/* We don't really care about the hardware monitor, but it offers better (more specific) device ID info than + * the simple device ID in the normal configuration registers. + * Note: This function expects to be called while the Super I/O is in config mode. */ -static const struct winbond_chip *winbond_superio_detect(uint16_t base) +static uint8_t w836xx_deviceid_hwmon(uint16_t sio_port) { - uint8_t chipid; - const struct winbond_chip *chip = NULL; - int i; + uint16_t hwmport; + uint16_t hwm_vendorid; + uint8_t hwm_deviceid; - w836xx_ext_enter(base); - chipid = sio_read(base, 0x20); + sio_write(sio_port, 0x07, 0x0b); /* Select LDN 0xb (HWM). */ + if ((sio_read(sio_port, 0x30) & (1 << 0)) != (1 << 0)) { + msg_pinfo("W836xx hardware monitor disabled or does not exist.\n"); + return 0; + } + /* Get HWM base address (stored in LDN 0xb, index 0x60/0x61). */ + hwmport = sio_read(sio_port, 0x60) << 8; + hwmport |= sio_read(sio_port, 0x61); + /* HWM address register = HWM base address + 5. */ + hwmport += 5; + msg_pdbg2("W836xx Hardware Monitor at port %04x\n", hwmport); + /* FIXME: This busy check should happen before each HWM access. */ + if (INB(hwmport) & 0x80) { + msg_pinfo("W836xx hardware monitor busy, ignoring it.\n"); + return 0; + } + /* Set HBACS=1. */ + sio_mask_alzheimer(hwmport, 0x4e, 0x80, 0x80); + /* Read upper byte of vendor ID. */ + hwm_vendorid = sio_read(hwmport, 0x4f) << 8; + /* Set HBACS=0. */ + sio_mask_alzheimer(hwmport, 0x4e, 0x00, 0x80); + /* Read lower byte of vendor ID. */ + hwm_vendorid |= sio_read(hwmport, 0x4f); + if (hwm_vendorid != 0x5ca3) { + msg_pinfo("W836xx hardware monitor vendor ID weirdness: expected 0x5ca3, got %04x\n", + hwm_vendorid); + return 0; + } + /* Set Bank=0. */ + sio_mask_alzheimer(hwmport, 0x4e, 0x00, 0x07); + /* Read "chip" ID. We call this one the device ID. */ + hwm_deviceid = sio_read(hwmport, 0x58); + return hwm_deviceid; +} - for (i = 0; i < ARRAY_SIZE(winbond_chips); i++) { - if (winbond_chips[i].device_id == chipid) { - chip = &winbond_chips[i]; +void probe_superio_winbond(void) +{ + struct superio s = {}; + uint16_t winbond_ports[] = {WINBOND_SUPERIO_PORT1, WINBOND_SUPERIO_PORT2, 0}; + uint16_t *i = winbond_ports; + uint8_t model; + uint8_t tmp; + + s.vendor = SUPERIO_VENDOR_WINBOND; + for (; *i; i++) { + s.port = *i; + /* If we're already in Super I/O config more, the W836xx enter sequence won't hurt. */ + w836xx_ext_enter(s.port); + model = sio_read(s.port, 0x20); + /* No response, no point leaving the config mode. */ + if (model == 0xff) + continue; + /* Try to leave config mode. If the ID register is still readable, it's not a Winbond chip. */ + w836xx_ext_leave(s.port); + if (model == sio_read(s.port, 0x20)) { + msg_pdbg("W836xx enter config mode worked or we were already in config mode. W836xx " + "leave config mode had no effect.\n"); + if (model == 0x87) { + /* ITE IT8707F and IT8710F are special: They need the W837xx enter sequence, + * but they want the ITE exit sequence. Handle them here. + */ + tmp = sio_read(s.port, 0x21); + switch (tmp) { + case 0x07: + case 0x10: + s.vendor = SUPERIO_VENDOR_ITE; + s.model = (0x87 << 8) | tmp ; + msg_pdbg("Found ITE Super I/O, ID 0x%04hx on port " + "0x%x\n", s.model, s.port); + register_superio(s); + /* Exit ITE config mode. */ + exit_conf_mode_ite(s.port); + /* Restore vendor for next loop iteration. */ + s.vendor = SUPERIO_VENDOR_WINBOND; + continue; + } + } + msg_pinfo("Active config mode, unknown reg 0x20 ID: %02x.\n", model); + msg_pinfo("Please send the output of \"flashrom -V\" to \n" + "flashrom@flashrom.org with W836xx: your board name: flashrom -V\n" + "as the subject to help us finish support for your Super I/O. Thanks.\n"); + continue; + } + /* The Super I/O reacts to W836xx enter and exit config mode, it's probably Winbond. */ + w836xx_ext_enter(s.port); + s.model = sio_read(s.port, 0x20); + switch (s.model) { + case WINBOND_W83627HF_ID: + case WINBOND_W83627EHF_ID: + case WINBOND_W83627THF_ID: + msg_pdbg("Found Winbond Super I/O, id %02hx\n", s.model); + register_superio(s); + break; + case WINBOND_W83697HF_ID: + /* This code is extremely paranoid. */ + tmp = sio_read(s.port, 0x26) & 0x40; + if (((tmp == 0x00) && (s.port != WINBOND_SUPERIO_PORT1)) || + ((tmp == 0x40) && (s.port != WINBOND_SUPERIO_PORT2))) { + msg_pdbg("Winbond Super I/O probe weirdness: Port mismatch for ID " + "%02x at port %04x\n", s.model, s.port); + break; + } + tmp = w836xx_deviceid_hwmon(s.port); + /* FIXME: This might be too paranoid... */ + if (!tmp) { + msg_pdbg("Probably not a Winbond Super I/O\n"); + break; + } + if (tmp != s.model) { + msg_pinfo("W83 series hardware monitor device ID weirdness: expected %02x, " + "got %02x\n", WINBOND_W83697HF_ID, tmp); + break; + } + msg_pinfo("Found Winbond Super I/O, id %02hx\n", s.model); + register_superio(s); break; } + w836xx_ext_leave(s.port); } + return; +} - w836xx_ext_leave(base); - return chip; +static const struct winbond_chip *winbond_superio_chipdef(void) +{ + int i, j; + + for (i = 0; i < superio_count; i++) { + if (superios[i].vendor != SUPERIO_VENDOR_WINBOND) + continue; + for (j = 0; j < ARRAY_SIZE(winbond_chips); j++) + if (winbond_chips[j].device_id == superios[i].model) + return &winbond_chips[j]; + } + return NULL; } /* @@ -264,7 +402,7 @@ static int winbond_gpio_set(uint16_t base, enum winbond_id chipid, int port = pin / 10; int bit = pin % 10; - chip = winbond_superio_detect(base); + chip = winbond_superio_chipdef(); if (!chip) { msg_perr("\nERROR: No supported Winbond Super I/O found\n"); return -1; @@ -101,12 +101,14 @@ int force_boardmismatch = 0; #if defined(__i386__) || defined(__x86_64__) void probe_superio(void) { + probe_superio_winbond(); + /* ITE probe causes SMSC LPC47N217 to power off the serial UART. + * Always probe for SMSC first, and if a SMSC Super I/O is detected + * at a given I/O port, do _not_ probe that port with the ITE probe. + * This means SMSC probing must be done before ITE probing. + */ + //probe_superio_smsc(); probe_superio_ite(); -#if 0 - /* Winbond Super I/O code is not yet available. */ - if (superio.vendor == SUPERIO_VENDOR_NONE) - superio = probe_superio_winbond(); -#endif } int superio_count = 0; diff --git a/programmer.h b/programmer.h index 4d153e6c..6f9ea8f8 100644 --- a/programmer.h +++ b/programmer.h @@ -246,6 +246,7 @@ void print_supported_pcidevs(const struct pcidev_status *devs); /* board_enable.c */ void w836xx_ext_enter(uint16_t port); void w836xx_ext_leave(uint16_t port); +void probe_superio_winbond(void); int it8705f_write_enable(uint8_t port); uint8_t sio_read(uint16_t port, uint8_t reg); void sio_write(uint16_t port, uint8_t reg, uint8_t data); @@ -290,6 +291,7 @@ extern struct superio superios[]; extern int superio_count; #define SUPERIO_VENDOR_NONE 0x0 #define SUPERIO_VENDOR_ITE 0x1 +#define SUPERIO_VENDOR_WINBOND 0x2 #endif #if NEED_PCI == 1 struct pci_dev *pci_dev_find_filter(struct pci_filter filter); |