blob: 0f5c7c98c3fb6edd88e324bc61713796b64569a5 [file] [log] [blame]
Lee Leahy77ff0b12015-05-05 15:07:29 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2013 Google Inc.
Lee Leahy32471722015-04-20 15:20:28 -07005 * Copyright (C) 2015 Intel Corp.
Lee Leahy77ff0b12015-05-05 15:07:29 -07006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of 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.
Lee Leahy77ff0b12015-05-05 15:07:29 -070015 */
16
Lee Leahy32471722015-04-20 15:20:28 -070017#include <arch/hlt.h>
Lee Leahy77ff0b12015-05-05 15:07:29 -070018#include <arch/io.h>
19#include <console/console.h>
20#include <cpu/x86/cache.h>
21#include <cpu/x86/smm.h>
22#include <device/pci_def.h>
23#include <elog.h>
Lee Leahy77ff0b12015-05-05 15:07:29 -070024#include <soc/nvs.h>
Lee Leahy32471722015-04-20 15:20:28 -070025#include <soc/pci_devs.h>
26#include <soc/pm.h>
27#include <spi-generic.h>
28#include <stdint.h>
29#include <stdlib.h>
30#include <soc/gpio.h>
Lee Leahy77ff0b12015-05-05 15:07:29 -070031
32/* GNVS needs to be set by coreboot initiating a software SMI. */
33static global_nvs_t *gnvs;
34static int smm_initialized;
35
36int southbridge_io_trap_handler(int smif)
37{
38 switch (smif) {
39 case 0x32:
40 printk(BIOS_DEBUG, "OS Init\n");
Lee Leahy32471722015-04-20 15:20:28 -070041 /*
42 * gnvs->smif:
Lee Leahy77ff0b12015-05-05 15:07:29 -070043 * On success, the IO Trap Handler returns 0
44 * On failure, the IO Trap Handler returns a value != 0
45 */
46 gnvs->smif = 0;
47 return 1; /* IO trap handled */
48 }
49
50 /* Not handled */
51 return 0;
52}
53
54void southbridge_smi_set_eos(void)
55{
56 enable_smi(EOS);
57}
58
59global_nvs_t *smm_get_gnvs(void)
60{
61 return gnvs;
62}
63
64static void busmaster_disable_on_bus(int bus)
65{
66 int slot, func;
67 unsigned int val;
68 unsigned char hdr;
69
70 for (slot = 0; slot < 0x20; slot++) {
71 for (func = 0; func < 8; func++) {
72 u32 reg32;
73 device_t dev = PCI_DEV(bus, slot, func);
74
75 val = pci_read_config32(dev, PCI_VENDOR_ID);
76
77 if (val == 0xffffffff || val == 0x00000000 ||
78 val == 0x0000ffff || val == 0xffff0000)
79 continue;
80
81 /* Disable Bus Mastering for this one device */
82 reg32 = pci_read_config32(dev, PCI_COMMAND);
83 reg32 &= ~PCI_COMMAND_MASTER;
84 pci_write_config32(dev, PCI_COMMAND, reg32);
85
86 /* If this is a bridge, then follow it. */
87 hdr = pci_read_config8(dev, PCI_HEADER_TYPE);
88 hdr &= 0x7f;
89 if (hdr == PCI_HEADER_TYPE_BRIDGE ||
90 hdr == PCI_HEADER_TYPE_CARDBUS) {
91 unsigned int buses;
92 buses = pci_read_config32(dev, PCI_PRIMARY_BUS);
93 busmaster_disable_on_bus((buses >> 8) & 0xff);
94 }
95 }
96 }
97}
98
Lee Leahy32471722015-04-20 15:20:28 -070099static void tristate_gpios(uint32_t val)
100{
101 /* Tri-state eMMC */
102 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
103 SDMMC1_CMD_MMIO_OFFSET, val);
104 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
105 SDMMC1_D0_MMIO_OFFSET, val);
106 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
107 SDMMC1_D1_MMIO_OFFSET, val);
108 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
109 SDMMC1_D2_MMIO_OFFSET, val);
110 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
111 SDMMC1_D3_MMIO_OFFSET, val);
112 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
113 MMC1_D4_SD_WE_MMIO_OFFSET, val);
114 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
115 MMC1_D5_MMIO_OFFSET, val);
116 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
117 MMC1_D6_MMIO_OFFSET, val);
118 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
119 MMC1_D7_MMIO_OFFSET, val);
Shobhit Srivastava97f09c32015-08-10 11:48:23 +0530120 write32((void *)COMMUNITY_GPSOUTHEAST_BASE +
121 MMC1_RCLK_OFFSET, val);
Lee Leahy32471722015-04-20 15:20:28 -0700122
123 /* Tri-state HDMI */
124 write32((void *)COMMUNITY_GPNORTH_BASE +
125 HV_DDI2_DDC_SDA_MMIO_OFFSET, val);
126 write32((void *)COMMUNITY_GPNORTH_BASE +
127 HV_DDI2_DDC_SCL_MMIO_OFFSET, val);
Ravi Sarawadia5d98882015-08-11 14:06:15 -0700128
129 /* Tri-state CFIO 139 and 140 */
130 write32((void *)COMMUNITY_GPSOUTHWEST_BASE +
131 CFIO_139_MMIO_OFFSET, val);
132 write32((void *)COMMUNITY_GPSOUTHWEST_BASE +
133 CFIO_140_MMIO_OFFSET, val);
Lee Leahy32471722015-04-20 15:20:28 -0700134}
135
136
Lee Leahy77ff0b12015-05-05 15:07:29 -0700137static void southbridge_smi_sleep(void)
138{
139 uint32_t reg32;
140 uint8_t slp_typ;
141 uint16_t pmbase = get_pmbase();
142
143 /* First, disable further SMIs */
144 disable_smi(SLP_SMI_EN);
145
146 /* Figure out SLP_TYP */
147 reg32 = inl(pmbase + PM1_CNT);
148 printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32);
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500149 slp_typ = acpi_sleep_from_pm1(reg32);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700150
151 /* Do any mainboard sleep handling */
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500152 mainboard_smi_sleep(slp_typ);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700153
Lee Leahy32471722015-04-20 15:20:28 -0700154#if IS_ENABLED(CONFIG_ELOG_GSMI)
Lee Leahy77ff0b12015-05-05 15:07:29 -0700155 /* Log S3, S4, and S5 entry */
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500156 if (slp_typ >= ACPI_S3)
157 elog_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700158#endif
Lee Leahy32471722015-04-20 15:20:28 -0700159 /* Clear pending GPE events */
160 clear_gpe_status();
Lee Leahy77ff0b12015-05-05 15:07:29 -0700161
Lee Leahy32471722015-04-20 15:20:28 -0700162 /* Next, do the deed. */
Lee Leahy77ff0b12015-05-05 15:07:29 -0700163
164 switch (slp_typ) {
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500165 case ACPI_S0:
Lee Leahy77ff0b12015-05-05 15:07:29 -0700166 printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n");
167 break;
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500168 case ACPI_S1:
Lee Leahy77ff0b12015-05-05 15:07:29 -0700169 printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n");
170 break;
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500171 case ACPI_S3:
Lee Leahy77ff0b12015-05-05 15:07:29 -0700172 printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
173
174 /* Invalidate the cache before going to S3 */
175 wbinvd();
176 break;
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500177 case ACPI_S4:
Lee Leahy77ff0b12015-05-05 15:07:29 -0700178 printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n");
179 break;
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500180 case ACPI_S5:
Lee Leahy77ff0b12015-05-05 15:07:29 -0700181 printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
182
183 /* Disable all GPE */
184 disable_all_gpe();
185
186 /* also iterates over all bridges on bus 0 */
187 busmaster_disable_on_bus(0);
188 break;
189 default:
190 printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n");
191 break;
192 }
Lee Leahy32471722015-04-20 15:20:28 -0700193 /* Clear pending wake status bit to avoid immediate wake */
194 write32((void *)(0xfed88000 + 0x0200),
195 read32((void *)(0xfed88000 + 0x0200)));
Lee Leahy77ff0b12015-05-05 15:07:29 -0700196
Lee Leahy32471722015-04-20 15:20:28 -0700197 /* Tri-state specific GPIOS to avoid leakage during S3/S5 */
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500198 if ((slp_typ == ACPI_S3) || (slp_typ == ACPI_S5))
Lee Leahy32471722015-04-20 15:20:28 -0700199 tristate_gpios(PAD_CONTROL_REG0_TRISTATE);
200
201 /*
202 * Write back to the SLP register to cause the originally intended
Lee Leahy77ff0b12015-05-05 15:07:29 -0700203 * event again. We need to set BIT13 (SLP_EN) though to make the
204 * sleep happen.
205 */
206 enable_pm1_control(SLP_EN);
207
208 /* Make sure to stop executing code here for S3/S4/S5 */
Aaron Durbin1b6196d2016-07-13 23:20:26 -0500209 if (slp_typ >= ACPI_S3)
Lee Leahy32471722015-04-20 15:20:28 -0700210 hlt();
Lee Leahy77ff0b12015-05-05 15:07:29 -0700211
Lee Leahy32471722015-04-20 15:20:28 -0700212 /*
213 * In most sleep states, the code flow of this function ends at
Lee Leahy77ff0b12015-05-05 15:07:29 -0700214 * the line above. However, if we entered sleep state S1 and wake
215 * up again, we will continue to execute code in this function.
216 */
217 reg32 = inl(pmbase + PM1_CNT);
218 if (reg32 & SCI_EN) {
219 /* The OS is not an ACPI OS, so we set the state to S0 */
220 disable_pm1_control(SLP_EN | SLP_TYP);
221 }
222}
223
224/*
225 * Look for Synchronous IO SMI and use save state from that
226 * core in case we are not running on the same core that
227 * initiated the IO transaction.
228 */
229static em64t100_smm_state_save_area_t *smi_apmc_find_state_save(uint8_t cmd)
230{
231 em64t100_smm_state_save_area_t *state;
232 int node;
233
234 /* Check all nodes looking for the one that issued the IO */
235 for (node = 0; node < CONFIG_MAX_CPUS; node++) {
236 state = smm_get_save_state(node);
237
238 /* Check for Synchronous IO (bit0==1) */
239 if (!(state->io_misc_info & (1 << 0)))
240 continue;
241
242 /* Make sure it was a write (bit4==0) */
243 if (state->io_misc_info & (1 << 4))
244 continue;
245
246 /* Check for APMC IO port */
247 if (((state->io_misc_info >> 16) & 0xff) != APM_CNT)
248 continue;
249
250 /* Check AX against the requested command */
251 if ((state->rax & 0xff) != cmd)
252 continue;
253
254 return state;
255 }
256
257 return NULL;
258}
259
Lee Leahy32471722015-04-20 15:20:28 -0700260#if IS_ENABLED(CONFIG_ELOG_GSMI)
Lee Leahy77ff0b12015-05-05 15:07:29 -0700261static void southbridge_smi_gsmi(void)
262{
263 u32 *ret, *param;
264 uint8_t sub_command;
265 em64t100_smm_state_save_area_t *io_smi =
266 smi_apmc_find_state_save(ELOG_GSMI_APM_CNT);
267
268 if (!io_smi)
269 return;
270
271 /* Command and return value in EAX */
Lee Leahy32471722015-04-20 15:20:28 -0700272 ret = (u32 *)&io_smi->rax;
Lee Leahy77ff0b12015-05-05 15:07:29 -0700273 sub_command = (uint8_t)(*ret >> 8);
274
275 /* Parameter buffer in EBX */
Lee Leahy32471722015-04-20 15:20:28 -0700276 param = (u32 *)&io_smi->rbx;
Lee Leahy77ff0b12015-05-05 15:07:29 -0700277
278 /* drivers/elog/gsmi.c */
279 *ret = gsmi_exec(sub_command, param);
280}
281#endif
282
283static void finalize(void)
284{
285 static int finalize_done;
286
287 if (finalize_done) {
288 printk(BIOS_DEBUG, "SMM already finalized.\n");
289 return;
290 }
291 finalize_done = 1;
292
Lee Leahy32471722015-04-20 15:20:28 -0700293#if IS_ENABLED(CONFIG_SPI_FLASH_SMM)
Lee Leahy77ff0b12015-05-05 15:07:29 -0700294 /* Re-init SPI driver to handle locked BAR */
295 spi_init();
296#endif
297}
298
Lee Leahy77ff0b12015-05-05 15:07:29 -0700299static void southbridge_smi_apmc(void)
300{
301 uint8_t reg8;
302 em64t100_smm_state_save_area_t *state;
303
304 /* Emulate B2 register as the FADT / Linux expects it */
305
306 reg8 = inb(APM_CNT);
307 switch (reg8) {
308 case APM_CNT_CST_CONTROL:
Lee Leahy32471722015-04-20 15:20:28 -0700309 /*
310 * Calling this function seems to cause
Lee Leahy77ff0b12015-05-05 15:07:29 -0700311 * some kind of race condition in Linux
312 * and causes a kernel oops
313 */
314 printk(BIOS_DEBUG, "C-state control\n");
315 break;
316 case APM_CNT_PST_CONTROL:
Lee Leahy32471722015-04-20 15:20:28 -0700317 /*
318 * Calling this function seems to cause
Lee Leahy77ff0b12015-05-05 15:07:29 -0700319 * some kind of race condition in Linux
320 * and causes a kernel oops
321 */
322 printk(BIOS_DEBUG, "P-state control\n");
323 break;
324 case APM_CNT_ACPI_DISABLE:
325 disable_pm1_control(SCI_EN);
326 printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
327 break;
328 case APM_CNT_ACPI_ENABLE:
329 enable_pm1_control(SCI_EN);
330 printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
331 break;
332 case APM_CNT_GNVS_UPDATE:
333 if (smm_initialized) {
334 printk(BIOS_DEBUG,
335 "SMI#: SMM structures already initialized!\n");
336 return;
337 }
338 state = smi_apmc_find_state_save(reg8);
339 if (state) {
340 /* EBX in the state save contains the GNVS pointer */
341 gnvs = (global_nvs_t *)((uint32_t)state->rbx);
342 smm_initialized = 1;
343 printk(BIOS_DEBUG, "SMI#: Setting GNVS to %p\n", gnvs);
344 }
345 break;
Lee Leahy32471722015-04-20 15:20:28 -0700346#if IS_ENABLED(CONFIG_ELOG_GSMI)
Lee Leahy77ff0b12015-05-05 15:07:29 -0700347 case ELOG_GSMI_APM_CNT:
348 southbridge_smi_gsmi();
349 break;
350#endif
351 case APM_CNT_FINALIZE:
352 finalize();
353 break;
Lee Leahy77ff0b12015-05-05 15:07:29 -0700354 }
355
356 mainboard_smi_apmc(reg8);
357}
358
359static void southbridge_smi_pm1(void)
360{
361 uint16_t pm1_sts = clear_pm1_status();
362
Lee Leahy32471722015-04-20 15:20:28 -0700363 /*
364 * While OSPM is not active, poweroff immediately
Lee Leahy77ff0b12015-05-05 15:07:29 -0700365 * on a power button event.
366 */
367 if (pm1_sts & PWRBTN_STS) {
Lee Leahy32471722015-04-20 15:20:28 -0700368 /* power button pressed */
369#if IS_ENABLED(CONFIG_ELOG_GSMI)
Lee Leahy77ff0b12015-05-05 15:07:29 -0700370 elog_add_event(ELOG_TYPE_POWER_BUTTON);
371#endif
372 disable_pm1_control(-1UL);
373 enable_pm1_control(SLP_EN | (SLP_TYP_S5 << SLP_TYP_SHIFT));
374 }
375}
376
377static void southbridge_smi_gpe0(void)
378{
379 clear_gpe_status();
380}
381
382static void southbridge_smi_tco(void)
383{
384 uint32_t tco_sts = clear_tco_status();
385
386 /* Any TCO event? */
387 if (!tco_sts)
388 return;
389
390 if (tco_sts & TCO_TIMEOUT) { /* TIMEOUT */
391 /* Handle TCO timeout */
392 printk(BIOS_DEBUG, "TCO Timeout.\n");
393 }
394}
395
396static void southbridge_smi_periodic(void)
397{
398 uint32_t reg32;
399
400 reg32 = inl(get_pmbase() + SMI_EN);
401
402 /* Are periodic SMIs enabled? */
403 if ((reg32 & PERIODIC_EN) == 0)
404 return;
405
406 printk(BIOS_DEBUG, "Periodic SMI.\n");
407}
408
409typedef void (*smi_handler_t)(void);
410
411static const smi_handler_t southbridge_smi[32] = {
Lee Leahy32471722015-04-20 15:20:28 -0700412 NULL, /* [0] reserved */
413 NULL, /* [1] reserved */
414 NULL, /* [2] BIOS_STS */
415 NULL, /* [3] LEGACY_USB_STS */
416 southbridge_smi_sleep, /* [4] SLP_SMI_STS */
417 southbridge_smi_apmc, /* [5] APM_STS */
418 NULL, /* [6] SWSMI_TMR_STS */
419 NULL, /* [7] reserved */
420 southbridge_smi_pm1, /* [8] PM1_STS */
421 southbridge_smi_gpe0, /* [9] GPE0_STS */
422 NULL, /* [10] reserved */
423 NULL, /* [11] reserved */
424 NULL, /* [12] reserved */
425 southbridge_smi_tco, /* [13] TCO_STS */
426 southbridge_smi_periodic, /* [14] PERIODIC_STS */
427 NULL, /* [15] SERIRQ_SMI_STS */
428 NULL, /* [16] SMBUS_SMI_STS */
429 NULL, /* [17] LEGACY_USB2_STS */
430 NULL, /* [18] INTEL_USB2_STS */
431 NULL, /* [19] reserved */
432 NULL, /* [20] PCI_EXP_SMI_STS */
433 NULL, /* [21] reserved */
434 NULL, /* [22] reserved */
435 NULL, /* [23] reserved */
436 NULL, /* [24] reserved */
437 NULL, /* [25] reserved */
438 NULL, /* [26] SPI_STS */
439 NULL, /* [27] reserved */
440 NULL, /* [28] PUNIT */
441 NULL, /* [29] GUNIT */
442 NULL, /* [30] reserved */
443 NULL /* [31] reserved */
Lee Leahy77ff0b12015-05-05 15:07:29 -0700444};
445
446void southbridge_smi_handler(void)
447{
448 int i;
449 uint32_t smi_sts;
450
Lee Leahy32471722015-04-20 15:20:28 -0700451 /*
452 * We need to clear the SMI status registers, or we won't see what's
Lee Leahy77ff0b12015-05-05 15:07:29 -0700453 * happening in the following calls.
454 */
455 smi_sts = clear_smi_status();
456
457 /* Call SMI sub handler for each of the status bits */
458 for (i = 0; i < ARRAY_SIZE(southbridge_smi); i++) {
459 if (!(smi_sts & (1 << i)))
460 continue;
461
462 if (southbridge_smi[i] != NULL) {
463 southbridge_smi[i]();
464 } else {
465 printk(BIOS_DEBUG,
Martin Roth2ed0aa22016-01-05 20:58:58 -0700466 "SMI_STS[%d] occurred, but no "
Lee Leahy77ff0b12015-05-05 15:07:29 -0700467 "handler available.\n", i);
468 }
469 }
470
Lee Leahy32471722015-04-20 15:20:28 -0700471 /*
472 * The GPIO SMI events do not have a status bit in SMI_STS. Therefore,
473 * these events need to be cleared and checked unconditionally.
474 */
Lee Leahy77ff0b12015-05-05 15:07:29 -0700475 mainboard_smi_gpi(clear_alt_status());
476}