]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c
da93eb582909def1ac1d28b53014b222b2e1e0cc
[mirror_edk2.git] / ArmPlatformPkg / ArmJunoPkg / Drivers / ArmJunoDxe / ArmJunoDxe.c
1 /** @file
2 *
3 * Copyright (c) 2013-2015, ARM Limited. All rights reserved.
4 *
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
9 *
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14
15 #include "ArmJunoDxeInternal.h"
16 #include <ArmPlatform.h>
17
18 #include <IndustryStandard/Pci.h>
19 #include <Protocol/DevicePathFromText.h>
20 #include <Protocol/PciIo.h>
21 #include <Protocol/PciRootBridgeIo.h>
22
23 #include <Guid/EventGroup.h>
24 #include <Guid/GlobalVariable.h>
25
26 #include <Library/ArmShellCmdLib.h>
27 #include <Library/AcpiLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/DevicePathLib.h>
30 #include <Library/MemoryAllocationLib.h>
31 #include <Library/NonDiscoverableDeviceRegistrationLib.h>
32 #include <Library/UefiRuntimeServicesTableLib.h>
33 #include <Library/IoLib.h>
34 #include <Library/PrintLib.h>
35
36
37 // This GUID must match the FILE_GUID in ArmPlatformPkg/ArmJunoPkg/AcpiTables/AcpiTables.inf
38 STATIC CONST EFI_GUID mJunoAcpiTableFile = { 0xa1dd808e, 0x1e95, 0x4399, { 0xab, 0xc0, 0x65, 0x3c, 0x82, 0xe8, 0x53, 0x0c } };
39
40 typedef struct {
41 ACPI_HID_DEVICE_PATH AcpiDevicePath;
42 PCI_DEVICE_PATH PciDevicePath;
43 EFI_DEVICE_PATH_PROTOCOL EndDevicePath;
44 } EFI_PCI_ROOT_BRIDGE_DEVICE_PATH;
45
46 STATIC CONST EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mPciRootComplexDevicePath = {
47 {
48 { ACPI_DEVICE_PATH,
49 ACPI_DP,
50 { (UINT8) (sizeof (ACPI_HID_DEVICE_PATH)),
51 (UINT8) ((sizeof (ACPI_HID_DEVICE_PATH)) >> 8) }
52 },
53 EISA_PNP_ID (0x0A03),
54 0
55 },
56 {
57 { HARDWARE_DEVICE_PATH,
58 HW_PCI_DP,
59 { (UINT8) (sizeof (PCI_DEVICE_PATH)),
60 (UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8) }
61 },
62 0,
63 0
64 },
65 {
66 END_DEVICE_PATH_TYPE,
67 END_ENTIRE_DEVICE_PATH_SUBTYPE,
68 { END_DEVICE_PATH_LENGTH, 0 }
69 }
70 };
71
72 EFI_EVENT mAcpiRegistration = NULL;
73
74 /**
75 This function reads PCI ID of the controller.
76
77 @param[in] PciIo PCI IO protocol handle
78 @param[in] PciId Looking for specified PCI ID Vendor/Device
79 **/
80 STATIC
81 EFI_STATUS
82 ReadMarvellYoukonPciId (
83 IN EFI_PCI_IO_PROTOCOL *PciIo,
84 IN UINT32 PciId
85 )
86 {
87 UINT32 DevicePciId;
88 EFI_STATUS Status;
89
90 Status = PciIo->Pci.Read (
91 PciIo,
92 EfiPciIoWidthUint32,
93 PCI_VENDOR_ID_OFFSET,
94 1,
95 &DevicePciId);
96 if (EFI_ERROR (Status)) {
97 return Status;
98 }
99
100 if (DevicePciId != PciId) {
101 return EFI_NOT_FOUND;
102 }
103
104 return EFI_SUCCESS;
105 }
106
107 /**
108 This function searches for Marvell Yukon NIC on the Juno
109 platform and returns PCI IO protocol handle for the controller.
110
111 @param[out] PciIo PCI IO protocol handle
112 **/
113 STATIC
114 EFI_STATUS
115 GetMarvellYukonPciIoProtocol (
116 OUT EFI_PCI_IO_PROTOCOL **PciIo
117 )
118 {
119 UINTN HandleCount;
120 EFI_HANDLE *HandleBuffer;
121 UINTN HIndex;
122 EFI_STATUS Status;
123
124 Status = gBS->LocateHandleBuffer (
125 ByProtocol,
126 &gEfiPciIoProtocolGuid,
127 NULL,
128 &HandleCount,
129 &HandleBuffer);
130 if (EFI_ERROR (Status)) {
131 return (Status);
132 }
133
134 for (HIndex = 0; HIndex < HandleCount; ++HIndex) {
135 // If PciIo opened with EFI_OPEN_PROTOCOL_GET_PROTOCOL, the CloseProtocol() is not required
136 Status = gBS->OpenProtocol (
137 HandleBuffer[HIndex],
138 &gEfiPciIoProtocolGuid,
139 (VOID **) PciIo,
140 NULL,
141 NULL,
142 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
143 if (EFI_ERROR (Status)) {
144 continue;
145 }
146
147 Status = ReadMarvellYoukonPciId (*PciIo, JUNO_MARVELL_YUKON_ID);
148 if (EFI_ERROR (Status)) {
149 continue;
150 } else {
151 break;
152 }
153 }
154
155 gBS->FreePool (HandleBuffer);
156
157 return Status;
158 }
159
160 /**
161 This function restore the original controller attributes
162
163 @param[in] PciIo PCI IO protocol handle
164 @param[in] PciAttr PCI controller attributes.
165 @param[in] AcpiResDescriptor ACPI 2.0 resource descriptors for the BAR
166 **/
167 STATIC
168 VOID
169 RestorePciDev (
170 IN EFI_PCI_IO_PROTOCOL *PciIo,
171 IN UINT64 PciAttr
172 )
173 {
174 PciIo->Attributes (
175 PciIo,
176 EfiPciIoAttributeOperationSet,
177 PciAttr,
178 NULL
179 );
180 }
181
182 /**
183 This function returns PCI MMIO base address for a controller
184
185 @param[in] PciIo PCI IO protocol handle
186 @param[out] PciRegBase PCI base MMIO address
187 **/
188 STATIC
189 EFI_STATUS
190 BarIsDeviceMemory (
191 IN EFI_PCI_IO_PROTOCOL *PciIo,
192 OUT UINT32 *PciRegBase
193 )
194 {
195 EFI_STATUS Status;
196 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AcpiResDescriptor;
197 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AcpiCurrentDescriptor;
198
199 // Marvell Yukon's Bar0 provides base memory address for control registers
200 Status = PciIo->GetBarAttributes (PciIo, PCI_BAR_IDX0, NULL, (VOID**)&AcpiResDescriptor);
201 if (EFI_ERROR (Status)) {
202 return Status;
203 }
204
205 AcpiCurrentDescriptor = AcpiResDescriptor;
206
207 // Search for a memory type descriptor
208 while (AcpiCurrentDescriptor->Desc != ACPI_END_TAG_DESCRIPTOR) {
209
210 // Check if Bar is memory type one and fetch a base address
211 if (AcpiCurrentDescriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR &&
212 AcpiCurrentDescriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM &&
213 !(AcpiCurrentDescriptor->SpecificFlag & ACPI_SPECFLAG_PREFETCHABLE)) {
214 *PciRegBase = AcpiCurrentDescriptor->AddrRangeMin;
215 break;
216 } else {
217 Status = EFI_UNSUPPORTED;
218 }
219
220 AcpiCurrentDescriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) (AcpiCurrentDescriptor + 1);
221 }
222
223 gBS->FreePool (AcpiResDescriptor);
224
225 return Status;
226 }
227
228 /**
229 This function provides PCI MMIO base address, old PCI controller attributes.
230
231 @param[in] PciIo PCI IO protocol handle
232 @param[out] PciRegBase PCI base MMIO address
233 @param[out] OldPciAttr Old PCI controller attributes.
234 **/
235 STATIC
236 EFI_STATUS
237 InitPciDev (
238 IN EFI_PCI_IO_PROTOCOL *PciIo,
239 OUT UINT32 *PciRegBase,
240 OUT UINT64 *OldPciAttr
241 )
242 {
243 UINT64 AttrSupports;
244 EFI_STATUS Status;
245
246 // Get controller's current attributes
247 Status = PciIo->Attributes (
248 PciIo,
249 EfiPciIoAttributeOperationGet,
250 0,
251 OldPciAttr);
252 if (EFI_ERROR (Status)) {
253 return Status;
254 }
255
256 // Fetch supported attributes
257 Status = PciIo->Attributes (
258 PciIo,
259 EfiPciIoAttributeOperationSupported,
260 0,
261 &AttrSupports);
262 if (EFI_ERROR (Status)) {
263 return Status;
264 }
265
266 // Enable EFI_PCI_IO_ATTRIBUTE_IO, EFI_PCI_IO_ATTRIBUTE_MEMORY and
267 // EFI_PCI_IO_ATTRIBUTE_BUS_MASTER bits in the PCI Config Header
268 AttrSupports &= EFI_PCI_DEVICE_ENABLE;
269 Status = PciIo->Attributes (
270 PciIo,
271 EfiPciIoAttributeOperationEnable,
272 AttrSupports,
273 NULL);
274 if (EFI_ERROR (Status)) {
275 return Status;
276 }
277
278 Status = BarIsDeviceMemory (PciIo, PciRegBase);
279 if (EFI_ERROR (Status)) {
280 RestorePciDev (PciIo, *OldPciAttr);
281 }
282
283 return Status;
284 }
285
286 /**
287 This function reads MAC address from IOFPGA and writes it to Marvell Yukon NIC
288
289 @param[in] PciRegBase PCI base MMIO address
290 **/
291 STATIC
292 EFI_STATUS
293 WriteMacAddress (
294 IN UINT32 PciRegBase
295 )
296 {
297 UINT32 MacHigh;
298 UINT32 MacLow;
299
300 // Read MAC address from IOFPGA
301 MacHigh= MmioRead32 (ARM_JUNO_SYS_PCIGBE_H);
302 MacLow = MmioRead32 (ARM_JUNO_SYS_PCIGBE_L);
303
304 // Set software reset control register to protect from deactivation
305 // the config write state
306 MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR);
307
308 // Convert to Marvell MAC Address register format
309 MacHigh = SwapBytes32 ((MacHigh & 0xFFFF) << 16 |
310 (MacLow & 0xFFFF0000) >> 16);
311 MacLow = SwapBytes32 (MacLow) >> 16;
312
313 // Set MAC Address
314 MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_ENABLE);
315 MmioWrite32 (PciRegBase + R_MAC, MacHigh);
316 MmioWrite32 (PciRegBase + R_MAC_MAINT, MacHigh);
317 MmioWrite32 (PciRegBase + R_MAC + R_MAC_LOW, MacLow);
318 MmioWrite32 (PciRegBase + R_MAC_MAINT + R_MAC_LOW, MacLow);
319 MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_DISABLE);
320
321 // Initiate device reset
322 MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_SET);
323 MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR);
324
325 return EFI_SUCCESS;
326 }
327
328 /**
329 The function reads MAC address from Juno IOFPGA registers and writes it
330 into Marvell Yukon NIC.
331 **/
332 STATIC
333 EFI_STATUS
334 ArmJunoSetNicMacAddress ()
335 {
336 UINT64 OldPciAttr;
337 EFI_PCI_IO_PROTOCOL* PciIo;
338 UINT32 PciRegBase;
339 EFI_STATUS Status;
340
341 Status = GetMarvellYukonPciIoProtocol (&PciIo);
342 if (EFI_ERROR (Status)) {
343 return Status;
344 }
345
346 Status = InitPciDev (PciIo, &PciRegBase, &OldPciAttr);
347 if (EFI_ERROR (Status)) {
348 return Status;
349 }
350
351 Status = WriteMacAddress (PciRegBase);
352
353 RestorePciDev (PciIo, OldPciAttr);
354
355 return EFI_SUCCESS;
356 }
357
358 /**
359 Notification function of the event defined as belonging to the
360 EFI_END_OF_DXE_EVENT_GROUP_GUID event group that was created in
361 the entry point of the driver.
362
363 This function is called when an event belonging to the
364 EFI_END_OF_DXE_EVENT_GROUP_GUID event group is signalled. Such an
365 event is signalled once at the end of the dispatching of all
366 drivers (end of the so called DXE phase).
367
368 @param[in] Event Event declared in the entry point of the driver whose
369 notification function is being invoked.
370 @param[in] Context NULL
371 **/
372 STATIC
373 VOID
374 OnEndOfDxe (
375 IN EFI_EVENT Event,
376 IN VOID *Context
377 )
378 {
379 EFI_DEVICE_PATH_PROTOCOL* PciRootComplexDevicePath;
380 EFI_HANDLE Handle;
381 EFI_STATUS Status;
382
383 //
384 // PCI Root Complex initialization
385 // At the end of the DXE phase, we should get all the driver dispatched.
386 // Force the PCI Root Complex to be initialized. It allows the OS to skip
387 // this step.
388 //
389 PciRootComplexDevicePath = (EFI_DEVICE_PATH_PROTOCOL*) &mPciRootComplexDevicePath;
390 Status = gBS->LocateDevicePath (&gEfiPciRootBridgeIoProtocolGuid,
391 &PciRootComplexDevicePath,
392 &Handle);
393
394 Status = gBS->ConnectController (Handle, NULL, PciRootComplexDevicePath, FALSE);
395 ASSERT_EFI_ERROR (Status);
396
397 Status = ArmJunoSetNicMacAddress ();
398 if (EFI_ERROR (Status)) {
399 DEBUG ((DEBUG_ERROR, "ArmJunoDxe: Failed to set Marvell Yukon NIC MAC address\n"));
400 }
401 }
402
403 EFI_STATUS
404 EFIAPI
405 ArmJunoEntryPoint (
406 IN EFI_HANDLE ImageHandle,
407 IN EFI_SYSTEM_TABLE *SystemTable
408 )
409 {
410 EFI_STATUS Status;
411 EFI_PHYSICAL_ADDRESS HypBase;
412 CHAR16 *TextDevicePath;
413 UINTN TextDevicePathSize;
414 VOID *Buffer;
415 UINT32 JunoRevision;
416 EFI_EVENT EndOfDxeEvent;
417
418 //
419 // Register the OHCI and EHCI controllers as non-coherent
420 // non-discoverable devices.
421 //
422 Status = RegisterNonDiscoverableMmioDevice (
423 NonDiscoverableDeviceTypeOhci,
424 NonDiscoverableDeviceDmaTypeNonCoherent,
425 NULL,
426 NULL,
427 1,
428 FixedPcdGet32 (PcdSynopsysUsbOhciBaseAddress),
429 SIZE_64KB
430 );
431 ASSERT_EFI_ERROR (Status);
432
433 Status = RegisterNonDiscoverableMmioDevice (
434 NonDiscoverableDeviceTypeEhci,
435 NonDiscoverableDeviceDmaTypeNonCoherent,
436 NULL,
437 NULL,
438 1,
439 FixedPcdGet32 (PcdSynopsysUsbEhciBaseAddress),
440 SIZE_64KB
441 );
442 ASSERT_EFI_ERROR (Status);
443
444 //
445 // If a hypervisor has been declared then we need to make sure its region is protected at runtime
446 //
447 // Note: This code is only a workaround for our dummy hypervisor (ArmPkg/Extra/AArch64ToAArch32Shim/)
448 // that does not set up (yet) the stage 2 translation table to hide its own memory to EL1.
449 //
450 if (FixedPcdGet32 (PcdHypFvSize) != 0) {
451 // Ensure the hypervisor region is strictly contained into a EFI_PAGE_SIZE-aligned region.
452 // The memory must be a multiple of EFI_PAGE_SIZE to ensure we do not reserve more memory than the hypervisor itself.
453 // A UEFI Runtime region size granularity cannot be smaller than EFI_PAGE_SIZE. If the hypervisor size is not rounded
454 // to this size then there is a risk some non-runtime memory could be visible to the OS view.
455 if (((FixedPcdGet32 (PcdHypFvSize) & EFI_PAGE_MASK) == 0) && ((FixedPcdGet32 (PcdHypFvBaseAddress) & EFI_PAGE_MASK) == 0)) {
456 // The memory needs to be declared because the DXE core marked it as reserved and removed it from the memory space
457 // as it contains the Firmware.
458 Status = gDS->AddMemorySpace (
459 EfiGcdMemoryTypeSystemMemory,
460 FixedPcdGet32 (PcdHypFvBaseAddress), FixedPcdGet32 (PcdHypFvSize),
461 EFI_MEMORY_WB | EFI_MEMORY_RUNTIME
462 );
463 if (!EFI_ERROR (Status)) {
464 // We allocate the memory to ensure it is marked as runtime memory
465 HypBase = FixedPcdGet32 (PcdHypFvBaseAddress);
466 Status = gBS->AllocatePages (AllocateAddress, EfiRuntimeServicesCode,
467 EFI_SIZE_TO_PAGES (FixedPcdGet32 (PcdHypFvSize)), &HypBase);
468 }
469 } else {
470 // The hypervisor must be contained into a EFI_PAGE_SIZE-aligned region and its size must also be aligned
471 // on a EFI_PAGE_SIZE boundary (ie: 4KB).
472 Status = EFI_UNSUPPORTED;
473 ASSERT_EFI_ERROR (Status);
474 }
475
476 if (EFI_ERROR (Status)) {
477 return Status;
478 }
479 }
480
481 // Install dynamic Shell command to run baremetal binaries.
482 Status = ShellDynCmdRunAxfInstall (ImageHandle);
483 if (EFI_ERROR (Status)) {
484 DEBUG ((EFI_D_ERROR, "ArmJunoDxe: Failed to install ShellDynCmdRunAxf\n"));
485 }
486
487 GetJunoRevision(JunoRevision);
488
489 //
490 // Try to install the ACPI Tables
491 //
492 Status = LocateAndInstallAcpiFromFv (&mJunoAcpiTableFile);
493 ASSERT_EFI_ERROR (Status);
494
495 //
496 // Setup R1/R2 options if not already done.
497 //
498 if (JunoRevision != JUNO_REVISION_R0) {
499 // Enable PCI enumeration
500 PcdSetBool (PcdPciDisableBusEnumeration, FALSE);
501
502 //
503 // Create an event belonging to the "gEfiEndOfDxeEventGroupGuid" group.
504 // The "OnEndOfDxe()" function is declared as the call back function.
505 // It will be called at the end of the DXE phase when an event of the
506 // same group is signalled to inform about the end of the DXE phase.
507 // Install the INSTALL_FDT_PROTOCOL protocol.
508 //
509 Status = gBS->CreateEventEx (
510 EVT_NOTIFY_SIGNAL,
511 TPL_CALLBACK,
512 OnEndOfDxe,
513 NULL,
514 &gEfiEndOfDxeEventGroupGuid,
515 &EndOfDxeEvent
516 );
517
518 // Declare the related ACPI Tables
519 EfiCreateProtocolNotifyEvent (
520 &gEfiAcpiTableProtocolGuid,
521 TPL_CALLBACK,
522 AcpiPciNotificationEvent,
523 NULL,
524 &mAcpiRegistration
525 );
526 }
527
528 //
529 // Set up the device path to the FDT.
530 //
531 TextDevicePath = (CHAR16*)FixedPcdGetPtr (PcdJunoFdtDevicePath);
532 if (TextDevicePath != NULL) {
533 TextDevicePathSize = StrSize (TextDevicePath);
534 Buffer = PcdSetPtr (PcdFdtDevicePaths, &TextDevicePathSize, TextDevicePath);
535 Status = (Buffer != NULL) ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL;
536 } else {
537 Status = EFI_NOT_FOUND;
538 }
539
540 if (EFI_ERROR (Status)) {
541 DEBUG (
542 (EFI_D_ERROR,
543 "ArmJunoDxe: Setting of FDT device path in PcdFdtDevicePaths failed - %r\n", Status)
544 );
545 return Status;
546 }
547
548 return Status;
549 }