/** @file * PCIe Sata support for the Silicon Image I3132 * * Copyright (c) 2011-2015, ARM Limited. All rights reserved. * * This program and the accompanying materials * are licensed and made available under the terms and conditions of the BSD License * which accompanies this distribution. The full text of the license may be found at * http://opensource.org/licenses/bsd-license.php * * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. * **/ #include "SataSiI3132.h" #include #include #include #include #include #define ACPI_SPECFLAG_PREFETCHABLE 0x06 EFI_DRIVER_BINDING_PROTOCOL gSataSiI3132DriverBinding = { SataSiI3132DriverBindingSupported, SataSiI3132DriverBindingStart, SataSiI3132DriverBindingStop, 0x30, NULL, NULL }; EFI_STATUS SataSiI3132PortConstructor ( IN SATA_SI3132_INSTANCE *SataSiI3132Instance, IN UINTN Index ) { EFI_STATUS Status; SATA_SI3132_PORT *Port; VOID *HostPRB; EFI_PHYSICAL_ADDRESS PhysAddrHostPRB; VOID *PciAllocMappingPRB; UINTN NumberOfBytes; Port = &(SataSiI3132Instance->Ports[Index]); Port->Index = Index; Port->RegBase = Index * 0x2000; Port->Instance = SataSiI3132Instance; InitializeListHead (&(Port->Devices)); NumberOfBytes = sizeof (SATA_SI3132_PRB); Status = SataSiI3132Instance->PciIo->AllocateBuffer ( SataSiI3132Instance->PciIo, AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES (NumberOfBytes), &HostPRB, 0 ); if (EFI_ERROR (Status)) { return Status; } // Check the alignment of the PCI Buffer ASSERT (((UINTN)HostPRB & (0x1000 - 1)) == 0); Status = SataSiI3132Instance->PciIo->Map ( SataSiI3132Instance->PciIo, EfiPciIoOperationBusMasterCommonBuffer, HostPRB, &NumberOfBytes, &PhysAddrHostPRB, &PciAllocMappingPRB ); if (EFI_ERROR (Status)) { return Status; } Port->HostPRB = HostPRB; Port->PhysAddrHostPRB = PhysAddrHostPRB; Port->PciAllocMappingPRB = PciAllocMappingPRB; return Status; } STATIC EFI_STATUS SataSiI3132Constructor ( IN EFI_PCI_IO_PROTOCOL *PciIo, OUT SATA_SI3132_INSTANCE** SataSiI3132Instance ) { SATA_SI3132_INSTANCE *Instance; EFI_ATA_PASS_THRU_MODE *AtaPassThruMode; if (!SataSiI3132Instance) { return EFI_INVALID_PARAMETER; } Instance = (SATA_SI3132_INSTANCE*)AllocateZeroPool (sizeof (SATA_SI3132_INSTANCE)); if (Instance == NULL) { return EFI_OUT_OF_RESOURCES; } Instance->Signature = SATA_SII3132_SIGNATURE; Instance->PciIo = PciIo; AtaPassThruMode = (EFI_ATA_PASS_THRU_MODE*)AllocatePool (sizeof (EFI_ATA_PASS_THRU_MODE)); AtaPassThruMode->Attributes = EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_ATA_PASS_THRU_ATTRIBUTES_LOGICAL; AtaPassThruMode->IoAlign = 0x1000; // Initialize SiI3132 ports SataSiI3132PortConstructor (Instance, 0); SataSiI3132PortConstructor (Instance, 1); // Set ATA Pass Thru Protocol Instance->AtaPassThruProtocol.Mode = AtaPassThruMode; Instance->AtaPassThruProtocol.PassThru = SiI3132AtaPassThru; Instance->AtaPassThruProtocol.GetNextPort = SiI3132GetNextPort; Instance->AtaPassThruProtocol.GetNextDevice = SiI3132GetNextDevice; Instance->AtaPassThruProtocol.BuildDevicePath = SiI3132BuildDevicePath; Instance->AtaPassThruProtocol.GetDevice = SiI3132GetDevice; Instance->AtaPassThruProtocol.ResetPort = SiI3132ResetPort; Instance->AtaPassThruProtocol.ResetDevice = SiI3132ResetDevice; *SataSiI3132Instance = Instance; return EFI_SUCCESS; } EFI_STATUS SiI3132SoftResetCommand ( IN SATA_SI3132_PORT *Port, OUT UINT32* Signature ) { EFI_STATUS Status; EFI_ATA_PASS_THRU_COMMAND_PACKET Packet; EFI_ATA_STATUS_BLOCK Asb; EFI_ATA_COMMAND_BLOCK Acb; CONST UINT16 PortMultiplierPort = 0; ZeroMem (&Acb, sizeof (EFI_ATA_COMMAND_BLOCK)); Acb.Reserved1[1] = 0; Packet.Asb = &Asb; Packet.Acb = &Acb; Packet.Timeout = 100000; Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_SOFTWARE_RESET; Status = SiI3132AtaPassThruCommand (Port->Instance, Port, PortMultiplierPort, &Packet, 0); if (Status == EFI_SUCCESS) { *Signature = (Asb.AtaCylinderHigh << 24) | (Asb.AtaCylinderLow << 16) | (Asb.AtaSectorNumber << 8 ) | (Asb.AtaSectorCount); } return Status; } EFI_STATUS SataSiI3132PortInitialization ( IN SATA_SI3132_PORT *Port ) { UINT32 Value32; SATA_SI3132_DEVICE* Device; UINT32 Signature; EFI_STATUS Status; EFI_PCI_IO_PROTOCOL* PciIo; Status = SiI3132HwResetPort (Port); if (EFI_ERROR (Status)) { return Status; } PciIo = Port->Instance->PciIo; // Is a device is present ? Status = SATA_PORT_READ32 (Port->RegBase + SII3132_PORT_SSTATUS_REG, &Value32); if (!EFI_ERROR (Status) && (Value32 & 0x3)) { // Do a soft reset to see if it is a port multiplier SATA_TRACE ("SataSiI3132PortInitialization: soft reset - it is a port multiplier\n"); Status = SiI3132SoftResetCommand (Port, &Signature); if (!EFI_ERROR (Status)) { if (Signature == SII3132_PORT_SIGNATURE_PMP) { SATA_TRACE ("SataSiI3132PortInitialization(): a Port Multiplier is present"); if (FeaturePcdGet (PcdSataSiI3132FeaturePMPSupport)) { ASSERT (0); // Not supported yet } else { return EFI_UNSUPPORTED; } } else if (Signature == SII3132_PORT_SIGNATURE_ATAPI) { ASSERT (0); // Not supported yet SATA_TRACE ("SataSiI3132PortInitialization(): an ATAPI device is present"); return EFI_UNSUPPORTED; } else if (Signature == SII3132_PORT_SIGNATURE_ATA) { SATA_TRACE ("SataSiI3132PortInitialization(): an ATA device is present"); } else { SATA_TRACE ("SataSiI3132PortInitialization(): Present device unknown!"); ASSERT (0); // Not supported return EFI_UNSUPPORTED; } // Create Device Device = (SATA_SI3132_DEVICE*)AllocatePool (sizeof (SATA_SI3132_DEVICE)); Device->Index = Port->Index; //TODO: Could need to be fixed when SATA Port Multiplier support Device->Port = Port; Device->BlockSize = 0; // Attached the device to the Sata Port InsertTailList (&Port->Devices, &Device->Link); SATA_TRACE ("SataSiI3132PortInitialization(): Port Ready"); } } return Status; } EFI_STATUS SataSiI3132Initialization ( IN SATA_SI3132_INSTANCE* SataSiI3132Instance ) { UINTN Index; EFI_PCI_IO_PROTOCOL* PciIo; if (!SataSiI3132Instance) { return EFI_INVALID_PARAMETER; } PciIo = SataSiI3132Instance->PciIo; // Turn Off GPIO SATA_GLOBAL_WRITE32 (SII3132_GLOBAL_FLASHADDR_REG, 0x0); // Clear Global Control Register SATA_GLOBAL_WRITE32 (SII3132_GLOBAL_CONTROL_REG, 0x0); for (Index = 0; Index < SATA_SII3132_MAXPORT; Index++) { SataSiI3132PortInitialization (&(SataSiI3132Instance->Ports[Index])); } return EFI_SUCCESS; } /** Test to see if this driver supports ControllerHandle. @param This Protocol instance pointer. @param Controller Handle of device to test. @param RemainingDevicePath Not used. @return EFI_SUCCESS This driver supports this device. @return EFI_UNSUPPORTED This driver does not support this device. **/ EFI_STATUS EFIAPI SataSiI3132DriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; UINT32 PciID; // // Test whether there is PCI IO Protocol attached on the controller handle. // Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { return Status; } Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, PCI_VENDOR_ID_OFFSET, 1, &PciID ); if (EFI_ERROR (Status)) { Status = EFI_UNSUPPORTED; goto ON_EXIT; } // // Test whether the controller belongs to SATA Mass Storage type // if (PciID != ((SATA_SII3132_DEVICE_ID << 16) | SATA_SII3132_VENDOR_ID)) { Status = EFI_UNSUPPORTED; } ON_EXIT: gBS->CloseProtocol ( Controller, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, Controller ); return Status; } BOOLEAN mbStarted = FALSE; /** Starting the Pci SATA Driver. @param This Protocol instance pointer. @param Controller Handle of device to test. @param RemainingDevicePath Not used. @return EFI_SUCCESS supports this device. @return EFI_UNSUPPORTED do not support this device. @return EFI_DEVICE_ERROR cannot be started due to device Error. @return EFI_OUT_OF_RESOURCES cannot allocate resources. **/ EFI_STATUS EFIAPI SataSiI3132DriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_PCI_IO_PROTOCOL *PciIo; UINT64 Supports; UINT64 OriginalPciAttributes; BOOLEAN PciAttributesSaved; UINT32 PciID; SATA_SI3132_INSTANCE *SataSiI3132Instance = NULL; SATA_TRACE ("SataSiI3132DriverBindingStart()"); //TODO: Find a nicer way to do it ! if (mbStarted) { return EFI_SUCCESS; // Don't restart me ! } // // Open the PciIo Protocol // Status = gBS->OpenProtocol ( Controller, &gEfiPciIoProtocolGuid, (VOID **) &PciIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { return Status; } PciAttributesSaved = FALSE; // // Save original PCI attributes // Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationGet, 0, &OriginalPciAttributes ); if (EFI_ERROR (Status)) { goto CLOSE_PCIIO; } PciAttributesSaved = TRUE; Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSupported, 0, &Supports ); if (!EFI_ERROR (Status)) { Supports &= EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE; Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationEnable, Supports, NULL ); } if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "SataSiI3132DriverBindingStart: failed to enable controller\n")); goto CLOSE_PCIIO; } // // Get the Pci device class code. // Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, PCI_VENDOR_ID_OFFSET, 1, &PciID ); if (EFI_ERROR (Status)) { Status = EFI_UNSUPPORTED; goto CLOSE_PCIIO; } // // Test whether the controller belongs to SATA Mass Storage type // if (PciID != ((SATA_SII3132_DEVICE_ID << 16) | SATA_SII3132_VENDOR_ID)) { Status = EFI_UNSUPPORTED; goto CLOSE_PCIIO; } // Create SiI3132 Sata Instance Status = SataSiI3132Constructor (PciIo, &SataSiI3132Instance); if (EFI_ERROR (Status)) { return Status; } // Initialize SiI3132 Sata Controller Status = SataSiI3132Initialization (SataSiI3132Instance); if (EFI_ERROR (Status)) { return Status; } // Install Ata Pass Thru Protocol Status = gBS->InstallProtocolInterface ( &Controller, &gEfiAtaPassThruProtocolGuid, EFI_NATIVE_INTERFACE, &(SataSiI3132Instance->AtaPassThruProtocol) ); if (EFI_ERROR (Status)) { goto FREE_POOL; } /* // // Create event to stop the HC when exit boot service. // Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, EhcExitBootService, Ehc, &gEfiEventExitBootServicesGuid, &Ehc->ExitBootServiceEvent ); if (EFI_ERROR (Status)) { goto UNINSTALL_USBHC; }*/ mbStarted = TRUE; SATA_TRACE ("SataSiI3132DriverBindingStart() Success!"); return EFI_SUCCESS; FREE_POOL: //TODO: Free SATA Instance CLOSE_PCIIO: if (PciAttributesSaved) { // // Restore original PCI attributes // PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSet, OriginalPciAttributes, NULL ); } gBS->CloseProtocol ( Controller, &gEfiPciIoProtocolGuid, This->DriverBindingHandle, Controller ); return Status; } /** Stop this driver on ControllerHandle. Support stopping any child handles created by this driver. @param This Protocol instance pointer. @param Controller Handle of device to stop driver on. @param NumberOfChildren Number of Children in the ChildHandleBuffer. @param ChildHandleBuffer List of handles for the children we need to stop. @return EFI_SUCCESS Success. @return EFI_DEVICE_ERROR Fail. **/ EFI_STATUS EFIAPI SataSiI3132DriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) { SATA_TRACE ("SataSiI3132DriverBindingStop()"); return EFI_UNSUPPORTED; } /** Entry point of this driver @param ImageHandle Handle of driver image @param SystemTable Point to EFI_SYSTEM_TABLE @retval EFI_OUT_OF_RESOURCES Can not allocate memory resource @retval EFI_DEVICE_ERROR Can not install the protocol instance @retval EFI_SUCCESS Success to initialize the Pci host bridge. **/ EFI_STATUS EFIAPI InitializeSataSiI3132 ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { SATA_TRACE ("InitializeSataSiI3132 ()"); return EfiLibInstallDriverBindingComponentName2 ( ImageHandle, SystemTable, &gSataSiI3132DriverBinding, ImageHandle, &gSataSiI3132ComponentName, &gSataSiI3132ComponentName2 ); }