/* * This file is part of the coreboot project. * * Copyright (C) 2008-2009 coresystems GmbH * Copyright (C) 2010 Rudolf Marek * * 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 #include #include #include #include #include #include #include "vt8237r.h" #define APM_CNT 0xb2 #define CST_CONTROL 0x85 #define PST_CONTROL 0x80 #define ACPI_DISABLE 0x1e #define ACPI_ENABLE 0xe1 #define GNVS_UPDATE 0xea #define APM_STS 0xb3 #include "nvs.h" /* While we read PMBASE dynamically in case it changed, let's * initialize it with a sane value */ u16 pmbase = DEFAULT_PMBASE; u8 smm_initialized = 0; /* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located * by coreboot. */ global_nvs_t *gnvs = (global_nvs_t *)0x0; void *tcg = (void *)0x0; void *smi1 = (void *)0x0; #if 0 /** * @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); } #endif /** * @brief read and clear SMI_STS * @return SMI_STS register */ static u16 reset_smi_status(void) { u16 reg16; reg16 = inw(pmbase + SMI_STS); /* set status bits are cleared by writing 1 to them */ outw(reg16, pmbase + SMI_STS); return reg16; } static void dump_smi_status(u16 smi_sts) { printk(BIOS_DEBUG, "SMI_STS: "); if (smi_sts & (1 << 15)) printk(BIOS_DEBUG, "GPIO_RANGE_1 "); if (smi_sts & (1 << 14)) printk(BIOS_DEBUG, "GPIO_RANGE_0 "); if (smi_sts & (1 << 13)) printk(BIOS_DEBUG, "GP3_TIMEOUT "); if (smi_sts & (1 << 12)) printk(BIOS_DEBUG, "GP2_TIMEOUT "); if (smi_sts & (1 << 11)) printk(BIOS_DEBUG, "SERR_IRQ "); if (smi_sts & (1 << 10)) printk(BIOS_DEBUG, "PMIO_5 "); if (smi_sts & (1 << 9)) printk(BIOS_DEBUG, "THRMTRIP# "); if (smi_sts & (1 << 8)) printk(BIOS_DEBUG, "CLKRUN# "); if (smi_sts & (1 << 7)) printk(BIOS_DEBUG, "PRIMARY_IRQ/NMI/SMI "); if (smi_sts & (1 << 6)) printk(BIOS_DEBUG, "SWSMI "); if (smi_sts & (1 << 5)) printk(BIOS_DEBUG, "BIOS_STATUS "); if (smi_sts & (1 << 4)) printk(BIOS_DEBUG, "LEGACY_USB "); if (smi_sts & (1 << 3)) printk(BIOS_DEBUG, "GP1_TIMEOUT "); if (smi_sts & (1 << 2)) printk(BIOS_DEBUG, "GP0_TIMEOUT "); if (smi_sts & (1 << 1)) printk(BIOS_DEBUG, "SECONDARY_EVENT_TIMEOUT "); if (smi_sts & (1 << 0)) printk(BIOS_DEBUG, "PRIMARY_ACTIVITY "); printk(BIOS_DEBUG, "\n"); } 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; } /** * @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 southbridge_smi_cmd(unsigned int node, smm_state_save_area_t *state_save) { u16 pmctrl; u8 reg8; reg8 = inb(pmbase + 0x2f); switch (reg8) { case 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 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 ACPI_DISABLE: pmctrl = inw(pmbase + PM1_CNT); pmctrl &= ~SCI_EN; outw(pmctrl, pmbase + PM1_CNT); printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n"); break; case ACPI_ENABLE: pmctrl = inw(pmbase + PM1_CNT); pmctrl |= SCI_EN; outw(pmctrl, pmbase + PM1_CNT); printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n"); break; case GNVS_UPDATE: if (smm_initialized) { printk(BIOS_DEBUG, "SMI#: SMM structures already initialized!\n"); return; } gnvs = *(global_nvs_t **)0x500; tcg = *(void **)0x504; smi1 = *(void **)0x508; smm_initialized = 1; printk(BIOS_DEBUG, "SMI#: Setting up structures to %p, %p, %p\n", gnvs, tcg, smi1); break; default: printk(BIOS_DEBUG, "SMI#: Unknown function SMI_CMD=%02x\n", reg8); } } typedef void (*smi_handler_t)(unsigned int node, smm_state_save_area_t *state_save); smi_handler_t southbridge_smi[32] = { NULL, // [0] NULL, // [1] NULL, // [2] NULL, // [3] southbridge_smi_cmd, // [4] NULL, // [5] NULL, // [6] NULL, // [7] NULL, // [8] NULL, // [9] NULL, // [10] NULL, // [11] NULL, // [12] NULL, // [13] NULL, // [14] NULL, // [15] }; /** * @brief Interrupt handler for SMI# * * @param smm_revision revision of the smm state save map */ void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save) { int i, dump = 0; u32 smi_sts; /* Update global variable pmbase */ pmbase = pci_read_config16(PCI_DEV(0, 0x11, 0), 0x88) & 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 < 16; 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); } }