]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPlatformPkg/Drivers/PL061GpioDxe/PL061Gpio.c
ArmPlatformPkg/PL061Gpio: fix the offset value in Get function
[mirror_edk2.git] / ArmPlatformPkg / Drivers / PL061GpioDxe / PL061Gpio.c
index 937b725821555804b6baae15c6acec10b7f046f7..81b9f6daece8c0cce9e2dc04c79655ce329590d3 100644 (file)
@@ -1,6 +1,7 @@
 /** @file\r
 *\r
 *  Copyright (c) 2011, ARM Limited. All rights reserved.\r
+*  Copyright (c) 2016, Linaro Limited. All rights reserved.\r
 *\r
 *  This program and the accompanying materials\r
 *  are licensed and made available under the terms and conditions of the BSD\r
@@ -19,6 +20,7 @@
 #include <Library/BaseMemoryLib.h>\r
 #include <Library/DebugLib.h>\r
 #include <Library/IoLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
 #include <Library/PcdLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
 #include <Library/UefiLib.h>\r
 #include <Protocol/EmbeddedGpio.h>\r
 #include <Drivers/PL061Gpio.h>\r
 \r
-BOOLEAN     mPL061Initialized = FALSE;\r
-\r
-/**\r
-  Function implementations\r
-**/\r
+PLATFORM_GPIO_CONTROLLER *mPL061PlatformGpio;\r
 \r
 EFI_STATUS\r
-PL061Identify (\r
-  VOID\r
+EFIAPI\r
+PL061Locate (\r
+  IN  EMBEDDED_GPIO_PIN Gpio,\r
+  OUT UINTN             *ControllerIndex,\r
+  OUT UINTN             *ControllerOffset,\r
+  OUT UINTN             *RegisterBase\r
   )\r
 {\r
-  // Check if this is a PrimeCell Peripheral\r
-  if (    (MmioRead8 (PL061_GPIO_PCELL_ID0) != 0x0D)\r
-      ||  (MmioRead8 (PL061_GPIO_PCELL_ID1) != 0xF0)\r
-      ||  (MmioRead8 (PL061_GPIO_PCELL_ID2) != 0x05)\r
-      ||  (MmioRead8 (PL061_GPIO_PCELL_ID3) != 0xB1)) {\r
-    return EFI_NOT_FOUND;\r
+  UINT32    Index;\r
+\r
+  for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) {\r
+    if (    (Gpio >= mPL061PlatformGpio->GpioController[Index].GpioIndex)\r
+        &&  (Gpio < mPL061PlatformGpio->GpioController[Index].GpioIndex\r
+             + mPL061PlatformGpio->GpioController[Index].InternalGpioCount)) {\r
+      *ControllerIndex = Index;\r
+      *ControllerOffset = Gpio % mPL061PlatformGpio->GpioController[Index].InternalGpioCount;\r
+      *RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase;\r
+      return EFI_SUCCESS;\r
+    }\r
   }\r
+  DEBUG ((EFI_D_ERROR, "%a, failed to locate gpio %d\n", __func__, Gpio));\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
 \r
-  // Check if this PrimeCell Peripheral is the PL061 GPIO\r
-  if (    (MmioRead8 (PL061_GPIO_PERIPH_ID0) != 0x61)\r
-      ||  (MmioRead8 (PL061_GPIO_PERIPH_ID1) != 0x10)\r
-      ||  ((MmioRead8 (PL061_GPIO_PERIPH_ID2) & 0xF) != 0x04)\r
-      ||  (MmioRead8 (PL061_GPIO_PERIPH_ID3) != 0x00)) {\r
-    return EFI_NOT_FOUND;\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
-  return EFI_SUCCESS;\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
+**/\r
+\r
 EFI_STATUS\r
-PL061Initialize (\r
+PL061Identify (\r
   VOID\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
+  UINTN    Index;\r
+  UINTN    RegisterBase;\r
 \r
-  // Check if the PL061 GPIO module exists on board\r
-  Status = PL061Identify();\r
-  if (EFI_ERROR (Status)) {\r
-    Status = EFI_DEVICE_ERROR;\r
-    goto EXIT;\r
+  if (   (mPL061PlatformGpio->GpioCount == 0)\r
+      || (mPL061PlatformGpio->GpioControllerCount == 0)) {\r
+     return EFI_NOT_FOUND;\r
   }\r
 \r
-  // Do other hardware initialisation things here as required\r
+  for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) {\r
+    if (mPL061PlatformGpio->GpioController[Index].InternalGpioCount != PL061_GPIO_PINS) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
 \r
-  // Disable Interrupts\r
-  //if (MmioRead8 (PL061_GPIO_IE_REG) != 0) {\r
-  //   // Ensure interrupts are disabled\r
-  //}\r
+    RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase;\r
 \r
-  mPL061Initialized = TRUE;\r
+    // Check if this is a PrimeCell Peripheral\r
+    if (    (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID0) != 0x0D)\r
+        ||  (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID1) != 0xF0)\r
+        ||  (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID2) != 0x05)\r
+        ||  (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID3) != 0xB1)) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
 \r
-  EXIT:\r
-  return Status;\r
+    // Check if this PrimeCell Peripheral is the PL061 GPIO\r
+    if (    (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID0) != 0x61)\r
+        ||  (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID1) != 0x10)\r
+        ||  ((MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID2) & 0xF) != 0x04)\r
+        ||  (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID3) != 0x00)) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -110,29 +177,22 @@ Get (
   )\r
 {\r
   EFI_STATUS    Status = EFI_SUCCESS;\r
+  UINTN         Index, Offset, RegisterBase;\r
 \r
-  if (    (Value == NULL)\r
-      ||  (Gpio > LAST_GPIO_PIN))\r
-  {\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
+  Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);\r
+  ASSERT_EFI_ERROR (Status);\r
 \r
-  // Initialize the hardware if not already done\r
-  if (!mPL061Initialized) {\r
-    Status = PL061Initialize();\r
-    if (EFI_ERROR(Status)) {\r
-      goto EXIT;\r
-    }\r
+  if (Value == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  if (MmioRead8 (PL061_GPIO_DATA_REG) & GPIO_PIN_MASK_HIGH_8BIT(Gpio)) {\r
+  if (PL061GetPins (RegisterBase, GPIO_PIN_MASK(Offset))) {\r
     *Value = 1;\r
   } else {\r
     *Value = 0;\r
   }\r
 \r
-  EXIT:\r
-  return Status;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -162,40 +222,31 @@ Set (
   )\r
 {\r
   EFI_STATUS    Status = EFI_SUCCESS;\r
+  UINTN         Index, Offset, RegisterBase;\r
 \r
-  // Check for errors\r
-  if (Gpio > LAST_GPIO_PIN) {\r
-    Status = EFI_INVALID_PARAMETER;\r
-    goto EXIT;\r
-  }\r
-\r
-  // Initialize the hardware if not already done\r
-  if (!mPL061Initialized) {\r
-    Status = PL061Initialize();\r
-    if (EFI_ERROR(Status)) {\r
-      goto EXIT;\r
-    }\r
-  }\r
+  Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);\r
+  ASSERT_EFI_ERROR (Status);\r
 \r
   switch (Mode)\r
   {\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 (RegisterBase + PL061_GPIO_DIR_REG,\r
+                ~GPIO_PIN_MASK(Offset) & 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 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset));\r
+      // Set the corresponding data bit to LOW for 0\r
+      PL061SetPins (RegisterBase, GPIO_PIN_MASK(Offset), 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
+      MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset));\r
+      // Set the corresponding data bit to HIGH for 1\r
+      PL061SetPins (RegisterBase, GPIO_PIN_MASK(Offset), 0xff);\r
       break;\r
 \r
     default:\r
@@ -203,8 +254,7 @@ Set (
       return EFI_UNSUPPORTED;\r
   }\r
 \r
-EXIT:\r
-  return Status;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -233,26 +283,21 @@ GetMode (
   OUT EMBEDDED_GPIO_MODE  *Mode\r
   )\r
 {\r
-  EFI_STATUS Status;\r
+  EFI_STATUS    Status = EFI_SUCCESS;\r
+  UINTN         Index, Offset, RegisterBase;\r
+\r
+  Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);\r
+  ASSERT_EFI_ERROR (Status);\r
 \r
   // Check for errors\r
-  if (    (Mode == NULL)\r
-      ||  (Gpio > LAST_GPIO_PIN)) {\r
+  if (Mode == NULL) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  // Initialize the hardware if not already done\r
-  if (!mPL061Initialized) {\r
-    Status = PL061Initialize();\r
-    if (EFI_ERROR(Status)) {\r
-      return Status;\r
-    }\r
-  }\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 (RegisterBase + PL061_GPIO_DIR_REG) & GPIO_PIN_MASK(Offset)) {\r
     // Pin set to output\r
-    if (MmioRead8 (PL061_GPIO_DATA_REG) & GPIO_PIN_MASK_HIGH_8BIT(Gpio)) {\r
+    if (PL061GetPins (RegisterBase, GPIO_PIN_MASK(Offset))) {\r
       *Mode = GPIO_MODE_OUTPUT_1;\r
     } else {\r
       *Mode = GPIO_MODE_OUTPUT_0;\r
@@ -321,14 +366,39 @@ PL061InstallProtocol (
   IN EFI_SYSTEM_TABLE   *SystemTable\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
-  EFI_HANDLE  Handle;\r
+  EFI_STATUS            Status;\r
+  EFI_HANDLE            Handle;\r
+  GPIO_CONTROLLER       *GpioController;\r
 \r
   //\r
   // Make sure the Gpio protocol has not been installed in the system yet.\r
   //\r
   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEmbeddedGpioProtocolGuid);\r
 \r
+  Status = gBS->LocateProtocol (&gPlatformGpioProtocolGuid, NULL, (VOID **)&mPL061PlatformGpio);\r
+  if (EFI_ERROR (Status) && (Status == EFI_NOT_FOUND)) {\r
+    // Create the mPL061PlatformGpio\r
+    mPL061PlatformGpio = (PLATFORM_GPIO_CONTROLLER *)AllocateZeroPool (sizeof (PLATFORM_GPIO_CONTROLLER) + sizeof (GPIO_CONTROLLER));\r
+    if (mPL061PlatformGpio == NULL) {\r
+      DEBUG ((EFI_D_ERROR, "%a: failed to allocate PLATFORM_GPIO_CONTROLLER\n", __func__));\r
+      return EFI_BAD_BUFFER_SIZE;\r
+    }\r
+\r
+    mPL061PlatformGpio->GpioCount = PL061_GPIO_PINS;\r
+    mPL061PlatformGpio->GpioControllerCount = 1;\r
+    mPL061PlatformGpio->GpioController = (GPIO_CONTROLLER *)((UINTN) mPL061PlatformGpio + sizeof (PLATFORM_GPIO_CONTROLLER));\r
+\r
+    GpioController = mPL061PlatformGpio->GpioController;\r
+    GpioController->RegisterBase = (UINTN) PcdGet32 (PcdPL061GpioBase);\r
+    GpioController->GpioIndex = 0;\r
+    GpioController->InternalGpioCount = PL061_GPIO_PINS;\r
+  }\r
+\r
+  Status = PL061Identify();\r
+  if (EFI_ERROR(Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
   // Install the Embedded GPIO Protocol onto a new handle\r
   Handle = NULL;\r
   Status = gBS->InstallMultipleProtocolInterfaces(\r