2 * This file is part of the coreboot project.
4 * Copyright (C) 2008-2009 coresystems GmbH
5 * Copyright (C) 2010 Rudolf Marek <r.marek@assembler.cz>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; version 2 of
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
25 #include <arch/romcc_io.h>
26 #include <console/console.h>
27 #include <cpu/x86/cache.h>
28 #include <cpu/x86/smm.h>
29 #include <device/pci_def.h>
33 #define CST_CONTROL 0x85
34 #define PST_CONTROL 0x80
35 #define ACPI_DISABLE 0x1e
36 #define ACPI_ENABLE 0xe1
37 #define GNVS_UPDATE 0xea
42 /* While we read PMBASE dynamically in case it changed, let's
43 * initialize it with a sane value
45 u16 pmbase = DEFAULT_PMBASE;
46 u8 smm_initialized = 0;
48 /* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located
51 global_nvs_t *gnvs = (global_nvs_t *)0x0;
52 void *tcg = (void *)0x0;
53 void *smi1 = (void *)0x0;
57 * @brief read and clear PM1_STS
58 * @return PM1_STS register
60 static u16 reset_pm1_status(void)
64 reg16 = inw(pmbase + PM1_STS);
65 /* set status bits are cleared by writing 1 to them */
66 outw(reg16, pmbase + PM1_STS);
71 static void dump_pm1_status(u16 pm1_sts)
73 printk(BIOS_SPEW, "PM1_STS: ");
74 if (pm1_sts & (1 << 15)) printk(BIOS_SPEW, "WAK ");
75 if (pm1_sts & (1 << 14)) printk(BIOS_SPEW, "PCIEXPWAK ");
76 if (pm1_sts & (1 << 11)) printk(BIOS_SPEW, "PRBTNOR ");
77 if (pm1_sts & (1 << 10)) printk(BIOS_SPEW, "RTC ");
78 if (pm1_sts & (1 << 8)) printk(BIOS_SPEW, "PWRBTN ");
79 if (pm1_sts & (1 << 5)) printk(BIOS_SPEW, "GBL ");
80 if (pm1_sts & (1 << 4)) printk(BIOS_SPEW, "BM ");
81 if (pm1_sts & (1 << 0)) printk(BIOS_SPEW, "TMROF ");
82 printk(BIOS_SPEW, "\n");
83 int reg16 = inw(pmbase + PM1_EN);
84 printk(BIOS_SPEW, "PM1_EN: %x\n", reg16);
89 * @brief read and clear SMI_STS
90 * @return SMI_STS register
92 static u16 reset_smi_status(void)
96 reg16 = inw(pmbase + SMI_STS);
97 /* set status bits are cleared by writing 1 to them */
98 outw(reg16, pmbase + SMI_STS);
103 static void dump_smi_status(u16 smi_sts)
105 printk(BIOS_DEBUG, "SMI_STS: ");
106 if (smi_sts & (1 << 15)) printk(BIOS_DEBUG, "GPIO_RANGE_1 ");
107 if (smi_sts & (1 << 14)) printk(BIOS_DEBUG, "GPIO_RANGE_0 ");
108 if (smi_sts & (1 << 13)) printk(BIOS_DEBUG, "GP3_TIMEOUT ");
109 if (smi_sts & (1 << 12)) printk(BIOS_DEBUG, "GP2_TIMEOUT ");
110 if (smi_sts & (1 << 11)) printk(BIOS_DEBUG, "SERR_IRQ ");
111 if (smi_sts & (1 << 10)) printk(BIOS_DEBUG, "PMIO_5 ");
112 if (smi_sts & (1 << 9)) printk(BIOS_DEBUG, "THRMTRIP# ");
113 if (smi_sts & (1 << 8)) printk(BIOS_DEBUG, "CLKRUN# ");
114 if (smi_sts & (1 << 7)) printk(BIOS_DEBUG, "PRIMARY_IRQ/NMI/SMI ");
115 if (smi_sts & (1 << 6)) printk(BIOS_DEBUG, "SWSMI ");
116 if (smi_sts & (1 << 5)) printk(BIOS_DEBUG, "BIOS_STATUS ");
117 if (smi_sts & (1 << 4)) printk(BIOS_DEBUG, "LEGACY_USB ");
118 if (smi_sts & (1 << 3)) printk(BIOS_DEBUG, "GP1_TIMEOUT ");
119 if (smi_sts & (1 << 2)) printk(BIOS_DEBUG, "GP0_TIMEOUT ");
120 if (smi_sts & (1 << 1)) printk(BIOS_DEBUG, "SECONDARY_EVENT_TIMEOUT ");
121 if (smi_sts & (1 << 0)) printk(BIOS_DEBUG, "PRIMARY_ACTIVITY ");
122 printk(BIOS_DEBUG, "\n");
125 int southbridge_io_trap_handler(int smif)
129 printk(BIOS_DEBUG, "OS Init\n");
131 * On success, the IO Trap Handler returns 0
132 * On failure, the IO Trap Handler returns a value != 0
135 return 1; /* IO trap handled */
143 * @brief Set the EOS bit
145 void southbridge_smi_set_eos(void)
149 reg8 = inb(pmbase + SMI_EN);
151 outb(reg8, pmbase + SMI_EN);
154 static void southbridge_smi_cmd(unsigned int node, smm_state_save_area_t *state_save)
159 reg8 = inb(pmbase + 0x2f);
162 /* Calling this function seems to cause
163 * some kind of race condition in Linux
164 * and causes a kernel oops
166 printk(BIOS_DEBUG, "C-state control\n");
169 /* Calling this function seems to cause
170 * some kind of race condition in Linux
171 * and causes a kernel oops
173 printk(BIOS_DEBUG, "P-state control\n");
176 pmctrl = inw(pmbase + PM1_CNT);
178 outw(pmctrl, pmbase + PM1_CNT);
179 printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
182 pmctrl = inw(pmbase + PM1_CNT);
184 outw(pmctrl, pmbase + PM1_CNT);
185 printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
188 if (smm_initialized) {
189 printk(BIOS_DEBUG, "SMI#: SMM structures already initialized!\n");
192 gnvs = *(global_nvs_t **)0x500;
193 tcg = *(void **)0x504;
194 smi1 = *(void **)0x508;
196 printk(BIOS_DEBUG, "SMI#: Setting up structures to %p, %p, %p\n", gnvs, tcg, smi1);
199 printk(BIOS_DEBUG, "SMI#: Unknown function SMI_CMD=%02x\n", reg8);
203 typedef void (*smi_handler_t)(unsigned int node,
204 smm_state_save_area_t *state_save);
206 smi_handler_t southbridge_smi[32] = {
211 southbridge_smi_cmd, // [4]
226 * @brief Interrupt handler for SMI#
228 * @param smm_revision revision of the smm state save map
231 void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
236 /* Update global variable pmbase */
237 pmbase = pci_read_config16(PCI_DEV(0, 0x11, 0), 0x88) & 0xfffc;
239 /* We need to clear the SMI status registers, or we won't see what's
240 * happening in the following calls.
242 smi_sts = reset_smi_status();
244 /* Filter all non-enabled SMI events */
245 // FIXME Double check, this clears MONITOR
246 // smi_sts &= inl(pmbase + SMI_EN);
248 /* Call SMI sub handler for each of the status bits */
249 for (i = 0; i < 16; i++) {
250 if (smi_sts & (1 << i)) {
251 if (southbridge_smi[i])
252 southbridge_smi[i](node, state_save);
254 printk(BIOS_DEBUG, "SMI_STS[%d] occured, but no "
255 "handler available.\n", i);
262 dump_smi_status(smi_sts);