ArmPlatformPkg: PL061 - rewrite the hardware interaction
The PL061 GPIO controller is a bit of an anachronism, and the existing driver does nothing to hide this - leading to it being very tricky to read. Rewrite it to document (in comments and code) what is actually happening, and fix some bugs in the process. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Leif Lindholm <leif.lindholm@linaro.org> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Ryan Harkin <ryan.harkin@linaro.org>
This commit is contained in:
@@ -28,6 +28,56 @@
|
|||||||
#include <Protocol/EmbeddedGpio.h>
|
#include <Protocol/EmbeddedGpio.h>
|
||||||
#include <Drivers/PL061Gpio.h>
|
#include <Drivers/PL061Gpio.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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
|
Function implementations
|
||||||
@@ -88,7 +138,7 @@ Get (
|
|||||||
return EFI_INVALID_PARAMETER;
|
return EFI_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MmioRead8 (PL061_GPIO_DATA_REG) & GPIO_PIN_MASK_HIGH_8BIT(Gpio)) {
|
if (PL061GetPins (PL061_GPIO_DATA_REG, Gpio)) {
|
||||||
*Value = 1;
|
*Value = 1;
|
||||||
} else {
|
} else {
|
||||||
*Value = 0;
|
*Value = 0;
|
||||||
@@ -135,22 +185,22 @@ Set (
|
|||||||
{
|
{
|
||||||
case GPIO_MODE_INPUT:
|
case GPIO_MODE_INPUT:
|
||||||
// Set the corresponding direction bit to LOW for input
|
// Set the corresponding direction bit to LOW for input
|
||||||
MmioAnd8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK_LOW_8BIT(Gpio));
|
MmioAnd8 (PL061_GPIO_DIR_REG, ~GPIO_PIN_MASK(Gpio) & 0xFF);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GPIO_MODE_OUTPUT_0:
|
case GPIO_MODE_OUTPUT_0:
|
||||||
// Set the corresponding data bit to LOW for 0
|
|
||||||
MmioAnd8 (PL061_GPIO_DATA_REG, GPIO_PIN_MASK_LOW_8BIT(Gpio));
|
|
||||||
// Set the corresponding direction bit to HIGH for output
|
// Set the corresponding direction bit to HIGH for output
|
||||||
MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK_HIGH_8BIT(Gpio));
|
MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Gpio));
|
||||||
|
// Set the corresponding data bit to LOW for 0
|
||||||
|
PL061SetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio), 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GPIO_MODE_OUTPUT_1:
|
case GPIO_MODE_OUTPUT_1:
|
||||||
// Set the corresponding data bit to HIGH for 1
|
|
||||||
MmioOr8 (PL061_GPIO_DATA_REG, GPIO_PIN_MASK_HIGH_8BIT(Gpio));
|
|
||||||
// Set the corresponding direction bit to HIGH for output
|
// Set the corresponding direction bit to HIGH for output
|
||||||
MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK_HIGH_8BIT(Gpio));
|
MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Gpio));
|
||||||
break;
|
// Set the corresponding data bit to HIGH for 1
|
||||||
|
PL061SetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio), 0xff);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Other modes are not supported
|
// Other modes are not supported
|
||||||
@@ -194,9 +244,9 @@ GetMode (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if it is input or output
|
// Check if it is input or output
|
||||||
if (MmioRead8 (PL061_GPIO_DIR_REG) & GPIO_PIN_MASK_HIGH_8BIT(Gpio)) {
|
if (MmioRead8 (PL061_GPIO_DIR_REG) & GPIO_PIN_MASK(Gpio)) {
|
||||||
// Pin set to output
|
// Pin set to output
|
||||||
if (MmioRead8 (PL061_GPIO_DATA_REG) & GPIO_PIN_MASK_HIGH_8BIT(Gpio)) {
|
if (PL061GetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio))) {
|
||||||
*Mode = GPIO_MODE_OUTPUT_1;
|
*Mode = GPIO_MODE_OUTPUT_1;
|
||||||
} else {
|
} else {
|
||||||
*Mode = GPIO_MODE_OUTPUT_0;
|
*Mode = GPIO_MODE_OUTPUT_0;
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
#include <Protocol/EmbeddedGpio.h>
|
#include <Protocol/EmbeddedGpio.h>
|
||||||
|
|
||||||
// PL061 GPIO Registers
|
// PL061 GPIO Registers
|
||||||
|
#define PL061_GPIO_DATA_REG_OFFSET ((UINTN) 0x000)
|
||||||
#define PL061_GPIO_DATA_REG ((UINT32)PcdGet32 (PcdPL061GpioBase) + 0x000)
|
#define PL061_GPIO_DATA_REG ((UINT32)PcdGet32 (PcdPL061GpioBase) + 0x000)
|
||||||
#define PL061_GPIO_DIR_REG ((UINT32)PcdGet32 (PcdPL061GpioBase) + 0x400)
|
#define PL061_GPIO_DIR_REG ((UINT32)PcdGet32 (PcdPL061GpioBase) + 0x400)
|
||||||
#define PL061_GPIO_IS_REG ((UINT32)PcdGet32 (PcdPL061GpioBase) + 0x404)
|
#define PL061_GPIO_IS_REG ((UINT32)PcdGet32 (PcdPL061GpioBase) + 0x404)
|
||||||
@@ -46,9 +47,5 @@
|
|||||||
|
|
||||||
// All bits low except one bit high, native bit length
|
// All bits low except one bit high, native bit length
|
||||||
#define GPIO_PIN_MASK(Pin) (1UL << ((UINTN)(Pin)))
|
#define GPIO_PIN_MASK(Pin) (1UL << ((UINTN)(Pin)))
|
||||||
// All bits low except one bit high, restricted to 8 bits (i.e. ensures zeros above 8bits)
|
|
||||||
#define GPIO_PIN_MASK_HIGH_8BIT(Pin) (GPIO_PIN_MASK(Pin) && 0xFF)
|
|
||||||
// All bits high except one bit low, restricted to 8 bits (i.e. ensures zeros above 8bits)
|
|
||||||
#define GPIO_PIN_MASK_LOW_8BIT(Pin) ((~GPIO_PIN_MASK(Pin)) && 0xFF)
|
|
||||||
|
|
||||||
#endif // __PL061_GPIO_H__
|
#endif // __PL061_GPIO_H__
|
||||||
|
Reference in New Issue
Block a user