/** @file
|
*
|
* GPIO manipulation.
|
*
|
* Copyright (c) 2020, Pete Batard <pete@akeo.ie>
|
* Copyright (c) 2018, Andrei Warkentin <andrey.warkentin@gmail.com>
|
* Copyright (c) 2021, ARM Limited. All rights reserved.
|
*
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
*
|
**/
|
|
#include <Uefi.h>
|
#include <Library/BaseLib.h>
|
#include <Library/DebugLib.h>
|
#include <Library/IoLib.h>
|
#include <Library/GpioLib.h>
|
#include <Library/TimerLib.h>
|
#include <IndustryStandard/Bcm2836.h>
|
#include <IndustryStandard/Bcm2836Gpio.h>
|
|
STATIC
|
VOID
|
GpioFSELModify (
|
IN UINTN RegIndex,
|
IN UINT32 ModifyMask,
|
IN UINT32 FunctionMask
|
)
|
{
|
UINT32 Val;
|
EFI_PHYSICAL_ADDRESS Reg;
|
|
Reg = RegIndex * sizeof (UINT32) + GPIO_GPFSEL0;
|
|
ASSERT (Reg <= GPIO_GPFSEL5);
|
ASSERT ((~ModifyMask & FunctionMask) == 0);
|
|
Val = MmioRead32 (Reg);
|
Val &= ~ModifyMask;
|
Val |= FunctionMask;
|
MmioWrite32 (Reg, Val);
|
}
|
|
VOID
|
GpioPinFuncSet (
|
IN UINTN Pin,
|
IN UINTN Function
|
)
|
{
|
UINTN RegIndex;
|
UINTN SelIndex;
|
UINT32 ModifyMask;
|
UINT32 FunctionMask;
|
|
ASSERT (Pin < GPIO_PINS);
|
ASSERT (Function <= GPIO_FSEL_MASK);
|
|
RegIndex = Pin / 10;
|
SelIndex = Pin % 10;
|
|
ModifyMask = GPIO_FSEL_MASK << (SelIndex * GPIO_FSEL_BITS_PER_PIN);
|
FunctionMask = Function << (SelIndex * GPIO_FSEL_BITS_PER_PIN);
|
GpioFSELModify (RegIndex, ModifyMask, FunctionMask);
|
}
|
|
UINTN
|
GpioPinFuncGet (
|
IN UINTN Pin
|
)
|
{
|
UINT32 Val;
|
UINTN RegIndex;
|
UINTN SelIndex;
|
EFI_PHYSICAL_ADDRESS Reg;
|
|
ASSERT (Pin < GPIO_PINS);
|
|
RegIndex = Pin / 10;
|
SelIndex = Pin % 10;
|
Reg = RegIndex * sizeof (UINT32) + GPIO_GPFSEL0;
|
|
Val = MmioRead32 (Reg);
|
Val >>= SelIndex * GPIO_FSEL_BITS_PER_PIN;
|
Val &= GPIO_FSEL_MASK;
|
return Val;
|
}
|
|
VOID
|
GpioPinConfigure (
|
IN UINTN Pin,
|
IN BOOLEAN Config
|
)
|
{
|
EFI_PHYSICAL_ADDRESS Reg;
|
UINTN RegIndex;
|
UINTN SelIndex;
|
|
ASSERT (Pin < GPIO_PINS);
|
|
RegIndex = Pin / 32;
|
SelIndex = Pin % 32;
|
|
//
|
// Different base addresses are used for clear and set
|
//
|
Reg = (Config == CLEAR_GPIO) ? GPIO_GPCLR0 : GPIO_GPSET0;
|
Reg += RegIndex * sizeof (UINT32);
|
MmioWrite32 (Reg, 1 << SelIndex);
|
}
|
|
UINTN
|
GpioPinGet (
|
IN UINTN Pin
|
)
|
{
|
EFI_PHYSICAL_ADDRESS Reg;
|
UINTN RegIndex;
|
UINTN SelIndex;
|
UINT32 Val;
|
|
ASSERT (Pin < GPIO_PINS);
|
|
RegIndex = Pin / 32;
|
SelIndex = Pin % 32;
|
|
Reg = RegIndex * sizeof (UINT32) + GPIO_GPLEV0;
|
Val = MmioRead32 (Reg) >> SelIndex;
|
return Val & 1;
|
}
|
|
VOID
|
GpioSetPull (
|
IN UINTN Pin,
|
IN UINTN Pud
|
)
|
{
|
EFI_PHYSICAL_ADDRESS Reg;
|
UINTN RegIndex;
|
UINTN SelIndex;
|
UINT32 Val;
|
|
ASSERT (Pin < GPIO_PINS);
|
ASSERT (Pud <= GPIO_PULL_UP);
|
|
//
|
// Check if we should use the legacy or newer method of
|
// enabling pullup/pulldown by querying GPPUPPDN3, which
|
// is unused on Bcm2835/2836/2837 and returns 'gpio'.
|
//
|
if (MmioRead32 (GPIO_GPPUPPDN3) == 0x6770696f) {
|
RegIndex = Pin / 32;
|
SelIndex = Pin % 32;
|
|
MmioWrite32 (GPIO_GPPUD, Pud);
|
MicroSecondDelay (5);
|
|
Reg = RegIndex * sizeof (UINT32) + GPIO_GPPUDCLK0;
|
MmioWrite32 (Reg, 1 << SelIndex);
|
MicroSecondDelay (5);
|
|
MmioWrite32 (GPIO_GPPUD, 0);
|
MmioWrite32 (Reg, 0);
|
} else {
|
//
|
// The newer method uses inverted values for up/down.
|
//
|
if (Pud != 0)
|
Pud ^= 0x3;
|
RegIndex = Pin / 16;
|
SelIndex = (Pin % 16) * 2;
|
Reg = RegIndex * sizeof (UINT32) + GPIO_GPPUPPDN0;
|
Val = MmioRead32 (Reg);
|
Val &= ~(0x3 << SelIndex);
|
Val |= Pud << SelIndex;
|
MmioWrite32 (Reg, Val);
|
}
|
}
|