summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soc/amd/common/block/gpio_banks/gpio.c71
-rw-r--r--src/soc/amd/common/block/include/amdblocks/gpio_banks.h17
2 files changed, 88 insertions, 0 deletions
diff --git a/src/soc/amd/common/block/gpio_banks/gpio.c b/src/soc/amd/common/block/gpio_banks/gpio.c
index 4f1b842311..7fb6622f0a 100644
--- a/src/soc/amd/common/block/gpio_banks/gpio.c
+++ b/src/soc/amd/common/block/gpio_banks/gpio.c
@@ -3,12 +3,14 @@
#include <device/mmio.h>
#include <device/device.h>
#include <console/console.h>
+#include <elog.h>
#include <gpio.h>
#include <amdblocks/acpimmio.h>
#include <amdblocks/gpio_banks.h>
#include <soc/gpio.h>
#include <soc/smi.h>
#include <assert.h>
+#include <string.h>
static int get_gpio_gevent(uint8_t gpio, const struct soc_amd_event *table,
size_t items)
@@ -298,3 +300,72 @@ void gpio_configure_pads_with_override(const struct soc_amd_gpio *base_cfg,
program_gpios(c, 1);
}
}
+
+static void check_and_add_wake_gpio(int begin, int end, struct gpio_wake_state *state)
+{
+ int i;
+ uint32_t reg;
+
+ for (i = begin; i < end; i++) {
+ reg = gpio_read32(i);
+ if (!(reg & GPIO_WAKE_STATUS))
+ continue;
+ printk(BIOS_INFO, "GPIO %d woke system.\n", i);
+ if (state->num_valid_wake_gpios >= ARRAY_SIZE(state->wake_gpios))
+ continue;
+ state->wake_gpios[state->num_valid_wake_gpios++] = i;
+ }
+}
+
+static void check_gpios(uint32_t wake_stat, int bit_limit, int gpio_base,
+ struct gpio_wake_state *state)
+{
+ int i;
+ int begin;
+ int end;
+
+ for (i = 0; i < bit_limit; i++) {
+ if (!(wake_stat & BIT(i)))
+ continue;
+ begin = gpio_base + i * 4;
+ end = begin + 4;
+ /* There is no gpio 63. */
+ if (begin == 60)
+ end = 63;
+ check_and_add_wake_gpio(begin, end, state);
+ }
+}
+
+void gpio_fill_wake_state(struct gpio_wake_state *state)
+{
+ /* Turn the wake registers into "gpio" index to conform to existing API. */
+ const uint8_t stat0 = GPIO_WAKE_STAT_0 / sizeof(uint32_t);
+ const uint8_t stat1 = GPIO_WAKE_STAT_1 / sizeof(uint32_t);
+ const uint8_t control_switch = GPIO_MASTER_SWITCH / sizeof(uint32_t);
+
+ /* Register fields and gpio availability need to be confirmed on other chipsets. */
+ if (!CONFIG(SOC_AMD_PICASSO))
+ dead_code();
+
+ memset(state, 0, sizeof(*state));
+
+ state->control_switch = gpio_read32(control_switch);
+ state->wake_stat[0] = gpio_read32(stat0);
+ state->wake_stat[1] = gpio_read32(stat1);
+
+ printk(BIOS_INFO, "GPIO Control Switch: 0x%08x, Wake Stat 0: 0x%08x, Wake Stat 1: 0x%08x\n",
+ state->control_switch, state->wake_stat[0], state->wake_stat[1]);
+
+ check_gpios(state->wake_stat[0], 32, 0, state);
+ check_gpios(state->wake_stat[1], 14, 128, state);
+}
+
+void gpio_add_events(const struct gpio_wake_state *state)
+{
+ int i;
+ int end;
+
+ end = MIN(state->num_valid_wake_gpios, ARRAY_SIZE(state->wake_gpios));
+ for (i = 0; i < end; i++)
+ elog_add_event_wake(ELOG_WAKE_SOURCE_GPIO, state->wake_gpios[i]);
+}
diff --git a/src/soc/amd/common/block/include/amdblocks/gpio_banks.h b/src/soc/amd/common/block/include/amdblocks/gpio_banks.h
index fdcd0f8ae4..b1663cf374 100644
--- a/src/soc/amd/common/block/include/amdblocks/gpio_banks.h
+++ b/src/soc/amd/common/block/include/amdblocks/gpio_banks.h
@@ -20,6 +20,23 @@ struct soc_amd_event {
#define GPIO_MASTER_SWITCH 0xFC
#define GPIO_MASK_STS_EN BIT(28)
#define GPIO_INTERRUPT_EN BIT(30)
+#define GPIO_WAKE_EN BIT(31)
+
+#define GPIO_WAKE_STAT_0 0x2F0
+#define GPIO_WAKE_STAT_1 0x2F4
+struct gpio_wake_state {
+ uint32_t control_switch;
+ uint32_t wake_stat[2];
+ /* Number of wake_gpio with a valid setting. */
+ uint32_t num_valid_wake_gpios;
+ /* GPIO index number that caused a wake. */
+ uint8_t wake_gpios[16];
+};
+
+/* Fill gpio_wake_state object for future event reporting. */
+void gpio_fill_wake_state(struct gpio_wake_state *state);
+/* Add gpio events to the eventlog. */
+void gpio_add_events(const struct gpio_wake_state *state);
#define GPIO_PIN_IN (1 << 0) /* for byte access */
#define GPIO_PIN_OUT (1 << 6) /* for byte access */