blob: 2830d18905147bf78863958e6f120ff7a0317a1c [file] [log] [blame]
Marc Jones24484842017-05-04 21:17:45 -06001/*
Marshall Dawson36a23562017-09-28 17:21:09 -06002 * This file is part of the coreboot project.
Marc Jones24484842017-05-04 21:17:45 -06003 *
Marshall Dawson36a23562017-09-28 17:21:09 -06004 * Copyright (C) 2017 Advanced Micro Devices, Inc.
Marc Jones24484842017-05-04 21:17:45 -06005 * Copyright (C) 2014 Alexandru Gagniuc <mr.nuke.me@gmail.com>
Marshall Dawson36a23562017-09-28 17:21:09 -06006 *
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, or (at your
10 * option) any later version.
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.
Marc Jones24484842017-05-04 21:17:45 -060016 */
17
Marc Jones24484842017-05-04 21:17:45 -060018#include <console/console.h>
19#include <cpu/x86/smm.h>
Marshall Dawson081851a2017-10-02 14:03:57 -060020#include <cpu/x86/cache.h>
21#include <arch/acpi.h>
22#include <arch/hlt.h>
Marc Jones24484842017-05-04 21:17:45 -060023#include <delay.h>
Marshall Dawson081851a2017-10-02 14:03:57 -060024#include <device/pci_def.h>
Marc Jones24484842017-05-04 21:17:45 -060025#include <soc/smi.h>
Marc Jonesdfeb1c42017-08-07 19:08:24 -060026#include <soc/southbridge.h>
John E. Kabat Jr7057a272017-10-16 20:23:00 -060027#include <elog.h>
28
29/* bits in smm_io_trap */
30#define SMM_IO_TRAP_PORT_OFFSET 16
31#define SMM_IO_TRAP_PORT_ADDRESS_MASK 0xffff
32#define SMM_IO_TRAP_RW (1 << 0)
33#define SMM_IO_TRAP_VALID (1 << 1)
34
35static inline u16 get_io_address(u32 info)
36{
37 return ((info >> SMM_IO_TRAP_PORT_OFFSET) &
38 SMM_IO_TRAP_PORT_ADDRESS_MASK);
39}
40
41static void *find_save_state(int cmd)
42{
43 int core;
44 amd64_smm_state_save_area_t *state;
45 u32 smm_io_trap;
46 u8 reg_al;
47
48 /* Check all nodes looking for the one that issued the IO */
49 for (core = 0; core < CONFIG_MAX_CPUS; core++) {
50 state = smm_get_save_state(core);
51 smm_io_trap = state->smm_io_trap_offset;
52 /* Check for Valid IO Trap Word (bit1==1) */
53 if (!(smm_io_trap & SMM_IO_TRAP_VALID))
54 continue;
55 /* Make sure it was a write (bit0==0) */
56 if (smm_io_trap & SMM_IO_TRAP_RW)
57 continue;
58 /* Check for APMC IO port */
59 if (pm_acpi_smi_cmd_port() != get_io_address(smm_io_trap))
60 continue;
61 /* Check AL against the requested command */
62 reg_al = state->rax;
63 if (reg_al == cmd)
64 return state;
65 }
66 return NULL;
67}
68
69static void southbridge_smi_gsmi(void)
70{
71 u8 sub_command;
72 amd64_smm_state_save_area_t *io_smi;
73 u32 reg_ebx;
74
75 io_smi = find_save_state(ELOG_GSMI_APM_CNT);
76 if (!io_smi)
77 return;
78 /* Command and return value in EAX */
79 sub_command = (io_smi->rax >> 8) & 0xff;
80
81 /* Parameter buffer in EBX */
82 reg_ebx = io_smi->rbx;
83
84 /* drivers/elog/gsmi.c */
85 io_smi->rax = gsmi_exec(sub_command, &reg_ebx);
86}
Marc Jones24484842017-05-04 21:17:45 -060087
Marc Jonesdfeb1c42017-08-07 19:08:24 -060088static void sb_apmc_smi_handler(void)
Marc Jones24484842017-05-04 21:17:45 -060089{
90 u32 reg32;
Marshall Dawson66e62da2017-09-27 13:32:38 -060091 const uint8_t cmd = inb(pm_acpi_smi_cmd_port());
Marc Jones24484842017-05-04 21:17:45 -060092
93 switch (cmd) {
Marshall Dawsone9b862e2017-09-22 15:14:46 -060094 case APM_CNT_ACPI_ENABLE:
Marc Jones24484842017-05-04 21:17:45 -060095 reg32 = inl(ACPI_PM1_CNT_BLK);
96 reg32 |= (1 << 0); /* SCI_EN */
97 outl(reg32, ACPI_PM1_CNT_BLK);
98 break;
Marshall Dawsone9b862e2017-09-22 15:14:46 -060099 case APM_CNT_ACPI_DISABLE:
Marc Jones24484842017-05-04 21:17:45 -0600100 reg32 = inl(ACPI_PM1_CNT_BLK);
101 reg32 &= ~(1 << 0); /* clear SCI_EN */
Martin Rothba397702018-02-08 19:52:20 -0700102 outl(reg32, ACPI_PM1_CNT_BLK);
Marc Jones24484842017-05-04 21:17:45 -0600103 break;
John E. Kabat Jr7057a272017-10-16 20:23:00 -0600104 case ELOG_GSMI_APM_CNT:
105 if (IS_ENABLED(CONFIG_ELOG_GSMI))
106 southbridge_smi_gsmi();
107 break;
Marc Jones24484842017-05-04 21:17:45 -0600108 }
109
110 mainboard_smi_apmc(cmd);
111}
112
Marshall Dawson081851a2017-10-02 14:03:57 -0600113static void disable_all_smi_status(void)
114{
115 smi_write32(smi_read32(SMI_SCI_STATUS), SMI_SCI_STATUS);
116 smi_write32(smi_read32(SMI_REG_SMISTS0), SMI_REG_SMISTS0);
117 smi_write32(smi_read32(SMI_REG_SMISTS1), SMI_REG_SMISTS1);
118 smi_write32(smi_read32(SMI_REG_SMISTS2), SMI_REG_SMISTS2);
119 smi_write32(smi_read32(SMI_REG_SMISTS3), SMI_REG_SMISTS3);
120 smi_write32(smi_read32(SMI_REG_SMISTS4), SMI_REG_SMISTS4);
121}
122
123static void sb_slp_typ_handler(void)
124{
125 uint32_t pm1cnt, pci_ctrl;
126 uint8_t slp_typ, rst_ctrl;
127
128 /* Figure out SLP_TYP */
129 pm1cnt = inl(pm_acpi_pm_cnt_blk());
130 printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", pm1cnt);
131 slp_typ = acpi_sleep_from_pm1(pm1cnt);
132
133 /* Do any mainboard sleep handling */
134 mainboard_smi_sleep(slp_typ);
135
136 switch (slp_typ) {
137 case ACPI_S0:
138 printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n");
139 break;
140 case ACPI_S3:
141 printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
142 break;
143 case ACPI_S4:
144 printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n");
145 break;
146 case ACPI_S5:
147 printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
148 break;
149 default:
150 printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n");
151 break;
152 }
153
154 if (slp_typ >= ACPI_S3) {
John E. Kabat Jr7057a272017-10-16 20:23:00 -0600155 /* Sleep Type Elog S3, S4, and S5 entry */
156 if (IS_ENABLED(CONFIG_ELOG_GSMI))
157 elog_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ);
158
Marshall Dawson081851a2017-10-02 14:03:57 -0600159 wbinvd();
160
161 disable_all_smi_status();
162
163 /* Do not send SMI before AcpiPm1CntBlkx00[SlpTyp] */
164 pci_ctrl = pm_read32(PM_PCI_CTRL);
165 pci_ctrl &= ~FORCE_SLPSTATE_RETRY;
166 pci_ctrl |= FORCE_STPCLK_RETRY;
167 pm_write32(PM_PCI_CTRL, pci_ctrl);
168
169 /* Enable SlpTyp */
170 rst_ctrl = pm_read8(PM_RST_CTRL1);
171 rst_ctrl |= SLPTYPE_CONTROL_EN;
172 pm_write8(PM_RST_CTRL1, rst_ctrl);
173
174 /* Reissue Pm1 write */
175 outl(pm1cnt | SLP_EN, pm_acpi_pm_cnt_blk());
176 hlt();
177 }
178}
179
Marc Jones24484842017-05-04 21:17:45 -0600180int southbridge_io_trap_handler(int smif)
181{
182 return 0;
183}
184
Marshall Dawson36a23562017-09-28 17:21:09 -0600185/*
186 * Table of functions supported in the SMI handler. Note that SMI source setup
187 * in southbridge.c is unrelated to this list.
188 */
189struct smi_sources_t smi_sources[] = {
190 { .type = SMITYPE_SMI_CMD_PORT, .handler = sb_apmc_smi_handler },
Marshall Dawson081851a2017-10-02 14:03:57 -0600191 { .type = SMITYPE_SLP_TYP, .handler = sb_slp_typ_handler},
Marshall Dawson36a23562017-09-28 17:21:09 -0600192};
193
Marc Jones24484842017-05-04 21:17:45 -0600194static void process_smi_sci(void)
195{
Marshall Dawsonc6c4a212017-09-26 14:52:45 -0600196 const uint32_t status = smi_read32(SMI_SCI_STATUS);
Marc Jones24484842017-05-04 21:17:45 -0600197
198 /* Clear events to prevent re-entering SMI if event isn't handled */
Marshall Dawsonc6c4a212017-09-26 14:52:45 -0600199 smi_write32(SMI_SCI_STATUS, status);
Marc Jones24484842017-05-04 21:17:45 -0600200}
201
Marshall Dawson36a23562017-09-28 17:21:09 -0600202static void *get_source_handler(int source)
Marc Jones24484842017-05-04 21:17:45 -0600203{
Marshall Dawson36a23562017-09-28 17:21:09 -0600204 int i;
Marc Jones24484842017-05-04 21:17:45 -0600205
Marshall Dawson36a23562017-09-28 17:21:09 -0600206 for (i = 0 ; i < ARRAY_SIZE(smi_sources) ; i++)
207 if (smi_sources[i].type == source)
208 return smi_sources[i].handler;
209
210 return NULL;
211}
212
213static void process_smi_sources(uint32_t reg)
214{
215 const uint32_t status = smi_read32(reg);
216 int bit_zero = 32 / sizeof(uint32_t) * (reg - SMI_REG_SMISTS0);
217 void (*source_handler)(void);
218 int i;
219
220 for (i = 0 ; i < 32 ; i++) {
221 if (status & (1 << i)) {
222 source_handler = get_source_handler(i + bit_zero);
223 if (source_handler)
224 source_handler();
225 }
Marc Jones24484842017-05-04 21:17:45 -0600226 }
227
Marshall Dawson36a23562017-09-28 17:21:09 -0600228 if (reg == SMI_REG_SMISTS0)
229 if (status & GEVENT_MASK)
230 /* Gevent[23:0] are assumed to be mainboard-specific */
231 mainboard_smi_gpi(status & GEVENT_MASK);
Marc Jones24484842017-05-04 21:17:45 -0600232
Marshall Dawson36a23562017-09-28 17:21:09 -0600233 /* Clear all events in this register */
234 smi_write32(reg, status);
Marc Jones24484842017-05-04 21:17:45 -0600235}
236
Marshall Dawsonb6172112017-09-13 17:47:31 -0600237void southbridge_smi_handler(void)
Marc Jones24484842017-05-04 21:17:45 -0600238{
Marshall Dawsonc6c4a212017-09-26 14:52:45 -0600239 const uint16_t smi_src = smi_read16(SMI_REG_POINTER);
Marc Jones24484842017-05-04 21:17:45 -0600240
Marshall Dawsonc6c4a212017-09-26 14:52:45 -0600241 if (smi_src & SMI_STATUS_SRC_SCI)
Marc Jones24484842017-05-04 21:17:45 -0600242 process_smi_sci();
Marshall Dawsonc6c4a212017-09-26 14:52:45 -0600243 if (smi_src & SMI_STATUS_SRC_0)
Marshall Dawson36a23562017-09-28 17:21:09 -0600244 process_smi_sources(SMI_REG_SMISTS0);
Marshall Dawsonc6c4a212017-09-26 14:52:45 -0600245 if (smi_src & SMI_STATUS_SRC_1)
Marshall Dawson36a23562017-09-28 17:21:09 -0600246 process_smi_sources(SMI_REG_SMISTS1);
Marshall Dawsonc6c4a212017-09-26 14:52:45 -0600247 if (smi_src & SMI_STATUS_SRC_2)
Marshall Dawson36a23562017-09-28 17:21:09 -0600248 process_smi_sources(SMI_REG_SMISTS2);
Marshall Dawsonc6c4a212017-09-26 14:52:45 -0600249 if (smi_src & SMI_STATUS_SRC_3)
Marshall Dawson36a23562017-09-28 17:21:09 -0600250 process_smi_sources(SMI_REG_SMISTS3);
Marshall Dawsonc6c4a212017-09-26 14:52:45 -0600251 if (smi_src & SMI_STATUS_SRC_4)
Marshall Dawson36a23562017-09-28 17:21:09 -0600252 process_smi_sources(SMI_REG_SMISTS4);
Marc Jones24484842017-05-04 21:17:45 -0600253}
254
255void southbridge_smi_set_eos(void)
256{
257 uint32_t reg = smi_read32(SMI_REG_SMITRIG0);
258 reg |= SMITRG0_EOS;
259 smi_write32(SMI_REG_SMITRIG0, reg);
260}