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