sb/intel/i82801gx: Use common Intel SMM code

Use the common Intel code to set up smm and the smihandler. This is
expected to break S3 resume and other smihandler related
functionality as this code is meant to be used with
CONFIG_SMM_TSEG. Platforms (i945, pineview, x4x) using this
southbridge will adapt the CONFIG_SMM_TSEG codepath in subsequent
patches.

Tested on Intel D945GCLF, still boots fine but breaks S3 resume
support because it hangs on SMI.

Change-Id: If7016a3b98fc5f14c287ce800325084f9dc602a0
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.coreboot.org/c/25594
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
This commit is contained in:
Arthur Heymans 2018-04-10 12:56:19 +02:00
parent 516f06e0fb
commit 31312b21d1
9 changed files with 22 additions and 881 deletions

View file

@ -18,7 +18,7 @@
#include <console/console.h>
#include <cpu/x86/smm.h>
#include <southbridge/intel/i82801gx/nvs.h>
#include <southbridge/intel/i82801gx/i82801gx.h>
#include <southbridge/intel/common/pmutil.h>
#include <delay.h>
#define GPE_EC_SCI 12

View file

@ -18,7 +18,7 @@
#include <console/console.h>
#include <cpu/x86/smm.h>
#include <southbridge/intel/i82801gx/nvs.h>
#include <southbridge/intel/i82801gx/i82801gx.h>
#include <southbridge/intel/common/pmutil.h>
#include <ec/acpi/ec.h>
#include "dock.h"
#include "smi.h"

View file

@ -18,7 +18,7 @@
#include <console/console.h>
#include <cpu/x86/smm.h>
#include <southbridge/intel/i82801gx/nvs.h>
#include <southbridge/intel/i82801gx/i82801gx.h>
#include <southbridge/intel/common/pmutil.h>
#include <ec/acpi/ec.h>
#include <pc80/mc146818rtc.h>
#include <ec/lenovo/h8/h8.h>

View file

@ -18,7 +18,7 @@
#include <console/console.h>
#include <cpu/x86/smm.h>
#include <southbridge/intel/i82801gx/nvs.h>
#include <southbridge/intel/i82801gx/i82801gx.h>
#include <southbridge/intel/common/pmutil.h>
#include <ec/acpi/ec.h>
#include "dock.h"
#include "smi.h"

View file

@ -63,8 +63,14 @@
#define LV2 0x14
#define LV3 0x15
#define LV4 0x16
#if IS_ENABLED(CONFIG_SOUTHBRIDGE_INTEL_I82801GX)
#define PM2_CNT 0x20 // mobile only
#define GPE0_STS 0x28
#else
#define PM2_CNT 0x50 // mobile only
#define GPE0_STS 0x20
#endif /* IS_ENABLED(CONFIG_SOUTHBRIDGE_INTEL_I82801GX) */
#define USB4_STS (1 << 14) /* i82801gx only */
#define PME_B0_STS (1 << 13)
#define PME_STS (1 << 11)
#define BATLOW_STS (1 << 10)
@ -74,7 +80,11 @@
#define TCOSCI_STS (1 << 6)
#define SWGPE_STS (1 << 2)
#define HOT_PLUG_STS (1 << 1)
#if IS_ENABLED(CONFIG_SOUTHBRIDGE_INTEL_I82801GX)
#define GPE0_EN 0x2c
#else
#define GPE0_EN 0x28
#endif /* IS_ENABLED(CONFIG_SOUTHBRIDGE_INTEL_I82801GX) */
#define PME_B0_EN (1 << 13)
#define PME_EN (1 << 11)
#define TCOSCI_EN (1 << 6)

View file

@ -28,6 +28,7 @@ config SOUTHBRIDGE_INTEL_I82801GX
select HAVE_INTEL_CHIPSET_LOCKDOWN
select SOUTHBRIDGE_INTEL_COMMON_RCBA_PIRQ
select INTEL_HAS_TOP_SWAP
select SOUTHBRIDGE_INTEL_COMMON_SMM
if SOUTHBRIDGE_INTEL_I82801GX

View file

@ -42,7 +42,6 @@
#if !defined(__SIMPLE_DEVICE__)
void i82801gx_enable(struct device *dev);
#endif
void gpi_route_interrupt(u8 gpi, u8 mode);
#else
void enable_smbus(void);
int smbus_read_byte(unsigned int device, unsigned int address);

View file

@ -23,6 +23,7 @@
#include <cpu/x86/cache.h>
#include <cpu/x86/smm.h>
#include <string.h>
#include <southbridge/intel/common/pmutil.h>
#include "i82801gx.h"
/* I945 */
@ -38,253 +39,6 @@
*/
static u16 pmbase = DEFAULT_PMBASE;
/**
* @brief read and clear PM1_STS
* @return PM1_STS register
*/
static u16 reset_pm1_status(void)
{
u16 reg16;
reg16 = inw(pmbase + PM1_STS);
/* set status bits are cleared by writing 1 to them */
outw(reg16, pmbase + PM1_STS);
return reg16;
}
static void dump_pm1_status(u16 pm1_sts)
{
printk(BIOS_DEBUG, "PM1_STS: ");
if (pm1_sts & (1 << 15))
printk(BIOS_DEBUG, "WAK ");
if (pm1_sts & (1 << 14))
printk(BIOS_DEBUG, "PCIEXPWAK ");
if (pm1_sts & (1 << 11))
printk(BIOS_DEBUG, "PRBTNOR ");
if (pm1_sts & (1 << 10))
printk(BIOS_DEBUG, "RTC ");
if (pm1_sts & (1 << 8))
printk(BIOS_DEBUG, "PWRBTN ");
if (pm1_sts & (1 << 5))
printk(BIOS_DEBUG, "GBL ");
if (pm1_sts & (1 << 4))
printk(BIOS_DEBUG, "BM ");
if (pm1_sts & (1 << 0))
printk(BIOS_DEBUG, "TMROF ");
printk(BIOS_DEBUG, "\n");
}
/**
* @brief read and clear SMI_STS
* @return SMI_STS register
*/
static u32 reset_smi_status(void)
{
u32 reg32;
reg32 = inl(pmbase + SMI_STS);
/* set status bits are cleared by writing 1 to them */
outl(reg32, pmbase + SMI_STS);
return reg32;
}
static void dump_smi_status(u32 smi_sts)
{
printk(BIOS_DEBUG, "SMI_STS: ");
if (smi_sts & (1 << 26))
printk(BIOS_DEBUG, "SPI ");
if (smi_sts & (1 << 25))
printk(BIOS_DEBUG, "EL_SMI ");
if (smi_sts & (1 << 21))
printk(BIOS_DEBUG, "MONITOR ");
if (smi_sts & (1 << 20))
printk(BIOS_DEBUG, "PCI_EXP_SMI ");
if (smi_sts & (1 << 18))
printk(BIOS_DEBUG, "INTEL_USB2 ");
if (smi_sts & (1 << 17))
printk(BIOS_DEBUG, "LEGACY_USB2 ");
if (smi_sts & (1 << 16))
printk(BIOS_DEBUG, "SMBUS_SMI ");
if (smi_sts & (1 << 15))
printk(BIOS_DEBUG, "SERIRQ_SMI ");
if (smi_sts & (1 << 14))
printk(BIOS_DEBUG, "PERIODIC ");
if (smi_sts & (1 << 13))
printk(BIOS_DEBUG, "TCO ");
if (smi_sts & (1 << 12))
printk(BIOS_DEBUG, "DEVMON ");
if (smi_sts & (1 << 11))
printk(BIOS_DEBUG, "MCSMI ");
if (smi_sts & (1 << 10))
printk(BIOS_DEBUG, "GPI ");
if (smi_sts & (1 << 9))
printk(BIOS_DEBUG, "GPE0 ");
if (smi_sts & (1 << 8))
printk(BIOS_DEBUG, "PM1 ");
if (smi_sts & (1 << 6))
printk(BIOS_DEBUG, "SWSMI_TMR ");
if (smi_sts & (1 << 5))
printk(BIOS_DEBUG, "APM ");
if (smi_sts & (1 << 4))
printk(BIOS_DEBUG, "SLP_SMI ");
if (smi_sts & (1 << 3))
printk(BIOS_DEBUG, "LEGACY_USB ");
if (smi_sts & (1 << 2))
printk(BIOS_DEBUG, "BIOS ");
printk(BIOS_DEBUG, "\n");
}
/**
* @brief read and clear GPE0_STS
* @return GPE0_STS register
*/
static u32 reset_gpe0_status(void)
{
u32 reg32;
reg32 = inl(pmbase + GPE0_STS);
/* set status bits are cleared by writing 1 to them */
outl(reg32, pmbase + GPE0_STS);
return reg32;
}
static void dump_gpe0_status(u32 gpe0_sts)
{
int i;
printk(BIOS_DEBUG, "GPE0_STS: ");
for (i = 31; i >= 16; i--) {
if (gpe0_sts & (1 << i))
printk(BIOS_DEBUG, "GPIO%d ", (i-16));
}
if (gpe0_sts & (1 << 14))
printk(BIOS_DEBUG, "USB4 ");
if (gpe0_sts & (1 << 13))
printk(BIOS_DEBUG, "PME_B0 ");
if (gpe0_sts & (1 << 12))
printk(BIOS_DEBUG, "USB3 ");
if (gpe0_sts & (1 << 11))
printk(BIOS_DEBUG, "PME ");
if (gpe0_sts & (1 << 10))
printk(BIOS_DEBUG, "EL_SCI/BATLOW ");
if (gpe0_sts & (1 << 9))
printk(BIOS_DEBUG, "PCI_EXP ");
if (gpe0_sts & (1 << 8))
printk(BIOS_DEBUG, "RI ");
if (gpe0_sts & (1 << 7))
printk(BIOS_DEBUG, "SMB_WAK ");
if (gpe0_sts & (1 << 6))
printk(BIOS_DEBUG, "TCO_SCI ");
if (gpe0_sts & (1 << 5))
printk(BIOS_DEBUG, "AC97 ");
if (gpe0_sts & (1 << 4))
printk(BIOS_DEBUG, "USB2 ");
if (gpe0_sts & (1 << 3))
printk(BIOS_DEBUG, "USB1 ");
if (gpe0_sts & (1 << 2))
printk(BIOS_DEBUG, "HOT_PLUG ");
if (gpe0_sts & (1 << 0))
printk(BIOS_DEBUG, "THRM ");
printk(BIOS_DEBUG, "\n");
}
/**
* @brief read and clear ALT_GP_SMI_STS
* @return ALT_GP_SMI_STS register
*/
static u16 reset_alt_gp_smi_status(void)
{
u16 reg16;
reg16 = inl(pmbase + ALT_GP_SMI_STS);
/* set status bits are cleared by writing 1 to them */
outl(reg16, pmbase + ALT_GP_SMI_STS);
return reg16;
}
static void dump_alt_gp_smi_status(u16 alt_gp_smi_sts)
{
int i;
printk(BIOS_DEBUG, "ALT_GP_SMI_STS: ");
for (i = 15; i >= 0; i--) {
if (alt_gp_smi_sts & (1 << i))
printk(BIOS_DEBUG, "GPI%d ", i);
}
printk(BIOS_DEBUG, "\n");
}
/**
* @brief read and clear TCOx_STS
* @return TCOx_STS registers
*/
static u32 reset_tco_status(void)
{
u32 tcobase = pmbase + 0x60;
u32 reg32;
reg32 = inl(tcobase + 0x04);
/* set status bits are cleared by writing 1 to them */
outl(reg32 & ~(1 << 18), tcobase + 0x04); // Don't clear BOOT_STS before SECOND_TO_STS
if (reg32 & (1 << 18))
outl(reg32 & (1 << 18), tcobase + 0x04); // clear BOOT_STS
return reg32;
}
static void dump_tco_status(u32 tco_sts)
{
printk(BIOS_DEBUG, "TCO_STS: ");
if (tco_sts & (1 << 20))
printk(BIOS_DEBUG, "SMLINK_SLV ");
if (tco_sts & (1 << 18))
printk(BIOS_DEBUG, "BOOT ");
if (tco_sts & (1 << 17))
printk(BIOS_DEBUG, "SECOND_TO ");
if (tco_sts & (1 << 16))
printk(BIOS_DEBUG, "INTRD_DET ");
if (tco_sts & (1 << 12))
printk(BIOS_DEBUG, "DMISERR ");
if (tco_sts & (1 << 10))
printk(BIOS_DEBUG, "DMISMI ");
if (tco_sts & (1 << 9))
printk(BIOS_DEBUG, "DMISCI ");
if (tco_sts & (1 << 8))
printk(BIOS_DEBUG, "BIOSWR ");
if (tco_sts & (1 << 7))
printk(BIOS_DEBUG, "NEWCENTURY ");
if (tco_sts & (1 << 3))
printk(BIOS_DEBUG, "TIMEOUT ");
if (tco_sts & (1 << 2))
printk(BIOS_DEBUG, "TCO_INT ");
if (tco_sts & (1 << 1))
printk(BIOS_DEBUG, "SW_TCO ");
if (tco_sts & (1 << 0))
printk(BIOS_DEBUG, "NMI2SMI ");
printk(BIOS_DEBUG, "\n");
}
/**
* @brief Set the EOS bit
*/
static void smi_set_eos(void)
{
u8 reg8;
reg8 = inb(pmbase + SMI_EN);
reg8 |= EOS;
outb(reg8, pmbase + SMI_EN);
}
extern uint8_t smm_relocation_start, smm_relocation_end;
static void *default_smm_area = NULL;
@ -429,12 +183,3 @@ void smm_lock(void)
pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
D_LCK | G_SMRAME | C_BASE_SEG);
}
void smm_setup_structures(void *gnvs, void *tcg, void *smi1)
{
/* The GDT or coreboot table is going to live here. But a long time
* after we relocated the GNVS, so this is not troublesome.
*/
*(u32 *)0x500 = (u32)gnvs;
outb(0xea, 0xb2);
}

View file

@ -22,6 +22,7 @@
#include <device/pci_def.h>
#include <halt.h>
#include <pc80/mc146818rtc.h>
#include <southbridge/intel/common/pmutil.h>
#include "i82801gx.h"
/* I945 */
@ -45,246 +46,10 @@ u8 smm_initialized = 0;
*/
global_nvs_t *gnvs = (global_nvs_t *)0x0;
static void alt_gpi_mask(u16 clr, u16 set)
void southbridge_update_gnvs(u8 apm_cnt, int *smm_done)
{
u16 alt_gp = inw(pmbase + ALT_GP_SMI_EN);
alt_gp &= ~clr;
alt_gp |= set;
outw(alt_gp, pmbase + ALT_GP_SMI_EN);
}
static void gpe0_mask(u32 clr, u32 set)
{
u32 gpe0 = inl(pmbase + GPE0_EN);
gpe0 &= ~clr;
gpe0 |= set;
outl(gpe0, pmbase + GPE0_EN);
}
void gpi_route_interrupt(u8 gpi, u8 mode)
{
u32 gpi_rout;
if (gpi >= 16)
return;
alt_gpi_mask(1 << gpi, 0);
gpe0_mask(1 << (gpi+16), 0);
gpi_rout = pci_read_config32(PCI_DEV(0, 0x1f, 0), GPIO_ROUT);
gpi_rout &= ~(3 << (2 * gpi));
gpi_rout |= ((mode & 3) << (2 * gpi));
pci_write_config32(PCI_DEV(0, 0x1f, 0), GPIO_ROUT, gpi_rout);
if (mode == GPI_IS_SCI)
gpe0_mask(0, 1 << (gpi+16));
else if (mode == GPI_IS_SMI)
alt_gpi_mask(0, 1 << gpi);
}
/**
* @brief read and clear PM1_STS
* @return PM1_STS register
*/
static u16 reset_pm1_status(void)
{
u16 reg16;
reg16 = inw(pmbase + PM1_STS);
/* set status bits are cleared by writing 1 to them */
outw(reg16, pmbase + PM1_STS);
return reg16;
}
static void dump_pm1_status(u16 pm1_sts)
{
printk(BIOS_SPEW, "PM1_STS: ");
if (pm1_sts & (1 << 15))
printk(BIOS_SPEW, "WAK ");
if (pm1_sts & (1 << 14))
printk(BIOS_SPEW, "PCIEXPWAK ");
if (pm1_sts & (1 << 11))
printk(BIOS_SPEW, "PRBTNOR ");
if (pm1_sts & (1 << 10))
printk(BIOS_SPEW, "RTC ");
if (pm1_sts & (1 << 8))
printk(BIOS_SPEW, "PWRBTN ");
if (pm1_sts & (1 << 5))
printk(BIOS_SPEW, "GBL ");
if (pm1_sts & (1 << 4))
printk(BIOS_SPEW, "BM ");
if (pm1_sts & (1 << 0))
printk(BIOS_SPEW, "TMROF ");
printk(BIOS_SPEW, "\n");
int reg16 = inw(pmbase + PM1_EN);
printk(BIOS_SPEW, "PM1_EN: %x\n", reg16);
}
/**
* @brief read and clear SMI_STS
* @return SMI_STS register
*/
static u32 reset_smi_status(void)
{
u32 reg32;
reg32 = inl(pmbase + SMI_STS);
/* set status bits are cleared by writing 1 to them */
outl(reg32, pmbase + SMI_STS);
return reg32;
}
static void dump_smi_status(u32 smi_sts)
{
printk(BIOS_DEBUG, "SMI_STS: ");
if (smi_sts & (1 << 26))
printk(BIOS_DEBUG, "SPI ");
if (smi_sts & (1 << 25))
printk(BIOS_DEBUG, "EL_SMI ");
if (smi_sts & (1 << 21))
printk(BIOS_DEBUG, "MONITOR ");
if (smi_sts & (1 << 20))
printk(BIOS_DEBUG, "PCI_EXP_SMI ");
if (smi_sts & (1 << 18))
printk(BIOS_DEBUG, "INTEL_USB2 ");
if (smi_sts & (1 << 17))
printk(BIOS_DEBUG, "LEGACY_USB2 ");
if (smi_sts & (1 << 16))
printk(BIOS_DEBUG, "SMBUS_SMI ");
if (smi_sts & (1 << 15))
printk(BIOS_DEBUG, "SERIRQ_SMI ");
if (smi_sts & (1 << 14))
printk(BIOS_DEBUG, "PERIODIC ");
if (smi_sts & (1 << 13))
printk(BIOS_DEBUG, "TCO ");
if (smi_sts & (1 << 12))
printk(BIOS_DEBUG, "DEVMON ");
if (smi_sts & (1 << 11))
printk(BIOS_DEBUG, "MCSMI ");
if (smi_sts & (1 << 10))
printk(BIOS_DEBUG, "GPI ");
if (smi_sts & (1 << 9))
printk(BIOS_DEBUG, "GPE0 ");
if (smi_sts & (1 << 8))
printk(BIOS_DEBUG, "PM1 ");
if (smi_sts & (1 << 6))
printk(BIOS_DEBUG, "SWSMI_TMR ");
if (smi_sts & (1 << 5))
printk(BIOS_DEBUG, "APM ");
if (smi_sts & (1 << 4))
printk(BIOS_DEBUG, "SLP_SMI ");
if (smi_sts & (1 << 3))
printk(BIOS_DEBUG, "LEGACY_USB ");
if (smi_sts & (1 << 2))
printk(BIOS_DEBUG, "BIOS ");
printk(BIOS_DEBUG, "\n");
}
/**
* @brief read and clear GPE0_STS
* @return GPE0_STS register
*/
static u32 reset_gpe0_status(void)
{
u32 reg32;
reg32 = inl(pmbase + GPE0_STS);
/* set status bits are cleared by writing 1 to them */
outl(reg32, pmbase + GPE0_STS);
return reg32;
}
static void dump_gpe0_status(u32 gpe0_sts)
{
int i;
printk(BIOS_DEBUG, "GPE0_STS: ");
for (i = 31; i >= 16; i--) {
if (gpe0_sts & (1 << i))
printk(BIOS_DEBUG, "GPIO%d ", (i-16));
}
if (gpe0_sts & (1 << 14))
printk(BIOS_DEBUG, "USB4 ");
if (gpe0_sts & (1 << 13))
printk(BIOS_DEBUG, "PME_B0 ");
if (gpe0_sts & (1 << 12))
printk(BIOS_DEBUG, "USB3 ");
if (gpe0_sts & (1 << 11))
printk(BIOS_DEBUG, "PME ");
if (gpe0_sts & (1 << 10))
printk(BIOS_DEBUG, "EL_SCI/BATLOW ");
if (gpe0_sts & (1 << 9))
printk(BIOS_DEBUG, "PCI_EXP ");
if (gpe0_sts & (1 << 8))
printk(BIOS_DEBUG, "RI ");
if (gpe0_sts & (1 << 7))
printk(BIOS_DEBUG, "SMB_WAK ");
if (gpe0_sts & (1 << 6))
printk(BIOS_DEBUG, "TCO_SCI ");
if (gpe0_sts & (1 << 5))
printk(BIOS_DEBUG, "AC97 ");
if (gpe0_sts & (1 << 4))
printk(BIOS_DEBUG, "USB2 ");
if (gpe0_sts & (1 << 3))
printk(BIOS_DEBUG, "USB1 ");
if (gpe0_sts & (1 << 2))
printk(BIOS_DEBUG, "HOT_PLUG ");
if (gpe0_sts & (1 << 0))
printk(BIOS_DEBUG, "THRM ");
printk(BIOS_DEBUG, "\n");
}
/**
* @brief read and clear TCOx_STS
* @return TCOx_STS registers
*/
static u32 reset_tco_status(void)
{
u32 tcobase = pmbase + 0x60;
u32 reg32;
reg32 = inl(tcobase + 0x04);
/* set status bits are cleared by writing 1 to them */
outl(reg32 & ~(1 << 18), tcobase + 0x04); // Don't clear BOOT_STS before SECOND_TO_STS
if (reg32 & (1 << 18))
outl(reg32 & (1 << 18), tcobase + 0x04); // clear BOOT_STS
return reg32;
}
static void dump_tco_status(u32 tco_sts)
{
printk(BIOS_DEBUG, "TCO_STS: ");
if (tco_sts & (1 << 20))
printk(BIOS_DEBUG, "SMLINK_SLV ");
if (tco_sts & (1 << 18))
printk(BIOS_DEBUG, "BOOT ");
if (tco_sts & (1 << 17))
printk(BIOS_DEBUG, "SECOND_TO ");
if (tco_sts & (1 << 16))
printk(BIOS_DEBUG, "INTRD_DET ");
if (tco_sts & (1 << 12))
printk(BIOS_DEBUG, "DMISERR ");
if (tco_sts & (1 << 10))
printk(BIOS_DEBUG, "DMISMI ");
if (tco_sts & (1 << 9))
printk(BIOS_DEBUG, "DMISCI ");
if (tco_sts & (1 << 8))
printk(BIOS_DEBUG, "BIOSWR ");
if (tco_sts & (1 << 7))
printk(BIOS_DEBUG, "NEWCENTURY ");
if (tco_sts & (1 << 3))
printk(BIOS_DEBUG, "TIMEOUT ");
if (tco_sts & (1 << 2))
printk(BIOS_DEBUG, "TCO_INT ");
if (tco_sts & (1 << 1))
printk(BIOS_DEBUG, "SW_TCO ");
if (tco_sts & (1 << 0))
printk(BIOS_DEBUG, "NMI2SMI ");
printk(BIOS_DEBUG, "\n");
gnvs = *(global_nvs_t **)0x500;
*smm_done = 1;
}
int southbridge_io_trap_handler(int smif)
@ -304,312 +69,7 @@ int southbridge_io_trap_handler(int smif)
return 0;
}
/**
* @brief Set the EOS bit
*/
void southbridge_smi_set_eos(void)
{
u8 reg8;
reg8 = inb(pmbase + SMI_EN);
reg8 |= EOS;
outb(reg8, pmbase + SMI_EN);
}
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;
pci_devfn_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(unsigned int node, smm_state_save_area_t *state_save)
{
u8 reg8;
u32 reg32;
u8 slp_typ;
u8 s5pwr = CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL;
// save and recover RTC port values
u8 tmp70, tmp72;
tmp70 = inb(0x70);
tmp72 = inb(0x72);
get_option(&s5pwr, "power_on_after_fail");
outb(tmp70, 0x70);
outb(tmp72, 0x72);
/* First, disable further SMIs */
reg8 = inb(pmbase + SMI_EN);
reg8 &= ~SLP_SMI_EN;
outb(reg8, pmbase + SMI_EN);
/* Figure out SLP_TYP */
reg32 = inl(pmbase + PM1_CNT);
printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32);
slp_typ = acpi_sleep_from_pm1(reg32);
/* Next, do the deed.
*/
switch (slp_typ) {
case ACPI_S0:
printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n"); break;
case ACPI_S1:
printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n"); break;
case ACPI_S3:
printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
/* Invalidate the cache before going to S3 */
wbinvd();
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");
outl(0, pmbase + GPE0_EN);
/* Always set the flag in case CMOS was changed on runtime. For
* "KEEP", switch to "OFF" - KEEP is software emulated
*/
reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3);
if (s5pwr == MAINBOARD_POWER_ON)
reg8 &= ~1;
else
reg8 |= 1;
pci_write_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3, reg8);
/* 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;
}
#if !IS_ENABLED(CONFIG_SMM_TSEG)
/* Unlock the SMI semaphore. We're currently in SMM, and the semaphore
* will never be unlocked because the next outl will switch off the CPU.
* This might open a small race between the smi_release_lock() and the outl()
* for other SMI handlers. Not sure if this could cause trouble. */
if (slp_typ == ACPI_S3)
smi_release_lock();
#endif
/* 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.
*/
outl(reg32 | SLP_EN, pmbase + PM1_CNT);
/* Make sure to stop executing code here for S3/S4/S5 */
if (slp_typ >= ACPI_S3)
halt();
/* 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 */
reg32 &= ~(SLP_EN | SLP_TYP);
outl(reg32, pmbase + PM1_CNT);
}
}
static void southbridge_smi_apmc(unsigned int node, smm_state_save_area_t *state_save)
{
u32 pmctrl;
u8 reg8;
/* Emulate B2 register as the FADT / Linux expects it */
reg8 = inb(APM_CNT);
if (mainboard_smi_apmc(reg8))
return;
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:
pmctrl = inl(pmbase + PM1_CNT);
pmctrl &= ~SCI_EN;
outl(pmctrl, pmbase + PM1_CNT);
printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
break;
case APM_CNT_ACPI_ENABLE:
pmctrl = inl(pmbase + PM1_CNT);
pmctrl |= SCI_EN;
outl(pmctrl, pmbase + PM1_CNT);
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;
}
gnvs = *(global_nvs_t **)0x500;
smm_initialized = 1;
printk(BIOS_DEBUG, "SMI#: Setting up structures to %p\n", gnvs);
break;
default:
printk(BIOS_DEBUG, "SMI#: Unknown function APM_CNT=%02x\n", reg8);
}
}
static void southbridge_smi_pm1(unsigned int node, smm_state_save_area_t *state_save)
{
u16 pm1_sts;
volatile u8 cmos_status;
pm1_sts = reset_pm1_status();
dump_pm1_status(pm1_sts);
/* While OSPM is not active, poweroff immediately
* on a power button event.
*/
if (pm1_sts & PWRBTN_STS) {
// power button pressed
u32 reg32;
reg32 = (7 << 10) | (1 << 13);
outl(reg32, pmbase + PM1_CNT);
}
if (pm1_sts & RTC_STS) {
/* read RTC status register to disable the interrupt */
cmos_status = cmos_read(RTC_REG_C);
printk(BIOS_DEBUG, "RTC IRQ status: %02X\n", cmos_status);
}
}
static void southbridge_smi_gpe0(unsigned int node, smm_state_save_area_t *state_save)
{
u32 gpe0_sts;
gpe0_sts = reset_gpe0_status();
dump_gpe0_status(gpe0_sts);
}
static void southbridge_smi_gpi(unsigned int node, smm_state_save_area_t *state_save)
{
u16 reg16;
reg16 = inw(pmbase + ALT_GP_SMI_STS);
outw(reg16, pmbase + ALT_GP_SMI_STS);
reg16 &= inw(pmbase + ALT_GP_SMI_EN);
mainboard_smi_gpi(reg16);
if (reg16)
printk(BIOS_DEBUG, "GPI (mask %04x)\n", reg16);
}
static void southbridge_smi_mc(unsigned int node, smm_state_save_area_t *state_save)
{
u32 reg32;
reg32 = inl(pmbase + SMI_EN);
/* Are periodic SMIs enabled? */
if ((reg32 & MCSMI_EN) == 0)
return;
printk(BIOS_DEBUG, "Microcontroller SMI.\n");
}
static void southbridge_smi_tco(unsigned int node, smm_state_save_area_t *state_save)
{
u32 tco_sts;
tco_sts = reset_tco_status();
/* Any TCO event? */
if (!tco_sts)
return;
if (tco_sts & (1 << 8)) {
/* BIOSWR */
u8 bios_cntl;
bios_cntl = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0xdc);
if (bios_cntl & 1) {
/* BWE is RW, so the SMI was caused by a
* write to BWE, not by a write to the BIOS
*/
/* This is the place where we notice someone
* is trying to tinker with the BIOS. We are
* trying to be nice and just ignore it. A more
* resolute answer would be to power down the
* box.
*/
printk(BIOS_DEBUG, "Switching back to RO\n");
pci_write_config32(PCI_DEV(0, 0x1f, 0), 0xdc, (bios_cntl & ~1));
} /* No else for now? */
} else if (tco_sts & (1 << 3)) { /* TIMEOUT */
/* Handle TCO timeout */
printk(BIOS_DEBUG, "TCO Timeout.\n");
} else if (!tco_sts) {
dump_tco_status(tco_sts);
}
}
static void southbridge_smi_periodic(unsigned int node, smm_state_save_area_t *state_save)
{
u32 reg32;
reg32 = inl(pmbase + SMI_EN);
/* Are periodic SMIs enabled? */
if ((reg32 & PERIODIC_EN) == 0)
return;
printk(BIOS_DEBUG, "Periodic SMI.\n");
}
static void southbridge_smi_monitor(unsigned int node, smm_state_save_area_t *state_save)
void southbridge_smi_monitor(void)
{
#define IOTRAP(x) (trap_sts & (1 << x))
u32 trap_sts, trap_cycle;
@ -654,80 +114,6 @@ static void southbridge_smi_monitor(unsigned int node, smm_state_save_area_t *st
#undef IOTRAP
}
typedef void (*smi_handler_t)(unsigned int node,
smm_state_save_area_t *state_save);
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
southbridge_smi_gpi, // [10] GPI_STS
southbridge_smi_mc, // [11] MCSMI_STS
NULL, // [12] DEVMON_STS
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
southbridge_smi_monitor, // [21] MONITOR_STS
NULL, // [22] reserved
NULL, // [23] reserved
NULL, // [24] reserved
NULL, // [25] EL_SMI_STS
NULL, // [26] SPI_STS
NULL, // [27] reserved
NULL, // [28] reserved
NULL, // [29] reserved
NULL, // [30] reserved
NULL // [31] reserved
};
/**
* @brief Interrupt handler for SMI#
* @param node
* @param state_save
*/
void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
void southbridge_finalize_all(void)
{
int i, dump = 0;
u32 smi_sts;
/* Update global variable pmbase */
pmbase = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0x40) & 0xfffc;
/* We need to clear the SMI status registers, or we won't see what's
* happening in the following calls.
*/
smi_sts = reset_smi_status();
/* Filter all non-enabled SMI events */
// FIXME Double check, this clears MONITOR
// smi_sts &= inl(pmbase + SMI_EN);
/* Call SMI sub handler for each of the status bits */
for (i = 0; i < 31; i++) {
if (smi_sts & (1 << i)) {
if (southbridge_smi[i])
southbridge_smi[i](node, state_save);
else {
printk(BIOS_DEBUG, "SMI_STS[%d] occured, but no "
"handler available.\n", i);
dump = 1;
}
}
}
if (dump)
dump_smi_status(smi_sts);
}