diff mbox

[edk2,4/4] ArmPlatformPkg: PL061 - rewrite the hardware interaction

Message ID 1456503415-17029-5-git-send-email-leif.lindholm@linaro.org
State Accepted
Commit 328d8cfa6278a5558ce510662df73f4c17086567
Headers show

Commit Message

Leif Lindholm Feb. 26, 2016, 4:16 p.m. UTC
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>

---
 ArmPlatformPkg/Drivers/PL061GpioDxe/PL061Gpio.c | 72 +++++++++++++++++++++----
 ArmPlatformPkg/Include/Drivers/PL061Gpio.h      |  5 +-
 2 files changed, 62 insertions(+), 15 deletions(-)

-- 
2.1.4

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
diff mbox

Patch

diff --git a/ArmPlatformPkg/Drivers/PL061GpioDxe/PL061Gpio.c b/ArmPlatformPkg/Drivers/PL061GpioDxe/PL061Gpio.c
index 45897ca..38341a3 100644
--- a/ArmPlatformPkg/Drivers/PL061GpioDxe/PL061Gpio.c
+++ b/ArmPlatformPkg/Drivers/PL061GpioDxe/PL061Gpio.c
@@ -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;
diff --git a/ArmPlatformPkg/Include/Drivers/PL061Gpio.h b/ArmPlatformPkg/Include/Drivers/PL061Gpio.h
index 38458f4..8fde2bb 100644
--- a/ArmPlatformPkg/Include/Drivers/PL061Gpio.h
+++ b/ArmPlatformPkg/Include/Drivers/PL061Gpio.h
@@ -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__