]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Drivers/PL061GpioDxe/PL061Gpio.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / ArmPlatformPkg / Drivers / PL061GpioDxe / PL061Gpio.c
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 }