From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: None (no SPF record) identity=mailfrom; client-ip=2a00:1450:4864:20::141; helo=mail-lf1-x141.google.com; envelope-from=mw@semihalf.com; receiver=edk2-devel@lists.01.org Received: from mail-lf1-x141.google.com (mail-lf1-x141.google.com [IPv6:2a00:1450:4864:20::141]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 9577F2194D3AE for ; Wed, 9 Jan 2019 17:45:10 -0800 (PST) Received: by mail-lf1-x141.google.com with SMTP id e26so7140888lfc.2 for ; Wed, 09 Jan 2019 17:45:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=semihalf-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=U8/GuorBRm9Uigf4EafLvtZp+XPctCGJ7Ao5voyAQ0c=; b=OfJaEoEH2bQZ66HSXFRVGO2DPXpAak/Ado8ffBQk9AomFX2Dh/2VAWBbucLtsfvx0T ywuv974jkpSLHrHWm4yH0+5ojwslsTE7pFzlauLlCTvJCRBpm6Zw7OosW1TnOqIUpHdb sZbxJO1/kVuOPZ7WYQ5Docd/wPRWMg1e2LZMsz0zJSHxQKTEZV+pN+OWKS2CwPjAu+PZ 8ilO2u19C0uSg/F9kJKlATE1SgdeOgNvGVOzIQDEq59yfIYewYtJw7q0Zo3GeyShbkaE aeMv1YRTtEvhd/P2dXsgqCOzhoMLMcG9p2fBf4i1tFPaaNNUJoorHo1Gx5a9iuux9/Jk R3hw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=U8/GuorBRm9Uigf4EafLvtZp+XPctCGJ7Ao5voyAQ0c=; b=ZGFSfi338iXZureb+gMO4Gwxti4yGB5Y5pYUXnJRPmb+aN5myHNj2v0ymcPEF2zCDv 0/Z7eZWnMvfOWCvE9gO1WbXpXfguZiV5RRznkyZb5jN75Lb50YfFr4zmVd3T85N2toaE 3aM+ItpUsqrlq/F503+0YDz3vKDa6qOvXd46hEn9dTz5kTQxybpyF4GOuyndYeyFPoeE EshUPF8opre7e4x3u8y5DK/9QiEYBS0Q9LRBLVmgikZiu+TrezCOYWVAr/Cvr6ytU4jb Zf+xtvPlQCkc3hRi/PzTCuwxjZqKIq21Uz935MXL2CjV/D/X5X+GNai2v4bHD0WOgixL qwLw== X-Gm-Message-State: AJcUukeO2EUCvRQNFb1NJrc8wQXhxoeZCTmpnqasZqpAsz0xkuuAj/yY 8ggKUDEuRyZmNK433X/sGKQC5WCxItE= X-Google-Smtp-Source: ALg8bN6r2OLdHFDRmEUtfCG1dpYYKOrk7DUmvgHC0EenxTO7Wjh653jDEYl3yMbR8Ci2WKQ+V+jnag== X-Received: by 2002:a19:5a05:: with SMTP id o5mr4988089lfb.140.1547084708192; Wed, 09 Jan 2019 17:45:08 -0800 (PST) Received: from gilgamesh.semihalf.com (31-172-191-173.noc.fibertech.net.pl. [31.172.191.173]) by smtp.gmail.com with ESMTPSA id l21-v6sm15034142ljj.48.2019.01.09.17.45.06 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 09 Jan 2019 17:45:07 -0800 (PST) From: Marcin Wojtas To: edk2-devel@lists.01.org Cc: leif.lindholm@linaro.org, ard.biesheuvel@linaro.org, nadavh@marvell.com, mw@semihalf.com, jsd@semihalf.com, jaz@semihalf.com, kostap@marvell.com Date: Thu, 10 Jan 2019 02:44:37 +0100 Message-Id: <1547084679-29597-11-git-send-email-mw@semihalf.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1547084679-29597-1-git-send-email-mw@semihalf.com> References: <1547084679-29597-1-git-send-email-mw@semihalf.com> Subject: [platforms: PATCH v2 10/12] Marvell/Drivers: MvPca95xxDxe: Introduce GPIO expander driver X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 10 Jan 2019 01:45:11 -0000 X-List-Received-Date: Thu, 10 Jan 2019 01:45:11 -0000 X-List-Received-Date: Thu, 10 Jan 2019 01:45:11 -0000 X-List-Received-Date: Thu, 10 Jan 2019 01:45:11 -0000 X-List-Received-Date: Thu, 10 Jan 2019 01:45:11 -0000 X-List-Received-Date: Thu, 10 Jan 2019 01:45:11 -0000 X-List-Received-Date: Thu, 10 Jan 2019 01:45:11 -0000 X-List-Received-Date: Thu, 10 Jan 2019 01:45:11 -0000 X-List-Received-Date: Thu, 10 Jan 2019 01:45:11 -0000 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 . Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Marcin Wojtas --- 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.
+# +# 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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 + +#include + +#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