Home | History | Annotate | Download | only in PL061GpioDxe
      1 /** @file
      2 *
      3 *  Copyright (c) 2011, ARM Limited. All rights reserved.
      4 *  Copyright (c) 2016, Linaro Limited. All rights reserved.
      5 *
      6 *  This program and the accompanying materials
      7 *  are licensed and made available under the terms and conditions of the BSD
      8 *  License which accompanies this distribution.  The full text of the license
      9 *  may be found at http://opensource.org/licenses/bsd-license.php
     10 *
     11 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 *
     14 **/
     15 
     16 
     17 #include <PiDxe.h>
     18 
     19 #include <Library/BaseLib.h>
     20 #include <Library/BaseMemoryLib.h>
     21 #include <Library/DebugLib.h>
     22 #include <Library/IoLib.h>
     23 #include <Library/MemoryAllocationLib.h>
     24 #include <Library/PcdLib.h>
     25 #include <Library/UefiBootServicesTableLib.h>
     26 #include <Library/UefiLib.h>
     27 #include <Library/UefiRuntimeServicesTableLib.h>
     28 
     29 #include <Protocol/EmbeddedGpio.h>
     30 #include <Drivers/PL061Gpio.h>
     31 
     32 PLATFORM_GPIO_CONTROLLER *mPL061PlatformGpio;
     33 
     34 EFI_STATUS
     35 EFIAPI
     36 PL061Locate (
     37   IN  EMBEDDED_GPIO_PIN Gpio,
     38   OUT UINTN             *ControllerIndex,
     39   OUT UINTN             *ControllerOffset,
     40   OUT UINTN             *RegisterBase
     41   )
     42 {
     43   UINT32    Index;
     44 
     45   for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) {
     46     if (    (Gpio >= mPL061PlatformGpio->GpioController[Index].GpioIndex)
     47         &&  (Gpio < mPL061PlatformGpio->GpioController[Index].GpioIndex
     48              + mPL061PlatformGpio->GpioController[Index].InternalGpioCount)) {
     49       *ControllerIndex = Index;
     50       *ControllerOffset = Gpio % mPL061PlatformGpio->GpioController[Index].InternalGpioCount;
     51       *RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase;
     52       return EFI_SUCCESS;
     53     }
     54   }
     55   DEBUG ((EFI_D_ERROR, "%a, failed to locate gpio %d\n", __func__, Gpio));
     56   return EFI_INVALID_PARAMETER;
     57 }
     58 
     59 //
     60 // The PL061 is a strange beast. The 8-bit data register is aliased across a
     61 // region 0x400 bytes in size, with bits [9:2] of the address operating as a
     62 // mask for both read and write operations:
     63 // For reads:
     64 //   - All bits where their corresponding mask bit is 1 return the current
     65 //     value of that bit in the GPIO_DATA register.
     66 //   - All bits where their corresponding mask bit is 0 return 0.
     67 // For writes:
     68 //   - All bits where their corresponding mask bit is 1 set the bit in the
     69 //     GPIO_DATA register to the written value.
     70 //   - All bits where their corresponding mask bit is 0 are left untouched
     71 //     in the GPIO_DATA register.
     72 //
     73 // To keep this driver intelligible, PL061EffectiveAddress, PL061GetPins and
     74 // Pl061SetPins provide an internal abstraction from this interface.
     75 
     76 STATIC
     77 UINTN
     78 EFIAPI
     79 PL061EffectiveAddress (
     80   IN UINTN Address,
     81   IN UINTN Mask
     82   )
     83 {
     84   return ((Address + PL061_GPIO_DATA_REG_OFFSET) + (Mask << 2));
     85 }
     86 
     87 STATIC
     88 UINTN
     89 EFIAPI
     90 PL061GetPins (
     91   IN UINTN Address,
     92   IN UINTN Mask
     93   )
     94 {
     95   return MmioRead8 (PL061EffectiveAddress (Address, Mask));
     96 }
     97 
     98 STATIC
     99 VOID
    100 EFIAPI
    101 PL061SetPins (
    102   IN UINTN Address,
    103   IN UINTN Mask,
    104   IN UINTN Value
    105   )
    106 {
    107   MmioWrite8 (PL061EffectiveAddress (Address, Mask), Value);
    108 }
    109 
    110 /**
    111   Function implementations
    112 **/
    113 
    114 EFI_STATUS
    115 PL061Identify (
    116   VOID
    117   )
    118 {
    119   UINTN    Index;
    120   UINTN    RegisterBase;
    121 
    122   if (   (mPL061PlatformGpio->GpioCount == 0)
    123       || (mPL061PlatformGpio->GpioControllerCount == 0)) {
    124      return EFI_NOT_FOUND;
    125   }
    126 
    127   for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) {
    128     if (mPL061PlatformGpio->GpioController[Index].InternalGpioCount != PL061_GPIO_PINS) {
    129       return EFI_INVALID_PARAMETER;
    130     }
    131 
    132     RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase;
    133 
    134     // Check if this is a PrimeCell Peripheral
    135     if (    (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID0) != 0x0D)
    136         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID1) != 0xF0)
    137         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID2) != 0x05)
    138         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID3) != 0xB1)) {
    139       return EFI_NOT_FOUND;
    140     }
    141 
    142     // Check if this PrimeCell Peripheral is the PL061 GPIO
    143     if (    (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID0) != 0x61)
    144         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID1) != 0x10)
    145         ||  ((MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID2) & 0xF) != 0x04)
    146         ||  (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID3) != 0x00)) {
    147       return EFI_NOT_FOUND;
    148     }
    149   }
    150 
    151   return EFI_SUCCESS;
    152 }
    153 
    154 /**
    155 
    156 Routine Description:
    157 
    158   Gets the state of a GPIO pin
    159 
    160 Arguments:
    161 
    162   This  - pointer to protocol
    163   Gpio  - which pin to read
    164   Value - state of the pin
    165 
    166 Returns:
    167 
    168   EFI_SUCCESS           - GPIO state returned in Value
    169   EFI_INVALID_PARAMETER - Value is NULL pointer or Gpio pin is out of range
    170 **/
    171 EFI_STATUS
    172 EFIAPI
    173 Get (
    174   IN  EMBEDDED_GPIO     *This,
    175   IN  EMBEDDED_GPIO_PIN Gpio,
    176   OUT UINTN             *Value
    177   )
    178 {
    179   EFI_STATUS    Status = EFI_SUCCESS;
    180   UINTN         Index, Offset, RegisterBase;
    181 
    182   Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);
    183   ASSERT_EFI_ERROR (Status);
    184 
    185   if (Value == NULL) {
    186     return EFI_INVALID_PARAMETER;
    187   }
    188 
    189   if (PL061GetPins (RegisterBase + PL061_GPIO_DATA_REG, GPIO_PIN_MASK (Offset))) {
    190     *Value = 1;
    191   } else {
    192     *Value = 0;
    193   }
    194 
    195   return EFI_SUCCESS;
    196 }
    197 
    198 /**
    199 
    200 Routine Description:
    201 
    202   Sets the state of a GPIO pin
    203 
    204 Arguments:
    205 
    206   This  - pointer to protocol
    207   Gpio  - which pin to modify
    208   Mode  - mode to set
    209 
    210 Returns:
    211 
    212   EFI_SUCCESS           - GPIO set as requested
    213   EFI_UNSUPPORTED       - Mode is not supported
    214   EFI_INVALID_PARAMETER - Gpio pin is out of range
    215 **/
    216 EFI_STATUS
    217 EFIAPI
    218 Set (
    219   IN  EMBEDDED_GPIO       *This,
    220   IN  EMBEDDED_GPIO_PIN   Gpio,
    221   IN  EMBEDDED_GPIO_MODE  Mode
    222   )
    223 {
    224   EFI_STATUS    Status = EFI_SUCCESS;
    225   UINTN         Index, Offset, RegisterBase;
    226 
    227   Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);
    228   ASSERT_EFI_ERROR (Status);
    229 
    230   switch (Mode)
    231   {
    232     case GPIO_MODE_INPUT:
    233       // Set the corresponding direction bit to LOW for input
    234       MmioAnd8 (RegisterBase + PL061_GPIO_DIR_REG,
    235                 ~GPIO_PIN_MASK(Offset) & 0xFF);
    236       break;
    237 
    238     case GPIO_MODE_OUTPUT_0:
    239       // Set the corresponding direction bit to HIGH for output
    240       MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset));
    241       // Set the corresponding data bit to LOW for 0
    242       PL061SetPins (RegisterBase + PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Offset), 0);
    243       break;
    244 
    245     case GPIO_MODE_OUTPUT_1:
    246       // Set the corresponding direction bit to HIGH for output
    247       MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset));
    248       // Set the corresponding data bit to HIGH for 1
    249       PL061SetPins (RegisterBase + PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Offset), 0xff);
    250       break;
    251 
    252     default:
    253       // Other modes are not supported
    254       return EFI_UNSUPPORTED;
    255   }
    256 
    257   return EFI_SUCCESS;
    258 }
    259 
    260 /**
    261 
    262 Routine Description:
    263 
    264   Gets the mode (function) of a GPIO pin
    265 
    266 Arguments:
    267 
    268   This  - pointer to protocol
    269   Gpio  - which pin
    270   Mode  - pointer to output mode value
    271 
    272 Returns:
    273 
    274   EFI_SUCCESS           - mode value retrieved
    275   EFI_INVALID_PARAMETER - Mode is a null pointer or Gpio pin is out of range
    276 
    277 **/
    278 EFI_STATUS
    279 EFIAPI
    280 GetMode (
    281   IN  EMBEDDED_GPIO       *This,
    282   IN  EMBEDDED_GPIO_PIN   Gpio,
    283   OUT EMBEDDED_GPIO_MODE  *Mode
    284   )
    285 {
    286   EFI_STATUS    Status = EFI_SUCCESS;
    287   UINTN         Index, Offset, RegisterBase;
    288 
    289   Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);
    290   ASSERT_EFI_ERROR (Status);
    291 
    292   // Check for errors
    293   if (Mode == NULL) {
    294     return EFI_INVALID_PARAMETER;
    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 (PL061GetPins (RegisterBase + PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Offset))) {
    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   Status = PL061Identify();
    398   if (EFI_ERROR(Status)) {
    399     return EFI_DEVICE_ERROR;
    400   }
    401 
    402   // Install the Embedded GPIO Protocol onto a new handle
    403   Handle = NULL;
    404   Status = gBS->InstallMultipleProtocolInterfaces(
    405                   &Handle,
    406                   &gEmbeddedGpioProtocolGuid, &gGpio,
    407                   NULL
    408                  );
    409   if (EFI_ERROR(Status)) {
    410     Status = EFI_OUT_OF_RESOURCES;
    411   }
    412 
    413   return Status;
    414 }
    415