superio/nuvoton/nct5104d: Add soft reset GPIO functionality

So far, only hard power off could reset GPIOs state to defaults:
IN, Open-drain. Now, defaults are set with every boot to ensure
that GPIOs are not in unknown/unwanted state.

Change-Id: I67878dbab2ddf0deaaa8f5d79416368c6164ba1d
Signed-off-by: Piotr Kleinschmidt <piotr.kleinschmidt@3mdeb.com>
Signed-off-by: Michał Żygowski <michal.zygowski@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/35482
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
diff --git a/src/superio/nuvoton/nct5104d/nct5104d.h b/src/superio/nuvoton/nct5104d/nct5104d.h
index 707e94a..9a88105 100644
--- a/src/superio/nuvoton/nct5104d/nct5104d.h
+++ b/src/superio/nuvoton/nct5104d/nct5104d.h
@@ -26,6 +26,16 @@
 #define GLOBAL_OPTION_CR26  0x26
 #define CR26_LOCK_REG       (1 << 4) /* required to access CR10/CR11 */
 
+/* LDN 0x07 specific registers */
+#define NCT5104D_GPIO0_IO   0xE0
+#define NCT5104D_GPIO1_IO   0xE4
+#define NCT5104D_GPIO6_IO   0xF8
+
+/* LDN 0x0F specific registers */
+#define NCT5104D_GPIO0_PP_OD   0xE0
+#define NCT5104D_GPIO1_PP_OD   0xE1
+#define NCT5104D_GPIO6_PP_OD   0xE6
+
 /* Logical Device Numbers (LDN). */
 #define NCT5104D_FDC        0x00 /* FDC - not pinned out */
 #define NCT5104D_SP1        0x02 /* UARTA */
diff --git a/src/superio/nuvoton/nct5104d/superio.c b/src/superio/nuvoton/nct5104d/superio.c
index 493e0ce..69f54a7 100644
--- a/src/superio/nuvoton/nct5104d/superio.c
+++ b/src/superio/nuvoton/nct5104d/superio.c
@@ -106,6 +106,47 @@
 	pnp_write_config(dev, 0x1c, reg);
 }
 
+static void reset_gpio_default_in(struct device *dev)
+{
+	pnp_set_logical_device(dev);
+
+	/* Soft reset GPIOs to default state: IN */
+	switch (dev->path.pnp.device) {
+	case NCT5104D_GPIO0:
+		pnp_write_config(dev, NCT5104D_GPIO0_IO, 0xFF);
+		break;
+	case NCT5104D_GPIO1:
+		pnp_write_config(dev, NCT5104D_GPIO1_IO, 0xFF);
+		break;
+	case NCT5104D_GPIO6:
+		pnp_write_config(dev, NCT5104D_GPIO6_IO, 0xFF);
+		break;
+	default:
+		break;
+	}
+}
+
+static void reset_gpio_default_od(struct device *dev)
+{
+	struct device *gpio0, *gpio1, *gpio6;
+
+	gpio0 = dev_find_slot_pnp(dev->path.pnp.port, NCT5104D_GPIO0);
+	gpio1 = dev_find_slot_pnp(dev->path.pnp.port, NCT5104D_GPIO1);
+	gpio6 = dev_find_slot_pnp(dev->path.pnp.port, NCT5104D_GPIO6);
+
+	pnp_set_logical_device(dev);
+
+	/* Soft reset GPIOs to default state: Open-drain */
+	if (gpio0 && gpio0->enabled)
+		pnp_write_config(dev, NCT5104D_GPIO0_PP_OD, 0xFF);
+
+	if (gpio1 && gpio1->enabled)
+		pnp_write_config(dev, NCT5104D_GPIO1_PP_OD, 0xFF);
+
+	if (gpio6 && gpio6->enabled)
+		pnp_write_config(dev, NCT5104D_GPIO6_PP_OD, 0xFF);
+}
+
 static void nct5104d_init(struct device *dev)
 {
 	struct superio_nuvoton_nct5104d_config *conf = dev->chip_info;
@@ -128,6 +169,13 @@
 	case NCT5104D_GPIO0:
 	case NCT5104D_GPIO1:
 		route_pins_to_uart(dev, false);
+		reset_gpio_default_in(dev);
+		break;
+	case NCT5104D_GPIO6:
+		reset_gpio_default_in(dev);
+		break;
+	case NCT5104D_GPIO_PP_OD:
+		reset_gpio_default_od(dev);
 		break;
 	default:
 		break;
@@ -152,10 +200,10 @@
 	{ NULL, NCT5104D_SP3,  PNP_IO0 | PNP_IRQ0, 0x07f8, },
 	{ NULL, NCT5104D_SP4,  PNP_IO0 | PNP_IRQ0, 0x07f8, },
 	{ NULL, NCT5104D_GPIO_WDT},
-	{ NULL, NCT5104D_GPIO_PP_OD},
 	{ NULL, NCT5104D_GPIO0},
 	{ NULL, NCT5104D_GPIO1},
 	{ NULL, NCT5104D_GPIO6},
+	{ NULL, NCT5104D_GPIO_PP_OD},
 	{ NULL, NCT5104D_PORT80},
 };