]> git.proxmox.com Git - mirror_edk2.git/blame - ArmPlatformPkg/ArmVirtualizationPkg/VirtFdtDxe/VirtFdtDxe.c
ArmPkg: allow HYP timer interrupt to be omitted
[mirror_edk2.git] / ArmPlatformPkg / ArmVirtualizationPkg / VirtFdtDxe / VirtFdtDxe.c
CommitLineData
ad106932
AB
1/** @file\r
2* Device tree enumeration DXE driver for ARM Virtual Machines\r
3*\r
4* Copyright (c) 2014, Linaro Ltd. All rights reserved.<BR>\r
5*\r
6* This program and the accompanying materials are\r
7* licensed and made available under the terms and conditions of the BSD License\r
8* which accompanies this distribution. The full text of the license may be found at\r
9* http://opensource.org/licenses/bsd-license.php\r
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#include <Library/BaseLib.h>\r
17#include <Library/DebugLib.h>\r
18#include <Library/UefiLib.h>\r
19#include <Library/BaseMemoryLib.h>\r
20#include <Library/UefiDriverEntryPoint.h>\r
21#include <Library/MemoryAllocationLib.h>\r
22#include <Library/UefiBootServicesTableLib.h>\r
23#include <Library/VirtioMmioDeviceLib.h>\r
24#include <Library/DevicePathLib.h>\r
25#include <Library/PcdLib.h>\r
26#include <Library/DxeServicesLib.h>\r
27#include <libfdt.h>\r
28\r
29#include <Guid/Fdt.h>\r
73bb8e68 30#include <Guid/VirtioMmioTransport.h>\r
ad106932
AB
31\r
32#pragma pack (1)\r
33typedef struct {\r
34 VENDOR_DEVICE_PATH Vendor;\r
35 UINT64 PhysBase;\r
36 EFI_DEVICE_PATH_PROTOCOL End;\r
37} VIRTIO_TRANSPORT_DEVICE_PATH;\r
38#pragma pack ()\r
39\r
40typedef enum {\r
41 PropertyTypeUnknown,\r
42 PropertyTypeGic,\r
43 PropertyTypeRtc,\r
44 PropertyTypeVirtio,\r
45 PropertyTypeUart,\r
46 PropertyTypeTimer,\r
47 PropertyTypePsci,\r
ad652d46 48 PropertyTypeFwCfg,\r
65bb13b0 49 PropertyTypePciHost,\r
ad106932
AB
50} PROPERTY_TYPE;\r
51\r
52typedef struct {\r
53 PROPERTY_TYPE Type;\r
65bb13b0 54 CHAR8 Compatible[32];\r
ad106932
AB
55} PROPERTY;\r
56\r
57STATIC CONST PROPERTY CompatibleProperties[] = {\r
65bb13b0
LE
58 { PropertyTypeGic, "arm,cortex-a15-gic" },\r
59 { PropertyTypeRtc, "arm,pl031" },\r
60 { PropertyTypeVirtio, "virtio,mmio" },\r
61 { PropertyTypeUart, "arm,pl011" },\r
62 { PropertyTypeTimer, "arm,armv7-timer" },\r
63 { PropertyTypeTimer, "arm,armv8-timer" },\r
64 { PropertyTypePsci, "arm,psci-0.2" },\r
65 { PropertyTypeFwCfg, "qemu,fw-cfg-mmio" },\r
66 { PropertyTypePciHost, "pci-host-ecam-generic" },\r
67 { PropertyTypeUnknown, "" }\r
ad106932
AB
68};\r
69\r
70typedef struct {\r
71 UINT32 Type;\r
72 UINT32 Number;\r
73 UINT32 Flags;\r
74} INTERRUPT_PROPERTY;\r
75\r
76STATIC\r
77PROPERTY_TYPE\r
78GetTypeFromNode (\r
79 IN CONST CHAR8 *NodeType,\r
80 IN UINTN Size\r
81 )\r
82{\r
83 CONST CHAR8 *Compatible;\r
84 CONST PROPERTY *CompatibleProperty;\r
85\r
86 //\r
87 // A 'compatible' node may contain a sequence of NULL terminated\r
88 // compatible strings so check each one\r
89 //\r
90 for (Compatible = NodeType; Compatible < NodeType + Size && *Compatible;\r
91 Compatible += 1 + AsciiStrLen (Compatible)) {\r
92 for (CompatibleProperty = CompatibleProperties; CompatibleProperty->Compatible[0]; CompatibleProperty++) {\r
93 if (AsciiStrCmp (CompatibleProperty->Compatible, Compatible) == 0) {\r
94 return CompatibleProperty->Type;\r
95 }\r
96 }\r
97 }\r
98 return PropertyTypeUnknown;\r
99}\r
100\r
65bb13b0
LE
101//\r
102// We expect the "ranges" property of "pci-host-ecam-generic" to consist of\r
103// records like this.\r
104//\r
105#pragma pack (1)\r
106typedef struct {\r
107 UINT32 Type;\r
108 UINT64 ChildBase;\r
109 UINT64 CpuBase;\r
110 UINT64 Size;\r
111} DTB_PCI_HOST_RANGE_RECORD;\r
112#pragma pack ()\r
113\r
114#define DTB_PCI_HOST_RANGE_RELOCATABLE BIT31\r
115#define DTB_PCI_HOST_RANGE_PREFETCHABLE BIT30\r
116#define DTB_PCI_HOST_RANGE_ALIASED BIT29\r
117#define DTB_PCI_HOST_RANGE_MMIO32 BIT25\r
118#define DTB_PCI_HOST_RANGE_MMIO64 (BIT25 | BIT24)\r
119#define DTB_PCI_HOST_RANGE_IO BIT24\r
120#define DTB_PCI_HOST_RANGE_TYPEMASK (BIT31 | BIT30 | BIT29 | BIT25 | BIT24)\r
121\r
122/**\r
123 Process the device tree node describing the generic PCI host controller.\r
124\r
125 param[in] DeviceTreeBase Pointer to the device tree.\r
126\r
127 param[in] Node Offset of the device tree node whose "compatible"\r
128 property is "pci-host-ecam-generic".\r
129\r
130 param[in] RegProp Pointer to the "reg" property of Node. The caller\r
131 is responsible for ensuring that the size of the\r
132 property is 4 UINT32 cells.\r
133\r
134 @retval EFI_SUCCESS Parsing successful, properties parsed from Node\r
135 have been stored in dynamic PCDs.\r
136\r
137 @retval EFI_PROTOCOL_ERROR Parsing failed. PCDs are left unchanged.\r
138**/\r
139STATIC\r
140EFI_STATUS\r
141EFIAPI\r
142ProcessPciHost (\r
143 IN CONST VOID *DeviceTreeBase,\r
144 IN INT32 Node,\r
145 IN CONST VOID *RegProp\r
146 )\r
147{\r
148 UINT64 ConfigBase, ConfigSize;\r
149 CONST VOID *Prop;\r
150 INT32 Len;\r
151 UINT32 BusMin, BusMax;\r
152 UINT32 RecordIdx;\r
153 UINT64 IoBase, IoSize, IoTranslation;\r
154 UINT64 MmioBase, MmioSize, MmioTranslation;\r
155\r
156 //\r
157 // Fetch the ECAM window.\r
158 //\r
159 ConfigBase = fdt64_to_cpu (((CONST UINT64 *)RegProp)[0]);\r
160 ConfigSize = fdt64_to_cpu (((CONST UINT64 *)RegProp)[1]);\r
161\r
162 //\r
163 // Fetch the bus range (note: inclusive).\r
164 //\r
165 Prop = fdt_getprop (DeviceTreeBase, Node, "bus-range", &Len);\r
166 if (Prop == NULL || Len != 2 * sizeof(UINT32)) {\r
167 DEBUG ((EFI_D_ERROR, "%a: 'bus-range' not found or invalid\n",\r
168 __FUNCTION__));\r
169 return EFI_PROTOCOL_ERROR;\r
170 }\r
171 BusMin = fdt32_to_cpu (((CONST UINT32 *)Prop)[0]);\r
172 BusMax = fdt32_to_cpu (((CONST UINT32 *)Prop)[1]);\r
173\r
174 //\r
175 // Sanity check: the config space must accommodate all 4K register bytes of\r
176 // all 8 functions of all 32 devices of all buses.\r
177 //\r
178 if (BusMax < BusMin || BusMax - BusMin == MAX_UINT32 ||\r
179 DivU64x32 (ConfigSize, SIZE_4KB * 8 * 32) < BusMax - BusMin + 1) {\r
180 DEBUG ((EFI_D_ERROR, "%a: invalid 'bus-range' and/or 'reg'\n",\r
181 __FUNCTION__));\r
182 return EFI_PROTOCOL_ERROR;\r
183 }\r
184\r
185 //\r
186 // Iterate over "ranges".\r
187 //\r
188 Prop = fdt_getprop (DeviceTreeBase, Node, "ranges", &Len);\r
189 if (Prop == NULL || Len == 0 ||\r
190 Len % sizeof (DTB_PCI_HOST_RANGE_RECORD) != 0) {\r
191 DEBUG ((EFI_D_ERROR, "%a: 'ranges' not found or invalid\n", __FUNCTION__));\r
192 return EFI_PROTOCOL_ERROR;\r
193 }\r
194\r
195 //\r
196 // IoBase, IoTranslation, MmioBase and MmioTranslation are initialized only\r
197 // in order to suppress '-Werror=maybe-uninitialized' warnings *incorrectly*\r
198 // emitted by some gcc versions.\r
199 //\r
200 IoBase = 0;\r
201 IoTranslation = 0;\r
202 MmioBase = 0;\r
203 MmioTranslation = 0;\r
204\r
205 //\r
206 // IoSize and MmioSize are initialized to zero because the logic below\r
207 // requires it.\r
208 //\r
209 IoSize = 0;\r
210 MmioSize = 0;\r
211 for (RecordIdx = 0; RecordIdx < Len / sizeof (DTB_PCI_HOST_RANGE_RECORD);\r
212 ++RecordIdx) {\r
213 CONST DTB_PCI_HOST_RANGE_RECORD *Record;\r
214\r
215 Record = (CONST DTB_PCI_HOST_RANGE_RECORD *)Prop + RecordIdx;\r
216 switch (fdt32_to_cpu (Record->Type) & DTB_PCI_HOST_RANGE_TYPEMASK) {\r
217 case DTB_PCI_HOST_RANGE_IO:\r
218 IoBase = fdt64_to_cpu (Record->ChildBase);\r
219 IoSize = fdt64_to_cpu (Record->Size);\r
220 IoTranslation = fdt64_to_cpu (Record->CpuBase) - IoBase;\r
221 break;\r
222\r
223 case DTB_PCI_HOST_RANGE_MMIO32:\r
224 MmioBase = fdt64_to_cpu (Record->ChildBase);\r
225 MmioSize = fdt64_to_cpu (Record->Size);\r
226 MmioTranslation = fdt64_to_cpu (Record->CpuBase) - MmioBase;\r
227\r
228 if (MmioBase > MAX_UINT32 || MmioSize > MAX_UINT32 ||\r
229 MmioBase + MmioSize > SIZE_4GB) {\r
230 DEBUG ((EFI_D_ERROR, "%a: MMIO32 space invalid\n", __FUNCTION__));\r
231 return EFI_PROTOCOL_ERROR;\r
232 }\r
233\r
234 if (MmioTranslation != 0) {\r
235 DEBUG ((EFI_D_ERROR, "%a: unsupported nonzero MMIO32 translation "\r
236 "0x%Lx\n", __FUNCTION__, MmioTranslation));\r
237 return EFI_UNSUPPORTED;\r
238 }\r
239\r
240 break;\r
241 }\r
242 }\r
243 if (IoSize == 0 || MmioSize == 0) {\r
244 DEBUG ((EFI_D_ERROR, "%a: %a space empty\n", __FUNCTION__,\r
245 (IoSize == 0) ? "IO" : "MMIO32"));\r
246 return EFI_PROTOCOL_ERROR;\r
247 }\r
248\r
249 PcdSet64 (PcdPciExpressBaseAddress, ConfigBase);\r
250\r
251 PcdSet32 (PcdPciBusMin, BusMin);\r
252 PcdSet32 (PcdPciBusMax, BusMax);\r
253\r
254 PcdSet64 (PcdPciIoBase, IoBase);\r
255 PcdSet64 (PcdPciIoSize, IoSize);\r
256 PcdSet64 (PcdPciIoTranslation, IoTranslation);\r
257\r
258 PcdSet32 (PcdPciMmio32Base, (UINT32)MmioBase);\r
259 PcdSet32 (PcdPciMmio32Size, (UINT32)MmioSize);\r
260\r
261 PcdSetBool (PcdPciDisableBusEnumeration, FALSE);\r
262\r
263 DEBUG ((EFI_D_INFO, "%a: Config[0x%Lx+0x%Lx) Bus[0x%x..0x%x] "\r
264 "Io[0x%Lx+0x%Lx)@0x%Lx Mem[0x%Lx+0x%Lx)@0x%Lx\n", __FUNCTION__, ConfigBase,\r
265 ConfigSize, BusMin, BusMax, IoBase, IoSize, IoTranslation, MmioBase,\r
266 MmioSize, MmioTranslation));\r
267 return EFI_SUCCESS;\r
268}\r
269\r
270\r
ad106932
AB
271EFI_STATUS\r
272EFIAPI\r
273InitializeVirtFdtDxe (\r
274 IN EFI_HANDLE ImageHandle,\r
275 IN EFI_SYSTEM_TABLE *SystemTable\r
276 )\r
277{\r
278 VOID *DeviceTreeBase;\r
279 INT32 Node, Prev;\r
280 INT32 RtcNode;\r
281 EFI_STATUS Status;\r
282 CONST CHAR8 *Type;\r
283 INT32 Len;\r
284 PROPERTY_TYPE PropType;\r
285 CONST VOID *RegProp;\r
286 VIRTIO_TRANSPORT_DEVICE_PATH *DevicePath;\r
287 EFI_HANDLE Handle;\r
288 UINT64 RegBase;\r
289 UINT64 DistBase, CpuBase;\r
290 CONST INTERRUPT_PROPERTY *InterruptProp;\r
291 INT32 SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum;\r
292 CONST CHAR8 *PsciMethod;\r
ad652d46
LE
293 UINT64 FwCfgSelectorAddress;\r
294 UINT64 FwCfgSelectorSize;\r
295 UINT64 FwCfgDataAddress;\r
296 UINT64 FwCfgDataSize;\r
ad106932
AB
297\r
298 DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeBaseAddress);\r
299 ASSERT (DeviceTreeBase != NULL);\r
300\r
301 if (fdt_check_header (DeviceTreeBase) != 0) {\r
302 DEBUG ((EFI_D_ERROR, "%a: No DTB found @ 0x%p\n", __FUNCTION__, DeviceTreeBase));\r
303 return EFI_NOT_FOUND;\r
304 }\r
305\r
306 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, DeviceTreeBase);\r
307 ASSERT_EFI_ERROR (Status);\r
308\r
309 DEBUG ((EFI_D_INFO, "%a: DTB @ 0x%p\n", __FUNCTION__, DeviceTreeBase));\r
310\r
311 RtcNode = -1;\r
312 //\r
313 // Now enumerate the nodes and install peripherals that we are interested in,\r
314 // i.e., GIC, RTC and virtio MMIO nodes\r
315 //\r
316 for (Prev = 0;; Prev = Node) {\r
317 Node = fdt_next_node (DeviceTreeBase, Prev, NULL);\r
318 if (Node < 0) {\r
319 break;\r
320 }\r
321\r
322 Type = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);\r
323 if (Type == NULL) {\r
324 continue;\r
325 }\r
326\r
327 PropType = GetTypeFromNode (Type, Len);\r
328 if (PropType == PropertyTypeUnknown) {\r
329 continue;\r
330 }\r
331\r
332 //\r
333 // Get the 'reg' property of this node. For now, we will assume\r
334 // 8 byte quantities for base and size, respectively.\r
335 // TODO use #cells root properties instead\r
336 //\r
337 RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);\r
338 ASSERT ((RegProp != NULL) || (PropType == PropertyTypeTimer) ||\r
339 (PropType == PropertyTypePsci));\r
340\r
341 switch (PropType) {\r
65bb13b0
LE
342 case PropertyTypePciHost:\r
343 ASSERT (Len == 2 * sizeof (UINT64));\r
344 Status = ProcessPciHost (DeviceTreeBase, Node, RegProp);\r
345 ASSERT_EFI_ERROR (Status);\r
346 break;\r
347\r
ad652d46
LE
348 case PropertyTypeFwCfg:\r
349 ASSERT (Len == 2 * sizeof (UINT64));\r
350\r
351 FwCfgDataAddress = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
352 FwCfgDataSize = 8;\r
353 FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;\r
354 FwCfgSelectorSize = 2;\r
355\r
356 //\r
357 // The following ASSERT()s express\r
358 //\r
359 // Address + Size - 1 <= MAX_UINTN\r
360 //\r
361 // for both registers, that is, that the last byte in each MMIO range is\r
362 // expressible as a MAX_UINTN. The form below is mathematically\r
363 // equivalent, and it also prevents any unsigned overflow before the\r
364 // comparison.\r
365 //\r
366 ASSERT (FwCfgSelectorAddress <= MAX_UINTN - FwCfgSelectorSize + 1);\r
367 ASSERT (FwCfgDataAddress <= MAX_UINTN - FwCfgDataSize + 1);\r
368\r
369 PcdSet64 (PcdFwCfgSelectorAddress, FwCfgSelectorAddress);\r
370 PcdSet64 (PcdFwCfgDataAddress, FwCfgDataAddress);\r
371\r
372 DEBUG ((EFI_D_INFO, "Found FwCfg @ 0x%Lx/0x%Lx\n", FwCfgSelectorAddress,\r
373 FwCfgDataAddress));\r
374 break;\r
375\r
ad106932
AB
376 case PropertyTypeVirtio:\r
377 ASSERT (Len == 16);\r
378 //\r
379 // Create a unique device path for this transport on the fly\r
380 //\r
381 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
382 DevicePath = (VIRTIO_TRANSPORT_DEVICE_PATH *)CreateDeviceNode (\r
383 HARDWARE_DEVICE_PATH,\r
384 HW_VENDOR_DP,\r
385 sizeof (VIRTIO_TRANSPORT_DEVICE_PATH));\r
386 if (DevicePath == NULL) {\r
387 DEBUG ((EFI_D_ERROR, "%a: Out of memory\n", __FUNCTION__));\r
388 break;\r
389 }\r
390\r
73bb8e68
LE
391 CopyMem (&DevicePath->Vendor.Guid, &gVirtioMmioTransportGuid,\r
392 sizeof (EFI_GUID));\r
ad106932
AB
393 DevicePath->PhysBase = RegBase;\r
394 SetDevicePathNodeLength (&DevicePath->Vendor,\r
395 sizeof (*DevicePath) - sizeof (DevicePath->End));\r
396 SetDevicePathEndNode (&DevicePath->End);\r
397\r
398 Handle = NULL;\r
399 Status = gBS->InstallProtocolInterface (&Handle,\r
400 &gEfiDevicePathProtocolGuid, EFI_NATIVE_INTERFACE,\r
401 DevicePath);\r
402 if (EFI_ERROR (Status)) {\r
403 DEBUG ((EFI_D_ERROR, "%a: Failed to install the EFI_DEVICE_PATH "\r
404 "protocol on a new handle (Status == %r)\n",\r
405 __FUNCTION__, Status));\r
406 FreePool (DevicePath);\r
407 break;\r
408 }\r
409\r
410 Status = VirtioMmioInstallDevice (RegBase, Handle);\r
411 if (EFI_ERROR (Status)) {\r
412 DEBUG ((EFI_D_ERROR, "%a: Failed to install VirtIO transport @ 0x%Lx "\r
413 "on handle %p (Status == %r)\n", __FUNCTION__, RegBase,\r
414 Handle, Status));\r
415\r
416 Status = gBS->UninstallProtocolInterface (Handle,\r
417 &gEfiDevicePathProtocolGuid, DevicePath);\r
418 ASSERT_EFI_ERROR (Status);\r
419 FreePool (DevicePath);\r
420 }\r
421 break;\r
422\r
423 case PropertyTypeGic:\r
424 ASSERT (Len == 32);\r
425\r
426 DistBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
427 CpuBase = fdt64_to_cpu (((UINT64 *)RegProp)[2]);\r
428 ASSERT (DistBase < MAX_UINT32);\r
429 ASSERT (CpuBase < MAX_UINT32);\r
430\r
431 PcdSet32 (PcdGicDistributorBase, (UINT32)DistBase);\r
432 PcdSet32 (PcdGicInterruptInterfaceBase, (UINT32)CpuBase);\r
433\r
434 DEBUG ((EFI_D_INFO, "Found GIC @ 0x%Lx/0x%Lx\n", DistBase, CpuBase));\r
435 break;\r
436\r
437 case PropertyTypeRtc:\r
438 ASSERT (Len == 16);\r
439\r
440 RegBase = fdt64_to_cpu (((UINT64 *)RegProp)[0]);\r
441 ASSERT (RegBase < MAX_UINT32);\r
442\r
443 PcdSet32 (PcdPL031RtcBase, (UINT32)RegBase);\r
444\r
445 DEBUG ((EFI_D_INFO, "Found PL031 RTC @ 0x%Lx\n", RegBase));\r
446 RtcNode = Node;\r
447 break;\r
448\r
449 case PropertyTypeTimer:\r
450 //\r
451 // - interrupts : Interrupt list for secure, non-secure, virtual and\r
452 // hypervisor timers, in that order.\r
453 //\r
454 InterruptProp = fdt_getprop (DeviceTreeBase, Node, "interrupts", &Len);\r
967efdcd 455 ASSERT (Len == 36 || Len == 48);\r
ad106932
AB
456\r
457 SecIntrNum = fdt32_to_cpu (InterruptProp[0].Number)\r
458 + (InterruptProp[0].Type ? 16 : 0);\r
459 IntrNum = fdt32_to_cpu (InterruptProp[1].Number)\r
460 + (InterruptProp[1].Type ? 16 : 0);\r
461 VirtIntrNum = fdt32_to_cpu (InterruptProp[2].Number)\r
462 + (InterruptProp[2].Type ? 16 : 0);\r
967efdcd
AB
463 HypIntrNum = Len < 48 ? 0 : fdt32_to_cpu (InterruptProp[3].Number)\r
464 + (InterruptProp[3].Type ? 16 : 0);\r
ad106932
AB
465\r
466 DEBUG ((EFI_D_INFO, "Found Timer interrupts %d, %d, %d, %d\n",\r
467 SecIntrNum, IntrNum, VirtIntrNum, HypIntrNum));\r
468\r
469 PcdSet32 (PcdArmArchTimerSecIntrNum, SecIntrNum);\r
470 PcdSet32 (PcdArmArchTimerIntrNum, IntrNum);\r
471 PcdSet32 (PcdArmArchTimerVirtIntrNum, VirtIntrNum);\r
472 PcdSet32 (PcdArmArchTimerHypIntrNum, HypIntrNum);\r
473 break;\r
474\r
475 case PropertyTypePsci:\r
476 PsciMethod = fdt_getprop (DeviceTreeBase, Node, "method", &Len);\r
477\r
478 if (PsciMethod && AsciiStrnCmp (PsciMethod, "hvc", 3) == 0) {\r
479 PcdSet32 (PcdArmPsciMethod, 1);\r
480 } else if (PsciMethod && AsciiStrnCmp (PsciMethod, "smc", 3) == 0) {\r
481 PcdSet32 (PcdArmPsciMethod, 2);\r
482 } else {\r
483 DEBUG ((EFI_D_ERROR, "%a: Unknown PSCI method \"%a\"\n", __FUNCTION__,\r
484 PsciMethod));\r
485 }\r
486 break;\r
487\r
488 default:\r
489 break;\r
490 }\r
491 }\r
492\r
493 //\r
494 // UEFI takes ownership of the RTC hardware, and exposes its functionality\r
495 // through the UEFI Runtime Services GetTime, SetTime, etc. This means we\r
496 // need to disable it in the device tree to prevent the OS from attaching its\r
497 // device driver as well.\r
498 //\r
499 if ((RtcNode != -1) &&\r
500 fdt_setprop_string (DeviceTreeBase, RtcNode, "status",\r
501 "disabled") != 0) {\r
502 DEBUG ((EFI_D_WARN, "Failed to set PL031 status to 'disabled'\n"));\r
503 }\r
504 return EFI_SUCCESS;\r
505}\r