ArmPlatformPkg: PL061 - rewrite the hardware interaction
[mirror_edk2.git] / ArmPlatformPkg / Drivers / PL061GpioDxe / PL061Gpio.c
index 45897ca..38341a3 100644 (file)
 #include <Protocol/EmbeddedGpio.h>\r
 #include <Drivers/PL061Gpio.h>\r
 \r
+//\r
+// The PL061 is a strange beast. The 8-bit data register is aliased across a\r
+// region 0x400 bytes in size, with bits [9:2] of the address operating as a\r
+// mask for both read and write operations:\r
+// For reads:\r
+//   - All bits where their corresponding mask bit is 1 return the current\r
+//     value of that bit in the GPIO_DATA register.\r
+//   - All bits where their corresponding mask bit is 0 return 0.\r
+// For writes:\r
+//   - All bits where their corresponding mask bit is 1 set the bit in the\r
+//     GPIO_DATA register to the written value.\r
+//   - All bits where their corresponding mask bit is 0 are left untouched\r
+//     in the GPIO_DATA register.\r
+//\r
+// To keep this driver intelligible, PL061EffectiveAddress, PL061GetPins and\r
+// Pl061SetPins provide an internal abstraction from this interface.\r
+\r
+STATIC\r
+UINTN\r
+EFIAPI\r
+PL061EffectiveAddress (\r
+  IN UINTN Address,\r
+  IN UINTN Mask\r
+  )\r
+{\r
+  return ((Address + PL061_GPIO_DATA_REG_OFFSET) + (Mask << 2));\r
+}\r
+\r
+STATIC\r
+UINTN\r
+EFIAPI\r
+PL061GetPins (\r
+  IN UINTN Address,\r
+  IN UINTN Mask\r
+  )\r
+{\r
+  return MmioRead8 (PL061EffectiveAddress (Address, Mask));\r
+}\r
+\r
+STATIC\r
+VOID\r
+EFIAPI\r
+PL061SetPins (\r
+  IN UINTN Address,\r
+  IN UINTN Mask,\r
+  IN UINTN Value\r
+  )\r
+{\r
+  MmioWrite8 (PL061EffectiveAddress (Address, Mask), Value);\r
+}\r
 \r
 /**\r
   Function implementations\r
@@ -88,7 +138,7 @@ Get (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  if (MmioRead8 (PL061_GPIO_DATA_REG) & GPIO_PIN_MASK_HIGH_8BIT(Gpio)) {\r
+  if (PL061GetPins (PL061_GPIO_DATA_REG, Gpio)) {\r
     *Value = 1;\r
   } else {\r
     *Value = 0;\r
@@ -135,22 +185,22 @@ Set (
   {\r
     case GPIO_MODE_INPUT:\r
       // Set the corresponding direction bit to LOW for input\r
-      MmioAnd8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK_LOW_8BIT(Gpio));\r
+      MmioAnd8 (PL061_GPIO_DIR_REG, ~GPIO_PIN_MASK(Gpio) & 0xFF);\r
       break;\r
 \r
     case GPIO_MODE_OUTPUT_0:\r
-      // Set the corresponding data bit to LOW for 0\r
-      MmioAnd8 (PL061_GPIO_DATA_REG, GPIO_PIN_MASK_LOW_8BIT(Gpio));\r
       // Set the corresponding direction bit to HIGH for output\r
-      MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK_HIGH_8BIT(Gpio));\r
+      MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Gpio));\r
+      // Set the corresponding data bit to LOW for 0\r
+      PL061SetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio), 0);\r
       break;\r
 \r
     case GPIO_MODE_OUTPUT_1:\r
-      // Set the corresponding data bit to HIGH for 1\r
-      MmioOr8 (PL061_GPIO_DATA_REG, GPIO_PIN_MASK_HIGH_8BIT(Gpio));\r
       // Set the corresponding direction bit to HIGH for output\r
-      MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK_HIGH_8BIT(Gpio));\r
-      break;\r
+      MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Gpio));\r
+      // Set the corresponding data bit to HIGH for 1\r
+      PL061SetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio), 0xff);\r
+    break;\r
 \r
     default:\r
       // Other modes are not supported\r
@@ -194,9 +244,9 @@ GetMode (
   }\r
 \r
   // Check if it is input or output\r
-  if (MmioRead8 (PL061_GPIO_DIR_REG) & GPIO_PIN_MASK_HIGH_8BIT(Gpio)) {\r
+  if (MmioRead8 (PL061_GPIO_DIR_REG) & GPIO_PIN_MASK(Gpio)) {\r
     // Pin set to output\r
-    if (MmioRead8 (PL061_GPIO_DATA_REG) & GPIO_PIN_MASK_HIGH_8BIT(Gpio)) {\r
+    if (PL061GetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio))) {\r
       *Mode = GPIO_MODE_OUTPUT_1;\r
     } else {\r
       *Mode = GPIO_MODE_OUTPUT_0;\r