PL061Gpio.c 10 KB

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