1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <arch/io.h>
#include <console/console.h>
#include <cpu/x86/smm.h>
#include <cpu/x86/cache.h>
#include <cpu/amd/amd64_save_state.h>
#include <acpi/acpi.h>
#include <arch/hlt.h>
#include <device/pci_def.h>
#include <smmstore.h>
#include <soc/smi.h>
#include <soc/southbridge.h>
#include <amdblocks/acpimmio.h>
#include <amdblocks/acpi.h>
#include <amdblocks/psp.h>
#include <amdblocks/smi.h>
#include <amdblocks/smm.h>
#include <elog.h>
#include <soc/smu.h>
#include <types.h>
/* bits in smm_io_trap */
#define SMM_IO_TRAP_PORT_OFFSET 16
#define SMM_IO_TRAP_PORT_ADDRESS_MASK 0xffff
#define SMM_IO_TRAP_RW (1 << 0)
#define SMM_IO_TRAP_VALID (1 << 1)
static inline u16 get_io_address(u32 info)
{
return ((info >> SMM_IO_TRAP_PORT_OFFSET) &
SMM_IO_TRAP_PORT_ADDRESS_MASK);
}
static void *find_save_state(int cmd)
{
unsigned int core;
amd64_smm_state_save_area_t *state;
u32 smm_io_trap;
u8 reg_al;
/* Check all nodes looking for the one that issued the IO */
for (core = 0; core < CONFIG_MAX_CPUS; core++) {
state = smm_get_save_state(core);
smm_io_trap = state->smm_io_trap_offset;
/* Check for Valid IO Trap Word (bit1==1) */
if (!(smm_io_trap & SMM_IO_TRAP_VALID))
continue;
/* Make sure it was a write (bit0==0) */
if (smm_io_trap & SMM_IO_TRAP_RW)
continue;
/* Check for APMC IO port */
if (pm_acpi_smi_cmd_port() != get_io_address(smm_io_trap))
continue;
/* Check AL against the requested command */
reg_al = state->rax;
if (reg_al == cmd)
return state;
}
return NULL;
}
static void southbridge_smi_gsmi(void)
{
u8 sub_command;
amd64_smm_state_save_area_t *io_smi;
u32 reg_ebx;
io_smi = find_save_state(APM_CNT_ELOG_GSMI);
if (!io_smi)
return;
/* Command and return value in EAX */
sub_command = (io_smi->rax >> 8) & 0xff;
/* Parameter buffer in EBX */
reg_ebx = io_smi->rbx;
/* drivers/elog/gsmi.c */
io_smi->rax = gsmi_exec(sub_command, ®_ebx);
}
static void southbridge_smi_store(void)
{
u8 sub_command;
amd64_smm_state_save_area_t *io_smi;
u32 reg_ebx;
io_smi = find_save_state(APM_CNT_SMMSTORE);
if (!io_smi)
return;
/* Command and return value in EAX */
sub_command = (io_smi->rax >> 8) & 0xff;
/* Parameter buffer in EBX */
reg_ebx = io_smi->rbx;
/* drivers/smmstore/smi.c */
io_smi->rax = smmstore_exec(sub_command, (void *)reg_ebx);
}
static void fch_apmc_smi_handler(void)
{
const uint8_t cmd = inb(pm_acpi_smi_cmd_port());
switch (cmd) {
case APM_CNT_ACPI_ENABLE:
acpi_enable_sci();
break;
case APM_CNT_ACPI_DISABLE:
acpi_disable_sci();
break;
case APM_CNT_ELOG_GSMI:
if (CONFIG(ELOG_GSMI))
southbridge_smi_gsmi();
break;
case APM_CNT_SMMSTORE:
if (CONFIG(SMMSTORE))
southbridge_smi_store();
break;
case APM_CNT_SMMINFO:
psp_notify_smm();
break;
}
mainboard_smi_apmc(cmd);
}
static void fch_slp_typ_handler(void)
{
uint32_t pci_ctrl, reg32;
uint16_t pm1cnt, reg16;
uint8_t slp_typ, rst_ctrl;
/* Figure out SLP_TYP */
pm1cnt = acpi_read16(MMIO_ACPI_PM1_CNT_BLK);
printk(BIOS_SPEW, "SMI#: SLP = 0x%04x\n", pm1cnt);
slp_typ = acpi_sleep_from_pm1(pm1cnt);
/* Do any mainboard sleep handling */
mainboard_smi_sleep(slp_typ);
switch (slp_typ) {
case ACPI_S0:
printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n");
break;
case ACPI_S3:
printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
break;
case ACPI_S4:
printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n");
break;
case ACPI_S5:
printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
break;
default:
printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n");
break;
}
if (slp_typ >= ACPI_S3) {
/* Sleep Type Elog S3, S4, and S5 entry */
elog_gsmi_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ);
wbinvd();
clear_all_smi_status();
/* Do not send SMI before AcpiPm1CntBlkx00[SlpTyp] */
pci_ctrl = pm_read32(PM_PCI_CTRL);
pci_ctrl &= ~FORCE_SLPSTATE_RETRY;
pm_write32(PM_PCI_CTRL, pci_ctrl);
/* Enable SlpTyp */
rst_ctrl = pm_read8(PM_RST_CTRL1);
rst_ctrl |= SLPTYPE_CONTROL_EN;
pm_write8(PM_RST_CTRL1, rst_ctrl);
/*
* Before the final command, check if there's pending wake
* event. Read enable first, so that reading the actual status
* is as close as possible to entering S3. The idea is to
* minimize the opportunity for a wake event to happen before
* actually entering S3. If there's a pending wake event, log
* it and continue normal path. S3 will fail and the wake event
* becomes a SCI.
*/
if (CONFIG(ELOG_GSMI)) {
reg16 = acpi_read16(MMIO_ACPI_PM1_EN);
reg16 &= acpi_read16(MMIO_ACPI_PM1_STS);
if (reg16)
elog_add_extended_event(
ELOG_SLEEP_PENDING_PM1_WAKE,
(u32)reg16);
reg32 = acpi_read32(MMIO_ACPI_GPE0_EN);
reg32 &= acpi_read32(MMIO_ACPI_GPE0_STS);
if (reg32)
elog_add_extended_event(
ELOG_SLEEP_PENDING_GPE0_WAKE,
reg32);
} /* if (CONFIG(ELOG_GSMI)) */
if (slp_typ == ACPI_S3)
psp_notify_sx_info(ACPI_S3);
smu_sx_entry(); /* Leave SlpTypeEn clear, SMU will set */
printk(BIOS_ERR, "Error: System did not go to sleep\n");
hlt();
}
}
int southbridge_io_trap_handler(int smif)
{
return 0;
}
/*
* Table of functions supported in the SMI handler. Note that SMI source setup
* in fch.c is unrelated to this list.
*/
static const struct smi_sources_t smi_sources[] = {
{ .type = SMITYPE_SMI_CMD_PORT, .handler = fch_apmc_smi_handler },
{ .type = SMITYPE_SLP_TYP, .handler = fch_slp_typ_handler},
};
void *get_smi_source_handler(int source)
{
size_t i;
for (i = 0 ; i < ARRAY_SIZE(smi_sources) ; i++)
if (smi_sources[i].type == source)
return smi_sources[i].handler;
return NULL;
}
|