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
PL061Identify(VOID)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
PL061Initialize(VOID)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
PL061Locate(IN EMBEDDED_GPIO_PIN Gpio,OUT UINTN * ControllerIndex,OUT UINTN * ControllerOffset,OUT UINTN * RegisterBase)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
Get(IN EMBEDDED_GPIO * This,IN EMBEDDED_GPIO_PIN Gpio,OUT UINTN * Value)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
Set(IN EMBEDDED_GPIO * This,IN EMBEDDED_GPIO_PIN Gpio,IN EMBEDDED_GPIO_MODE Mode)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
GetMode(IN EMBEDDED_GPIO * This,IN EMBEDDED_GPIO_PIN Gpio,OUT EMBEDDED_GPIO_MODE * Mode)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
SetPull(IN EMBEDDED_GPIO * This,IN EMBEDDED_GPIO_PIN Gpio,IN EMBEDDED_GPIO_PULL Direction)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
PL061InstallProtocol(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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