blob: eb24d219fa18e580019d39fd462ae0f9a2468bac [file] [log] [blame]
Martin Roth433659a2014-05-12 21:55:00 -06001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2013 Google Inc.
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.
Martin Roth433659a2014-05-12 21:55:00 -060015 */
16
17#include <device/device.h>
18#include <device/pci.h>
19#include <console/console.h>
20#include <arch/io.h>
21#include <cpu/cpu.h>
22#include <cpu/x86/smm.h>
23#include <string.h>
24
Ben Gardnerfa6014a2015-12-08 21:20:25 -060025#include <soc/iomap.h>
26#include <soc/pmc.h>
27#include <soc/smm.h>
Martin Roth433659a2014-05-12 21:55:00 -060028
29/* Save the gpio route register. The settings are committed from
30 * southcluster_smm_enable_smi(). */
31static uint32_t gpio_route;
32
33void southcluster_smm_save_gpio_route(uint32_t route)
34{
35 gpio_route = route;
36}
37
38void southcluster_smm_clear_state(void)
39{
40 uint32_t smi_en;
41
42 /* Log events from chipset before clearing */
43 southcluster_log_state();
44
45 printk(BIOS_DEBUG, "Initializing Southbridge SMI...");
46 printk(BIOS_SPEW, " pmbase = 0x%04x\n", get_pmbase());
47
48 smi_en = inl(get_pmbase() + SMI_EN);
49 if (smi_en & APMC_EN) {
50 printk(BIOS_INFO, "SMI# handler already enabled?\n");
51 return;
52 }
53
54 /* Dump and clear status registers */
55 clear_smi_status();
56 clear_pm1_status();
57 clear_tco_status();
58 clear_gpe_status();
59 clear_alt_status();
60 clear_pmc_status();
61}
62
63static void southcluster_smm_route_gpios(void)
64{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080065 u32 *gpio_rout = (u32 *)(PMC_BASE_ADDRESS + GPIO_ROUT);
Martin Roth433659a2014-05-12 21:55:00 -060066 const unsigned short alt_gpio_smi = ACPI_BASE_ADDRESS + ALT_GPIO_SMI;
67 uint32_t alt_gpio_reg = 0;
68 uint32_t route_reg = gpio_route;
69 int i;
70
71 printk(BIOS_DEBUG, "GPIO_ROUT = %08x\n", route_reg);
72
73 /* Start the routing for the specific gpios. */
74 write32(gpio_rout, route_reg);
75
76 /* Enable SMIs for the gpios that are set to trigger the SMI. */
77 for (i = 0; i < 16; i++) {
78 if ((route_reg & ROUTE_MASK) == ROUTE_SMI) {
79 alt_gpio_reg |= (1 << i);
80 }
81 route_reg >>= 2;
82 }
83 printk(BIOS_DEBUG, "ALT_GPIO_SMI = %08x\n", alt_gpio_reg);
84
85 outl(alt_gpio_reg, alt_gpio_smi);
86}
87
88void southcluster_smm_enable_smi(void)
89{
90
91 printk(BIOS_DEBUG, "Enabling SMIs.\n");
92 /* Configure events Disable pcie wake. */
93 enable_pm1(PWRBTN_EN | GBL_EN | PCIEXPWAK_DIS);
94 disable_gpe(PME_B0_EN);
95
96 /* Set up the GPIO route. */
97 southcluster_smm_route_gpios();
98
99 /* Enable SMI generation:
100 * - on APMC writes (io 0xb2)
101 * - on writes to SLP_EN (sleep states)
102 * - on writes to GBL_RLS (bios commands)
103 * No SMIs:
104 * - on TCO events
105 * - on microcontroller writes (io 0x62/0x66)
106 */
107 enable_smi(APMC_EN | SLP_SMI_EN | GBL_SMI_EN | EOS);
108}
109
110void smm_setup_structures(void *gnvs, void *tcg, void *smi1)
111{
112 /*
113 * Issue SMI to set the gnvs pointer in SMM.
114 * tcg and smi1 are unused.
115 *
116 * EAX = APM_CNT_GNVS_UPDATE
117 * EBX = gnvs pointer
118 * EDX = APM_CNT
119 */
120 asm volatile (
121 "outb %%al, %%dx\n\t"
122 : /* ignore result */
123 : "a" (APM_CNT_GNVS_UPDATE),
124 "b" ((uint32_t)gnvs),
125 "d" (APM_CNT)
126 );
127}