/**
|
*
|
* Copyright (c) 2018, Marvell International Ltd. All rights reserved.
|
*
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
*
|
**/
|
|
#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__));
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
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;
|
}
|
|
return EFI_SUCCESS;
|
|
ErrInstallProtocols:
|
gBS->FreePool (mPca95xxInstance);
|
|
ErrPca95xxInstanceAlloc:
|
gBS->FreePool (Pca95xxDevicePath);
|
|
return Status;
|
}
|