summaryrefslogtreecommitdiff
path: root/ReferenceCode/Chipset/LynxPoint/SataController/Dxe/SataController.c
diff options
context:
space:
mode:
Diffstat (limited to 'ReferenceCode/Chipset/LynxPoint/SataController/Dxe/SataController.c')
-rw-r--r--ReferenceCode/Chipset/LynxPoint/SataController/Dxe/SataController.c943
1 files changed, 943 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/LynxPoint/SataController/Dxe/SataController.c b/ReferenceCode/Chipset/LynxPoint/SataController/Dxe/SataController.c
new file mode 100644
index 0000000..c53575c
--- /dev/null
+++ b/ReferenceCode/Chipset/LynxPoint/SataController/Dxe/SataController.c
@@ -0,0 +1,943 @@
+/** @file
+ This driver module produces IDE_CONTROLLER_INIT protocol for serial ATA
+ driver and will be used by IDE Bus driver to support chipset dependent timing
+ information, config SATA control/status registers. This driver
+ is responsible for early initialization of serial ATA controller.
+
+ Serial ATA spec requires SATA controller compatible with parallel IDE
+ controller. That's why lots of code here is the same with IDE controller
+ driver. However, We need this driver to optimize timing settings for SATA
+ device and set SATA config/error/status registers.
+
+@copyright
+ Copyright (c) 1999 - 2012 Intel Corporation. All rights reserved
+ This software and associated documentation (if any) is furnished
+ under a license and may only be used or copied in accordance
+ with the terms of the license. Except as permitted by such
+ license, no part o f this software or documentation may be
+ reproduced, stored in a retrieval system, or transmitted in any
+ form or by any means without the express written consent of
+ Intel Corporation.
+
+ This file contains an 'Intel Peripheral Driver' and uniquely
+ identified as "Intel Reference Module" and is
+ licensed for Intel CPUs and chipsets under the terms of your
+ license agreement with Intel or your vendor. This file may
+ be modified by the user, subject to additional terms of the
+ license agreement
+
+**/
+#include "SataController.h"
+
+///
+/// SATA controller Driver GUID
+///
+EFI_GUID gSataControllerDriverGuid = PCH_SATA_CONTROLLER_DRIVER_GUID;
+
+///
+/// EFI_DRIVER_BINDING_PROTOCOL instance
+///
+EFI_DRIVER_BINDING_PROTOCOL mSataControllerDriverBinding = {
+ SataControllerSupported,
+ SataControllerStart,
+ SataControllerStop,
+ 0x10,
+ NULL,
+ NULL
+};
+
+//
+// Internal function definitions
+//
+EFI_STATUS
+CalculateBestPioMode (
+ IN EFI_IDENTIFY_DATA * IdentifyData,
+ IN UINT16 *DisPioMode OPTIONAL,
+ OUT UINT16 *SelectedMode
+ );
+
+EFI_STATUS
+CalculateBestUdmaMode (
+ IN EFI_IDENTIFY_DATA * IdentifyData,
+ IN UINT16 *DisUDmaMode OPTIONAL,
+ OUT UINT16 *SelectedMode
+ );
+
+/**
+ Chipset SATA Driver EntryPoint function. It follows the standard EFI driver
+ model. It's called by StartImage() of DXE Core
+
+ @param[in] ImageHandle While the driver image loaded be the ImageLoader(),
+ an image handle is assigned to this driver binary,
+ all activities of the driver is tied to this ImageHandle
+ @param[in] SystemTable A pointer to the system table, for all BS(Boo Services) and
+ RT(Runtime Services)
+
+ @retval EFI_SUCCESS Function completes successfully
+**/
+EFI_STATUS
+EFIAPI
+InitializeSataControllerDriver (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ This function checks to see if the driver supports a device specified by
+ "Controller handle" parameter. It is called by DXE Core StartImage() or
+ ConnectController() routines. The driver uses 'device path' and/or
+ 'services' from the Bus I/O abstraction attached to the controller handle
+ to determine if the driver support this controller handle.
+ Note: In the BDS (Boot Device Selection) phase, the DXE core enumerate all
+ devices (or, controller) and assigns GUIDs to them.
+
+ @param[in] This A pointer points to the Binding Protocol instance
+ @param[in] Controller The handle of controller to be tested.
+ @param[in] RemainingDevicePath A pointer to the device path. Ignored by device
+ driver but used by bus driver
+
+ @retval EFI_SUCCESS The device is supported
+ @exception EFI_UNSUPPORTED The device is not supported
+**/
+EFI_STATUS
+EFIAPI
+SataControllerSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ UINT32 SataDeviceIdFound;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 PciData;
+
+ ///
+ /// SATA Controller is a device driver, and should ingore the
+ /// "RemainingDevicePath" according to EFI spec
+ ///
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID *) &ParentDevicePath,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ ///
+ /// EFI_ALREADY_STARTED is also an error
+ ///
+ return Status;
+ }
+ ///
+ /// Close the protocol because we don't use it here
+ ///
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ ///
+ /// Now test the EfiPciIoProtocol
+ ///
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ///
+ /// Now further check the PCI header: Base class (offset 0x0B) and
+ /// Sub Class (offset 0x0A). This controller should be an SATA controller
+ ///
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint8,
+ 0,
+ sizeof (PciData),
+ &PciData
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ return EFI_UNSUPPORTED;
+ }
+ ///
+ /// Since we already got the PciData, we can close protocol to avoid to carry it on for multiple exit points.
+ ///
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ ///
+ /// Examine SATA PCI Configuration table fields
+ ///
+ SataDeviceIdFound = FALSE;
+ ///
+ /// When found is storage device and provided by Intel then detect for right device Ids
+ ///
+ if (PciData.Hdr.VendorId == V_PCH_SATA_VENDOR_ID) {
+ if ((PciData.Hdr.ClassCode[2] == PCI_CLASS_MASS_STORAGE)) {
+ if ((PciData.Hdr.ClassCode[1] == V_PCH_SATA_SUB_CLASS_CODE_IDE)) {
+ if (IS_PCH_LPT_SATA_IDE_DEVICE_ID (PciData.Hdr.DeviceId)) {
+ SataDeviceIdFound = TRUE;
+ }
+ }
+
+ if (PciData.Hdr.ClassCode[1] == V_PCH_SATA_SUB_CLASS_CODE_AHCI) {
+ if (IS_PCH_LPT_SATA_AHCI_DEVICE_ID (PciData.Hdr.DeviceId)) {
+ SataDeviceIdFound = TRUE;
+ }
+ }
+
+ if (PciData.Hdr.ClassCode[1] == V_PCH_SATA_SUB_CLASS_CODE_RAID) {
+ if (IS_PCH_LPT_SATA_RAID_DEVICE_ID (PciData.Hdr.DeviceId)) {
+ SataDeviceIdFound = TRUE;
+ }
+ }
+ }
+ }
+
+ if (!SataDeviceIdFound) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return Status;
+}
+
+/**
+ This routine is called right after the .Supported() is called and returns
+ EFI_SUCCESS. Notes: The supported protocols are checked but the Protocols
+ are closed.
+
+ @param[in] This A pointer points to the Binding Protocol instance
+ @param[in] Controller The handle of controller to be tested. Parameter
+ passed by the caller
+ @param[in] RemainingDevicePath A pointer to the device path. Should be ignored by
+ device driver
+
+ @retval EFI_SUCCESS The device is started
+ @retval Other values Something error happened
+**/
+EFI_STATUS
+EFIAPI
+SataControllerStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ EFI_SATA_CONTROLLER_PRIVATE_DATA *SataPrivateData;
+ PCI_TYPE00 PciData;
+ UINTN SegNum;
+ UINTN BusNum;
+ UINTN DevNum;
+ UINTN FuncNum;
+ UINT64 CommandVal;
+
+ DEBUG ((EFI_D_INFO, "SataControllerStart() Start\n"));
+
+ SataPrivateData = NULL;
+ ///
+ /// Now test and open the EfiPciIoProtocol
+ ///
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &PciIo,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ ///
+ /// Status == 0 - A normal execution flow, SUCCESS and the program proceeds.
+ /// Status == ALREADY_STARTED - A non-zero Status code returned. It indicates
+ /// that the protocol has been opened and should be treated as a
+ /// normal condition and the program proceeds. The Protocol will not
+ /// opened 'again' by this call.
+ /// Status != ALREADY_STARTED - Error status, terminate program execution
+ ///
+ if (EFI_ERROR (Status)) {
+ ///
+ /// EFI_ALREADY_STARTED is also an error
+ ///
+ return Status;
+ }
+ ///
+ /// Allocate SATA private data structure
+ ///
+ SataPrivateData = AllocatePool (sizeof (EFI_SATA_CONTROLLER_PRIVATE_DATA));
+ if (SataPrivateData == NULL) {
+ DEBUG ((EFI_D_ERROR, "SATA Controller START ERROR: Allocating pool for IdePrivateData failed!\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+ ///
+ /// Initialize SATA private data
+ ///
+ ZeroMem (SataPrivateData, sizeof (EFI_SATA_CONTROLLER_PRIVATE_DATA));
+ SataPrivateData->Signature = SATA_CONTROLLER_SIGNATURE;
+ SataPrivateData->PciIo = PciIo;
+ SataPrivateData->IdeInit.GetChannelInfo = IdeInitGetChannelInfo;
+ SataPrivateData->IdeInit.NotifyPhase = IdeInitNotifyPhase;
+ SataPrivateData->IdeInit.SubmitData = IdeInitSubmitData;
+ SataPrivateData->IdeInit.DisqualifyMode = IdeInitDisqualifyMode;
+ SataPrivateData->IdeInit.CalculateMode = IdeInitCalculateMode;
+ SataPrivateData->IdeInit.SetTiming = IdeInitSetTiming;
+ SataPrivateData->IdeInit.EnumAll = PCH_SATA_ENUMER_ALL;
+
+ Status = PciIo->GetLocation (
+ PciIo,
+ &SegNum,
+ &BusNum,
+ &DevNum,
+ &FuncNum
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint8,
+ 0,
+ sizeof (PciData),
+ &PciData
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ ///
+ /// Get device capabilities
+ ///
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationSupported,
+ 0,
+ &CommandVal
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ ///
+ /// Enable Command Register
+ ///
+ Status = PciIo->Attributes (
+ PciIo,
+ EfiPciIoAttributeOperationEnable,
+ CommandVal & EFI_PCI_DEVICE_ENABLE,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ if (PciData.Hdr.ClassCode[1] == V_PCH_SATA_SUB_CLASS_CODE_IDE) {
+ SataPrivateData->IdeInit.ChannelCount = PCH_IDE_1_MAX_CHANNELS;
+ } else if (PciData.Hdr.ClassCode[1] == V_PCH_SATA_SUB_CLASS_CODE_AHCI ||
+ PciData.Hdr.ClassCode[1] == V_PCH_SATA_SUB_CLASS_CODE_RAID) {
+ ///
+ /// Default MAX port number
+ ///
+ SataPrivateData->IdeInit.ChannelCount = GetPchMaxSataPortNum ();
+ }
+ ///
+ /// Install IDE_CONTROLLER_INIT protocol & private data to this instance
+ ///
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gSataControllerDriverGuid,
+ SataPrivateData,
+ &gEfiIdeControllerInitProtocolGuid,
+ &(SataPrivateData->IdeInit),
+ NULL
+ );
+
+Done:
+
+ if (EFI_ERROR (Status)) {
+
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+ if (SataPrivateData != NULL) {
+ FreePool (SataPrivateData);
+ }
+ }
+
+ DEBUG ((EFI_D_INFO, "SataControllerStart() End\n"));
+
+ return Status;
+}
+
+/**
+ Stop managing the target device
+
+ @param[in] This A pointer pointing to the Binding Protocol instance
+ @param[in] Controller The handle of controller to be stopped
+ @param[in] NumberOfChildren Number of child devices
+ @param[in] ChildHandleBuffer Buffer holding child device handles
+
+ @retval EFI_SUCCESS The target device is stopped
+**/
+EFI_STATUS
+EFIAPI
+SataControllerStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_SATA_CONTROLLER_PRIVATE_DATA *SataPrivateData;
+
+ DEBUG ((EFI_D_INFO, "SataControllerStop() Start\n"));
+
+ ///
+ /// Get private data
+ ///
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gSataControllerDriverGuid,
+ (VOID **) &SataPrivateData,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Controller,
+ &gSataControllerDriverGuid,
+ SataPrivateData,
+ &gEfiIdeControllerInitProtocolGuid,
+ &(SataPrivateData->IdeInit),
+ NULL
+ );
+ }
+ ///
+ /// Close protocols opened by SATA controller driver
+ ///
+ gBS->CloseProtocol (
+ Controller,
+ &gEfiPciIoProtocolGuid,
+ This->DriverBindingHandle,
+ Controller
+ );
+
+ if (SataPrivateData != NULL) {
+ FreePool (SataPrivateData);
+ }
+
+ DEBUG ((EFI_D_INFO, "SataControllerStop() End\n"));
+
+ return EFI_SUCCESS;
+}
+
+//
+// Interface functions of IDE_CONTROLLER_INIT protocol
+//
+
+/**
+ This function can be used to obtain information about a specified channel.
+ It's usually used by IDE Bus driver during enumeration process.
+
+ @param[in] This the EFI_IDE_CONTROLLER_INIT_PROTOCOL instance.
+ @param[in] Channel Channel number (0 based, either 0 or 1)
+ @param[out] Enabled TRUE if the channel is enabled. If the channel is disabled,
+ then it will no be enumerated.
+ @param[out] MaxDevices The Max number of IDE devices that the bus driver can expect
+ on this channel. For ATA/ATAPI, this number is either 1 or 2.
+
+ @retval EFI_SUCCESS Function completes successfully
+ @retval Other Values Something error happened
+ @retval EFI_INVALID_PARAMETER The Channel parameter is invalid
+**/
+EFI_STATUS
+EFIAPI
+IdeInitGetChannelInfo (
+ IN EFI_IDE_CONTROLLER_INIT_PROTOCOL *This,
+ IN UINT8 Channel,
+ OUT BOOLEAN *Enabled,
+ OUT UINT8 *MaxDevices
+ )
+{
+ ///
+ /// Channel number (0 based, either 0 or 1)
+ ///
+ EFI_SATA_CONTROLLER_PRIVATE_DATA *SataPrivateData;
+
+ SataPrivateData = SATA_CONTROLLER_PRIVATE_DATA_FROM_THIS (This);
+ ASSERT (SataPrivateData);
+
+ if (Channel < This->ChannelCount) {
+ *Enabled = TRUE;
+ *MaxDevices = PCH_IDE_1_MAX_DEVICES;
+ return EFI_SUCCESS;
+ } else {
+ *Enabled = FALSE;
+ return EFI_INVALID_PARAMETER;
+ }
+}
+
+/**
+ This function is called by IdeBus driver before executing certain actions.
+ This allows IDE Controller Init to prepare for each action.
+
+ @param[in] This the EFI_IDE_CONTROLLER_INIT_PROTOCOL instance.
+ @param[in] Phase phase indicator defined by IDE_CONTROLLER_INIT protocol
+ @param[in] Channel Channel number (0 based, either 0 or 1)
+
+ @retval EFI_SUCCESS Function completes successfully
+ @retval EFI_INVALID_PARAMETER Channel parameter is out of range
+ @exception EFI_UNSUPPORTED Phase is not supported
+**/
+EFI_STATUS
+EFIAPI
+IdeInitNotifyPhase (
+ IN EFI_IDE_CONTROLLER_INIT_PROTOCOL *This,
+ IN EFI_IDE_CONTROLLER_ENUM_PHASE Phase,
+ IN UINT8 Channel
+ )
+{
+ if (Channel >= This->ChannelCount) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (Phase) {
+
+ case EfiIdeBeforeChannelEnumeration:
+ case EfiIdeAfterChannelEnumeration:
+ case EfiIdeBeforeChannelReset:
+ case EfiIdeAfterChannelReset:
+ case EfiIdeBusBeforeDevicePresenceDetection:
+ case EfiIdeBusAfterDevicePresenceDetection:
+ case EfiIdeResetMode:
+ ///
+ /// Do nothing at present
+ ///
+ break;
+
+ default:
+ return EFI_UNSUPPORTED;
+ break;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called by IdeBus driver to submit EFI_IDENTIFY_DATA data structure
+ obtained from IDE deivce. This structure is used to set IDE timing
+
+ @param[in] This the EFI_IDE_CONTROLLER_INIT_PROTOCOL instance.
+ @param[in] Channel IDE channel number (0 based, either 0 or 1)
+ @param[in] Device IDE device number
+ @param[in] IdentifyData A pointer to EFI_IDENTIFY_DATA data structure
+
+ @retval EFI_SUCCESS Function completes successfully
+ @retval EFI_INVALID_PARAMETER Channel or Device parameter is out of range
+**/
+EFI_STATUS
+EFIAPI
+IdeInitSubmitData (
+ IN EFI_IDE_CONTROLLER_INIT_PROTOCOL *This,
+ IN UINT8 Channel,
+ IN UINT8 Device,
+ IN EFI_IDENTIFY_DATA *IdentifyData
+ )
+{
+ EFI_SATA_CONTROLLER_PRIVATE_DATA *SataPrivateData;
+ SataPrivateData = SATA_CONTROLLER_PRIVATE_DATA_FROM_THIS (This);
+ ASSERT (SataPrivateData);
+
+ if ((Channel >= This->ChannelCount) || (Device >= PCH_IDE_1_MAX_DEVICES)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ ///
+ /// Make a local copy of device's IdentifyData and mark the valid flag
+ ///
+ if (IdentifyData != NULL) {
+ CopyMem (
+ &(SataPrivateData->IdentifyData[Channel][Device]),
+ IdentifyData,
+ sizeof (EFI_IDENTIFY_DATA)
+ );
+
+ SataPrivateData->IdentifyValid[Channel][Device] = TRUE;
+ } else {
+ SataPrivateData->IdentifyValid[Channel][Device] = FALSE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called by IdeBus driver to disqualify unsupported operation
+ mode on specfic IDE device
+
+ @param[in] This The EFI_IDE_CONTROLLER_INIT_PROTOCOL instance.
+ @param[in] Channel IDE channel number (0 based, either 0 or 1)
+ @param[in] Device IDE device number
+ @param[in] BadModes Operation mode indicator
+
+ @retval EFI_SUCCESS Function completes successfully
+ @retval EFI_INVALID_PARAMETER Channel parameter or Devicde parameter is out of range,
+ or BadModes is NULL
+**/
+EFI_STATUS
+EFIAPI
+IdeInitDisqualifyMode (
+ IN EFI_IDE_CONTROLLER_INIT_PROTOCOL *This,
+ IN UINT8 Channel,
+ IN UINT8 Device,
+ IN EFI_ATA_COLLECTIVE_MODE *BadModes
+ )
+{
+ EFI_SATA_CONTROLLER_PRIVATE_DATA *SataPrivateData;
+ SataPrivateData = SATA_CONTROLLER_PRIVATE_DATA_FROM_THIS (This);
+ ASSERT (SataPrivateData);
+
+ if ((Channel >= This->ChannelCount) || (BadModes == NULL) || (Device >= PCH_IDE_1_MAX_DEVICES)) {
+ return EFI_INVALID_PARAMETER;
+ }
+ ///
+ /// Record the disqualified modes per channel per device. From ATA/ATAPI spec,
+ /// if a mode is not supported, the modes higher than it is also not
+ /// supported
+ ///
+ CopyMem (
+ &(SataPrivateData->DisqulifiedModes[Channel][Device]),
+ BadModes,
+ sizeof (EFI_ATA_COLLECTIVE_MODE)
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called by IdeBus driver to calculate the best operation mode
+ supported by specific IDE device
+
+ @param[in] This The EFI_IDE_CONTROLLER_INIT_PROTOCOL instance.
+ @param[in] Channel IDE channel number (0 based, either 0 or 1)
+ @param[in] Device IDE device number
+ @param[in, out] SupportedModes Modes collection supported by IDE device
+
+ @retval EFI_SUCCESS Function completes successfully
+ @retval EFI_INVALID_PARAMETER Channel parameter or Device parameter is out of range;
+ Or SupportedModes is NULL
+ @retval EFI_NOT_READY Identify data is not valid
+ @retval EFI_OUT_OF_RESOURCES SupportedModes is out of range
+**/
+EFI_STATUS
+EFIAPI
+IdeInitCalculateMode (
+ IN EFI_IDE_CONTROLLER_INIT_PROTOCOL *This,
+ IN UINT8 Channel,
+ IN UINT8 Device,
+ IN OUT EFI_ATA_COLLECTIVE_MODE **SupportedModes
+ )
+{
+ EFI_SATA_CONTROLLER_PRIVATE_DATA *SataPrivateData;
+ EFI_IDENTIFY_DATA *IdentifyData;
+ BOOLEAN IdentifyValid;
+ EFI_ATA_COLLECTIVE_MODE *DisqulifiedModes;
+ UINT16 SelectedMode;
+ EFI_STATUS Status;
+
+ SataPrivateData = SATA_CONTROLLER_PRIVATE_DATA_FROM_THIS (This);
+ ASSERT (SataPrivateData);
+
+ if ((Channel >= This->ChannelCount) || (SupportedModes == NULL) || (Device >= PCH_IDE_1_MAX_DEVICES)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IdentifyData = &(SataPrivateData->IdentifyData[Channel][Device]);
+ DisqulifiedModes = &(SataPrivateData->DisqulifiedModes[Channel][Device]);
+ IdentifyValid = SataPrivateData->IdentifyValid[Channel][Device];
+
+ ///
+ /// Make sure we've got the valid identify data of the device from SubmitData()
+ ///
+ if (!IdentifyValid) {
+ return EFI_NOT_READY;
+ }
+
+ *SupportedModes = AllocateZeroPool (sizeof (EFI_ATA_COLLECTIVE_MODE));
+ if (*SupportedModes == NULL) {
+ ASSERT (FALSE);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = CalculateBestPioMode (
+ IdentifyData,
+ (DisqulifiedModes->PioMode.Valid ? ((UINT16 *) &(DisqulifiedModes->PioMode.Mode)) : NULL),
+ &SelectedMode
+ );
+ if (!EFI_ERROR (Status)) {
+ (*SupportedModes)->PioMode.Valid = TRUE;
+ (*SupportedModes)->PioMode.Mode = SelectedMode;
+
+ } else {
+ (*SupportedModes)->PioMode.Valid = FALSE;
+ }
+
+ Status = CalculateBestUdmaMode (
+ IdentifyData,
+ (DisqulifiedModes->UdmaMode.Valid ? ((UINT16 *) &(DisqulifiedModes->UdmaMode.Mode)) : NULL),
+ &SelectedMode
+ );
+
+ if (!EFI_ERROR (Status)) {
+ (*SupportedModes)->UdmaMode.Valid = TRUE;
+ (*SupportedModes)->UdmaMode.Mode = SelectedMode;
+
+ } else {
+ (*SupportedModes)->UdmaMode.Valid = FALSE;
+ }
+ ///
+ /// The modes other than PIO and UDMA are not supported by SATA controller
+ ///
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is called by IdeBus driver to set appropriate timing on IDE
+ controller according supported operation mode
+
+ @param[in] This The EFI_IDE_CONTROLLER_INIT_PROTOCOL instance.
+ @param[in] Channel IDE channel number (0 based, either 0 or 1)
+ @param[in] Device IDE device number
+ @param[in] Modes Operation modes
+
+ @retval EFI_SUCCESS This function always returns EFI_SUCCESS
+**/
+EFI_STATUS
+EFIAPI
+IdeInitSetTiming (
+ IN EFI_IDE_CONTROLLER_INIT_PROTOCOL *This,
+ IN UINT8 Channel,
+ IN UINT8 Device,
+ IN EFI_ATA_COLLECTIVE_MODE *Modes
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is used to calculate the best PIO mode supported by
+ specific IDE device
+
+ @param[in] IdentifyData The identify data of specific IDE device
+ @param[in] DisPioMode Disqualified PIO modes collection
+ @param[out] SelectedMode Available PIO modes collection
+
+ @retval EFI_SUCCESS Function completes successfully
+ @exception EFI_UNSUPPORTED Some invalid condition
+**/
+EFI_STATUS
+CalculateBestPioMode (
+ IN EFI_IDENTIFY_DATA * IdentifyData,
+ IN UINT16 *DisPioMode OPTIONAL,
+ OUT UINT16 *SelectedMode
+ )
+{
+ UINT16 PioMode;
+ UINT16 AdvancedPioMode;
+ UINT16 Temp;
+ UINT16 Index;
+ UINT16 MinimumPioCycleTime;
+
+ Temp = 0xff;
+
+ PioMode = (UINT8) (IdentifyData->AtaData.pio_cycle_timing >> 8);
+
+ ///
+ /// see whether Identify Data word 64 - 70 are valid
+ ///
+ if ((IdentifyData->AtaData.field_validity & 0x02) == 0x02) {
+
+ AdvancedPioMode = IdentifyData->AtaData.advanced_pio_modes;
+
+ for (Index = 0; Index < 8; Index++) {
+ if ((AdvancedPioMode & 0x01) != 0) {
+ Temp = Index;
+ }
+
+ AdvancedPioMode >>= 1;
+ }
+ ///
+ /// if Temp is modified, meant the advanced_pio_modes is not zero;
+ /// if Temp is not modified, meant the no advanced PIO Mode is supported,
+ /// the best PIO Mode is the value in pio_cycle_timing.
+ ///
+ if (Temp != 0xff) {
+ AdvancedPioMode = (UINT16) (Temp + 3);
+ } else {
+ AdvancedPioMode = PioMode;
+ }
+ ///
+ /// Limit the PIO mode to at most PIO4.
+ ///
+ PioMode = (UINT16) (AdvancedPioMode < 4 ? AdvancedPioMode : 4);
+
+ MinimumPioCycleTime = IdentifyData->AtaData.min_pio_cycle_time_with_flow_control;
+
+ if (MinimumPioCycleTime <= 120) {
+ PioMode = (UINT16) (4 < PioMode ? 4 : PioMode);
+ } else if (MinimumPioCycleTime <= 180) {
+ PioMode = (UINT16) (3 < PioMode ? 3 : PioMode);
+ } else if (MinimumPioCycleTime <= 240) {
+ PioMode = (UINT16) (2 < PioMode ? 2 : PioMode);
+ } else {
+ PioMode = 0;
+ }
+ ///
+ /// Degrade the PIO mode if the mode has been disqualified
+ ///
+ if (DisPioMode != NULL) {
+
+ if (*DisPioMode < 2) {
+ return EFI_UNSUPPORTED;
+ ///
+ /// no mode below ATA_PIO_MODE_BELOW_2
+ ///
+ }
+
+ if (PioMode >= *DisPioMode) {
+ PioMode = (UINT16) (*DisPioMode - 1);
+ }
+ }
+
+ if (PioMode < 2) {
+ *SelectedMode = 1;
+ ///
+ /// ATA_PIO_MODE_BELOW_2;
+ ///
+ } else {
+ *SelectedMode = PioMode;
+ ///
+ /// ATA_PIO_MODE_2 to ATA_PIO_MODE_4;
+ ///
+ }
+
+ } else {
+ ///
+ /// Identify Data word 64 - 70 are not valid
+ /// Degrade the PIO mode if the mode has been disqualified
+ ///
+ if (DisPioMode != NULL) {
+
+ if (*DisPioMode < 2) {
+ return EFI_UNSUPPORTED;
+ ///
+ /// no mode below ATA_PIO_MODE_BELOW_2
+ ///
+ }
+
+ if (PioMode == *DisPioMode) {
+ PioMode--;
+ }
+ }
+
+ if (PioMode < 2) {
+ *SelectedMode = 1;
+ ///
+ /// ATA_PIO_MODE_BELOW_2;
+ ///
+ } else {
+ *SelectedMode = 2;
+ ///
+ /// ATA_PIO_MODE_2;
+ ///
+ }
+
+ }
+
+ DEBUG ((EFI_D_ERROR, "CalculateBestPioMode() End\n"));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function is used to calculate the best UDMA mode supported by
+ specific IDE device
+
+ @param[in] IdentifyData The identify data of specific IDE device
+ @param[in] DisUDmaMode Disqualified UDMA modes collection
+ @param[out] SelectedMode Available UMDA modes collection
+
+ @retval EFI_SUCCESS Function completes successfully
+ @exception EFI_UNSUPPORTED Some invalid condition
+**/
+EFI_STATUS
+CalculateBestUdmaMode (
+ IN EFI_IDENTIFY_DATA * IdentifyData,
+ IN UINT16 *DisUDmaMode OPTIONAL,
+ OUT UINT16 *SelectedMode
+ )
+{
+ UINT16 TempMode;
+ UINT16 DeviceUDmaMode;
+
+ DeviceUDmaMode = 0;
+ ///
+ /// flag for 'Udma mode is not supported'
+ ///
+ /// Check whether the WORD 88 (supported UltraDMA by drive) is valid
+ ///
+ if ((IdentifyData->AtaData.field_validity & 0x04) == 0x00) {
+ return EFI_UNSUPPORTED;
+ }
+
+ DeviceUDmaMode = IdentifyData->AtaData.ultra_dma_mode;
+ DeviceUDmaMode &= 0x3f;
+ TempMode = 0;
+ ///
+ /// initialize it to UDMA-0
+ ///
+ while ((DeviceUDmaMode >>= 1) != 0) {
+ TempMode++;
+ }
+ ///
+ /// Degrade the UDMA mode if the mode has been disqualified
+ ///
+ if (DisUDmaMode != NULL) {
+ if (*DisUDmaMode == 0) {
+ *SelectedMode = 0;
+ return EFI_UNSUPPORTED;
+ ///
+ /// no mode below ATA_UDMA_MODE_0
+ ///
+ }
+
+ if (TempMode >= *DisUDmaMode) {
+ TempMode = (UINT16) (*DisUDmaMode - 1);
+ }
+ }
+ ///
+ /// Possible returned mode is between ATA_UDMA_MODE_0 and ATA_UDMA_MODE_5
+ ///
+ *SelectedMode = TempMode;
+
+ return EFI_SUCCESS;
+}