-/** @file\r
- \r
+/** @file \r
The Ehci controller driver.\r
\r
-Copyright (c) 2006 - 2009, Intel Corporation\r
-All rights reserved. This program and the accompanying materials\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
+ 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 - 2014, Intel Corporation. All rights reserved.<BR>\r
+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
EhcDriverBindingSupported,\r
EhcDriverBindingStart,\r
EhcDriverBindingStop,\r
- 0x10,\r
+ 0x30,\r
NULL,\r
NULL\r
};\r
USB2_HC_DEV *Ehc;\r
EFI_TPL OldTpl;\r
EFI_STATUS Status;\r
+ UINT32 DbgCtrlStatus;\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 (Ehc->DebugPortNum != 0) {\r
+ DbgCtrlStatus = EhcReadDbgRegister(Ehc, 0);\r
+ if ((DbgCtrlStatus & (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) == (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) {\r
+ Status = EFI_SUCCESS;\r
+ goto ON_EXIT;\r
+ }\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
UINTN Index;\r
UINTN MapSize;\r
EFI_STATUS Status;\r
+ UINT32 DbgCtrlStatus;\r
\r
if (PortStatus == NULL) {\r
return EFI_INVALID_PARAMETER;\r
PortStatus->PortStatus = 0;\r
PortStatus->PortChangeStatus = 0;\r
\r
+ if ((Ehc->DebugPortNum != 0) && (PortNumber == (Ehc->DebugPortNum - 1))) {\r
+ DbgCtrlStatus = EhcReadDbgRegister(Ehc, 0);\r
+ if ((DbgCtrlStatus & (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) == (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) {\r
+ goto ON_EXIT;\r
+ }\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
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
//\r
// Test whether the controller belongs to Ehci type\r
//\r
- if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) ||\r
- (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB) ||\r
- (UsbClassCReg.PI != EHC_PCI_CLASSC_PI)) {\r
+ if ((UsbClassCReg.BaseCode != PCI_CLASS_SERIAL) || (UsbClassCReg.SubClassCode != PCI_CLASS_SERIAL_USB)\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
return Ehc;\r
}\r
\r
+/**\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 handler private data\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+EhcExitBootService (\r
+ EFI_EVENT Event,\r
+ VOID *Context\r
+ )\r
+\r
+{\r
+ USB2_HC_DEV *Ehc;\r
+\r
+ Ehc = (USB2_HC_DEV *) Context;\r
+\r
+ //\r
+ // Reset the Host Controller\r
+ //\r
+ EhcResetHC (Ehc, EHC_RESET_TIMEOUT);\r
+}\r
+\r
\r
/**\r
Starting the Usb EHCI Driver.\r
EFI_STATUS Status;\r
USB2_HC_DEV *Ehc;\r
EFI_PCI_IO_PROTOCOL *PciIo;\r
+ EFI_PCI_IO_PROTOCOL *Instance;\r
UINT64 Supports;\r
UINT64 OriginalPciAttributes;\r
BOOLEAN PciAttributesSaved;\r
+ USB_CLASSC UsbClassCReg;\r
+ EFI_HANDLE *HandleBuffer;\r
+ UINTN NumberOfHandles;\r
+ UINTN Index;\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
+ UINT32 State;\r
+ EFI_DEVICE_PATH_PROTOCOL *HcDevicePath;\r
\r
//\r
// Open the PciIo Protocol, then enable the USB host controller\r
);\r
\r
if (EFI_ERROR (Status)) {\r
- DEBUG ((EFI_D_ERROR, "EhcDriverBindingStart: failed to open PCI_IO\n"));\r
- return EFI_DEVICE_ERROR;\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
+ PCI_CLASSCODE_OFFSET,\r
+ sizeof (USB_CLASSC) / sizeof (UINT8),\r
+ &UsbClassCReg\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto CLOSE_PCIIO;\r
+ }\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
+ &CompanionSegmentNumber,\r
+ &CompanionBusNumber,\r
+ &CompanionDeviceNumber,\r
+ &CompanionFunctionNumber\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto CLOSE_PCIIO;\r
+ }\r
+\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiPciIoProtocolGuid,\r
+ NULL,\r
+ &NumberOfHandles,\r
+ &HandleBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto CLOSE_PCIIO;\r
+ }\r
+\r
+ for (Index = 0; Index < NumberOfHandles; Index++) {\r
+ //\r
+ // Get the device path on this handle\r
+ //\r
+ Status = gBS->HandleProtocol (\r
+ HandleBuffer[Index],\r
+ &gEfiPciIoProtocolGuid,\r
+ (VOID **)&Instance\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = Instance->Pci.Read (\r
+ Instance,\r
+ EfiPciIoWidthUint8,\r
+ PCI_CLASSCODE_OFFSET,\r
+ sizeof (USB_CLASSC) / sizeof (UINT8),\r
+ &UsbClassCReg\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_UNSUPPORTED;\r
+ goto CLOSE_PCIIO;\r
+ }\r
+\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
+ &EhciSegmentNumber,\r
+ &EhciBusNumber,\r
+ &EhciDeviceNumber,\r
+ &EhciFunctionNumber\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto CLOSE_PCIIO;\r
+ }\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
+ This->DriverBindingHandle,\r
+ Controller\r
+ );\r
+ EhcDriverBindingStart(This, HandleBuffer[Index], NULL);\r
+ }\r
+ }\r
+ }\r
+ Status = EFI_NOT_FOUND;\r
+ goto CLOSE_PCIIO;\r
+ }\r
+\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
}\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 (Ehc->DebugPortNum != 0) {\r
+ State = EhcReadDbgRegister(Ehc, 0);\r
+ if ((State & (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) != (USB_DEBUG_PORT_IN_USE | USB_DEBUG_PORT_OWNER)) {\r
+ EhcResetHC (Ehc, EHC_RESET_TIMEOUT);\r
+ }\r
+ }\r
\r
Status = EhcInitHC (Ehc);\r
\r
goto UNINSTALL_USBHC;\r
}\r
\r
+ //\r
+ // Create event to stop the HC when exit boot service.\r
+ //\r
+ Status = gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ EhcExitBootService,\r
+ Ehc,\r
+ &gEfiEventExitBootServicesGuid,\r
+ &Ehc->ExitBootServiceEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto UNINSTALL_USBHC;\r
+ }\r
+\r
//\r
// Install the component name protocol, don't fail the start\r
// because of something for display.\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
\r
+ if (Ehc->ExitBootServiceEvent != NULL) {\r
+ gBS->CloseEvent (Ehc->ExitBootServiceEvent);\r
+ }\r
+\r
EhcFreeSched (Ehc);\r
\r
if (Ehc->ControllerNameTable != NULL) {\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