Marvell Armada 7k/8k-based platforms may use Pca95xx to extend
amount of the GPIO pins.

This patch introduces support for them. The new driver implements
a generic EMBEDDED_GPIO protocol.

In order to ease description of used PCA9XXX controllers
add a common enum type. It can be used e.g. in the board description
library to specify the expander model on a board (instead of passing
a raw number). Update relevant libraries.

Driver is based on initial work done by Allen Yan <yan...@marvell.com>.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Marcin Wojtas <m...@semihalf.com>
---
 Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.inf                     
   |  45 ++
 Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.h                       
   |  65 ++
 Silicon/Marvell/Include/Protocol/MvGpio.h                                      
   |  16 +
 
Platform/Marvell/Armada70x0Db/Armada70x0DbBoardDescLib/Armada70x0DbBoardDescLib.c
 |   2 +-
 
Platform/Marvell/Armada80x0Db/Armada80x0DbBoardDescLib/Armada80x0DbBoardDescLib.c
 |   4 +-
 Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.c                       
   | 651 ++++++++++++++++++++
 6 files changed, 780 insertions(+), 3 deletions(-)
 create mode 100644 Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.inf
 create mode 100644 Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.h
 create mode 100644 Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.c

diff --git a/Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.inf 
b/Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.inf
new file mode 100644
index 0000000..3b1ab4e
--- /dev/null
+++ b/Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.inf
@@ -0,0 +1,45 @@
+## @file
+#
+#  Copyright (c) 2017, Marvell International Ltd. All rights reserved.<BR>
+#
+#  This program and the accompanying materials are licensed and made available
+#  under the terms and conditions of the BSD License which accompanies this
+#  distribution. The full text of the license may be found at
+#  http://opensource.org/licenses/bsd-license.php
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR
+#  IMPLIED.
+#
+
+[Defines]
+  INF_VERSION                    = 0x0001001A
+  BASE_NAME                      = MvPca95xxDxe
+  FILE_GUID                      = f0e405eb-8407-43b9-88e6-2f7d70715c72
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = MvPca95xxEntryPoint
+
+[Sources]
+  MvPca95xxDxe.c
+  MvPca95xxDxe.h
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  Silicon/Marvell/Marvell.dec
+
+[LibraryClasses]
+  DebugLib
+  MemoryAllocationLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEfiDriverBindingProtocolGuid
+  gEfiI2cIoProtocolGuid
+  gEmbeddedGpioProtocolGuid
+  gMarvellBoardDescProtocolGuid
+
+[Depex]
+  TRUE
diff --git a/Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.h 
b/Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.h
new file mode 100644
index 0000000..1fed739
--- /dev/null
+++ b/Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.h
@@ -0,0 +1,65 @@
+/**
+*
+*  Copyright (c) 2018, Marvell International Ltd. All rights reserved.
+*
+*  This program and the accompanying materials are licensed and made available
+*  under the terms and conditions of the BSD License which accompanies this
+*  distribution. The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR 
IMPLIED.
+*
+**/
+#ifndef __MV_PCA953X_H__
+#define __MV_PCA953X_H__
+
+#include <Library/ArmadaBoardDescLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <Protocol/BoardDesc.h>
+#include <Protocol/MvGpio.h>
+#include <Protocol/MvI2c.h>
+
+#include <Uefi/UefiBaseType.h>
+
+#define PCA95XX_GPIO_SIGNATURE   SIGNATURE_32 ('I', 'O', 'E', 'X')
+
+#define PCA95XX_INPUT_REG        0x0
+#define PCA95XX_OUTPUT_REG       0x2
+#define PCA95XX_DIRECTION_REG    0x6
+
+#define PCA95XX_BANK_SIZE        8
+#define PCA95XX_OPERATION_COUNT  2
+#define PCA95XX_OPERATION_LENGTH 1
+
+typedef enum {
+  PCA9505_PIN_COUNT = 40,
+  PCA9534_PIN_COUNT = 8,
+  PCA9535_PIN_COUNT = 16,
+  PCA9536_PIN_COUNT = 4,
+  PCA9537_PIN_COUNT = 4,
+  PCA9538_PIN_COUNT = 8,
+  PCA9539_PIN_COUNT = 16,
+  PCA9554_PIN_COUNT = 8,
+  PCA9555_PIN_COUNT = 16,
+  PCA9556_PIN_COUNT = 16,
+  PCA9557_PIN_COUNT = 16,
+} PCA95XX_PIN_COUNT;
+
+typedef struct {
+  EMBEDDED_GPIO      GpioProtocol;
+  MV_GPIO_EXPANDER  *GpioExpanders;
+  UINTN              GpioExpanderCount;
+  UINTN              Signature;
+  EFI_HANDLE         ControllerHandle;
+} PCA95XX;
+
+#endif
diff --git a/Silicon/Marvell/Include/Protocol/MvGpio.h 
b/Silicon/Marvell/Include/Protocol/MvGpio.h
index 3319b79..5037fc2 100644
--- a/Silicon/Marvell/Include/Protocol/MvGpio.h
+++ b/Silicon/Marvell/Include/Protocol/MvGpio.h
@@ -25,6 +25,7 @@
 
 typedef enum {
   MV_GPIO_DRIVER_TYPE_SOC_CONTROLLER,
+  MV_GPIO_DRIVER_TYPE_PCA95XX,
 } MV_GPIO_DRIVER_TYPE;
 
 typedef enum {
@@ -37,6 +38,21 @@ typedef enum {
   MV_GPIO_CP2_CONTROLLER1,
 } MV_GPIO_SOC_CONTROLLER_TYPE;
 
+typedef enum {
+  PCA9505_ID,
+  PCA9534_ID,
+  PCA9535_ID,
+  PCA9536_ID,
+  PCA9537_ID,
+  PCA9538_ID,
+  PCA9539_ID,
+  PCA9554_ID,
+  PCA9555_ID,
+  PCA9556_ID,
+  PCA9557_ID,
+  PCA95XX_MAX_ID,
+} MV_GPIO_EXPANDER_TYPE_PCA95XX;
+
 typedef struct {
   VENDOR_DEVICE_PATH        Header;
   MV_GPIO_DRIVER_TYPE       GpioDriverType;
diff --git 
a/Platform/Marvell/Armada70x0Db/Armada70x0DbBoardDescLib/Armada70x0DbBoardDescLib.c
 
b/Platform/Marvell/Armada70x0Db/Armada70x0DbBoardDescLib/Armada70x0DbBoardDescLib.c
index f63d8fd..9595b01 100644
--- 
a/Platform/Marvell/Armada70x0Db/Armada70x0DbBoardDescLib/Armada70x0DbBoardDescLib.c
+++ 
b/Platform/Marvell/Armada70x0Db/Armada70x0DbBoardDescLib/Armada70x0DbBoardDescLib.c
@@ -27,7 +27,7 @@
 // GPIO Expander
 //
 STATIC MV_GPIO_EXPANDER mGpioExpander = {
-  8, /* PCA9555 */
+  PCA9555_ID,
   0x21,
   0x0,
 };
diff --git 
a/Platform/Marvell/Armada80x0Db/Armada80x0DbBoardDescLib/Armada80x0DbBoardDescLib.c
 
b/Platform/Marvell/Armada80x0Db/Armada80x0DbBoardDescLib/Armada80x0DbBoardDescLib.c
index f6e3675..a1ac4bf 100644
--- 
a/Platform/Marvell/Armada80x0Db/Armada80x0DbBoardDescLib/Armada80x0DbBoardDescLib.c
+++ 
b/Platform/Marvell/Armada80x0Db/Armada80x0DbBoardDescLib/Armada80x0DbBoardDescLib.c
@@ -28,12 +28,12 @@
 //
 STATIC MV_GPIO_EXPANDER mGpioExpanders[] = {
   {
-    8, /* PCA9555 */
+    PCA9555_ID,
     0x21,
     0x0,
   },
   {
-    8, /* PCA9555 */
+    PCA9555_ID,
     0x25,
     0x0,
   },
diff --git a/Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.c 
b/Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.c
new file mode 100644
index 0000000..37c2b27
--- /dev/null
+++ b/Silicon/Marvell/Drivers/Gpio/MvPca95xxDxe/MvPca95xxDxe.c
@@ -0,0 +1,651 @@
+/**
+*
+*  Copyright (c) 2018, Marvell International Ltd. All rights reserved.
+*
+*  This program and the accompanying materials are licensed and made available
+*  under the terms and conditions of the BSD License which accompanies this
+*  distribution. The full text of the license may be found at
+*  http://opensource.org/licenses/bsd-license.php
+*
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR 
IMPLIED.
+*
+**/
+
+#include <Protocol/I2cIo.h>
+
+#include <Pi/PiI2c.h>
+
+#include "MvPca95xxDxe.h"
+
+STATIC PCA95XX *mPca95xxInstance;
+
+STATIC MV_GPIO_DEVICE_PATH mDevicePathTemplate = {
+  {
+    {
+      HARDWARE_DEVICE_PATH,
+      HW_VENDOR_DP,
+      {
+        (UINT8) (sizeof (VENDOR_DEVICE_PATH) +
+                 sizeof (MV_GPIO_DRIVER_TYPE)),
+        (UINT8) ((sizeof (VENDOR_DEVICE_PATH) +
+                 sizeof (MV_GPIO_DRIVER_TYPE)) >> 8),
+      },
+    },
+    EFI_CALLER_ID_GUID
+  },
+  MV_GPIO_DRIVER_TYPE_PCA95XX,
+  {
+    END_DEVICE_PATH_TYPE,
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    {
+      sizeof(EFI_DEVICE_PATH_PROTOCOL),
+      0
+    }
+  }
+};
+
+STATIC PCA95XX_PIN_COUNT mPca95xxPinCount[PCA95XX_MAX_ID] = {
+  PCA9505_PIN_COUNT,
+  PCA9534_PIN_COUNT,
+  PCA9535_PIN_COUNT,
+  PCA9536_PIN_COUNT,
+  PCA9537_PIN_COUNT,
+  PCA9538_PIN_COUNT,
+  PCA9539_PIN_COUNT,
+  PCA9554_PIN_COUNT,
+  PCA9555_PIN_COUNT,
+  PCA9556_PIN_COUNT,
+  PCA9557_PIN_COUNT,
+};
+
+#if !defined(MDEPKG_NDEBUG)
+/**
+
+Routine Description:
+
+  Verifies if controller index / GPIO pin values
+  are within proper boundaries.
+
+Arguments:
+
+  ControllerIndex - index of controller
+  GpioPin - which pin to read
+
+Returns:
+
+  EFI_SUCCESS           - GPIO pin / controller index are proper
+  EFI_INVALID_PARAMETER - GPIO pin / controller index is out of range
+**/
+STATIC
+EFI_STATUS
+MvPca95xxValidate (
+  IN UINTN ControllerIndex,
+  IN UINTN GpioPin
+  )
+{
+  UINTN ControllerId;
+
+  if (ControllerIndex >= mPca95xxInstance->GpioExpanderCount) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Invalid GPIO ControllerIndex: %d\n",
+      __FUNCTION__,
+      ControllerIndex));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ControllerId = mPca95xxInstance->GpioExpanders[ControllerIndex].ChipId;
+
+  if (GpioPin >= mPca95xxPinCount[ControllerId]) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: GPIO pin #%d not available in Controller#%d\n",
+      __FUNCTION__,
+      GpioPin,
+      ControllerIndex));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+#endif
+
+EFI_STATUS
+EFIAPI
+MvPca95xxGetI2c (
+  IN     UINTN                 ControllerIndex,
+  IN OUT EFI_I2C_IO_PROTOCOL **I2cIo
+  )
+{
+  UINTN        I2cBus, I2cAddress;
+  UINTN        HandleCount, Index;
+  EFI_HANDLE  *HandleBuffer;
+  EFI_STATUS   Status;
+
+  I2cBus = mPca95xxInstance->GpioExpanders[ControllerIndex].I2cBus;
+  I2cAddress = mPca95xxInstance->GpioExpanders[ControllerIndex].I2cAddress;
+
+  /* Locate Handles of all EfiI2cIoProtocol producers */
+  Status = gBS->LocateHandleBuffer (ByProtocol,
+                  &gEfiI2cIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &HandleBuffer);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Unable to locate handles\n", __FUNCTION__));
+    return Status;
+  }
+
+  /* Iterate over all protocol producers and pick one upon DeviceIndex match */
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->OpenProtocol (HandleBuffer[Index],
+                    &gEfiI2cIoProtocolGuid,
+                    (VOID **)I2cIo,
+                    gImageHandle,
+                    NULL,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: Unable to open protocol\n", __FUNCTION__));
+      gBS->FreePool (HandleBuffer);
+      return Status;
+    }
+    if ((*I2cIo)->DeviceIndex == I2C_DEVICE_INDEX (I2cBus, I2cAddress)) {
+      gBS->FreePool (HandleBuffer);
+      return EFI_SUCCESS;
+    }
+  }
+
+  gBS->FreePool (HandleBuffer);
+
+  return EFI_NOT_FOUND;
+}
+
+EFI_STATUS
+EFIAPI
+MvPca95xxI2cTransfer (
+  IN EFI_I2C_IO_PROTOCOL *I2cIo,
+  IN UINT8                Address,
+  IN UINT8               *Buffer,
+  IN UINT32               Flag
+  )
+{
+  EFI_I2C_REQUEST_PACKET *RequestPacket;
+  UINTN RequestPacketSize;
+  UINT8 AddressBuffer;
+  EFI_STATUS Status;
+
+  RequestPacketSize = sizeof (UINTN) +
+                      sizeof (EFI_I2C_OPERATION) * PCA95XX_OPERATION_COUNT;
+  RequestPacket = AllocateZeroPool (RequestPacketSize);
+  if (RequestPacket == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  /* Operations contain address and payload, consecutively. */
+  RequestPacket->OperationCount = PCA95XX_OPERATION_COUNT;
+  RequestPacket->Operation[0].LengthInBytes = PCA95XX_OPERATION_LENGTH;
+  RequestPacket->Operation[0].Buffer = &AddressBuffer;
+  RequestPacket->Operation[0].Buffer[0] = Address & MAX_UINT8;
+  RequestPacket->Operation[1].LengthInBytes = PCA95XX_OPERATION_LENGTH;
+  RequestPacket->Operation[1].Buffer = Buffer;
+  RequestPacket->Operation[1].Flags = Flag;
+
+  Status = I2cIo->QueueRequest (I2cIo, 0, NULL, RequestPacket, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: transmission error: 0x%d\n",
+      __FUNCTION__,
+      Status));
+  }
+
+  gBS->FreePool(RequestPacket);
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+MvPca95xxReadRegs (
+  IN  EFI_I2C_IO_PROTOCOL *I2cIo,
+  IN  UINT8                Reg,
+  OUT UINT8               *RegVal
+  )
+{
+  return MvPca95xxI2cTransfer (I2cIo, Reg, RegVal, I2C_FLAG_READ);
+}
+
+STATIC
+EFI_STATUS
+MvPca95xxWriteRegs (
+  IN  EFI_I2C_IO_PROTOCOL *I2cIo,
+  IN  UINTN                Reg,
+  IN  UINT8                RegVal
+  )
+{
+  return MvPca95xxI2cTransfer (I2cIo, Reg, &RegVal, I2C_FLAG_NORESTART);
+}
+
+STATIC
+EFI_STATUS
+MvPca95xxSetOutputValue (
+  IN UINTN               ControllerIndex,
+  IN UINTN               GpioPin,
+  IN EMBEDDED_GPIO_MODE  Mode
+  )
+{
+  EFI_I2C_IO_PROTOCOL *I2cIo;
+  EFI_STATUS Status;
+  UINT8 RegVal;
+  UINTN Bank;
+
+  Status = MvPca95xxGetI2c (ControllerIndex, &I2cIo);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to get I2C protocol\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  Bank = GpioPin / PCA95XX_BANK_SIZE;
+
+  Status = MvPca95xxReadRegs (I2cIo, PCA95XX_OUTPUT_REG + Bank, &RegVal);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to read device register\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (Mode == GPIO_MODE_OUTPUT_1) {
+    RegVal |= (1 << (GpioPin % PCA95XX_BANK_SIZE));
+  } else {
+    RegVal &= ~(1 << (GpioPin % PCA95XX_BANK_SIZE));
+  }
+
+  Status = MvPca95xxWriteRegs (I2cIo, PCA95XX_OUTPUT_REG + Bank, RegVal);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to write device register\n", __FUNCTION__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MvPca95xxSetDirection (
+  IN UINTN              ControllerIndex,
+  IN UINTN              GpioPin,
+  IN EMBEDDED_GPIO_MODE Mode
+  )
+{
+  EFI_I2C_IO_PROTOCOL *I2cIo;
+  EFI_STATUS Status;
+  UINT8 RegVal;
+  UINTN Bank;
+
+  Status = MvPca95xxGetI2c (ControllerIndex, &I2cIo);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to get I2C protocol\n", __FUNCTION__));
+    return Status;
+  }
+
+  Bank = GpioPin / PCA95XX_BANK_SIZE;
+
+  Status = MvPca95xxReadRegs (I2cIo, PCA95XX_DIRECTION_REG + Bank, &RegVal);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to read device register\n", __FUNCTION__));
+    return Status;
+  }
+
+  if (Mode == GPIO_MODE_INPUT) {
+    RegVal |= (1 << (GpioPin % PCA95XX_BANK_SIZE));
+  } else {
+    RegVal &= ~(1 << (GpioPin % PCA95XX_BANK_SIZE));
+  }
+
+  Status = MvPca95xxWriteRegs (I2cIo, PCA95XX_DIRECTION_REG + Bank, RegVal);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to write device register\n", __FUNCTION__));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+MvPca95xxReadMode (
+  IN  UINTN               ControllerIndex,
+  IN  UINTN               GpioPin,
+  OUT EMBEDDED_GPIO_MODE *Mode
+  )
+{
+  EFI_I2C_IO_PROTOCOL *I2cIo;
+  EFI_STATUS Status;
+  UINT8 RegVal;
+  UINTN Bank;
+
+  ASSERT_EFI_ERROR (MvPca95xxValidate (ControllerIndex, GpioPin));
+
+  Status = MvPca95xxGetI2c (ControllerIndex, &I2cIo);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to get I2C protocol\n", __FUNCTION__));
+    return Status;
+  }
+
+  Bank = GpioPin / PCA95XX_BANK_SIZE;
+
+  Status = MvPca95xxReadRegs (I2cIo, PCA95XX_DIRECTION_REG + Bank, &RegVal);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to read device register\n", __FUNCTION__));
+    return Status;
+  }
+
+  if (RegVal & (1 << (GpioPin % PCA95XX_BANK_SIZE))) {
+    *Mode = GPIO_MODE_INPUT;
+  } else {
+    Status = MvPca95xxReadRegs (I2cIo, PCA95XX_INPUT_REG + Bank, &RegVal);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: fail to read device register\n", 
__FUNCTION__));
+      return Status;
+    }
+
+    if (RegVal & (1 << (GpioPin % PCA95XX_BANK_SIZE))) {
+      *Mode = GPIO_MODE_OUTPUT_1;
+    } else {
+      *Mode = GPIO_MODE_OUTPUT_0;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+Routine Description:
+
+  Gets the mode (function) of a GPIO pin
+
+Arguments:
+
+  This  - pointer to protocol
+  Gpio  - which pin
+  Mode  - pointer to output mode value
+
+Returns:
+
+  EFI_SUCCESS           - mode value retrieved
+  EFI_INVALID_PARAMETER - Mode is a null pointer or Gpio pin is out of range
+
+**/
+STATIC
+EFI_STATUS
+MvPca95xxGetMode (
+  IN  EMBEDDED_GPIO       *This,
+  IN  EMBEDDED_GPIO_PIN    Gpio,
+  OUT EMBEDDED_GPIO_MODE  *Mode
+  )
+{
+  EFI_STATUS Status;
+  UINTN ControllerIndex;
+  UINTN GpioPin;
+
+  GpioPin = GPIO_PIN (Gpio);
+  ControllerIndex = GPIO_PORT (Gpio);
+
+  ASSERT_EFI_ERROR (MvPca95xxValidate (ControllerIndex, GpioPin));
+
+  Status = MvPca95xxReadMode (ControllerIndex, GpioPin, Mode);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: fail to get pin %d of controller#%d mode\n",
+      __FUNCTION__,
+      GpioPin,
+      ControllerIndex));
+  }
+
+  return Status;
+}
+
+/**
+
+Routine Description:
+
+  Gets the state of a GPIO pin
+
+Arguments:
+
+  This  - pointer to protocol
+  Gpio  - which pin to read
+  Value - state of the pin
+
+Returns:
+
+  EFI_SUCCESS           - GPIO state returned in Value
+  EFI_INVALID_PARAMETER - Value is NULL pointer or Gpio pin is out of range
+**/
+STATIC
+EFI_STATUS
+MvPca95xxGet (
+  IN  EMBEDDED_GPIO      *This,
+  IN  EMBEDDED_GPIO_PIN   Gpio,
+  OUT UINTN              *Value
+  )
+{
+  EFI_I2C_IO_PROTOCOL *I2cIo;
+  EFI_STATUS Status;
+  UINTN ControllerIndex;
+  UINTN GpioPin;
+  UINT8 RegVal;
+  UINTN Bank;
+
+  GpioPin = GPIO_PIN (Gpio);
+  ControllerIndex = GPIO_PORT (Gpio);
+
+  ASSERT_EFI_ERROR (MvPca95xxValidate (ControllerIndex, GpioPin));
+
+  Status = MvPca95xxGetI2c (ControllerIndex, &I2cIo);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to get I2C protocol\n", __FUNCTION__));
+    return Status;
+  }
+
+  Bank = GpioPin / PCA95XX_BANK_SIZE;
+
+  Status = MvPca95xxReadRegs (I2cIo, PCA95XX_INPUT_REG + Bank, &RegVal);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: fail to read device register\n", __FUNCTION__));
+    return Status;
+  }
+
+  if (RegVal & (1 << (GpioPin % PCA95XX_BANK_SIZE))) {
+    *Value = 1;
+  } else {
+    *Value = 0;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+Routine Description:
+
+  Sets the state of a GPIO pin
+
+Arguments:
+
+  This  - pointer to protocol
+  Gpio  - which pin to modify
+  Mode  - mode to set
+
+Returns:
+
+  EFI_SUCCESS           - GPIO set as requested
+  EFI_UNSUPPORTED       - Mode is not supported
+  EFI_INVALID_PARAMETER - Gpio pin is out of range
+**/
+STATIC
+EFI_STATUS
+MvPca95xxSet (
+  IN EMBEDDED_GPIO       *This,
+  IN EMBEDDED_GPIO_PIN   Gpio,
+  IN EMBEDDED_GPIO_MODE  Mode
+  )
+{
+  EFI_STATUS Status;
+  UINTN ControllerIndex;
+  UINTN GpioPin;
+
+  GpioPin = GPIO_PIN (Gpio);
+  ControllerIndex = GPIO_PORT (Gpio);
+
+  ASSERT_EFI_ERROR (MvPca95xxValidate (ControllerIndex, GpioPin));
+
+  switch (Mode) {
+  case GPIO_MODE_OUTPUT_0:
+  case GPIO_MODE_OUTPUT_1:
+    Status = MvPca95xxSetOutputValue (ControllerIndex, GpioPin, Mode);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: fail to set ouput value\n", __FUNCTION__));
+      return Status;
+    }
+
+  /* Fall-through */
+  case GPIO_MODE_INPUT:
+    Status = MvPca95xxSetDirection (ControllerIndex, GpioPin, Mode);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: fail to set direction\n", __FUNCTION__));
+      return Status;
+    }
+    break;
+
+  default:
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+
+Routine Description:
+
+  Sets the pull-up / pull-down resistor of a GPIO pin
+
+Arguments:
+
+  This  - pointer to protocol
+  Gpio  - which pin
+  Direction - pull-up, pull-down, or none
+
+Returns:
+
+  EFI_UNSUPPORTED - Can not perform the requested operation
+
+**/
+EFI_STATUS
+EFIAPI
+MvPca95xxSetPull (
+  IN EMBEDDED_GPIO       *This,
+  IN EMBEDDED_GPIO_PIN   Gpio,
+  IN EMBEDDED_GPIO_PULL  Direction
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+STATIC
+VOID
+MvPca95xxInitProtocol (
+  IN EMBEDDED_GPIO *GpioProtocol
+  )
+{
+  GpioProtocol->Get     = MvPca95xxGet;
+  GpioProtocol->Set     = MvPca95xxSet;
+  GpioProtocol->GetMode = MvPca95xxGetMode;
+  GpioProtocol->SetPull = MvPca95xxSetPull;
+}
+
+EFI_STATUS
+EFIAPI
+MvPca95xxEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  MARVELL_BOARD_DESC_PROTOCOL *MvBoardProtocol;
+  MV_BOARD_GPIO_DESCRIPTION *GpioDescription;
+  MV_GPIO_DEVICE_PATH *Pca95xxDevicePath;
+  EFI_STATUS Status;
+
+  /* Obtain list of available controllers */
+  Status = gBS->LocateProtocol (&gMarvellBoardDescProtocolGuid,
+                  NULL,
+                  (VOID **)&MvBoardProtocol);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Cannot locate BoardDesc protocol\n",
+      __FUNCTION__));
+    return Status;
+  }
+
+  Status = MvBoardProtocol->GpioDescriptionGet (MvBoardProtocol,
+             &GpioDescription);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Cannot get GPIO board desc from BoardDesc protocol\n",
+      __FUNCTION__));
+    return Status;
+  } else if (GpioDescription->GpioExpanders == NULL) {
+    /* Silently exit, if the board does not support the controllers */
+    return EFI_SUCCESS;
+  }
+
+  Pca95xxDevicePath = AllocateCopyPool (sizeof (MV_GPIO_DEVICE_PATH),
+                        &mDevicePathTemplate);
+  if (Pca95xxDevicePath == NULL) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Fail to allocate Pca95xxDevicePath\n",
+      __FUNCTION__));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ErrPca95xxDevicePathAlloc;
+  }
+
+  mPca95xxInstance = AllocateZeroPool (sizeof (PCA95XX));
+  if (mPca95xxInstance == NULL) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Fail to allocate mPca95xxInstance\n",
+      __FUNCTION__));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ErrPca95xxInstanceAlloc;
+  }
+
+  MvPca95xxInitProtocol (&mPca95xxInstance->GpioProtocol);
+
+  mPca95xxInstance->Signature = PCA95XX_GPIO_SIGNATURE;
+  mPca95xxInstance->GpioExpanders = GpioDescription->GpioExpanders;
+  mPca95xxInstance->GpioExpanderCount = GpioDescription->GpioExpanderCount;
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &(mPca95xxInstance->ControllerHandle),
+                  &gEmbeddedGpioProtocolGuid,
+                  &(mPca95xxInstance->GpioProtocol),
+                  &gEfiDevicePathProtocolGuid,
+                  (EFI_DEVICE_PATH_PROTOCOL *)Pca95xxDevicePath,
+                  NULL);
+  if (EFI_ERROR (Status)) {
+    goto ErrInstallProtocols;
+  }
+
+  gBS->FreePool (GpioDescription);
+
+  return EFI_SUCCESS;
+
+ErrInstallProtocols:
+  gBS->FreePool (mPca95xxInstance);
+
+ErrPca95xxInstanceAlloc:
+  gBS->FreePool (Pca95xxDevicePath);
+
+ErrPca95xxDevicePathAlloc:
+  gBS->FreePool (GpioDescription);
+
+  return Status;
+}
-- 
2.7.4

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

Reply via email to