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