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