ArmPlatformPkg: PL061 - rewrite the hardware interaction
[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/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 //
32 // The PL061 is a strange beast. The 8-bit data register is aliased across a
33 // region 0x400 bytes in size, with bits [9:2] of the address operating as a
34 // mask for both read and write operations:
35 // For reads:
36 // - All bits where their corresponding mask bit is 1 return the current
37 // value of that bit in the GPIO_DATA register.
38 // - All bits where their corresponding mask bit is 0 return 0.
39 // For writes:
40 // - All bits where their corresponding mask bit is 1 set the bit in the
41 // GPIO_DATA register to the written value.
42 // - All bits where their corresponding mask bit is 0 are left untouched
43 // in the GPIO_DATA register.
44 //
45 // To keep this driver intelligible, PL061EffectiveAddress, PL061GetPins and
46 // Pl061SetPins provide an internal abstraction from this interface.
47
48 STATIC
49 UINTN
50 EFIAPI
51 PL061EffectiveAddress (
52 IN UINTN Address,
53 IN UINTN Mask
54 )
55 {
56 return ((Address + PL061_GPIO_DATA_REG_OFFSET) + (Mask << 2));
57 }
58
59 STATIC
60 UINTN
61 EFIAPI
62 PL061GetPins (
63 IN UINTN Address,
64 IN UINTN Mask
65 )
66 {
67 return MmioRead8 (PL061EffectiveAddress (Address, Mask));
68 }
69
70 STATIC
71 VOID
72 EFIAPI
73 PL061SetPins (
74 IN UINTN Address,
75 IN UINTN Mask,
76 IN UINTN Value
77 )
78 {
79 MmioWrite8 (PL061EffectiveAddress (Address, Mask), Value);
80 }
81
82 /**
83 Function implementations
84 **/
85
86 EFI_STATUS
87 PL061Identify (
88 VOID
89 )
90 {
91 // Check if this is a PrimeCell Peripheral
92 if ( (MmioRead8 (PL061_GPIO_PCELL_ID0) != 0x0D)
93 || (MmioRead8 (PL061_GPIO_PCELL_ID1) != 0xF0)
94 || (MmioRead8 (PL061_GPIO_PCELL_ID2) != 0x05)
95 || (MmioRead8 (PL061_GPIO_PCELL_ID3) != 0xB1)) {
96 return EFI_NOT_FOUND;
97 }
98
99 // Check if this PrimeCell Peripheral is the PL061 GPIO
100 if ( (MmioRead8 (PL061_GPIO_PERIPH_ID0) != 0x61)
101 || (MmioRead8 (PL061_GPIO_PERIPH_ID1) != 0x10)
102 || ((MmioRead8 (PL061_GPIO_PERIPH_ID2) & 0xF) != 0x04)
103 || (MmioRead8 (PL061_GPIO_PERIPH_ID3) != 0x00)) {
104 return EFI_NOT_FOUND;
105 }
106
107 return EFI_SUCCESS;
108 }
109
110 /**
111
112 Routine Description:
113
114 Gets the state of a GPIO pin
115
116 Arguments:
117
118 This - pointer to protocol
119 Gpio - which pin to read
120 Value - state of the pin
121
122 Returns:
123
124 EFI_SUCCESS - GPIO state returned in Value
125 EFI_INVALID_PARAMETER - Value is NULL pointer or Gpio pin is out of range
126 **/
127 EFI_STATUS
128 EFIAPI
129 Get (
130 IN EMBEDDED_GPIO *This,
131 IN EMBEDDED_GPIO_PIN Gpio,
132 OUT UINTN *Value
133 )
134 {
135 if ( (Value == NULL)
136 || (Gpio > LAST_GPIO_PIN))
137 {
138 return EFI_INVALID_PARAMETER;
139 }
140
141 if (PL061GetPins (PL061_GPIO_DATA_REG, Gpio)) {
142 *Value = 1;
143 } else {
144 *Value = 0;
145 }
146
147 return EFI_SUCCESS;
148 }
149
150 /**
151
152 Routine Description:
153
154 Sets the state of a GPIO pin
155
156 Arguments:
157
158 This - pointer to protocol
159 Gpio - which pin to modify
160 Mode - mode to set
161
162 Returns:
163
164 EFI_SUCCESS - GPIO set as requested
165 EFI_UNSUPPORTED - Mode is not supported
166 EFI_INVALID_PARAMETER - Gpio pin is out of range
167 **/
168 EFI_STATUS
169 EFIAPI
170 Set (
171 IN EMBEDDED_GPIO *This,
172 IN EMBEDDED_GPIO_PIN Gpio,
173 IN EMBEDDED_GPIO_MODE Mode
174 )
175 {
176 EFI_STATUS Status = EFI_SUCCESS;
177
178 // Check for errors
179 if (Gpio > LAST_GPIO_PIN) {
180 Status = EFI_INVALID_PARAMETER;
181 goto EXIT;
182 }
183
184 switch (Mode)
185 {
186 case GPIO_MODE_INPUT:
187 // Set the corresponding direction bit to LOW for input
188 MmioAnd8 (PL061_GPIO_DIR_REG, ~GPIO_PIN_MASK(Gpio) & 0xFF);
189 break;
190
191 case GPIO_MODE_OUTPUT_0:
192 // Set the corresponding direction bit to HIGH for output
193 MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Gpio));
194 // Set the corresponding data bit to LOW for 0
195 PL061SetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio), 0);
196 break;
197
198 case GPIO_MODE_OUTPUT_1:
199 // Set the corresponding direction bit to HIGH for output
200 MmioOr8 (PL061_GPIO_DIR_REG, GPIO_PIN_MASK(Gpio));
201 // Set the corresponding data bit to HIGH for 1
202 PL061SetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio), 0xff);
203 break;
204
205 default:
206 // Other modes are not supported
207 return EFI_UNSUPPORTED;
208 }
209
210 EXIT:
211 return Status;
212 }
213
214 /**
215
216 Routine Description:
217
218 Gets the mode (function) of a GPIO pin
219
220 Arguments:
221
222 This - pointer to protocol
223 Gpio - which pin
224 Mode - pointer to output mode value
225
226 Returns:
227
228 EFI_SUCCESS - mode value retrieved
229 EFI_INVALID_PARAMETER - Mode is a null pointer or Gpio pin is out of range
230
231 **/
232 EFI_STATUS
233 EFIAPI
234 GetMode (
235 IN EMBEDDED_GPIO *This,
236 IN EMBEDDED_GPIO_PIN Gpio,
237 OUT EMBEDDED_GPIO_MODE *Mode
238 )
239 {
240 // Check for errors
241 if ( (Mode == NULL)
242 || (Gpio > LAST_GPIO_PIN)) {
243 return EFI_INVALID_PARAMETER;
244 }
245
246 // Check if it is input or output
247 if (MmioRead8 (PL061_GPIO_DIR_REG) & GPIO_PIN_MASK(Gpio)) {
248 // Pin set to output
249 if (PL061GetPins (PL061_GPIO_DATA_REG, GPIO_PIN_MASK(Gpio))) {
250 *Mode = GPIO_MODE_OUTPUT_1;
251 } else {
252 *Mode = GPIO_MODE_OUTPUT_0;
253 }
254 } else {
255 // Pin set to input
256 *Mode = GPIO_MODE_INPUT;
257 }
258
259 return EFI_SUCCESS;
260 }
261
262 /**
263
264 Routine Description:
265
266 Sets the pull-up / pull-down resistor of a GPIO pin
267
268 Arguments:
269
270 This - pointer to protocol
271 Gpio - which pin
272 Direction - pull-up, pull-down, or none
273
274 Returns:
275
276 EFI_UNSUPPORTED - Can not perform the requested operation
277
278 **/
279 EFI_STATUS
280 EFIAPI
281 SetPull (
282 IN EMBEDDED_GPIO *This,
283 IN EMBEDDED_GPIO_PIN Gpio,
284 IN EMBEDDED_GPIO_PULL Direction
285 )
286 {
287 return EFI_UNSUPPORTED;
288 }
289
290 /**
291 Protocol variable definition
292 **/
293 EMBEDDED_GPIO gGpio = {
294 Get,
295 Set,
296 GetMode,
297 SetPull
298 };
299
300 /**
301 Initialize the state information for the Embedded Gpio protocol.
302
303 @param ImageHandle of the loaded driver
304 @param SystemTable Pointer to the System Table
305
306 @retval EFI_SUCCESS Protocol registered
307 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
308 @retval EFI_DEVICE_ERROR Hardware problems
309
310 **/
311 EFI_STATUS
312 EFIAPI
313 PL061InstallProtocol (
314 IN EFI_HANDLE ImageHandle,
315 IN EFI_SYSTEM_TABLE *SystemTable
316 )
317 {
318 EFI_STATUS Status;
319 EFI_HANDLE Handle;
320
321 //
322 // Make sure the Gpio protocol has not been installed in the system yet.
323 //
324 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEmbeddedGpioProtocolGuid);
325
326 Status = PL061Identify();
327 if (EFI_ERROR(Status)) {
328 return EFI_DEVICE_ERROR;
329 }
330
331 // Install the Embedded GPIO Protocol onto a new handle
332 Handle = NULL;
333 Status = gBS->InstallMultipleProtocolInterfaces(
334 &Handle,
335 &gEmbeddedGpioProtocolGuid, &gGpio,
336 NULL
337 );
338 if (EFI_ERROR(Status)) {
339 Status = EFI_OUT_OF_RESOURCES;
340 }
341
342 return Status;
343 }