From: Jan Dąbroś jsd@semihalf.com
Improve PciEmulation driver in order to support SATA controllers. This patch also adds workaround for Cp110 chip, where SATA controller isn't fully compatible with AHCI standard. This patch introduces quirk with AE bit not being set in Global HBA Control register (AHCI Specification section 3.1.2).
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Jan Dabros jsd@semihalf.com Signed-off-by: Marcin Wojtas mw@semihalf.com --- Documentation/Marvell/PortingGuide/Sata.txt | 16 ++++ Platforms/Marvell/Marvell.dec | 9 +- Platforms/Marvell/PciEmulation/PciEmulation.c | 114 +++++++++++++++++++++++- Platforms/Marvell/PciEmulation/PciEmulation.h | 6 ++ Platforms/Marvell/PciEmulation/PciEmulation.inf | 2 + 5 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 Documentation/Marvell/PortingGuide/Sata.txt
diff --git a/Documentation/Marvell/PortingGuide/Sata.txt b/Documentation/Marvell/PortingGuide/Sata.txt new file mode 100644 index 0000000..00df49f --- /dev/null +++ b/Documentation/Marvell/PortingGuide/Sata.txt @@ -0,0 +1,16 @@ +SATA configuration +------------------ +In order to add new SATA device, please refer to document describing +PciEmulation - /path/to/opp/Documenation/Marvell/PortingGuide/PciEmulation.txt + +There are also two additional PCDs: + + gMarvellTokenSpaceGuid.PcdSataBaseAddress + +Base address of SATA controller register space - used in SATA ComPhy init +sequence. + + gMarvellTokenSpaceGuid.PcdSataMapPortAddress + +Boolean indicating if controller on platform needs specific address mapping for +SATA ports. Setting this flag is necessary for CP110-A0 revisions. diff --git a/Platforms/Marvell/Marvell.dec b/Platforms/Marvell/Marvell.dec index f56987d..cf7b555 100644 --- a/Platforms/Marvell/Marvell.dec +++ b/Platforms/Marvell/Marvell.dec @@ -175,9 +175,6 @@ gMarvellTokenSpaceGuid.PcdChip3ComPhySpeeds|{ 0x0 }|VOID*|0x30000176 gMarvellTokenSpaceGuid.PcdChip3ComPhyInvFlags|{ 0x0 }|VOID*|0x30000177
-#SATA - gMarvellTokenSpaceGuid.PcdSataBaseAddress|0|UINT32|0x4000052 - #UtmiPhy gMarvellTokenSpaceGuid.PcdUtmiPhyCount|0|UINT32|0x30000205 gMarvellTokenSpaceGuid.PcdUtmiPhyRegUsbCfg|{ 0x0 }|VOID*|0x30000206 @@ -221,6 +218,12 @@ gMarvellTokenSpaceGuid.PcdResetRegAddress|0|UINT64|0x40000050 gMarvellTokenSpaceGuid.PcdResetRegMask|0|UINT32|0x4000051
+#SATA + gMarvellTokenSpaceGuid.PcdSataBaseAddress|0|UINT32|0x4000052 + +[PcdsFeatureFlag.common] + gMarvellTokenSpaceGuid.PcdSataMapPortAddress|FALSE|BOOLEAN|0x4000053 + [Protocols] gMarvellEepromProtocolGuid = { 0x71954bda, 0x60d3, 0x4ef8, { 0x8e, 0x3c, 0x0e, 0x33, 0x9f, 0x3b, 0xc2, 0x2b }} gMarvellMdioProtocolGuid = { 0x40010b03, 0x5f08, 0x496a, { 0xa2, 0x64, 0x10, 0x5e, 0x72, 0xd3, 0x71, 0xaa }} diff --git a/Platforms/Marvell/PciEmulation/PciEmulation.c b/Platforms/Marvell/PciEmulation/PciEmulation.c index a800bcb..7cfab89 100644 --- a/Platforms/Marvell/PciEmulation/PciEmulation.c +++ b/Platforms/Marvell/PciEmulation/PciEmulation.c @@ -71,6 +71,30 @@ EFI_PCI_IO_DEVICE_PATH PciIoDevicePathTemplate = } };
+STATIC +UINT64 +MapSataPortAddress ( + IN UINT64 Offset + ) +{ + + // + // Since AHCI controller in CP110 isn't compatible with AHCI specification, + // below workaround is necessary. It maps PORT address' offsets. + // + // PORT | AHCI port offset | CP110 port offset + // -------------------------------------------------------- + // 1 | 100h - 180h | 10000h - 10080h + // 2 | 180h - 260h | 20000h - 20080h + // + if ((Offset >= 0x100) && (Offset < 0x180)) + Offset = (Offset - 0x100) + 0x10000; + if ((Offset >= 0x180) && (Offset < 0x260)) + Offset = (Offset - 0x180) + 0x20000; + + return Offset; +} + EFI_STATUS PciIoPollMem ( IN EFI_PCI_IO_PROTOCOL *This, @@ -125,6 +149,43 @@ PciIoMemRead ( }
EFI_STATUS +PciIoSataMemRead ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + EFI_PCI_IO_PRIVATE_DATA *Private = EFI_PCI_IO_PRIVATE_DATA_FROM_THIS(This); + EFI_STATUS Status; + UINT32 *TmpBuffer; + + if (FeaturePcdGet (PcdSataMapPortAddress)) { + Offset = MapSataPortAddress (Offset); + } + + Status = PciRootBridgeIoMemRead (&Private->RootBridge.Io, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Private->ConfigSpace->Device.Bar[BarIndex] + Offset, + Count, + Buffer); + + // + // AE bit in Global HBA Control register is set only if SAM bit is cleared in + // CAP register. Below quirk makes PciIoMemRead to return value in this + // register with SAM bit unset. + // + if (Offset == 0) { + TmpBuffer = (UINT32 *) Buffer; + *TmpBuffer &= ~(1<<18); + } + + return Status; +} + +EFI_STATUS PciIoMemWrite ( IN EFI_PCI_IO_PROTOCOL *This, IN EFI_PCI_IO_PROTOCOL_WIDTH Width, @@ -136,6 +197,37 @@ PciIoMemWrite ( { EFI_PCI_IO_PRIVATE_DATA *Private = EFI_PCI_IO_PRIVATE_DATA_FROM_THIS(This);
+ if (FeaturePcdGet (PcdSataMapPortAddress)) { + if (BarIndex == EFI_AHCI_BAR_INDEX) { + Offset = MapSataPortAddress (Offset); + } + } + + return PciRootBridgeIoMemWrite ( + &Private->RootBridge.Io, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Private->ConfigSpace->Device.Bar[BarIndex] + Offset, + Count, + Buffer + ); +} + +EFI_STATUS +PciIoSataMemWrite ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + EFI_PCI_IO_PRIVATE_DATA *Private = EFI_PCI_IO_PRIVATE_DATA_FROM_THIS(This); + + if (FeaturePcdGet (PcdSataMapPortAddress)) { + Offset = MapSataPortAddress (Offset); + } + return PciRootBridgeIoMemWrite ( &Private->RootBridge.Io, (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, @@ -545,6 +637,7 @@ InstallDevices ( EFI_PCI_IO_PRIVATE_DATA *Private; EFI_STATUS Status; EFI_HANDLE Handle; + UINT32 ClassCodes;
// Create a private structure Private = AllocatePool(sizeof(EFI_PCI_IO_PRIVATE_DATA)); @@ -588,7 +681,6 @@ InstallDevices ( Private->ConfigSpace->Hdr.ClassCode[0] = ClassCode1; Private->ConfigSpace->Hdr.ClassCode[1] = ClassCode2; Private->ConfigSpace->Hdr.ClassCode[2] = ClassCode3; - Private->ConfigSpace->Device.Bar[0] = Private->RootBridge.MemoryStart;
Handle = NULL;
@@ -600,6 +692,26 @@ InstallDevices ( // Copy protocol structure CopyMem(&Private->PciIoProtocol, &PciIoTemplate, sizeof(PciIoTemplate));
+ // + // Concatenate ClassCodes into single number, which will be next used to + // recognize device and fill proper BAR + // + ClassCodes = (ClassCode1 << 16) + (ClassCode2 << 8) + ClassCode3; + + switch (ClassCodes) { + case XHCI_PCI_CLASS_CODE_NR: + Private->ConfigSpace->Device.Bar[EFI_XHCI_BAR_INDEX] = Private->RootBridge.MemoryStart; + break; + case AHCI_PCI_CLASS_CODE_NR: + Private->ConfigSpace->Device.Bar[EFI_AHCI_BAR_INDEX] = Private->RootBridge.MemoryStart; + Private->PciIoProtocol.Mem.Read = PciIoSataMemRead; + Private->PciIoProtocol.Mem.Write = PciIoSataMemWrite; + break; + default: + DEBUG((DEBUG_ERROR, "PciEmulation: Unknown PCI device. Abort.\n")); + return EFI_INVALID_PARAMETER; + } + Status = gBS->InstallMultipleProtocolInterfaces( &Handle, &gEfiPciIoProtocolGuid, diff --git a/Platforms/Marvell/PciEmulation/PciEmulation.h b/Platforms/Marvell/PciEmulation/PciEmulation.h index 4f75539..2323962 100644 --- a/Platforms/Marvell/PciEmulation/PciEmulation.h +++ b/Platforms/Marvell/PciEmulation/PciEmulation.h @@ -47,6 +47,12 @@
#define PCI_ROOT_BRIDGE_SIGNATURE SIGNATURE_32 ('P', 'c', 'i', 'F')
+#define XHCI_PCI_CLASS_CODE_NR 0x30030C +#define AHCI_PCI_CLASS_CODE_NR 0x010601 + +#define EFI_XHCI_BAR_INDEX 0x0 +#define EFI_AHCI_BAR_INDEX 0x5 + typedef struct { ACPI_HID_DEVICE_PATH AcpiDevicePath; EFI_DEVICE_PATH_PROTOCOL EndDevicePath; diff --git a/Platforms/Marvell/PciEmulation/PciEmulation.inf b/Platforms/Marvell/PciEmulation/PciEmulation.inf index 30ddfc2..1651715 100644 --- a/Platforms/Marvell/PciEmulation/PciEmulation.inf +++ b/Platforms/Marvell/PciEmulation/PciEmulation.inf @@ -61,5 +61,7 @@ gMarvellTokenSpaceGuid.PcdPciEDevCount gMarvellTokenSpaceGuid.PcdPciEDevRegSize
+ gMarvellTokenSpaceGuid.PcdSataMapPortAddress + [Depex] TRUE