/** @file\r
- \r
The Ehci controller driver.\r
\r
-Copyright (c) 2006 - 2009, Intel Corporation\r
-All rights reserved. This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution. The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
+ EhciDxe driver is responsible for managing the behavior of EHCI controller.\r
+ It implements the interfaces of monitoring the status of all ports and transferring\r
+ Control, Bulk, Interrupt and Isochronous requests to Usb2.0 device.\r
\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+ Note that EhciDxe driver is enhanced to guarantee that the EHCI controller get attached\r
+ to the EHCI controller before a UHCI or OHCI driver attaches to the companion UHCI or\r
+ OHCI controller. This way avoids the control transfer on a shared port between EHCI\r
+ and companion host controller when UHCI or OHCI gets attached earlier than EHCI and a\r
+ USB 2.0 device inserts.\r
+\r
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
NULL\r
};\r
\r
-///\r
-/// USB host controller Programming Interface.\r
-///\r
-#define PCI_CLASSC_PI_UHCI 0x00\r
-#define PCI_CLASSC_PI_EHCI 0x20\r
-\r
/**\r
Retrieves the capability of root hub ports.\r
\r
\r
*MaxSpeed = EFI_USB_SPEED_HIGH;\r
*PortNumber = (UINT8) (Ehc->HcStructParams & HCSP_NPORTS);\r
- *Is64BitCapable = (UINT8) (Ehc->HcCapParams & HCCP_64BIT);\r
+ *Is64BitCapable = (UINT8) Ehc->Support64BitDma;\r
\r
DEBUG ((EFI_D_INFO, "EhcGetCapability: %d ports, 64 bit %d\n", *PortNumber, *Is64BitCapable));\r
\r
EFI_TPL OldTpl;\r
EFI_STATUS Status;\r
\r
+ Ehc = EHC_FROM_THIS (This);\r
+\r
+ if (Ehc->DevicePath != NULL) {\r
+ //\r
+ // Report Status Code to indicate reset happens\r
+ //\r
+ REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
+ EFI_PROGRESS_CODE,\r
+ (EFI_IO_BUS_USB | EFI_IOB_PC_RESET),\r
+ Ehc->DevicePath\r
+ );\r
+ }\r
+\r
OldTpl = gBS->RaiseTPL (EHC_TPL);\r
- Ehc = EHC_FROM_THIS (This);\r
\r
switch (Attributes) {\r
case EFI_USB_HC_RESET_GLOBAL:\r
//\r
// Host Controller must be Halt when Reset it\r
//\r
+ if (EhcIsDebugPortInUse (Ehc, NULL)) {\r
+ Status = EFI_SUCCESS;\r
+ goto ON_EXIT;\r
+ }\r
+\r
if (!EhcIsHalt (Ehc)) {\r
Status = EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);\r
\r
EFI_STATUS\r
EFIAPI\r
EhcGetState (\r
- IN CONST EFI_USB2_HC_PROTOCOL *This,\r
- OUT EFI_USB_HC_STATE *State\r
+ IN EFI_USB2_HC_PROTOCOL *This,\r
+ OUT EFI_USB_HC_STATE *State\r
)\r
{\r
EFI_TPL OldTpl;\r
EFI_STATUS\r
EFIAPI\r
EhcGetRootHubPortStatus (\r
- IN CONST EFI_USB2_HC_PROTOCOL *This,\r
- IN CONST UINT8 PortNumber,\r
- OUT EFI_USB_PORT_STATUS *PortStatus\r
+ IN EFI_USB2_HC_PROTOCOL *This,\r
+ IN UINT8 PortNumber,\r
+ OUT EFI_USB_PORT_STATUS *PortStatus\r
)\r
{\r
USB2_HC_DEV *Ehc;\r
PortStatus->PortStatus = 0;\r
PortStatus->PortChangeStatus = 0;\r
\r
+ if (EhcIsDebugPortInUse (Ehc, &PortNumber)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
State = EhcReadOpReg (Ehc, Offset);\r
\r
//\r
\r
case EfiUsbPortPower:\r
//\r
- // Not supported, ignore the operation\r
+ // Set port power bit when PPC is 1\r
//\r
- Status = EFI_SUCCESS;\r
+ if ((Ehc->HcCapParams & HCSP_PPC) == HCSP_PPC) {\r
+ State |= PORTSC_POWER;\r
+ EhcWriteOpReg (Ehc, Offset, State);\r
+ }\r
break;\r
\r
case EfiUsbPortOwner:\r
break;\r
\r
case EfiUsbPortPower:\r
+ //\r
+ // Clear port power bit when PPC is 1\r
+ //\r
+ if ((Ehc->HcCapParams & HCSP_PPC) == HCSP_PPC) {\r
+ State &= ~PORTSC_POWER;\r
+ EhcWriteOpReg (Ehc, Offset, State);\r
+ }\r
+ break;\r
case EfiUsbPortSuspendChange:\r
case EfiUsbPortResetChange:\r
//\r
URB *Urb;\r
EFI_TPL OldTpl;\r
EFI_STATUS Status;\r
- UINT8 *Data;\r
\r
//\r
// Validate parameters\r
\r
EhcAckAllInterrupt (Ehc);\r
\r
- Data = AllocatePool (DataLength);\r
-\r
- if (Data == NULL) {\r
- DEBUG ((EFI_D_ERROR, "EhcAsyncInterruptTransfer: failed to allocate buffer\n"));\r
-\r
- Status = EFI_OUT_OF_RESOURCES;\r
- goto ON_EXIT;\r
- }\r
-\r
- Urb = EhcCreateUrb (\r
+ Urb = EhciInsertAsyncIntTransfer (\r
Ehc,\r
DeviceAddress,\r
EndPointAddress,\r
*DataToggle,\r
MaximumPacketLength,\r
Translator,\r
- EHC_INT_TRANSFER_ASYNC,\r
- NULL,\r
- Data,\r
DataLength,\r
CallBackFunction,\r
Context,\r
);\r
\r
if (Urb == NULL) {\r
- DEBUG ((EFI_D_ERROR, "EhcAsyncInterruptTransfer: failed to create URB\n"));\r
-\r
- gBS->FreePool (Data);\r
Status = EFI_OUT_OF_RESOURCES;\r
goto ON_EXIT;\r
}\r
\r
- //\r
- // New asynchronous transfer must inserted to the head.\r
- // Check the comments in EhcMoniteAsyncRequests\r
- //\r
- EhcLinkQhToPeriod (Ehc, Urb->Qh);\r
- InsertHeadList (&Ehc->AsyncIntTransfers, &Urb->UrbList);\r
-\r
ON_EXIT:\r
Ehc->PciIo->Flush (Ehc->PciIo);\r
gBS->RestoreTPL (OldTpl);\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
- if (!EHCI_IS_DATAIN (EndPointAddress)) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
if ((*DataToggle != 1) && (*DataToggle != 0)) {\r
return EFI_INVALID_PARAMETER;\r
}\r
Status = EFI_SUCCESS;\r
}\r
\r
+ EhcFreeUrb (Ehc, Urb);\r
ON_EXIT:\r
Ehc->PciIo->Flush (Ehc->PciIo);\r
gBS->RestoreTPL (OldTpl);\r
Status = PciIo->Pci.Read (\r
PciIo,\r
EfiPciIoWidthUint8,\r
- EHC_PCI_CLASSC,\r
+ PCI_CLASSCODE_OFFSET,\r
sizeof (USB_CLASSC) / sizeof (UINT8),\r
&UsbClassCReg\r
);\r
// Test whether the controller belongs to Ehci type\r
//\r
if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB)\r
- || ((UsbClassCReg.PI != EHC_PCI_CLASSC_PI) && (UsbClassCReg.PI !=PCI_CLASSC_PI_UHCI))) {\r
+ || ((UsbClassCReg.ProgInterface != PCI_IF_EHCI) && (UsbClassCReg.ProgInterface != PCI_IF_UHCI) && (UsbClassCReg.ProgInterface != PCI_IF_OHCI))) {\r
\r
Status = EFI_UNSUPPORTED;\r
}\r
return Status;\r
}\r
\r
+/**\r
+ Get the usb debug port related information.\r
+\r
+ @param Ehc The EHCI device.\r
+\r
+ @retval RETURN_SUCCESS Get debug port number, bar and offset successfully.\r
+ @retval Others The usb host controller does not supported usb debug port capability.\r
+\r
+**/\r
+EFI_STATUS\r
+EhcGetUsbDebugPortInfo (\r
+ IN USB2_HC_DEV *Ehc\r
+ )\r
+{\r
+ EFI_PCI_IO_PROTOCOL *PciIo;\r
+ UINT16 PciStatus;\r
+ UINT8 CapabilityPtr;\r
+ UINT8 CapabilityId;\r
+ UINT16 DebugPort;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (Ehc->PciIo != NULL);\r
+ PciIo = Ehc->PciIo;\r
+\r
+ //\r
+ // Detect if the EHCI host controller support Capaility Pointer.\r
+ //\r
+ Status = PciIo->Pci.Read (\r
+ PciIo,\r
+ EfiPciIoWidthUint8,\r
+ PCI_PRIMARY_STATUS_OFFSET,\r
+ sizeof (UINT16),\r
+ &PciStatus\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if ((PciStatus & EFI_PCI_STATUS_CAPABILITY) == 0) {\r
+ //\r
+ // The Pci Device Doesn't Support Capability Pointer.\r
+ //\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Get Pointer To Capability List\r
+ //\r
+ Status = PciIo->Pci.Read (\r
+ PciIo,\r
+ EfiPciIoWidthUint8,\r
+ PCI_CAPBILITY_POINTER_OFFSET,\r
+ 1,\r
+ &CapabilityPtr\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Find Capability ID 0xA, Which Is For Debug Port\r
+ //\r
+ while (CapabilityPtr != 0) {\r
+ Status = PciIo->Pci.Read (\r
+ PciIo,\r
+ EfiPciIoWidthUint8,\r
+ CapabilityPtr,\r
+ 1,\r
+ &CapabilityId\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (CapabilityId == EHC_DEBUG_PORT_CAP_ID) {\r
+ break;\r
+ }\r
+\r
+ Status = PciIo->Pci.Read (\r
+ PciIo,\r
+ EfiPciIoWidthUint8,\r
+ CapabilityPtr + 1,\r
+ 1,\r
+ &CapabilityPtr\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // No Debug Port Capability Found\r
+ //\r
+ if (CapabilityPtr == 0) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Get The Base Address Of Debug Port Register In Debug Port Capability Register\r
+ //\r
+ Status = PciIo->Pci.Read (\r
+ Ehc->PciIo,\r
+ EfiPciIoWidthUint8,\r
+ CapabilityPtr + 2,\r
+ sizeof (UINT16),\r
+ &DebugPort\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Ehc->DebugPortOffset = DebugPort & 0x1FFF;\r
+ Ehc->DebugPortBarNum = (UINT8)((DebugPort >> 13) - 1);\r
+ Ehc->DebugPortNum = (UINT8)((Ehc->HcStructParams & 0x00F00000) >> 20);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
\r
/**\r
Create and initialize a USB2_HC_DEV.\r
\r
@param PciIo The PciIo on this device.\r
+ @param DevicePath The device path of host controller.\r
@param OriginalPciAttributes Original PCI attributes.\r
\r
@return The allocated and initialized USB2_HC_DEV structure if created,\r
**/\r
USB2_HC_DEV *\r
EhcCreateUsb2Hc (\r
- IN EFI_PCI_IO_PROTOCOL *PciIo,\r
- IN UINT64 OriginalPciAttributes\r
+ IN EFI_PCI_IO_PROTOCOL *PciIo,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ IN UINT64 OriginalPciAttributes\r
)\r
{\r
USB2_HC_DEV *Ehc;\r
Ehc->Usb2Hc.MinorRevision = 0x0;\r
\r
Ehc->PciIo = PciIo;\r
+ Ehc->DevicePath = DevicePath;\r
Ehc->OriginalPciAttributes = OriginalPciAttributes;\r
\r
InitializeListHead (&Ehc->AsyncIntTransfers);\r
\r
DEBUG ((EFI_D_INFO, "EhcCreateUsb2Hc: capability length %d\n", Ehc->CapLen));\r
\r
+ //\r
+ // EHCI Controllers with a CapLen of 0 are ignored.\r
+ //\r
+ if (Ehc->CapLen == 0) {\r
+ gBS->FreePool (Ehc);\r
+ return NULL;\r
+ }\r
+\r
+ EhcGetUsbDebugPortInfo (Ehc);\r
+\r
//\r
// Create AsyncRequest Polling Timer\r
//\r
Status = gBS->CreateEvent (\r
EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
- TPL_CALLBACK,\r
+ TPL_NOTIFY,\r
EhcMonitorAsyncRequests,\r
Ehc,\r
&Ehc->PollTimer\r
One notified function to stop the Host Controller when gBS->ExitBootServices() called.\r
\r
@param Event Pointer to this event\r
- @param Context Event hanlder private data\r
+ @param Context Event handler private data\r
\r
**/\r
VOID\r
Ehc = (USB2_HC_DEV *) Context;\r
\r
//\r
- // Stop the Host Controller\r
+ // Reset the Host Controller\r
//\r
- EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);\r
-\r
- return;\r
+ EhcResetHC (Ehc, EHC_RESET_TIMEOUT);\r
}\r
\r
\r
EFI_HANDLE *HandleBuffer;\r
UINTN NumberOfHandles;\r
UINTN Index;\r
- UINTN UhciSegmentNumber;\r
- UINTN UhciBusNumber;\r
- UINTN UhciDeviceNumber;\r
- UINTN UhciFunctionNumber;\r
+ UINTN CompanionSegmentNumber;\r
+ UINTN CompanionBusNumber;\r
+ UINTN CompanionDeviceNumber;\r
+ UINTN CompanionFunctionNumber;\r
UINTN EhciSegmentNumber;\r
UINTN EhciBusNumber;\r
UINTN EhciDeviceNumber;\r
UINTN EhciFunctionNumber;\r
+ EFI_DEVICE_PATH_PROTOCOL *HcDevicePath;\r
\r
//\r
// Open the PciIo Protocol, then enable the USB host controller\r
return Status;\r
}\r
\r
+ //\r
+ // Open Device Path Protocol for on USB host controller\r
+ //\r
+ HcDevicePath = NULL;\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **) &HcDevicePath,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
PciAttributesSaved = FALSE;\r
//\r
// Save original PCI attributes\r
&Supports\r
);\r
if (!EFI_ERROR (Status)) {\r
- Supports &= EFI_PCI_DEVICE_ENABLE;\r
+ Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;\r
Status = PciIo->Attributes (\r
PciIo,\r
EfiPciIoAttributeOperationEnable,\r
goto CLOSE_PCIIO;\r
}\r
\r
+ //\r
+ // Get the Pci device class code.\r
+ //\r
Status = PciIo->Pci.Read (\r
PciIo,\r
EfiPciIoWidthUint8,\r
- EHC_PCI_CLASSC,\r
+ PCI_CLASSCODE_OFFSET,\r
sizeof (USB_CLASSC) / sizeof (UINT8),\r
&UsbClassCReg\r
);\r
Status = EFI_UNSUPPORTED;\r
goto CLOSE_PCIIO;\r
}\r
-\r
- if ((UsbClassCReg.PI == PCI_CLASSC_PI_UHCI) &&\r
- (UsbClassCReg.BaseCode == PCI_CLASS_SERIAL) && \r
+ //\r
+ // Determine if the device is UHCI or OHCI host controller or not. If yes, then find out the\r
+ // companion usb ehci host controller and force EHCI driver get attached to it before\r
+ // UHCI or OHCI driver attaches to UHCI or OHCI host controller.\r
+ //\r
+ if ((UsbClassCReg.ProgInterface == PCI_IF_UHCI || UsbClassCReg.ProgInterface == PCI_IF_OHCI) &&\r
+ (UsbClassCReg.BaseCode == PCI_CLASS_SERIAL) &&\r
(UsbClassCReg.SubClassCode == PCI_CLASS_SERIAL_USB)) {\r
Status = PciIo->GetLocation (\r
PciIo,\r
- &UhciSegmentNumber,\r
- &UhciBusNumber,\r
- &UhciDeviceNumber,\r
- &UhciFunctionNumber\r
+ &CompanionSegmentNumber,\r
+ &CompanionBusNumber,\r
+ &CompanionDeviceNumber,\r
+ &CompanionFunctionNumber\r
);\r
if (EFI_ERROR (Status)) {\r
goto CLOSE_PCIIO;\r
Status = Instance->Pci.Read (\r
Instance,\r
EfiPciIoWidthUint8,\r
- EHC_PCI_CLASSC,\r
+ PCI_CLASSCODE_OFFSET,\r
sizeof (USB_CLASSC) / sizeof (UINT8),\r
&UsbClassCReg\r
);\r
goto CLOSE_PCIIO;\r
}\r
\r
- if ((UsbClassCReg.PI == PCI_CLASSC_PI_EHCI) &&\r
- (UsbClassCReg.BaseCode == PCI_CLASS_SERIAL) && \r
+ if ((UsbClassCReg.ProgInterface == PCI_IF_EHCI) &&\r
+ (UsbClassCReg.BaseCode == PCI_CLASS_SERIAL) &&\r
(UsbClassCReg.SubClassCode == PCI_CLASS_SERIAL_USB)) {\r
Status = Instance->GetLocation (\r
Instance,\r
if (EFI_ERROR (Status)) {\r
goto CLOSE_PCIIO;\r
}\r
- if (EhciBusNumber == UhciBusNumber) {\r
+ //\r
+ // Currently, the judgment on the companion usb host controller is through the\r
+ // same bus number, which may vary on different platform.\r
+ //\r
+ if (EhciBusNumber == CompanionBusNumber) {\r
gBS->CloseProtocol (\r
Controller,\r
&gEfiPciIoProtocolGuid,\r
//\r
// Create then install USB2_HC_PROTOCOL\r
//\r
- Ehc = EhcCreateUsb2Hc (PciIo, OriginalPciAttributes);\r
+ Ehc = EhcCreateUsb2Hc (PciIo, HcDevicePath, OriginalPciAttributes);\r
\r
if (Ehc == NULL) {\r
DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to create USB2_HC\n"));\r
goto CLOSE_PCIIO;\r
}\r
\r
+ //\r
+ // Enable 64-bit DMA support in the PCI layer if this controller\r
+ // supports it.\r
+ //\r
+ if (EHC_BIT_IS_SET (Ehc->HcCapParams, HCCP_64BIT)) {\r
+ Status = PciIo->Attributes (\r
+ PciIo,\r
+ EfiPciIoAttributeOperationEnable,\r
+ EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,\r
+ NULL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Ehc->Support64BitDma = TRUE;\r
+ } else {\r
+ DEBUG ((EFI_D_WARN,\r
+ "%a: failed to enable 64-bit DMA on 64-bit capable controller @ %p (%r)\n",\r
+ __FUNCTION__, Controller, Status));\r
+ }\r
+ }\r
+\r
Status = gBS->InstallProtocolInterface (\r
&Controller,\r
&gEfiUsb2HcProtocolGuid,\r
}\r
\r
//\r
- // Robustnesss improvement such as for UoL\r
+ // Robustnesss improvement such as for Duet platform\r
// Default is not required.\r
//\r
if (FeaturePcdGet (PcdTurnOffUsbLegacySupport)) {\r
EhcClearLegacySupport (Ehc);\r
}\r
- EhcResetHC (Ehc, EHC_RESET_TIMEOUT);\r
+\r
+ if (!EhcIsDebugPortInUse (Ehc, NULL)) {\r
+ EhcResetHC (Ehc, EHC_RESET_TIMEOUT);\r
+ }\r
\r
Status = EhcInitHC (Ehc);\r
\r
\r
\r
/**\r
- Stop this driver on ControllerHandle. Support stoping any child handles\r
+ Stop this driver on ControllerHandle. Support stopping any child handles\r
created by this driver.\r
\r
@param This Protocol instance pointer.\r
Ehc = EHC_FROM_THIS (Usb2Hc);\r
PciIo = Ehc->PciIo;\r
\r
- //\r
- // Stop AsyncRequest Polling timer then stop the EHCI driver\r
- // and uninstall the EHCI protocl.\r
- //\r
- gBS->SetTimer (Ehc->PollTimer, TimerCancel, EHC_ASYNC_POLL_INTERVAL);\r
- EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);\r
-\r
Status = gBS->UninstallProtocolInterface (\r
Controller,\r
&gEfiUsb2HcProtocolGuid,\r
return Status;\r
}\r
\r
+ //\r
+ // Stop AsyncRequest Polling timer then stop the EHCI driver\r
+ // and uninstall the EHCI protocl.\r
+ //\r
+ gBS->SetTimer (Ehc->PollTimer, TimerCancel, EHC_ASYNC_POLL_INTERVAL);\r
+ EhcHaltHC (Ehc, EHC_GENERIC_TIMEOUT);\r
+\r
if (Ehc->PollTimer != NULL) {\r
gBS->CloseEvent (Ehc->PollTimer);\r
}\r
FreeUnicodeStringTable (Ehc->ControllerNameTable);\r
}\r
\r
+ //\r
+ // Disable routing of all ports to EHCI controller, so all ports are\r
+ // routed back to the UHCI or OHCI controller.\r
+ //\r
+ EhcClearOpRegBit (Ehc, EHC_CONFIG_FLAG_OFFSET, CONFIGFLAG_ROUTE_EHC);\r
+\r
//\r
// Restore original PCI attributes\r
//\r