UPSTREAM: util/superiotool/nuvoton.c: Add NCT6687D-W register definitions

Based on public NCT6686D hardware datasheet revision 0.5 which should
be similar to NCT6687D.

TEST=Dump NCT6687D, GPIO and EC registers on MSI PRO Z690-A WIFI DDR4

(cherry picked from commit b33ee1da7d35c55a5d88583a96ae036b3cc458a2)

Original-Signed-off-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Original-Change-Id: I38db1de0f3d3b6de14bcb758afc9804c072c1895
Original-Reviewed-on: https://review.coreboot.org/c/coreboot/+/63868
Original-Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Original-Reviewed-by: Krystian Hebel <krystian.hebel@3mdeb.com>
GitOrigin-RevId: b33ee1da7d35c55a5d88583a96ae036b3cc458a2
Change-Id: I6b7005a182c6bd38042cd77347e9c02bbc807762
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/3948935
Tested-by: CopyBot Service Account <copybot.service@gmail.com>
Reviewed-by: Rob Barnes <robbarnes@google.com>
Tested-by: Rob Barnes <robbarnes@google.com>
Commit-Queue: Rob Barnes <robbarnes@google.com>
diff --git a/util/superiotool/nuvoton.c b/util/superiotool/nuvoton.c
index dfcbaf7..66dcfce 100644
--- a/util/superiotool/nuvoton.c
+++ b/util/superiotool/nuvoton.c
@@ -5,6 +5,41 @@
 #define DEVICE_ID_REG		0x20	/* Super I/O ID (SID) / family */
 #define DEVICE_REV_REG		0x27	/* Super I/O revision ID (SRID) */
 
+static uint8_t regread(uint16_t port, uint8_t reg)
+{
+	OUTB(reg, port);
+	return INB(port + 1);
+}
+
+/* For Nuvoton EC space */
+static void set_page(uint16_t port, uint8_t page)
+{
+	/*
+	 * INDEX reg can be written if PAGE reg is not 0xff
+	 * PAGE reg can be written if value or writing data is 0xff
+	 */
+	OUTB(0xff, port);
+	OUTB(page, port);
+}
+
+static void dump_page_index_data(uint16_t iobase)
+{
+	uint16_t i,j ;
+
+	for (i = 0; i < 255; i++) {
+		printf("Page %d:\n", i);
+		for (j = 0; j < 256; j++) {
+			if (j % 16 == 0)
+				printf("\n%02x: ", j);
+			/* PAGE must be selected before each data read */
+			set_page(iobase, i);
+			printf("%02x ", regread(iobase + 1, j));
+		}
+		printf("\n");
+	}
+	printf("\n");
+}
+
 static const struct superio_registers reg_table[] = {
 	{0xfc, "WPCE775x / NPCE781x", {
 		{NOLDN, NULL,
@@ -47,6 +82,73 @@
 		{EOT}}},
 	{0x1a, "WPCM450", {
 		{EOT}}},
+	{0xd592, "NCT6687D-W", {
+		{NOLDN, "Global Configuration",
+			{0x10,0x11,0x13,0x14,0x15,0x1a,0x1b,0x1d,0x1e,
+			 0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
+			 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2f,EOT},
+			{0xff,0xff,0x00,0x00,0x00,0x00,0x10,0x00,0x00,
+			 0x00,0xd5,0x92,0x00,0x80,0x67,0x01,0x00,0x3e,
+			 0x00,0x03,0x0f,0x00,0x00,0x00,MISC,EOT}},
+		{0x02, "Parallel Port",
+			{0x30,0x60,0x61,0x70,0x74,0xf0,EOT},
+			{0x00,0x00,0x00,0x00,0x00,0x3f,EOT}},
+		{0x02, "UART A",
+			{0x30,0x60,0x61,0x70,0xf0,EOT},
+			{0x00,0x00,0x00,0x00,0x00,EOT}},
+		{0x03, "UART B, IR",
+			{0x30,0x60,0x61,0x70,0xf0,0xf1,EOT},
+			{0x00,0x00,0x00,0x00,0x00,0x00,EOT}},
+		{0x05, "Keyboard Controller",
+			{0x30,0x60,0x61,0x62,0x63,0x70,0x72,0xf0,EOT},
+			{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,EOT}},
+		{0x06, "CIR",
+			{0x30,0x60,0x61,0x70,0xf0,0xf1,0xf2,0xf3,EOT},
+			{0x00,0x00,0x00,0x00,0x08,0x09,0x32,0x00,EOT}},
+		{0x07, "GPIO0-7",
+			{0x30,0x60,0x61,0x70,0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,
+			 0xe6,0xe7,0xe8,0xe9,0xeb,0xec,0xed,0xee,0xef,0xf0,
+			 0xf1,EOT},
+			{0x00,0x00,0x00,0x00,NANA,NANA,NANA,NANA,NANA,NANA,
+			 NANA,NANA,NANA,NANA,NANA,NANA,NANA,NANA,NANA,NANA,
+			 0x01,EOT}},
+		{0x08, "PORT80 UART",
+			{0xe0,0xe1,0xe2,0xe3,0xe4,EOT},
+			{0x80,0x00,0x00,0x10,0x00,EOT}},
+		{0x09, "GPIO8-9, GPIO1-8 Alternate Function",
+			{0x30,0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,
+			 0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,EOT},
+			{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+			 0x00,0x00,0x00,0x00,0x00,0x00,0x00,EOT}},
+		{0x0a, "ACPI",
+			{0x30,0x60,0x61,0x70,0xe0,0xe1,0xe2,0xe3,0xe4,0xe6,
+			 0xe7,0xe8,0xea,0xeb,0xec,0xee,0xf0,0xf1,0xf2,0xf3,
+			 0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,EOT},
+			{0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,
+			 0xef,0x80,0x2e,0x00,0x01,0x00,0x00,0x00,0x00,0x00,
+			 0x0d,0x0d,0x01,0x00,0x04,0x00,0x00,0x00,0x04,EOT}},
+		{0x0b, "EC",
+			{0x30,0x60,0x61,0x70,0xe0,0xe3,0xe4,EOT},
+			{0x00,0x00,0x00,0x00,0x00,0x00,0x00,EOT}},
+		{0x0c, "RTC",
+			{0x30,0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,
+			 0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf0,EOT},
+			{0x00,NANA,NANA,NANA,NANA,NANA,NANA,NANA,0x00,0x00,
+			 0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x80,EOT}},
+		{0x0d, "Deep Sleep, Power Fault",
+			{0x30,0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,
+			 0xf0,0xf1,0xf3,EOT},
+			{0xa0,0x20,0x04,0x05,0x6e,0x00,0x00,0x00,0x88,0x77,
+			 0x70,0xaa,0x01,EOT}},
+		{0x0e, "TACHIN/PWMOUT Assignment",
+			{0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,
+			 0xea,0xeb,EOT},
+			{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+			 0x00,0x00,EOT}},
+		{0x0f, "Function Register",
+			{0xe3,0xe4,0xe5,0xe8,0xe9,0xea,EOT},
+			{0x80,0x01,0x00,0x00,0x00,0x00,EOT}},
+		{EOT}}},
 	{0xb472, "NCT6775F (A)", {
 		{NOLDN, NULL,
 			{0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
@@ -682,6 +784,74 @@
 	{EOT}
 };
 
+static void dump_nct6687d_gpios(uint16_t port)
+{
+	uint8_t group, sel;
+
+	const char *gpio_groups[] ={
+		"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+		"Reserved", /* Does not exist */
+		"EN0", /* Enhance 0 */
+		"EN1", /* Enhance 1 */
+	};
+
+	/* Used by reg at 0xd offset to select which registers to reflect */
+	const char *gpio_sel[] ={
+		"GPIO Data",
+		"GPIO Interrupt Enable",
+		"GPIO Status",
+		"GPIO I/O Control",
+		"GPIO Inversion Control",
+		"GPIO PP/OD Control",
+		"GPIO Interrupt Type",
+		"GPIO Output Data Reflection",
+		"GPIO Internal Pull Down Control",
+		"GPIO Reset Source Control",
+		"Reserved",
+		/* Below are valid only for GPIO Enhance Group 0 and 1 */
+		"GPIO De-bounce Clock Option",
+		"GPIO De-bounce Type 0",
+		"GPIO De-bounce Type 1",
+		"GPIO De-bounce Time Option 0",
+		"GPIO De-bounce Time Option 1",
+	};
+
+	enter_conf_mode_winbond_fintek_ite_8787(port);
+	regwrite(port, LDN_SEL, 0x07);
+
+	printf("\nDumping GPIO configuration...\n\n");
+
+	printf("%-35s", "GPIO Group");
+	for (group = 0; group < ARRAY_SIZE(gpio_groups); group++) {
+		if (group == 10)
+			continue;
+		printf("%-5s", gpio_groups[group]);
+	}
+
+	printf("\n");
+
+	for (sel = 0; sel < ARRAY_SIZE(gpio_sel); sel++) {
+		if (sel == 10)
+			continue;
+		printf("%-35s", gpio_sel[sel]);
+		for (group = 0; group < ARRAY_SIZE(gpio_groups); group++) {
+			if (group == 10)
+				continue;
+			/* Select GPIO group */
+			regwrite(port, 0xf0, group);
+			if (group < 11 && sel > 10)
+				printf("XX   ");
+			else
+				/* GPIO registers start at LDN 7 offset 0xe0 */
+				printf("%02x   ", regval(port, 0xe0 + sel));
+		}
+		printf("\n");
+	}
+
+	printf("\n");
+	exit_conf_mode_winbond_fintek_ite_8787(port);
+}
+
 void probe_idregs_nuvoton(uint16_t port)
 {
 	uint8_t sid, srid;
@@ -758,6 +928,17 @@
 			for (i = 0; i < 10; i++)
 				dump_data(iobase + 5, i);
 			break;
+		case 0xd590: /* NCT6687D-W */
+			dump_nct6687d_gpios(port);
+			/* One can use the APCI/BIOS register set, although the
+			 * resulting data is still the same when using software
+			 * register set.
+			 * printf("EC I/O base for ACPI/BIOS: 0x%x\n", iobase);
+			 * dump_page_index_data(iobase);
+			*/
+			printf("EC I/O base for software: 0x%x\n", iobase + 4);
+			dump_page_index_data(iobase + 4);
+			break;
 		}
 	}
 }