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