1 /** @file
2   This file contains GPIO routines for RC usage
3 
4   Copyright (c) 2019 Intel Corporation. All rights reserved. <BR>
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8 
9 #include <Base.h>
10 #include <Uefi/UefiBaseType.h>
11 #include <Library/IoLib.h>
12 #include <Library/DebugLib.h>
13 #include <Library/BaseMemoryLib.h>
14 #include <Library/GpioLib.h>
15 #include <Library/GpioNativeLib.h>
16 #include <Private/Library/GpioPrivateLib.h>
17 #include <Pch/Library/PeiDxeSmmGpioLib/GpioLibrary.h>
18 #include <Private/Library/GpioNameBufferLib.h>
19 #include <Register/PchRegsPcr.h>
20 
21 #include "GpioNativePrivateLibInternal.h"
22 
23 /**
24   This procedure is used to check if GpioPad is valid for certain chipset
25 
26   @param[in]  GpioPad             GPIO pad
27 
28   @retval TRUE                    This pin is valid on this chipset
29           FALSE                   Incorrect pin
30 **/
31 BOOLEAN
GpioIsCorrectPadForThisChipset(IN GPIO_PAD GpioPad)32 GpioIsCorrectPadForThisChipset (
33   IN  GPIO_PAD        GpioPad
34   )
35 {
36   return ((GPIO_GET_CHIPSET_ID (GpioPad) == GpioGetThisChipsetId ()) &&
37          (GpioGetGroupIndexFromGpioPad (GpioPad) < GpioGetNumberOfGroups ()));
38 }
39 
40 /**
41   This procedure will get value of selected gpio register
42 
43   @param[in]  Group               GPIO group number
44   @param[in]  Offset              GPIO register offset
45   @param[out] RegVal              Value of gpio register
46 
47   @retval EFI_SUCCESS             The function completed successfully
48   @retval EFI_INVALID_PARAMETER   Invalid group or pad number
49 **/
50 EFI_STATUS
GpioGetReg(IN GPIO_GROUP Group,IN UINT32 Offset,OUT UINT32 * RegVal)51 GpioGetReg (
52   IN  GPIO_GROUP              Group,
53   IN  UINT32                  Offset,
54   OUT UINT32                  *RegVal
55   )
56 {
57   CONST GPIO_GROUP_INFO  *GpioGroupInfo;
58   UINT32                 GpioGroupInfoLength;
59   UINT32                 GroupIndex;
60 
61   GroupIndex = GpioGetGroupIndexFromGroup (Group);
62   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
63   //
64   // Check if group argument exceeds GPIO GROUP INFO array
65   //
66   if ((UINTN) GroupIndex >= GpioGroupInfoLength) {
67     ASSERT (FALSE);
68     return EFI_INVALID_PARAMETER;
69   }
70 
71   *RegVal = MmioRead32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, Offset));
72 
73   return EFI_SUCCESS;
74 }
75 
76 /**
77   This procedure will set value of selected gpio register
78 
79   @param[in] Group               GPIO group number
80   @param[in] Offset              GPIO register offset
81   @param[in] RegVal              Value of gpio register
82 
83   @retval EFI_SUCCESS            The function completed successfully
84   @retval EFI_INVALID_PARAMETER  Invalid group or pad number
85 **/
86 EFI_STATUS
GpioSetReg(IN GPIO_GROUP Group,IN UINT32 Offset,IN UINT32 RegVal)87 GpioSetReg (
88   IN GPIO_GROUP              Group,
89   IN UINT32                  Offset,
90   IN UINT32                  RegVal
91   )
92 {
93   CONST GPIO_GROUP_INFO  *GpioGroupInfo;
94   UINT32                 GpioGroupInfoLength;
95   UINT32                 GroupIndex;
96 
97   GroupIndex = GpioGetGroupIndexFromGroup (Group);
98   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
99   //
100   // Check if group argument exceeds GPIO GROUP INFO array
101   //
102   if ((UINTN) GroupIndex >= GpioGroupInfoLength) {
103     ASSERT (FALSE);
104     return EFI_INVALID_PARAMETER;
105   }
106 
107   MmioWrite32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, Offset), RegVal);
108 
109   return EFI_SUCCESS;
110 }
111 
112 /**
113   This procedure is used by PchSmiDispatcher and will return information
114   needed to register GPI SMI.
115 
116   @param[in]  Index                   GPI SMI number
117   @param[out] GpioPin                 GPIO pin
118   @param[out] GpiSmiBitOffset         GPI SMI bit position within GpiSmi Registers
119   @param[out] GpiHostSwOwnRegAddress  Address of HOSTSW_OWN register
120   @param[out] GpiSmiStsRegAddress     Address of GPI SMI status register
121 
122   @retval EFI_SUCCESS             The function completed successfully
123   @retval EFI_INVALID_PARAMETER   Invalid group or pad number
124 **/
125 EFI_STATUS
GpioGetPadAndSmiRegs(IN UINT32 Index,OUT GPIO_PAD * GpioPin,OUT UINT8 * GpiSmiBitOffset,OUT UINT32 * GpiHostSwOwnRegAddress,OUT UINT32 * GpiSmiStsRegAddress)126 GpioGetPadAndSmiRegs (
127   IN UINT32            Index,
128   OUT GPIO_PAD         *GpioPin,
129   OUT UINT8            *GpiSmiBitOffset,
130   OUT UINT32           *GpiHostSwOwnRegAddress,
131   OUT UINT32           *GpiSmiStsRegAddress
132   )
133 {
134   UINT32                 GroupIndex;
135   UINT32                 PadNumber;
136   CONST GPIO_GROUP_INFO  *GpioGroupInfo;
137   GPIO_GROUP             GpioGroup;
138   UINT32                 GpioGroupInfoLength;
139   UINT32                 SmiStsRegOffset;
140   UINT32                 HostSwOwnRegOffset;
141   GPIO_PAD_OWN           PadOwnVal;
142 
143   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
144 
145   PadNumber = 0;
146   GroupIndex = 0;
147   for (GroupIndex = 0; GroupIndex < GpioGroupInfoLength; GroupIndex++) {
148     PadNumber = Index;
149     if (PadNumber < GpioGroupInfo[GroupIndex].PadPerGroup) {
150       //
151       // Found group and pad number
152       //
153       break;
154     }
155     Index = Index - GpioGroupInfo[GroupIndex].PadPerGroup;
156   }
157 
158   //
159   // Check if legal pad number
160   //
161   if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup){
162     return EFI_INVALID_PARAMETER;
163   }
164 
165   //
166   // Check if selected group has GPI SMI Enable and Status registers
167   //
168   if (GpioGroupInfo[GroupIndex].SmiEnOffset == NO_REGISTER_FOR_PROPERTY) {
169     return EFI_INVALID_PARAMETER;
170   }
171 
172   GpioGroup = GpioGetGroupFromGroupIndex (GroupIndex);
173   *GpioPin = GpioGetGpioPadFromGroupAndPadNumber (GpioGroup, PadNumber);
174 
175   DEBUG_CODE_BEGIN ();
176   //
177   // Check if selected GPIO Pad is not owned by CSME/ISH/IE
178   //
179   GpioGetPadOwnership (*GpioPin, &PadOwnVal);
180   if (PadOwnVal != GpioPadOwnHost) {
181     DEBUG ((DEBUG_ERROR, "GPIO ERROR: %a not owned by host!\n", GpioName (*GpioPin)));
182     return EFI_INVALID_PARAMETER;
183   }
184   DEBUG_CODE_END ();
185 
186   *GpiSmiBitOffset = (UINT8)(PadNumber % 32);
187 
188   HostSwOwnRegOffset = GpioGroupInfo[GroupIndex].HostOwnOffset + (PadNumber / 32) * 0x4;
189   *GpiHostSwOwnRegAddress = PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, HostSwOwnRegOffset);
190 
191   SmiStsRegOffset = GpioGroupInfo[GroupIndex].SmiStsOffset + (PadNumber / 32) * 0x4;
192   *GpiSmiStsRegAddress = PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, SmiStsRegOffset);
193 
194   return EFI_SUCCESS;
195 }
196 
197 /**
198   This procedure will set GPIO Driver IRQ number
199 
200   @param[in]  Irq                 Irq number
201 
202   @retval EFI_SUCCESS             The function completed successfully
203   @retval EFI_INVALID_PARAMETER   Invalid IRQ number
204 **/
205 EFI_STATUS
GpioSetIrq(IN UINT8 Irq)206 GpioSetIrq (
207   IN  UINT8          Irq
208   )
209 {
210   UINT32       Data32And;
211   UINT32       Data32Or;
212   PCH_SBI_PID  *GpioComSbiIds;
213   UINT32       NoOfGpioComs;
214   UINT32       GpioComIndex;
215 
216   Data32And = (UINT32)~(B_GPIO_PCR_MISCCFG_IRQ_ROUTE);
217   Data32Or  = (UINT32)Irq << N_GPIO_PCR_MISCCFG_IRQ_ROUTE;
218 
219   NoOfGpioComs = GpioGetComSbiPortIds (&GpioComSbiIds);
220 
221   //
222   // Program MISCCFG register for each community
223   //
224   for (GpioComIndex = 0; GpioComIndex < NoOfGpioComs; GpioComIndex++) {
225     MmioAndThenOr32 (
226       PCH_PCR_ADDRESS (GpioComSbiIds[GpioComIndex], R_GPIO_PCR_MISCCFG),
227       Data32And,
228       Data32Or
229       );
230   }
231 
232   return EFI_SUCCESS;
233 }
234 
235 /**
236   This procedure will return Port ID of GPIO Community from GpioPad
237 
238   @param[in] GpioPad            GpioPad
239 
240   @retval GpioCommunityPortId   Port ID of GPIO Community
241 **/
242 UINT8
GpioGetGpioCommunityPortIdFromGpioPad(IN GPIO_PAD GpioPad)243 GpioGetGpioCommunityPortIdFromGpioPad (
244   IN GPIO_PAD        GpioPad
245   )
246 {
247   CONST GPIO_GROUP_INFO  *GpioGroupInfo;
248   UINT32                 GpioGroupInfoLength;
249   UINT32                 GroupIndex;
250 
251   GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad);
252 
253   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
254 
255   return GpioGroupInfo[GroupIndex].Community;
256 }
257 
258 /**
259   This procedure will return PadCfg address from GpioPad
260 
261   @param[in] GpioPad            GpioPad
262 
263   @retval GpioPadCfgAddress     PadCfg Address of GpioPad
264 **/
265 UINT32
GpioGetGpioPadCfgAddressFromGpioPad(IN GPIO_PAD GpioPad)266 GpioGetGpioPadCfgAddressFromGpioPad (
267   IN GPIO_PAD        GpioPad
268   )
269 {
270   UINT32                 PadCfgRegAddress;
271   CONST GPIO_GROUP_INFO  *GpioGroupInfo;
272   UINT32                 GpioGroupInfoLength;
273   UINT32                 GroupIndex;
274   UINT32                 PadNumber;
275 
276   GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad);
277   PadNumber = GpioGetPadNumberFromGpioPad (GpioPad);
278 
279   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
280 
281   //
282   // Create Pad Configuration register offset
283   //
284   PadCfgRegAddress = GpioGroupInfo[GroupIndex].PadCfgOffset + S_GPIO_PCR_PADCFG * PadNumber;
285 
286   return PadCfgRegAddress;
287 }
288 
289 
290 /**
291   This procedure will check if GpioPad is owned by host.
292 
293   @param[in] GpioPad       GPIO pad
294 
295   @retval TRUE             GPIO pad is owned by host
296   @retval FALSE            GPIO pad is not owned by host and should not be used with GPIO lib API
297 **/
298 BOOLEAN
GpioIsPadHostOwned(IN GPIO_PAD GpioPad)299 GpioIsPadHostOwned (
300   IN GPIO_PAD             GpioPad
301   )
302 {
303   GPIO_PAD_OWN         PadOwnVal;
304 
305   //
306   // Check if selected GPIO Pad is not owned by CSME/ISH
307   // If GPIO is not owned by Host all access to PadCfg will be dropped
308   //
309   GpioGetPadOwnership (GpioPad, &PadOwnVal);
310   if (PadOwnVal != GpioPadOwnHost) {
311     DEBUG ((DEBUG_ERROR, "GPIO ERROR: %a is not owned by host!\n", GpioName (GpioPad)));
312     return FALSE;
313   }
314 
315   return TRUE;
316 }
317 
318 /**
319   This procedure will check if GpioPad argument is valid.
320   Function will check below conditions:
321    - GpioPad represents a pad for current PCH
322    - GpioPad belongs to valid GpioGroup
323    - GPIO PadNumber is not greater than number of pads for this group
324 
325   @param[in] GpioPad       GPIO pad
326 
327   @retval TRUE             GPIO pad is valid and can be used with GPIO lib API
328   @retval FALSE            GPIO pad is invalid and cannot be used with GPIO lib API
329 **/
330 BOOLEAN
GpioIsPadValid(IN GPIO_PAD GpioPad)331 GpioIsPadValid (
332   IN GPIO_PAD             GpioPad
333   )
334 {
335   CONST GPIO_GROUP_INFO  *GpioGroupInfo;
336   UINT32                 GpioGroupInfoLength;
337   UINT32                 PadNumber;
338 
339   if (!GpioIsCorrectPadForThisChipset (GpioPad)) {
340     DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad (0x%08x) used on this chipset!\n", GpioPad));
341     goto Error;
342   }
343 
344   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
345 
346   //
347   // Check if legal pin number
348   //
349   PadNumber = GpioGetPadNumberFromGpioPad (GpioPad);
350   if (PadNumber >= GpioGroupInfo[GpioGetGroupIndexFromGpioPad (GpioPad)].PadPerGroup) {
351     DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber));
352     goto Error;
353   }
354 
355   return TRUE;
356 Error:
357   ASSERT (FALSE);
358   return FALSE;
359 }
360 
361 /**
362   This procedure will read GPIO Pad Configuration register
363 
364   @param[in] GpioPad          GPIO pad
365   @param[in] DwReg            Choose PADCFG register: 0:DW0, 1:DW1
366 
367   @retval PadCfgRegValue      PADCFG_DWx value
368 **/
369 UINT32
GpioReadPadCfgReg(IN GPIO_PAD GpioPad,IN UINT8 DwReg)370 GpioReadPadCfgReg (
371   IN GPIO_PAD             GpioPad,
372   IN UINT8                DwReg
373   )
374 {
375   UINT32                 PadCfgReg;
376   CONST GPIO_GROUP_INFO  *GpioGroupInfo;
377   UINT32                 GpioGroupInfoLength;
378   UINT32                 GroupIndex;
379   UINT32                 PadNumber;
380 
381   GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad);
382   PadNumber = GpioGetPadNumberFromGpioPad (GpioPad);
383 
384   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
385 
386   //
387   // Create Pad Configuration register offset
388   //
389   PadCfgReg = GpioGroupInfo[GroupIndex].PadCfgOffset + S_GPIO_PCR_PADCFG * PadNumber + 0x4 * DwReg;
390 
391   return MmioRead32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg));
392 }
393 
394 /**
395   This procedure will write or read GPIO Pad Configuration register
396 
397   @param[in] GpioPad              GPIO pad
398   @param[in] DwReg                Choose PADCFG register: 0:DW0, 1:DW1
399   @param[in] PadCfgAndMask        Mask to be AND'ed with PADCFG reg value
400   @param[in] PadCfgOrMask         Mask to be OR'ed with PADCFG reg value
401 
402   @retval none
403 **/
404 VOID
GpioWritePadCfgReg(IN GPIO_PAD GpioPad,IN UINT8 DwReg,IN UINT32 PadCfgAndMask,IN UINT32 PadCfgOrMask)405 GpioWritePadCfgReg (
406   IN GPIO_PAD             GpioPad,
407   IN UINT8                DwReg,
408   IN UINT32               PadCfgAndMask,
409   IN UINT32               PadCfgOrMask
410   )
411 {
412   UINT32                 PadCfgReg;
413   CONST GPIO_GROUP_INFO  *GpioGroupInfo;
414   UINT32                 GpioGroupInfoLength;
415   UINT32                 GroupIndex;
416   UINT32                 PadNumber;
417   UINT32                 PadCfgLock;
418   UINT32                 PadCfgLockTx;
419 
420   PadCfgLock = 0;
421   PadCfgLockTx = 0;
422 
423   //
424   // Check if Pad Configuration (except output state) is to be changed.
425   // If AND and OR masks will indicate that configuration fields (other than output control)
426   // are to be modified it means that there is a need to perform an unlock (if set)
427   //
428   if ((~PadCfgAndMask | PadCfgOrMask) & (UINT32)~B_GPIO_PCR_TX_STATE) {
429     GpioGetPadCfgLock (GpioPad, &PadCfgLock);
430     if (PadCfgLock) {
431       GpioUnlockPadCfg (GpioPad);
432     }
433   }
434 
435   //
436   // Check if Pad Output state is to be changed
437   // If AND and OR masks will indicate that output control
438   // is to be modified it means that there is a need to perform an unlock (if set)
439   //
440   if ((~PadCfgAndMask | PadCfgOrMask) & B_GPIO_PCR_TX_STATE) {
441     GpioGetPadCfgLockTx (GpioPad, &PadCfgLockTx);
442     if (PadCfgLockTx) {
443       GpioUnlockPadCfgTx (GpioPad);
444     }
445   }
446 
447   GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad);
448   PadNumber = GpioGetPadNumberFromGpioPad (GpioPad);
449 
450   GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
451 
452   //
453   // Create Pad Configuration register offset
454   //
455   PadCfgReg = GpioGroupInfo[GroupIndex].PadCfgOffset + S_GPIO_PCR_PADCFG * PadNumber + 0x4 * DwReg;
456 
457   MmioAndThenOr32 (
458     PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg),
459     PadCfgAndMask,
460     PadCfgOrMask
461     );
462 
463   if (PadCfgLock) {
464     GpioLockPadCfg (GpioPad);
465   }
466   if (PadCfgLockTx) {
467     GpioLockPadCfgTx (GpioPad);
468   }
469 }
470 
471 /**
472   This procedure will set GPIO mode
473 
474   @param[in]  GpioPad             GPIO pad
475   @param[out] PadModeValue        GPIO pad mode value
476 
477   @retval EFI_SUCCESS             The function completed successfully
478   @retval EFI_INVALID_PARAMETER   Invalid group or pad number
479 **/
480 EFI_STATUS
GpioSetPadMode(IN GPIO_PAD GpioPad,IN GPIO_PAD_MODE PadModeValue)481 GpioSetPadMode (
482   IN GPIO_PAD                GpioPad,
483   IN GPIO_PAD_MODE           PadModeValue
484   )
485 {
486   UINT32               PadCfgOrMask;
487 
488   if (!GpioIsPadValid (GpioPad)) {
489     return EFI_INVALID_PARAMETER;
490   }
491 
492   if (!GpioIsPadHostOwned (GpioPad)) {
493     return EFI_UNSUPPORTED;
494   }
495 
496   if (PadModeValue != (GPIO_PAD_MODE)GpioHardwareDefault) {
497 
498     PadCfgOrMask = (((PadModeValue & B_GPIO_PAD_MODE_MASK) >> (N_GPIO_PAD_MODE_BIT_POS + 1)) << N_GPIO_PCR_PAD_MODE);
499 
500     GpioWritePadCfgReg (
501       GpioPad,
502       0,
503       (UINT32)~B_GPIO_PCR_PAD_MODE,
504       PadCfgOrMask
505       );
506   }
507 
508   return EFI_SUCCESS;
509 }
510 
511 /**
512   This procedure will get GPIO mode
513 
514   @param[in]  GpioPad             GPIO pad
515   @param[out] PadModeValue        GPIO pad mode value
516 
517   @retval EFI_SUCCESS             The function completed successfully
518   @retval EFI_INVALID_PARAMETER   Invalid GpioPad
519 **/
520 EFI_STATUS
GpioGetPadMode(IN GPIO_PAD GpioPad,OUT GPIO_PAD_MODE * PadModeValue)521 GpioGetPadMode (
522   IN  GPIO_PAD                 GpioPad,
523   OUT GPIO_PAD_MODE            *PadModeValue
524   )
525 {
526   UINT32        PadCfgRegValue;
527 
528   if (!GpioIsPadValid (GpioPad)) {
529     return EFI_INVALID_PARAMETER;
530   }
531 
532   if (!GpioIsPadHostOwned (GpioPad)) {
533     return EFI_UNSUPPORTED;
534   }
535 
536   PadCfgRegValue = GpioReadPadCfgReg (GpioPad, 0);
537 
538   *PadModeValue = (GPIO_PAD_MODE)(((PadCfgRegValue & B_GPIO_PCR_PAD_MODE) >> (N_GPIO_PCR_PAD_MODE - (N_GPIO_PAD_MODE_BIT_POS + 1))) | (0x1 << N_GPIO_PAD_MODE_BIT_POS));
539 
540   return EFI_SUCCESS;
541 }
542 
543 /**
544   The function performs GPIO Power Management programming.
545 **/
546 VOID
GpioConfigurePm(VOID)547 GpioConfigurePm (
548   VOID
549   )
550 {
551   UINT32       Data32Or;
552   UINT32       Data32And;
553   PCH_SBI_PID  *GpioComSbiIds;
554   UINT32       NoOfGpioComs;
555   UINT32       GpioComIndex;
556 
557   Data32And = (UINT32)~0,
558   //
559   // Enable MISCCFG.GPSIDEDPCGEn, MISCCFG.GPRCOMPCDLCGEn, MISCCFG.GPRTCDLCGEn,
560   // MISCCFG.GPDLCGEn and MISCCFG.GPDPCGEn for GPIO communities
561   //
562   Data32Or = (B_GPIO_PCR_MISCCFG_GPSIDEDPCGEN |
563               B_GPIO_PCR_MISCCFG_GPRCOMPCDLCGEN |
564               B_GPIO_PCR_MISCCFG_GPRTCDLCGEN |
565               B_GPIO_PCR_MISCCFG_GPDLCGEN |
566               B_GPIO_PCR_MISCCFG_GPDPCGEN);
567 
568   NoOfGpioComs = GpioGetComSbiPortIds (&GpioComSbiIds);
569 
570   //
571   // Configure Clock Gating in each community
572   //
573   for (GpioComIndex = 0; GpioComIndex < NoOfGpioComs; GpioComIndex++) {
574     MmioAndThenOr32 (
575       PCH_PCR_ADDRESS (GpioComSbiIds[GpioComIndex], R_GPIO_PCR_MISCCFG),
576       Data32And,
577       Data32Or
578       );
579   }
580 }
581 
582 /**
583   This procedure is used to unlock all GPIO pads.
584   This function can only be called when platform is still in HOSTIA_BOOT_SAI.
585 **/
586 VOID
GpioUnlockAllPads(VOID)587 GpioUnlockAllPads (
588   VOID
589   )
590 {
591   UINT32         DwNum;
592   UINT32         GroupIndex;
593   UINT32         NumberOfGroups;
594   GPIO_GROUP     Group;
595   UINT32         LockValue;
596   EFI_STATUS     Status;
597 
598   NumberOfGroups = GpioGetNumberOfGroups ();
599 
600   for (GroupIndex = 0; GroupIndex < NumberOfGroups; GroupIndex++) {
601     Group = GpioGetGroupFromGroupIndex (GroupIndex);
602     for (DwNum = 0; DwNum <= GPIO_GET_DW_NUM (GpioGetPadPerGroup (Group)); DwNum++) {
603 
604       GpioGetPadCfgLockForGroupDw (Group, DwNum, &LockValue);
605 
606       if (LockValue) {
607         Status = GpioUnlockPadCfgForGroupDw (Group, DwNum, ~0u);
608         ASSERT_EFI_ERROR (Status);
609       }
610 
611       GpioGetPadCfgLockTxForGroupDw (Group, DwNum, &LockValue);
612 
613       if (LockValue) {
614         Status = GpioUnlockPadCfgTxForGroupDw (Group, DwNum, ~0u);
615         ASSERT_EFI_ERROR (Status);
616       }
617     }
618   }
619 }
620 
621 /**
622   Generates GPIO name from GpioPad
623   This function returns pointer to the static buffer
624 
625   @param[in] GpioPad  GpioPad
626 
627   @retval CHAR8*  Pointer to the gpio name string
628 **/
629 CHAR8*
GpioName(IN GPIO_PAD GpioPad)630 GpioName (
631   IN GPIO_PAD  GpioPad
632   )
633 {
634   return GpioGetPadName (GpioPad, GpioGetStaticNameBuffer (), GPIO_NAME_LENGTH_MAX);
635 }
636 
637 
638 //
639 // For GPIO debounce feature glitch filter clock is used
640 // which is driven by RTC clock with f = 32kHz (T = 31.25us)
641 //
642 #define GPIO_DEB_CLK_PERIOD_IN_NS  31250
643 
644 /**
645   This procedure enables debounce feature on a selected pad configured in input mode
646   Debounce time can be specified in microseconds. GPIO HW supports only certain values
647   according to below formula:
648    DebounceTime = (2 ^ PADCFG_DW2.DEBOUNCE)*(glitch filter clock period).
649   RTC clock with f = 32 KHz is used for glitch filter.
650    DebounceTime = (2 ^ PADCFG_DW2.DEBOUNCE)*(31.25 us).
651   Supported DebounceTime values are following:
652    DebounceTime = 0 -> Debounce feature disabled
653    DebounceTime > 0 && < 250us -> Not supported
654    DebounceTime = 250us - 1024000us -> Supported range (DebounceTime = 250us * 2^n)
655   For values not supported by GPIO HW, function will round down
656   to closest supported
657 
658   @param[in] GpioPad              GPIO pad
659   @param[in, out] DebounceTime    Debounce Time in microseconds
660                                   If Debounce Time = 0, Debouncer feature will be disabled
661                                   Function will set DebounceTime argument to rounded supported value
662 
663   @retval EFI_SUCCESS             The function completed successfully
664   @retval EFI_INVALID_PARAMETER   Invalid GpioPad or unsupported DebounceDuration value
665   @retval EFI_UNSUPPORTED         GpioPad is not owned by host
666 **/
667 EFI_STATUS
GpioSetDebounceTimer(IN GPIO_PAD GpioPad,IN OUT UINT32 * DebounceTime)668 GpioSetDebounceTimer (
669   IN GPIO_PAD                  GpioPad,
670   IN OUT UINT32                *DebounceTime
671   )
672 {
673   UINT32   DebounceEnable;
674   UINT32   DebounceValue;
675   UINT32   InRangeDebounceTime;
676   UINT32   SupportedDebounceTime;
677   UINT32   Temp;
678   BOOLEAN  SupportedValue;
679 
680   if (!GpioIsPadValid (GpioPad)) {
681     return EFI_INVALID_PARAMETER;
682   }
683 
684   if (!GpioIsPadHostOwned (GpioPad)) {
685     return EFI_UNSUPPORTED;
686   }
687 
688   if (*DebounceTime > 1024000) {
689     InRangeDebounceTime = 1024000;
690     SupportedValue = FALSE;
691   } else if ((*DebounceTime < 250) && (*DebounceTime > 0)) {
692     InRangeDebounceTime = 0;
693     SupportedValue = FALSE;
694   } else {
695     InRangeDebounceTime = *DebounceTime;
696     SupportedValue = TRUE;
697   }
698 
699   //
700   // DebounceValue = log2 (InRangeDebounceTime * f_deb_clk)
701   //
702   DebounceValue = 0;
703   Temp = InRangeDebounceTime * 1000 / GPIO_DEB_CLK_PERIOD_IN_NS;
704 
705   //
706   // Check if any rounding occurred
707   //
708   if (InRangeDebounceTime != (Temp * GPIO_DEB_CLK_PERIOD_IN_NS / 1000)) {
709     SupportedValue = FALSE;
710   }
711 
712   //
713   // Check if value is power of 2
714   //
715   if ((Temp != 0) && ((Temp & (Temp - 1)) != 0)) {
716     SupportedValue = FALSE;
717   }
718 
719   //
720   // DebounceValue = log2 (Temp)
721   //
722   while (Temp > 1) {
723     Temp >>= 1;
724     DebounceValue++;
725   }
726 
727   if (DebounceValue > 0) {
728     DebounceEnable = B_GPIO_PCR_DEBEN;
729     SupportedDebounceTime = (1 << DebounceValue) * GPIO_DEB_CLK_PERIOD_IN_NS / 1000;
730   } else {
731     DebounceEnable = 0;
732     SupportedDebounceTime = 0;
733   }
734 
735   GpioWritePadCfgReg (
736     GpioPad,
737     2,
738     (UINT32)~(B_GPIO_PCR_DEBOUNCE | B_GPIO_PCR_DEBEN),
739     (DebounceValue << N_GPIO_PCR_DEBOUNCE) | DebounceEnable
740     );
741 
742   if (!SupportedValue) {
743     DEBUG ((DEBUG_WARN, "GPIO WARNING: %a %dus debounce time rounded down to %dus\n",
744             GpioName (GpioPad),
745             *DebounceTime,
746             SupportedDebounceTime));
747   }
748 
749   *DebounceTime = SupportedDebounceTime;
750 
751   return EFI_SUCCESS;
752 }
753