/** @file SynQuacer DXE platform driver - PCIe support Copyright (c) 2017, Linaro, Ltd. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PlatformDxe.h" #define ASMEDIA_VID 0x1b21 #define ASM1061_PID 0x0612 #define ASM1182E_PID 0x1182 #define ASM1184E_PID 0x1184 #define ASM1061_SSC_OFFSET 0xA10 #define ASM118x_PCIE_CAPABILITY_OFFSET 0x80 #define ASM118x_PCIE_LINK_CONTROL_OFFSET (ASM118x_PCIE_CAPABILITY_OFFSET + \ OFFSET_OF (PCI_CAPABILITY_PCIEXP, \ LinkControl)) STATIC VOID *mPciProtocolNotifyRegistration; STATIC EFI_EVENT mPciProtocolNotifyEvent; #pragma pack(1) typedef struct { EFI_PCI_CAPABILITY_HDR CapHdr; PCI_REG_PCIE_CAPABILITY Pcie; } PCIE_CAP; #pragma pack() STATIC VOID RetrainAsm1184eDownstreamPort ( IN EFI_PCI_IO_PROTOCOL *PciIo ) { EFI_STATUS Status; PCIE_CAP Cap; PCI_REG_PCIE_LINK_CONTROL LinkControl; UINTN SegmentNumber; UINTN BusNumber; UINTN DeviceNumber; UINTN FunctionNumber; UINT8 MaxSpeed; // // The upstream and downstream ports share the same PID/VID, so check // the port type. This assumes the PCIe Express capability block lives // at offset 0x80 in the port's config space, which is known to be the // case for these particular chips. // ASSERT (sizeof (Cap) == sizeof (UINT32)); ASSERT (sizeof (LinkControl) == sizeof (UINT16)); Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ASM118x_PCIE_CAPABILITY_OFFSET, 1, &Cap); ASSERT_EFI_ERROR (Status); ASSERT (Cap.CapHdr.CapabilityID == EFI_PCI_CAPABILITY_ID_PCIEXP); if (Cap.Pcie.Bits.DevicePortType != PCIE_DEVICE_PORT_TYPE_DOWNSTREAM_PORT) { return; } Status = PciIo->GetLocation (PciIo, &SegmentNumber, &BusNumber, &DeviceNumber, &FunctionNumber); ASSERT_EFI_ERROR (Status); // // Check whether this downstream port is described by any of our 'slot' // definitions, and get the maximum speed if this is the case. // switch (SYNQUACER_PCI_LOCATION (SegmentNumber, BusNumber, DeviceNumber)) { case SYNQUACER_PCI_SLOT0_LOCATION: MaxSpeed = mHiiSettings->PcieSlot0MaxSpeed; break; case SYNQUACER_PCI_SLOT1_LOCATION: MaxSpeed = mHiiSettings->PcieSlot1MaxSpeed; break; case SYNQUACER_PCI_SLOT2_LOCATION: MaxSpeed = mHiiSettings->PcieSlot2MaxSpeed; break; default: MaxSpeed = PCIE_MAX_SPEED_UNLIMITED; } if (MaxSpeed == PCIE_MAX_SPEED_GEN1) { return; } DEBUG ((DEBUG_INFO, "%a: retraining ASM118x downstream PCIe port at %04x:%02x:%02x to Gen2\n", __FUNCTION__, SegmentNumber, BusNumber, DeviceNumber)); Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, ASM118x_PCIE_LINK_CONTROL_OFFSET, 1, &LinkControl); ASSERT_EFI_ERROR (Status); LinkControl.Bits.RetrainLink = 1; Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, ASM118x_PCIE_LINK_CONTROL_OFFSET, 1, &LinkControl); ASSERT_EFI_ERROR (Status); } STATIC VOID EnableAsm1061SpreadSpectrum ( IN EFI_PCI_IO_PROTOCOL *PciIo ) { EFI_STATUS Status; UINT8 SscVal; DEBUG ((DEBUG_INFO, "%a: enabling spread spectrum mode 0 for ASM1061\n", __FUNCTION__)); // SSC mode 0~-4000 ppm, 1:1 modulation SscVal = 0; Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, ASM1061_SSC_OFFSET, 1, &SscVal); ASSERT_EFI_ERROR (Status); MemoryFence (); gBS->Stall (1); // delay at least 100 ns between writes of the same register SscVal = 1; Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, ASM1061_SSC_OFFSET, 1, &SscVal); ASSERT_EFI_ERROR (Status); } STATIC VOID EFIAPI OnPciIoProtocolNotify ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_PCI_IO_PROTOCOL *PciIo; EFI_STATUS Status; EFI_HANDLE HandleBuffer; UINTN BufferSize; UINT16 PciVidPid[2]; while (TRUE) { BufferSize = sizeof (EFI_HANDLE); Status = gBS->LocateHandle (ByRegisterNotify, NULL, mPciProtocolNotifyRegistration, &BufferSize, &HandleBuffer); if (EFI_ERROR (Status)) { break; } Status = gBS->HandleProtocol (HandleBuffer, &gEfiPciIoProtocolGuid, (VOID **)&PciIo); ASSERT_EFI_ERROR (Status); Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PCI_VENDOR_ID_OFFSET, ARRAY_SIZE (PciVidPid), &PciVidPid); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "%a: failed to read PCI vendor/product ID - %r\n", __FUNCTION__, Status)); continue; } if (PciVidPid[0] != ASMEDIA_VID) { continue; } switch (PciVidPid[1]) { case ASM1061_PID: // // The ASM1061 SATA controller as integrated into the DeveloperBox design // emits too much electromagnetic radiation. So enable spread spectrum // mode. // EnableAsm1061SpreadSpectrum (PciIo); break; case ASM1182E_PID: case ASM1184E_PID: // // The ASM1184E 4-port PCIe switch on the DeveloperBox board (and its // 2-port sibling of which samples were used in development) needs a // little nudge to get it to train the downstream links at Gen2 speed. // RetrainAsm1184eDownstreamPort (PciIo); break; } } } EFI_STATUS EFIAPI RegisterPcieNotifier ( VOID ) { mPciProtocolNotifyEvent = EfiCreateProtocolNotifyEvent ( &gEfiPciIoProtocolGuid, TPL_CALLBACK, OnPciIoProtocolNotify, NULL, &mPciProtocolNotifyRegistration); return EFI_SUCCESS; }