mirror of https://github.com/acidanthera/audk.git
410 lines
10 KiB
C
410 lines
10 KiB
C
/** @file
|
|
*
|
|
* Copyright (c) 2011, ARM Limited. All rights reserved.
|
|
* 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 ((EFI_D_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 = EFI_SUCCESS;
|
|
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))) {
|
|
*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 = EFI_SUCCESS;
|
|
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 = EFI_SUCCESS;
|
|
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))) {
|
|
*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 ((EFI_D_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;
|
|
}
|