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