]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Drivers/PL061GpioDxe/PL061Gpio.c
ArmPlatformPkg/PL061Gpio: fix the offset value in Get function
[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 #include <Drivers/PL061Gpio.h>
31
32 PLATFORM_GPIO_CONTROLLER *mPL061PlatformGpio;
33
34 EFI_STATUS
35 EFIAPI
36 PL061Locate (
37 IN EMBEDDED_GPIO_PIN Gpio,
38 OUT UINTN *ControllerIndex,
39 OUT UINTN *ControllerOffset,
40 OUT UINTN *RegisterBase
41 )
42 {
43 UINT32 Index;
44
45 for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) {
46 if ( (Gpio >= mPL061PlatformGpio->GpioController[Index].GpioIndex)
47 && (Gpio < mPL061PlatformGpio->GpioController[Index].GpioIndex
48 + mPL061PlatformGpio->GpioController[Index].InternalGpioCount)) {
49 *ControllerIndex = Index;
50 *ControllerOffset = Gpio % mPL061PlatformGpio->GpioController[Index].InternalGpioCount;
51 *RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase;
52 return EFI_SUCCESS;
53 }
54 }
55 DEBUG ((EFI_D_ERROR, "%a, failed to locate gpio %d\n", __func__, Gpio));
56 return EFI_INVALID_PARAMETER;
57 }
58
59 //
60 // The PL061 is a strange beast. The 8-bit data register is aliased across a
61 // region 0x400 bytes in size, with bits [9:2] of the address operating as a
62 // mask for both read and write operations:
63 // For reads:
64 // - All bits where their corresponding mask bit is 1 return the current
65 // value of that bit in the GPIO_DATA register.
66 // - All bits where their corresponding mask bit is 0 return 0.
67 // For writes:
68 // - All bits where their corresponding mask bit is 1 set the bit in the
69 // GPIO_DATA register to the written value.
70 // - All bits where their corresponding mask bit is 0 are left untouched
71 // in the GPIO_DATA register.
72 //
73 // To keep this driver intelligible, PL061EffectiveAddress, PL061GetPins and
74 // Pl061SetPins provide an internal abstraction from this interface.
75
76 STATIC
77 UINTN
78 EFIAPI
79 PL061EffectiveAddress (
80 IN UINTN Address,
81 IN UINTN Mask
82 )
83 {
84 return ((Address + PL061_GPIO_DATA_REG_OFFSET) + (Mask << 2));
85 }
86
87 STATIC
88 UINTN
89 EFIAPI
90 PL061GetPins (
91 IN UINTN Address,
92 IN UINTN Mask
93 )
94 {
95 return MmioRead8 (PL061EffectiveAddress (Address, Mask));
96 }
97
98 STATIC
99 VOID
100 EFIAPI
101 PL061SetPins (
102 IN UINTN Address,
103 IN UINTN Mask,
104 IN UINTN Value
105 )
106 {
107 MmioWrite8 (PL061EffectiveAddress (Address, Mask), Value);
108 }
109
110 /**
111 Function implementations
112 **/
113
114 EFI_STATUS
115 PL061Identify (
116 VOID
117 )
118 {
119 UINTN Index;
120 UINTN RegisterBase;
121
122 if ( (mPL061PlatformGpio->GpioCount == 0)
123 || (mPL061PlatformGpio->GpioControllerCount == 0)) {
124 return EFI_NOT_FOUND;
125 }
126
127 for (Index = 0; Index < mPL061PlatformGpio->GpioControllerCount; Index++) {
128 if (mPL061PlatformGpio->GpioController[Index].InternalGpioCount != PL061_GPIO_PINS) {
129 return EFI_INVALID_PARAMETER;
130 }
131
132 RegisterBase = mPL061PlatformGpio->GpioController[Index].RegisterBase;
133
134 // Check if this is a PrimeCell Peripheral
135 if ( (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID0) != 0x0D)
136 || (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID1) != 0xF0)
137 || (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID2) != 0x05)
138 || (MmioRead8 (RegisterBase + PL061_GPIO_PCELL_ID3) != 0xB1)) {
139 return EFI_NOT_FOUND;
140 }
141
142 // Check if this PrimeCell Peripheral is the PL061 GPIO
143 if ( (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID0) != 0x61)
144 || (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID1) != 0x10)
145 || ((MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID2) & 0xF) != 0x04)
146 || (MmioRead8 (RegisterBase + PL061_GPIO_PERIPH_ID3) != 0x00)) {
147 return EFI_NOT_FOUND;
148 }
149 }
150
151 return EFI_SUCCESS;
152 }
153
154 /**
155
156 Routine Description:
157
158 Gets the state of a GPIO pin
159
160 Arguments:
161
162 This - pointer to protocol
163 Gpio - which pin to read
164 Value - state of the pin
165
166 Returns:
167
168 EFI_SUCCESS - GPIO state returned in Value
169 EFI_INVALID_PARAMETER - Value is NULL pointer or Gpio pin is out of range
170 **/
171 EFI_STATUS
172 EFIAPI
173 Get (
174 IN EMBEDDED_GPIO *This,
175 IN EMBEDDED_GPIO_PIN Gpio,
176 OUT UINTN *Value
177 )
178 {
179 EFI_STATUS Status = EFI_SUCCESS;
180 UINTN Index, Offset, RegisterBase;
181
182 Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);
183 ASSERT_EFI_ERROR (Status);
184
185 if (Value == NULL) {
186 return EFI_INVALID_PARAMETER;
187 }
188
189 if (PL061GetPins (RegisterBase, GPIO_PIN_MASK(Offset))) {
190 *Value = 1;
191 } else {
192 *Value = 0;
193 }
194
195 return EFI_SUCCESS;
196 }
197
198 /**
199
200 Routine Description:
201
202 Sets the state of a GPIO pin
203
204 Arguments:
205
206 This - pointer to protocol
207 Gpio - which pin to modify
208 Mode - mode to set
209
210 Returns:
211
212 EFI_SUCCESS - GPIO set as requested
213 EFI_UNSUPPORTED - Mode is not supported
214 EFI_INVALID_PARAMETER - Gpio pin is out of range
215 **/
216 EFI_STATUS
217 EFIAPI
218 Set (
219 IN EMBEDDED_GPIO *This,
220 IN EMBEDDED_GPIO_PIN Gpio,
221 IN EMBEDDED_GPIO_MODE Mode
222 )
223 {
224 EFI_STATUS Status = EFI_SUCCESS;
225 UINTN Index, Offset, RegisterBase;
226
227 Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);
228 ASSERT_EFI_ERROR (Status);
229
230 switch (Mode)
231 {
232 case GPIO_MODE_INPUT:
233 // Set the corresponding direction bit to LOW for input
234 MmioAnd8 (RegisterBase + PL061_GPIO_DIR_REG,
235 ~GPIO_PIN_MASK(Offset) & 0xFF);
236 break;
237
238 case GPIO_MODE_OUTPUT_0:
239 // Set the corresponding direction bit to HIGH for output
240 MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset));
241 // Set the corresponding data bit to LOW for 0
242 PL061SetPins (RegisterBase, GPIO_PIN_MASK(Offset), 0);
243 break;
244
245 case GPIO_MODE_OUTPUT_1:
246 // Set the corresponding direction bit to HIGH for output
247 MmioOr8 (RegisterBase + PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Offset));
248 // Set the corresponding data bit to HIGH for 1
249 PL061SetPins (RegisterBase, GPIO_PIN_MASK(Offset), 0xff);
250 break;
251
252 default:
253 // Other modes are not supported
254 return EFI_UNSUPPORTED;
255 }
256
257 return EFI_SUCCESS;
258 }
259
260 /**
261
262 Routine Description:
263
264 Gets the mode (function) of a GPIO pin
265
266 Arguments:
267
268 This - pointer to protocol
269 Gpio - which pin
270 Mode - pointer to output mode value
271
272 Returns:
273
274 EFI_SUCCESS - mode value retrieved
275 EFI_INVALID_PARAMETER - Mode is a null pointer or Gpio pin is out of range
276
277 **/
278 EFI_STATUS
279 EFIAPI
280 GetMode (
281 IN EMBEDDED_GPIO *This,
282 IN EMBEDDED_GPIO_PIN Gpio,
283 OUT EMBEDDED_GPIO_MODE *Mode
284 )
285 {
286 EFI_STATUS Status = EFI_SUCCESS;
287 UINTN Index, Offset, RegisterBase;
288
289 Status = PL061Locate (Gpio, &Index, &Offset, &RegisterBase);
290 ASSERT_EFI_ERROR (Status);
291
292 // Check for errors
293 if (Mode == NULL) {
294 return EFI_INVALID_PARAMETER;
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 (PL061GetPins (RegisterBase, GPIO_PIN_MASK(Offset))) {
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
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
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 Status = PL061Identify();
398 if (EFI_ERROR(Status)) {
399 return EFI_DEVICE_ERROR;
400 }
401
402 // Install the Embedded GPIO Protocol onto a new handle
403 Handle = NULL;
404 Status = gBS->InstallMultipleProtocolInterfaces(
405 &Handle,
406 &gEmbeddedGpioProtocolGuid, &gGpio,
407 NULL
408 );
409 if (EFI_ERROR(Status)) {
410 Status = EFI_OUT_OF_RESOURCES;
411 }
412
413 return Status;
414 }