baytrail: SMM support

Initialize SMM on all CPUs by relocating the SMM region
and setting SMRR on all the cores. Additionally SMI
is enabled in the south cluster.

BUG=chrome-os-partner:22862
BRANCH=None
TEST=Built and booted rambi. Tested with DEBUG_SMI and noted
     power button turns off board while in firmware.

Change-Id: I92e3460572feeb67d4a3d4d26af5f0ecaf7d3dd5
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/173983
Reviewed-on: http://review.coreboot.org/4892
Tested-by: build bot (Jenkins)
Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
This commit is contained in:
Aaron Durbin 2013-10-21 22:32:00 -05:00 committed by Aaron Durbin
parent 6a360048a1
commit 7837be6cbb
11 changed files with 1135 additions and 13 deletions

View File

@ -3,6 +3,7 @@ subdirs-y += microcode
subdirs-y += romstage
subdirs-y += ../../../cpu/x86/lapic
subdirs-y += ../../../cpu/x86/mtrr
subdirs-y += ../../../cpu/x86/smm
subdirs-y += ../../../cpu/x86/tsc
subdirs-y += ../../../cpu/intel/microcode
@ -10,10 +11,12 @@ ramstage-y += memmap.c
romstage-y += memmap.c
ramstage-y += tsc_freq.c
romstage-y += tsc_freq.c
smm-y += tsc_freq.c
ramstage-$(CONFIG_CACHE_MRC_SETTINGS) += nvm.c
ramstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c
romstage-$(CONFIG_CACHE_MRC_SETTINGS) += mrc_cache.c
ramstage-y += spi.c
smm-y += spi.c
ramstage-y += chip.c
ramstage-y += iosf.c
romstage-y += iosf.c
@ -23,6 +26,10 @@ ramstage-y += gpio.c
romstage-y += reset.c
ramstage-y += reset.c
ramstage-y += cpu.c
ramstage-y += pmutil.c
smm-y += pmutil.c
smm-y += smihandler.c
ramstage-y += smm.c
# Remove as ramstage gets fleshed out
ramstage-y += placeholders.c

View File

@ -25,6 +25,7 @@
/* Memory mapped IO registers behind PMC_BASE_ADDRESS */
#define GEN_PMCONF1 0x20
# define AFTERG3_EN (1 << 0)
# define UART_EN (1 << 24)
#define ETR 0x48
# define CF9LOCK (1 << 31)
@ -34,6 +35,105 @@
# define CWORWRE (1 << 18)
/* IO Mapped registers behind ACPI_BASE_ADDRESS */
#define PM1_STS 0x00
#define WAK_STS (1 << 15)
#define PCIEXPWAK_STS (1 << 14)
#define USB_STS (1 << 13)
#define PRBTNOR_STS (1 << 11)
#define RTC_STS (1 << 10)
#define PWRBTN_STS (1 << 8)
#define GBL_STS (1 << 5)
#define TMROF_STS (1 << 0)
#define PM1_EN 0x02
#define PCIEXPWAK_DIS (1 << 14)
#define USB_WAKE_EN (1 << 13)
#define RTC_EN (1 << 10)
#define PWRBTN_EN (1 << 8)
#define GBL_EN (1 << 5)
#define TMROF_EN (1 << 0)
#define PM1_CNT 0x04
#define SLP_EN (1 << 13)
#define SLP_TYP (7 << 10)
#define SLP_TYP_S0 0
#define SLP_TYP_S1 1
#define SLP_TYP_S3 5
#define SLP_TYP_S4 6
#define SLP_TYP_S5 7
#define GBL_RLS (1 << 2)
#define BM_RLD (1 << 1)
#define SCI_EN (1 << 0)
#define PM1_TMR 0x08
#define GPE0_STS 0x20
#define CORE_GPIO_STS7 (1 << 31)
#define CORE_GPIO_STS6 (1 << 30)
#define CORE_GPIO_STS5 (1 << 29)
#define CORE_GPIO_STS4 (1 << 28)
#define CORE_GPIO_STS3 (1 << 27)
#define CORE_GPIO_STS2 (1 << 26)
#define CORE_GPIO_STS1 (1 << 25)
#define CORE_GPIO_STS0 (1 << 24)
#define SUS_GPIO_STS7 (1 << 23)
#define SUS_GPIO_STS6 (1 << 22)
#define SUS_GPIO_STS5 (1 << 21)
#define SUS_GPIO_STS4 (1 << 20)
#define SUS_GPIO_STS3 (1 << 19)
#define SUS_GPIO_STS2 (1 << 18)
#define SUS_GPIO_STS1 (1 << 17)
#define SUS_GPIO_STS0 (1 << 16)
#define PME_B0_STS (1 << 13)
#define BATLOW_STS (1 << 10)
#define PCI_EXP_STS (1 << 9)
#define PCIE_WAKE3_STS (1 << 8)
#define PCIE_WAKE2_STS (1 << 7)
#define PCIE_WAKE1_STS (1 << 6)
#define GUNIT_SCI_STS (1 << 5)
#define PUNIT_SCI_STS (1 << 4)
#define PCIE_WAKE0_STS (1 << 3)
#define SWGPE_STS (1 << 2)
#define HOT_PLUG_STS (1 << 1)
#define GPE0_EN 0x28
#define CORE_GPIO_EN7 (1 << 31)
#define CORE_GPIO_EN6 (1 << 30)
#define CORE_GPIO_EN5 (1 << 29)
#define CORE_GPIO_EN4 (1 << 28)
#define CORE_GPIO_EN3 (1 << 27)
#define CORE_GPIO_EN2 (1 << 26)
#define CORE_GPIO_EN1 (1 << 25)
#define CORE_GPIO_EN0 (1 << 24)
#define SUS_GPIO_EN7 (1 << 23)
#define SUS_GPIO_EN6 (1 << 22)
#define SUS_GPIO_EN5 (1 << 21)
#define SUS_GPIO_EN4 (1 << 20)
#define SUS_GPIO_EN3 (1 << 19)
#define SUS_GPIO_EN2 (1 << 18)
#define SUS_GPIO_EN1 (1 << 17)
#define SUS_GPIO_EN0 (1 << 16)
#define PME_B0_EN (1 << 13)
#define BATLOW_EN (1 << 10)
#define PCI_EXP_EN (1 << 9)
#define PCIE_WAKE3_EN (1 << 8)
#define PCIE_WAKE2_EN (1 << 7)
#define PCIE_WAKE1_EN (1 << 6)
#define PCIE_WAKE0_EN (1 << 3)
#define SWGPE_EN (1 << 2)
#define HOT_PLUG_EN (1 << 1)
#define SMI_EN 0x30
#define INTEL_USB2_EN (1 << 18) // Intel-Specific USB2 SMI logic
#define USB_EN (1 << 17) // Legacy USB2 SMI logic
#define PERIODIC_EN (1 << 14) // SMI on PERIODIC_STS in SMI_STS
#define TCO_EN (1 << 13) // Enable TCO Logic (BIOSWE et al)
#define BIOS_RLS (1 << 7) // asserts SCI on bit set
#define SWSMI_TMR_EN (1 << 6) // start software smi timer on bit set
#define APMC_EN (1 << 5) // Writes to APM_CNT cause SMI#
#define SLP_SMI_EN (1 << 4) // Write to SLP_EN in PM1_CNT asserts SMI#
#define BIOS_EN (1 << 2) // Assert SMI# on setting GBL_RLS bit
#define EOS (1 << 1) // End of SMI (deassert SMI#)
#define GBL_SMI_EN (1 << 0) // SMI# generation at all?
#define SMI_STS 0x34
#define ALT_GPIO_SMI 0x38
#define UPRWC 0x3c
#define GPE_CTRL 0x40
#define PM2A_CNT_BLK 0x50
#define TCO_RLD 0x60
#define TCO_STS 0x64
# define SECOND_TO_STS (1 << 17)
@ -49,4 +149,22 @@
# define RST_CPU (1 << 2)
# define SYS_RST (1 << 1)
/* Power Management Utility Functions. */
uint16_t get_pmbase(void);
uint32_t clear_smi_status(void);
uint16_t clear_pm1_status(void);
uint32_t clear_tco_status(void);
uint32_t clear_gpe_status(void);
void enable_smi(uint32_t mask);
void disable_smi(uint32_t mask);
void enable_pm1(uint16_t events);
void enable_pm1_control(uint32_t mask);
void disable_pm1_control(uint32_t mask);
void enable_gpe(uint32_t mask);
void disable_gpe(uint32_t mask);
void disable_all_gpe(void);
static inline void southcluster_log_state(void) {}
#endif /* _BAYTRAIL_PMC_H_ */

View File

@ -0,0 +1,41 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2013 Google Inc.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _BAYTRAIL_SMM_H_
#define _BAYTRAIL_SMM_H_
/* There is a bug in the order of Kconfig includes in that arch/x86/Kconfig
* is included after chipset code. This causes the chipset's Kconfig to be
* cloberred by the arch/x86/Kconfig if they have the same name. */
static inline int smm_region_size(void)
{
/* Make it 8MiB by default. */
if (CONFIG_SMM_TSEG_SIZE == 0)
return (8 << 20);
return CONFIG_SMM_TSEG_SIZE;
}
void *smm_region_start(void);
#if !defined(__PRE_RAM__) && !defined(__SMM___)
void southcluster_smm_clear_state(void);
void southcluster_smm_enable_smi(void);
#endif
#endif /* _BAYTRAIL_SMM_H_ */

View File

@ -21,14 +21,27 @@
#include <console/console.h>
#include <cpu/cpu.h>
#include <cpu/intel/microcode.h>
#include <cpu/x86/mtrr.h>
#include <cpu/x86/cache.h>
#include <cpu/x86/lapic.h>
#include <cpu/x86/mp.h>
#include <cpu/x86/msr.h>
#include <cpu/x86/mtrr.h>
#include <cpu/x86/smm.h>
#include <baytrail/pattrs.h>
#include <baytrail/ramstage.h>
#include <baytrail/smm.h>
static const void *microcode_ptr;
static void smm_relocate(void *unused);
static void enable_smis(void *unused);
static struct mp_flight_record mp_steps[] = {
MP_FR_BLOCK_APS(smm_relocate, NULL, smm_relocate, NULL),
MP_FR_BLOCK_APS(mp_initialize_cpu, NULL, mp_initialize_cpu, NULL),
/* Wait for APs to finish initialization before proceeding. */
MP_FR_BLOCK_APS(NULL, NULL, enable_smis, NULL),
};
/* The APIC id space on Bay Trail is sparse. Each id is separated by 2. */
@ -48,13 +61,15 @@ void baytrail_init_cpus(device_t dev)
x86_setup_var_mtrrs(pattrs->address_bits, 2);
x86_mtrr_check();
/* Stash microcode. */
microcode_ptr = intel_microcode_find();
mp_params.num_cpus = pattrs->num_cpus,
mp_params.parallel_microcode_load = 1,
mp_params.adjust_apic_id = adjust_apic_id;
mp_params.flight_plan = &mp_steps[0];
mp_params.num_records = ARRAY_SIZE(mp_steps);
mp_params.microcode_pointer = intel_microcode_find();
mp_params.microcode_pointer = NULL;
mp_params.microcode_pointer = microcode_ptr;
if (mp_init(cpu_bus, &mp_params)) {
printk(BIOS_ERR, "MP initialization failure.\n");
@ -80,3 +95,148 @@ static const struct cpu_driver driver __cpu_driver = {
.id_table = cpu_table,
};
/*
* SMM loading and initialization.
*/
struct smm_relocation_attrs {
uint32_t smbase;
uint32_t smrr_base;
uint32_t smrr_mask;
};
static struct smm_relocation_attrs relo_attrs;
static void adjust_apic_id_map(struct smm_loader_params *smm_params)
{
int i;
struct smm_runtime *runtime = smm_params->runtime;
for (i = 0; i < CONFIG_MAX_CPUS; i++)
runtime->apic_id_to_cpu[i] = mp_get_apic_id(i);
}
static void asmlinkage
cpu_smm_do_relocation(void *arg, int cpu, const struct smm_runtime *runtime)
{
msr_t smrr;
em64t100_smm_state_save_area_t *smm_state;
if (cpu >= CONFIG_MAX_CPUS) {
printk(BIOS_CRIT,
"Invalid CPU number assigned in SMM stub: %d\n", cpu);
return;
}
/* Set up SMRR. */
smrr.lo = relo_attrs.smrr_base;
smrr.hi = 0;
wrmsr(SMRRphysBase_MSR, smrr);
smrr.lo = relo_attrs.smrr_mask;
smrr.hi = 0;
wrmsr(SMRRphysMask_MSR, smrr);
/* The relocated handler runs with all CPUs concurrently. Therefore
* stagger the entry points adjusting SMBASE downwards by save state
* size * CPU num. */
smm_state = (void *)(SMM_EM64T100_SAVE_STATE_OFFSET + runtime->smbase);
smm_state->smbase = relo_attrs.smbase - cpu * runtime->save_state_size;
printk(BIOS_DEBUG, "New SMBASE 0x%08x\n", smm_state->smbase);
}
static int install_relocation_handler(int num_cpus)
{
const int save_state_size = sizeof(em64t100_smm_state_save_area_t);
struct smm_loader_params smm_params = {
.per_cpu_stack_size = save_state_size,
.num_concurrent_stacks = num_cpus,
.per_cpu_save_state_size = save_state_size,
.num_concurrent_save_states = 1,
.handler = (smm_handler_t)&cpu_smm_do_relocation,
};
if (smm_setup_relocation_handler(&smm_params))
return -1;
adjust_apic_id_map(&smm_params);
return 0;
}
static int install_permanent_handler(int num_cpus)
{
/* There are num_cpus concurrent stacks and num_cpus concurrent save
* state areas. Lastly, set the stack size to the save state size. */
int save_state_size = sizeof(em64t100_smm_state_save_area_t);
struct smm_loader_params smm_params = {
.per_cpu_stack_size = save_state_size,
.num_concurrent_stacks = num_cpus,
.per_cpu_save_state_size = save_state_size,
.num_concurrent_save_states = num_cpus,
};
const int tseg_size = smm_region_size() - CONFIG_SMM_RESERVED_SIZE;
printk(BIOS_DEBUG, "Installing SMM handler to 0x%08x\n",
relo_attrs.smbase);
if (smm_load_module((void *)relo_attrs.smbase, tseg_size, &smm_params))
return -1;
adjust_apic_id_map(&smm_params);
return 0;
}
static int smm_load_handlers(void)
{
/* All range registers are aligned to 4KiB */
const uint32_t rmask = ~((1 << 12) - 1);
const struct pattrs *pattrs = pattrs_get();
/* Initialize global tracking state. */
relo_attrs.smbase = (uint32_t)smm_region_start();
relo_attrs.smrr_base = relo_attrs.smbase | MTRR_TYPE_WRBACK;
relo_attrs.smrr_mask = ~(smm_region_size() - 1) & rmask;
relo_attrs.smrr_mask |= MTRRphysMaskValid;
/* Install handlers. */
if (install_relocation_handler(pattrs->num_cpus) < 0) {
printk(BIOS_ERR, "Unable to install SMM relocation handler.\n");
return -1;
}
if (install_permanent_handler(pattrs->num_cpus) < 0) {
printk(BIOS_ERR, "Unable to install SMM permanent handler.\n");
return -1;
}
/* Ensure the SMM handlers hit DRAM before performing first SMI. */
wbinvd();
return 0;
}
static void smm_relocate(void *unused)
{
/* Load relocation and permanent handler. */
if (boot_cpu()) {
if (smm_load_handlers() < 0) {
printk(BIOS_ERR, "Error loading SMM handlers.\n");
return;
}
southcluster_smm_clear_state();
}
/* Relocate SMM space. */
smm_initiate_relocation();
/* Load microcode after SMM relocation. */
intel_microcode_load_unlocked(microcode_ptr);
}
static void enable_smis(void *unused)
{
southcluster_smm_enable_smi();
}

View File

@ -20,8 +20,14 @@
#include <arch/io.h>
#include <cbmem.h>
#include <baytrail/iosf.h>
#include <baytrail/smm.h>
void *smm_region_start(void)
{
return (void *)(iosf_bunit_read(BUNIT_SMRRL) << 20);
}
unsigned long get_top_of_ram(void)
{
return (unsigned long)(iosf_bunit_read(BUNIT_SMRRL) << 20);
return (unsigned long)smm_region_start();
}

View File

@ -13,8 +13,6 @@ void acpi_create_serialio_ssdt(acpi_header_t *ssdt) {}
unsigned long acpi_fill_mcfg(unsigned long current) { return current; }
void smm_setup_structures(void *gnvs, void *tcg, void *smi1) {}
void smm_init(void) {}
/* Rmodules don't like weak symbols. */

View File

@ -0,0 +1,297 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2013 Google Inc.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include <arch/io.h>
#include <console/console.h>
#include <baytrail/lpc.h>
#include <baytrail/pci_devs.h>
#include <baytrail/pmc.h>
#if defined(__SMM__)
static const device_t pcu_dev = PCI_DEV(0, PCU_DEV, 0);
static inline device_t get_pcu_dev(void)
{
return pcu_dev;
}
#else /* !__SMM__ */
#include <device/device.h>
#include <device/pci.h>
static device_t pcu_dev;
static device_t get_pcu_dev(void)
{
if (pcu_dev == NULL)
pcu_dev = dev_find_slot(0, PCI_DEVFN(PCU_DEV, 0));
return pcu_dev;
}
#endif
uint16_t get_pmbase(void)
{
return pci_read_config16(get_pcu_dev(), ABASE) & 0xfff8;
}
static void print_status_bits(uint32_t status, const char *bit_names[])
{
int i;
if (!status)
return;
for (i = 31; i >= 0; i--) {
if (status & (1 << i)) {
if (bit_names[i])
printk(BIOS_DEBUG, "%s ", bit_names[i]);
else
printk(BIOS_DEBUG, "BIT%d ", i);
}
}
}
static uint32_t print_smi_status(uint32_t smi_sts)
{
static const char *smi_sts_bits[] = {
[2] = "BIOS",
[4] = "SLP_SMI",
[5] = "APM",
[6] = "SWSMI_TMR",
[8] = "PM1",
[9] = "GPE0",
[12] = "DEVMON",
[13] = "TCO",
[14] = "PERIODIC",
[15] = "ILB",
[16] = "SMBUS_SMI",
[17] = "LEGACY_USB2",
[18] = "INTEL_USB2",
[20] = "PCI_EXP_SMI",
[26] = "SPI",
[28] = "PUNIT",
[29] = "GUNIT",
};
if (!smi_sts)
return 0;
printk(BIOS_DEBUG, "SMI_STS: ");
print_status_bits(smi_sts, smi_sts_bits);
printk(BIOS_DEBUG, "\n");
return smi_sts;
}
static uint32_t reset_smi_status(void)
{
uint16_t pmbase = get_pmbase();
uint32_t smi_sts = inl(pmbase + SMI_STS);
outl(smi_sts, pmbase + SMI_STS);
return smi_sts;
}
uint32_t clear_smi_status(void)
{
return print_smi_status(reset_smi_status());
}
void enable_smi(uint32_t mask)
{
uint16_t pmbase = get_pmbase();
uint32_t smi_en = inl(pmbase + SMI_EN);
smi_en |= mask;
outl(smi_en, pmbase + SMI_EN);
}
void disable_smi(uint32_t mask)
{
uint16_t pmbase = get_pmbase();
uint32_t smi_en = inl(pmbase + SMI_EN);
smi_en &= ~mask;
outl(smi_en, pmbase + SMI_EN);
}
void enable_pm1_control(uint32_t mask)
{
uint16_t pmbase = get_pmbase();
uint32_t pm1_cnt = inl(pmbase + PM1_CNT);
pm1_cnt |= mask;
outl(pm1_cnt, pmbase + PM1_CNT);
}
void disable_pm1_control(uint32_t mask)
{
uint16_t pmbase = get_pmbase();
uint32_t pm1_cnt = inl(pmbase + PM1_CNT);
pm1_cnt &= ~mask;
outl(pm1_cnt, pmbase + PM1_CNT);
}
static uint16_t reset_pm1_status(void)
{
uint16_t pmbase = get_pmbase();
uint16_t pm1_sts = inw(pmbase + PM1_STS);
outw(pm1_sts, pmbase + PM1_STS);
return pm1_sts;
}
static uint16_t print_pm1_status(uint16_t pm1_sts)
{
static const char *pm1_sts_bits[] = {
[0] = "TMROF",
[5] = "GBL",
[8] = "PWRBTN",
[10] = "RTC",
[11] = "PRBTNOR",
[13] = "USB",
[14] = "PCIEXPWAK",
[15] = "WAK",
};
if (!pm1_sts)
return 0;
printk(BIOS_SPEW, "PM1_STS: ");
print_status_bits(pm1_sts, pm1_sts_bits);
printk(BIOS_SPEW, "\n");
return pm1_sts;
}
uint16_t clear_pm1_status(void)
{
return print_pm1_status(reset_pm1_status());
}
void enable_pm1(uint16_t events)
{
outw(events, get_pmbase() + PM1_EN);
}
static uint32_t print_tco_status(uint32_t tco_sts)
{
static const char *tco_sts_bits[] = {
[3] = "TIMEOUT",
[17] = "SECOND_TO",
};
if (!tco_sts)
return 0;
printk(BIOS_DEBUG, "TCO_STS: ");
print_status_bits(tco_sts, tco_sts_bits);
printk(BIOS_DEBUG, "\n");
return tco_sts;
}
static uint32_t reset_tco_status(void)
{
uint16_t pmbase = get_pmbase();
uint32_t tco_sts = inl(pmbase + TCO_STS);
uint32_t tco_en = inl(pmbase + TCO1_CNT);
outl(tco_sts, pmbase + TCO_STS);
return tco_sts & tco_en;
}
uint32_t clear_tco_status(void)
{
return print_tco_status(reset_tco_status());
}
void enable_gpe(uint32_t mask)
{
uint16_t pmbase = get_pmbase();
uint32_t gpe0_en = inl(pmbase + GPE0_EN);
gpe0_en |= mask;
outl(gpe0_en, pmbase + GPE0_EN);
}
void disable_gpe(uint32_t mask)
{
uint16_t pmbase = get_pmbase();
uint32_t gpe0_en = inl(pmbase + GPE0_EN);
gpe0_en &= ~mask;
outl(gpe0_en, pmbase + GPE0_EN);
}
void disable_all_gpe(void)
{
disable_gpe(~0);
}
static uint32_t reset_gpe_status(void)
{
uint16_t pmbase = get_pmbase();
uint32_t gpe_sts = inl(pmbase + GPE0_STS);
outl(gpe_sts, pmbase + GPE0_STS);
return gpe_sts;
}
static uint32_t print_gpe_sts(uint32_t gpe_sts)
{
static const char *gpe_sts_bits[] = {
[1] = "HOTPLUG",
[2] = "SWGPE",
[3] = "PCIE_WAKE0",
[4] = "PUNIT",
[5] = "GUNIT",
[6] = "PCIE_WAKE1",
[7] = "PCIE_WAKE2",
[8] = "PCIE_WAKE3",
[9] = "PCI_EXP",
[10] = "BATLOW",
[13] = "PME_B0",
[16] = "SUS_GPIO_0",
[17] = "SUS_GPIO_1",
[18] = "SUS_GPIO_2",
[19] = "SUS_GPIO_3",
[20] = "SUS_GPIO_4",
[21] = "SUS_GPIO_5",
[22] = "SUS_GPIO_6",
[23] = "SUS_GPIO_7",
[24] = "CORE_GPIO_0",
[25] = "CORE_GPIO_1",
[26] = "CORE_GPIO_2",
[27] = "CORE_GPIO_3",
[28] = "CORE_GPIO_4",
[29] = "CORE_GPIO_5",
[30] = "CORE_GPIO_6",
[31] = "CORE_GPIO_7",
};
if (!gpe_sts)
return gpe_sts;
printk(BIOS_DEBUG, "GPE0a_STS: ");
print_status_bits(gpe_sts, gpe_sts_bits);
printk(BIOS_DEBUG, "\n");
return gpe_sts;
}
uint32_t clear_gpe_status(void)
{
return print_gpe_sts(reset_gpe_status());
}

View File

@ -34,6 +34,7 @@
#include <baytrail/pci_devs.h>
#include <baytrail/reset.h>
#include <baytrail/romstage.h>
#include <baytrail/smm.h>
static inline uint64_t timestamp_get(void)
{
@ -277,13 +278,13 @@ struct ramstage_cache *ramstage_cache_location(long *size)
{
char *smm_base;
/* 1MiB cache size */
const long cache_size = (1 << 20);
const long cache_size = CONFIG_SMM_RESERVED_SIZE;
/* Ramstage cache lives in TSEG region which is the definition of
* cbmem_top(). */
smm_base = cbmem_top();
*size = cache_size;
return (void *)&smm_base[CONFIG_SMM_TSEG_SIZE - cache_size];
return (void *)&smm_base[smm_region_size() - cache_size];
}
void ramstage_cache_invalid(struct ramstage_cache *cache)

View File

@ -0,0 +1,399 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2013 Google Inc.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdint.h>
#include <stdlib.h>
#include <arch/hlt.h>
#include <arch/io.h>
#include <console/console.h>
#include <cpu/x86/cache.h>
#include <cpu/x86/smm.h>
#include <device/pci_def.h>
#include <elog.h>
#include <baytrail/pci_devs.h>
#include <baytrail/pmc.h>
#include <baytrail/nvs.h>
/* GNVS needs to be set by coreboot initiating a software SMI. */
static global_nvs_t *gnvs;
static int smm_initialized;
int southbridge_io_trap_handler(int smif)
{
switch (smif) {
case 0x32:
printk(BIOS_DEBUG, "OS Init\n");
/* gnvs->smif:
* On success, the IO Trap Handler returns 0
* On failure, the IO Trap Handler returns a value != 0
*/
gnvs->smif = 0;
return 1; /* IO trap handled */
}
/* Not handled */
return 0;
}
void southbridge_smi_set_eos(void)
{
enable_smi(EOS);
}
global_nvs_t *smm_get_gnvs(void)
{
return gnvs;
}
static void busmaster_disable_on_bus(int bus)
{
int slot, func;
unsigned int val;
unsigned char hdr;
for (slot = 0; slot < 0x20; slot++) {
for (func = 0; func < 8; func++) {
u32 reg32;
device_t dev = PCI_DEV(bus, slot, func);
val = pci_read_config32(dev, PCI_VENDOR_ID);
if (val == 0xffffffff || val == 0x00000000 ||
val == 0x0000ffff || val == 0xffff0000)
continue;
/* Disable Bus Mastering for this one device */
reg32 = pci_read_config32(dev, PCI_COMMAND);
reg32 &= ~PCI_COMMAND_MASTER;
pci_write_config32(dev, PCI_COMMAND, reg32);
/* If this is a bridge, then follow it. */
hdr = pci_read_config8(dev, PCI_HEADER_TYPE);
hdr &= 0x7f;
if (hdr == PCI_HEADER_TYPE_BRIDGE ||
hdr == PCI_HEADER_TYPE_CARDBUS) {
unsigned int buses;
buses = pci_read_config32(dev, PCI_PRIMARY_BUS);
busmaster_disable_on_bus((buses >> 8) & 0xff);
}
}
}
}
static void southbridge_smi_sleep(void)
{
uint32_t reg32;
uint8_t slp_typ;
uint16_t pmbase = get_pmbase();
/* First, disable further SMIs */
disable_smi(SLP_SMI_EN);
/* Figure out SLP_TYP */
reg32 = inl(pmbase + PM1_CNT);
printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32);
slp_typ = (reg32 >> 10) & 7;
/* Do any mainboard sleep handling */
mainboard_smi_sleep(slp_typ-2);
#if CONFIG_ELOG_GSMI
/* Log S3, S4, and S5 entry */
if (slp_typ >= 5)
elog_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ-2);
#endif
/* Next, do the deed.
*/
switch (slp_typ) {
case SLP_TYP_S0:
printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n");
break;
case SLP_TYP_S1:
printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n");
break;
case SLP_TYP_S3:
printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
/* Invalidate the cache before going to S3 */
wbinvd();
break;
case SLP_TYP_S4:
printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n");
break;
case SLP_TYP_S5:
printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
/* Disable all GPE */
disable_all_gpe();
/* also iterates over all bridges on bus 0 */
busmaster_disable_on_bus(0);
break;
default:
printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n");
break;
}
/* Write back to the SLP register to cause the originally intended
* event again. We need to set BIT13 (SLP_EN) though to make the
* sleep happen.
*/
enable_pm1_control(SLP_EN);
/* Make sure to stop executing code here for S3/S4/S5 */
if (slp_typ > 1)
hlt();
/* In most sleep states, the code flow of this function ends at
* the line above. However, if we entered sleep state S1 and wake
* up again, we will continue to execute code in this function.
*/
reg32 = inl(pmbase + PM1_CNT);
if (reg32 & SCI_EN) {
/* The OS is not an ACPI OS, so we set the state to S0 */
disable_pm1_control(SLP_EN | SLP_TYP);
}
}
/*
* Look for Synchronous IO SMI and use save state from that
* core in case we are not running on the same core that
* initiated the IO transaction.
*/
static em64t100_smm_state_save_area_t *smi_apmc_find_state_save(uint8_t cmd)
{
em64t100_smm_state_save_area_t *state;
int node;
/* Check all nodes looking for the one that issued the IO */
for (node = 0; node < CONFIG_MAX_CPUS; node++) {
state = smm_get_save_state(node);
/* Check for Synchronous IO (bit0==1) */
if (!(state->io_misc_info & (1 << 0)))
continue;
/* Make sure it was a write (bit4==0) */
if (state->io_misc_info & (1 << 4))
continue;
/* Check for APMC IO port */
if (((state->io_misc_info >> 16) & 0xff) != APM_CNT)
continue;
/* Check AX against the requested command */
if ((state->rax & 0xff) != cmd)
continue;
return state;
}
return NULL;
}
#if CONFIG_ELOG_GSMI
static void southbridge_smi_gsmi(void)
{
u32 *ret, *param;
uint8_t sub_command;
em64t100_smm_state_save_area_t *io_smi =
smi_apmc_find_state_save(ELOG_GSMI_APM_CNT);
if (!io_smi)
return;
/* Command and return value in EAX */
ret = (u32*)&io_smi->rax;
sub_command = (uint8_t)(*ret >> 8);
/* Parameter buffer in EBX */
param = (u32*)&io_smi->rbx;
/* drivers/elog/gsmi.c */
*ret = gsmi_exec(sub_command, param);
}
#endif
static void southbridge_smi_apmc(void)
{
uint8_t reg8;
em64t100_smm_state_save_area_t *state;
/* Emulate B2 register as the FADT / Linux expects it */
reg8 = inb(APM_CNT);
switch (reg8) {
case APM_CNT_CST_CONTROL:
/* Calling this function seems to cause
* some kind of race condition in Linux
* and causes a kernel oops
*/
printk(BIOS_DEBUG, "C-state control\n");
break;
case APM_CNT_PST_CONTROL:
/* Calling this function seems to cause
* some kind of race condition in Linux
* and causes a kernel oops
*/
printk(BIOS_DEBUG, "P-state control\n");
break;
case APM_CNT_ACPI_DISABLE:
disable_pm1_control(SCI_EN);
printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
break;
case APM_CNT_ACPI_ENABLE:
enable_pm1_control(SCI_EN);
printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
break;
case APM_CNT_GNVS_UPDATE:
if (smm_initialized) {
printk(BIOS_DEBUG,
"SMI#: SMM structures already initialized!\n");
return;
}
state = smi_apmc_find_state_save(reg8);
if (state) {
/* EBX in the state save contains the GNVS pointer */
gnvs = (global_nvs_t *)((uint32_t)state->rbx);
smm_initialized = 1;
printk(BIOS_DEBUG, "SMI#: Setting GNVS to %p\n", gnvs);
}
break;
#if CONFIG_ELOG_GSMI
case ELOG_GSMI_APM_CNT:
southbridge_smi_gsmi();
break;
#endif
}
mainboard_smi_apmc(reg8);
}
static void southbridge_smi_pm1(void)
{
uint16_t pm1_sts = clear_pm1_status();
/* While OSPM is not active, poweroff immediately
* on a power button event.
*/
if (pm1_sts & PWRBTN_STS) {
// power button pressed
#if CONFIG_ELOG_GSMI
elog_add_event(ELOG_TYPE_POWER_BUTTON);
#endif
disable_pm1_control(-1UL);
enable_pm1_control(SLP_EN | (SLP_TYP_S5 << 10));
}
}
static void southbridge_smi_gpe0(void)
{
clear_gpe_status();
}
static void southbridge_smi_tco(void)
{
uint32_t tco_sts = clear_tco_status();
/* Any TCO event? */
if (!tco_sts)
return;
if (tco_sts & TCO_TIMEOUT) { /* TIMEOUT */
/* Handle TCO timeout */
printk(BIOS_DEBUG, "TCO Timeout.\n");
}
}
static void southbridge_smi_periodic(void)
{
uint32_t reg32;
reg32 = inl(get_pmbase() + SMI_EN);
/* Are periodic SMIs enabled? */
if ((reg32 & PERIODIC_EN) == 0)
return;
printk(BIOS_DEBUG, "Periodic SMI.\n");
}
typedef void (*smi_handler_t)(void);
static const smi_handler_t southbridge_smi[32] = {
NULL, // [0] reserved
NULL, // [1] reserved
NULL, // [2] BIOS_STS
NULL, // [3] LEGACY_USB_STS
southbridge_smi_sleep, // [4] SLP_SMI_STS
southbridge_smi_apmc, // [5] APM_STS
NULL, // [6] SWSMI_TMR_STS
NULL, // [7] reserved
southbridge_smi_pm1, // [8] PM1_STS
southbridge_smi_gpe0, // [9] GPE0_STS
NULL, // [10] reserved
NULL, // [11] reserved
NULL, // [12] reserved
southbridge_smi_tco, // [13] TCO_STS
southbridge_smi_periodic, // [14] PERIODIC_STS
NULL, // [15] SERIRQ_SMI_STS
NULL, // [16] SMBUS_SMI_STS
NULL, // [17] LEGACY_USB2_STS
NULL, // [18] INTEL_USB2_STS
NULL, // [19] reserved
NULL, // [20] PCI_EXP_SMI_STS
NULL, // [21] reserved
NULL, // [22] reserved
NULL, // [23] reserved
NULL, // [24] reserved
NULL, // [25] reserved
NULL, // [26] SPI_STS
NULL, // [27] reserved
NULL, // [28] PUNIT
NULL, // [29] GUNIT
NULL, // [30] reserved
NULL // [31] reserved
};
void southbridge_smi_handler(void)
{
int i;
uint32_t smi_sts;
/* We need to clear the SMI status registers, or we won't see what's
* happening in the following calls.
*/
smi_sts = clear_smi_status();
/* Call SMI sub handler for each of the status bits */
for (i = 0; i < ARRAY_SIZE(southbridge_smi); i++) {
if (!(smi_sts & (1 << i)))
continue;
if (southbridge_smi[i] != NULL) {
southbridge_smi[i]();
} else {
printk(BIOS_DEBUG,
"SMI_STS[%d] occured, but no "
"handler available.\n", i);
}
}
}

View File

@ -0,0 +1,91 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2013 Google Inc.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <device/device.h>
#include <device/pci.h>
#include <console/console.h>
#include <arch/io.h>
#include <cpu/cpu.h>
#include <cpu/x86/smm.h>
#include <string.h>
#include <baytrail/pmc.h>
#include <baytrail/smm.h>
void southcluster_smm_clear_state(void)
{
uint32_t smi_en;
/* Log events from chipset before clearing */
southcluster_log_state();
printk(BIOS_DEBUG, "Initializing Southbridge SMI...");
printk(BIOS_SPEW, " pmbase = 0x%04x\n", get_pmbase());
smi_en = inl(get_pmbase() + SMI_EN);
if (smi_en & APMC_EN) {
printk(BIOS_INFO, "SMI# handler already enabled?\n");
return;
}
/* Dump and clear status registers */
clear_smi_status();
clear_pm1_status();
clear_tco_status();
clear_gpe_status();
}
void southcluster_smm_enable_smi(void)
{
printk(BIOS_DEBUG, "Enabling SMIs.\n");
/* Configure events */
enable_pm1(PWRBTN_EN | GBL_EN);
disable_gpe(PME_B0_EN);
/* Enable SMI generation:
* - on TCO events
* - on APMC writes (io 0xb2)
* - on writes to SLP_EN (sleep states)
* - on writes to GBL_RLS (bios commands)
* No SMIs:
* - on microcontroller writes (io 0x62/0x66)
*/
enable_smi(TCO_EN | APMC_EN | SLP_SMI_EN | GBL_SMI_EN | EOS);
}
void smm_setup_structures(void *gnvs, void *tcg, void *smi1)
{
/*
* Issue SMI to set the gnvs pointer in SMM.
* tcg and smi1 are unused.
*
* EAX = APM_CNT_GNVS_UPDATE
* EBX = gnvs pointer
* EDX = APM_CNT
*/
asm volatile (
"outb %%al, %%dx\n\t"
: /* ignore result */
: "a" (APM_CNT_GNVS_UPDATE),
"b" ((uint32_t)gnvs),
"d" (APM_CNT)
);
}

View File

@ -21,11 +21,6 @@
#include <cpu/x86/msr.h>
#include <cpu/x86/tsc.h>
#include <baytrail/msr.h>
#if !defined(__PRE_RAM__)
#include <baytrail/ramstage.h>
#else
#include <baytrail/romstage.h>
#endif
unsigned long tsc_freq_mhz(void)
{
@ -52,6 +47,13 @@ unsigned long tsc_freq_mhz(void)
return (bclk_khz * ((platform_info.lo >> 8) & 0xff)) / 1000;
}
#if !defined(__SMM__)
#if !defined(__PRE_RAM__)
#include <baytrail/ramstage.h>
#else
#include <baytrail/romstage.h>
#endif
void set_max_freq(void)
{
msr_t perf_ctl;
@ -74,3 +76,5 @@ void set_max_freq(void)
wrmsr(MSR_IA32_PERF_CTL, perf_ctl);
}
#endif /* __SMM__ */