amd/stoneyridge: Disable GPIO MASK STATUS

MASK_STATUS disables interrupt status generation for the entire GPIO
controller when any debounce register is configured. This causes
problems when the kernel is loading drivers because we could lose
interrupts for previously loaded devices.

sb_program_gpios is also not setup to wait when configuring
PAD_DEBOUNCE, so there is a potential that we could lose the interrupt
status enable bit for other registers. By disabling MASK_STATUS we avoid
that problem.

BUG=b:113880780
BRANCH=none
TEST=Ran a reboot stress test that concluded that we are no longer
losing TPM interrupts while booting.

Change-Id: Ife1db3b1449f205092509595cbc3eca511bff57a
Signed-off-by: Raul E Rangel <rrangel@chromium.org>
Reviewed-on: https://review.coreboot.org/c/31082
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Daniel Kurtz <djkurtz@google.com>
diff --git a/src/soc/amd/stoneyridge/gpio.c b/src/soc/amd/stoneyridge/gpio.c
index 955cc6a..621aca3 100644
--- a/src/soc/amd/stoneyridge/gpio.c
+++ b/src/soc/amd/stoneyridge/gpio.c
@@ -222,15 +222,30 @@
 void sb_program_gpios(const struct soc_amd_gpio *gpio_list_ptr, size_t size)
 {
 	uint8_t *mux_ptr;
-	uint32_t *gpio_ptr;
+	uint32_t *gpio_ptr, *inter_master;
 	uint32_t control, control_flags, edge_level, direction;
 	uint32_t mask, bit_edge, bit_level;
 	uint8_t mux, index, gpio;
 	int gevent_num;
 
+	inter_master = (uint32_t *)(uintptr_t)(GPIO_CONTROL_MMIO_BASE
+					       + GPIO_MASTER_SWITCH);
 	direction = 0;
 	edge_level = 0;
 	mask = 0;
+
+	/*
+	 * Disable blocking wake/interrupt status generation while updating
+	 * debounce registers. Otherwise when a debounce register is updated
+	 * the whole GPIO controller will zero out all interrupt enable status
+	 * bits while the delay happens. This could cause us to drop the bits
+	 * due to the read-modify-write that happens on each register.
+	 *
+	 * Additionally disable interrupt generation so we don't get any
+	 * spurious interrupts while updating the registers.
+	 */
+	mem_read_write32(inter_master, 0, GPIO_MASK_STS_EN | GPIO_INTERRUPT_EN);
+
 	for (index = 0; index < size; index++) {
 		gpio = gpio_list_ptr[index].gpio;
 		mux = gpio_list_ptr[index].function;
@@ -290,6 +305,16 @@
 						AMD_GPIO_CONTROL_MASK);
 		}
 	}
+
+	/*
+	 * Re-enable interrupt status generation.
+	 *
+	 * We leave MASK_STATUS disabled because the kernel may reconfigure the
+	 * debounce registers while the drivers load. This will cause interrupts
+	 * to be missed during boot.
+	 */
+	mem_read_write32(inter_master, GPIO_INTERRUPT_EN, GPIO_INTERRUPT_EN);
+
 	/* Set all SCI trigger direction (high/low) */
 	mem_read_write32((uint32_t *)(uintptr_t)(APU_SMI_BASE + SMI_SCI_TRIG),
 					direction, mask);
diff --git a/src/soc/amd/stoneyridge/include/soc/gpio.h b/src/soc/amd/stoneyridge/include/soc/gpio.h
index 47eae84..fe8240f 100644
--- a/src/soc/amd/stoneyridge/include/soc/gpio.h
+++ b/src/soc/amd/stoneyridge/include/soc/gpio.h
@@ -41,6 +41,10 @@
 	uint8_t mux_value;
 };
 
+#define GPIO_MASTER_SWITCH	0xFC
+#define   GPIO_MASK_STS_EN	BIT(28)
+#define   GPIO_INTERRUPT_EN	BIT(30)
+
 #define GPIO_I2C0_SCL		BIT(0)
 #define GPIO_I2C1_SCL		BIT(1)
 #define GPIO_I2C2_SCL		BIT(2)