blob: fff3656498ed6cc0711c153216347fa759ba9557 [file] [log] [blame]
Stefan Reinauercadc5452010-12-18 23:29:37 +00001/*
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.
Stefan Reinauercadc5452010-12-18 23:29:37 +000016 */
17
18#include <types.h>
19#include <arch/io.h>
Stefan Reinauercadc5452010-12-18 23:29:37 +000020#include <console/console.h>
21#include <cpu/x86/cache.h>
22#include <cpu/x86/smm.h>
23#include <device/pci_def.h>
24#include "vt8237r.h"
25
Stefan Reinauercadc5452010-12-18 23:29:37 +000026#include "nvs.h"
27
28/* While we read PMBASE dynamically in case it changed, let's
29 * initialize it with a sane value
30 */
31u16 pmbase = DEFAULT_PMBASE;
32u8 smm_initialized = 0;
33
34/* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located
35 * by coreboot.
36 */
37global_nvs_t *gnvs = (global_nvs_t *)0x0;
38void *tcg = (void *)0x0;
39void *smi1 = (void *)0x0;
40
41#if 0
42/**
43 * @brief read and clear PM1_STS
44 * @return PM1_STS register
45 */
46static u16 reset_pm1_status(void)
47{
48 u16 reg16;
49
50 reg16 = inw(pmbase + PM1_STS);
51 /* set status bits are cleared by writing 1 to them */
52 outw(reg16, pmbase + PM1_STS);
53
54 return reg16;
55}
56
57static void dump_pm1_status(u16 pm1_sts)
58{
59 printk(BIOS_SPEW, "PM1_STS: ");
60 if (pm1_sts & (1 << 15)) printk(BIOS_SPEW, "WAK ");
61 if (pm1_sts & (1 << 14)) printk(BIOS_SPEW, "PCIEXPWAK ");
62 if (pm1_sts & (1 << 11)) printk(BIOS_SPEW, "PRBTNOR ");
63 if (pm1_sts & (1 << 10)) printk(BIOS_SPEW, "RTC ");
64 if (pm1_sts & (1 << 8)) printk(BIOS_SPEW, "PWRBTN ");
65 if (pm1_sts & (1 << 5)) printk(BIOS_SPEW, "GBL ");
66 if (pm1_sts & (1 << 4)) printk(BIOS_SPEW, "BM ");
67 if (pm1_sts & (1 << 0)) printk(BIOS_SPEW, "TMROF ");
68 printk(BIOS_SPEW, "\n");
69 int reg16 = inw(pmbase + PM1_EN);
70 printk(BIOS_SPEW, "PM1_EN: %x\n", reg16);
71}
72#endif
73
74/**
75 * @brief read and clear SMI_STS
76 * @return SMI_STS register
77 */
78static u16 reset_smi_status(void)
79{
80 u16 reg16;
81
82 reg16 = inw(pmbase + SMI_STS);
83 /* set status bits are cleared by writing 1 to them */
84 outw(reg16, pmbase + SMI_STS);
85
86 return reg16;
87}
88
89static void dump_smi_status(u16 smi_sts)
90{
91 printk(BIOS_DEBUG, "SMI_STS: ");
92 if (smi_sts & (1 << 15)) printk(BIOS_DEBUG, "GPIO_RANGE_1 ");
93 if (smi_sts & (1 << 14)) printk(BIOS_DEBUG, "GPIO_RANGE_0 ");
94 if (smi_sts & (1 << 13)) printk(BIOS_DEBUG, "GP3_TIMEOUT ");
95 if (smi_sts & (1 << 12)) printk(BIOS_DEBUG, "GP2_TIMEOUT ");
96 if (smi_sts & (1 << 11)) printk(BIOS_DEBUG, "SERR_IRQ ");
97 if (smi_sts & (1 << 10)) printk(BIOS_DEBUG, "PMIO_5 ");
98 if (smi_sts & (1 << 9)) printk(BIOS_DEBUG, "THRMTRIP# ");
99 if (smi_sts & (1 << 8)) printk(BIOS_DEBUG, "CLKRUN# ");
100 if (smi_sts & (1 << 7)) printk(BIOS_DEBUG, "PRIMARY_IRQ/NMI/SMI ");
101 if (smi_sts & (1 << 6)) printk(BIOS_DEBUG, "SWSMI ");
102 if (smi_sts & (1 << 5)) printk(BIOS_DEBUG, "BIOS_STATUS ");
103 if (smi_sts & (1 << 4)) printk(BIOS_DEBUG, "LEGACY_USB ");
104 if (smi_sts & (1 << 3)) printk(BIOS_DEBUG, "GP1_TIMEOUT ");
105 if (smi_sts & (1 << 2)) printk(BIOS_DEBUG, "GP0_TIMEOUT ");
106 if (smi_sts & (1 << 1)) printk(BIOS_DEBUG, "SECONDARY_EVENT_TIMEOUT ");
107 if (smi_sts & (1 << 0)) printk(BIOS_DEBUG, "PRIMARY_ACTIVITY ");
108 printk(BIOS_DEBUG, "\n");
109}
110
111int southbridge_io_trap_handler(int smif)
112{
113 switch (smif) {
114 case 0x32:
115 printk(BIOS_DEBUG, "OS Init\n");
116 /* gnvs->smif:
117 * On success, the IO Trap Handler returns 0
118 * On failure, the IO Trap Handler returns a value != 0
119 */
120 gnvs->smif = 0;
121 return 1; /* IO trap handled */
122 }
123
124 /* Not handled */
125 return 0;
126}
127
128/**
129 * @brief Set the EOS bit
130 */
131void southbridge_smi_set_eos(void)
132{
133 u8 reg8;
134
135 reg8 = inb(pmbase + SMI_EN);
136 reg8 |= EOS;
137 outb(reg8, pmbase + SMI_EN);
138}
139
140static void southbridge_smi_cmd(unsigned int node, smm_state_save_area_t *state_save)
141{
142 u16 pmctrl;
143 u8 reg8;
144
145 reg8 = inb(pmbase + 0x2f);
146 switch (reg8) {
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200147 case APM_CNT_CST_CONTROL:
Stefan Reinauercadc5452010-12-18 23:29:37 +0000148 /* Calling this function seems to cause
149 * some kind of race condition in Linux
150 * and causes a kernel oops
151 */
152 printk(BIOS_DEBUG, "C-state control\n");
153 break;
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200154 case APM_CNT_PST_CONTROL:
Stefan Reinauercadc5452010-12-18 23:29:37 +0000155 /* Calling this function seems to cause
156 * some kind of race condition in Linux
157 * and causes a kernel oops
158 */
159 printk(BIOS_DEBUG, "P-state control\n");
160 break;
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200161 case APM_CNT_ACPI_DISABLE:
Stefan Reinauercadc5452010-12-18 23:29:37 +0000162 pmctrl = inw(pmbase + PM1_CNT);
163 pmctrl &= ~SCI_EN;
164 outw(pmctrl, pmbase + PM1_CNT);
165 printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
166 break;
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200167 case APM_CNT_ACPI_ENABLE:
Stefan Reinauercadc5452010-12-18 23:29:37 +0000168 pmctrl = inw(pmbase + PM1_CNT);
169 pmctrl |= SCI_EN;
170 outw(pmctrl, pmbase + PM1_CNT);
171 printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
172 break;
Sven Schnellef4dc1a72011-06-05 11:33:41 +0200173 case APM_CNT_GNVS_UPDATE:
Stefan Reinauercadc5452010-12-18 23:29:37 +0000174 if (smm_initialized) {
175 printk(BIOS_DEBUG, "SMI#: SMM structures already initialized!\n");
176 return;
177 }
178 gnvs = *(global_nvs_t **)0x500;
179 tcg = *(void **)0x504;
180 smi1 = *(void **)0x508;
181 smm_initialized = 1;
182 printk(BIOS_DEBUG, "SMI#: Setting up structures to %p, %p, %p\n", gnvs, tcg, smi1);
183 break;
184 default:
185 printk(BIOS_DEBUG, "SMI#: Unknown function SMI_CMD=%02x\n", reg8);
186 }
187}
188
189typedef void (*smi_handler_t)(unsigned int node,
190 smm_state_save_area_t *state_save);
191
192smi_handler_t southbridge_smi[32] = {
193 NULL, // [0]
194 NULL, // [1]
195 NULL, // [2]
196 NULL, // [3]
Rudolf Marek2c366272010-12-18 23:30:59 +0000197 NULL, // [4]
Stefan Reinauercadc5452010-12-18 23:29:37 +0000198 NULL, // [5]
Rudolf Marek2c366272010-12-18 23:30:59 +0000199 southbridge_smi_cmd, // [6]
Stefan Reinauercadc5452010-12-18 23:29:37 +0000200 NULL, // [7]
201 NULL, // [8]
202 NULL, // [9]
203 NULL, // [10]
204 NULL, // [11]
205 NULL, // [12]
206 NULL, // [13]
207 NULL, // [14]
208 NULL, // [15]
209};
210
211/**
212 * @brief Interrupt handler for SMI#
213 *
Martin Roth182e551f2014-12-29 22:29:08 -0700214 * @param node
215 * @param state_save revision of the smm state save map
Stefan Reinauercadc5452010-12-18 23:29:37 +0000216 */
217
218void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
219{
220 int i, dump = 0;
221 u32 smi_sts;
222
223 /* Update global variable pmbase */
224 pmbase = pci_read_config16(PCI_DEV(0, 0x11, 0), 0x88) & 0xfffc;
225
226 /* We need to clear the SMI status registers, or we won't see what's
227 * happening in the following calls.
228 */
229 smi_sts = reset_smi_status();
230
231 /* Filter all non-enabled SMI events */
232 // FIXME Double check, this clears MONITOR
233 // smi_sts &= inl(pmbase + SMI_EN);
234
235 /* Call SMI sub handler for each of the status bits */
236 for (i = 0; i < 16; i++) {
237 if (smi_sts & (1 << i)) {
238 if (southbridge_smi[i])
239 southbridge_smi[i](node, state_save);
240 else {
Martin Roth84422b12014-12-09 13:49:05 -0700241 printk(BIOS_DEBUG, "SMI_STS[%d] occurred, but no "
Stefan Reinauercadc5452010-12-18 23:29:37 +0000242 "handler available.\n", i);
243 dump = 1;
244 }
245 }
246 }
247
248 if(dump) {
249 dump_smi_status(smi_sts);
250 }
251
252}