From 2876e4f49aec84288d2b8d24885e5b22241fe907 Mon Sep 17 00:00:00 2001 From: Felix Held Date: Wed, 22 Sep 2021 18:20:54 +0200 Subject: soc/amd/common/blocks: rename gpio_banks folder to gpio This brings the AMD SoC GPIO code in line with the Intel SoC code and removes the not really needed suffix. Signed-off-by: Felix Held Change-Id: I3dfcca2f126eb49c962b5cc32cbcf72e04f3f170 Reviewed-on: https://review.coreboot.org/c/coreboot/+/57841 Tested-by: build bot (Jenkins) Reviewed-by: Raul Rangel --- src/soc/amd/common/block/gpio/Kconfig | 20 ++ src/soc/amd/common/block/gpio/Makefile.inc | 6 + src/soc/amd/common/block/gpio/gpio.c | 401 +++++++++++++++++++++++ src/soc/amd/common/block/gpio_banks/Kconfig | 20 -- src/soc/amd/common/block/gpio_banks/Makefile.inc | 6 - src/soc/amd/common/block/gpio_banks/gpio.c | 401 ----------------------- 6 files changed, 427 insertions(+), 427 deletions(-) create mode 100644 src/soc/amd/common/block/gpio/Kconfig create mode 100644 src/soc/amd/common/block/gpio/Makefile.inc create mode 100644 src/soc/amd/common/block/gpio/gpio.c delete mode 100644 src/soc/amd/common/block/gpio_banks/Kconfig delete mode 100644 src/soc/amd/common/block/gpio_banks/Makefile.inc delete mode 100644 src/soc/amd/common/block/gpio_banks/gpio.c diff --git a/src/soc/amd/common/block/gpio/Kconfig b/src/soc/amd/common/block/gpio/Kconfig new file mode 100644 index 0000000000..0a1bffee81 --- /dev/null +++ b/src/soc/amd/common/block/gpio/Kconfig @@ -0,0 +1,20 @@ +config SOC_AMD_COMMON_BLOCK_BANKED_GPIOS + bool + depends on SOC_AMD_COMMON_BLOCK_ACPIMMIO + help + Select this option to use the newer style banks of GPIO signals. + These are at offsets +0x1500, +0x1600, and +0x1700 from the AcpiMmio + base. + +if SOC_AMD_COMMON_BLOCK_BANKED_GPIOS + +config SOC_AMD_COMMON_BLOCK_BANKED_GPIOS_NON_SOC_CODEBASE + bool + help + Select this option when selecting the GPIO bank support from AMD + chipsets outside the soc/ subtree that only support a subset of the + features available on the chipsets inside the soc/ subtree. When this + option is selected, no SMI or SCI event can be configured by the GPIO + code. + +endif # SOC_AMD_COMMON_BLOCK_BANKED_GPIOS diff --git a/src/soc/amd/common/block/gpio/Makefile.inc b/src/soc/amd/common/block/gpio/Makefile.inc new file mode 100644 index 0000000000..616b58986d --- /dev/null +++ b/src/soc/amd/common/block/gpio/Makefile.inc @@ -0,0 +1,6 @@ +ifeq ($(CONFIG_SOC_AMD_COMMON_BLOCK_BANKED_GPIOS),y) + +all-y += gpio.c +smm-y += gpio.c + +endif # CONFIG_SOC_AMD_COMMON_BLOCK_BANKED_GPIOS diff --git a/src/soc/amd/common/block/gpio/gpio.c b/src/soc/amd/common/block/gpio/gpio.c new file mode 100644 index 0000000000..263757988a --- /dev/null +++ b/src/soc/amd/common/block/gpio/gpio.c @@ -0,0 +1,401 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * acpimmio_gpio0, acpimmio_remote_gpio and acpimmio_iomux are defined in + * soc/amd/common/block/acpimmio/mmio_util.c and declared as extern variables/constants in + * amdblocks/acpimmio.h which is included in this file. + */ + +/* MMIO access of new-style GPIO bank configuration registers */ +static inline void *gpio_ctrl_ptr(gpio_t gpio_num) +{ + if (SOC_GPIO_TOTAL_PINS < AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER || + /* Verstage on PSP would need to map acpimmio_remote_gpio */ + (CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK) && ENV_SEPARATE_VERSTAGE) || + gpio_num < AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER) + return acpimmio_gpio0 + gpio_num * sizeof(uint32_t); + else + return acpimmio_remote_gpio + + (gpio_num - AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER) * sizeof(uint32_t); +} + +static inline uint32_t gpio_read32(gpio_t gpio_num) +{ + return read32(gpio_ctrl_ptr(gpio_num)); +} + +static inline void gpio_write32(gpio_t gpio_num, uint32_t value) +{ + write32(gpio_ctrl_ptr(gpio_num), value); +} + +static inline void *gpio_mux_ptr(gpio_t gpio_num) +{ + if (SOC_GPIO_TOTAL_PINS < AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER || + /* Verstage on PSP would need to map acpimmio_remote_gpio */ + (CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK) && ENV_SEPARATE_VERSTAGE) || + gpio_num < AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER) + return acpimmio_iomux + gpio_num; + else + return acpimmio_remote_gpio + AMD_GPIO_REMOTE_GPIO_MUX_OFFSET + + (gpio_num - AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER); +} + +static uint8_t get_gpio_mux(gpio_t gpio_num) +{ + return read8(gpio_mux_ptr(gpio_num)); +} + +static void set_gpio_mux(gpio_t gpio_num, uint8_t function) +{ + write8(gpio_mux_ptr(gpio_num), function & AMD_GPIO_MUX_MASK); + get_gpio_mux(gpio_num); /* Flush posted write */ +} + +static int get_gpio_gevent(gpio_t gpio, const struct soc_amd_event *table, + size_t items) +{ + size_t i; + + for (i = 0; i < items; i++) { + if ((table + i)->gpio == gpio) + return (int)(table + i)->event; + } + return -1; +} + +static void program_smi(uint32_t flags, unsigned int gevent_num) +{ + uint8_t level; + + if (!is_gpio_event_level_triggered(flags)) { + printk(BIOS_ERR, "ERROR: %s - Only level trigger allowed for SMI!\n", __func__); + BUG(); + return; + } + + if (is_gpio_event_active_high(flags)) + level = SMI_SCI_LVL_HIGH; + else + level = SMI_SCI_LVL_LOW; + + configure_gevent_smi(gevent_num, SMI_MODE_SMI, level); +} + +/* + * For each general purpose event, GPE, the choice of edge/level triggered + * event is represented as a single bit in SMI_SCI_LEVEL register. + * + * In a similar fashion, polarity (rising/falling, hi/lo) of each GPE is + * represented as a single bit in SMI_SCI_TRIG register. + */ +static void program_sci(uint32_t flags, unsigned int gevent_num) +{ + struct sci_source sci; + + sci.scimap = gevent_num; + sci.gpe = gevent_num; + + if (is_gpio_event_level_triggered(flags)) + sci.level = SMI_SCI_LVL; + else + sci.level = SMI_SCI_EDG; + + if (is_gpio_event_active_high(flags)) + sci.direction = SMI_SCI_LVL_HIGH; + else + sci.direction = SMI_SCI_LVL_LOW; + + configure_scimap(&sci); +} + +uintptr_t gpio_get_address(gpio_t gpio_num) +{ + return (uintptr_t)gpio_ctrl_ptr(gpio_num); +} + +static void gpio_update32(gpio_t gpio_num, uint32_t mask, uint32_t or) +{ + uint32_t reg; + + reg = gpio_read32(gpio_num); + reg &= mask; + reg |= or; + gpio_write32(gpio_num, reg); +} + +/* Set specified bits of a register to match those of ctrl. */ +static void gpio_setbits32(gpio_t gpio_num, uint32_t mask, uint32_t ctrl) +{ + gpio_update32(gpio_num, ~mask, ctrl & mask); +} + +static void gpio_and32(gpio_t gpio_num, uint32_t mask) +{ + gpio_update32(gpio_num, mask, 0); +} + +static void gpio_or32(gpio_t gpio_num, uint32_t or) +{ + gpio_update32(gpio_num, -1UL, or); +} + +static void master_switch_clr(uint32_t mask) +{ + const gpio_t master_reg = GPIO_MASTER_SWITCH / sizeof(uint32_t); + gpio_and32(master_reg, ~mask); +} + +static void master_switch_set(uint32_t or) +{ + const gpio_t master_reg = GPIO_MASTER_SWITCH / sizeof(uint32_t); + gpio_or32(master_reg, or); +} + +int gpio_get(gpio_t gpio_num) +{ + uint32_t reg; + + reg = gpio_read32(gpio_num); + return !!(reg & GPIO_PIN_STS); +} + +void gpio_set(gpio_t gpio_num, int value) +{ + gpio_setbits32(gpio_num, GPIO_OUTPUT_VALUE, value ? GPIO_OUTPUT_VALUE : 0); +} + +void gpio_input_pulldown(gpio_t gpio_num) +{ + gpio_setbits32(gpio_num, GPIO_PULL_MASK | GPIO_OUTPUT_ENABLE, GPIO_PULLDOWN_ENABLE); +} + +void gpio_input_pullup(gpio_t gpio_num) +{ + gpio_setbits32(gpio_num, GPIO_PULL_MASK | GPIO_OUTPUT_ENABLE, GPIO_PULLUP_ENABLE); +} + +void gpio_input(gpio_t gpio_num) +{ + gpio_and32(gpio_num, ~(GPIO_PULL_MASK | GPIO_OUTPUT_ENABLE)); +} + +void gpio_output(gpio_t gpio_num, int value) +{ + /* set GPIO output value before setting the direction to output to avoid glitches */ + gpio_set(gpio_num, value); + gpio_or32(gpio_num, GPIO_OUTPUT_ENABLE); +} + +const char *gpio_acpi_path(gpio_t gpio) +{ + return "\\_SB.GPIO"; +} + +uint16_t gpio_acpi_pin(gpio_t gpio) +{ + return gpio; +} + +void gpio_save_pin_registers(gpio_t gpio, struct soc_amd_gpio_register_save *save) +{ + save->mux_value = get_gpio_mux(gpio); + save->control_value = gpio_read32(gpio); +} + +void gpio_restore_pin_registers(gpio_t gpio, struct soc_amd_gpio_register_save *save) +{ + set_gpio_mux(gpio, save->mux_value); + gpio_write32(gpio, save->control_value); + gpio_read32(gpio); /* Flush posted write */ +} + +static void set_single_gpio(const struct soc_amd_gpio *g) +{ + static const struct soc_amd_event *gev_tbl; + static size_t gev_items; + int gevent_num; + const bool can_set_smi_flags = !((CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK) && + ENV_SEPARATE_VERSTAGE) || + CONFIG(SOC_AMD_COMMON_BLOCK_BANKED_GPIOS_NON_SOC_CODEBASE)); + + set_gpio_mux(g->gpio, g->function); + + gpio_setbits32(g->gpio, PAD_CFG_MASK, g->control); + /* Clear interrupt and wake status (write 1-to-clear bits) */ + gpio_or32(g->gpio, GPIO_INT_STATUS | GPIO_WAKE_STATUS); + if (g->flags == 0) + return; + + /* Can't set SMI flags from PSP */ + if (!can_set_smi_flags) + return; + + if (gev_tbl == NULL) + soc_get_gpio_event_table(&gev_tbl, &gev_items); + + gevent_num = get_gpio_gevent(g->gpio, gev_tbl, gev_items); + if (gevent_num < 0) { + printk(BIOS_WARNING, "Warning: GPIO pin %d has no associated gevent!\n", + g->gpio); + return; + } + + if (g->flags & GPIO_FLAG_SMI) { + program_smi(g->flags, gevent_num); + } else if (g->flags & GPIO_FLAG_SCI) { + program_sci(g->flags, gevent_num); + } +} + +void gpio_configure_pads_with_override(const struct soc_amd_gpio *base_cfg, + size_t base_num_pads, + const struct soc_amd_gpio *override_cfg, + size_t override_num_pads) +{ + const struct soc_amd_gpio *c; + size_t i, j; + + if (!base_cfg || !base_num_pads) + return; + + /* + * 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. + */ + master_switch_clr(GPIO_MASK_STS_EN | GPIO_INTERRUPT_EN); + + for (i = 0; i < base_num_pads; i++) { + c = &base_cfg[i]; + /* Check if override exist for GPIO from the base configuration */ + for (j = 0; override_cfg && j < override_num_pads; j++) { + if (c->gpio == override_cfg[j].gpio) { + c = &override_cfg[j]; + break; + } + } + set_single_gpio(c); + } + + /* + * 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. + */ + master_switch_set(GPIO_INTERRUPT_EN); +} + +void gpio_configure_pads(const struct soc_amd_gpio *gpio_list_ptr, size_t size) +{ + gpio_configure_pads_with_override(gpio_list_ptr, size, NULL, 0); +} + +int gpio_interrupt_status(gpio_t gpio) +{ + uint32_t reg = gpio_read32(gpio); + + if (reg & GPIO_INT_STATUS) { + /* Clear interrupt status, preserve wake status */ + reg &= ~GPIO_WAKE_STATUS; + gpio_write32(gpio, reg); + return 1; + } + + return 0; +} + +static void check_and_add_wake_gpio(gpio_t begin, gpio_t end, struct gpio_wake_state *state) +{ + gpio_t 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, unsigned int bit_limit, gpio_t gpio_base, + struct gpio_wake_state *state) +{ + unsigned int i; + gpio_t begin; + gpio_t end; + + for (i = 0; i < bit_limit; i++) { + if (!(wake_stat & BIT(i))) + continue; + /* Each wake status register bit is for 4 GPIOs that then will be checked */ + 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 gpio_t stat0 = GPIO_WAKE_STAT_0 / sizeof(uint32_t); + const gpio_t stat1 = GPIO_WAKE_STAT_1 / sizeof(uint32_t); + const gpio_t control_switch = GPIO_MASTER_SWITCH / sizeof(uint32_t); + + 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(void) +{ + const struct chipset_power_state *ps; + const struct gpio_wake_state *state; + unsigned int i; + unsigned int end; + + if (acpi_pm_state_for_elog(&ps) < 0) + return; + state = &ps->gpio_state; + + 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/gpio_banks/Kconfig b/src/soc/amd/common/block/gpio_banks/Kconfig deleted file mode 100644 index 0a1bffee81..0000000000 --- a/src/soc/amd/common/block/gpio_banks/Kconfig +++ /dev/null @@ -1,20 +0,0 @@ -config SOC_AMD_COMMON_BLOCK_BANKED_GPIOS - bool - depends on SOC_AMD_COMMON_BLOCK_ACPIMMIO - help - Select this option to use the newer style banks of GPIO signals. - These are at offsets +0x1500, +0x1600, and +0x1700 from the AcpiMmio - base. - -if SOC_AMD_COMMON_BLOCK_BANKED_GPIOS - -config SOC_AMD_COMMON_BLOCK_BANKED_GPIOS_NON_SOC_CODEBASE - bool - help - Select this option when selecting the GPIO bank support from AMD - chipsets outside the soc/ subtree that only support a subset of the - features available on the chipsets inside the soc/ subtree. When this - option is selected, no SMI or SCI event can be configured by the GPIO - code. - -endif # SOC_AMD_COMMON_BLOCK_BANKED_GPIOS diff --git a/src/soc/amd/common/block/gpio_banks/Makefile.inc b/src/soc/amd/common/block/gpio_banks/Makefile.inc deleted file mode 100644 index 616b58986d..0000000000 --- a/src/soc/amd/common/block/gpio_banks/Makefile.inc +++ /dev/null @@ -1,6 +0,0 @@ -ifeq ($(CONFIG_SOC_AMD_COMMON_BLOCK_BANKED_GPIOS),y) - -all-y += gpio.c -smm-y += gpio.c - -endif # CONFIG_SOC_AMD_COMMON_BLOCK_BANKED_GPIOS diff --git a/src/soc/amd/common/block/gpio_banks/gpio.c b/src/soc/amd/common/block/gpio_banks/gpio.c deleted file mode 100644 index 263757988a..0000000000 --- a/src/soc/amd/common/block/gpio_banks/gpio.c +++ /dev/null @@ -1,401 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * acpimmio_gpio0, acpimmio_remote_gpio and acpimmio_iomux are defined in - * soc/amd/common/block/acpimmio/mmio_util.c and declared as extern variables/constants in - * amdblocks/acpimmio.h which is included in this file. - */ - -/* MMIO access of new-style GPIO bank configuration registers */ -static inline void *gpio_ctrl_ptr(gpio_t gpio_num) -{ - if (SOC_GPIO_TOTAL_PINS < AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER || - /* Verstage on PSP would need to map acpimmio_remote_gpio */ - (CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK) && ENV_SEPARATE_VERSTAGE) || - gpio_num < AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER) - return acpimmio_gpio0 + gpio_num * sizeof(uint32_t); - else - return acpimmio_remote_gpio + - (gpio_num - AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER) * sizeof(uint32_t); -} - -static inline uint32_t gpio_read32(gpio_t gpio_num) -{ - return read32(gpio_ctrl_ptr(gpio_num)); -} - -static inline void gpio_write32(gpio_t gpio_num, uint32_t value) -{ - write32(gpio_ctrl_ptr(gpio_num), value); -} - -static inline void *gpio_mux_ptr(gpio_t gpio_num) -{ - if (SOC_GPIO_TOTAL_PINS < AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER || - /* Verstage on PSP would need to map acpimmio_remote_gpio */ - (CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK) && ENV_SEPARATE_VERSTAGE) || - gpio_num < AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER) - return acpimmio_iomux + gpio_num; - else - return acpimmio_remote_gpio + AMD_GPIO_REMOTE_GPIO_MUX_OFFSET + - (gpio_num - AMD_GPIO_FIRST_REMOTE_GPIO_NUMBER); -} - -static uint8_t get_gpio_mux(gpio_t gpio_num) -{ - return read8(gpio_mux_ptr(gpio_num)); -} - -static void set_gpio_mux(gpio_t gpio_num, uint8_t function) -{ - write8(gpio_mux_ptr(gpio_num), function & AMD_GPIO_MUX_MASK); - get_gpio_mux(gpio_num); /* Flush posted write */ -} - -static int get_gpio_gevent(gpio_t gpio, const struct soc_amd_event *table, - size_t items) -{ - size_t i; - - for (i = 0; i < items; i++) { - if ((table + i)->gpio == gpio) - return (int)(table + i)->event; - } - return -1; -} - -static void program_smi(uint32_t flags, unsigned int gevent_num) -{ - uint8_t level; - - if (!is_gpio_event_level_triggered(flags)) { - printk(BIOS_ERR, "ERROR: %s - Only level trigger allowed for SMI!\n", __func__); - BUG(); - return; - } - - if (is_gpio_event_active_high(flags)) - level = SMI_SCI_LVL_HIGH; - else - level = SMI_SCI_LVL_LOW; - - configure_gevent_smi(gevent_num, SMI_MODE_SMI, level); -} - -/* - * For each general purpose event, GPE, the choice of edge/level triggered - * event is represented as a single bit in SMI_SCI_LEVEL register. - * - * In a similar fashion, polarity (rising/falling, hi/lo) of each GPE is - * represented as a single bit in SMI_SCI_TRIG register. - */ -static void program_sci(uint32_t flags, unsigned int gevent_num) -{ - struct sci_source sci; - - sci.scimap = gevent_num; - sci.gpe = gevent_num; - - if (is_gpio_event_level_triggered(flags)) - sci.level = SMI_SCI_LVL; - else - sci.level = SMI_SCI_EDG; - - if (is_gpio_event_active_high(flags)) - sci.direction = SMI_SCI_LVL_HIGH; - else - sci.direction = SMI_SCI_LVL_LOW; - - configure_scimap(&sci); -} - -uintptr_t gpio_get_address(gpio_t gpio_num) -{ - return (uintptr_t)gpio_ctrl_ptr(gpio_num); -} - -static void gpio_update32(gpio_t gpio_num, uint32_t mask, uint32_t or) -{ - uint32_t reg; - - reg = gpio_read32(gpio_num); - reg &= mask; - reg |= or; - gpio_write32(gpio_num, reg); -} - -/* Set specified bits of a register to match those of ctrl. */ -static void gpio_setbits32(gpio_t gpio_num, uint32_t mask, uint32_t ctrl) -{ - gpio_update32(gpio_num, ~mask, ctrl & mask); -} - -static void gpio_and32(gpio_t gpio_num, uint32_t mask) -{ - gpio_update32(gpio_num, mask, 0); -} - -static void gpio_or32(gpio_t gpio_num, uint32_t or) -{ - gpio_update32(gpio_num, -1UL, or); -} - -static void master_switch_clr(uint32_t mask) -{ - const gpio_t master_reg = GPIO_MASTER_SWITCH / sizeof(uint32_t); - gpio_and32(master_reg, ~mask); -} - -static void master_switch_set(uint32_t or) -{ - const gpio_t master_reg = GPIO_MASTER_SWITCH / sizeof(uint32_t); - gpio_or32(master_reg, or); -} - -int gpio_get(gpio_t gpio_num) -{ - uint32_t reg; - - reg = gpio_read32(gpio_num); - return !!(reg & GPIO_PIN_STS); -} - -void gpio_set(gpio_t gpio_num, int value) -{ - gpio_setbits32(gpio_num, GPIO_OUTPUT_VALUE, value ? GPIO_OUTPUT_VALUE : 0); -} - -void gpio_input_pulldown(gpio_t gpio_num) -{ - gpio_setbits32(gpio_num, GPIO_PULL_MASK | GPIO_OUTPUT_ENABLE, GPIO_PULLDOWN_ENABLE); -} - -void gpio_input_pullup(gpio_t gpio_num) -{ - gpio_setbits32(gpio_num, GPIO_PULL_MASK | GPIO_OUTPUT_ENABLE, GPIO_PULLUP_ENABLE); -} - -void gpio_input(gpio_t gpio_num) -{ - gpio_and32(gpio_num, ~(GPIO_PULL_MASK | GPIO_OUTPUT_ENABLE)); -} - -void gpio_output(gpio_t gpio_num, int value) -{ - /* set GPIO output value before setting the direction to output to avoid glitches */ - gpio_set(gpio_num, value); - gpio_or32(gpio_num, GPIO_OUTPUT_ENABLE); -} - -const char *gpio_acpi_path(gpio_t gpio) -{ - return "\\_SB.GPIO"; -} - -uint16_t gpio_acpi_pin(gpio_t gpio) -{ - return gpio; -} - -void gpio_save_pin_registers(gpio_t gpio, struct soc_amd_gpio_register_save *save) -{ - save->mux_value = get_gpio_mux(gpio); - save->control_value = gpio_read32(gpio); -} - -void gpio_restore_pin_registers(gpio_t gpio, struct soc_amd_gpio_register_save *save) -{ - set_gpio_mux(gpio, save->mux_value); - gpio_write32(gpio, save->control_value); - gpio_read32(gpio); /* Flush posted write */ -} - -static void set_single_gpio(const struct soc_amd_gpio *g) -{ - static const struct soc_amd_event *gev_tbl; - static size_t gev_items; - int gevent_num; - const bool can_set_smi_flags = !((CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK) && - ENV_SEPARATE_VERSTAGE) || - CONFIG(SOC_AMD_COMMON_BLOCK_BANKED_GPIOS_NON_SOC_CODEBASE)); - - set_gpio_mux(g->gpio, g->function); - - gpio_setbits32(g->gpio, PAD_CFG_MASK, g->control); - /* Clear interrupt and wake status (write 1-to-clear bits) */ - gpio_or32(g->gpio, GPIO_INT_STATUS | GPIO_WAKE_STATUS); - if (g->flags == 0) - return; - - /* Can't set SMI flags from PSP */ - if (!can_set_smi_flags) - return; - - if (gev_tbl == NULL) - soc_get_gpio_event_table(&gev_tbl, &gev_items); - - gevent_num = get_gpio_gevent(g->gpio, gev_tbl, gev_items); - if (gevent_num < 0) { - printk(BIOS_WARNING, "Warning: GPIO pin %d has no associated gevent!\n", - g->gpio); - return; - } - - if (g->flags & GPIO_FLAG_SMI) { - program_smi(g->flags, gevent_num); - } else if (g->flags & GPIO_FLAG_SCI) { - program_sci(g->flags, gevent_num); - } -} - -void gpio_configure_pads_with_override(const struct soc_amd_gpio *base_cfg, - size_t base_num_pads, - const struct soc_amd_gpio *override_cfg, - size_t override_num_pads) -{ - const struct soc_amd_gpio *c; - size_t i, j; - - if (!base_cfg || !base_num_pads) - return; - - /* - * 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. - */ - master_switch_clr(GPIO_MASK_STS_EN | GPIO_INTERRUPT_EN); - - for (i = 0; i < base_num_pads; i++) { - c = &base_cfg[i]; - /* Check if override exist for GPIO from the base configuration */ - for (j = 0; override_cfg && j < override_num_pads; j++) { - if (c->gpio == override_cfg[j].gpio) { - c = &override_cfg[j]; - break; - } - } - set_single_gpio(c); - } - - /* - * 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. - */ - master_switch_set(GPIO_INTERRUPT_EN); -} - -void gpio_configure_pads(const struct soc_amd_gpio *gpio_list_ptr, size_t size) -{ - gpio_configure_pads_with_override(gpio_list_ptr, size, NULL, 0); -} - -int gpio_interrupt_status(gpio_t gpio) -{ - uint32_t reg = gpio_read32(gpio); - - if (reg & GPIO_INT_STATUS) { - /* Clear interrupt status, preserve wake status */ - reg &= ~GPIO_WAKE_STATUS; - gpio_write32(gpio, reg); - return 1; - } - - return 0; -} - -static void check_and_add_wake_gpio(gpio_t begin, gpio_t end, struct gpio_wake_state *state) -{ - gpio_t 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, unsigned int bit_limit, gpio_t gpio_base, - struct gpio_wake_state *state) -{ - unsigned int i; - gpio_t begin; - gpio_t end; - - for (i = 0; i < bit_limit; i++) { - if (!(wake_stat & BIT(i))) - continue; - /* Each wake status register bit is for 4 GPIOs that then will be checked */ - 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 gpio_t stat0 = GPIO_WAKE_STAT_0 / sizeof(uint32_t); - const gpio_t stat1 = GPIO_WAKE_STAT_1 / sizeof(uint32_t); - const gpio_t control_switch = GPIO_MASTER_SWITCH / sizeof(uint32_t); - - 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(void) -{ - const struct chipset_power_state *ps; - const struct gpio_wake_state *state; - unsigned int i; - unsigned int end; - - if (acpi_pm_state_for_elog(&ps) < 0) - return; - state = &ps->gpio_state; - - 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]); -} -- cgit v1.2.3