1 /** @file
2   This file contains GPIO routines for RC usage
3 
4 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 #include <Base.h>
9 #include <Uefi/UefiBaseType.h>
10 #include <Library/IoLib.h>
11 #include <Library/DebugLib.h>
12 #include <Library/BaseMemoryLib.h>
13 #include <PchAccess.h>
14 #include <Library/GpioLib.h>
15 #include <Library/GpioNativeLib.h>
16 #include <Library/GpioPrivateLib.h>
17 #include <Library/PchCycleDecodingLib.h>
18 #include <Pch/Library/PeiDxeSmmGpioLib/GpioLibrary.h>
19 /**
20   This procedure will get value of selected gpio register
21 
22   @param[in]  Group               GPIO group number
23   @param[in]  Offset              GPIO register offset
24   @param[out] RegVal              Value of gpio register
25 
26   @retval EFI_SUCCESS             The function completed successfully
27   @retval EFI_INVALID_PARAMETER   Invalid group or pad number
28 **/
29 EFI_STATUS
30 GpioGetReg (
31   IN  GPIO_GROUP              Group,
32   IN  UINT32                  Offset,
33   OUT UINT32                  *RegVal
34   )
35 {
36   GPIO_GROUP_INFO      *GpioGroupInfo;
37   UINTN                GpioGroupInfoLength;
38   UINTN                GroupIndex;
39 
40   GroupIndex = GpioGetGroupIndexFromGroup (Group);
41   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
42   //
43   // Check if group argument exceeds GPIO GROUP INFO array
44   //
45   if ((UINTN) GroupIndex >= GpioGroupInfoLength) {
46     ASSERT (FALSE);
47     return EFI_INVALID_PARAMETER;
48   }
49 
50   *RegVal = MmioRead32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, Offset));
51 
52   return EFI_SUCCESS;
53 }
54 
55 /**
56   This procedure will set value of selected gpio register
57 
58   @param[in] Group               GPIO group number
59   @param[in] Offset              GPIO register offset
60   @param[in] RegVal              Value of gpio register
61 
62   @retval EFI_SUCCESS            The function completed successfully
63   @retval EFI_INVALID_PARAMETER  Invalid group or pad number
64 **/
65 EFI_STATUS
66 GpioSetReg (
67   IN GPIO_GROUP              Group,
68   IN UINT32                  Offset,
69   IN UINT32                  RegVal
70   )
71 {
72   GPIO_GROUP_INFO      *GpioGroupInfo;
73   UINTN                GpioGroupInfoLength;
74   UINTN                GroupIndex;
75 
76   GroupIndex = GpioGetGroupIndexFromGroup (Group);
77   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
78   //
79   // Check if group argument exceeds GPIO GROUP INFO array
80   //
81   if ((UINTN) GroupIndex >= GpioGroupInfoLength) {
82     ASSERT (FALSE);
83     return EFI_INVALID_PARAMETER;
84   }
85 
86   MmioWrite32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, Offset), RegVal);
87 
88   return EFI_SUCCESS;
89 }
90 
91 /**
92   This procedure is used by PchSmiDispatcher and will return information
93   needed to register GPI SMI. Relation between Index and GpioPad number is:
94   Index = GpioGroup + 24 * GpioPad
95 
96   @param[in]  Index               GPI SMI number
97   @param[out] GpioPin             GPIO pin
98   @param[out] GpiSmiBitOffset     GPI SMI bit position within GpiSmi Registers
99   @param[out] GpiSmiEnRegAddress  Address of GPI SMI Enable register
100   @param[out] GpiSmiStsRegAddress Address of GPI SMI status register
101 
102   @retval EFI_SUCCESS             The function completed successfully
103   @retval EFI_INVALID_PARAMETER   Invalid group or pad number
104 **/
105 EFI_STATUS
106 GpioGetPadAndSmiRegs (
107   IN UINT32            Index,
108   OUT GPIO_PAD         *GpioPin,
109   OUT UINT8            *GpiSmiBitOffset,
110   OUT UINT32           *GpiSmiEnRegAddress,
111   OUT UINT32           *GpiSmiStsRegAddress
112   )
113 {
114   UINT32               GroupIndex;
115   UINT32               PadNumber;
116   GPIO_GROUP_INFO      *GpioGroupInfo;
117   GPIO_GROUP           GpioGroupOffset;
118   UINTN                GpioGroupInfoLength;
119   UINT32               SmiRegOffset;
120 
121   GPIO_PAD_OWN       PadOwnVal;
122   UINT32             HostOwnVal;
123 
124 
125   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
126 
127   GpioGroupOffset = GpioGetLowestGroup ();
128 
129   PadNumber = 0;
130   GroupIndex = 0;
131   for (GroupIndex = 0; GroupIndex < GpioGroupInfoLength; GroupIndex++) {
132     PadNumber = Index;
133     if (PadNumber < GpioGroupInfo[GroupIndex].PadPerGroup) {
134       //
135       // Found group and pad number
136       //
137       break;
138     }
139     Index = Index - GpioGroupInfo[GroupIndex].PadPerGroup;
140   }
141 
142   //
143   // Check if legal pad number
144   //
145   if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup){
146     return EFI_INVALID_PARAMETER;
147   }
148 
149   //
150   // Check if selected group has GPI SMI Enable and Status registers
151   //
152   if (GpioGroupInfo[GroupIndex].SmiEnOffset == NO_REGISTER_FOR_PROPERTY) {
153     return EFI_INVALID_PARAMETER;
154   }
155 
156   DEBUG_CODE_BEGIN ();
157   //
158   // Check if selected GPIO Pad is not owned by CSME/ISH/IE
159   //
160   GpioGetPadOwnership (GpioGetGpioPadFromGroupAndPadNumber (GroupIndex + GpioGroupOffset, PadNumber), &PadOwnVal);
161   if (PadOwnVal != GpioPadOwnHost) {
162     DEBUG ((DEBUG_ERROR, "GPIO ERROR: Accessing pad not owned by host (Group=%d, Pad=%d)!\n",GroupIndex, PadNumber));
163     return EFI_INVALID_PARAMETER;
164   }
165   //
166   // Check if Host Software Pad Ownership is set to ACPI Mode
167   //
168   GpioGetHostSwOwnershipForPad (GpioGetGpioPadFromGroupAndPadNumber (GroupIndex + GpioGroupOffset, PadNumber), &HostOwnVal);
169   if (HostOwnVal != V_PCH_PCR_GPIO_HOSTSW_OWN_ACPI) {
170     ASSERT (FALSE);
171     return EFI_INVALID_PARAMETER;
172   }
173 
174   DEBUG_CODE_END ();
175 
176   *GpioPin = GpioGetGpioPadFromGroupAndPadNumber (GroupIndex + GpioGroupOffset, PadNumber);
177   *GpiSmiBitOffset = (UINT8)(PadNumber % 32);
178 
179   SmiRegOffset = GpioGroupInfo[GroupIndex].SmiEnOffset + (PadNumber / 32) * 0x4;
180   *GpiSmiEnRegAddress = PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, SmiRegOffset);
181 
182   SmiRegOffset = GpioGroupInfo[GroupIndex].SmiStsOffset + (PadNumber / 32) * 0x4;
183   *GpiSmiStsRegAddress = PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, SmiRegOffset);
184 
185   return EFI_SUCCESS;
186 }
187 
188 /**
189   This procedure will clear GPIO_UNLOCK_SMI_STS
190 
191   @param[in]  None
192 
193   @retval EFI_SUCCESS             The function completed successfully
194   @retval EFI_INVALID_PARAMETER   Invalid group or pad number
195 **/
196 EFI_STATUS
197 GpioClearUnlockSmiSts (
198   VOID
199   )
200 {
201   UINT16  AcpiBaseAddr;
202 
203   PchAcpiBaseGet (&AcpiBaseAddr);
204 
205   //
206   // GPIO_UNLOCK_SMI_STS is cleared by writing 1 to it. Other bits in
207   // R_PCH_SMI_STS registers are either RO or RW/1C so writing 0 to them
208   // will not change their state.
209   //
210   IoWrite32 (AcpiBaseAddr + R_PCH_SMI_STS, B_PCH_SMI_STS_GPIO_UNLOCK);
211 
212   return EFI_SUCCESS;
213 }
214 
215 
216 /**
217   This procedure will set GPIO Driver IRQ number
218 
219   @param[in]  Irq                 Irq number
220 
221   @retval EFI_SUCCESS             The function completed successfully
222   @retval EFI_INVALID_PARAMETER   Invalid IRQ number
223 **/
224 EFI_STATUS
225 GpioSetIrq (
226   IN  UINT8          Irq
227   )
228 {
229   UINT32   Data32And;
230   UINT32   Data32Or;
231 
232   //
233   // Check if Irq is 14 or 15
234   //
235   if ((Irq < 14) || (Irq > 15)) {
236     ASSERT (FALSE);
237     return EFI_INVALID_PARAMETER;
238   }
239 
240   Data32And = (UINT32) ~(B_PCH_PCR_GPIO_MISCCFG_IRQ_ROUTE);
241   Data32Or  = (UINT32) (Irq - 14) << N_PCH_PCR_GPIO_MISCCFG_IRQ_ROUTE;
242 
243   //
244   // Program MISCCFG register for Community 0
245   //
246   MmioAndThenOr32 (
247     PCH_PCR_ADDRESS (PID_GPIOCOM0, R_PCH_PCR_GPIO_MISCCFG),
248     Data32And,
249     Data32Or
250     );
251 
252   //
253   // Program MISCCFG register for Community 1
254   //
255   MmioAndThenOr32 (
256     PCH_PCR_ADDRESS (PID_GPIOCOM1, R_PCH_PCR_GPIO_MISCCFG),
257     Data32And,
258     Data32Or
259     );
260 
261   //
262   // Program MISCCFG register for Community 2
263   //
264   MmioAndThenOr32 (
265     PCH_PCR_ADDRESS (PID_GPIOCOM2, R_PCH_PCR_GPIO_MISCCFG),
266     Data32And,
267     Data32Or
268     );
269 
270   //
271   // Program MISCCFG register for Community 3
272   //
273   MmioAndThenOr32 (
274     PCH_PCR_ADDRESS (PID_GPIOCOM3, R_PCH_PCR_GPIO_MISCCFG),
275     Data32And,
276     Data32Or
277     );
278 
279   return EFI_SUCCESS;
280 }
281 
282 /**
283   This procedure will perform special handling of GPP_A_12 on PCH-LP.
284 
285   @param[in]  None
286 
287   @retval None
288 **/
289 VOID
290 GpioA12SpecialHandling (
291   VOID
292   )
293 {
294   GPIO_PAD_OWN         PadOwnVal;
295 
296   //
297   // SKL PCH BWG 16.6. PCH-LP GPP_A_12 Special Handling
298   //
299   if (GetPchSeries () == PchLp) {
300     GpioGetPadOwnership (GPIO_SKL_LP_GPP_A12, &PadOwnVal);
301 
302     //
303     // If the pad is host-own, BIOS has to always lock this pad after being initialized
304     //
305     if (PadOwnVal == GpioPadOwnHost) {
306       //
307       // Set PadCfgLock for GPP_A_12
308       //
309       GpioLockPadCfg (GPIO_SKL_LP_GPP_A12);
310     }
311   }
312 }
313