blob: 3a08daad3fd7f15eb9b96bfc216a782581b588cb [file] [log] [blame]
Stefan Reinauer800379f2010-03-01 08:34:19 +00001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2008-2009 coresystems GmbH
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; version 2 of
9 * the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Stefan Reinauer800379f2010-03-01 08:34:19 +000015 */
16
17#include <types.h>
18#include <arch/io.h>
Stefan Reinauer800379f2010-03-01 08:34:19 +000019#include <console/console.h>
20#include <cpu/x86/cache.h>
21#include <cpu/x86/smm.h>
22#include <device/pci_def.h>
23#include "i82801dx.h"
24
25#define DEBUG_SMI
26
Stefan Reinauer800379f2010-03-01 08:34:19 +000027/* I830M */
28#define SMRAM 0x90
29#define D_OPEN (1 << 6)
30#define D_CLS (1 << 5)
31#define D_LCK (1 << 4)
32#define G_SMRANE (1 << 3)
33#define C_BASE_SEG ((0 << 2) | (1 << 1) | (0 << 0))
34
stepan836ae292010-12-08 05:42:47 +000035#include "nvs.h"
Stefan Reinauer800379f2010-03-01 08:34:19 +000036
37/* While we read PMBASE dynamically in case it changed, let's
38 * initialize it with a sane value
39 */
40u16 pmbase = PMBASE_ADDR;
41u8 smm_initialized = 0;
42
43unsigned char *mbi = NULL;
44u32 mbi_len;
45u8 mbi_initialized = 0;
46
47/* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located
48 * by coreboot.
49 */
50global_nvs_t *gnvs = (global_nvs_t *)0x0;
51void *tcg = (void *)0x0;
52void *smi1 = (void *)0x0;
53
54/**
55 * @brief read and clear PM1_STS
56 * @return PM1_STS register
57 */
58static u16 reset_pm1_status(void)
59{
60 u16 reg16;
61
62 reg16 = inw(pmbase + PM1_STS);
63 /* set status bits are cleared by writing 1 to them */
64 outw(reg16, pmbase + PM1_STS);
65
66 return reg16;
67}
68
69static void dump_pm1_status(u16 pm1_sts)
70{
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000071 printk(BIOS_SPEW, "PM1_STS: ");
72 if (pm1_sts & (1 << 15)) printk(BIOS_SPEW, "WAK ");
73 if (pm1_sts & (1 << 14)) printk(BIOS_SPEW, "PCIEXPWAK ");
74 if (pm1_sts & (1 << 11)) printk(BIOS_SPEW, "PRBTNOR ");
75 if (pm1_sts & (1 << 10)) printk(BIOS_SPEW, "RTC ");
76 if (pm1_sts & (1 << 8)) printk(BIOS_SPEW, "PWRBTN ");
77 if (pm1_sts & (1 << 5)) printk(BIOS_SPEW, "GBL ");
78 if (pm1_sts & (1 << 4)) printk(BIOS_SPEW, "BM ");
79 if (pm1_sts & (1 << 0)) printk(BIOS_SPEW, "TMROF ");
80 printk(BIOS_SPEW, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +000081 int reg16 = inw(pmbase + PM1_EN);
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +000082 printk(BIOS_SPEW, "PM1_EN: %x\n", reg16);
Stefan Reinauer800379f2010-03-01 08:34:19 +000083}
84
85/**
86 * @brief read and clear SMI_STS
87 * @return SMI_STS register
88 */
89static u32 reset_smi_status(void)
90{
91 u32 reg32;
92
93 reg32 = inl(pmbase + SMI_STS);
94 /* set status bits are cleared by writing 1 to them */
95 outl(reg32, pmbase + SMI_STS);
96
97 return reg32;
98}
99
100static void dump_smi_status(u32 smi_sts)
101{
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000102 printk(BIOS_DEBUG, "SMI_STS: ");
103 if (smi_sts & (1 << 26)) printk(BIOS_DEBUG, "SPI ");
104 if (smi_sts & (1 << 25)) printk(BIOS_DEBUG, "EL_SMI ");
105 if (smi_sts & (1 << 21)) printk(BIOS_DEBUG, "MONITOR ");
106 if (smi_sts & (1 << 20)) printk(BIOS_DEBUG, "PCI_EXP_SMI ");
107 if (smi_sts & (1 << 18)) printk(BIOS_DEBUG, "INTEL_USB2 ");
108 if (smi_sts & (1 << 17)) printk(BIOS_DEBUG, "LEGACY_USB2 ");
109 if (smi_sts & (1 << 16)) printk(BIOS_DEBUG, "SMBUS_SMI ");
110 if (smi_sts & (1 << 15)) printk(BIOS_DEBUG, "SERIRQ_SMI ");
111 if (smi_sts & (1 << 14)) printk(BIOS_DEBUG, "PERIODIC ");
112 if (smi_sts & (1 << 13)) printk(BIOS_DEBUG, "TCO ");
113 if (smi_sts & (1 << 12)) printk(BIOS_DEBUG, "DEVMON ");
114 if (smi_sts & (1 << 11)) printk(BIOS_DEBUG, "MCSMI ");
115 if (smi_sts & (1 << 10)) printk(BIOS_DEBUG, "GPI ");
116 if (smi_sts & (1 << 9)) printk(BIOS_DEBUG, "GPE0 ");
117 if (smi_sts & (1 << 8)) printk(BIOS_DEBUG, "PM1 ");
118 if (smi_sts & (1 << 6)) printk(BIOS_DEBUG, "SWSMI_TMR ");
119 if (smi_sts & (1 << 5)) printk(BIOS_DEBUG, "APM ");
120 if (smi_sts & (1 << 4)) printk(BIOS_DEBUG, "SLP_SMI ");
121 if (smi_sts & (1 << 3)) printk(BIOS_DEBUG, "LEGACY_USB ");
122 if (smi_sts & (1 << 2)) printk(BIOS_DEBUG, "BIOS ");
123 printk(BIOS_DEBUG, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000124}
125
126
127/**
128 * @brief read and clear GPE0_STS
129 * @return GPE0_STS register
130 */
131static u32 reset_gpe0_status(void)
132{
133 u32 reg32;
134
135 reg32 = inl(pmbase + GPE0_STS);
136 /* set status bits are cleared by writing 1 to them */
137 outl(reg32, pmbase + GPE0_STS);
138
139 return reg32;
140}
141
142static void dump_gpe0_status(u32 gpe0_sts)
143{
144 int i;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000145 printk(BIOS_DEBUG, "GPE0_STS: ");
Konstantin Aladyshev62f80832013-03-07 04:04:27 +0400146 for (i=31; i>= 16; i--) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000147 if (gpe0_sts & (1 << i)) printk(BIOS_DEBUG, "GPIO%d ", (i-16));
Stefan Reinauer800379f2010-03-01 08:34:19 +0000148 }
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000149 if (gpe0_sts & (1 << 14)) printk(BIOS_DEBUG, "USB4 ");
150 if (gpe0_sts & (1 << 13)) printk(BIOS_DEBUG, "PME_B0 ");
151 if (gpe0_sts & (1 << 12)) printk(BIOS_DEBUG, "USB3 ");
152 if (gpe0_sts & (1 << 11)) printk(BIOS_DEBUG, "PME ");
153 if (gpe0_sts & (1 << 10)) printk(BIOS_DEBUG, "EL_SCI/BATLOW ");
154 if (gpe0_sts & (1 << 9)) printk(BIOS_DEBUG, "PCI_EXP ");
155 if (gpe0_sts & (1 << 8)) printk(BIOS_DEBUG, "RI ");
156 if (gpe0_sts & (1 << 7)) printk(BIOS_DEBUG, "SMB_WAK ");
157 if (gpe0_sts & (1 << 6)) printk(BIOS_DEBUG, "TCO_SCI ");
158 if (gpe0_sts & (1 << 5)) printk(BIOS_DEBUG, "AC97 ");
159 if (gpe0_sts & (1 << 4)) printk(BIOS_DEBUG, "USB2 ");
160 if (gpe0_sts & (1 << 3)) printk(BIOS_DEBUG, "USB1 ");
161 if (gpe0_sts & (1 << 2)) printk(BIOS_DEBUG, "HOT_PLUG ");
162 if (gpe0_sts & (1 << 0)) printk(BIOS_DEBUG, "THRM ");
163 printk(BIOS_DEBUG, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000164}
165
166
167/**
168 * @brief read and clear TCOx_STS
169 * @return TCOx_STS registers
170 */
171static u32 reset_tco_status(void)
172{
173 u32 tcobase = pmbase + 0x60;
174 u32 reg32;
175
176 reg32 = inl(tcobase + 0x04);
177 /* set status bits are cleared by writing 1 to them */
178 outl(reg32 & ~(1<<18), tcobase + 0x04); // Don't clear BOOT_STS before SECOND_TO_STS
179 if (reg32 & (1 << 18))
180 outl(reg32 & (1<<18), tcobase + 0x04); // clear BOOT_STS
181
182 return reg32;
183}
184
185
186static void dump_tco_status(u32 tco_sts)
187{
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000188 printk(BIOS_DEBUG, "TCO_STS: ");
189 if (tco_sts & (1 << 20)) printk(BIOS_DEBUG, "SMLINK_SLV ");
190 if (tco_sts & (1 << 18)) printk(BIOS_DEBUG, "BOOT ");
191 if (tco_sts & (1 << 17)) printk(BIOS_DEBUG, "SECOND_TO ");
192 if (tco_sts & (1 << 16)) printk(BIOS_DEBUG, "INTRD_DET ");
193 if (tco_sts & (1 << 12)) printk(BIOS_DEBUG, "DMISERR ");
194 if (tco_sts & (1 << 10)) printk(BIOS_DEBUG, "DMISMI ");
195 if (tco_sts & (1 << 9)) printk(BIOS_DEBUG, "DMISCI ");
196 if (tco_sts & (1 << 8)) printk(BIOS_DEBUG, "BIOSWR ");
197 if (tco_sts & (1 << 7)) printk(BIOS_DEBUG, "NEWCENTURY ");
198 if (tco_sts & (1 << 3)) printk(BIOS_DEBUG, "TIMEOUT ");
199 if (tco_sts & (1 << 2)) printk(BIOS_DEBUG, "TCO_INT ");
200 if (tco_sts & (1 << 1)) printk(BIOS_DEBUG, "SW_TCO ");
201 if (tco_sts & (1 << 0)) printk(BIOS_DEBUG, "NMI2SMI ");
202 printk(BIOS_DEBUG, "\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000203}
204
Stefan Reinauer800379f2010-03-01 08:34:19 +0000205int southbridge_io_trap_handler(int smif)
206{
207 switch (smif) {
208 case 0x32:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000209 printk(BIOS_DEBUG, "OS Init\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000210 /* gnvs->smif:
211 * On success, the IO Trap Handler returns 0
212 * On failure, the IO Trap Handler returns a value != 0
213 */
214 gnvs->smif = 0;
215 return 1; /* IO trap handled */
216 }
217
218 /* Not handled */
219 return 0;
220}
221
222/**
223 * @brief Set the EOS bit
224 */
225void southbridge_smi_set_eos(void)
226{
227 u8 reg8;
228
229 reg8 = inb(pmbase + SMI_EN);
230 reg8 |= EOS;
231 outb(reg8, pmbase + SMI_EN);
232}
233
234static void busmaster_disable_on_bus(int bus)
235{
Elyes HAOUASba28e8d2016-08-31 19:22:16 +0200236 int slot, func;
237 unsigned int val;
238 unsigned char hdr;
Stefan Reinauer800379f2010-03-01 08:34:19 +0000239
Elyes HAOUASba28e8d2016-08-31 19:22:16 +0200240 for (slot = 0; slot < 0x20; slot++) {
241 for (func = 0; func < 8; func++) {
242 u32 reg32;
Antonello Dettorif9aac2f2016-09-03 10:45:33 +0200243 pci_devfn_t dev = PCI_DEV(bus, slot, func);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000244
Elyes HAOUASba28e8d2016-08-31 19:22:16 +0200245 val = pci_read_config32(dev, PCI_VENDOR_ID);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000246
Elyes HAOUASba28e8d2016-08-31 19:22:16 +0200247 if (val == 0xffffffff || val == 0x00000000 ||
248 val == 0x0000ffff || val == 0xffff0000)
249 continue;
Stefan Reinauer800379f2010-03-01 08:34:19 +0000250
Elyes HAOUASba28e8d2016-08-31 19:22:16 +0200251 /* Disable Bus Mastering for this one device */
252 reg32 = pci_read_config32(dev, PCI_COMMAND);
253 reg32 &= ~PCI_COMMAND_MASTER;
254 pci_write_config32(dev, PCI_COMMAND, reg32);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000255
Elyes HAOUASba28e8d2016-08-31 19:22:16 +0200256 /* If this is a bridge, then follow it. */
257 hdr = pci_read_config8(dev, PCI_HEADER_TYPE);
258 hdr &= 0x7f;
259 if (hdr == PCI_HEADER_TYPE_BRIDGE ||
260 hdr == PCI_HEADER_TYPE_CARDBUS) {
261 unsigned int buses;
262 buses = pci_read_config32(dev, PCI_PRIMARY_BUS);
263 busmaster_disable_on_bus((buses >> 8) & 0xff);
264 }
265 }
266 }
Stefan Reinauer800379f2010-03-01 08:34:19 +0000267}
268
269
270static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *state_save)
271{
272 u8 reg8;
273 u32 reg32;
274 u8 slp_typ;
275 /* FIXME: the power state on boot should be read from
276 * CMOS or even better from GNVS. Right now it's hard
277 * coded at compile time.
278 */
279 u8 s5pwr = CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL;
280
281 /* First, disable further SMIs */
282 reg8 = inb(pmbase + SMI_EN);
283 reg8 &= ~SLP_SMI_EN;
284 outb(reg8, pmbase + SMI_EN);
285
286 /* Figure out SLP_TYP */
287 reg32 = inl(pmbase + PM1_CNT);
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000288 printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32);
Aaron Durbin671909b2016-07-13 23:24:36 -0500289 slp_typ = acpi_sleep_from_pm1(reg32);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000290
291 /* Next, do the deed.
292 */
293
294 switch (slp_typ) {
Aaron Durbin671909b2016-07-13 23:24:36 -0500295 case ACPI_S0: printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n"); break;
296 case ACPI_S1: printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n"); break;
297 case ACPI_S3:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000298 printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000299 /* Invalidate the cache before going to S3 */
300 wbinvd();
301 break;
Aaron Durbin671909b2016-07-13 23:24:36 -0500302 case ACPI_S4: printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n"); break;
303 case ACPI_S5:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000304 printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000305
306 outl(0, pmbase + GPE0_EN);
307
308 /* Should we keep the power state after a power loss?
309 * In case the setting is "ON" or "OFF" we don't have
310 * to do anything. But if it's "KEEP" we have to switch
311 * to "OFF" before entering S5.
312 */
313 if (s5pwr == MAINBOARD_POWER_KEEP) {
314 reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3);
315 reg8 |= 1;
316 pci_write_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3, reg8);
317 }
318
319 /* also iterates over all bridges on bus 0 */
320 busmaster_disable_on_bus(0);
321 break;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000322 default: printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n"); break;
Stefan Reinauer800379f2010-03-01 08:34:19 +0000323 }
324
325 /* Write back to the SLP register to cause the originally intended
326 * event again. We need to set BIT13 (SLP_EN) though to make the
327 * sleep happen.
328 */
329 outl(reg32 | SLP_EN, pmbase + PM1_CNT);
330
331 /* In most sleep states, the code flow of this function ends at
332 * the line above. However, if we entered sleep state S1 and wake
333 * up again, we will continue to execute code in this function.
334 */
335 reg32 = inl(pmbase + PM1_CNT);
336 if (reg32 & SCI_EN) {
337 /* The OS is not an ACPI OS, so we set the state to S0 */
338 reg32 &= ~(SLP_EN | SLP_TYP);
339 outl(reg32, pmbase + PM1_CNT);
340 }
341}
342
343static void southbridge_smi_apmc(unsigned int node, smm_state_save_area_t *state_save)
344{
345 u32 pmctrl;
346 u8 reg8;
347
348 /* Emulate B2 register as the FADT / Linux expects it */
349
350 reg8 = inb(APM_CNT);
351 switch (reg8) {
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200352 case APM_CNT_CST_CONTROL:
Stefan Reinauer800379f2010-03-01 08:34:19 +0000353 /* Calling this function seems to cause
354 * some kind of race condition in Linux
355 * and causes a kernel oops
356 */
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000357 printk(BIOS_DEBUG, "C-state control\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000358 break;
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200359 case APM_CNT_PST_CONTROL:
Stefan Reinauer800379f2010-03-01 08:34:19 +0000360 /* Calling this function seems to cause
361 * some kind of race condition in Linux
362 * and causes a kernel oops
363 */
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000364 printk(BIOS_DEBUG, "P-state control\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000365 break;
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200366 case APM_CNT_ACPI_DISABLE:
Stefan Reinauer800379f2010-03-01 08:34:19 +0000367 pmctrl = inl(pmbase + PM1_CNT);
368 pmctrl &= ~SCI_EN;
369 outl(pmctrl, pmbase + PM1_CNT);
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000370 printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000371 break;
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200372 case APM_CNT_ACPI_ENABLE:
Stefan Reinauer800379f2010-03-01 08:34:19 +0000373 pmctrl = inl(pmbase + PM1_CNT);
374 pmctrl |= SCI_EN;
375 outl(pmctrl, pmbase + PM1_CNT);
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000376 printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000377 break;
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200378 case APM_CNT_GNVS_UPDATE:
Stefan Reinauer800379f2010-03-01 08:34:19 +0000379 if (smm_initialized) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000380 printk(BIOS_DEBUG, "SMI#: SMM structures already initialized!\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000381 return;
382 }
383 gnvs = *(global_nvs_t **)0x500;
384 tcg = *(void **)0x504;
385 smi1 = *(void **)0x508;
386 smm_initialized = 1;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000387 printk(BIOS_DEBUG, "SMI#: Setting up structures to %p, %p, %p\n", gnvs, tcg, smi1);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000388 break;
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200389 case APM_CNT_MBI_UPDATE: // FIXME
Stefan Reinauer800379f2010-03-01 08:34:19 +0000390 if (mbi_initialized) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000391 printk(BIOS_DEBUG, "SMI#: mbi already registered!\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000392 return;
393 }
394 mbi = *(void **)0x500;
395 mbi_len = *(u32 *)0x504;
396 mbi_initialized = 1;
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000397 printk(BIOS_DEBUG, "SMI#: Registered MBI at %p (%d bytes)\n", mbi, mbi_len);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000398 break;
399
400 default:
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000401 printk(BIOS_DEBUG, "SMI#: Unknown function APM_CNT=%02x\n", reg8);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000402 }
403}
404
405static void southbridge_smi_pm1(unsigned int node, smm_state_save_area_t *state_save)
406{
407 u16 pm1_sts;
408
409 pm1_sts = reset_pm1_status();
410 dump_pm1_status(pm1_sts);
411
412 /* While OSPM is not active, poweroff immediately
413 * on a power button event.
414 */
415 if (pm1_sts & PWRBTN_STS) {
416 // power button pressed
417 u32 reg32;
418 reg32 = (7 << 10) | (1 << 13);
419 outl(reg32, pmbase + PM1_CNT);
420 }
421}
422
423static void southbridge_smi_gpe0(unsigned int node, smm_state_save_area_t *state_save)
424{
425 u32 gpe0_sts;
426
427 gpe0_sts = reset_gpe0_status();
428 dump_gpe0_status(gpe0_sts);
429}
430
Stefan Reinauer800379f2010-03-01 08:34:19 +0000431static void southbridge_smi_gpi(unsigned int node, smm_state_save_area_t *state_save)
432{
433 u16 reg16;
434 reg16 = inw(pmbase + ALT_GP_SMI_STS);
435 outl(reg16, pmbase + ALT_GP_SMI_STS);
436
437 reg16 &= inw(pmbase + ALT_GP_SMI_EN);
438
Kyösti Mälkki48b3dbc2014-12-29 19:36:50 +0200439 mainboard_smi_gpi(reg16);
440
441 if (reg16)
442 printk(BIOS_DEBUG, "GPI (mask %04x)\n",reg16);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000443}
444
445static void southbridge_smi_mc(unsigned int node, smm_state_save_area_t *state_save)
446{
447 u32 reg32;
448
449 reg32 = inl(pmbase + SMI_EN);
450
451 /* Are periodic SMIs enabled? */
452 if ((reg32 & MCSMI_EN) == 0)
453 return;
454
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000455 printk(BIOS_DEBUG, "Microcontroller SMI.\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000456}
457
458
459
460static void southbridge_smi_tco(unsigned int node, smm_state_save_area_t *state_save)
461{
462 u32 tco_sts;
463
464 tco_sts = reset_tco_status();
465
466 /* Any TCO event? */
467 if (!tco_sts)
468 return;
469
470 if (tco_sts & (1 << 8)) { // BIOSWR
471 u8 bios_cntl;
472
473 bios_cntl = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0xdc);
474
475 if (bios_cntl & 1) {
476 /* BWE is RW, so the SMI was caused by a
477 * write to BWE, not by a write to the BIOS
478 */
479
480 /* This is the place where we notice someone
481 * is trying to tinker with the BIOS. We are
482 * trying to be nice and just ignore it. A more
483 * resolute answer would be to power down the
484 * box.
485 */
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000486 printk(BIOS_DEBUG, "Switching back to RO\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000487 pci_write_config32(PCI_DEV(0, 0x1f, 0), 0xdc, (bios_cntl & ~1));
488 } /* No else for now? */
489 } else if (tco_sts & (1 << 3)) { /* TIMEOUT */
490 /* Handle TCO timeout */
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000491 printk(BIOS_DEBUG, "TCO Timeout.\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000492 } else if (!tco_sts) {
493 dump_tco_status(tco_sts);
494 }
495}
496
497static void southbridge_smi_periodic(unsigned int node, smm_state_save_area_t *state_save)
498{
499 u32 reg32;
500
501 reg32 = inl(pmbase + SMI_EN);
502
503 /* Are periodic SMIs enabled? */
504 if ((reg32 & PERIODIC_EN) == 0)
505 return;
506
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000507 printk(BIOS_DEBUG, "Periodic SMI.\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000508}
509
510static void southbridge_smi_monitor(unsigned int node, smm_state_save_area_t *state_save)
511{
512#define IOTRAP(x) (trap_sts & (1 << x))
513#if 0
514 u32 trap_sts, trap_cycle;
515 u32 data, mask = 0;
516 int i;
517
518 trap_sts = RCBA32(0x1e00); // TRSR - Trap Status Register
519 RCBA32(0x1e00) = trap_sts; // Clear trap(s) in TRSR
520
521 trap_cycle = RCBA32(0x1e10);
522 for (i=16; i<20; i++) {
523 if (trap_cycle & (1 << i))
524 mask |= (0xff << ((i - 16) << 2));
525 }
526
527
528 /* IOTRAP(3) SMI function call */
529 if (IOTRAP(3)) {
530 if (gnvs && gnvs->smif)
531 io_trap_handler(gnvs->smif); // call function smif
532 return;
533 }
534
535 /* IOTRAP(2) currently unused
536 * IOTRAP(1) currently unused */
537
538 /* IOTRAP(0) SMIC */
539 if (IOTRAP(0)) {
540 if (!(trap_cycle & (1 << 24))) { // It's a write
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000541 printk(BIOS_DEBUG, "SMI1 command\n");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000542 data = RCBA32(0x1e18);
543 data &= mask;
544 // if (smi1)
545 // southbridge_smi_command(data);
546 // return;
547 }
548 // Fall through to debug
549 }
550
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000551 printk(BIOS_DEBUG, " trapped io address = 0x%x\n", trap_cycle & 0xfffc);
Elyes HAOUAS70d79a42016-08-21 18:36:06 +0200552 for (i=0; i < 4; i++) if (IOTRAP(i)) printk(BIOS_DEBUG, " TRAP = %d\n", i);
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000553 printk(BIOS_DEBUG, " AHBE = %x\n", (trap_cycle >> 16) & 0xf);
554 printk(BIOS_DEBUG, " MASK = 0x%08x\n", mask);
555 printk(BIOS_DEBUG, " read/write: %s\n", (trap_cycle & (1 << 24)) ? "read" : "write");
Stefan Reinauer800379f2010-03-01 08:34:19 +0000556
557 if (!(trap_cycle & (1 << 24))) {
558 /* Write Cycle */
559 data = RCBA32(0x1e18);
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000560 printk(BIOS_DEBUG, " iotrap written data = 0x%08x\n", data);
Stefan Reinauer800379f2010-03-01 08:34:19 +0000561 }
562#endif
563#undef IOTRAP
564}
565
Stefan Reinauer348a1ba2010-03-17 01:51:11 +0000566typedef void (*smi_handler_t)(unsigned int node,
Stefan Reinauer800379f2010-03-01 08:34:19 +0000567 smm_state_save_area_t *state_save);
568
Stefan Reinauer348a1ba2010-03-17 01:51:11 +0000569smi_handler_t southbridge_smi[32] = {
Stefan Reinauer800379f2010-03-01 08:34:19 +0000570 NULL, // [0] reserved
571 NULL, // [1] reserved
572 NULL, // [2] BIOS_STS
573 NULL, // [3] LEGACY_USB_STS
574 southbridge_smi_sleep, // [4] SLP_SMI_STS
575 southbridge_smi_apmc, // [5] APM_STS
576 NULL, // [6] SWSMI_TMR_STS
577 NULL, // [7] reserved
578 southbridge_smi_pm1, // [8] PM1_STS
579 southbridge_smi_gpe0, // [9] GPE0_STS
580 southbridge_smi_gpi, // [10] GPI_STS
581 southbridge_smi_mc, // [11] MCSMI_STS
582 NULL, // [12] DEVMON_STS
583 southbridge_smi_tco, // [13] TCO_STS
584 southbridge_smi_periodic, // [14] PERIODIC_STS
585 NULL, // [15] SERIRQ_SMI_STS
586 NULL, // [16] SMBUS_SMI_STS
587 NULL, // [17] LEGACY_USB2_STS
588 NULL, // [18] INTEL_USB2_STS
589 NULL, // [19] reserved
590 NULL, // [20] PCI_EXP_SMI_STS
591 southbridge_smi_monitor, // [21] MONITOR_STS
592 NULL, // [22] reserved
593 NULL, // [23] reserved
594 NULL, // [24] reserved
595 NULL, // [25] EL_SMI_STS
596 NULL, // [26] SPI_STS
597 NULL, // [27] reserved
598 NULL, // [28] reserved
599 NULL, // [29] reserved
600 NULL, // [30] reserved
601 NULL // [31] reserved
602};
603
604/**
605 * @brief Interrupt handler for SMI#
Martin Roth182e551f2014-12-29 22:29:08 -0700606 * @param node
607 * @param state_save
Stefan Reinauer800379f2010-03-01 08:34:19 +0000608 */
Stefan Reinauer800379f2010-03-01 08:34:19 +0000609void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
610{
611 int i, dump = 0;
612 u32 smi_sts;
613
614 /* Update global variable pmbase */
615 pmbase = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0x40) & 0xfffc;
616
617 /* We need to clear the SMI status registers, or we won't see what's
618 * happening in the following calls.
619 */
620 smi_sts = reset_smi_status();
621
622 /* Filter all non-enabled SMI events */
623 // FIXME Double check, this clears MONITOR
624 // smi_sts &= inl(pmbase + SMI_EN);
625
626 /* Call SMI sub handler for each of the status bits */
627 for (i = 0; i < 31; i++) {
628 if (smi_sts & (1 << i)) {
629 if (southbridge_smi[i])
630 southbridge_smi[i](node, state_save);
631 else {
Martin Roth2ed0aa22016-01-05 20:58:58 -0700632 printk(BIOS_DEBUG, "SMI_STS[%d] occurred, but no "
Stefan Reinauer800379f2010-03-01 08:34:19 +0000633 "handler available.\n", i);
634 dump = 1;
635 }
636 }
637 }
638
Elyes HAOUASba28e8d2016-08-31 19:22:16 +0200639 if (dump) {
Stefan Reinauer800379f2010-03-01 08:34:19 +0000640 dump_smi_status(smi_sts);
641 }
642
643}