[flashrom] [PATCH] Add Winbond SuperI/O detection

Carl-Daniel Hailfinger c-d.hailfinger.devel.2006 at gmx.net
Sun May 6 21:13:29 CEST 2012


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 at 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;


-- 
http://www.hailfinger.org/





More information about the flashrom mailing list