/** @file | |
Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.<BR> | |
Copyright (c) 2016, Linaro Limited. All rights reserved. | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiDxe.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/IoLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/PcdLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/UefiLib.h> | |
#include <Library/UefiRuntimeServicesTableLib.h> | |
#include <Protocol/EmbeddedGpio.h> | |
#include "PL061Gpio.h" | |
PLATFORM_GPIO_CONTROLLER *mPL061PlatformGpio; | |
EFI_STATUS | |
EFIAPI | |
PL061Locate ( | |
IN EMBEDDED_GPIO_PIN Gpio, | |
OUT UINTN *ControllerIndex, | |
OUT UINTN *ControllerOffset, | |
OUT UINTN *RegisterBase | |
) | |
{ | |
UINT32 Index; | |
for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) { | |
if ( (Gpio >= mPL061PlatformGpio->GpioController[Index].GpioIndex) | |
&& (Gpio < mPL061PlatformGpio->GpioController[Index].GpioIndex | |
+ mPL061PlatformGpio->GpioController[Index].InternalGpioCount)) | |
{ | |
*ControllerIndex = Index; | |
*ControllerOffset = Gpio % mPL061PlatformGpio->GpioController[Index].InternalGpioCount; | |
*RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase; | |
return EFI_SUCCESS; | |
} | |
} | |
DEBUG ((DEBUG_ERROR, "%a, failed to locate gpio %d\n", __func__, Gpio)); | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// The PL061 is a strange beast. The 8-bit data register is aliased across a | |
// region 0x400 bytes in size, with bits [9:2] of the address operating as a | |
// mask for both read and write operations: | |
// For reads: | |
// - All bits where their corresponding mask bit is 1 return the current | |
// value of that bit in the GPIO_DATA register. | |
// - All bits where their corresponding mask bit is 0 return 0. | |
// For writes: | |
// - All bits where their corresponding mask bit is 1 set the bit in the | |
// GPIO_DATA register to the written value. | |
// - All bits where their corresponding mask bit is 0 are left untouched | |
// in the GPIO_DATA register. | |
// | |
// To keep this driver intelligible, PL061EffectiveAddress, PL061GetPins and | |
// Pl061SetPins provide an internal abstraction from this interface. | |
STATIC | |
UINTN | |
EFIAPI | |
PL061EffectiveAddress ( | |
IN UINTN Address, | |
IN UINTN Mask | |
) | |
{ | |
return ((Address + PL061_GPIO_DATA_REG_OFFSET) + (Mask << 2)); | |
} | |
STATIC | |
UINTN | |
EFIAPI | |
PL061GetPins ( | |
IN UINTN Address, | |
IN UINTN Mask | |
) | |
{ | |
return MmioRead8 (PL061EffectiveAddress (Address, Mask)); | |
} | |
STATIC | |
VOID | |
EFIAPI | |
PL061SetPins ( | |
IN UINTN Address, | |
IN UINTN Mask, | |
IN UINTN Value | |
) | |
{ | |
MmioWrite8 (PL061EffectiveAddress (Address, Mask), Value); | |
} | |
/** | |
Function implementations | |
**/ | |
EFI_STATUS | |
PL061Identify ( | |
VOID | |
) | |
{ | |
UINTN Index; | |
UINTN RegisterBase; | |
if ( (mPL061PlatformGpio->GpioCount == 0) | |
|| (mPL061PlatformGpio->GpioControllerCount == 0)) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) { | |
if (mPL061PlatformGpio->GpioController[Index].InternalGpioCount != PL061_GPIO_PINS) { | |
return EFI_INVALID_PARAMETER; | |
} | |
RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase; | |
// Check if this is a PrimeCell Peripheral | |
if ( (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID0) != 0x0D) | |
|| (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID1) != 0xF0) | |
|| (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID2) != 0x05) | |
|| (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID3) != 0xB1)) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
// Check if this PrimeCell Peripheral is the PL061 GPIO | |
if ( (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID0) != 0x61) | |
|| (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID1) != 0x10) | |
|| ((MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID2) & 0xF) != 0x04) | |
|| (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID3) != 0x00)) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Routine Description: | |
Gets the state of a GPIO pin | |
Arguments: | |
This - pointer to protocol | |
Gpio - which pin to read | |
Value - state of the pin | |
Returns: | |
EFI_SUCCESS - GPIO state returned in Value | |
EFI_INVALID_PARAMETER - Value is NULL pointer or Gpio pin is out of range | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Get ( | |
IN EMBEDDED_GPIO *This, | |
IN EMBEDDED_GPIO_PIN Gpio, | |
OUT UINTN *Value | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index, Offset, RegisterBase; | |
Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase); | |
ASSERT_EFI_ERROR (Status); | |
if (Value == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (PL061GetPins (RegisterBase, GPIO_PIN_MASK (Offset)) != 0) { | |
*Value = 1; | |
} else { | |
*Value = 0; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Routine Description: | |
Sets the state of a GPIO pin | |
Arguments: | |
This - pointer to protocol | |
Gpio - which pin to modify | |
Mode - mode to set | |
Returns: | |
EFI_SUCCESS - GPIO set as requested | |
EFI_UNSUPPORTED - Mode is not supported | |
EFI_INVALID_PARAMETER - Gpio pin is out of range | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Set ( | |
IN EMBEDDED_GPIO *This, | |
IN EMBEDDED_GPIO_PIN Gpio, | |
IN EMBEDDED_GPIO_MODE Mode | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index, Offset, RegisterBase; | |
Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase); | |
ASSERT_EFI_ERROR (Status); | |
switch (Mode) { | |
case GPIO_MODE_INPUT: | |
// Set the corresponding direction bit to LOW for input | |
MmioAnd8 ( | |
RegisterBase + PL061_GPIO_DIR_REG, | |
~GPIO_PIN_MASK(Offset) & 0xFF | |
); | |
break; | |
case GPIO_MODE_OUTPUT_0: | |
// Set the corresponding direction bit to HIGH for output | |
MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK (Offset)); | |
// Set the corresponding data bit to LOW for 0 | |
PL061SetPins (RegisterBase, GPIO_PIN_MASK (Offset), 0); | |
break; | |
case GPIO_MODE_OUTPUT_1: | |
// Set the corresponding direction bit to HIGH for output | |
MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK (Offset)); | |
// Set the corresponding data bit to HIGH for 1 | |
PL061SetPins (RegisterBase, GPIO_PIN_MASK (Offset), 0xff); | |
break; | |
default: | |
// Other modes are not supported | |
return EFI_UNSUPPORTED; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Routine Description: | |
Gets the mode (function) of a GPIO pin | |
Arguments: | |
This - pointer to protocol | |
Gpio - which pin | |
Mode - pointer to output mode value | |
Returns: | |
EFI_SUCCESS - mode value retrieved | |
EFI_INVALID_PARAMETER - Mode is a null pointer or Gpio pin is out of range | |
**/ | |
EFI_STATUS | |
EFIAPI | |
GetMode ( | |
IN EMBEDDED_GPIO *This, | |
IN EMBEDDED_GPIO_PIN Gpio, | |
OUT EMBEDDED_GPIO_MODE *Mode | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index, Offset, RegisterBase; | |
Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase); | |
ASSERT_EFI_ERROR (Status); | |
// Check for errors | |
if (Mode == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// Check if it is input or output | |
if (MmioRead8 (RegisterBase + PL061_GPIO_DIR_REG) & GPIO_PIN_MASK (Offset)) { | |
// Pin set to output | |
if (PL061GetPins (RegisterBase, GPIO_PIN_MASK (Offset)) != 0) { | |
*Mode = GPIO_MODE_OUTPUT_1; | |
} else { | |
*Mode = GPIO_MODE_OUTPUT_0; | |
} | |
} else { | |
// Pin set to input | |
*Mode = GPIO_MODE_INPUT; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Routine Description: | |
Sets the pull-up / pull-down resistor of a GPIO pin | |
Arguments: | |
This - pointer to protocol | |
Gpio - which pin | |
Direction - pull-up, pull-down, or none | |
Returns: | |
EFI_UNSUPPORTED - Can not perform the requested operation | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SetPull ( | |
IN EMBEDDED_GPIO *This, | |
IN EMBEDDED_GPIO_PIN Gpio, | |
IN EMBEDDED_GPIO_PULL Direction | |
) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
/** | |
Protocol variable definition | |
**/ | |
EMBEDDED_GPIO gGpio = { | |
Get, | |
Set, | |
GetMode, | |
SetPull | |
}; | |
/** | |
Initialize the state information for the Embedded Gpio protocol. | |
@param ImageHandle of the loaded driver | |
@param SystemTable Pointer to the System Table | |
@retval EFI_SUCCESS Protocol registered | |
@retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure | |
@retval EFI_DEVICE_ERROR Hardware problems | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PL061InstallProtocol ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE Handle; | |
GPIO_CONTROLLER *GpioController; | |
// | |
// Make sure the Gpio protocol has not been installed in the system yet. | |
// | |
ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEmbeddedGpioProtocolGuid); | |
Status = gBS->LocateProtocol (&gPlatformGpioProtocolGuid, NULL, (VOID **)&mPL061PlatformGpio); | |
if (EFI_ERROR (Status) && (Status == EFI_NOT_FOUND)) { | |
// Create the mPL061PlatformGpio | |
mPL061PlatformGpio = (PLATFORM_GPIO_CONTROLLER *)AllocateZeroPool (sizeof (PLATFORM_GPIO_CONTROLLER) + sizeof (GPIO_CONTROLLER)); | |
if (mPL061PlatformGpio == NULL) { | |
DEBUG ((DEBUG_ERROR, "%a: failed to allocate PLATFORM_GPIO_CONTROLLER\n", __func__)); | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
mPL061PlatformGpio->GpioCount = PL061_GPIO_PINS; | |
mPL061PlatformGpio->GpioControllerCount = 1; | |
mPL061PlatformGpio->GpioController = (GPIO_CONTROLLER *)((UINTN)mPL061PlatformGpio + sizeof (PLATFORM_GPIO_CONTROLLER)); | |
GpioController = mPL061PlatformGpio->GpioController; | |
GpioController->RegisterBase = (UINTN)PcdGet32 (PcdPL061GpioBase); | |
GpioController->GpioIndex = 0; | |
GpioController->InternalGpioCount = PL061_GPIO_PINS; | |
} | |
Status = PL061Identify (); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// Install the Embedded GPIO Protocol onto a new handle | |
Handle = NULL; | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&Handle, | |
&gEmbeddedGpioProtocolGuid, | |
&gGpio, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_OUT_OF_RESOURCES; | |
} | |
return Status; | |
} |