1 /** @file
2 File to contain all the hardware specific stuff for the Smm Sx dispatch protocol.
3
4 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8 #include "PchSmmHelpers.h"
9
10 #define PROGRESS_CODE_S3_SUSPEND_END PcdGet32 (PcdProgressCodeS3SuspendEnd)
11
12 GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC SX_SOURCE_DESC = {
13 PCH_SMM_NO_FLAGS,
14 {
15 {
16 {
17 ACPI_ADDR_TYPE,
18 {R_PCH_SMI_EN}
19 },
20 S_PCH_SMI_EN,
21 N_PCH_SMI_EN_ON_SLP_EN
22 },
23 NULL_BIT_DESC_INITIALIZER
24 },
25 {
26 {
27 {
28 ACPI_ADDR_TYPE,
29 {R_PCH_SMI_STS}
30 },
31 S_PCH_SMI_STS,
32 N_PCH_SMI_STS_ON_SLP_EN
33 }
34 },
35 {
36 {
37 ACPI_ADDR_TYPE,
38 {R_PCH_SMI_STS}
39 },
40 S_PCH_SMI_STS,
41 N_PCH_SMI_STS_ON_SLP_EN
42 }
43 };
44
45 /**
46 Get the Sleep type
47
48 @param[in] Record No use
49 @param[out] Context The context that includes SLP_TYP bits to be filled
50
51 **/
52 VOID
53 EFIAPI
SxGetContext(IN DATABASE_RECORD * Record,OUT PCH_SMM_CONTEXT * Context)54 SxGetContext (
55 IN DATABASE_RECORD *Record,
56 OUT PCH_SMM_CONTEXT *Context
57 )
58 {
59 UINT32 Pm1Cnt;
60
61 Pm1Cnt = IoRead32 ((UINTN) (mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT));
62
63 ///
64 /// By design, the context phase will always be ENTRY
65 ///
66 Context->Sx.Phase = SxEntry;
67
68 ///
69 /// Map the PM1_CNT register's SLP_TYP bits to the context type
70 ///
71 switch (Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) {
72 case V_PCH_ACPI_PM1_CNT_S0:
73 Context->Sx.Type = SxS0;
74 break;
75
76 case V_PCH_ACPI_PM1_CNT_S1:
77 Context->Sx.Type = SxS1;
78 break;
79
80 case V_PCH_ACPI_PM1_CNT_S3:
81 Context->Sx.Type = SxS3;
82 break;
83
84 case V_PCH_ACPI_PM1_CNT_S4:
85 Context->Sx.Type = SxS4;
86 break;
87
88 case V_PCH_ACPI_PM1_CNT_S5:
89 Context->Sx.Type = SxS5;
90 break;
91
92 default:
93 ASSERT (FALSE);
94 break;
95 }
96 }
97
98 /**
99 Check whether sleep type of two contexts match
100
101 @param[in] Context1 Context 1 that includes sleep type 1
102 @param[in] Context2 Context 2 that includes sleep type 2
103
104 @retval FALSE Sleep types match
105 @retval TRUE Sleep types don't match
106 **/
107 BOOLEAN
108 EFIAPI
SxCmpContext(IN PCH_SMM_CONTEXT * Context1,IN PCH_SMM_CONTEXT * Context2)109 SxCmpContext (
110 IN PCH_SMM_CONTEXT *Context1,
111 IN PCH_SMM_CONTEXT *Context2
112 )
113 {
114 return (BOOLEAN) (Context1->Sx.Type == Context2->Sx.Type);
115 }
116
117 /**
118 For each PCIE RP clear PME SCI status and disable SCI, then PCIEXP_WAKE_STS from PMC.
119 This prevents platform from waking more than one time due to a single PCIE wake event.
120 Normally it's up to OS to clear SCI statuses. But in a scenario where platform wakes
121 and goes to S5 instead of booting to OS, the SCI status would remain set and would trigger another wake.
122 **/
123 VOID
ClearPcieSci(VOID)124 ClearPcieSci (
125 VOID
126 )
127 {
128 UINT32 MaxPorts;
129 UINT32 RpIndex;
130 UINTN RpBase;
131
132 MaxPorts = GetPchMaxPciePortNum ();
133 for (RpIndex = 0; RpIndex < MaxPorts; RpIndex++) {
134 RpBase = PchPcieBase (RpIndex);
135 if (MmioRead16 (RpBase + PCI_VENDOR_ID_OFFSET) != 0xFFFF) {
136 MmioAnd8 ((RpBase + R_PCH_PCIE_MPC + 3), (UINT8)~((UINT8)(B_PCH_PCIE_MPC_PMCE >> 24)));
137 MmioWrite32 (RpBase + R_PCH_PCIE_SMSCS, B_PCH_PCIE_SMSCS_PMCS);
138 }
139 }
140 IoWrite16 (mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, B_PCH_ACPI_PM1_STS_PCIEXP_WAKE_STS);
141 }
142
143
144 /**
145 When we get an SMI that indicates that we are transitioning to a sleep state,
146 we need to actually transition to that state. We do this by disabling the
147 "SMI on sleep enable" feature, which generates an SMI when the operating system
148 tries to put the system to sleep, and then physically putting the system to sleep.
149
150
151 **/
152 VOID
PchSmmSxGoToSleep(VOID)153 PchSmmSxGoToSleep (
154 VOID
155 )
156 {
157 UINT32 Pm1Cnt;
158 UINT32 PchPwrmBase;
159
160 ClearPcieSci ();
161
162 PchPwrmBaseGet (&PchPwrmBase);
163
164 ///
165 /// Flush cache into memory before we go to sleep. It is necessary for S3 sleep
166 /// because we may update memory in SMM Sx sleep handlers -- the updates are in cache now
167 ///
168 AsmWbinvd ();
169
170 ///
171 /// Disable SMIs
172 ///
173 PchSmmClearSource (&SX_SOURCE_DESC);
174 PchSmmDisableSource (&SX_SOURCE_DESC);
175
176 ///
177 /// Get Power Management 1 Control Register Value
178 ///
179 Pm1Cnt = IoRead32 ((UINTN) (mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT));
180
181 ///
182 /// Record S3 suspend performance data
183 ///
184 if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S3) {
185 ///
186 /// Report status code before goto S3 sleep
187 ///
188 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PROGRESS_CODE_S3_SUSPEND_END);
189
190 ///
191 /// Flush cache into memory before we go to sleep.
192 ///
193 AsmWbinvd ();
194 }
195
196 ///
197 /// Now that SMIs are disabled, write to the SLP_EN bit again to trigger the sleep
198 ///
199 Pm1Cnt |= B_PCH_ACPI_PM1_CNT_SLP_EN;
200
201 IoWrite32 ((UINTN) (mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT), Pm1Cnt);
202
203 ///
204 /// Should only proceed if wake event is generated.
205 ///
206 if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S1) {
207 while (((IoRead16 ((UINTN) (mAcpiBaseAddr + R_PCH_ACPI_PM1_STS))) & B_PCH_ACPI_PM1_STS_WAK) == 0x0);
208 } else {
209 CpuDeadLoop ();
210 }
211 ///
212 /// The system just went to sleep. If the sleep state was S1, then code execution will resume
213 /// here when the system wakes up.
214 ///
215 Pm1Cnt = IoRead32 ((UINTN) (mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT));
216
217 if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SCI_EN) == 0) {
218 ///
219 /// An ACPI OS isn't present, clear the sleep information
220 ///
221 Pm1Cnt &= ~B_PCH_ACPI_PM1_CNT_SLP_TYP;
222 Pm1Cnt |= V_PCH_ACPI_PM1_CNT_S0;
223
224 IoWrite32 ((UINTN) (mAcpiBaseAddr + R_PCH_ACPI_PM1_CNT), Pm1Cnt);
225 }
226
227 PchSmmClearSource (&SX_SOURCE_DESC);
228 PchSmmEnableSource (&SX_SOURCE_DESC);
229 }
230