1 /** @file
2   File to contain all the hardware specific stuff for the Smm Sx dispatch protocol.
3 
4   Copyright (c) 2019 Intel Corporation. All rights reserved. <BR>
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8 
9 #include "PchSmmHelpers.h"
10 #include <Register/PchRegsPmc.h>
11 #include <Register/PchRegsPcie.h>
12 
13 #define PROGRESS_CODE_S3_SUSPEND_END  PcdGet32 (PcdProgressCodeS3SuspendEnd)
14 
15 GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSxSourceDesc = {
16   PCH_SMM_NO_FLAGS,
17   {
18     {
19       {
20         ACPI_ADDR_TYPE,
21         {R_ACPI_IO_SMI_EN}
22       },
23       S_ACPI_IO_SMI_EN,
24       N_ACPI_IO_SMI_EN_ON_SLP_EN
25     },
26     NULL_BIT_DESC_INITIALIZER
27   },
28   {
29     {
30       {
31         ACPI_ADDR_TYPE,
32         {R_ACPI_IO_SMI_STS}
33       },
34       S_ACPI_IO_SMI_STS,
35       N_ACPI_IO_SMI_STS_ON_SLP_EN
36     }
37   },
38   {
39     {
40       ACPI_ADDR_TYPE,
41       {R_ACPI_IO_SMI_STS}
42     },
43     S_ACPI_IO_SMI_STS,
44     N_ACPI_IO_SMI_STS_ON_SLP_EN
45   }
46 };
47 
48 /**
49   Get the Sleep type
50 
51   @param[in] Record               No use
52   @param[out] Context             The context that includes SLP_TYP bits to be filled
53 
54 **/
55 VOID
56 EFIAPI
57 SxGetContext (
58   IN  DATABASE_RECORD    *Record,
59   OUT PCH_SMM_CONTEXT    *Context
60   )
61 {
62   UINT32  Pm1Cnt;
63 
64   Pm1Cnt = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT));
65 
66   ///
67   /// By design, the context phase will always be ENTRY
68   ///
69   Context->Sx.Phase = SxEntry;
70 
71   ///
72   /// Map the PM1_CNT register's SLP_TYP bits to the context type
73   ///
74   switch (Pm1Cnt & B_ACPI_IO_PM1_CNT_SLP_TYP) {
75     case V_ACPI_IO_PM1_CNT_S0:
76       Context->Sx.Type = SxS0;
77       break;
78 
79     case V_ACPI_IO_PM1_CNT_S1:
80       Context->Sx.Type = SxS1;
81       break;
82 
83     case V_ACPI_IO_PM1_CNT_S3:
84       Context->Sx.Type = SxS3;
85       break;
86 
87     case V_ACPI_IO_PM1_CNT_S4:
88       Context->Sx.Type = SxS4;
89       break;
90 
91     case V_ACPI_IO_PM1_CNT_S5:
92       Context->Sx.Type = SxS5;
93       break;
94 
95     default:
96       ASSERT (FALSE);
97       break;
98   }
99 }
100 
101 /**
102   Check whether sleep type of two contexts match
103 
104   @param[in] Context1             Context 1 that includes sleep type 1
105   @param[in] Context2             Context 2 that includes sleep type 2
106 
107   @retval FALSE                   Sleep types match
108   @retval TRUE                    Sleep types don't match
109 **/
110 BOOLEAN
111 EFIAPI
112 SxCmpContext (
113   IN PCH_SMM_CONTEXT     *Context1,
114   IN PCH_SMM_CONTEXT     *Context2
115   )
116 {
117   return (BOOLEAN) (Context1->Sx.Type == Context2->Sx.Type);
118 }
119 
120 /**
121   For each PCIE RP clear PME SCI status and disable SCI, then PCIEXP_WAKE_STS from PMC.
122   This prevents platform from waking more than one time due to a single PCIE wake event.
123   Normally it's up to OS to clear SCI statuses. But in a scenario where platform wakes
124   and goes to S5 instead of booting to OS, the SCI status would remain set and would trigger another wake.
125 **/
126 VOID
127 ClearPcieSci (
128   VOID
129   )
130 {
131   UINT32 MaxPorts;
132   UINT32 RpIndex;
133   UINT64 RpBase;
134 
135   MaxPorts = GetPchMaxPciePortNum ();
136   for (RpIndex = 0; RpIndex < MaxPorts; RpIndex++) {
137     RpBase = PchPcieBase (RpIndex);
138     if (PciSegmentRead16 (RpBase + PCI_VENDOR_ID_OFFSET) != 0xFFFF) {
139       PciSegmentAnd8 ((RpBase + R_PCH_PCIE_CFG_MPC + 3), (UINT8)~((UINT8)(B_PCH_PCIE_CFG_MPC_PMCE >> 24)));
140       PciSegmentWrite32 (RpBase + R_PCH_PCIE_CFG_SMSCS, B_PCH_PCIE_CFG_SMSCS_PMCS);
141     }
142   }
143   IoWrite16 (mAcpiBaseAddr + R_ACPI_IO_PM1_STS, B_ACPI_IO_PM1_STS_PCIEXP_WAKE_STS);
144 }
145 
146 
147 /**
148   When we get an SMI that indicates that we are transitioning to a sleep state,
149   we need to actually transition to that state.  We do this by disabling the
150   "SMI on sleep enable" feature, which generates an SMI when the operating system
151   tries to put the system to sleep, and then physically putting the system to sleep.
152 
153 
154 **/
155 VOID
156 PchSmmSxGoToSleep (
157   VOID
158   )
159 {
160   UINT32      Pm1Cnt;
161 
162   ClearPcieSci ();
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 (&mSxSourceDesc);
174   PchSmmDisableSource (&mSxSourceDesc);
175 
176   ///
177   /// Get Power Management 1 Control Register Value
178   ///
179   Pm1Cnt = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT));
180 
181   ///
182   /// Record S3 suspend performance data
183   ///
184   if ((Pm1Cnt & B_ACPI_IO_PM1_CNT_SLP_TYP) == V_ACPI_IO_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_ACPI_IO_PM1_CNT_SLP_EN;
200 
201   IoWrite32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT), Pm1Cnt);
202 
203   ///
204   /// Should only proceed if wake event is generated.
205   ///
206   if ((Pm1Cnt & B_ACPI_IO_PM1_CNT_SLP_TYP) == V_ACPI_IO_PM1_CNT_S1) {
207     while (((IoRead16 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_STS))) & B_ACPI_IO_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_ACPI_IO_PM1_CNT));
216 
217   if ((Pm1Cnt & B_ACPI_IO_PM1_CNT_SCI_EN) == 0) {
218     ///
219     /// An ACPI OS isn't present, clear the sleep information
220     ///
221     Pm1Cnt &= ~B_ACPI_IO_PM1_CNT_SLP_TYP;
222     Pm1Cnt |= V_ACPI_IO_PM1_CNT_S0;
223 
224     IoWrite32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT), Pm1Cnt);
225   }
226 
227   PchSmmClearSource (&mSxSourceDesc);
228   PchSmmEnableSource (&mSxSourceDesc);
229 }
230