3 Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.<BR>
4 Copyright (c) 2016, Linaro Limited. All rights reserved.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include <Library/BaseLib.h>
13 #include <Library/BaseMemoryLib.h>
14 #include <Library/DebugLib.h>
15 #include <Library/IoLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/PcdLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
19 #include <Library/UefiLib.h>
20 #include <Library/UefiRuntimeServicesTableLib.h>
22 #include <Protocol/EmbeddedGpio.h>
24 #include "PL061Gpio.h"
26 PLATFORM_GPIO_CONTROLLER
*mPL061PlatformGpio
;
31 IN EMBEDDED_GPIO_PIN Gpio
,
32 OUT UINTN
*ControllerIndex
,
33 OUT UINTN
*ControllerOffset
,
34 OUT UINTN
*RegisterBase
39 for (Index
= 0; Index
< mPL061PlatformGpio
->GpioControllerCount
; Index
++) {
40 if ( (Gpio
>= mPL061PlatformGpio
->GpioController
[Index
].GpioIndex
)
41 && (Gpio
< mPL061PlatformGpio
->GpioController
[Index
].GpioIndex
42 + mPL061PlatformGpio
->GpioController
[Index
].InternalGpioCount
))
44 *ControllerIndex
= Index
;
45 *ControllerOffset
= Gpio
% mPL061PlatformGpio
->GpioController
[Index
].InternalGpioCount
;
46 *RegisterBase
= mPL061PlatformGpio
->GpioController
[Index
].RegisterBase
;
51 DEBUG ((DEBUG_ERROR
, "%a, failed to locate gpio %d\n", __func__
, Gpio
));
52 return EFI_INVALID_PARAMETER
;
56 // The PL061 is a strange beast. The 8-bit data register is aliased across a
57 // region 0x400 bytes in size, with bits [9:2] of the address operating as a
58 // mask for both read and write operations:
60 // - All bits where their corresponding mask bit is 1 return the current
61 // value of that bit in the GPIO_DATA register.
62 // - All bits where their corresponding mask bit is 0 return 0.
64 // - All bits where their corresponding mask bit is 1 set the bit in the
65 // GPIO_DATA register to the written value.
66 // - All bits where their corresponding mask bit is 0 are left untouched
67 // in the GPIO_DATA register.
69 // To keep this driver intelligible, PL061EffectiveAddress, PL061GetPins and
70 // Pl061SetPins provide an internal abstraction from this interface.
75 PL061EffectiveAddress (
80 return ((Address
+ PL061_GPIO_DATA_REG_OFFSET
) + (Mask
<< 2));
91 return MmioRead8 (PL061EffectiveAddress (Address
, Mask
));
103 MmioWrite8 (PL061EffectiveAddress (Address
, Mask
), Value
);
107 Function implementations
117 if ( (mPL061PlatformGpio
->GpioCount
== 0)
118 || (mPL061PlatformGpio
->GpioControllerCount
== 0))
120 return EFI_NOT_FOUND
;
123 for (Index
= 0; Index
< mPL061PlatformGpio
->GpioControllerCount
; Index
++) {
124 if (mPL061PlatformGpio
->GpioController
[Index
].InternalGpioCount
!= PL061_GPIO_PINS
) {
125 return EFI_INVALID_PARAMETER
;
128 RegisterBase
= mPL061PlatformGpio
->GpioController
[Index
].RegisterBase
;
130 // Check if this is a PrimeCell Peripheral
131 if ( (MmioRead8 (RegisterBase
+ PL061_GPIO_PCELL_ID0
) != 0x0D)
132 || (MmioRead8 (RegisterBase
+ PL061_GPIO_PCELL_ID1
) != 0xF0)
133 || (MmioRead8 (RegisterBase
+ PL061_GPIO_PCELL_ID2
) != 0x05)
134 || (MmioRead8 (RegisterBase
+ PL061_GPIO_PCELL_ID3
) != 0xB1))
136 return EFI_NOT_FOUND
;
139 // Check if this PrimeCell Peripheral is the PL061 GPIO
140 if ( (MmioRead8 (RegisterBase
+ PL061_GPIO_PERIPH_ID0
) != 0x61)
141 || (MmioRead8 (RegisterBase
+ PL061_GPIO_PERIPH_ID1
) != 0x10)
142 || ((MmioRead8 (RegisterBase
+ PL061_GPIO_PERIPH_ID2
) & 0xF) != 0x04)
143 || (MmioRead8 (RegisterBase
+ PL061_GPIO_PERIPH_ID3
) != 0x00))
145 return EFI_NOT_FOUND
;
156 Gets the state of a GPIO pin
160 This - pointer to protocol
161 Gpio - which pin to read
162 Value - state of the pin
166 EFI_SUCCESS - GPIO state returned in Value
167 EFI_INVALID_PARAMETER - Value is NULL pointer or Gpio pin is out of range
172 IN EMBEDDED_GPIO
*This
,
173 IN EMBEDDED_GPIO_PIN Gpio
,
178 UINTN Index
, Offset
, RegisterBase
;
180 Status
= PL061Locate (Gpio
, &Index
, &Offset
, &RegisterBase
);
181 ASSERT_EFI_ERROR (Status
);
184 return EFI_INVALID_PARAMETER
;
187 if (PL061GetPins (RegisterBase
, GPIO_PIN_MASK (Offset
)) != 0) {
200 Sets the state of a GPIO pin
204 This - pointer to protocol
205 Gpio - which pin to modify
210 EFI_SUCCESS - GPIO set as requested
211 EFI_UNSUPPORTED - Mode is not supported
212 EFI_INVALID_PARAMETER - Gpio pin is out of range
217 IN EMBEDDED_GPIO
*This
,
218 IN EMBEDDED_GPIO_PIN Gpio
,
219 IN EMBEDDED_GPIO_MODE Mode
223 UINTN Index
, Offset
, RegisterBase
;
225 Status
= PL061Locate (Gpio
, &Index
, &Offset
, &RegisterBase
);
226 ASSERT_EFI_ERROR (Status
);
229 case GPIO_MODE_INPUT
:
230 // Set the corresponding direction bit to LOW for input
232 RegisterBase
+ PL061_GPIO_DIR_REG
,
233 ~GPIO_PIN_MASK(Offset
) & 0xFF
237 case GPIO_MODE_OUTPUT_0
:
238 // Set the corresponding direction bit to HIGH for output
239 MmioOr8 (RegisterBase
+ PL061_GPIO_DIR_REG
, GPIO_PIN_MASK (Offset
));
240 // Set the corresponding data bit to LOW for 0
241 PL061SetPins (RegisterBase
, GPIO_PIN_MASK (Offset
), 0);
244 case GPIO_MODE_OUTPUT_1
:
245 // Set the corresponding direction bit to HIGH for output
246 MmioOr8 (RegisterBase
+ PL061_GPIO_DIR_REG
, GPIO_PIN_MASK (Offset
));
247 // Set the corresponding data bit to HIGH for 1
248 PL061SetPins (RegisterBase
, GPIO_PIN_MASK (Offset
), 0xff);
252 // Other modes are not supported
253 return EFI_UNSUPPORTED
;
263 Gets the mode (function) of a GPIO pin
267 This - pointer to protocol
269 Mode - pointer to output mode value
273 EFI_SUCCESS - mode value retrieved
274 EFI_INVALID_PARAMETER - Mode is a null pointer or Gpio pin is out of range
280 IN EMBEDDED_GPIO
*This
,
281 IN EMBEDDED_GPIO_PIN Gpio
,
282 OUT EMBEDDED_GPIO_MODE
*Mode
286 UINTN Index
, Offset
, RegisterBase
;
288 Status
= PL061Locate (Gpio
, &Index
, &Offset
, &RegisterBase
);
289 ASSERT_EFI_ERROR (Status
);
293 return EFI_INVALID_PARAMETER
;
296 // Check if it is input or output
297 if (MmioRead8 (RegisterBase
+ PL061_GPIO_DIR_REG
) & GPIO_PIN_MASK (Offset
)) {
299 if (PL061GetPins (RegisterBase
, GPIO_PIN_MASK (Offset
)) != 0) {
300 *Mode
= GPIO_MODE_OUTPUT_1
;
302 *Mode
= GPIO_MODE_OUTPUT_0
;
306 *Mode
= GPIO_MODE_INPUT
;
316 Sets the pull-up / pull-down resistor of a GPIO pin
320 This - pointer to protocol
322 Direction - pull-up, pull-down, or none
326 EFI_UNSUPPORTED - Can not perform the requested operation
332 IN EMBEDDED_GPIO
*This
,
333 IN EMBEDDED_GPIO_PIN Gpio
,
334 IN EMBEDDED_GPIO_PULL Direction
337 return EFI_UNSUPPORTED
;
341 Protocol variable definition
343 EMBEDDED_GPIO gGpio
= {
351 Initialize the state information for the Embedded Gpio protocol.
353 @param ImageHandle of the loaded driver
354 @param SystemTable Pointer to the System Table
356 @retval EFI_SUCCESS Protocol registered
357 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
358 @retval EFI_DEVICE_ERROR Hardware problems
363 PL061InstallProtocol (
364 IN EFI_HANDLE ImageHandle
,
365 IN EFI_SYSTEM_TABLE
*SystemTable
370 GPIO_CONTROLLER
*GpioController
;
373 // Make sure the Gpio protocol has not been installed in the system yet.
375 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL
, &gEmbeddedGpioProtocolGuid
);
377 Status
= gBS
->LocateProtocol (&gPlatformGpioProtocolGuid
, NULL
, (VOID
**)&mPL061PlatformGpio
);
378 if (EFI_ERROR (Status
) && (Status
== EFI_NOT_FOUND
)) {
379 // Create the mPL061PlatformGpio
380 mPL061PlatformGpio
= (PLATFORM_GPIO_CONTROLLER
*)AllocateZeroPool (sizeof (PLATFORM_GPIO_CONTROLLER
) + sizeof (GPIO_CONTROLLER
));
381 if (mPL061PlatformGpio
== NULL
) {
382 DEBUG ((DEBUG_ERROR
, "%a: failed to allocate PLATFORM_GPIO_CONTROLLER\n", __func__
));
383 return EFI_BAD_BUFFER_SIZE
;
386 mPL061PlatformGpio
->GpioCount
= PL061_GPIO_PINS
;
387 mPL061PlatformGpio
->GpioControllerCount
= 1;
388 mPL061PlatformGpio
->GpioController
= (GPIO_CONTROLLER
*)((UINTN
)mPL061PlatformGpio
+ sizeof (PLATFORM_GPIO_CONTROLLER
));
390 GpioController
= mPL061PlatformGpio
->GpioController
;
391 GpioController
->RegisterBase
= (UINTN
)PcdGet32 (PcdPL061GpioBase
);
392 GpioController
->GpioIndex
= 0;
393 GpioController
->InternalGpioCount
= PL061_GPIO_PINS
;
396 Status
= PL061Identify ();
397 if (EFI_ERROR (Status
)) {
398 return EFI_DEVICE_ERROR
;
401 // Install the Embedded GPIO Protocol onto a new handle
403 Status
= gBS
->InstallMultipleProtocolInterfaces (
405 &gEmbeddedGpioProtocolGuid
,
409 if (EFI_ERROR (Status
)) {
410 Status
= EFI_OUT_OF_RESOURCES
;