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