/** @file * * Copyright (c) 2011, ARM Limited. All rights reserved. * Copyright (c) 2016, Linaro Limited. 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 #include #include #include #include #include #include #include #include #include PLATFORM_GPIO_CONTROLLER *mPL061PlatformGpio; EFI_STATUS EFIAPI PL061Locate ( IN EMBEDDED_GPIO_PIN Gpio, OUT UINTN *ControllerIndex, OUT UINTN *ControllerOffset, OUT UINTN *RegisterBase ) { UINT32 Index; for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) { if ( (Gpio >= mPL061PlatformGpio->GpioController[Index].GpioIndex) && (Gpio < mPL061PlatformGpio->GpioController[Index].GpioIndex + mPL061PlatformGpio->GpioController[Index].InternalGpioCount)) { *ControllerIndex = Index; *ControllerOffset = Gpio % mPL061PlatformGpio->GpioController[Index].InternalGpioCount; *RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase; return EFI_SUCCESS; } } DEBUG ((EFI_D_ERROR, "%a, failed to locate gpio %d\n", __func__, Gpio)); return EFI_INVALID_PARAMETER; } // // The PL061 is a strange beast. The 8-bit data register is aliased across a // region 0x400 bytes in size, with bits [9:2] of the address operating as a // mask for both read and write operations: // For reads: // - All bits where their corresponding mask bit is 1 return the current // value of that bit in the GPIO_DATA register. // - All bits where their corresponding mask bit is 0 return 0. // For writes: // - All bits where their corresponding mask bit is 1 set the bit in the // GPIO_DATA register to the written value. // - All bits where their corresponding mask bit is 0 are left untouched // in the GPIO_DATA register. // // To keep this driver intelligible, PL061EffectiveAddress, PL061GetPins and // Pl061SetPins provide an internal abstraction from this interface. STATIC UINTN EFIAPI PL061EffectiveAddress ( IN UINTN Address, IN UINTN Mask ) { return ((Address + PL061_GPIO_DATA_REG_OFFSET) + (Mask << 2)); } STATIC UINTN EFIAPI PL061GetPins ( IN UINTN Address, IN UINTN Mask ) { return MmioRead8 (PL061EffectiveAddress (Address, Mask)); } STATIC VOID EFIAPI PL061SetPins ( IN UINTN Address, IN UINTN Mask, IN UINTN Value ) { MmioWrite8 (PL061EffectiveAddress (Address, Mask), Value); } /** Function implementations **/ EFI_STATUS PL061Identify ( VOID ) { UINTN Index; UINTN RegisterBase; if ( (mPL061PlatformGpio->GpioCount == 0) || (mPL061PlatformGpio->GpioControllerCount == 0)) { return EFI_NOT_FOUND; } for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) { if (mPL061PlatformGpio->GpioController[Index].InternalGpioCount != PL061_GPIO_PINS) { return EFI_INVALID_PARAMETER; } RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase; // Check if this is a PrimeCell Peripheral if ( (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID0) != 0x0D) || (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID1) != 0xF0) || (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID2) != 0x05) || (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID3) != 0xB1)) { return EFI_NOT_FOUND; } // Check if this PrimeCell Peripheral is the PL061 GPIO if ( (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID0) != 0x61) || (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID1) != 0x10) || ((MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID2) & 0xF) != 0x04) || (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID3) != 0x00)) { return EFI_NOT_FOUND; } } return EFI_SUCCESS; } /** 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 **/ EFI_STATUS EFIAPI Get ( IN EMBEDDED_GPIO *This, IN EMBEDDED_GPIO_PIN Gpio, OUT UINTN *Value ) { EFI_STATUS Status = EFI_SUCCESS; UINTN Index, Offset, RegisterBase; Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase); ASSERT_EFI_ERROR (Status); if (Value == NULL) { return EFI_INVALID_PARAMETER; } if (PL061GetPins (RegisterBase, GPIO_PIN_MASK(Offset))) { *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 **/ EFI_STATUS EFIAPI Set ( IN EMBEDDED_GPIO *This, IN EMBEDDED_GPIO_PIN Gpio, IN EMBEDDED_GPIO_MODE Mode ) { EFI_STATUS Status = EFI_SUCCESS; UINTN Index, Offset, RegisterBase; Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase); ASSERT_EFI_ERROR (Status); switch (Mode) { case GPIO_MODE_INPUT: // Set the corresponding direction bit to LOW for input MmioAnd8 (RegisterBase + PL061_GPIO_DIR_REG, ~GPIO_PIN_MASK(Offset) & 0xFF); break; case GPIO_MODE_OUTPUT_0: // Set the corresponding direction bit to HIGH for output MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset)); // Set the corresponding data bit to LOW for 0 PL061SetPins (RegisterBase, GPIO_PIN_MASK(Offset), 0); break; case GPIO_MODE_OUTPUT_1: // Set the corresponding direction bit to HIGH for output MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset)); // Set the corresponding data bit to HIGH for 1 PL061SetPins (RegisterBase, GPIO_PIN_MASK(Offset), 0xff); break; default: // Other modes are not supported return EFI_UNSUPPORTED; } 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 **/ EFI_STATUS EFIAPI GetMode ( IN EMBEDDED_GPIO *This, IN EMBEDDED_GPIO_PIN Gpio, OUT EMBEDDED_GPIO_MODE *Mode ) { EFI_STATUS Status = EFI_SUCCESS; UINTN Index, Offset, RegisterBase; Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase); ASSERT_EFI_ERROR (Status); // Check for errors if (Mode == NULL) { return EFI_INVALID_PARAMETER; } // Check if it is input or output if (MmioRead8 (RegisterBase + PL061_GPIO_DIR_REG) & GPIO_PIN_MASK(Offset)) { // Pin set to output if (PL061GetPins (RegisterBase, GPIO_PIN_MASK(Offset))) { *Mode = GPIO_MODE_OUTPUT_1; } else { *Mode = GPIO_MODE_OUTPUT_0; } } else { // Pin set to input *Mode = GPIO_MODE_INPUT; } 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 SetPull ( IN EMBEDDED_GPIO *This, IN EMBEDDED_GPIO_PIN Gpio, IN EMBEDDED_GPIO_PULL Direction ) { return EFI_UNSUPPORTED; } /** Protocol variable definition **/ EMBEDDED_GPIO gGpio = { Get, Set, GetMode, SetPull }; /** Initialize the state information for the Embedded Gpio protocol. @param ImageHandle of the loaded driver @param SystemTable Pointer to the System Table @retval EFI_SUCCESS Protocol registered @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure @retval EFI_DEVICE_ERROR Hardware problems **/ EFI_STATUS EFIAPI PL061InstallProtocol ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE Handle; GPIO_CONTROLLER *GpioController; // // Make sure the Gpio protocol has not been installed in the system yet. // ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEmbeddedGpioProtocolGuid); Status = gBS->LocateProtocol (&gPlatformGpioProtocolGuid, NULL, (VOID **)&mPL061PlatformGpio); if (EFI_ERROR (Status) && (Status == EFI_NOT_FOUND)) { // Create the mPL061PlatformGpio mPL061PlatformGpio = (PLATFORM_GPIO_CONTROLLER *)AllocateZeroPool (sizeof (PLATFORM_GPIO_CONTROLLER) + sizeof (GPIO_CONTROLLER)); if (mPL061PlatformGpio == NULL) { DEBUG ((EFI_D_ERROR, "%a: failed to allocate PLATFORM_GPIO_CONTROLLER\n", __func__)); return EFI_BAD_BUFFER_SIZE; } mPL061PlatformGpio->GpioCount = PL061_GPIO_PINS; mPL061PlatformGpio->GpioControllerCount = 1; mPL061PlatformGpio->GpioController = (GPIO_CONTROLLER *)((UINTN) mPL061PlatformGpio + sizeof (PLATFORM_GPIO_CONTROLLER)); GpioController = mPL061PlatformGpio->GpioController; GpioController->RegisterBase = (UINTN) PcdGet32 (PcdPL061GpioBase); GpioController->GpioIndex = 0; GpioController->InternalGpioCount = PL061_GPIO_PINS; } Status = PL061Identify(); if (EFI_ERROR(Status)) { return EFI_DEVICE_ERROR; } // Install the Embedded GPIO Protocol onto a new handle Handle = NULL; Status = gBS->InstallMultipleProtocolInterfaces( &Handle, &gEmbeddedGpioProtocolGuid, &gGpio, NULL ); if (EFI_ERROR(Status)) { Status = EFI_OUT_OF_RESOURCES; } return Status; }