SMM for AMD K8 Part 1/2
[coreboot.git] / src / southbridge / via / vt8237r / smihandler.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2008-2009 coresystems GmbH
5  * Copyright (C) 2010 Rudolf Marek <r.marek@assembler.cz>
6  *
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
10  * the License.
11  *
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.
16  *
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,
20  * MA 02110-1301 USA
21  */
22
23 #include <types.h>
24 #include <arch/io.h>
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>
30 #include "vt8237r.h"
31
32 #define APM_CNT         0xb2
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
38 #define APM_STS         0xb3
39
40 #include "nvs.h"
41
42 /* While we read PMBASE dynamically in case it changed, let's
43  * initialize it with a sane value
44  */
45 u16 pmbase = DEFAULT_PMBASE;
46 u8 smm_initialized = 0;
47
48 /* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located
49  * by coreboot.
50  */
51 global_nvs_t *gnvs = (global_nvs_t *)0x0;
52 void *tcg = (void *)0x0;
53 void *smi1 = (void *)0x0;
54
55 #if 0
56 /**
57  * @brief read and clear PM1_STS
58  * @return PM1_STS register
59  */
60 static u16 reset_pm1_status(void)
61 {
62         u16 reg16;
63
64         reg16 = inw(pmbase + PM1_STS);
65         /* set status bits are cleared by writing 1 to them */
66         outw(reg16, pmbase + PM1_STS);
67
68         return reg16;
69 }
70
71 static void dump_pm1_status(u16 pm1_sts)
72 {
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);
85 }
86 #endif
87
88 /**
89  * @brief read and clear SMI_STS
90  * @return SMI_STS register
91  */
92 static u16 reset_smi_status(void)
93 {
94         u16 reg16;
95
96         reg16 = inw(pmbase + SMI_STS);
97         /* set status bits are cleared by writing 1 to them */
98         outw(reg16, pmbase + SMI_STS);
99
100         return reg16;
101 }
102
103 static void dump_smi_status(u16 smi_sts)
104 {
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");
123 }
124
125 int southbridge_io_trap_handler(int smif)
126 {
127         switch (smif) {
128         case 0x32:
129                 printk(BIOS_DEBUG, "OS Init\n");
130                 /* gnvs->smif:
131                  *  On success, the IO Trap Handler returns 0
132                  *  On failure, the IO Trap Handler returns a value != 0
133                  */
134                 gnvs->smif = 0;
135                 return 1; /* IO trap handled */
136         }
137
138         /* Not handled */
139         return 0;
140 }
141
142 /**
143  * @brief Set the EOS bit
144  */
145 void southbridge_smi_set_eos(void)
146 {
147         u8 reg8;
148
149         reg8 = inb(pmbase + SMI_EN);
150         reg8 |= EOS;
151         outb(reg8, pmbase + SMI_EN);
152 }
153
154 static void southbridge_smi_cmd(unsigned int node, smm_state_save_area_t *state_save)
155 {
156         u16 pmctrl;
157         u8 reg8;
158
159         reg8 = inb(pmbase + 0x2f);
160         switch (reg8) {
161         case CST_CONTROL:
162                 /* Calling this function seems to cause
163                  * some kind of race condition in Linux
164                  * and causes a kernel oops
165                  */
166                 printk(BIOS_DEBUG, "C-state control\n");
167                 break;
168         case PST_CONTROL:
169                 /* Calling this function seems to cause
170                  * some kind of race condition in Linux
171                  * and causes a kernel oops
172                  */
173                 printk(BIOS_DEBUG, "P-state control\n");
174                 break;
175         case ACPI_DISABLE:
176                 pmctrl = inw(pmbase + PM1_CNT);
177                 pmctrl &= ~SCI_EN;
178                 outw(pmctrl, pmbase + PM1_CNT);
179                 printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
180                 break;
181         case ACPI_ENABLE:
182                 pmctrl = inw(pmbase + PM1_CNT);
183                 pmctrl |= SCI_EN;
184                 outw(pmctrl, pmbase + PM1_CNT);
185                 printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
186                 break;
187         case GNVS_UPDATE:
188                 if (smm_initialized) {
189                         printk(BIOS_DEBUG, "SMI#: SMM structures already initialized!\n");
190                         return;
191                 }
192                 gnvs = *(global_nvs_t **)0x500;
193                 tcg  = *(void **)0x504;
194                 smi1 = *(void **)0x508;
195                 smm_initialized = 1;
196                 printk(BIOS_DEBUG, "SMI#: Setting up structures to %p, %p, %p\n", gnvs, tcg, smi1);
197                 break;
198         default:
199                 printk(BIOS_DEBUG, "SMI#: Unknown function SMI_CMD=%02x\n", reg8);
200         }
201 }
202
203 typedef void (*smi_handler_t)(unsigned int node,
204                 smm_state_save_area_t *state_save);
205
206 smi_handler_t southbridge_smi[32] = {
207         NULL,                     //  [0]
208         NULL,                     //  [1]
209         NULL,                     //  [2]
210         NULL,                     //  [3]
211         southbridge_smi_cmd,      //  [4]
212         NULL,                     //  [5]
213         NULL,                     //  [6]
214         NULL,                     //  [7]
215         NULL,                     //  [8]
216         NULL,                     //  [9]
217         NULL,                     // [10]
218         NULL,                     // [11]
219         NULL,                     // [12]
220         NULL,                     // [13]
221         NULL,                     // [14]
222         NULL,                     // [15]
223 };
224
225 /**
226  * @brief Interrupt handler for SMI#
227  *
228  * @param smm_revision revision of the smm state save map
229  */
230
231 void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
232 {
233         int i, dump = 0;
234         u32 smi_sts;
235
236         /* Update global variable pmbase */
237         pmbase = pci_read_config16(PCI_DEV(0, 0x11, 0), 0x88) & 0xfffc;
238
239         /* We need to clear the SMI status registers, or we won't see what's
240          * happening in the following calls.
241          */
242         smi_sts = reset_smi_status();
243
244         /* Filter all non-enabled SMI events */
245         // FIXME Double check, this clears MONITOR
246         // smi_sts &= inl(pmbase + SMI_EN);
247
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);
253                         else {
254                                 printk(BIOS_DEBUG, "SMI_STS[%d] occured, but no "
255                                                 "handler available.\n", i);
256                                 dump = 1;
257                         }
258                 }
259         }
260
261         if(dump) {
262                 dump_smi_status(smi_sts);
263         }
264
265 }