summaryrefslogtreecommitdiff
path: root/src/soc/amd/picasso/smihandler.c
diff options
context:
space:
mode:
authorMartin Roth <martinroth@chromium.org>2019-04-22 14:55:16 -0600
committerMartin Roth <martinroth@google.com>2019-07-02 14:11:11 +0000
commit5c354b9979c7e7ad9af668bad0e1b6a5c3003d26 (patch)
treec597ef067e0d6eb65c2ba7e4b5e754f49e65572c /src/soc/amd/picasso/smihandler.c
parent76378b3c01b52f4d4184284d3d07bf63fef2ca17 (diff)
soc/amd/picasso: Create picasso as a copy of stoneyridge
So that everyone can see what's being updated from stoney, we're starting with a direct copy of the stoney directory. There are arguments both for and against doing it this way, but I believe This the most transparent way. We've moved much of the duplicated stoney code into the soc/amd/common directory and will continue that work as it becomes obvious that we have unchanged code between the SOCs. Makefile.inc has been renamed as makefile.inc so that it won't build in jenkins until the directory is updated. Other than that change, this is an exact copy of the stoneyridge SOC directory which will be updated in the follow-on commits in the patch train. TEST=None BUG=b:130804851 Signed-off-by: Martin Roth <martinroth@chromium.org> Change-Id: I6809bd1eea304f76dd9000c079b3ed09f94dbd3b Reviewed-on: https://review.coreboot.org/c/coreboot/+/32407 Reviewed-by: Richard Spiegel <richard.spiegel@silverbackltd.com> Reviewed-by: Angel Pons <th3fanbus@gmail.com> Reviewed-by: Edward O'Callaghan <quasisec@chromium.org> Reviewed-by: HAOUAS Elyes <ehaouas@noos.fr> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/soc/amd/picasso/smihandler.c')
-rw-r--r--src/soc/amd/picasso/smihandler.c287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/soc/amd/picasso/smihandler.c b/src/soc/amd/picasso/smihandler.c
new file mode 100644
index 0000000000..d8438bb366
--- /dev/null
+++ b/src/soc/amd/picasso/smihandler.c
@@ -0,0 +1,287 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 Advanced Micro Devices, Inc.
+ * Copyright (C) 2014 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <cpu/x86/smm.h>
+#include <cpu/x86/cache.h>
+#include <arch/acpi.h>
+#include <arch/hlt.h>
+#include <device/pci_def.h>
+#include <soc/smi.h>
+#include <soc/southbridge.h>
+#include <amdblocks/acpimmio.h>
+#include <amdblocks/acpi.h>
+#include <elog.h>
+
+/* bits in smm_io_trap */
+#define SMM_IO_TRAP_PORT_OFFSET 16
+#define SMM_IO_TRAP_PORT_ADDRESS_MASK 0xffff
+#define SMM_IO_TRAP_RW (1 << 0)
+#define SMM_IO_TRAP_VALID (1 << 1)
+
+static inline u16 get_io_address(u32 info)
+{
+ return ((info >> SMM_IO_TRAP_PORT_OFFSET) &
+ SMM_IO_TRAP_PORT_ADDRESS_MASK);
+}
+
+static void *find_save_state(int cmd)
+{
+ int core;
+ amd64_smm_state_save_area_t *state;
+ u32 smm_io_trap;
+ u8 reg_al;
+
+ /* Check all nodes looking for the one that issued the IO */
+ for (core = 0; core < CONFIG_MAX_CPUS; core++) {
+ state = smm_get_save_state(core);
+ smm_io_trap = state->smm_io_trap_offset;
+ /* Check for Valid IO Trap Word (bit1==1) */
+ if (!(smm_io_trap & SMM_IO_TRAP_VALID))
+ continue;
+ /* Make sure it was a write (bit0==0) */
+ if (smm_io_trap & SMM_IO_TRAP_RW)
+ continue;
+ /* Check for APMC IO port */
+ if (pm_acpi_smi_cmd_port() != get_io_address(smm_io_trap))
+ continue;
+ /* Check AL against the requested command */
+ reg_al = state->rax;
+ if (reg_al == cmd)
+ return state;
+ }
+ return NULL;
+}
+
+static void southbridge_smi_gsmi(void)
+{
+ u8 sub_command;
+ amd64_smm_state_save_area_t *io_smi;
+ u32 reg_ebx;
+
+ io_smi = find_save_state(APM_CNT_ELOG_GSMI);
+ if (!io_smi)
+ return;
+ /* Command and return value in EAX */
+ sub_command = (io_smi->rax >> 8) & 0xff;
+
+ /* Parameter buffer in EBX */
+ reg_ebx = io_smi->rbx;
+
+ /* drivers/elog/gsmi.c */
+ io_smi->rax = gsmi_exec(sub_command, &reg_ebx);
+}
+
+static void sb_apmc_smi_handler(void)
+{
+ const uint8_t cmd = inb(pm_acpi_smi_cmd_port());
+
+ switch (cmd) {
+ case APM_CNT_ACPI_ENABLE:
+ acpi_enable_sci();
+ break;
+ case APM_CNT_ACPI_DISABLE:
+ acpi_disable_sci();
+ break;
+ case APM_CNT_ELOG_GSMI:
+ if (CONFIG(ELOG_GSMI))
+ southbridge_smi_gsmi();
+ break;
+ }
+
+ mainboard_smi_apmc(cmd);
+}
+
+static void disable_all_smi_status(void)
+{
+ smi_write32(SMI_SCI_STATUS, smi_read32(SMI_SCI_STATUS));
+ smi_write32(SMI_EVENT_STATUS, smi_read32(SMI_EVENT_STATUS));
+ smi_write32(SMI_REG_SMISTS0, smi_read32(SMI_REG_SMISTS0));
+ smi_write32(SMI_REG_SMISTS1, smi_read32(SMI_REG_SMISTS1));
+ smi_write32(SMI_REG_SMISTS2, smi_read32(SMI_REG_SMISTS2));
+ smi_write32(SMI_REG_SMISTS3, smi_read32(SMI_REG_SMISTS3));
+ smi_write32(SMI_REG_SMISTS4, smi_read32(SMI_REG_SMISTS4));
+}
+
+static void sb_slp_typ_handler(void)
+{
+ uint32_t pci_ctrl, reg32;
+ uint16_t pm1cnt, reg16;
+ uint8_t slp_typ, rst_ctrl;
+
+ /* Figure out SLP_TYP */
+ pm1cnt = acpi_read16(MMIO_ACPI_PM1_CNT_BLK);
+ printk(BIOS_SPEW, "SMI#: SLP = 0x%04x\n", pm1cnt);
+ slp_typ = acpi_sleep_from_pm1(pm1cnt);
+
+ /* Do any mainboard sleep handling */
+ mainboard_smi_sleep(slp_typ);
+
+ switch (slp_typ) {
+ case ACPI_S0:
+ printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n");
+ break;
+ case ACPI_S3:
+ printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
+ break;
+ case ACPI_S4:
+ printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n");
+ break;
+ case ACPI_S5:
+ printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
+ break;
+ default:
+ printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n");
+ break;
+ }
+
+ if (slp_typ >= ACPI_S3) {
+ /* Sleep Type Elog S3, S4, and S5 entry */
+ if (CONFIG(ELOG_GSMI))
+ elog_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ);
+
+ wbinvd();
+
+ disable_all_smi_status();
+
+ /* Do not send SMI before AcpiPm1CntBlkx00[SlpTyp] */
+ pci_ctrl = pm_read32(PM_PCI_CTRL);
+ pci_ctrl &= ~FORCE_SLPSTATE_RETRY;
+ pci_ctrl |= FORCE_STPCLK_RETRY;
+ pm_write32(PM_PCI_CTRL, pci_ctrl);
+
+ /* Enable SlpTyp */
+ rst_ctrl = pm_read8(PM_RST_CTRL1);
+ rst_ctrl |= SLPTYPE_CONTROL_EN;
+ pm_write8(PM_RST_CTRL1, rst_ctrl);
+
+ /*
+ * Before the final command, check if there's pending wake
+ * event. Read enable first, so that reading the actual status
+ * is as close as possible to entering S3. The idea is to
+ * minimize the opportunity for a wake event to happen before
+ * actually entering S3. If there's a pending wake event, log
+ * it and continue normal path. S3 will fail and the wake event
+ * becomes a SCI.
+ */
+ if (CONFIG(ELOG_GSMI)) {
+ reg16 = acpi_read16(MMIO_ACPI_PM1_EN);
+ reg16 &= acpi_read16(MMIO_ACPI_PM1_STS);
+ if (reg16)
+ elog_add_extended_event(
+ ELOG_SLEEP_PENDING_PM1_WAKE,
+ (u32)reg16);
+
+ reg32 = acpi_read32(MMIO_ACPI_GPE0_EN);
+ reg32 &= acpi_read32(MMIO_ACPI_GPE0_STS);
+ if (reg32)
+ elog_add_extended_event(
+ ELOG_SLEEP_PENDING_GPE0_WAKE,
+ reg32);
+ } /* if (CONFIG(ELOG_GSMI)) */
+
+ /*
+ * An IO cycle is required to trigger the STPCLK/STPGNT
+ * handshake when the Pm1 write is reissued.
+ */
+ outw(pm1cnt | SLP_EN, pm_read16(PM1_CNT_BLK));
+ hlt();
+ }
+}
+
+int southbridge_io_trap_handler(int smif)
+{
+ return 0;
+}
+
+/*
+ * Table of functions supported in the SMI handler. Note that SMI source setup
+ * in southbridge.c is unrelated to this list.
+ */
+static const struct smi_sources_t smi_sources[] = {
+ { .type = SMITYPE_SMI_CMD_PORT, .handler = sb_apmc_smi_handler },
+ { .type = SMITYPE_SLP_TYP, .handler = sb_slp_typ_handler},
+};
+
+static void process_smi_sci(void)
+{
+ const uint32_t status = smi_read32(SMI_SCI_STATUS);
+
+ /* Clear events to prevent re-entering SMI if event isn't handled */
+ smi_write32(SMI_SCI_STATUS, status);
+}
+
+static void *get_source_handler(int source)
+{
+ int i;
+
+ for (i = 0 ; i < ARRAY_SIZE(smi_sources) ; i++)
+ if (smi_sources[i].type == source)
+ return smi_sources[i].handler;
+
+ return NULL;
+}
+
+static void process_smi_sources(uint32_t reg)
+{
+ const uint32_t status = smi_read32(reg);
+ int bit_zero = 32 / sizeof(uint32_t) * (reg - SMI_REG_SMISTS0);
+ void (*source_handler)(void);
+ int i;
+
+ for (i = 0 ; i < 32 ; i++) {
+ if (status & (1 << i)) {
+ source_handler = get_source_handler(i + bit_zero);
+ if (source_handler)
+ source_handler();
+ }
+ }
+
+ if (reg == SMI_REG_SMISTS0)
+ if (status & GEVENT_MASK)
+ /* Gevent[23:0] are assumed to be mainboard-specific */
+ mainboard_smi_gpi(status & GEVENT_MASK);
+
+ /* Clear all events in this register */
+ smi_write32(reg, status);
+}
+
+void southbridge_smi_handler(void)
+{
+ const uint16_t smi_src = smi_read16(SMI_REG_POINTER);
+
+ if (smi_src & SMI_STATUS_SRC_SCI)
+ process_smi_sci();
+ if (smi_src & SMI_STATUS_SRC_0)
+ process_smi_sources(SMI_REG_SMISTS0);
+ if (smi_src & SMI_STATUS_SRC_1)
+ process_smi_sources(SMI_REG_SMISTS1);
+ if (smi_src & SMI_STATUS_SRC_2)
+ process_smi_sources(SMI_REG_SMISTS2);
+ if (smi_src & SMI_STATUS_SRC_3)
+ process_smi_sources(SMI_REG_SMISTS3);
+ if (smi_src & SMI_STATUS_SRC_4)
+ process_smi_sources(SMI_REG_SMISTS4);
+}
+
+void southbridge_smi_set_eos(void)
+{
+ uint32_t reg = smi_read32(SMI_REG_SMITRIG0);
+ reg |= SMITRG0_EOS;
+ smi_write32(SMI_REG_SMITRIG0, reg);
+}