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