9befbf9ac18b3497fd3d3c5a3ada174f663bb881
[coreboot.git] / src / southbridge / intel / i82801gx / smihandler.c
1 /*
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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19  * MA 02110-1301 USA
20  */
21
22 #include <types.h>
23 #include <arch/io.h>
24 #include <arch/romcc_io.h>
25 #include <console/console.h>
26 #include <cpu/x86/cache.h>
27 #include <cpu/x86/smm.h>
28 #include <device/pci_def.h>
29 #include "i82801gx.h"
30
31 /* I945 */
32 #define SMRAM           0x9d
33 #define   D_OPEN        (1 << 6)
34 #define   D_CLS         (1 << 5)
35 #define   D_LCK         (1 << 4)
36 #define   G_SMRANE      (1 << 3)
37 #define   C_BASE_SEG    ((0 << 2) | (1 << 1) | (0 << 0))
38
39 #include "nvs.h"
40
41 /* While we read PMBASE dynamically in case it changed, let's
42  * initialize it with a sane value
43  */
44 u16 pmbase = DEFAULT_PMBASE;
45 u8 smm_initialized = 0;
46
47 /* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located
48  * by coreboot.
49  */
50 global_nvs_t *gnvs = (global_nvs_t *)0x0;
51 void *tcg = (void *)0x0;
52 void *smi1 = (void *)0x0;
53
54 /**
55  * @brief read and clear PM1_STS
56  * @return PM1_STS register
57  */
58 static 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
69 static void dump_pm1_status(u16 pm1_sts)
70 {
71         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");
81         int reg16 = inw(pmbase + PM1_EN);
82         printk(BIOS_SPEW, "PM1_EN: %x\n", reg16);
83 }
84
85 /**
86  * @brief read and clear SMI_STS
87  * @return SMI_STS register
88  */
89 static 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
100 static void dump_smi_status(u32 smi_sts)
101 {
102         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");
124 }
125
126
127 /**
128  * @brief read and clear GPE0_STS
129  * @return GPE0_STS register
130  */
131 static 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
142 static void dump_gpe0_status(u32 gpe0_sts)
143 {
144         int i;
145         printk(BIOS_DEBUG, "GPE0_STS: ");
146         for (i=31; i<= 16; i--) {
147                 if (gpe0_sts & (1 << i)) printk(BIOS_DEBUG, "GPIO%d ", (i-16));
148         }
149         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");
164 }
165
166
167 /**
168  * @brief read and clear TCOx_STS
169  * @return TCOx_STS registers
170  */
171 static 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
186 static void dump_tco_status(u32 tco_sts)
187 {
188         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");
203 }
204
205 /* We are using PCIe accesses for now
206  *  1. the chipset can do it
207  *  2. we don't need to worry about how we leave 0xcf8/0xcfc behind
208  */
209 #include "../../../northbridge/intel/i945/pcie_config.c"
210
211 int southbridge_io_trap_handler(int smif)
212 {
213         switch (smif) {
214         case 0x32:
215                 printk(BIOS_DEBUG, "OS Init\n");
216                 /* gnvs->smif:
217                  *  On success, the IO Trap Handler returns 0
218                  *  On failure, the IO Trap Handler returns a value != 0
219                  */
220                 gnvs->smif = 0;
221                 return 1; /* IO trap handled */
222         }
223
224         /* Not handled */
225         return 0;
226 }
227
228 /**
229  * @brief Set the EOS bit
230  */
231 void southbridge_smi_set_eos(void)
232 {
233         u8 reg8;
234
235         reg8 = inb(pmbase + SMI_EN);
236         reg8 |= EOS;
237         outb(reg8, pmbase + SMI_EN);
238 }
239
240 static void busmaster_disable_on_bus(int bus)
241 {
242         int slot, func;
243         unsigned int val;
244         unsigned char hdr;
245
246         for (slot = 0; slot < 0x20; slot++) {
247                 for (func = 0; func < 8; func++) {
248                         u32 reg32;
249                         device_t dev = PCI_DEV(bus, slot, func);
250
251                         val = pci_read_config32(dev, PCI_VENDOR_ID);
252
253                         if (val == 0xffffffff || val == 0x00000000 ||
254                             val == 0x0000ffff || val == 0xffff0000)
255                                 continue;
256
257                         /* Disable Bus Mastering for this one device */
258                         reg32 = pci_read_config32(dev, PCI_COMMAND);
259                         reg32 &= ~PCI_COMMAND_MASTER;
260                         pci_write_config32(dev, PCI_COMMAND, reg32);
261
262                         /* If this is a bridge, then follow it. */
263                         hdr = pci_read_config8(dev, PCI_HEADER_TYPE);
264                         hdr &= 0x7f;
265                         if (hdr == PCI_HEADER_TYPE_BRIDGE ||
266                             hdr == PCI_HEADER_TYPE_CARDBUS) {
267                                 unsigned int buses;
268                                 buses = pci_read_config32(dev, PCI_PRIMARY_BUS);
269                                 busmaster_disable_on_bus((buses >> 8) & 0xff);
270                         }
271                 }
272         }
273 }
274
275
276 static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *state_save)
277 {
278         u8 reg8;
279         u32 reg32;
280         u8 slp_typ;
281         /* FIXME: the power state on boot should be read from
282          * CMOS or even better from GNVS. Right now it's hard
283          * coded at compile time.
284          */
285         u8 s5pwr = CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL;
286
287         /* First, disable further SMIs */
288         reg8 = inb(pmbase + SMI_EN);
289         reg8 &= ~SLP_SMI_EN;
290         outb(reg8, pmbase + SMI_EN);
291
292         /* Figure out SLP_TYP */
293         reg32 = inl(pmbase + PM1_CNT);
294         printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32);
295         slp_typ = (reg32 >> 10) & 7;
296
297         /* Next, do the deed.
298          */
299
300         switch (slp_typ) {
301         case 0: printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n"); break;
302         case 1: printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n"); break;
303         case 5:
304                 printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
305                 /* Invalidate the cache before going to S3 */
306                 wbinvd();
307                 break;
308         case 6: printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n"); break;
309         case 7:
310                 printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
311
312                 outl(0, pmbase + GPE0_EN);
313
314                 /* Should we keep the power state after a power loss?
315                  * In case the setting is "ON" or "OFF" we don't have
316                  * to do anything. But if it's "KEEP" we have to switch
317                  * to "OFF" before entering S5.
318                  */
319                 if (s5pwr == MAINBOARD_POWER_KEEP) {
320                         reg8 = pcie_read_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3);
321                         reg8 |= 1;
322                         pcie_write_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3, reg8);
323                 }
324
325                 /* also iterates over all bridges on bus 0 */
326                 busmaster_disable_on_bus(0);
327                 break;
328         default: printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n"); break;
329         }
330
331         /* Write back to the SLP register to cause the originally intended
332          * event again. We need to set BIT13 (SLP_EN) though to make the
333          * sleep happen.
334          */
335         outl(reg32 | SLP_EN, pmbase + PM1_CNT);
336
337         /* In most sleep states, the code flow of this function ends at
338          * the line above. However, if we entered sleep state S1 and wake
339          * up again, we will continue to execute code in this function.
340          */
341         reg32 = inl(pmbase + PM1_CNT);
342         if (reg32 & SCI_EN) {
343                 /* The OS is not an ACPI OS, so we set the state to S0 */
344                 reg32 &= ~(SLP_EN | SLP_TYP);
345                 outl(reg32, pmbase + PM1_CNT);
346         }
347 }
348
349 static void southbridge_smi_apmc(unsigned int node, smm_state_save_area_t *state_save)
350 {
351         u32 pmctrl;
352         u8 reg8;
353
354         /* Emulate B2 register as the FADT / Linux expects it */
355
356         reg8 = inb(APM_CNT);
357         if (mainboard_apm_cnt && mainboard_apm_cnt(reg8))
358                 return;
359
360         switch (reg8) {
361         case APM_CNT_CST_CONTROL:
362                 /* Calling this function seems to cause
363                  * some kind of race condition in Linux
364                  * and causes a kernel oops
365                  */
366                 printk(BIOS_DEBUG, "C-state control\n");
367                 break;
368         case APM_CNT_PST_CONTROL:
369                 /* Calling this function seems to cause
370                  * some kind of race condition in Linux
371                  * and causes a kernel oops
372                  */
373                 printk(BIOS_DEBUG, "P-state control\n");
374                 break;
375         case APM_CNT_ACPI_DISABLE:
376                 pmctrl = inl(pmbase + PM1_CNT);
377                 pmctrl &= ~SCI_EN;
378                 outl(pmctrl, pmbase + PM1_CNT);
379                 printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
380                 break;
381         case APM_CNT_ACPI_ENABLE:
382                 pmctrl = inl(pmbase + PM1_CNT);
383                 pmctrl |= SCI_EN;
384                 outl(pmctrl, pmbase + PM1_CNT);
385                 printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
386                 break;
387         case APM_CNT_GNVS_UPDATE:
388                 if (smm_initialized) {
389                         printk(BIOS_DEBUG, "SMI#: SMM structures already initialized!\n");
390                         return;
391                 }
392                 gnvs = *(global_nvs_t **)0x500;
393                 tcg  = *(void **)0x504;
394                 smi1 = *(void **)0x508;
395                 smm_initialized = 1;
396                 printk(BIOS_DEBUG, "SMI#: Setting up structures to %p, %p, %p\n", gnvs, tcg, smi1);
397                 break;
398         default:
399                 printk(BIOS_DEBUG, "SMI#: Unknown function APM_CNT=%02x\n", reg8);
400         }
401 }
402
403 static void southbridge_smi_pm1(unsigned int node, smm_state_save_area_t *state_save)
404 {
405         u16 pm1_sts;
406
407         pm1_sts = reset_pm1_status();
408         dump_pm1_status(pm1_sts);
409
410         /* While OSPM is not active, poweroff immediately
411          * on a power button event.
412          */
413         if (pm1_sts & PWRBTN_STS) {
414                 // power button pressed
415                 u32 reg32;
416                 reg32 = (7 << 10) | (1 << 13);
417                 outl(reg32, pmbase + PM1_CNT);
418         }
419 }
420
421 static void southbridge_smi_gpe0(unsigned int node, smm_state_save_area_t *state_save)
422 {
423         u32 gpe0_sts;
424
425         gpe0_sts = reset_gpe0_status();
426         dump_gpe0_status(gpe0_sts);
427 }
428
429 static void southbridge_smi_gpi(unsigned int node, smm_state_save_area_t *state_save)
430 {
431         u16 reg16;
432         reg16 = inw(pmbase + ALT_GP_SMI_STS);
433         outl(reg16, pmbase + ALT_GP_SMI_STS);
434
435         reg16 &= inw(pmbase + ALT_GP_SMI_EN);
436
437         if (mainboard_smi_gpi) {
438                 mainboard_smi_gpi(reg16);
439         } else {
440                 if (reg16)
441                         printk(BIOS_DEBUG, "GPI (mask %04x)\n",reg16);
442         }
443 }
444
445 static 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
455         printk(BIOS_DEBUG, "Microcontroller SMI.\n");
456 }
457
458
459
460 static 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 = pcie_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                          */
486                         printk(BIOS_DEBUG, "Switching back to RO\n");
487                         pcie_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 */
491                 printk(BIOS_DEBUG, "TCO Timeout.\n");
492         } else if (!tco_sts) {
493                 dump_tco_status(tco_sts);
494         }
495 }
496
497 static 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
507         printk(BIOS_DEBUG, "Periodic SMI.\n");
508 }
509
510 static void southbridge_smi_monitor(unsigned int node, smm_state_save_area_t *state_save)
511 {
512 #define IOTRAP(x) (trap_sts & (1 << x))
513         u32 trap_sts, trap_cycle;
514         u32 data, mask = 0;
515         int i;
516
517         trap_sts = RCBA32(0x1e00); // TRSR - Trap Status Register
518         RCBA32(0x1e00) = trap_sts; // Clear trap(s) in TRSR
519
520         trap_cycle = RCBA32(0x1e10);
521         for (i=16; i<20; i++) {
522                 if (trap_cycle & (1 << i))
523                         mask |= (0xff << ((i - 16) << 2));
524         }
525
526
527         /* IOTRAP(3) SMI function call */
528         if (IOTRAP(3)) {
529                 if (gnvs && gnvs->smif)
530                         io_trap_handler(gnvs->smif); // call function smif
531                 return;
532         }
533
534         /* IOTRAP(2) currently unused
535          * IOTRAP(1) currently unused */
536
537         /* IOTRAP(0) SMIC */
538         if (IOTRAP(0)) {
539                 if (!(trap_cycle & (1 << 24))) { // It's a write
540                         printk(BIOS_DEBUG, "SMI1 command\n");
541                         data = RCBA32(0x1e18);
542                         data &= mask;
543                         // if (smi1)
544                         //      southbridge_smi_command(data);
545                         // return;
546                 }
547                 // Fall through to debug
548         }
549
550         printk(BIOS_DEBUG, "  trapped io address = 0x%x\n", trap_cycle & 0xfffc);
551         for (i=0; i < 4; i++) if(IOTRAP(i)) printk(BIOS_DEBUG, "  TRAPĀ = %d\n", i);
552         printk(BIOS_DEBUG, "  AHBE = %x\n", (trap_cycle >> 16) & 0xf);
553         printk(BIOS_DEBUG, "  MASK = 0x%08x\n", mask);
554         printk(BIOS_DEBUG, "  read/write: %s\n", (trap_cycle & (1 << 24)) ? "read" : "write");
555
556         if (!(trap_cycle & (1 << 24))) {
557                 /* Write Cycle */
558                 data = RCBA32(0x1e18);
559                 printk(BIOS_DEBUG, "  iotrap written data = 0x%08x\n", data);
560         }
561 #undef IOTRAP
562 }
563
564 typedef void (*smi_handler_t)(unsigned int node,
565                 smm_state_save_area_t *state_save);
566
567 smi_handler_t southbridge_smi[32] = {
568         NULL,                     //  [0] reserved
569         NULL,                     //  [1] reserved
570         NULL,                     //  [2] BIOS_STS
571         NULL,                     //  [3] LEGACY_USB_STS
572         southbridge_smi_sleep,    //  [4] SLP_SMI_STS
573         southbridge_smi_apmc,     //  [5] APM_STS
574         NULL,                     //  [6] SWSMI_TMR_STS
575         NULL,                     //  [7] reserved
576         southbridge_smi_pm1,      //  [8] PM1_STS
577         southbridge_smi_gpe0,     //  [9] GPE0_STS
578         southbridge_smi_gpi,      // [10] GPI_STS
579         southbridge_smi_mc,       // [11] MCSMI_STS
580         NULL,                     // [12] DEVMON_STS
581         southbridge_smi_tco,      // [13] TCO_STS
582         southbridge_smi_periodic, // [14] PERIODIC_STS
583         NULL,                     // [15] SERIRQ_SMI_STS
584         NULL,                     // [16] SMBUS_SMI_STS
585         NULL,                     // [17] LEGACY_USB2_STS
586         NULL,                     // [18] INTEL_USB2_STS
587         NULL,                     // [19] reserved
588         NULL,                     // [20] PCI_EXP_SMI_STS
589         southbridge_smi_monitor,  // [21] MONITOR_STS
590         NULL,                     // [22] reserved
591         NULL,                     // [23] reserved
592         NULL,                     // [24] reserved
593         NULL,                     // [25] EL_SMI_STS
594         NULL,                     // [26] SPI_STS
595         NULL,                     // [27] reserved
596         NULL,                     // [28] reserved
597         NULL,                     // [29] reserved
598         NULL,                     // [30] reserved
599         NULL                      // [31] reserved
600 };
601
602 /**
603  * @brief Interrupt handler for SMI#
604  *
605  * @param smm_revision revision of the smm state save map
606  */
607
608 void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
609 {
610         int i, dump = 0;
611         u32 smi_sts;
612
613         /* Update global variable pmbase */
614         pmbase = pcie_read_config16(PCI_DEV(0, 0x1f, 0), 0x40) & 0xfffc;
615
616         /* We need to clear the SMI status registers, or we won't see what's
617          * happening in the following calls.
618          */
619         smi_sts = reset_smi_status();
620
621         /* Filter all non-enabled SMI events */
622         // FIXME Double check, this clears MONITOR
623         // smi_sts &= inl(pmbase + SMI_EN);
624
625         /* Call SMI sub handler for each of the status bits */
626         for (i = 0; i < 31; i++) {
627                 if (smi_sts & (1 << i)) {
628                         if (southbridge_smi[i])
629                                 southbridge_smi[i](node, state_save);
630                         else {
631                                 printk(BIOS_DEBUG, "SMI_STS[%d] occured, but no "
632                                                 "handler available.\n", i);
633                                 dump = 1;
634                         }
635                 }
636         }
637
638         if(dump) {
639                 dump_smi_status(smi_sts);
640         }
641
642 }