blob: a5f0eaf0b2d06581a385e3715aa4dfb4822cdca8 [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
#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 <amdblocks/smi.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)
{
int 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, 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);
}
struct sci_trigger_regs {
uint32_t mask;
uint32_t polarity;
uint32_t 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 fill_sci_trigger(uint32_t flags, int gpe, struct sci_trigger_regs *regs)
{
uint32_t mask = 1 << gpe;
regs->mask |= mask;
if (is_gpio_event_level_triggered(flags))
regs->level |= mask;
else
regs->level &= ~mask;
if (is_gpio_event_active_high(flags))
regs->polarity |= mask;
else
regs->polarity &= ~mask;
}
/* TODO: See configure_scimap() implementations. */
static void set_sci_trigger(const struct sci_trigger_regs *regs)
{
uint32_t value;
value = smi_read32(SMI_SCI_TRIG);
value &= ~regs->mask;
value |= regs->polarity;
smi_write32(SMI_SCI_TRIG, value);
value = smi_read32(SMI_SCI_LEVEL);
value &= ~regs->mask;
value |= regs->level;
smi_write32(SMI_SCI_LEVEL, value);
}
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 uint8_t master_reg = GPIO_MASTER_SWITCH / sizeof(uint32_t);
__gpio_and32(master_reg, ~mask);
}
static void master_switch_set(uint32_t or)
{
const uint8_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_PULLDOWN_ENABLE);
}
void gpio_input_pullup(gpio_t gpio_num)
{
__gpio_setbits32(gpio_num, GPIO_PULL_MASK, GPIO_PULLUP_ENABLE);
}
void gpio_input(gpio_t gpio_num)
{
__gpio_and32(gpio_num, ~GPIO_OUTPUT_ENABLE);
}
void gpio_output(gpio_t gpio_num, int value)
{
__gpio_or32(gpio_num, GPIO_OUTPUT_ENABLE);
gpio_set(gpio_num, value);
}
const char *gpio_acpi_path(gpio_t gpio)
{
return "\\_SB.GPIO";
}
uint16_t gpio_acpi_pin(gpio_t gpio)
{
return gpio;
}
__weak void soc_gpio_hook(uint8_t gpio, uint8_t mux) {}
void program_gpios(const struct soc_amd_gpio *gpio_list_ptr, size_t size)
{
uint32_t control, control_flags;
uint8_t mux, index, gpio;
int gevent_num;
const struct soc_amd_event *gev_tbl;
struct sci_trigger_regs sci_trigger_cfg = { 0 };
size_t gev_items;
const bool can_set_smi_flags = !(CONFIG(VBOOT_STARTS_BEFORE_BOOTBLOCK) &&
ENV_SEPARATE_VERSTAGE);
if (!gpio_list_ptr || !size)
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);
if (can_set_smi_flags)
soc_get_gpio_event_table(&gev_tbl, &gev_items);
for (index = 0; index < size; index++) {
gpio = gpio_list_ptr[index].gpio;
mux = gpio_list_ptr[index].function;
control = gpio_list_ptr[index].control;
control_flags = gpio_list_ptr[index].flags;
iomux_write8(gpio, mux & AMD_GPIO_MUX_MASK);
iomux_read8(gpio); /* Flush posted write */
soc_gpio_hook(gpio, mux);
__gpio_setbits32(gpio, PAD_CFG_MASK, control);
/* Clear interrupt and wake status (write 1-to-clear bits) */
__gpio_or32(gpio, GPIO_INT_STATUS | GPIO_WAKE_STATUS);
if (control_flags == 0)
continue;
/* Can't set SMI flags from PSP */
if (!can_set_smi_flags)
continue;
gevent_num = get_gpio_gevent(gpio, gev_tbl, gev_items);
if (gevent_num < 0) {
printk(BIOS_WARNING, "Warning: GPIO pin %d has no associated gevent!\n",
gpio);
continue;
}
if (control_flags & GPIO_FLAG_SMI) {
program_smi(control_flags, gevent_num);
} else if (control_flags & GPIO_FLAG_SCI) {
fill_sci_trigger(control_flags, gevent_num, &sci_trigger_cfg);
soc_route_sci(gevent_num);
}
}
/*
* 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);
/* Set all SCI trigger polarity (high/low) and level (edge/level). */
if (can_set_smi_flags)
set_sci_trigger(&sci_trigger_cfg);
}
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;
}
/*
* This function checks to see if there is an override config present for the
* provided pad_config. If no override config is present, then the input config
* is returned. Else, it returns the override config.
*/
static const struct soc_amd_gpio *gpio_get_config(const struct soc_amd_gpio *c,
const struct soc_amd_gpio *override_cfg_table,
size_t num)
{
size_t i;
if (override_cfg_table == NULL)
return c;
for (i = 0; i < num; i++) {
if (c->gpio == override_cfg_table[i].gpio)
return override_cfg_table + i;
}
return c;
}
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)
{
size_t i;
const struct soc_amd_gpio *c;
for (i = 0; i < base_num_pads; i++) {
c = gpio_get_config(base_cfg + i, override_cfg,
override_num_pads);
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]);
}