Home | History | Annotate | Download | only in PL061GpioDxe
      1 /** @file
      2 *
      3 *  Copyright (c) 2011, ARM Limited. All rights reserved.
      4 *
      5 *  This program and the accompanying materials
      6 *  are licensed and made available under the terms and conditions of the BSD License
      7 *  which accompanies this distribution.  The full text of the license may be found at
      8 *  http://opensource.org/licenses/bsd-license.php
      9 *
     10 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 *
     13 **/
     14 
     15 
     16 #include <PiDxe.h>
     17 
     18 #include <Library/BaseLib.h>
     19 #include <Library/BaseMemoryLib.h>
     20 #include <Library/DebugLib.h>
     21 #include <Library/IoLib.h>
     22 #include <Library/MemoryAllocationLib.h>
     23 #include <Library/PcdLib.h>
     24 #include <Library/UefiBootServicesTableLib.h>
     25 #include <Library/UefiLib.h>
     26 #include <Library/UefiRuntimeServicesTableLib.h>
     27 
     28 #include <Protocol/EmbeddedGpio.h>
     29 #include <Drivers/PL061Gpio.h>
     30 
     31 BOOLEAN     mPL061Initialized = FALSE;
     32 PLATFORM_GPIO_CONTROLLER *mPL061PlatformGpio;
     33 
     34 /**
     35   Function implementations
     36 **/
     37 
     38 EFI_STATUS
     39 PL061Identify (
     40   VOID
     41   )
     42 {
     43   UINTN    Index;
     44   UINTN    RegisterBase;
     45 
     46   if (   (mPL061PlatformGpio->GpioCount == 0)
     47       || (mPL061PlatformGpio->GpioControllerCount == 0)) {
     48      return EFI_NOT_FOUND;
     49   }
     50 
     51   for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) {
     52     if (mPL061PlatformGpio->GpioController[Index].InternalGpioCount != PL061_GPIO_PINS) {
     53       return EFI_INVALID_PARAMETER;
     54     }
     55 
     56     RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase;
     57 
     58     // Check if this is a PrimeCell Peripheral
     59     if (    (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID0) != 0x0D)
     60         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID1) != 0xF0)
     61         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID2) != 0x05)
     62         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID3) != 0xB1)) {
     63       return EFI_NOT_FOUND;
     64     }
     65 
     66     // Check if this PrimeCell Peripheral is the PL061 GPIO
     67     if (    (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID0) != 0x61)
     68         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID1) != 0x10)
     69         ||  ((MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID2) & 0xF) != 0x04)
     70         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID3) != 0x00)) {
     71       return EFI_NOT_FOUND;
     72     }
     73   }
     74 
     75   return EFI_SUCCESS;
     76 }
     77 
     78 EFI_STATUS
     79 PL061Initialize (
     80   VOID
     81   )
     82 {
     83   EFI_STATUS  Status;
     84 
     85   // Check if the PL061 GPIO module exists on board
     86   Status = PL061Identify();
     87   if (EFI_ERROR (Status)) {
     88     Status = EFI_DEVICE_ERROR;
     89     goto EXIT;
     90   }
     91 
     92   // Do other hardware initialisation things here as required
     93 
     94   // Disable Interrupts
     95   //if (MmioRead8 (PL061_GPIO_IE_REG) != 0) {
     96   //   // Ensure interrupts are disabled
     97   //}
     98 
     99   mPL061Initialized = TRUE;
    100 
    101   EXIT:
    102   return Status;
    103 }
    104 
    105 EFI_STATUS
    106 EFIAPI
    107 PL061Locate (
    108   IN  EMBEDDED_GPIO_PIN Gpio,
    109   OUT UINTN             *ControllerIndex,
    110   OUT UINTN             *ControllerOffset,
    111   OUT UINTN             *RegisterBase
    112   )
    113 {
    114   UINT32    Index;
    115 
    116   for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) {
    117     if (    (Gpio >= mPL061PlatformGpio->GpioController[Index].GpioIndex)
    118         &&  (Gpio < mPL061PlatformGpio->GpioController[Index].GpioIndex
    119              + mPL061PlatformGpio->GpioController[Index].InternalGpioCount)) {
    120       *ControllerIndex = Index;
    121       *ControllerOffset = Gpio % mPL061PlatformGpio->GpioController[Index].InternalGpioCount;
    122       *RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase;
    123       return EFI_SUCCESS;
    124     }
    125   }
    126   DEBUG ((EFI_D_ERROR, "%a, failed to locate gpio %d\n", __func__, Gpio));
    127   return EFI_INVALID_PARAMETER;
    128 }
    129 
    130 /**
    131 
    132 Routine Description:
    133 
    134   Gets the state of a GPIO pin
    135 
    136 Arguments:
    137 
    138   This  - pointer to protocol
    139   Gpio  - which pin to read
    140   Value - state of the pin
    141 
    142 Returns:
    143 
    144   EFI_SUCCESS           - GPIO state returned in Value
    145   EFI_INVALID_PARAMETER - Value is NULL pointer or Gpio pin is out of range
    146 **/
    147 EFI_STATUS
    148 EFIAPI
    149 Get (
    150   IN  EMBEDDED_GPIO     *This,
    151   IN  EMBEDDED_GPIO_PIN Gpio,
    152   OUT UINTN             *Value
    153   )
    154 {
    155   EFI_STATUS    Status = EFI_SUCCESS;
    156   UINTN         Index, Offset, RegisterBase;
    157 
    158   Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);
    159   if (EFI_ERROR (Status))
    160     goto EXIT;
    161 
    162   if (Value == NULL) {
    163     Status = EFI_INVALID_PARAMETER;
    164     goto EXIT;
    165   }
    166 
    167   // Initialize the hardware if not already done
    168   if (!mPL061Initialized) {
    169     Status = PL061Initialize();
    170     if (EFI_ERROR(Status)) {
    171       goto EXIT;
    172     }
    173   }
    174 
    175   if (MmioRead8 (RegisterBase + PL061_GPIO_DATA_REG + (GPIO_PIN_MASK(Offset) << 2))) {
    176     *Value = 1;
    177   } else {
    178     *Value = 0;
    179   }
    180 
    181   EXIT:
    182   return Status;
    183 }
    184 
    185 /**
    186 
    187 Routine Description:
    188 
    189   Sets the state of a GPIO pin
    190 
    191 Arguments:
    192 
    193   This  - pointer to protocol
    194   Gpio  - which pin to modify
    195   Mode  - mode to set
    196 
    197 Returns:
    198 
    199   EFI_SUCCESS           - GPIO set as requested
    200   EFI_UNSUPPORTED       - Mode is not supported
    201   EFI_INVALID_PARAMETER - Gpio pin is out of range
    202 **/
    203 EFI_STATUS
    204 EFIAPI
    205 Set (
    206   IN  EMBEDDED_GPIO       *This,
    207   IN  EMBEDDED_GPIO_PIN   Gpio,
    208   IN  EMBEDDED_GPIO_MODE  Mode
    209   )
    210 {
    211   EFI_STATUS    Status = EFI_SUCCESS;
    212   UINTN         Index, Offset, RegisterBase;
    213 
    214   Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);
    215   if (EFI_ERROR (Status))
    216     goto EXIT;
    217 
    218   // Initialize the hardware if not already done
    219   if (!mPL061Initialized) {
    220     Status = PL061Initialize();
    221     if (EFI_ERROR(Status)) {
    222       goto EXIT;
    223     }
    224   }
    225 
    226   switch (Mode)
    227   {
    228     case GPIO_MODE_INPUT:
    229       // Set the corresponding direction bit to LOW for input
    230       MmioAnd8 (RegisterBase + PL061_GPIO_DIR_REG, ~GPIO_PIN_MASK(Offset));
    231       break;
    232 
    233     case GPIO_MODE_OUTPUT_0:
    234       // Set the corresponding data bit to LOW for 0
    235       MmioWrite8 (RegisterBase + PL061_GPIO_DATA_REG + (GPIO_PIN_MASK(Offset) << 2), 0);
    236       // Set the corresponding direction bit to HIGH for output
    237       MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset));
    238       break;
    239 
    240     case GPIO_MODE_OUTPUT_1:
    241       // Set the corresponding data bit to HIGH for 1
    242       MmioWrite8 (RegisterBase + PL061_GPIO_DATA_REG + (GPIO_PIN_MASK(Offset) << 2), 0xff);
    243       // Set the corresponding direction bit to HIGH for output
    244       MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset));
    245       break;
    246 
    247     default:
    248       // Other modes are not supported
    249       return EFI_UNSUPPORTED;
    250   }
    251 
    252 EXIT:
    253   return Status;
    254 }
    255 
    256 /**
    257 
    258 Routine Description:
    259 
    260   Gets the mode (function) of a GPIO pin
    261 
    262 Arguments:
    263 
    264   This  - pointer to protocol
    265   Gpio  - which pin
    266   Mode  - pointer to output mode value
    267 
    268 Returns:
    269 
    270   EFI_SUCCESS           - mode value retrieved
    271   EFI_INVALID_PARAMETER - Mode is a null pointer or Gpio pin is out of range
    272 
    273 **/
    274 EFI_STATUS
    275 EFIAPI
    276 GetMode (
    277   IN  EMBEDDED_GPIO       *This,
    278   IN  EMBEDDED_GPIO_PIN   Gpio,
    279   OUT EMBEDDED_GPIO_MODE  *Mode
    280   )
    281 {
    282   EFI_STATUS    Status;
    283   UINTN         Index, Offset, RegisterBase;
    284 
    285   Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);
    286   if (EFI_ERROR (Status))
    287     return Status;
    288 
    289   // Initialize the hardware if not already done
    290   if (!mPL061Initialized) {
    291     Status = PL061Initialize();
    292     if (EFI_ERROR(Status)) {
    293       return Status;
    294     }
    295   }
    296 
    297   // Check if it is input or output
    298   if (MmioRead8 (RegisterBase + PL061_GPIO_DIR_REG) & GPIO_PIN_MASK(Offset)) {
    299     // Pin set to output
    300     if (MmioRead8 (RegisterBase + PL061_GPIO_DATA_REG + (GPIO_PIN_MASK(Offset) << 2))) {
    301       *Mode = GPIO_MODE_OUTPUT_1;
    302     } else {
    303       *Mode = GPIO_MODE_OUTPUT_0;
    304     }
    305   } else {
    306     // Pin set to input
    307     *Mode = GPIO_MODE_INPUT;
    308   }
    309 
    310   return EFI_SUCCESS;
    311 }
    312 
    313 /**
    314 
    315 Routine Description:
    316 
    317   Sets the pull-up / pull-down resistor of a GPIO pin
    318 
    319 Arguments:
    320 
    321   This  - pointer to protocol
    322   Gpio  - which pin
    323   Direction - pull-up, pull-down, or none
    324 
    325 Returns:
    326 
    327   EFI_UNSUPPORTED - Can not perform the requested operation
    328 
    329 **/
    330 EFI_STATUS
    331 EFIAPI
    332 SetPull (
    333   IN  EMBEDDED_GPIO       *This,
    334   IN  EMBEDDED_GPIO_PIN   Gpio,
    335   IN  EMBEDDED_GPIO_PULL  Direction
    336   )
    337 {
    338   return EFI_UNSUPPORTED;
    339 }
    340 
    341 /**
    342  Protocol variable definition
    343  **/
    344 EMBEDDED_GPIO gGpio = {
    345   Get,
    346   Set,
    347   GetMode,
    348   SetPull
    349 };
    350 
    351 /**
    352   Initialize the state information for the Embedded Gpio protocol.
    353 
    354   @param  ImageHandle   of the loaded driver
    355   @param  SystemTable   Pointer to the System Table
    356 
    357   @retval EFI_SUCCESS           Protocol registered
    358   @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
    359   @retval EFI_DEVICE_ERROR      Hardware problems
    360 
    361 **/
    362 EFI_STATUS
    363 EFIAPI
    364 PL061InstallProtocol (
    365   IN EFI_HANDLE         ImageHandle,
    366   IN EFI_SYSTEM_TABLE   *SystemTable
    367   )
    368 {
    369   EFI_STATUS            Status;
    370   EFI_HANDLE            Handle;
    371   GPIO_CONTROLLER       *GpioController;
    372 
    373   //
    374   // Make sure the Gpio protocol has not been installed in the system yet.
    375   //
    376   ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEmbeddedGpioProtocolGuid);
    377 
    378   Status = gBS->LocateProtocol (&gPlatformGpioProtocolGuid, NULL, (VOID **)&mPL061PlatformGpio);
    379   if (EFI_ERROR (Status) && (Status == EFI_NOT_FOUND)) {
    380     // Create the mPL061PlatformGpio
    381     mPL061PlatformGpio = (PLATFORM_GPIO_CONTROLLER *)AllocateZeroPool (sizeof (PLATFORM_GPIO_CONTROLLER) + sizeof (GPIO_CONTROLLER));
    382     if (mPL061PlatformGpio == NULL) {
    383       DEBUG ((EFI_D_ERROR, "%a: failed to allocate PLATFORM_GPIO_CONTROLLER\n", __func__));
    384       return EFI_BAD_BUFFER_SIZE;
    385     }
    386 
    387     mPL061PlatformGpio->GpioCount = PL061_GPIO_PINS;
    388     mPL061PlatformGpio->GpioControllerCount = 1;
    389     mPL061PlatformGpio->GpioController = (GPIO_CONTROLLER *)((UINTN) mPL061PlatformGpio + sizeof (PLATFORM_GPIO_CONTROLLER));
    390 
    391     GpioController = mPL061PlatformGpio->GpioController;
    392     GpioController->RegisterBase = (UINTN) PcdGet32 (PcdPL061GpioBase);
    393     GpioController->GpioIndex = 0;
    394     GpioController->InternalGpioCount = PL061_GPIO_PINS;
    395   }
    396 
    397   // Install the Embedded GPIO Protocol onto a new handle
    398   Handle = NULL;
    399   Status = gBS->InstallMultipleProtocolInterfaces(
    400                   &Handle,
    401                   &gEmbeddedGpioProtocolGuid, &gGpio,
    402                   NULL
    403                  );
    404   if (EFI_ERROR(Status)) {
    405     Status = EFI_OUT_OF_RESOURCES;
    406   }
    407 
    408   return Status;
    409 }
    410