1 /** @file
2 *
3 *  Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.<BR>
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
PL061Locate(IN EMBEDDED_GPIO_PIN Gpio,OUT UINTN * ControllerIndex,OUT UINTN * ControllerOffset,OUT UINTN * RegisterBase)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
PL061EffectiveAddress(IN UINTN Address,IN UINTN Mask)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
PL061GetPins(IN UINTN Address,IN UINTN Mask)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
PL061SetPins(IN UINTN Address,IN UINTN Mask,IN UINTN Value)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
PL061Identify(VOID)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
Get(IN EMBEDDED_GPIO * This,IN EMBEDDED_GPIO_PIN Gpio,OUT UINTN * Value)168 Get (
169   IN  EMBEDDED_GPIO     *This,
170   IN  EMBEDDED_GPIO_PIN Gpio,
171   OUT UINTN             *Value
172   )
173 {
174   EFI_STATUS    Status;
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)) != 0) {
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
Set(IN EMBEDDED_GPIO * This,IN EMBEDDED_GPIO_PIN Gpio,IN EMBEDDED_GPIO_MODE Mode)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;
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
GetMode(IN EMBEDDED_GPIO * This,IN EMBEDDED_GPIO_PIN Gpio,OUT EMBEDDED_GPIO_MODE * Mode)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;
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)) != 0) {
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
SetPull(IN EMBEDDED_GPIO * This,IN EMBEDDED_GPIO_PIN Gpio,IN EMBEDDED_GPIO_PULL Direction)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
PL061InstallProtocol(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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