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:
Leif Lindholm 2015-10-28 18:11:49 +00:00
parent 41501f18fd
commit 328d8cfa62
2 changed files with 62 additions and 15 deletions

View File

@ -28,6 +28,56 @@
#include <Protocol/EmbeddedGpio.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
@ -88,7 +138,7 @@ Get (
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;
} else {
*Value = 0;
@ -135,22 +185,22 @@ Set (
{
case GPIO_MODE_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;
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
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;
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
MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK_HIGH_8BIT(Gpio));
break;
MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Gpio));
// Set the corresponding data bit to HIGH for 1
PL061SetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio), 0xff);
break;
default:
// Other modes are not supported
@ -194,9 +244,9 @@ GetMode (
}
// 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
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;
} else {
*Mode = GPIO_MODE_OUTPUT_0;

View File

@ -19,6 +19,7 @@
#include <Protocol/EmbeddedGpio.h>
// PL061 GPIO Registers
#define PL061_GPIO_DATA_REG_OFFSET ((UINTN) 0x000)
#define PL061_GPIO_DATA_REG ((UINT32)PcdGet32 (PcdPL061GpioBase) + 0x000)
#define PL061_GPIO_DIR_REG ((UINT32)PcdGet32 (PcdPL061GpioBase) + 0x400)
#define PL061_GPIO_IS_REG ((UINT32)PcdGet32 (PcdPL061GpioBase) + 0x404)
@ -46,9 +47,5 @@
// All bits low except one bit high, native bit length
#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__