Add Winbond Super I/O detection and hook it up to the generic Super I/O detection framework. Incomplete and untested, but at least it shouldn't break anything.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-winbond_superio_detect/flash.h =================================================================== --- flashrom-winbond_superio_detect/flash.h (Revision 1064) +++ flashrom-winbond_superio_detect/flash.h (Arbeitskopie) @@ -350,6 +350,7 @@ /* board_enable.c */ void w836xx_ext_enter(uint16_t port); void w836xx_ext_leave(uint16_t port); +struct superio probe_superio_winbond(void); uint8_t sio_read(uint16_t port, uint8_t reg); void sio_write(uint16_t port, uint8_t reg, uint8_t data); void sio_mask(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask); @@ -388,7 +389,8 @@ }; extern struct superio superio; #define SUPERIO_VENDOR_NONE 0x0 -#define SUPERIO_VENDOR_ITE 0x1 +#define SUPERIO_VENDOR_WINBOND 0x1 +#define SUPERIO_VENDOR_ITE 0x2 struct pci_dev *pci_dev_find_filter(struct pci_filter filter); struct pci_dev *pci_dev_find_vendorclass(uint16_t vendor, uint16_t class); struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device); Index: flashrom-winbond_superio_detect/internal.c =================================================================== --- flashrom-winbond_superio_detect/internal.c (Revision 1064) +++ flashrom-winbond_superio_detect/internal.c (Arbeitskopie) @@ -104,12 +104,14 @@
void probe_superio(void) { - superio = probe_superio_ite(); + superio = probe_superio_winbond(); #if 0 - /* Winbond Super I/O code is not yet available. */ + /* ITE probe causes SMSC LPC47N217 to power off the serial UART. */ if (superio.vendor == SUPERIO_VENDOR_NONE) - superio = probe_superio_winbond(); + superio = probe_superio_smsc(); #endif + if (superio.vendor == SUPERIO_VENDOR_NONE) + superio = probe_superio_ite(); } #endif
Index: flashrom-winbond_superio_detect/board_enable.c =================================================================== --- flashrom-winbond_superio_detect/board_enable.c (Revision 1064) +++ flashrom-winbond_superio_detect/board_enable.c (Arbeitskopie) @@ -229,22 +229,59 @@ address, but takes no effort to make sure the chip is really a Winbond Super I/O */
-static const struct winbond_chip * winbond_superio_detect(uint16_t base) +uint8_t probe_id_winbond(uint16_t port) { uint8_t chipid; + + w836xx_ext_enter(port); + chipid = sio_read(port, 0x20); + w836xx_ext_leave(port); + + return chipid; +} + +#define WINBOND_SUPERIO_PORT1 0x2e +#define WINBOND_SUPERIO_PORT2 0x4e + +struct superio probe_superio_winbond(void) +{ + struct superio ret = {}; + uint16_t winbond_ports[] = {WINBOND_SUPERIO_PORT1, WINBOND_SUPERIO_PORT2, 0}; + uint16_t *i = winbond_ports; + + ret.vendor = SUPERIO_VENDOR_WINBOND; + for (; *i; i++) { + ret.port = *i; + ret.model = probe_id_winbond(ret.port); + switch (ret.model) { + case WINBOND_W83627HF_ID: + case WINBOND_W83627EHF_ID: + case WINBOND_W83627THF_ID: + msg_pinfo("Found Winbond Super I/O, id %02hx\n", + ret.model); + return ret; + } + } + + /* No good ID found. */ + ret.vendor = SUPERIO_VENDOR_NONE; + ret.port = 0; + ret.model = 0; + return ret; +} + +static const struct winbond_chip *winbond_superio_chipdef(void) +{ const struct winbond_chip * chip = NULL; int i;
- w836xx_ext_enter(base); - chipid = sio_read(base, 0x20); for (i = 0; i < ARRAY_SIZE(winbond_chips); i++) - if (winbond_chips[i].device_id == chipid) + if (winbond_chips[i].device_id == superio.model) { chip = &winbond_chips[i]; break; } - w836xx_ext_leave(base); return chip; }
@@ -259,7 +296,7 @@ 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;
Am Mittwoch, den 30.06.2010, 14:12 +0200 schrieb Carl-Daniel Hailfinger:
-static const struct winbond_chip * winbond_superio_detect(uint16_t base) +uint8_t probe_id_winbond(uint16_t port) { uint8_t chipid;
- w836xx_ext_enter(port);
- chipid = sio_read(port, 0x20);
- w836xx_ext_leave(port);
- return chipid;
+}
If this code is to be run on any board, I would like some more checking in it. For example, you could check that sio_read(port, 0x20) returns 0xFF after the w836xx_ext_leave(). There are ECs that don't need any enables, so they will respond after the Winbond enable although they are not Winbond chips. But they still respond after the disable, so they can be told apart.
I think the kernel has Super I/O detection in the parport-pc driver. Maybe one could also peek there for safe detection code that has been tested on thousands of boxes.
+static const struct winbond_chip *winbond_superio_chipdef(void) +{ const struct winbond_chip * chip = NULL; int i;
- w836xx_ext_enter(base);
- chipid = sio_read(base, 0x20); for (i = 0; i < ARRAY_SIZE(winbond_chips); i++)
if (winbond_chips[i].device_id == chipid)
{ chip = &winbond_chips[i]; break; }if (winbond_chips[i].device_id == superio.model)
- w836xx_ext_leave(base); return chip;
}
Hmm. Shouldn't you check that superio.vendor is in fact SUPERIO_VENDOR_WINBOND here?
Regards, Michael Karcher
On 30.06.2010 14:48, Michael Karcher wrote:
Am Mittwoch, den 30.06.2010, 14:12 +0200 schrieb Carl-Daniel Hailfinger:
-static const struct winbond_chip * winbond_superio_detect(uint16_t base) +uint8_t probe_id_winbond(uint16_t port) { uint8_t chipid;
- w836xx_ext_enter(port);
- chipid = sio_read(port, 0x20);
- w836xx_ext_leave(port);
- return chipid;
+}
If this code is to be run on any board, I would like some more checking in it. For example, you could check that sio_read(port, 0x20) returns 0xFF after the w836xx_ext_leave(). There are ECs that don't need any enables, so they will respond after the Winbond enable although they are not Winbond chips. But they still respond after the disable, so they can be told apart.
That's a good idea, but my goal for this patch was to have a quick hack for discussion. We want to use the discovery code from superiotool or the Linux kernel to avoid a maintenance headache.
I think the kernel has Super I/O detection in the parport-pc driver. Maybe one could also peek there for safe detection code that has been tested on thousands of boxes.
Yes. http://git.openvz.org/?p=linux-2.6.32-openvz;a=blob;f=drivers/parport/parpor...
I'm not sure if that code is still tested at all. AFAIK most modern systems offer Parport discovery via ACPI.
+static const struct winbond_chip *winbond_superio_chipdef(void) +{ const struct winbond_chip * chip = NULL; int i;
- w836xx_ext_enter(base);
- chipid = sio_read(base, 0x20); for (i = 0; i < ARRAY_SIZE(winbond_chips); i++)
if (winbond_chips[i].device_id == chipid)
{ chip = &winbond_chips[i]; break; }if (winbond_chips[i].device_id == superio.model)
- w836xx_ext_leave(base); return chip;
}
Hmm. Shouldn't you check that superio.vendor is in fact SUPERIO_VENDOR_WINBOND here?
Yes, thanks for pointing this out.
We should try to decide which exernal superio detection code we want before we proceed.
Regards, Carl-Daniel
New version, this time with extremely paranoid checks.
Please note that this is only SuperI/O detection, we don't call any write enable functions.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-winbond_superio_detect/internal.c =================================================================== --- flashrom-winbond_superio_detect/internal.c (Revision 1530) +++ flashrom-winbond_superio_detect/internal.c (Arbeitskopie) @@ -101,12 +101,14 @@ #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; Index: flashrom-winbond_superio_detect/programmer.h =================================================================== --- flashrom-winbond_superio_detect/programmer.h (Revision 1530) +++ flashrom-winbond_superio_detect/programmer.h (Arbeitskopie) @@ -246,6 +246,7 @@ /* 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 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); Index: flashrom-winbond_superio_detect/board_enable.c =================================================================== --- flashrom-winbond_superio_detect/board_enable.c (Revision 1530) +++ flashrom-winbond_superio_detect/board_enable.c (Arbeitskopie) @@ -67,6 +67,17 @@ 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 @@ 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,117 @@ {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. */ + OUTB(0x4f, hwmport); + hwm_vendorid = INB(hwmport + 1) << 8; + /* Set HBACS=0. */ + sio_mask_alzheimer(hwmport, 0x4e, 0x00, 0x80); + /* Read lower byte of vendor ID. */ + OUTB(0x4f, hwmport); + hwm_vendorid |= INB(hwmport + 1); + 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. */ + OUTB(0x58, hwmport); + hwm_deviceid = INB(hwmport + 1); + 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 tmp; + + s.vendor = SUPERIO_VENDOR_WINBOND; + for (; *i; i++) { + s.port = *i; + 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); + 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 +365,7 @@ 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;
Am 06.05.2012 21:13 schrieb Carl-Daniel Hailfinger:
New version, this time with extremely paranoid checks.
Even more paranoid checks... and we now handle IT8707F and IT8710F in the Winbond detection routine.
Please note that this is only SuperI/O detection, we don't call any write enable functions.
IT8707F and IT8710F write enable needs to be hooked up in it87spi.c, the W836xx write enable functions need to be hooked up somewhere.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-winbond_superio_detect/internal.c =================================================================== --- flashrom-winbond_superio_detect/internal.c (Revision 1530) +++ flashrom-winbond_superio_detect/internal.c (Arbeitskopie) @@ -101,12 +101,14 @@ #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; Index: flashrom-winbond_superio_detect/programmer.h =================================================================== --- flashrom-winbond_superio_detect/programmer.h (Revision 1530) +++ flashrom-winbond_superio_detect/programmer.h (Arbeitskopie) @@ -246,6 +246,7 @@ /* 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 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); Index: flashrom-winbond_superio_detect/board_enable.c =================================================================== --- flashrom-winbond_superio_detect/board_enable.c (Revision 1530) +++ flashrom-winbond_superio_detect/board_enable.c (Arbeitskopie) @@ -67,6 +67,17 @@ 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 @@ 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,157 @@ {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. */ + OUTB(0x4f, hwmport); + hwm_vendorid = INB(hwmport + 1) << 8; + /* Set HBACS=0. */ + sio_mask_alzheimer(hwmport, 0x4e, 0x00, 0x80); + /* Read lower byte of vendor ID. */ + OUTB(0x4f, hwmport); + hwm_vendorid |= INB(hwmport + 1); + 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. */ + OUTB(0x58, hwmport); + hwm_deviceid = INB(hwmport + 1); + 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 +405,7 @@ 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;
On Sun, 2012-05-06 at 23:51 +0200, Carl-Daniel Hailfinger wrote:
Am 06.05.2012 21:13 schrieb Carl-Daniel Hailfinger:
New version, this time with extremely paranoid checks.
Even more paranoid checks... and we now handle IT8707F and IT8710F in the Winbond detection routine.
As these chips share the config mode init sequence, this makes sense.
Please note that this is only SuperI/O detection, we don't call any write enable functions.
IT8707F and IT8710F write enable needs to be hooked up in it87spi.c, the W836xx write enable functions need to be hooked up somewhere.
- /* Set HBACS=1. */
- sio_mask_alzheimer(hwmport, 0x4e, 0x80, 0x80);
- /* Read upper byte of vendor ID. */
- OUTB(0x4f, hwmport);
- hwm_vendorid = INB(hwmport + 1) << 8;
Why don't you use sio_read to read from the hardware monitor? (three times)
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;
}
I think this *is* to paranoid, but maybe you are right being paranoid.
Acked-by: Michael Karcher flashrom@mkarcher.dialup.fu-berlin.de
Thanks for working through the mess of detecting chip generically that were never meant for that.
Best regards, Michael Karcher
Am 07.05.2012 00:08 schrieb Michael Karcher:
On Sun, 2012-05-06 at 23:51 +0200, Carl-Daniel Hailfinger wrote:
Am 06.05.2012 21:13 schrieb Carl-Daniel Hailfinger:
New version, this time with extremely paranoid checks.
Even more paranoid checks... and we now handle IT8707F and IT8710F in the Winbond detection routine.
As these chips share the config mode init sequence, this makes sense.
Please note that this is only SuperI/O detection, we don't call any write enable functions.
IT8707F and IT8710F write enable needs to be hooked up in it87spi.c, the W836xx write enable functions need to be hooked up somewhere.
- /* Set HBACS=1. */
- sio_mask_alzheimer(hwmport, 0x4e, 0x80, 0x80);
- /* Read upper byte of vendor ID. */
- OUTB(0x4f, hwmport);
- hwm_vendorid = INB(hwmport + 1) << 8;
Why don't you use sio_read to read from the hardware monitor? (three times)
Good point. Fixed.
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;
}
I think this *is* too paranoid, but maybe you are right being paranoid.
I also think this is too paranoid, but hey... we can still disable it later.
Acked-by: Michael Karcher flashrom@mkarcher.dialup.fu-berlin.de
Thanks, I have attached the new version with sio_read for hwm accesses.
Thanks for working through the mess of detecting chip generically that were never meant for that.
Thanks.
New patch, changed to use sio_read in w836xx_deviceid_hwmon().
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net Acked-by: Michael Karcher flashrom@mkarcher.dialup.fu-berlin.de
Index: flashrom-winbond_superio_detect/internal.c =================================================================== --- flashrom-winbond_superio_detect/internal.c (Revision 1530) +++ flashrom-winbond_superio_detect/internal.c (Arbeitskopie) @@ -101,12 +101,14 @@ #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; Index: flashrom-winbond_superio_detect/programmer.h =================================================================== --- flashrom-winbond_superio_detect/programmer.h (Revision 1530) +++ flashrom-winbond_superio_detect/programmer.h (Arbeitskopie) @@ -246,6 +246,7 @@ /* 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 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); Index: flashrom-winbond_superio_detect/board_enable.c =================================================================== --- flashrom-winbond_superio_detect/board_enable.c (Revision 1530) +++ flashrom-winbond_superio_detect/board_enable.c (Arbeitskopie) @@ -67,6 +67,17 @@ 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 @@ 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 @@ {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 @@ 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;
Am 07.05.2012 00:44 schrieb Carl-Daniel Hailfinger:
Am 07.05.2012 00:08 schrieb Michael Karcher:
Thanks for working through the mess of detecting chip generically that were never meant for that.
Thanks.
New patch, changed to use sio_read in w836xx_deviceid_hwmon().
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net Acked-by: Michael Karcher flashrom@mkarcher.dialup.fu-berlin.de
Thanks for the review, committed in r1533.
Regards, Carl-Daniel