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