diff options
Diffstat (limited to 'Core/EM/usb/rt/ehci.c')
-rw-r--r-- | Core/EM/usb/rt/ehci.c | 5916 |
1 files changed, 5916 insertions, 0 deletions
diff --git a/Core/EM/usb/rt/ehci.c b/Core/EM/usb/rt/ehci.c new file mode 100644 index 0000000..faeb5fa --- /dev/null +++ b/Core/EM/usb/rt/ehci.c @@ -0,0 +1,5916 @@ +//**************************************************************************** +//**************************************************************************** +//** ** +//** (C)Copyright 1985-2016, American Megatrends, Inc. ** +//** ** +//** All Rights Reserved. ** +//** ** +//** 5555 Oakbrook Pkwy, Norcross, GA 30093 ** +//** ** +//** Phone (770)-246-8600 ** +//** ** +//**************************************************************************** +//**************************************************************************** + +//**************************************************************************** +// $Header: /Alaska/SOURCE/Modules/USB/ALASKA/RT/ehci.c 152 10/16/16 10:12p Wilsonlee $ +// +// $Revision: 152 $ +// +// $Date: 10/16/16 10:12p $ +//**************************************************************************** +//**************************************************************************** +// Revision History +// ---------------- +// $Log: /Alaska/SOURCE/Modules/USB/ALASKA/RT/ehci.c $ +// +// 152 10/16/16 10:12p Wilsonlee +// [TAG] EIP288158 +// [Category] Improvement +// [Description] Check if gUsbData is integrity. +// [Files] amiusb.cif, usbsb.c, AmiUsbLib.cif, AmiUsbLib.sdl, +// AmiUsbSmmGlobalDataValidationLib.c, +// AmiUsbSmmGlobalDataValidationLib.cif, +// AmiUsbSmmGlobalDataValidationLib.mak, Crc32.c, amiusb.c, amiusb.h, +// ehci.c, elib.c, ohci.c, syskbc.c, uhci.c, usb.c, usbCCID.c, usbdef.h, +// usbhid.c, usbhub.c, usbkbd.c, usbmass.c, usbms.c, usbrt.mak, xhci.c, +// amiusbhc.c, efiusbccid.c, efiusbmass.c, uhcd.c, usbmisc.c, +// AmiUsbController.h, AmiUsbLibInclude.cif, +// AmiUsbSmmGlobalDataValidationLib.h +// +// 151 7/22/16 3:50a Wilsonlee +// [TAG] EIP277810 +// [Category] Improvement +// [Description] Validate the memory buffer is entirely outside of SMM. +// [Files] usbsb.c, amiusb.c, ehci.c, ohci.c, uhci.c, usbCCID.c, +// usbdef.h, usbmass.c, xhci.c, amiusbhc.c, efiusbccid.c, efiusbmass.c, +// uhcd.c, uhcd.h, usbmisc.c +// +// 150 7/07/16 2:08a Wilsonlee +// [TAG] EIP268836 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] The system will loop in USB runtime module during IPMI are +// resetting USB controller. +// [RootCause] Qh->dLinkPointer is invalid after device is disconnected. +// [Solution] Check if Qh is changed after we process it. +// [Files] ehci.c +// +// 149 7/07/16 1:09a Wilsonlee +// [TAG] EIP277810 +// [Category] Improvement +// [Description] Validate the memory buffer is entirely outside of SMM. +// [Files] usbsb.c, amiusb.c, ehci.c, ohci.c, uhci.c, usbCCID.c, +// usbdef.h, usbmass.c, xhci.c, amiusbhc.c, efiusbccid.c, efiusbmass.c, +// uhcd.c, uhcd.h, usbmisc.c +// +// 148 3/14/16 10:21p Wilsonlee +// [TAG] EIP257506 +// [Category] Improvement +// [Description] Add USB_KEYREPEAT_INTERVAL token to change the key +// repeat interval. +// [Files] usb.sdl, xhci.h, usbkbd.h, uhci.c, ohci.c, ehci.c +// +// 147 3/02/16 9:42p Wilsonlee +// [TAG] EIP254309 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] GK-FORCE K83 USB KB function abnormal. +// [RootCause] This device has an interrupt out endpoint and doesn't +// support "Set Report" request. +// [Solution] Use the interrupt out endpoint instead of sending "Set +// Report" request. +// [Files] AmiUsbController.h, xhci.c, usbmass.c, usbkbd.h, usbkbd.c, +// usbhub.c, usbhid.c, usbdef.h, usbCCID.c, usb.c, uhci.c, ohci.c, ehci.c, +// amiusb.h, efiusbms,c, amiusbhc.c +// +// 146 9/06/15 10:05p Wilsonlee +// +// 145 7/24/15 4:41a Wilsonlee +// [TAG] EIP226493 +// [Category] Improvement +// [Description] Block to process periodic list to prevent that we might +// send the wrong command sequences to the same device. +// [Files] usbmass.c, ehci.c, xhci.h, xhci.c, usbdef.h, uhcd.c +// +// 144 5/26/15 10:48p Wilsonlee +// [TAG] EIP219177 +// [Category] Improvement +// [Description] Fixed static code analysis issues in Usb module. +// [Files] UsbInt13.c, ehci.c, usbbus.c +// +// 143 4/10/15 3:11a Wilsonlee +// [TAG] EIP207413 +// [Category] Improvement +// [Description] Install UsbApiTable and UsbMassApitTable in +// AmiUsbSmmProtocol. +// [Files] amiusbhc.c, AmiUsbController.h, usbdef.h, usbCCID.c, uhci.c, +// ehci.c, amiusbrtCCID.h, amiusb.h, amiusb.c, uhcd.c +// +// 142 4/07/15 4:36a Wilsonlee +// [TAG] EIP212211 +// [Category] Bug Fix +// [Severity] Minor +// [Symptom] USB transfer speed is slower than before in EHCI. +// [RootCause] If we parse through the all periodic list, the +// performance is low in AMD platforms. +// [Solution] Only parse the first entry through +// MAX_SPLIT_PERIODIC_NUMBER. +// [Files] ehci.c +// +// 141 3/22/15 11:12p Wilsonlee +// [TAG] EIP206118 +// [Category] Improvement +// [Description] Remove dead loop in EHCIStartAsyncSchedule and +// EHCIStopAsyncSchedule functions. +// [Files] ehci.c +// +// 140 3/08/15 10:50p Wilsonlee +// [TAG] EIP207774 +// [Category] Improvement +// [Description] Set USB_FLAG_DRIVER_STARTED flag when HC is running and +// clear it if we don't start any HC. +// [Files] uhci.c, usb.c, ehci.c, ohci.c, xhci.c, amiusb.h +// +// 139 2/24/15 9:53p Wilsonlee +// [TAG] EIP205784 +// [Category] Improvement +// [Description] Don't read Periodic Frame List Base Address Register if +// the controller doesn't support periodic schedule. +// [Files] ehci.c +// +// 138 2/24/15 9:35p Wilsonlee +// [TAG] EIP206118 +// [Category] Improvement +// [Description] Remove dead loop in EHCIStartAsyncSchedule and +// EHCIStopAsyncSchedule functions. +// [Files] ehci.c +// +// 137 1/22/15 10:19p Wilsonlee +// [TAG] EIP201434 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] Number of connected devices isn't correct if we plug out +// keyboards or mice behind hub in xhci. +// [RootCause] The PortConnectChange bit is cleared when we check port +// status for interrupt endpoint transaction error. +// [Solution] Don't clear change bits if we check port status for +// interrupt endpoint transaction error. +// [Files] xhci.c, usbhub.c, usbdef.h, usb.c, uhci.c, ohci.c, ehci.c, +// amiusbhc.c +// +// 136 12/21/14 9:30p Wilsonlee +// [TAG] EIP194553 +// [Category] Improvement +// [Description] Realtek 8111EP EHCI controller workaround, this +// controller doesn't work if the buffer address isn't DWORD alignment +// (current offset of qTD). Provide the workaround to locate DWORD +// alignment buffer. +// [Files] ehci.c +// +// 135 11/24/14 12:50a Wilsonlee +// [TAG] EIP185972 +// [Category] Improvement +// [Description] To acquire more bandwidth, a dynamically transfer queue +// allocation mechanism is required. +// [Files] ehci.c, usbdef.h +// +// 134 5/01/14 3:56a Ryanchou +// [TAG] EIP162589 +// [Category] Improvement +// [Description] Do not register external controller as key repeat +// controller. +// [Files] ehci.c, ohci.c, uhci.c +// +// 133 4/30/14 6:12a Ryanchou +// [TAG] EIP151374 +// [Category] Improvement +// [Description] Calculates maximum data length to be reported in the +// HID device. +// [Files] ehci.c, ohci.c, uhci.c, usbCCID.c, usbdef.h, usbhid.c, +// usbhub.c, xhci.c +// +// 132 4/30/14 5:25a Wilsonlee +// [TAG] EIP164842 +// [Category] Improvement +// [Description] Check if the devices have put into to our queue before +// we put them. +// [Files] UsbInt13.c, amiusb.c, ehci.c, ohci.c, usb.c, usbdef.h, +// usbmass.c, xhci.c, amiusbhc.c, efiusbmass.c, uhcd.c, usbbus.c, usbsb.c +// +// 131 2/26/14 1:56a Wilsonlee +// [TAG] EIP149854 +// [Category] Improvement +// [Description] Add data length parameter to polling callback function. +// [Files] usbkbd.c, uhci.c, usb.c, usbhub.c, usbCCID.c, usbms.c, +// usbhid.c, usbpoint.c, usbkbd.h, ehci.c, ohci.c, xhci.c, usbdef.h +// +// 130 11/26/13 4:15a Ryanchou +// [TAG] EIP142940 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] USB keyboard cannot work if token USB_RUNTIME_DRIVER_IN_SMM +// is disabled. +// [RootCause] The variable Qh in EhciAddPeriodicQh() does not have +// initialize value. +// [Solution] Check if the value of variable Qh is valid first. +// [Files] ehci.c +// +// 129 9/05/13 12:14a Wilsonlee +// [TAG] EIP135237 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] USB can't work when turn on AMIDEBUG_RX_SUPPORT. +// [RootCause] EHCI_PORT_CHANGE_DETECT bit isn't set when turn on +// AMIDEBUG_RX_SUPPORT. +// [Solution] Ignore the EHCI_PORT_CHANGE_DETECT bit to check the root +// hub ports at first time. +// [Files] ehci.c +// +// 128 7/26/13 2:33a Ryanchou +// [TAG] EIP122142 +// [Category] Improvement +// [Description] Improve periodic schedule mechanism +// [Files] ehci.c, ehci.h, ohci.c, ohci.h, uhci.c, uhci.h, usbdef.h, +// amiusbhc.c +// +// 127 7/22/13 10:31p Wilsonlee +// [TAG] EIP125357 +// [Category] Improvement +// [Description] Check if the port releases to a select host controller. +// [Files] uhci.c, usb.c, usbhub.c, ehci.c, ohci.c, xhci.c, usbdef.h +// +// 126 6/26/13 2:18a Roberthsu +// [TAG] EIP122174 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] Usb keyboard can not work.When special usb key insert +// [RootCause] This keyboard detect connect status signal during this +// key initial. So port detect not service. +// [Solution] Clear port detect status before check port change. +// [Files] ehci.c +// +// 125 6/02/13 11:41p Wilsonlee +// [TAG] EIP123235 +// [Category] Improvement +// [Description] Stop the usb host controller at ExitBootService if it +// is an extend card or it doesn't support HW SMI. +// [Files] xhci.c, ehci.c, uhci.c, ohci.c, amiusb.c, usbdef.h, usbsb.c, +// uhcd.c +// +// 124 5/12/13 11:52p Wilsonlee +// Set the data toggle to QH at EHCI_ActivatePolling and +// EHCI_InterruptTransfer. +// +// 123 5/02/13 2:35a Wilsonlee +// [TAG] EIP121790 +// [Category] Bug Fix +// [Severity] Minor +// [Symptom] The usb keyboard doesn't work fine behind usb 2.0 hub. +// [RootCause] Teradici ehci controlle doesn't preserves DT bit in the +// qTD. +// [Solution] Don't set "Data toggle control" bit and check DT bit in +// the queue head. +// [Files] ehci.c +// +// 122 5/01/13 9:54p Wilsonlee +// [TAG] EIP121643 +// [Category] Bug Fix +// [Severity] Minor +// [Symptom] Cannot detect the usb floppy. +// [RootCause] This device doesn't return data when we sned +// get-config-descriptor command. +// [Solution] We retry to send get-config-descriptor command if there is +// no data. +// [Files] usb.c, ehci.c +// +// 121 4/18/13 1:02p Ryanchou +// Add Teradici USB controller support. +// +// 120 4/16/13 6:44a Ryanchou +// [TAG] EIP118912 +// [Category] Improvement +// [Description] Add VIA VT6212 EHCI controller support. +// [Files] ehci.c, uhci.c, usbdef.h, uhcd.c +// +// 119 3/19/13 3:59a Ryanchou +// [TAG] EIP118177 +// [Category] Improvement +// [Description] Dynamically allocate HCStrucTable at runtime. +// [Files] usb.sdl, usbport.c, usbsb.c, amiusb.c, ehci.c, ohci.c, +// syskbc.c, sysnokbc.c, uhci.c, usb.c, usbCCID.c, usbdef.h, usbhid.c, +// usbhub.c, usbmass.c, usbrt.mak, usb.sd, amiusbhc.c, efiusbccid.c, +// efiusbhid.c, efiusbmass.c, efiusbms.c, uhcd.c, uhcd.h, uhcd.mak, +// usbmisc.c, usbsrc.sdl +// +// 118 3/18/13 4:47a Ryanchou +// [TAG] EIP98377 +// [Category] Improvement +// [Description] Optimize USB controller timing. +// [Files] usb.sdl, usbport.c, ehci.c, elib.c, ohci.c, uhci.c, +// usbdef.h, usbhub.c, xhci.c, uhcd.c +// +// 117 2/24/13 8:59p Wilsonlee +// [TAG] EIP113541 +// [Category] Bug Fix +// [Severity] Critical +// [Symptom] System hangs at checkpoint 0xA2 when Win8 resume from S4. +// [RootCause] The "HCHalted" bit and "Port Change Detect" bit are set +// when the system S4 resume to Win8 OS. +// [Solution] We need to clear the interrupt status even if the +// "HCHalted" bit is set. +// [Files] ehci.c, ohci.c, uhci.c +// +// 116 1/24/13 3:20a Roberthsu +// [TAG] EIP111010 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] Touch panel button not work. +// [RootCause] Device write wrong buffer. +// [Solution] Clear bufferptr in pollingtdcallback. +// [Files] ehci.c +// +// 115 1/11/13 4:16a Ryanchou +// [TAG] EIP102491 +// [Category] Improvement +// [Description] Synchronized with Aptio V USB module +// [Files] usbport.c, usbsb.c, ehci.c, ehci.h, ohci.c, ohci.h, uhci.h, +// usb.c, usbdef.h, usbhid.c, usbhub.c, usbkbd.c, usbkbd.h, usbmass.c. +// usbms.c, usbpoint.c, xhci.h, usb.sd, amiusbhc.c, componentname.c, +// efiusbkc.c, efiusbmass.c, uhcd.c, uhcd.h, usbbus.c, usbbus.h, usbmisc.c +// +// 114 12/24/12 5:06a Ryanchou +// [TAG] EIP103031 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] System hans when loading QNX 6.5.0 +// [RootCause] The EHCI port detect change SMI is generated after +// ownership change to OS. +// [Solution] Clear the SMI enable bits and status bits even the +// controller is OS owned. +// [Files] ehci.c, ohci.c +// +// 113 12/06/12 12:38a Wilsonlee +// [TAG] EIP103186 +// [Category] Improvement +// [Description] Handle the error case "MEMIO was disabled" in USB +// driver. +// [Files] uhci.c, ohci.c, ehci.c, xhci.c +// +// 112 11/24/12 9:00p Wilsonlee +// [TAG] EIP107513 +// [Category] Improvement +// [Description] Stop periodic schedule before removing QH. +// [Files] ehci.c +// +// 111 11/13/12 7:11a Wilsonlee +// [TAG] EIP82553 +// [Category] New Feature +// [Description] Support usb S5 wake up function for XHCI. +// [Files] usb.c, ehci.c, ohci.c, xhci.c, xhci.h +// +// 110 11/10/12 6:40a Ryanchou +// [TAG] EIP99431 +// [Category] Bug Fix +// [Severity] Minor +// [Symptom] Cannot use the UsbIo's UsbAsyncInterruptTransfer for +// keyboard input +// [RootCause] Stopping EFI USB keyboard driver does not stop the +// endpoint polling, then application calls UsbAsyncInterruptTransfer, +// error will be returned. +// [Solution] Stops endpoint polling and release resource when +// disconnecting the device driver. And improve the +// UsbSyncInterruptTransfer. +// [Files] amiusb.c, amiusb.h, ehci.c, ohci.c, uhci.c, usb.c, +// usbCCID.c, usbdef.h, usbhub.c, usbkbd.c, usbmass.c, usbms.c, +// usbpoint.c, amiusbhc.c, efiusbhid.c, usbbus.c, usbbus.h +// +// 109 10/26/12 8:58a Roberthsu +// [TAG] EIP102781 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] After update usb module, DebugRx hang at UHCD.Start(). +// [RootCause] dHCSParams vaule not vaild. +// [Solution] Function EhciIsolateDebugPort move after variable +// dHCSParams fill. +// [Files] ehci.c +// +// 108 10/25/12 4:28a Wilsonlee +// [TAG] EIP103051 +// [Category] Bug Fix +// [Severity] Minor +// [Symptom] With USB3.0 flash on USB3.0 port and set to USB mode2, +// system will hang at post code 0x92 during the first cold boot after +// connecting AC power. +// [RootCause] This device connects to ehci first and it was +// disconnected after we reset the port. +// [Solution] We need to check the connect status after resetting the +// port. +// [Files] ehci.c +// +// 107 10/25/12 4:15a Wilsonlee +// [TAG] EIP82354 +// [Category] New Feature +// [Description] Support usb S5 wake up function for OHCI. +// [Files] usb.c, ehci.c, ohci.c +// +// 106 9/28/12 2:37a Wilsonlee +// [TAG] EIP93154 +// [Category] Improvement +// [Description] Change the unit of the FixedDelay from 15 us to 1 us. +// [Files] amiusb.h, xhci.c, ehci.c, ohci.c, uhci.c, usb.c, usbCCID.c, +// usbmass.c, usbhub.c, elib.c +// +// 105 8/29/12 9:32a Ryanchou +// [TAG] EIP88307 +// [Category] Bug Fix +// [Severity] Minor +// [Symptom] Key repeat cannot be stopped if the keyboard is connected +// to xHCI. +// [RootCause] Periodic timer SMI stop generating when USB SMI and +// periodic timer SMI is generated frequently. +// [Solution] Reduces the key repeat rate to avoid this issue. +// [Files] ehci.c, usb.c, usb.sdl +// +// 104 8/29/12 8:16a Ryanchou +// [TAG] EIP77262 +// [Category] New Feature +// [Description] Remove SMM dependency of USB. +// [Files] usb.sdl, usbport.c, amiusb.c, amiusb.dxs, amiusb.h, ehci.c, +// elib.c, ohci.c, uhci.c, usb.c, usbdef.h, usbrt.mak, xhci.c, amiusbhc.c, +// efiusbccid.c, efiusbhid.c, efiusbkb.c, efiusbmass.c, uhcd.c, uhcd.dxs, +// uhcd.h, usbmisc.c, AmiUsbController.h +// +// 103 5/04/12 6:38a Ryanchou +// [TAG] EIP82875 +// [Category] Improvement +// [Description] Support start/stop individual USB host to avoid +// reconnect issues. +// [Files] usbport.c, usbsb.c, amiusb.c, amiusb.h, ehci.c, ohci.c, +// uhci.c, uhci.h, usb.c, usbdef.h, xhci.c, amiusbhc.c, uhcd.c, uhcd.h, +// usbbus.c, usbmisc.c +// +// 102 5/03/12 5:59a Roberthsu +// [TAG] EIP84455 +// [Category] Improvement +// [Description] Implement usb hid device gencric. +// [Files] amiusb.c,amiusbhc.c,efiusbhid.c,efiusbkb.c,ehci.c,ohci.c,uhc +// d.c,uhci.c,usbdef.h,usbhid.c,usbhub.c,usbkbd.c,usbkbd.h,usbms.c,usbsb.c +// ,usbsrc.sdl +// +// 101 5/03/12 5:17a Ryanchou +// [TAG] EIP88085 +// [Category] Improvement +// [Description] Added 1 ms delay after port reset completed. +// [Files] ehci.c +// +// 100 5/03/12 5:08a Ryanchou +// [TAG] EIP83361 +// [Category] New Feature +// [Description] Added "USB 2.0 Controller Mode" setup item. +// [Files] ehci.c, usb.sd, usb.sdl, usb.uni, usbdef.h, UsbPolicy.h, +// usbport.c +// +// 99 4/10/12 10:12p Wilsonlee +// [TAG] EIP84790 +// [Category] Bug Fix +// [Severity] Minor +// [Symptom] USB IO PROTOCOL under XHCI issue +// [RootCause] The DevMiscInfo is NULL. +// [Solution] Fill the DevMiscInfo for the xhci controller. +// [Files] ehci.c, xhci.c, amiusbhc.c +// +// 98 4/05/12 7:42a Wilsonlee +// [TAG] EIP86001 +// [Category] Improvement +// [Description] Free the chunk of memory allocated using the +// USBMem_Alloc call when we didn't use it. +// [Files] usbhid.c, ehci.c +// +// 97 3/20/12 10:34p Wilsonlee +// [TAG] EIP83295 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] System will hang at B2 when do S3/S4 long run test or DOS +// reboot test. +// [RootCause] It causes the stack problem when we call +// IsUSBKeyboardBufferEmpty function. +// [Solution] Change to use pointer as parameter to +// IsUSBKeyboardBufferEmpty function. +// [Files] efiusbkb.c, efisubkb.h, usbmass.c, ehci.c +// +// 96 2/16/12 9:04p Wilsonlee +// [TAG] EIP82307 +// [Category] Improvement +// [Description] Port routing route to EHCI when EHCI initialization. +// [Files] ehci.c +// +// 95 1/16/12 1:05a Wilsonlee +// [TAG] EIP81030 +// [Category] Bug Fix +// [Severity] Critical +// [Symptom] System hangs randomly at POST. +// [RootCause] This is EIP71067 side effect. It uses error memory +// address. +// [Solution] The change used the correct memory address. +// [Files] ehci.c +// +// 94 12/23/11 8:39p Wilsonlee +// [TAG] EIP71067 +// [Category] Bug Fix +// [Severity] Minor +// [Symptom] SMI Timeout with USB FD and USB Flash Memory +// [RootCause] It uses the same area to bulk transfer on EHCI/ +// [Solution] It uses local QH and QTD for each control and bulk +// transfer on EHCI +// [Files] ehci.c +// +// 93 11/09/11 3:30a Ryanchou +// [TAG] EIP73692 +// [Category] Improvement +// [Description] Implement the ownership change mechanism for PCH. +// [Files] ehci.c, usbdef.h, usbsrc.sdl +// +// 92 11/08/11 1:57a Ryanchou +// [TAG] EIP63188 +// [Category] Improvement +// [Description] External USB controller support. +// [Files] amidef.h, amiusb.c, ehci.c, ohci.c, uhcd.c, uhcd.h, uhci.c, +// usbdef.h, usbmisc.c, usbsb.c, xhci.c +// +// 91 9/09/11 5:06a Roberthsu +// [TAG] EIP68865 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] VIA plug in USB KB/MS cause system hang +// [RootCause] After release port.Ehci_line_status still exist. +// [Solution] Release port owner when connect status change. +// [Files] ehci.c +// +// 90 8/08/11 6:58a Ryanchou +// [TAG] EIP54018 +// [Category] New Feature +// [Description] Added USB S5 wake up support. +// [Files] amiusb.c, ehci.c, ohci.c, uhci.c, usb.c, usb.sdl, usbdef.h, +// usbsb.c xhci.c +// +// 89 8/08/11 5:14a Ryanchou +// [TAG] EIP60561 +// [Category] New Feature +// [Description] Add USB timing policy protocol for timing override. +// [Files] ehci.c, guids.c, ohci.c, uhcd.c, uhci.c usb.c, usbdef.h, +// usbhub.c, usbmass.c, UsbPolicy.h, usbport.c usbsrc.sdl +// +// 88 7/19/11 5:16a Ryanchou +// [TAG] EIP64498 +// [Category] New Feature +// [Description] Implement EHCI key repeat function. +// [Files] ehci.c, ehci.h, usb.c, usbdef.h +// +// 87 7/15/11 6:11a Ryanchou +// [TAG] EIP38434 +// [Category] New Feature +// [Description] Added USB HID report protocol support. +// [Files] amiusb.c, AmiUsbController.h, amiusbhc.c, efiusbkb.c, +// efiusbkb.h, ehci.c, ohci.c, uhcd.c uhcd.cif, uhci.c, usb.c, usbdef.h, +// usbkbd.c, usbkbd.h, usbms.c, usbrt.cif, usbsb.c, usbsetup.c, +// usbsrc.sdl, xhci.c +// +// 86 7/12/11 8:09a Ryanchou +// [TAG] EIP56918 +// [Category] New Feature +// [Description] Added CCID device support. +// [Files] amiusb.c, amiusb.h, amiusbrtCCID.h, ehci.c, ohci.c, uhci.c, +// usb.c, UsbCCID.c, usbdef.h, usbrt.cif, usbsetup.c, efiusbccid.c, +// framework.cif, uhcd.c, uhcd.cif, uhcd.h, usbsrc.sdl, AmiusbCCID.h, +// AmiUsbController.h, AmiUSBProtocols.cif +// +// 85 7/01/11 3:18a Ryanchou +// [TAG] EIP61385 +// [Category] Bug Fix +// [Severity] Important +// [Symptom] USB devices can't detected. +// [RootCause] This is the side effect of EIP59663, the port status +// doesn't reflect connect status and connect status change. +// [Solution] Add 100 us delay. +// [Files] ehci.c, uhci.c +// +// 84 6/21/11 10:26a Ryanchou +// [TAG] EIP60632 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] Generic Usb Redirection caused system hung at checkpoint A2 +// [RootCause] The timeout value is zero, and the bulk transfer never +// completed. +// [Solution] UEFI spec defines "If Timeout is 0, then the +// caller must wait for the function to be completed until +// EFI_SUCCESS or EFI_DEVICE_ERROR is returned". +// [Files] ehci.c +// +// 83 6/21/11 10:00a Ryanchou +// [TAG] EIP62708 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] Unexpected USB SMI after USB Owner Ship Change +// [RootCause] Unexpected USB SMI causes an expection after USB onwer +// ship change +// executed. +// [Solution] Check if HC is still under BIOS control before service USB +// interrupts. +// [Files] ehci.c +// +// 82 6/21/11 9:55a Ryanchou +// [TAG] EIP59663 +// [Category] Bug Fix +// [Severity] Normal +// [Symptom] Plug USB WLAN device may causes OHCI malfunction. +// [RootCause] The devices have to connect to OHCI first, or it can't be +// configured. +// [Solution] Port routing route to EHCI after OHCI initialization. +// [Files] ehci.c, ohci.c +// +// 81 5/30/11 8:41a Ryanchou +// [TAG] EIP61030 +// [Category] Improvement +// [Description] PORTSC register should be read before write the +// register, and set the USB_PORT_STAT_DEV_CONNECT_CHANGED flag if +// bIgnoreConnectStsChng is set and connect status change bit is not set. +// [Files] ehci.c +// +// 80 5/03/11 10:10a Ryanchou +// [TAG] EIP54283 +// [Category] Improvement +// [Description] Follow XHCI spec ver 1.0 section 4.6.8 to recovery +// endpoint halt. +// [Files] ehci.c, ohci.c, uhci.c, usbdef.h, usbmass.c, xhci.c +// +// 79 5/03/11 8:54a Ryanchou +// [TAG] EIP58108 +// [Category] Improvement +// [Description] Disable port only if this port is enabled and remove +// the delay after port disable. +// [Files] ehci.c +// +// 78 4/06/11 3:52a Ryanchou +// [TAG] EIP55960 +// [Category] Improvement +// [Description] The Host Controller must halt within 16 micro-frames +// after software clears the Run bit. +// [Files] ehci.c, elib.c +// +// 77 4/06/11 1:32a Ryanchou +// [TAG] EIP54782 +// [Category] Improvement +// [Description] Change polling data size of HID devices to max packet +// size of interrupt endpoint. +// [Files] ehci.c, ohci.c, uhci.c, usb.c, usbdef.h, xhci.c +// +// 76 3/29/11 10:47p Ryanchou +// [TAG] EIP55401 +// [Category] Improvement +// [Description] Improve the USB 3.0 device compatibility. +// [Files] ehci.c, ehci.h, ohci.c, uhci.c, usb.c, usbdef.h, usbhub.c, +// xhci.c +// +// 75 3/29/11 10:10a Ryanchou +// [TAG] EIP53518 +// [Category] Improvement +// [Description] Added chipset xHCI chip support. +// [Files] amiusb.c, amiusb.h, ehci.c, ohci.c, uhcd.c, uhci.c, usb.c, +// usb.sdl, usbdef.h, usbport, usbsb.c, xhci.c +// +// 74 2/17/11 8:03a Ryanchou +// [TAG] EIP48592 +// [Category] Improvement +// [Description] Add timeout check in EHCIStartPeriodicSchedule and +// EHCIStopPeriodicSchedule to avoid the endless loop. +// [Files] ehci.c +// +// 73 11/11/10 11:32p Ryanchou +// [TAG] EIP45578 +// [Category] Bug Fix +// [Severity] Minor +// [Symptom] USB 3.0 device can't be detected. +// [RootCause] Address Device Command fails. +// [Solution] Reset the device and attempt the Address Device Command +// again. +// [Files] ehci.c, ohci.c, uhci.c, usb.c, usbdef.h, usbhub.c, xhci.c +// +// 72 10/20/10 1:02a Ryanchou +// EIP45689: Uncomment code in EHCI_GetRootHubStatus that wait 20ms for +// host controller could report accurate port status properly. +// +// 71 9/24/10 5:38p Olegi +// EIP38221: Added the code that properly initializes +// DEV_INFO.bIntEndpoint field; interrupt endpoint polling is using this +// endpoint number. +// +// 70 8/09/10 1:29a Ryanchou +// EIP41187: Set CERR field as 3 in a qTD, and service all interrupts +// after transfer complete. +// +// 69 7/13/10 7:16a Ryanchou +// EIP38356: Implement shutdown USB legacy support in ACPI enable call. +// +// 68 6/10/10 3:07a Ryanchou +// EIP38648: Enable USB Error SMI. +// +// 67 3/11/10 9:42a Olegi +// +// 66 3/02/10 4:51p Olegi +// Undone changes in EHCI_ControlTransfer for EIP32956. +// +// 65 2/27/10 11:59a Olegi +// +// 64 2/26/10 5:14p Olegi +// +// 63 2/08/10 9:59a Olegi +// EIP33381: Implement multiple bulk endpoint in UsbIoProtocol. +// +// 62 1/26/10 10:21a Olegi +// EIP32624: fix Change in InterruptTransfer. +// +// 61 1/04/10 9:20a Olegi +// EIP32956: Polling rate for the keyboards has been changed from 8 ms to +// 32 ms. +// +// 60 12/23/09 11:59a Olegi +// +// 59 12/09/09 1:07p Davidd +// Corrected the USBkeyboard can't hotplug in DOS issue. (EIP 26477) +// +// 58 12/09/09 12:33p Olegi +// Code cleanup. +// +// 57 11/30/09 6:13p Olegi +// +// 56 11/25/09 1:48p Olegi +// +// 55 11/18/09 7:10p Olegi +// Restored EHCIResetHC call in Start function that was removed for +// EIP23479. This EIP is resolved differently. +// +// 54 10/30/09 5:47p Olegi +// +// 52 10/15/09 3:12p Olegi +// EIP24437: Change EHCI_ProcessInterrupt to handle the HC error bit in +// the periodic TDs. +// +// 51 10/07/09 9:48a Olegi +// USB Hub error handling improvement. EIP#25601. +// +// 50 9/17/09 11:13a Olegi +// Change in ProcessInterrupt: +// There may be a condition in which OS changes the base address before +// owning the semaphore and SMI may occur on USB complete or port change +// detect. This may cause SMI storm. Disabling SMI will not affect +// anything since we dont have any control over it. +// +// 49 9/15/09 10:28a Olegi +// EIP26685: uncommented code in EHCI_Stop that disconnects all devices. +// +// 48 9/15/09 10:21a Olegi +// Added USB_INCMPT_HID_DATA_OVERFLOW incompatibility type. +// +// 47 8/28/09 11:49a Olegi +// EIP#25760: change the sequence of periodic schedule and async schedule +// execution. +// +// 46 7/02/09 10:06a Olegi +// Fix for EIP#23479: while processing EHCI change ownership request, we +// will not reset the HC. This change will preserve the state of +// EHCI_CONFIGURE register (MMIO reg 60h, bit0) so the controller +// switching will not occur. +// +// 45 6/24/09 2:35p Olegi +// Correction in EHCIInitializePeriodicSchedule(); fix for the EIP#23498. +// +// 44 6/02/09 2:47p Olegi +// +// 43 5/22/09 1:48p Olegi +// Bugfix in EHCI_GetRootHubStatus. +// +// 42 5/15/09 6:16p Olegi +// Skip enabling PortPower for the ports that already have it enabled. +// Saves ~100ms per controller. +// +// 41 3/19/09 4:56p Olegi +// +// 40 3/09/09 8:49a Olegi +// Modified EHCIProgramLegacyRegisters, EIP#20084. +// +// 39 2/20/09 2:30p Olegi +// Modifications in ProcessOwnerShipChangeSMI function, EIP#19525 +// +// 38 2/18/09 10:10a Olegi +// Fast unplug/insert fix, EIP#19206. +// +// 37 2/17/09 4:01p Olegi +// +// 36 1/16/09 4:11p Olegi +// Added dependency on MAX_BULK_DATA in EHCI_BulkTransfer. +// +// 35 11/06/08 1:59p Olegi +// Change in EHCI_EnumeratePorts that will avoid unnecessary delays during +// enabling the port power. +// +// 34 10/06/08 3:33p Olegi +// EHCI change ownership testing in DOS fix (EIP#14855). +// +// 33 9/02/08 10:28a Olegi +// EIP14855 bugfix: change ownership request is not processed properly in +// case of multiple controllers of the same type. +// +// 32 8/08/08 2:37p Olegi +// Fix in EHCI_GetRootHubStatus that did not report connection properly in +// some cases. +// +// 31 5/16/08 12:01p Olegi +// Compliance with AMI coding standard. +// +// 30 7/09/07 2:11p Olegi +// Changed the maximum data size of the BulkTransfer from 1kB to 64kB. +// +// 29 4/17/07 8:24a Olegi +// Device detection algorythm update, in sync with Core8. +// +// 28 3/20/07 12:21p Olegi +// +// 27 1/25/07 10:19a Olegi +// +// 26 12/28/06 5:27p Olegi +// +// 25 12/26/06 10:23a Olegi +// +// 24 12/22/06 4:05p Olegi +// Timeout implementation. +// +// 23 12/20/06 2:30p Olegi +// +// 22 11/21/06 5:35p Olegi +// +// 21 11/16/06 6:10p Olegi +// DebugPort support initial changes. +// +// 19 10/19/06 5:16p Andriyn +// +// 18 10/12/06 9:37p Andriyn +// Fix: unexpected plug-off hangs with endless TIMEOUTs +// +// 17 10/12/06 9:07p Andriyn +// +// 16 7/21/06 6:34p Olegi +// +// 15 7/19/06 3:53p Olegi +// Bugfix in EHCIRemoveQH routine. +// +// 14 6/09/06 10:29a Olegi +// USB_FLAG_ENABLE_BEEP_MESSAGE flag is reset while handling change of the +// controller ownership. +// +// 13 5/22/06 9:08a Olegi +// ASYNC_BELL_SUPPORT modifications +// +// 12 5/16/06 11:23a Olegi +// +// 11 5/16/06 11:19a Olegi +// Removed DisconnectDevice call from EHCI_Stop +// +// 10 4/14/06 6:39p Olegi +// Conversion to be able to use x64 compiler. +// +// 9 3/20/06 3:37p Olegi +// Version 8.5 - x64 compatible. +// +// 8 3/16/06 2:34p Olegi +// +// 7 3/06/06 6:25p Olegi +// +// 6 1/11/06 11:52a Olegi +// +// 5 11/29/05 12:33p Andriyn +// +// 4 6/03/05 6:09p Olegi +// HW SMI registration change. +// +// 3 5/19/05 8:07p Olegi +// Aptio changes in driver 8.1 implementation. +// +// 2 5/17/05 7:51p Andriyn +// USB BUS pre-release +// +// 1 3/28/05 6:20p Olegi +// +// 1 3/15/05 9:23a Olegi +// Initial VSS check-in. +// +//**************************************************************************** + +//<AMI_FHDR_START> +//----------------------------------------------------------------------------- +// +// Name: Ehci.h +// +// Description: AMI USB EHCI support +// +//----------------------------------------------------------------------------- +//<AMI_FHDR_END> + +#include "amidef.h" +#include "usbdef.h" +#include "amiusb.h" +#include "usbkbd.h" +#if USB_RUNTIME_DRIVER_IN_SMM +#include <AmiBufferValidationLib.h> +#endif + +UINT8 EHCI_Start (HC_STRUC*); +UINT8 EHCI_Stop (HC_STRUC*); +UINT8 EHCI_EnumeratePorts (HC_STRUC*); +UINT8 EHCI_DisableInterrupts (HC_STRUC*); +UINT8 EHCI_EnableInterrupts (HC_STRUC*); +UINT8 EHCI_ProcessInterrupt(HC_STRUC*); +UINT8 EHCI_GetRootHubStatus (HC_STRUC*,UINT8, BOOLEAN); +UINT8 EHCI_DisableRootHub (HC_STRUC*,UINT8); +UINT8 EHCI_EnableRootHub (HC_STRUC*,UINT8); +UINT16 EHCI_ControlTransfer (HC_STRUC*,DEV_INFO*,UINT16,UINT16,UINT16,UINT8*,UINT16); +UINT32 EHCI_BulkTransfer (HC_STRUC*,DEV_INFO*,UINT8,UINT8*,UINT32); +UINT16 EHCI_InterruptTransfer (HC_STRUC*, DEV_INFO*, UINT8, UINT16, UINT8*, UINT16); +UINT8 EHCI_DeactivatePolling (HC_STRUC*,DEV_INFO*); +UINT8 EHCI_ActivatePolling (HC_STRUC*,DEV_INFO*); +UINT8 EHCI_DisableKeyRepeat (HC_STRUC*); +UINT8 EHCI_EnableKeyRepeat (HC_STRUC*); +UINT8 EHCI_ResetRootHub (HC_STRUC*,UINT8); +UINT8 EHCI_GlobalSuspend (HC_STRUC*); //(EIP54018+) + +UINT8 EHCIResetHC(HC_STRUC*); +UINT8 EHCIInitializePeriodicSchedule(HC_STRUC*, UINT32); +UINT8 EHCIProgramLegacyRegisters(HC_STRUC*, UINT8); +UINT8 EHCIStartAsyncSchedule(HC_STRUC*); +UINT8 EHCIStopAsyncSchedule(HC_STRUC*); +UINT8 EHCIStartPeriodicSchedule(HC_STRUC*); +UINT8 EHCIStopPeriodicSchedule(HC_STRUC*); +UINT8 EHCIProcessQH(HC_STRUC*, EHCI_QH*); +VOID EHCIProcessPeriodicList(HC_STRUC*); +VOID EHCIInitializeQueueHead (EHCI_QH*); +VOID EHCISetQTDBufferPointers(EHCI_QTD*, UINT8*, UINT32); +UINT16 EHCIWaitForTransferComplete(HC_STRUC*, EHCI_QH*,DEV_INFO* ); +UINT8 EhciAddPeriodicQh(HC_STRUC*,EHCI_QH*); +UINT8 EhciRemovePeriodicQh(HC_STRUC*,EHCI_QH*); +VOID ProcessOwnerShipChangeSMI(HC_STRUC*); +VOID ProcessSmiChangeToEHCD(HC_STRUC*); +VOID ProcessSmiChangeToBIOS(HC_STRUC*); +UINT8 EHCIGetLegacySupportOffset(HC_STRUC*, UINT16); +VOID EHCIRemoveQHFromAsyncList(HC_STRUC*, EHCI_QH*); +UINT8 EhciPollingTDCallback(HC_STRUC*, DEV_INFO*, UINT8*, UINT8*, UINT16); +UINT8 EhciRepeatTDCallback(HC_STRUC*, DEV_INFO*, UINT8*, UINT8*, UINT16); + +UINT32 ReadPCIConfig(UINT16, UINT8); +VOID WordWritePCIConfig(UINT16, UINT8, UINT16); +VOID DwordWritePCIConfig(UINT16, UINT8, UINT32); + +UINT32 DwordReadMem(UINT32, UINT16); +VOID DwordWriteMem(UINT32, UINT16, UINT32); +VOID DwordResetMem(UINT32, UINT16, UINT32); +VOID DwordSetMem(UINT32, UINT16, UINT32); + +UINT32 EhciReadPciReg(HC_STRUC*, UINT32); +VOID EhciWritePciReg(HC_STRUC*, UINT32, UINT32); +UINT32 EhciReadHcMem(HC_STRUC*, UINT32); +VOID EhciWriteHcMem(HC_STRUC*, UINT32, UINT32); +UINT32 EhciReadOpReg(HC_STRUC*, UINT32); +VOID EhciWriteOpReg(HC_STRUC*, UINT32, UINT32); +VOID EhciClearOpReg(HC_STRUC*, UINT32, UINT32); +VOID EhciSetOpReg(HC_STRUC*, UINT32, UINT32); +UINT32 EhciReadDebugReg(HC_STRUC*, UINT8, UINT32); +VOID* EhciMemAlloc(HC_STRUC*, UINT16); +VOID EhciMemFree(HC_STRUC*, VOID*, UINT16); +UINT8 EhciDmaMap(HC_STRUC*, UINT8, UINT8*, UINT32, UINT8**, VOID**); +UINT8 EhciDmaUnmap(HC_STRUC*, VOID*); +BOOLEAN EhciIsHalted(HC_STRUC*); +UINT16 EhciTranslateInterval(UINT8, UINT8); + +UINT8 USBCheckPortChange (HC_STRUC*, UINT8, UINT8); +UINT8 USBLogError(UINT16); +UINT8 UsbGetDataToggle(DEV_INFO*,UINT8); +VOID UsbUpdateDataToggle(DEV_INFO*, UINT8, UINT8); + +VOID USB_InitFrameList (HC_STRUC*, UINT32); +VOID FixedDelay(UINTN); + +VOID* USB_MemAlloc (UINT16); +UINT8 USB_MemFree (VOID _FAR_ *, UINT16); +UINT8 USB_DisconnectDevice (HC_STRUC*, UINT8, UINT8); +DEV_INFO* USB_GetDeviceInfoStruc(UINT8, DEV_INFO*, UINT8, HC_STRUC*); +UINT8 USB_StopDevice (HC_STRUC*, UINT8, UINT8); +UINT8 USB_InstallCallBackFunction (CALLBACK_FUNC); +VOID USBKeyRepeat(HC_STRUC*, UINT8); + +#if USB_DEV_KBD +VOID USBKBDPeriodicInterruptHandler(HC_STRUC*); +#endif + + +extern USB_GLOBAL_DATA *gUsbData; +extern BOOLEAN gCheckUsbApiParameter; + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: EHCI_FillHCDEntries (HCD_HEADER *fpHCDHeader) +// +// Description: This function fills the host controller driver +// routine pointers +// +// Parameters: fpHCDHeader Ptr to the host controller header structure +// +// Output: Status: USB_SUCCESS = Success +// USB_ERROR = Failure +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_FillHCDEntries (HCD_HEADER *fpHCDHeader) +{ + fpHCDHeader->pfnHCDStart = EHCI_Start; + fpHCDHeader->pfnHCDStop = EHCI_Stop; + fpHCDHeader->pfnHCDEnumeratePorts = EHCI_EnumeratePorts; + fpHCDHeader->pfnHCDDisableInterrupts = EHCI_DisableInterrupts; + fpHCDHeader->pfnHCDEnableInterrupts = EHCI_EnableInterrupts; + fpHCDHeader->pfnHCDProcessInterrupt = EHCI_ProcessInterrupt; + fpHCDHeader->pfnHCDGetRootHubStatus = EHCI_GetRootHubStatus; + fpHCDHeader->pfnHCDDisableRootHub = EHCI_DisableRootHub; + fpHCDHeader->pfnHCDEnableRootHub = EHCI_EnableRootHub; + fpHCDHeader->pfnHCDControlTransfer = EHCI_ControlTransfer; + fpHCDHeader->pfnHCDBulkTransfer = EHCI_BulkTransfer; + fpHCDHeader->pfnHCDInterruptTransfer = EHCI_InterruptTransfer; + fpHCDHeader->pfnHCDDeactivatePolling = EHCI_DeactivatePolling; + fpHCDHeader->pfnHCDActivatePolling = EHCI_ActivatePolling; + fpHCDHeader->pfnHCDDisableKeyRepeat = EHCI_DisableKeyRepeat; + fpHCDHeader->pfnHCDEnableKeyRepeat = EHCI_EnableKeyRepeat; + fpHCDHeader->pfnHCDEnableEndpoints = USB_EnableEndpointsDummy; + fpHCDHeader->pfnHCDInitDeviceData = USB_InitDeviceDataDummy; + fpHCDHeader->pfnHCDDeinitDeviceData = USB_DeinitDeviceDataDummy; + fpHCDHeader->pfnHCDResetRootHub = EHCI_ResetRootHub; + fpHCDHeader->pfnHCDClearEndpointState = 0; //(EIP54283+) + fpHCDHeader->pfnHCDGlobalSuspend = EHCI_GlobalSuspend; //(EIP54018+) + + USB_InstallCallBackFunction(EhciRepeatTDCallback); + USB_InstallCallBackFunction(EhciPollingTDCallback); + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: EhciIsolateDebugPort +// +// Description: This routine locates EHCI debug port and determines whether +// or not the debug port is initialized and being used by other +// agents. If so, the global flag will be set to instruct the +// EHCI runtime routines about debug port presence and prevent +// any unwanted reset/reconfiguration of this port. +// +// Parameters: fpHCStruc Ptr to the host controller structure +// +// Output: fpHCStruc->DebugPort is updated if Debug Port is active on +// this controller; otherwise it will remain 0. +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID EhciIsolateDebugPort(HC_STRUC *fpHCStruc) +{ + UINT32 HcSParams = fpHCStruc->dHCSParams; // Host Controller Structural Parameters + UINT8 DebugPortNo; + UINT32 NextCap; + UINT8 DebugPortBarIndex; + UINT16 DebugPortOffset; + + // + // Locate debug port by looking at the PCI capabilities + // + DebugPortNo = (UINT8)((HcSParams & (EHCI_DEBUG_N)) >> 20); + + //ASSERT(DebugPortNo); // No debug port implemented + fpHCStruc->DebugPort = 0; + if (DebugPortNo == 0) return; + + ASSERT(DebugPortNo <= (UINT8)(HcSParams & (EHCI_N_PORTS))); // Invalid debug port number + if (DebugPortNo > (UINT8)(HcSParams & (EHCI_N_PORTS))) return; + + // + // Check whether device implements Capability list that starts at register 0x34 + // + if (!(EhciReadPciReg(fpHCStruc, 4) & BIT20)) { + //ASSERT(FALSE); // Capabilities list is not implemented + return; + } + + // + // Find the beginning of Debug Port registers block + // + for (NextCap = EhciReadPciReg(fpHCStruc, 0x34); + (UINT8)NextCap > 0; + ) + { + NextCap = EhciReadPciReg(fpHCStruc, (UINT8)NextCap); + if ((UINT8)NextCap == 0xA) break; // Debug port capability found + NextCap >>= 8; + } + if ((UINT8)NextCap == 0) { + //ASSERT(FALSE); // Debug capabilities not found + return; + } + DebugPortBarIndex = (UINT8)((NextCap >> 29) - 1); + DebugPortOffset = (UINT16)((NextCap >> 16) & 0x1FFF); + ASSERT(DebugPortBarIndex >= 0 && DebugPortBarIndex <= 5); // Wrong BAR + if (!(DebugPortBarIndex >= 0 && DebugPortBarIndex <= 5)) return; + // + // See whether Debug Port is acquired by other software + // + if (EhciReadDebugReg(fpHCStruc, DebugPortBarIndex, DebugPortOffset) & BIT28) { + fpHCStruc->DebugPort = DebugPortNo; + USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC Debug Port #%d enabled.\n", DebugPortNo); + } +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: EHCI_Start +// +// Description: This API function is called to start a EHCI host controller. +// The input to the routine is the pointer to the HC structure +// that defines this host controller +// +// Parameters: fpHCStruc Ptr to the host controller structure +// +// Output: Status: USB_SUCCESS = Success +// USB_ERROR = Failure +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_Start (HC_STRUC* fpHCStruc) +{ + UINT32 dTemp; +//#if EHCI_ASYNC_BELL_SUPPORT +// EHCI_QH *fpQHAsyncXfer; +//#endif + EHCI_QH *fpQHRepeat = NULL; + EHCI_QTD *fpqTDRepeat = NULL; + UINT32 i; //(EIP55960+) + BOOLEAN SetPortPower = FALSE; + UINT16 PortReg; + EHCI_DESC_PTRS *DescPtr = NULL; + EFI_STATUS EfiStatus = EFI_SUCCESS; + +/* +USB_DEBUG(DEBUG_LEVEL_3, "Enabling MEM/BM for EHCI HC %02X\n", fpHCStruc->wBusDevFuncNum); + + // + // Enable IO access and Bus Mastering + // + WordWritePCIConfig((UINT16)fpHCStruc->wBusDevFuncNum, 4, BIT1 + BIT2); +*/ + // + // Get memory base address of the HC and store it in the HCStruc + // + fpHCStruc->BaseAddress = EhciReadPciReg(fpHCStruc, USB_MEM_BASE_ADDRESS) & 0xFFFFFFF0; + + USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC Mem Addr: %X\n", fpHCStruc->BaseAddress); + +#if USB_RUNTIME_DRIVER_IN_SMM + EfiStatus = AmiValidateMmioBuffer((VOID*)fpHCStruc->BaseAddress, fpHCStruc->BaseAddressSize); + if (EFI_ERROR(EfiStatus)) { + USB_DEBUG(3, "Usb Mmio address is invalid, it is in SMRAM\n"); + return USB_ERROR; + } +#endif + // + // Get the number of ports supported by the host controller (Offset 4) + // and store it in HCStruc + // + fpHCStruc->dHCSParams = EhciReadHcMem(fpHCStruc, EHCI_HCSPARAMS); + fpHCStruc->bNumPorts = (UINT8)(fpHCStruc->dHCSParams & EHCI_N_PORTS); + USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC Number of ports: %d\n", fpHCStruc->bNumPorts); + + EhciIsolateDebugPort(fpHCStruc); //(EIP102781) + + // + // Read the Capability Registers Length to find the Offset address for the + // beginning of the operational registers + // + fpHCStruc->bOpRegOffset = (UINT8)EhciReadHcMem(fpHCStruc, EHCI_VERCAPLENGTH); + USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC Operational Registers Offset: %d\n", fpHCStruc->bOpRegOffset); + + // + // Read and store the HCCPARAMS value + // + fpHCStruc->dHCCParams = EhciReadHcMem(fpHCStruc, EHCI_HCCPARAMS); + //USB_DEBUG(DEBUG_LEVEL_3, "EHCI HC HCPARAMS: %x\n", gUsbData->dHCCParams); + + // + // Get PCI register offset for the legacy support in EHCI controller + // and store it in HC_STRUC + // + fpHCStruc->bExtCapPtr = EHCIGetLegacySupportOffset( + fpHCStruc, + fpHCStruc->wBusDevFuncNum); + +#if EHCI_64BIT_DATA_STRUCTURE == 0 + // + // 64bit data structures are not enabled. So check whether this host controller + // needs 64bit data structure or not. + // + if (fpHCStruc->dHCCParams & EHCI_64BIT_CAP) + { + // + // Engineer has to enable the 64bit capability. Post an error message + // + USBLogError(ERRUSB_EHCI_64BIT_DATA_STRUC); + ASSERT(FALSE); + + // + // Connect all ports to the classic host controller + // + EhciClearOpReg(fpHCStruc, EHCI_CONFIGFLAG, BIT0); + return USB_ERROR; + } +#endif + +#if HIDE_USB_HISPEED_SUPPORT_SETUP_QUESTION == 0 + if ((gUsbData->UsbHiSpeedSupport == 0) && ((fpHCStruc->dHCSParams & EHCI_N_CC) != 0)) { + EhciClearOpReg(fpHCStruc, EHCI_CONFIGFLAG, BIT0); + return USB_ERROR; + } +#endif + +//---------------------------------------------------------------------------- +// Note: after this point any access to the operational registers is through +// the macros EHCI_DWORD_READ_MEM and EHCI_DWORD_WRITE_MEM; access to the +// capability registers is through the macro USBPORT_DWORD_READ_MEM and +// there is no macro to write to the registers +//---------------------------------------------------------------------------- + //(EIP55960)> + if ((EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) == 0) { + // Turn HC off and wait for the Halted bit to get set + EhciClearOpReg(fpHCStruc, EHCI_USBCMD, EHCI_RUNSTOP); + + // The Host Controller must halt within 16 micro-frames after + // software clears the Run bit. + for (i = 0; i < 16; i++) { + if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) { + break; + } + FixedDelay(125); // 125 us delay + } + //while ((DwordReadMem(dMemAddr, EHCI_USBSTS) & EHCI_HCHALTED) == 0) {}; + } + //<(EIP55960) +// /* EIP#23479 + // + // Reset the host controller (HC must be halted) + // + if (EHCIResetHC(fpHCStruc) == USB_ERROR) + { + return USB_ERROR; // HC reset error, error log is updated + } +//*/ + // + // Get the frame list size from the EHCI command register + // + dTemp = EhciReadOpReg(fpHCStruc, EHCI_USBCMD); + dTemp = (dTemp & (BIT2 + BIT3)) >> 2; + + // + // Calculate number of elements in the asynchronous list + // and store the value in the HCStruc + // + switch (dTemp) + { + case 0: fpHCStruc->wAsyncListSize = 1024; + break; + case 1: fpHCStruc->wAsyncListSize = 512; + break; + case 2: fpHCStruc->wAsyncListSize = 256; + break; + case 3: return USB_ERROR; + + } + + USB_DEBUG(DEBUG_LEVEL_3, "EHCI AsyncListSize: %d\n", fpHCStruc->wAsyncListSize); + + // + // Set the max bulk data size + // + fpHCStruc->dMaxBulkDataSize = MAX_EHCI_DATA_SIZE; + + // + // Initialize the frame list pointers + // + USB_InitFrameList (fpHCStruc, EHCI_TERMINATE); + + // + // Write the base address of the Periodic Frame List to the PERIODIC BASE + // register + // + EhciWriteOpReg(fpHCStruc, EHCI_PERIODICLISTBASE, (UINT32)(UINTN)fpHCStruc->fpFrameList); + + // + // Initialize the periodic schedule + // + EHCIInitializePeriodicSchedule(fpHCStruc, (UINT32)fpHCStruc->BaseAddress); +/* +#if EHCI_ASYNC_BELL_SUPPORT + // + // Allocate and initialize an queue head for Async transfer + // Set the QHDummy as Async list head + // + fpQHAsyncXfer = EhciMemAlloc (fpHCStruc, GET_MEM_BLK_COUNT_STRUC(EHCI_QH)); + + if (!fpQHAsyncXfer) { + return USB_ERROR; + } + + gUsbData->fpQHAsyncXfer = fpQHAsyncXfer; + + fpQHAsyncXfer->dEndPntCap = QH_ONE_XFER; + fpQHAsyncXfer->fpFirstqTD = 0; + fpQHAsyncXfer->dAltNextqTDPtr = EHCI_TERMINATE; + fpQHAsyncXfer->dNextqTDPtr = EHCI_TERMINATE; + + // + // Assume as a high speed device + // + dTemp = QH_HIGH_SPEED; // 10b - High speed + + // + // Use data toggle from qTD and this QH is the head of the queue + // + dTemp |= (QH_USE_QTD_DT | QH_HEAD_OF_LIST | DUMMY_DEVICE_ADDR); // Endpoint is 0 + + // + // dTemp[6:0] = Dev. Addr, dTemp[7] = I bit(0) & dTemp[11:8] = Endpoint (0) + // + fpQHAsyncXfer->dEndPntCharac = dTemp; + + // + // Set the ASYNCLISTADDR register to point to the QHDummy + // + EhciWriteOpReg(fpHCStruc, EHCI_ASYNCLISTADDR, (UINT32)(UINTN)fpQHAsyncXfer); + + // + // Set next QH pointer to itself (circular link) + // + fpQHAsyncXfer->dLinkPointer = (UINT32)(UINTN)fpQHAsyncXfer | EHCI_QUEUE_HEAD; + fpQHAsyncXfer->bActive = TRUE; +#endif // EHCI_ASYNC_BELL_SUPPORT +*/ + +#if USB_RUNTIME_DRIVER_IN_SMM + // Check whether no companion host controllers + if (!(fpHCStruc->dHCFlag & HC_STATE_EXTERNAL) && + (fpHCStruc->dHCSParams & EHCI_N_CC) == 0) { + // + // Allocate a QH/qTD for QHRepeat/qTDRepeat + // + fpQHRepeat = EhciMemAlloc(fpHCStruc, + GET_MEM_BLK_COUNT(sizeof(EHCI_QH)+sizeof(EHCI_QTD))); + + if (!fpQHRepeat) { + return USB_ERROR; // Memory allocation error + } + DescPtr = fpHCStruc->stDescPtrs.fpEHCIDescPtrs; + DescPtr->fpQHRepeat = fpQHRepeat; + fpqTDRepeat = (EHCI_QTD*)((UINT32)fpQHRepeat + sizeof(EHCI_QH)); + DescPtr->fpqTDRepeat = fpqTDRepeat; +// +// Setup QHRepeat and qTDRepeat. It will run a interrupt transaction to a +// nonexistant dummy device. This will have the effect of generating +// a periodic interrupt used to generate keyboard repeat. This QH/qTD +// is normally inactive, and is only activated when a key is pressed. +// + // + // Set the first qTD pointer + // + fpQHRepeat->fpFirstqTD = fpqTDRepeat; + + //fpQHRepeat->fpDevInfoPtr = (UINT8*)fpDevInfo; + fpQHRepeat->dNextqTDPtr = (UINT32)fpqTDRepeat; + + // + // Intialize the queue head + // + fpQHRepeat->dAltNextqTDPtr = EHCI_TERMINATE; + fpQHRepeat->dLinkPointer = EHCI_TERMINATE; + + // + // Set max packet size, address, endpoint and high speed + // Update the AH's endpoint characteristcs field with the data formed + // + fpQHRepeat->dEndPntCharac |= ((0x40 << 16) | DUMMY_DEVICE_ADDR | + QH_HIGH_SPEED); + + // + // Set a bit in interrupt mask + // + fpQHRepeat->dEndPntCap = (BIT0 | QH_ONE_XFER); + fpQHRepeat->Interval = REPEAT_INTERVAL; + +// +// Fill the repeat qTD with relevant information +// The token field will be set so +// Direction PID = QTD_IN_TOKEN, +// Size = size of the data, +// Data Toggle = QTD_DATA0_TOGGLE, +// Error Count = QTD_NO_ERRORS, +// Status code = QTD_ACTIVE +// The buffer pointers field will point to the fpBuffer buffer +// which was before initialized to contain a DeviceRequest struc. +// The dNextqTDPtr field will point to the qTDControlSetup +// The dAltNextqTDPtr field will be set to EHCI_TERMINATE +// + fpQHRepeat->dTokenReload = ((UINT32)8 << 16) | QTD_IN_TOKEN | QTD_ONE_ERROR; + fpqTDRepeat->dToken = ((UINT32)8 << 16) | QTD_IN_TOKEN | QTD_ONE_ERROR; + + EHCISetQTDBufferPointers(fpqTDRepeat, + &fpQHRepeat->aDataBuffer[0], 8); + + // + // Update next & alternate next qTD pointers + // + fpqTDRepeat->dNextqTDPtr = EHCI_TERMINATE; + fpqTDRepeat->dAltNextqTDPtr = EHCI_TERMINATE; + + // + // Schedule the QHRepeat to 8ms schedule + // + EhciAddPeriodicQh(fpHCStruc,fpQHRepeat); + + fpQHRepeat->bCallBackIndex = USB_InstallCallBackFunction(EhciRepeatTDCallback); + fpQHRepeat->bActive = FALSE; + + USBKeyRepeat(fpHCStruc, 0); + } +#endif + + // + // Clear status register - all R/WC bits + // + EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, + EHCI_USB_INTERRUPT | // Interrupt + EHCI_USB_ERROR_INTERRUPT | // Error interrupt + EHCI_PORT_CHANGE_DETECT | // Port Change Detect + EHCI_FRAME_LIST_ROLLOVER | // Frame List Rollover + EHCI_HOST_SYSTEM_ERROR | // Host System Error + EHCI_INT_ASYNC_ADVANCE); // Interrupt on Async Advance + // + // Program the HC BIOS owned bit and return the legacy support register offset + // + if (fpHCStruc->bExtCapPtr) { + EHCIProgramLegacyRegisters(fpHCStruc, 1); // Set HC BIOS owned semaphore + + // + // Enable USB SMI, SMI on port change and SMI on ownership change + // + dTemp = EHCI_SMI + EHCI_PORT_CHANGE_SMI + EHCI_OWNERSHIP_CHANGE_SMI; + + EhciWritePciReg(fpHCStruc, fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, dTemp); + } + + // + // Turn HC on + // + EhciSetOpReg(fpHCStruc, EHCI_USBCMD, \ + (EHCI_RUNSTOP | EHCI_PER_SCHED_ENABLE)); + + // Wait for halt bit get cleared + for (i = 0; i < 20; i++) { + if (!(EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED)) { + break; + } + FixedDelay(100); // 100 us delay + } + + // + // If the port has the power switch then enable the port. Otherwise + // Power for the port is already present. So don't need to enable the power. + // ( Refer EHCI Spec 2.2.3 HCSPARAMS Structural Parameters Bit 4 (PPC) ) + if (fpHCStruc->dHCSParams & EHCI_PPC) { + // + // Enable port power + // + for (i = 1, PortReg = EHCI_PORTSC; i <= fpHCStruc->bNumPorts; i++, PortReg += 4) { + // + // Skip enabling DebugPort + // + if (fpHCStruc->DebugPort && fpHCStruc->DebugPort == i) continue; + + if (EhciReadOpReg(fpHCStruc, PortReg) & EHCI_PORTPOWER) { + continue; + } + + EhciSetOpReg(fpHCStruc, PortReg, EHCI_PORTPOWER); + SetPortPower = TRUE; + } + // + // Delay till the ports power is stabilised + // + if (SetPortPower) { + FixedDelay(20 * 1000); // 20 msec delay + } + } + + // Set HC flag as running + fpHCStruc->dHCFlag |= HC_STATE_RUNNING; + + // Set USB_FLAG_DRIVER_STARTED flag when HC is running. + if (!(gUsbData->dUSBStateFlag & USB_FLAG_DRIVER_STARTED)) { + gUsbData->dUSBStateFlag |= USB_FLAG_DRIVER_STARTED; + } + + // + // Disconnect all ports from companion HC (if any) and route them to EHCI + // + EhciSetOpReg(fpHCStruc, EHCI_CONFIGFLAG, BIT0); //(EIP59663-) //(EIP80307+) + + if (fpHCStruc->dHCFlag & HC_STATE_CONTROLLER_WITH_RMH) { + // Wait for port change detect bit set + for (i = 0; i < 50; i++) { + if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_PORT_CHANGE_DETECT) { + break; + } + FixedDelay(100); // 100 us delay + } + } else { + FixedDelay(100); // 100 us delay + } + +#if USB_RUNTIME_DRIVER_IN_SMM + // + // Register the USB HW SMI handler + // + if (!(fpHCStruc->dHCFlag & HC_STATE_EXTERNAL)) { + UsbInstallHwSmiHandler(fpHCStruc); + } else { + USBSB_InstallUsbIntTimerHandler(); + } +#endif + + return USB_SUCCESS; + +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIGetLegacySupportOffset +// +// Description: This function returns the PCI register offset for the legacy +// support in EHCI controller +// +// Input: fpHCStruc - HCStruc pointer +// wPciAddr - PCI address of the EHCI host controller +// +// Output: 0 If the feature is not present +// <>0 Legacy support capability offset +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCIGetLegacySupportOffset( + HC_STRUC* fpHCStruc, + UINT16 wPciAddr) +{ + UINT8 bData = 0; + UINT32 dData = 0; + +#if USB_RUNTIME_DRIVER_IN_SMM + + if (fpHCStruc->dHCFlag & HC_STATE_EXTERNAL) { + return 0; + } + + // + // Get EHCI Extended Capabilities Pointer + // + bData = (UINT8)((fpHCStruc->dHCCParams >> 8) & 0xFF); + + if (!bData) + { + return 0; // No extended capabilities are implemented. + } + + dData = EhciReadPciReg(fpHCStruc, bData); + if (!((UINT8)dData & 1)) { + return 0; + } +#endif + return bData; + +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIProgramLegacyRegisters +// +// Description: This function programs the EHCI legacy registers as per the +// input. Also this routine returns the PCI register offset +// for the legacy support in EHCI controller +// +// Input: fpHCStruc HCStruc pointer +// bSetReset: +// 0 Reset HC BIOS owned bit +// 1 Set HC BIOS owned bit +// +// Output: 0 If the feature is not present +// <>0 Legacy support capability offset +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCIProgramLegacyRegisters( + HC_STRUC* fpHCStruc, + UINT8 bSetReset) +{ + UINT32 dTemp; + + // + // Check whether EHCI extended capabilities pointer is present + // + if (!fpHCStruc->bExtCapPtr) + { + return 0; // No extended capabilities are implemented. + } + + // + // Program 'HC BIOS owned semaphore bit' + // + dTemp = EhciReadPciReg(fpHCStruc, fpHCStruc->bExtCapPtr); + dTemp &= ~BIT16; + + if (bSetReset) + { + dTemp |= BIT16; + } + + // (USB_S4_RESUME_ISSUE, EIP#20084)> + if (gUsbData->dUSBStateFlag & USB_FLAG_RUNNING_UNDER_EFI) + dTemp &= ~BIT24; + // <(USB_S4_RESUME_ISSUE, EIP#20084) + + EhciWritePciReg(fpHCStruc, fpHCStruc->bExtCapPtr, dTemp); + + // + // Reset all enable bits and clear the status + // + dTemp = 0xE0000000 | EHCI_OWNERSHIP_CHANGE_SMI; + + EhciWritePciReg(fpHCStruc, + fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, + dTemp); + + return fpHCStruc->bExtCapPtr; + +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: ClearEECPstatus +// +// Description: This procedure clear EHCI legacy support status. +// +// Input: fpHCStruc - HCStruc pointer +// wSTatus - Legacy status to clear +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> +VOID +ClearEECPstatus( + HC_STRUC* fpHCStruc, + UINT16 wStatus) +{ + UINT32 dTemp; + + if (!fpHCStruc->bExtCapPtr) + { + return; // No extended capabilities are implemented. + } + + // + // Read control and status register + // + dTemp = EhciReadPciReg(fpHCStruc, + fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG); + + // + // Keep enable bits and set clear status bit + // + dTemp = (dTemp & 0xFFFF) | ((UINT32)wStatus << 16); + EhciWritePciReg( + fpHCStruc, + fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, + dTemp); +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: GetEhciUSBLEGSUP +// +// Description: This routine return USBLEGSUP register content. It could be +// used to check EHCI semaphore owened by BIOS or OS. +// +// Input: fpHCStruc HCStruc pointer +// +// Output: UINT32 Legacy support extended capability register content. +// -1 if no extended capabilities are implemented. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT32 +GetEhciUSBLEGSUP(HC_STRUC* fpHCStruc) +{ + UINT32 dTemp; + + // + // Check whether EHCI extended capabilities pointer is present + // + if (!fpHCStruc->bExtCapPtr) + { + return 0xFFFFFFFF; // No extended capabilities are implemented. + } + + // + // Read Legacy support register + // + dTemp = EhciReadPciReg( + fpHCStruc, + fpHCStruc->bExtCapPtr); + + return dTemp; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCI_EnumeratePorts +// +// Description: This function enumerates the HC ports for devices +// +// Input: fpHCStruc Host controller's HCStruc structure +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_EnumeratePorts(HC_STRUC* fpHCStruc) +{ + UINT16 wPortCtl = EHCI_PORTSC; // Port Status and Control Register (44h) + UINT8 bHCNumber; + UINT8 bPortNum; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + if (EhciIsHalted(fpHCStruc)) { + return USB_ERROR; + } + + bHCNumber = (UINT8)(fpHCStruc->bHCNumber | BIT7); + + + // + // Enable port power so that new devices can be detected. + // + // Check whether enumeration flag is set by us or by somebody else by checking + // local enum flag. + // + if (gUsbData->bEnumFlag == FALSE) + { + gUsbData->bIgnoreConnectStsChng = TRUE; + gUsbData->bEnumFlag = TRUE; + //(EIP122174+)> + do { + // + // Clear the EHCI_PCD bit of the interrupt status register EHCI_USBSTS + // + EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EHCI_PORT_CHANGE_DETECT); + + // + // Check the root hub ports to see if a device is connected. If so, then + // call USBCheckPortChange to handle the attachment of a new device. + // + for ( bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) { + // + // Skip DebugPort enumeration + // + if (fpHCStruc->DebugPort && fpHCStruc->DebugPort==bPortNum) continue; + + // + // Process device connect/disconnect + // + USBCheckPortChange(fpHCStruc, bHCNumber, bPortNum); + } + } while ((EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_PORT_CHANGE_DETECT)); + //<(EIP122174+) + gUsbData->bIgnoreConnectStsChng = FALSE; + + // + // Reset enumeration flag and enable hub enumeration + // + gUsbData->bEnumFlag = FALSE; + } + + // + // Enable appropriate interrupts + // + EhciWriteOpReg(fpHCStruc, EHCI_USBINTR, EHCI_USBINT_EN | EHCI_PCDINT_EN); + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCICheckHCStatus +// +// Description: This function checks whether the host controller is still +// under BIOS +// +// Input: fpHCStruc - Host controller's HCStruc structure +// +// Output: USB_SUCCESS - If the control is with the BIOS +// USB_ERROR - If the control is not with the BIOS +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCICheckHCStatus( + HC_STRUC* HcStruc +) +{ + UINT32 Cmd; + UINT32 Sts; + + Cmd = EhciReadOpReg(HcStruc, EHCI_USBCMD); + Sts = EhciReadOpReg(HcStruc, EHCI_USBSTS); + + // Don't read Periodic Frame List Base Address Register if the controller + // doesn't support periodic schedule. + if (Cmd & EHCI_PER_SCHED_ENABLE) { + if (!(Sts & EHCI_PER_SCHED_STATUS)) { + return USB_SUCCESS; + } + } + // + // Check whether the controller is still under BIOS control + // Read the base address of the Periodic Frame List to the PERIODIC BASE + // register and compare with stored value + // + if ((UINTN)HcStruc->fpFrameList == + (EhciReadOpReg(HcStruc, EHCI_PERIODICLISTBASE) & 0xFFFFF000)) + { + return USB_SUCCESS; // Control is with BIOS + } + return USB_ERROR; // HC is controlled by someone else +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIStop +// +// Description: This function stops the EHCI controller. +// +// Input: fpHCStruc Host controller's HCStruc structure +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_Stop (HC_STRUC* fpHCStruc) +{ + UINT8 bPortNum; //(EIP26685+) + UINT8 Status; + UINT8 i; //(EIP55960+) + EHCI_DESC_PTRS *DescPtr; + EFI_STATUS EfiStatus; + UINT8 *MemBlockEnd = gUsbData->fpMemBlockStart + (gUsbData->MemPages << 12); + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + + if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + DescPtr = fpHCStruc->stDescPtrs.fpEHCIDescPtrs; + +#if USB_RUNTIME_DRIVER_IN_SMM + if (((UINT8*)DescPtr < gUsbData->fpMemBlockStart) || + ((UINT8*)(DescPtr + sizeof(EHCI_DESC_PTRS)) > MemBlockEnd)) { + return USB_ERROR; + } +#endif + // + // Check whether the control is with BIOS or not + // + if (EHCICheckHCStatus(fpHCStruc) == USB_SUCCESS) // Controlled by BIOS + { +#if PCH_EHCI_OWNERSHIP_CHANGE_MECHANISM + if (fpHCStruc->dHCFlag & HC_STATE_OWNERSHIP_CHANGE_IN_PROGRESS) { + UINT16 PortReg; + UINT32 PortSts; + UINT32 Data32; + + // Disconnect all the devices connected to its ports + for (bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) { + USB_StopDevice(fpHCStruc, (UINT8)(fpHCStruc->bHCNumber | BIT7), bPortNum); + } + + // Stop the asynchronous schedule + EHCIStopAsyncSchedule(fpHCStruc); + + // Stop the periodic schedule + EHCIStopPeriodicSchedule(fpHCStruc); + + for (bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) { + PortReg = (UINT16)((bPortNum-1)*4 + EHCI_PORTSC); + PortSts = EhciReadOpReg(fpHCStruc, PortReg); + + if (!(PortSts & EHCI_PORTENABLE)) { + continue; + } + EhciWriteOpReg(fpHCStruc, PortReg, PortSts | EHCI_SUSPEND); + } + FixedDelay(250); // 250 us delay + + // Stop the host controller (Reset bit 0) + EhciClearOpReg(fpHCStruc, EHCI_USBCMD, EHCI_RUNSTOP); + + // The Host Controller must halt within 16 micro-frames after + // software clears the Run bit. + for (i = 0; i < 16; i++) { + if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) { + break; + } + FixedDelay(125); // 125 us delay + } + + // Clear the SMI enable bits + if (fpHCStruc->bExtCapPtr) { + Data32 = EhciReadPciReg(fpHCStruc, + fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG); + + EhciWritePciReg(fpHCStruc, + fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, Data32 & ~(0x3F)); + } + + // Clear the USBSTS register bits + EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EhciReadOpReg(fpHCStruc, EHCI_USBSTS)); + + // Clear the Configure Flag bit + EhciClearOpReg(fpHCStruc, EHCI_CONFIGFLAG, BIT0); + } else +#endif + { + //(EIP26685+)> + // + // Disconnect all the devices connected to its ports + // + for (bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) + { + USB_DisconnectDevice(fpHCStruc, + (UINT8)(fpHCStruc->bHCNumber | BIT7), bPortNum); + } + //<(EIP26685+) + + if (fpHCStruc->DebugPort == 0) { + // + // Stop the host controller (Reset bit 0) + // + EhciClearOpReg(fpHCStruc, EHCI_USBCMD, EHCI_RUNSTOP); + //(EIP55960)> + // The Host Controller must halt within 16 micro-frames after + // software clears the Run bit. + for (i = 0; i < 16; i++) { + if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) { + break; + } + FixedDelay(125); // 125 us delay + } + //<(EIP55960) + // + // Reset the host controller + // +// EIP#23479 EHCIResetHC(fpHCStruc); // ERROR CONDITION RETURNED IS NOT TAKEN CARE + Status = EHCIResetHC(fpHCStruc); + ASSERT(Status == USB_SUCCESS); + } + } + // + // Program the HC BIOS owned bit and return the legacy + // support register offset + // + EHCIProgramLegacyRegisters(fpHCStruc, 0); // Reset HC BIOS owned semaphore + // ERROR CONDITION IS NOT HANDLED + + // + // Clear the frame list pointers + // + USB_InitFrameList (fpHCStruc, EHCI_TERMINATE); + + // + // Disable TD schedule and free the data structures + // + if (DescPtr->fpQHRepeat) { + EhciMemFree(fpHCStruc, DescPtr->fpQHRepeat, + GET_MEM_BLK_COUNT(sizeof(EHCI_QH) + sizeof(EHCI_QTD) )); + } + + // + // Free the scheduling QHs + // + EhciMemFree(fpHCStruc, DescPtr->PeriodicQh, + GET_MEM_BLK_COUNT(1 * sizeof(EHCI_QH))); + + // + // Free descriptor structure + // + EhciMemFree(fpHCStruc, DescPtr, + GET_MEM_BLK_COUNT_STRUC(EHCI_DESC_PTRS)); + +//#if EHCI_ASYNC_BELL_SUPPORT + // + // Free the Async transfer QH + // +// EhciMemFree(fpHCStruc, gUsbData->fpQHAsyncXfer, GET_MEM_BLK_COUNT_STRUC(EHCI_QH)); +//#endif + + USBKeyRepeat(fpHCStruc, 3); + } + else // not controlled by BIOS + { + // + // Program the HC BIOS owned bit and return the legacy + // support register offset + // + EHCIProgramLegacyRegisters(fpHCStruc, 0); // Reset HC BIOS owned semaphore + } + + // + // Set the HC state to stopped + // + fpHCStruc->dHCFlag &= ~(HC_STATE_RUNNING); + + CheckBiosOwnedHc(); + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCI_DisableInterrupts +// +// Description: This function disables the HC interrupts +// +// Input: fpHCStruc Pointer to the HCStruc structure +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_DisableInterrupts (HC_STRUC* fpHCStruc) +{ + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + // + // Disable interrupt generation + // + EhciClearOpReg(fpHCStruc, EHCI_USBINTR, EHCI_USBINT_EN | EHCI_PCDINT_EN); + + // + // Stop periodic and asynchoronous schedule + // + EHCIStopAsyncSchedule(fpHCStruc); + EHCIStopPeriodicSchedule(fpHCStruc); + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCI_EnableInterrupts +// +// Description: This function enables the HC interrupts +// +// Input: fpHCStruc Pointer to the HCStruc structure +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_EnableInterrupts (HC_STRUC* fpHCStruc) +{ + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + // + // Start periodic and asynchoronous schedule + // + EHCIStartAsyncSchedule(fpHCStruc); + EHCIStartPeriodicSchedule(fpHCStruc); + + // + // Enable interrupt generation + // + EhciSetOpReg(fpHCStruc, EHCI_USBINTR, EHCI_USBINT_EN | EHCI_PCDINT_EN); + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: ProcessRootHubChanges +// +// Description: Root hub change processing code +// +// Parameters: fpHCStruc Pointer to the HCStruc structure +// +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +ProcessRootHubChanges( + HC_STRUC* fpHCStruc +) +{ + UINT8 bPortNum; + + // + // Check bEnumFlag before enumerating devices behind root hub + // + if ((gUsbData->bEnumFlag) == TRUE) { + return USB_ERROR; + } + + // + // Clear the port change bit of the interrupt status register EHCI_USBSTS + // + EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EHCI_PORT_CHANGE_DETECT); + + // + // Check all the ports on the root hub for any change in connect status. + // If the connect status has been changed on either or both of these ports, + // then call the routine UsbHubPortChange for each changed port. + // + // Set enumeration flag and avoid hub port enumeration + // + gUsbData->bEnumFlag = TRUE; + + for (bPortNum = 1; bPortNum <= fpHCStruc->bNumPorts; bPortNum++) { + if (fpHCStruc->DebugPort && fpHCStruc->DebugPort==bPortNum) continue; + // + // Process device connect/disconnect + // Note: port connect status is cleared while processing + // connect/disconnect (EHCIGetRootHubStatus) + // + USBCheckPortChange(fpHCStruc, (UINT8)(fpHCStruc->bHCNumber | BIT7), bPortNum); + } + + // + // Reset enumeration flag and enable hub enumeration + // + gUsbData->bEnumFlag = FALSE; + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: EHCI_ProcessInterrupt +// +// Description: This function is called when the USB interrupt bit is +// set. This function will parse through the TDs and QHs to +// find out completed TDs and call their respective call +// back functions +// +// Parameters: fpHCStruc Pointer to the HCStruc structure +// +// Output: USB_ERROR - Need more Interrupt processing +// USB_SUCCESS - No interrupts pending +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_ProcessInterrupt(HC_STRUC* fpHCStruc) +{ + UINT32 dSts, dTmp; + UINT16 wStatus; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + //(EIP71067-)> +//#if (EHCI_ASYNC_BELL_SUPPORT==0) +// EHCI_QH *fpQH; +//#endif + //<(EIP71067-) + // + // If EHCI extended capabilities pointer is present, + // then service OwnerShipChange SMI + // + if (fpHCStruc->bExtCapPtr) { + // + // Read control and status register + // + dTmp = EhciReadPciReg( + fpHCStruc, + fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG); + wStatus = (UINT16)dTmp; + wStatus &= (UINT16)(dTmp >> 16); // "And" enable and status bits + if (wStatus & EHCI_OWNERSHIP_CHANGE_SMI_STS) { + ClearEECPstatus(fpHCStruc, wStatus); + ProcessOwnerShipChangeSMI(fpHCStruc); + return USB_SUCCESS; // Break from Interrupt process loop + } + } + + // + // Check whether the controller is still under BIOS control + // Read the base address of the Periodic Frame List to the PERIODIC BASE + // register and compare with stored value + // + if (EHCICheckHCStatus(fpHCStruc) == USB_ERROR) { + // + // Control is not with us anymore, we should disable SMI generation + // and come out. + // + if (fpHCStruc->bExtCapPtr) { + // + // Read control and status register + // + dTmp = EhciReadPciReg( + fpHCStruc, + fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG); + + // + // Leave only Ownership SMI active. + // + dTmp &= 0xE0002000; + EhciWritePciReg( + fpHCStruc, + fpHCStruc->bExtCapPtr + EHCI_LEGACY_CTRL_STS_REG, + dTmp); + } + return USB_SUCCESS; + } + + if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_SUCCESS; + } + + while(TRUE){ + // + // Get the interrupt status + // + dSts = EhciReadOpReg(fpHCStruc, EHCI_USBSTS); + + //USB_DEBUG(DEBUG_LEVEL_3, "-->> %x <<--\n", dSts); + + if (dSts & EHCI_HOST_SYSTEM_ERROR) { + gUsbData->dUSBStateFlag &= (~USB_FLAG_ENABLE_BEEP_MESSAGE); + EHCI_Start(fpHCStruc); + EHCI_EnumeratePorts(fpHCStruc); + gUsbData->dUSBStateFlag |= USB_FLAG_ENABLE_BEEP_MESSAGE; + continue; + } + + if (dSts & EHCI_HCHALTED) { + // Clear the USBSTS register bits + EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EhciReadOpReg(fpHCStruc, EHCI_USBSTS)); + break; + } + + // + // Check for transaction complete + // + if ((gUsbData->ProcessingPeriodicList == TRUE) && (dSts & EHCI_USB_INTERRUPT)) { + + // + // Clear the interrupt status + // + EhciWriteOpReg(fpHCStruc, EHCI_USBSTS, EHCI_USB_INTERRUPT); + + //Section 4.4 Schedule traversal rules. + //if the periodic schedule is enabled (see Section 4.6) then the host controller must + //execute from the periodic schedule before executing from the asynchronous schedule. + //It will only execute from the asynchronous schedule after it encounters the end of + //the periodic schedule. + + // + // Check and process periodic schedule + // + if (dSts & EHCI_PER_SCHED_STATUS) { + // + // Check the command register for Async status + // + dTmp = EhciReadOpReg(fpHCStruc, EHCI_USBCMD); + + if (dTmp & EHCI_PER_SCHED_ENABLE) { + EHCIProcessPeriodicList(fpHCStruc); + } + } + + // + // Check for Asynchronous schedule completion + // +/* //(EIP71067-)> + if (dSts & EHCI_ASYNC_SCHED_STATUS) { + dTmp = DwordReadMem(dMemAddr, EHCI_USBCMD); + if (dTmp & EHCI_ASYNC_SCHED_ENABLE) { + // + // Check and process Async. QH + // +#if EHCI_ASYNC_BELL_SUPPORT + EHCIProcessQH(fpHCStruc, fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpQHControl); + EHCIProcessQH(fpHCStruc, fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpQHBulk); +#else + // + // Get the Async list address + // + fpQH = (EHCI_QH*)(UINTN)DwordReadMem(dMemAddr, EHCI_ASYNCLISTADDR); + if (EHCIProcessQH(fpHCStruc, fpQH) == USB_ERROR) { + //continue; + //return USB_SUCCESS; + } else { + // + // Async list processed; stop the Async transfer + // + EHCIStopAsyncSchedule(fpHCStruc); + } +#endif + } + } +*/ //<(EIP71067-) + continue; + } + + // + // Check PORT_CHANGE_DETECT bit + // + if ((dSts & EHCI_PORT_CHANGE_DETECT )) { + if(ProcessRootHubChanges(fpHCStruc) == USB_SUCCESS) { + continue; + } + } + break; // No more statuses to process + } + return USB_SUCCESS; // Set as interrupt processed +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: ProcessOwnerShipChangeSMI +// +// Description: This procedure process EHCI OwnerShipChange SMI. +// +// Input: fpHCStruc HCStruc pointer +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> +VOID +ProcessOwnerShipChangeSMI(HC_STRUC* fpHCStruc) +{ + UINT32 dTemp = GetEhciUSBLEGSUP(fpHCStruc); + fpHCStruc->dHCFlag |= HC_STATE_OWNERSHIP_CHANGE_IN_PROGRESS; + if (dTemp & EHCI_HC_OS) { + gUsbData->dUSBStateFlag &= (~USB_FLAG_ENABLE_BEEP_MESSAGE); + ProcessSmiChangeToEHCD(fpHCStruc); + } + else { + gUsbData->dUSBStateFlag |= USB_FLAG_ENABLE_BEEP_MESSAGE; + ProcessSmiChangeToBIOS(fpHCStruc); + } + fpHCStruc->dHCFlag &= ~(HC_STATE_OWNERSHIP_CHANGE_IN_PROGRESS); +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: ProcessSmiChangeToEHCD +// +// Description: This procedure process OwnerShipChange for BIOS -> EHCD. +// +// Input: fpHCStruc HCStruc pointer +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> +VOID +ProcessSmiChangeToEHCD (HC_STRUC* fpHCStruc) +{ + EHCI_Stop(fpHCStruc); // Stop EHCI legacy +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: ProcessSmiChangeToBIOS +// +// Description: This procedure process OwnerShipChange for EHCD -> BIOS. +// +// Input: SI HCStruc pointer +// +// Output: None +// +// Modified: None +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +ProcessSmiChangeToBIOS ( + HC_STRUC *fpHCStruc +) +{ + HC_STRUC *Hc; + UINT8 Count; + DEV_INFO *Device; + + // Stop UHCI devices connected to the companions + // Core8 executes this under MKF_PCCHECK_PATCH==1 condition, EIP10272 + for (Count = 1; Count < MAX_DEVICES; Count++) { + Device = &gUsbData->aDevInfoTable[Count]; + Hc = gUsbData->HcTable[Device->bHCNumber - 1]; + + if (Hc->bHCType != USB_HC_UHCI) continue; // Not UHCI + + if ((Device->Flag & DEV_INFO_VALID_STRUC)==0) continue; // Not valid + + if ((Hc->wBusDevFuncNum & 0xfff8) != + (fpHCStruc->wBusDevFuncNum & 0xfff8)) continue; // Not a companion + + // Found a device connected to UHCI companion controller. Stop it. + USB_StopDevice(Hc, Device->bHubDeviceNumber, Device->bHubPortNumber); + } + + EHCI_Start(fpHCStruc); // Reinitialize EHCI host controller +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCI_ReleasePortOwner +// +// Description: +// +// Input: HcStruc - Pointer to HCStruc of the host controller +// PortNum - Port in the HC whose status is requested +// +// Output: +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_ReleasePortOwner( + HC_STRUC* HcStruc, + UINT8 PortNum +) +{ + UINT16 PortReg = (UINT16)((PortNum-1)*4 + EHCI_PORTSC); + UINT16 i; + + if ((HcStruc->dHCSParams & EHCI_N_CC) == 0) { + return USB_SUCCESS; + } + + if (!(EhciReadOpReg(HcStruc, PortReg) & EHCI_CURRENTCONNECTSTATUS)) { + return USB_ERROR; + } + + USB_DEBUG(DEBUG_LEVEL_3, "Release EHCI port %d\n", PortNum); + EhciSetOpReg(HcStruc, PortReg, EHCI_PORTOWNER); + + // Loop until Full speed device disconnect event process done. + // This change is done in sync with Core8 except the extra 400mS delay + for (i = 0; i < 200; i++) { + if (EhciReadOpReg(HcStruc, PortReg) & EHCI_CONNECTSTATUSCHANGE) { + break; + } + FixedDelay(100); + } + + EhciSetOpReg(HcStruc, PortReg, EHCI_CONNECTSTATUSCHANGE); + + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIGetRootHubStatus +// +// Description: This function returns the port connect status for the +// root hub port +// +// Input: fpHCStruc - Pointer to HCStruc of the host controller +// bPortNum - Port in the HC whose status is requested +// +// Output: Port status flags (see USB_PORT_STAT_XX equates) +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_GetRootHubStatus( + HC_STRUC* fpHCStruc, + UINT8 bPortNum, + BOOLEAN ClearChangeBits +) +{ + UINT32 dTmp; + UINT8 bStatus = USB_PORT_STAT_DEV_OWNER; + UINT16 wPortReg = (UINT16)((bPortNum-1)*4 + EHCI_PORTSC); + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (fpHCStruc->DebugPort && fpHCStruc->DebugPort==bPortNum) return 0; + + // + // Read the status of the port + // + dTmp = EhciReadOpReg(fpHCStruc, wPortReg); + USB_DEBUG(3, "Ehci port[%d] status: %08x\n", bPortNum, dTmp); + + // Detect the high-speed device. + // In case of low-speed or full-speed change the ownership to a + // companion 1.1 controller (if any) + if (dTmp & EHCI_CURRENTCONNECTSTATUS) { + // Analyze Line Status + if ((dTmp & EHCI_LINE_STATUS) == EHCI_DMINUSBIT) { // Low speed device connected + EHCI_ReleasePortOwner(fpHCStruc, bPortNum); + dTmp = EhciReadOpReg(fpHCStruc, wPortReg); + } + } + + // + // Check the connect status change bit + // + if (dTmp & EHCI_CONNECTSTATUSCHANGE) { + // + // Set connect status change flag + // + bStatus |= USB_PORT_STAT_DEV_CONNECT_CHANGED; + + // + // Wait 20ms for host controller could report accurate port status properly. + // + //FixedDelay(gUsbData->UsbTimingPolicy.EhciPortConnect * 1000); // 20ms delay + + // + // Read the status of the port + // + //dTmp = EhciReadOpReg(fpHCStruc, wPortReg); + + // Clear connect status change + if (ClearChangeBits == TRUE) { + EhciSetOpReg(fpHCStruc, wPortReg, EHCI_CONNECTSTATUSCHANGE); //(EIP61030+) + } + } + + if (dTmp & EHCI_CURRENTCONNECTSTATUS) { + bStatus |= USB_PORT_STAT_DEV_CONNECTED; + + if (dTmp & EHCI_PORTENABLE) { + bStatus |= USB_PORT_STAT_DEV_HISPEED; + + // Patch for CloverTrail + if (fpHCStruc->Vid == 0x8086 && + (fpHCStruc->Did == 0xE006 || fpHCStruc->Did == 0x08F2)) { + if ((dTmp & EHCI_LINE_STATUS) == EHCI_DMINUSBIT) { + bStatus &= ~USB_PORT_STAT_DEV_HISPEED; + bStatus |= USB_PORT_STAT_DEV_LOWSPEED; + } else if ((dTmp & EHCI_LINE_STATUS) == EHCI_DPLUSBIT) { + bStatus &= ~USB_PORT_STAT_DEV_HISPEED; + bStatus |= USB_PORT_STAT_DEV_FULLSPEED; + } + } + bStatus |= USB_PORT_STAT_DEV_ENABLED; + //(EIP61030+)> + } else { + if (gUsbData->bIgnoreConnectStsChng == TRUE) { + if (!(dTmp & EHCI_CONNECTSTATUSCHANGE)) { + bStatus |= USB_PORT_STAT_DEV_CONNECT_CHANGED; + } + } + //<(EIP61030+) + } + } + + if (dTmp & EHCI_PORTOWNER) { + bStatus &= ~USB_PORT_STAT_DEV_OWNER; + } + + return bStatus; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIDisableRootHub +// +// Description: This function disables the EHCI HC Ruoot hub port. +// +// Input: fpHCStruc - Pointer to HCStruc of the host controller +// bPortNum - Port in the HC to disable +// +// Output: USB_SUCCESS on success +// USB_ERROR on error +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_DisableRootHub( + HC_STRUC* fpHCStruc, + UINT8 bPortNum) +{ + //(EIP58108+)> + UINT16 PortReg = (UINT16)((bPortNum-1)*4 + EHCI_PORTSC); + UINT32 i; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (fpHCStruc->DebugPort && fpHCStruc->DebugPort==bPortNum) return USB_SUCCESS; + + if (!(EhciReadOpReg(fpHCStruc, PortReg) & EHCI_PORTENABLE)) { + return USB_SUCCESS; + } + EhciClearOpReg(fpHCStruc, PortReg, EHCI_PORTENABLE); + + for (i = 0; i < 100; i++) { + if ((EhciReadOpReg(fpHCStruc, PortReg) & EHCI_PORTENABLE) == 0) { + break; + } + FixedDelay(100); + } + //<(EIP58108+) + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIEnableRootHub +// +// Description: This function enables the EHCI HC Root hub port. +// +// Input: fpHCStruc - Pointer to HCStruc of the host controller +// bPortNum - Port in the HC to enable +// +// Output: USB_SUCCESS on success +// USB_ERROR on error +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_EnableRootHub( + HC_STRUC* fpHCStruc, + UINT8 bPortNum) +{ +// +// Software can only enable the EHCI root hub ports by port RESET. HC will +// enable the port only if it is a high speed device +// + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCI_ResetRootHub +// +// Description: This function resets the EHCI HC Root hub port. +// +// Input: HcStruc - Pointer to HCStruc of the host controller +// PortNum - Port in the HC to enable +// +// Output: USB_SUCCESS on success +// USB_ERROR on error +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_ResetRootHub( + HC_STRUC* HcStruc, + UINT8 PortNum +) +{ + UINT16 PortReg = (UINT16)((PortNum-1)*4 + EHCI_PORTSC); + UINT32 i; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + // Disable the port if it is enabled + if (EhciReadOpReg(HcStruc, PortReg) & EHCI_PORTENABLE) { + EhciClearOpReg(HcStruc, PortReg, EHCI_PORTENABLE); + + // There may be a delay in disabling or enabling a port due to other + // host controller and bus events. + for (i = 0; i < 100; i++) { + if ((EhciReadOpReg(HcStruc, PortReg) & EHCI_PORTENABLE) == 0) { + break; + } + FixedDelay(100); // 100 us delay + } + } + + // Reset the port + EhciSetOpReg(HcStruc, PortReg, EHCI_PORTRESET); + + if ((HcStruc->dHCFlag & HC_STATE_CONTROLLER_WITH_RMH) && (PortNum == 1)) { + FixedDelay(3 * 1000); // 3 ms delay + } else { + // Wait til port disable is complete (Tdrstr=50ms Ref 7.1.7.5 of USB Spec 2.0) + FixedDelay(50 * 1000); // 50 ms delay + } + + EhciClearOpReg(HcStruc, PortReg, EHCI_PORTRESET); // Terminate reset + + if (!(EhciReadOpReg(HcStruc, PortReg) & EHCI_CURRENTCONNECTSTATUS)) { + return USB_ERROR; + } + + // if the port detects that the attached device is high-speed during reset, + // then the host controller must have the port in the enabled state within 2ms + // of software writing this bit to a zero. + for (i = 0; i < 20; i++) { + if ((EhciReadOpReg(HcStruc, PortReg) & (EHCI_PORTRESET | + EHCI_PORTENABLE)) == EHCI_PORTENABLE) { + break; + } + FixedDelay(100); // 100 us delay + } + + if (EhciReadOpReg(HcStruc, PortReg) & EHCI_PORTRESET) { // Reset failed + USBLogError(USB_ERR_PORT_RESET_FAILED); + return USB_ERROR; + } + + if (!(EhciReadOpReg(HcStruc, PortReg) & EHCI_PORTENABLE)) { + if (!(EhciReadOpReg(HcStruc, PortReg) & EHCI_CURRENTCONNECTSTATUS)) { + return USB_ERROR; + } else { + EHCI_ReleasePortOwner(HcStruc, PortNum); + return USB_ERROR; + } + } + + FixedDelay(1 * 1000); // 1 ms delay + + return USB_SUCCESS; +} + + //(EIP54018+)> +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Name: EHCI_GlobalSuspend +// +// Description: +// This function suspend the EHCI HC. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_GlobalSuspend( + HC_STRUC* HcStruc +) +{ + UINT16 PortReg; + UINT32 PortSts; + UINT8 PortNum; + UINT8 i; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + if (EhciIsHalted(HcStruc)) { + return USB_ERROR; + } + + for (PortNum = 1; PortNum <= HcStruc->bNumPorts; PortNum++) { + PortReg = (UINT16)(EHCI_PORTSC + (PortNum - 1) * 4 ); + PortSts = EhciReadOpReg(HcStruc, PortReg ); + USB_DEBUG(DEBUG_LEVEL_3,"EHCI PortSts[%x] %x \n", + PortNum, PortSts); + // Check if port is disabled or suspended. + if((PortSts & EHCI_PORTENABLE) && (!(PortSts & EHCI_SUSPEND))) { + // Suspend if necessary. + EhciClearOpReg(HcStruc, PortReg, + EHCI_WKOC_E | EHCI_WKDSCNNT_E | EHCI_WKCNNT_E); + EhciSetOpReg(HcStruc, PortReg, EHCI_SUSPEND); + // Read PortSc until port shows suspended. + for(i = 0; i < 100; i++) { + if(EhciReadOpReg(HcStruc, PortReg) & EHCI_SUSPEND) { + break; + } + FixedDelay(100); // 100 us delay + } + } + } + + // Turn HC off and wait for the Halted bit to get set + EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_RUNSTOP); + // The Host Controller must halt within 16 micro-frames after + // software clears the Run bit. + for (i = 0; i < 16; i++) { + if(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_HCHALTED) { + break; + } + FixedDelay(125); // 125 us delay + } + + HcStruc->dHCFlag &= ~(HC_STATE_RUNNING); + HcStruc->dHCFlag |= HC_STATE_SUSPEND; + + return USB_SUCCESS; +} + //<(EIP54018+) + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCI_GetHiSpeedHubPortNumber +// +// Description: This function gets the hi-speed hub's device and port number +// to which this low speed device is connected. It parses +// through its parents until it finds the correct device. This +// information is used for split transaction +// +// Input: fpDevInfo - Device info pointer of the device +// +// Output: UINT16 - Device/port number of the hi-speed hub +// +// Notes: This low/full speed device may be behind different hubs as +// shown below. In any case this routine will get the device +// address of the hub number HISP_A : +// Notations used: +// MBPortX Motherboard USB port +// HISP_X Hi-speed hub number X +// FUSP_X Full-speed hub number X +// Device Low/Full speed device +// Config 1: +// MBPortX --> HISP_A --> Device +// Config 2: +// MBPortX --> HISP_A --> FUSP_1 --> Device +// Config 3: +// MBPortX --> HISP_B --> HISP_A --> Device +// Config 4: +// MBPortX --> HISP_A --> FUSP_1 --> HISP_B --> Device +// In the above configuration the HISP_B will be operated in +// full speed rather than hi-speed since it is connected to a +// full speed hub +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 +EHCI_GetHiSpeedHubPortNumber(DEV_INFO* fpDevInfo) +{ + DEV_INFO* fpHubDev = fpDevInfo; + DEV_INFO* fpParentHubDev; + UINT16 wRetCode; + + if( !VALID_DEVINFO( fpDevInfo) ) + return 0; + +// +// Get the device info structure for the matching device address +// + // + // Get the device number of the immediate hub, then get the device + // info structure for this device number + // + for(;;) + { + fpParentHubDev = USB_GetDeviceInfoStruc(USB_SRCH_DEV_ADDR, + 0, fpHubDev->bHubDeviceNumber, 0); + if ( !fpParentHubDev ) // Error. Exit ! + { + return 0; + } + if (((fpParentHubDev->bEndpointSpeed << USB_PORT_STAT_DEV_SPEED_MASK_SHIFT) + & USB_PORT_STAT_DEV_SPEED_MASK) == 0) break; + fpHubDev = fpParentHubDev; + } + // + // The first USB 2.0 hub found as fpHubDev to which the low/full speed + // device is connected + // + wRetCode = (UINT16)((fpHubDev->bHubPortNumber << 7) | + fpHubDev->bHubDeviceNumber); + + return wRetCode; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EhciExexuteAsyncSchedule +// +// Description: This function insert the requested QH to asynchronous schedule +// and waits until the QH completes or the transaction time-out. +// +// Input: HcStruc - Pointer to HCStruc of the host controller +// XferQh - Pointer to the QH which has to be completed +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 +EhciExexuteAsyncSchedule( + HC_STRUC *HcStruc, + EHCI_QH *XferQh +) +{ + UINT16 Status = USB_SUCCESS; + UINT32 Count; + UINT32 TimeOut = gUsbData->wTimeOutValue * 100; // in 10 macrosecond unit +// +//#if EHCI_ASYNC_BELL_SUPPORT +// UINT32 Tmp; + +// XferQh->dLinkPointer = EHCI_TERMINATE; +// XferQh->bActive = TRUE; + + // + // Insert the Control/Bulk QH into the Async list + // +// Tmp = gUsbData->fpQHAsyncXfer->dLinkPointer; +// gUsbData->fpQHAsyncXfer->dLinkPointer = (UINT32)XferQh | EHCI_QUEUE_HEAD; +// XferQh->dLinkPointer = Tmp; +//#else + // + // Set the ASYNCLISTADDR register to point to the Control/Bulk QH + // + EhciWriteOpReg(HcStruc, EHCI_ASYNCLISTADDR, (UINT32)(UINTN)XferQh); + + // + // Set next QH pointer to itself (circular link) + // + XferQh->dLinkPointer = (UINT32)((UINTN)XferQh | EHCI_QUEUE_HEAD); + XferQh->bActive = TRUE; +//#endif + + // + // Now put the Control/Bulk QH into the HC's schedule by + // setting the Async. schedule enabled field of USBCMD register + // This will cause the HC to execute the transaction in the next active frame. + // + Status = EHCIStartAsyncSchedule(HcStruc); + + if (Status == USB_ERROR) { + return Status; + } + + // Wait for tansfer complete + for(Count = 0; !TimeOut || Count < TimeOut; Count++) { + EHCIProcessQH(HcStruc, XferQh); + if(XferQh->bActive == FALSE) { + break; + } + FixedDelay(10); // 10 microsec + } + +//#if EHCI_ASYNC_BELL_SUPPORT + // + // Disconnect Control/Bulk QH from the Async list + // +// EHCIRemoveQHFromAsyncList(HcStruc, XferQh); +//#else + // + // Stop the Async transfer + // + EHCIStopAsyncSchedule(HcStruc); +//#endif + + if(XferQh->bActive == TRUE) { + XferQh->bActive = FALSE; + Status = USB_ERROR; + USB_DEBUG (DEBUG_LEVEL_3, "EHCI Time-Out\n"); + } + + // Service all interrupts + EHCI_ProcessInterrupt(HcStruc); + + return Status; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: EHCI_ControlTransfer +// +// Description: This function executes a device request command transaction +// on the USB. One setup packet is generated containing the +// device request parameters supplied by the caller. The setup +// packet may be followed by data in or data out packets +// containing data sent from the host to the device +// or vice-versa. This function will not return until the +// request either completes successfully or completes in error +// (due to time out, etc.) +// +// Parameters: fpHCStruc Pointer to HCStruc of the host controller +// pDevInfo DeviceInfo structure (if available else 0) +// wRequest Request type (low byte) +// Bit 7 : Data direction +// 0 = Host sending data to device +// 1 = Device sending data to host +// Bit 6-5 : Type +// 00 = Standard USB request +// 01 = Class specific +// 10 = Vendor specific +// 11 = Reserved +// Bit 4-0 : Recipient +// 00000 = Device +// 00001 = Interface +// 00010 = Endpoint +// 00100 - 11111 = Reserved +// Request code, a one byte code describing +// the actual device request to be executed +// (ex: Get Configuration, Set Address etc) +// wIndex wIndex request parameter (meaning varies) +// wValue wValue request parameter (meaning varies) +// fpBuffer Buffer containing data to be sent to the +// device or buffer to be used to receive data +// wLength wLength request parameter, number of bytes +// of data to be transferred in or out +// of the host controller +// +// +// Output: Number of bytes actually transferred +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 +EHCI_ControlTransfer ( + HC_STRUC *fpHCStruc, + DEV_INFO *fpDevInfo, + UINT16 wRequest, + UINT16 wIndex, + UINT16 wValue, + UINT8 *fpBuffer, + UINT16 wLength) +{ + UINT32 dTmp, dTmp1; + UINT16 wRetCode = 0; // Initialize with error + EHCI_QH *fpQHCtl; + EHCI_QTD *fpQTDCtlSetup, *fpQTDCtlData, *fpQTDCtlStatus; + DEV_REQ *fpRequest = NULL; + UINT8 bEndpointSpeed; + UINT8 *BufPhyAddr = NULL; + VOID *BufferMapping = NULL; + EFI_STATUS EfiStatus = EFI_SUCCESS; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + + EfiStatus = UsbDevInfoValidation(fpDevInfo); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (gCheckUsbApiParameter) { + if (wLength != 0) { + EfiStatus = AmiValidateMemoryBuffer((VOID*)fpBuffer, wLength); + if (EFI_ERROR(EfiStatus)) { + USB_DEBUG(3, "Ehci ControlTransfer Invalid Pointer, Buffer is in SMRAM.\n"); + return 0; + } + } + gCheckUsbApiParameter = FALSE; + } +#endif + + if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { + return 0; + } + + if (EhciIsHalted(fpHCStruc)) { + return 0; + } + + if( !VALID_DEVINFO( fpDevInfo) ) + return 0; + + gUsbData->dLastCommandStatusExtended = 0; //(EIP84790+) + +//USB_DEBUG(DEBUG_LEVEL_3, "EHCI_ControlTransfer..\n"); + // + // Build the device request in the data area of the control setup qTD + // + fpRequest = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT(sizeof(DEV_REQ))); + ASSERT(fpRequest); + if (fpRequest == NULL) { + return 0; + } + + fpRequest->wRequestType = wRequest; + fpRequest->wIndex = wIndex; + fpRequest->wValue = wValue; + fpRequest->wDataLength = wLength; + +// +// The QH endpoint characteristic field will be set so +// Function address & Endpoint number = From DeviceInfo structure, +// Direction = From TD, +// Speed = DeviceInfo.bEndpointSpeed, +// Skip = 1, Format = 0, +// Max packet size = DeviceInfo.wEndp0MaxPacket +// The dNextqTDPtr field will be set to qTDControlSetup +// The dAltNextqTDPtr field will be set to EHCI_TERMINATE +// The dCurrentqTDPtr field will be set to 0 +// + // + // Intialize the queue head with null pointers + // + //(EIP71067)> + fpQHCtl = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ + (3 * sizeof(EHCI_QTD)))); + //(EIP83295+)> + if(!fpQHCtl) { + return 0; + } + //<(EIP83295+) + //(EIP81030)> + fpQTDCtlSetup = (EHCI_QTD*)((UINTN)fpQHCtl + sizeof(EHCI_QH)); + fpQTDCtlData = (EHCI_QTD*)((UINTN)fpQTDCtlSetup + sizeof(EHCI_QTD)); + fpQTDCtlStatus = (EHCI_QTD*)((UINTN)fpQTDCtlData + sizeof(EHCI_QTD)); + //<(EIP71067) + //<EIP81030) + EHCIInitializeQueueHead(fpQHCtl); + + bEndpointSpeed = fpDevInfo->bEndpointSpeed; // 00/01/10 for HI/LO/FULL + + // + // Assume as a high speed device + // + dTmp = QH_HIGH_SPEED; // 10b - High speed + + // + // Check for high speed + // + if (bEndpointSpeed) // Low/Full speed device + { + dTmp = ((UINT32)bEndpointSpeed & 1) << 12; // Bit 12 = full/low speed flag + dTmp |= QH_CONTROL_ENDPOINT; + // + // Set the hub address and port number + // Get the Hispeed hub port number & device number + // + dTmp1 = (UINT32)EHCI_GetHiSpeedHubPortNumber(fpDevInfo); + dTmp1 = (dTmp1 << 16); // Split complete Xaction + fpQHCtl->dEndPntCap |= dTmp1; + } + +//USB_DEBUG(DEBUG_LEVEL_3, "Tmp1..%x\n", dTmp); + + // + // Use data toggle from qTD and this QH is the head of the queue + // +//#if EHCI_ASYNC_BELL_SUPPORT +// dTmp |= QH_USE_QTD_DT; +//#else + dTmp |= QH_USE_QTD_DT; + // Do not set QH_HEAD_OF_LIST bit on VIA controller + if (fpHCStruc->Vid != 0x1106) { + dTmp |= QH_HEAD_OF_LIST; + } +//#endif + dTmp |= (UINT32)fpDevInfo->bDeviceAddress; + // + // dTmp[Bits 6:0] = Dev. Addr + // dTmp[Bit7] = I bit(0) + // dTmp[Bits11:8] = Endpoint (0) + // + + dTmp1 = (UINT32)fpDevInfo->wEndp0MaxPacket; + dTmp |= (dTmp1 << 16); // Tmp[Bits26:16] = device's packet size + fpQHCtl->dEndPntCharac = dTmp; + + // + // Fill in various fields in the qTDControlSetup. + // + //fpQTDCtlSetup = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpqTDControlSetup; //(EIP71067-) + + // + // The token field will be set so + // Direction PID = QTD_SETUP_TOKEN, + // Size = size of the data, + // Data Toggle = QTD_SETUP_TOGGLE, + // Error Count = QTD_THREE_ERRORS, + // Status code = QTD_DO_OUT + QTD_ACTIVE + // The buffer pointers field will point to the aControlSetupData buffer + // which was before initialized to contain a DeviceRequest struc. + // The dNextqTDPtr field will point to the qTDControlData if data will + // be sent/received or to the qTDControlStatus if no data is expected. + // The dAltNextqTDPtr field will be set to EHCI_TERMINATE + // + fpQTDCtlSetup->dToken = QTD_SETUP_TOKEN | + QTD_SETUP_TOGGLE | QTD_IOC_BIT | + QTD_THREE_ERRORS | QTD_DO_OUT | QTD_ACTIVE | + (8 << 16); // Data size + + // + // Update buffer pointers + // + EHCISetQTDBufferPointers(fpQTDCtlSetup, (UINT8*)fpRequest, 8); + //fpQTDCtlData = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpqTDControlData; //(EIP71067-) + + if (wLength) // br if no data to transfer + { + // + // Fill in various fields in the qTDControlData + // + // The token field will be set so + // Direction PID = QTD_OUT_TOKEN/QTD_IN_TOKEN, + // Size = size of the data, + // Data Toggle = QTD_DATA1_TOGGLE, + // Error Count = QTD_THREE_ERRORS, + // Status code = QTD_DO_OUT(if it is out) + QTD_ACTIVE + // The buffer pointers field will point to the fpBuffer buffer + // which was before initialized to contain a DeviceRequest struc. + // The dNextqTDPtr field will point to the qTDControlSetup + // The dAltNextqTDPtr field will be set to EHCI_TERMINATE + // + fpQTDCtlData->dToken = QTD_IN_TOKEN | + QTD_DATA1_TOGGLE | QTD_IOC_BIT | + QTD_THREE_ERRORS | QTD_ACTIVE; + if (!(wRequest & BIT7)) // Br if host sending data to device (OUT) + { + fpQTDCtlData->dToken = QTD_OUT_TOKEN | + QTD_DATA1_TOGGLE | QTD_IOC_BIT | + QTD_THREE_ERRORS | QTD_DO_OUT | QTD_ACTIVE; + } + + // + // Set length + // + fpQTDCtlData->dToken |= ((UINT32)wLength << 16); + + EhciDmaMap(fpHCStruc, (UINT8)(wRequest & BIT7), fpBuffer, wLength, + &BufPhyAddr, &BufferMapping); + + // + // Update buffer pointers + // + EHCISetQTDBufferPointers(fpQTDCtlData, + (UINT8*)BufPhyAddr, + (UINT32)wLength); + } + + // + // Fill in various fields in the qTDControlStatus + // + //fpQTDCtlStatus = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpqTDControlStatus; //(EIP71067-) + + // + // The token field will be set so + // Direction PID = QTD_OUT_TOKEN/QTD_IN_TOKEN, + // Size = 0, + // Data Toggle = QTD_DATA1_TOGGLE, + // Error Count = QTD_THREE_ERRORS, + // Status code = QTD_DO_OUT(if it is out) + QTD_ACTIVE + // The buffer pointers field will be 0 + // The dNextqTDPtr field will set to EHCI_TERMINATE + // The dAltNextqTDPtr field will be set to EHCI_TERMINATE + // + // For OUT control transfer status should be IN and + // for IN cotrol transfer, status should be OUT + // + fpQTDCtlStatus->dToken = QTD_IN_TOKEN | + QTD_DATA1_TOGGLE | QTD_IOC_BIT | + QTD_THREE_ERRORS | QTD_ACTIVE; + if(wRequest & BIT7) + { + fpQTDCtlStatus->dToken = QTD_OUT_TOKEN | + QTD_DATA1_TOGGLE | QTD_IOC_BIT | + QTD_THREE_ERRORS | QTD_DO_OUT | QTD_ACTIVE; + } + + EHCISetQTDBufferPointers(fpQTDCtlStatus, NULL, 0); + + // + // Link the qTD formed now and connect them with the control queue head + // + fpQHCtl->fpFirstqTD = fpQTDCtlSetup; + fpQHCtl->dNextqTDPtr = (UINT32)(UINTN)fpQTDCtlSetup; + + if(wLength) + { + fpQTDCtlSetup->dNextqTDPtr = (UINT32)(UINTN)fpQTDCtlData; + fpQTDCtlData->dNextqTDPtr = (UINT32)(UINTN)fpQTDCtlStatus; + } + else + { + fpQTDCtlSetup->dNextqTDPtr = (UINT32)(UINTN)fpQTDCtlStatus; + } + + fpQTDCtlStatus->dNextqTDPtr = EHCI_TERMINATE; + + wRetCode = EhciExexuteAsyncSchedule(fpHCStruc, fpQHCtl); + fpQHCtl->fpFirstqTD = 0; + fpQHCtl->dNextqTDPtr = EHCI_TERMINATE; + + if (wLength) { + EhciDmaUnmap(fpHCStruc, BufferMapping); + wLength = wLength - (UINT16)((fpQTDCtlData->dToken & ~(QTD_DATA_TOGGLE)) >> 16); + } + + // + // Clear the stalled condition flag + // + gUsbData->bLastCommandStatus &= ~USB_CONTROL_STALLED; + + // + // Check whether the QH stopped or timed out + // + if (wRetCode != USB_SUCCESS) { + gUsbData->dLastCommandStatusExtended |= USB_TRNSFR_TIMEOUT; //(EIP84790+) + wLength = 0; //(EIP71067) + } + + if (fpQHCtl->bErrorStatus & QTD_HALTED) { + // + // Command stalled set the error bit appropriately + // + gUsbData->bLastCommandStatus |= USB_CONTROL_STALLED; + wLength = 0; //(EIP71067) + } + //(EIP71067+)> + EhciMemFree(fpHCStruc, fpQHCtl, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ + (3 * sizeof(EHCI_QTD)))); + //<(EIP71067+) + EhciMemFree(fpHCStruc, fpRequest, GET_MEM_BLK_COUNT(sizeof(DEV_REQ))); + + return wLength; +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: EHCI_BulkTransfer +// +// Description: This function executes a bulk transaction on the USB. The +// transfer may be either DATA_IN or DATA_OUT packets containing +// data sent from the host to the device or vice-versa. This +// function wil not return until the request either completes +// successfully or completes with error (due to time out, etc.) +// NOTE: Make sure that amount of bytes to transfer should not +// exceed MAX_EHCI_DATA_SIZE +// +// Parameters: pHCStruc Pointer to HCStruc of the host controller +// pDevInfo DeviceInfo structure (if available else 0) +// bXferDir Transfer direction +// Bit 7: Data direction +// 0 Host sending data to device +// 1 Device sending data to host +// Bit 6-0 : Reserved +// fpBuffer Buffer containing data to be sent to the +// device or buffer to be used to receive data +// value in Segment:Offset format +// dwLength dwLength request parameter, number of bytes +// of data to be transferred in or out +// of the host controller +// +// Output: Amount of data transferred +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT32 +EHCI_BulkTransfer( + HC_STRUC *fpHCStruc, + DEV_INFO *fpDevInfo, + UINT8 bXferDir, + UINT8 *fpBuffer, + UINT32 dwLength) +{ + UINT16 wMaxPkt; + UINT8 bEndp, bDatToggle; + EHCI_QH *fpQHBulk; + EHCI_QTD *fpQTDBulkData; + UINT32 dTmp, dTmp1; + UINT16 Status; + UINT32 dBytesToTransfer, dBytesRemaining; + UINT32 dBytesTransferred; + UINT8 *BufPhyAddr = NULL; + VOID *BufferMapping = NULL; + UINT8 *TempBuffer = NULL; + EFI_STATUS EfiStatus = EFI_SUCCESS; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + + EfiStatus = UsbDevInfoValidation(fpDevInfo); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (gCheckUsbApiParameter) { + EfiStatus = AmiValidateMemoryBuffer((VOID*)fpBuffer, dwLength); + if (EFI_ERROR(EfiStatus)) { + USB_DEBUG(3, "Ehci BulkTransfer Invalid Pointer, Buffer is in SMRAM.\n"); + return 0; + } + gCheckUsbApiParameter = FALSE; + } +#endif + + if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { + return 0; + } + + if (EhciIsHalted(fpHCStruc)) { + return 0; + } + + if (!VALID_DEVINFO(fpDevInfo)) { + return 0; + } + + // Realtek 8111EP EHCI controller workaround + // The controller doesn't work if the buffer address isn't DWORD alignment + // (current offset of qTD). Provide the workaround to locate DWORD alignment buffer. + + if ((fpHCStruc->Vid == 0x10EC) && (fpHCStruc->Did == 0x816D)) { + if ((UINTN)fpBuffer & (BIT0 | BIT1)) { + if (dwLength < HIGHSPEED_MAX_BULK_DATA_SIZE) { + TempBuffer = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((UINT16)dwLength)); + } else { + TempBuffer = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT(HIGHSPEED_MAX_BULK_DATA_SIZE)); + } + } + } + + //clear HW source of error + gUsbData->dLastCommandStatusExtended = 0; + + + dBytesRemaining = dwLength; + dBytesTransferred = 0; + + // + // Get Bulk IN/OUT enpoint number, data sync value & max packet size + // + if (bXferDir & BIT7) + { + wMaxPkt = fpDevInfo->wBulkInMaxPkt; + bEndp = fpDevInfo->bBulkInEndpoint; + } + else + { + wMaxPkt = fpDevInfo->wBulkOutMaxPkt; + bEndp = fpDevInfo->bBulkOutEndpoint; + } + if( wMaxPkt == 0){ + return 0; + } + //(EIP71067+)> + fpQHBulk = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ + (1 * sizeof(EHCI_QTD)))); + //(EIP83295+)> + if(!fpQHBulk) { + return 0; + } + //<(EIP83295+) + fpQTDBulkData = (EHCI_QTD*)((UINTN)fpQHBulk + sizeof(EHCI_QH)); //(EIP81030) + //<(EIP71067+) + EhciDmaMap(fpHCStruc, bXferDir, fpBuffer, dwLength, &BufPhyAddr, &BufferMapping); + + while (dBytesRemaining) { + dBytesToTransfer = + (dBytesRemaining < HIGHSPEED_MAX_BULK_DATA_SIZE)? + dBytesRemaining : HIGHSPEED_MAX_BULK_DATA_SIZE; + + // + // Get data toggle value + // + bDatToggle = UsbGetDataToggle(fpDevInfo, bEndp | bXferDir); + + // + // Set the QH's dNextqTDPtr field to bulk data qTD and dAltqTDPtr field to + // EHCI_TERMINATE. Also set QH's link pointer to itself + // + //(EIP71067-)> + //fpQHBulk = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpQHBulk; + //fpQTDBulkData = fpHCStruc->stDescPtrs.fpEHCIDescPtrs->fpqTDBulkData; + //<(EIP71067-) + // + // Intialize the queue head + // + EHCIInitializeQueueHead(fpQHBulk); + + // + // Set the first qTD pointer + // + fpQHBulk->fpFirstqTD = fpQTDBulkData; + fpQHBulk->dNextqTDPtr = (UINT32)(UINTN)fpQTDBulkData; + fpQHBulk->dLinkPointer = (UINT32)((UINTN)fpQHBulk | EHCI_QUEUE_HEAD); + + // + // Device address & Endpoint + // + dTmp = (UINT32)(fpDevInfo->bDeviceAddress | (bEndp << 8)); + + // + // Set max packet size + // + dTmp = dTmp | ((UINT32)wMaxPkt << 16); + + // + // Set the data toggle control + // +// #if EHCI_ASYNC_BELL_SUPPORT +// dTmp |= QH_USE_QTD_DT; +// #else + dTmp |= QH_USE_QTD_DT; + // Do not set QH_HEAD_OF_LIST bit on VIA controller + if (fpHCStruc->Vid != 0x1106) { + dTmp |= QH_HEAD_OF_LIST; + } +// #endif + + // + // Set the device speed + // Reset the device speed bits + // + dTmp1 = (UINT32)fpDevInfo->bEndpointSpeed; // 00/01/10 for HI/LO/FULL + + // + // Assume as a high speed device + // + dTmp |= QH_HIGH_SPEED; // 10b - High speed + + // + // Check for high speed + // + if (dTmp1) + { + dTmp1 = (dTmp1 & 1) << 12; // Bit 12 = full/low speed flag + dTmp &= ~(QH_ENDPOINT_SPEED); + dTmp |= dTmp1; + // + // Set the hub address and port number + // + dTmp1 = (UINT32)EHCI_GetHiSpeedHubPortNumber(fpDevInfo); + dTmp1 = (dTmp1 << 16); // Hispeed hub port number & device number + fpQHBulk->dEndPntCap |= dTmp1; // Split complete Xaction + } + + // + // Update the endpoint characteristcs field with the data formed + // + fpQHBulk->dEndPntCharac = dTmp; + + // + // Fill the bulk data qTD with relevant information + // The token field will be set so + // Direction PID = QTD_OUT_TOKEN/QTD_IN_TOKEN, + // Size = size of the data, + // Data Toggle = bDatToggle, + // Error Count = QTD_THREE_ERRORS, + // Status code = QTD_DO_OUT(if it is out) + QTD_ACTIVE + // The buffer pointers field will point to the fpBuffer buffer + // which was before initialized to contain a DeviceRequest struc. + // The dNextqTDPtr field will point to the qTDControlSetup + // The dAltNextqTDPtr field will be set to EHCI_TERMINATE + // + if (bXferDir & BIT7) { + fpQTDBulkData->dToken = QTD_IN_TOKEN | + QTD_IOC_BIT | + QTD_THREE_ERRORS | QTD_ACTIVE; + } else { + fpQTDBulkData->dToken = QTD_OUT_TOKEN | + QTD_IOC_BIT | + QTD_THREE_ERRORS | QTD_DO_OUT | QTD_ACTIVE; + if (TempBuffer != NULL) { + MemCpy(BufPhyAddr, TempBuffer, dBytesToTransfer); + } + } + + // + // Set the data toggle depending on the bDatToggle value + // + fpQTDBulkData->dToken |= (UINT32)bDatToggle << 31; + + // + // Set length + // + fpQTDBulkData->dToken |= (dBytesToTransfer << 16); + + // + // Update buffer pointers + // + if (TempBuffer != NULL) { + EHCISetQTDBufferPointers(fpQTDBulkData, TempBuffer, dBytesToTransfer); + } else { + EHCISetQTDBufferPointers(fpQTDBulkData, BufPhyAddr, dBytesToTransfer); + } + + // + // Update next & alternate next qTD pointers + // + fpQTDBulkData->dNextqTDPtr = EHCI_TERMINATE; + fpQTDBulkData->dAltNextqTDPtr = EHCI_TERMINATE; + + fpQHBulk->bActive = TRUE; + + + // + // Set bulk condition as not stalled + // + gUsbData->bLastCommandStatus &= ~(USB_BULK_STALLED + USB_BULK_TIMEDOUT); + + // + // Now wait for bulk transaction to be complete + // the EHCIProcessInterrupt will set its active flag to FALSE. + // Now wait for the bulk transfer to complete + // + Status = EhciExexuteAsyncSchedule(fpHCStruc, fpQHBulk); + + fpQHBulk->fpFirstqTD = 0; + fpQHBulk->dNextqTDPtr = EHCI_TERMINATE; + + if (Status != USB_SUCCESS) { + // + // Set time out status + // + gUsbData->bLastCommandStatus |= USB_BULK_TIMEDOUT; + gUsbData->dLastCommandStatusExtended |= USB_TRNSFR_TIMEOUT; + break; + } + + if (fpQHBulk->bErrorStatus & QTD_HALTED) { + // + // Stall condition + // + gUsbData->bLastCommandStatus &= ~(USB_BULK_TIMEDOUT); + gUsbData->bLastCommandStatus |= USB_BULK_STALLED; + gUsbData->dLastCommandStatusExtended |= USB_TRSFR_STALLED; + break; + } + + // + // Update the data toggle value into the mass info structure + // + bDatToggle = + (UINT8)(((fpQHBulk->dToken & QH_DATA_TOGGLE) >> 31) & 1); + UsbUpdateDataToggle(fpDevInfo, bEndp | bXferDir, bDatToggle); + + // + // Get the size of data transferred + // + dTmp = (fpQTDBulkData->dToken & ~(QTD_DATA_TOGGLE)) >> 16; + dTmp = (dTmp)? dBytesToTransfer-dTmp : dBytesToTransfer; + + if (!dTmp) { + break; + } + + if (TempBuffer != NULL) { + if (bXferDir & BIT7) { + MemCpy(TempBuffer, BufPhyAddr, dTmp); + } + } + + // + // Adjust loop variables + // + dBytesRemaining = dBytesRemaining - dTmp; + dBytesTransferred += dTmp; + + if (dTmp < dBytesToTransfer) { + break; + } + + BufPhyAddr += dTmp; + } + + EhciDmaUnmap(fpHCStruc, BufferMapping); + //(EIP71067+)> + EhciMemFree(fpHCStruc, fpQHBulk, GET_MEM_BLK_COUNT( (1 * sizeof(EHCI_QH)) + + (1 * sizeof(EHCI_QTD)))); + //<(EIP71067+) + if (TempBuffer != NULL) { + if (dwLength < HIGHSPEED_MAX_BULK_DATA_SIZE) { + EhciMemFree(fpHCStruc, TempBuffer, GET_MEM_BLK_COUNT((UINT16)dwLength)); + } else { + EhciMemFree(fpHCStruc, TempBuffer, GET_MEM_BLK_COUNT(HIGHSPEED_MAX_BULK_DATA_SIZE)); + } + } + + return dBytesTransferred; + +} + + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: EHCI_InterruptTransfer +// +// Description: This function executes an interrupt transaction on the USB. +// The data transfer direction is always DATA_IN. This +// function wil not return until the request either completes +// successfully or completes in error (due to time out, etc.) +// +// Parameters: fpHCStruc Pointer to HCStruc of the host controller +// fpDevInfo DeviceInfo structure (if available else 0) +// EndpointAddress The destination USB device endpoint to which the device request +// is being sent. +// MaxPktSize Indicates the maximum packet size the target endpoint is capable +// of sending or receiving. +// fpBuffer Buffer containing data to be sent to the +// device or buffer to be used to receive data +// wLength wLength request parameter, number of bytes +// of data to be transferred in +// +// Output: Number of bytes transferred +// +// +// Notes: DO NOT TOUCH THE LINK POINTER OF THE TDInterruptData. It is +// statically allocated and linked with other items in the +// 1ms schedule +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 +EHCI_InterruptTransfer ( + HC_STRUC *fpHCStruc, + DEV_INFO *fpDevInfo, + UINT8 EndpointAddress, + UINT16 MaxPktSize, + UINT8 *fpBuffer, + UINT16 wLength +) +{ + + UINT8 bDatToggle; + EHCI_QH *fpQHInt; + UINT32 dTmp, dTmp1; + EHCI_QTD *fpqTDIntData; + UINT32 Count; + UINT32 Timeout; + UINT32 BytesTransferred; + UINT8 *BufPhyAddr = NULL; + VOID *BufferMapping = NULL; + EFI_STATUS EfiStatus = EFI_SUCCESS; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + + EfiStatus = UsbDevInfoValidation(fpDevInfo); + + if (EFI_ERROR(EfiStatus)) { + return 0; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (gCheckUsbApiParameter) { + EfiStatus = AmiValidateMemoryBuffer((VOID*)fpBuffer, wLength); + if (EFI_ERROR(EfiStatus)) { + USB_DEBUG(3, "Ehci InterruptTransfer Invalid Pointer, Buffer is in SMRAM.\n"); + return 0; + } + gCheckUsbApiParameter = FALSE; + } +#endif + + if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { + return 0; + } + + if (EhciIsHalted(fpHCStruc)) { + return 0; + } + + gUsbData->dLastCommandStatusExtended = 0; + + bDatToggle = UsbGetDataToggle(fpDevInfo, EndpointAddress); + + // + // Get the QHInterrupt pointer + // + fpQHInt = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ + (1 * sizeof(EHCI_QTD)))); + if (fpQHInt == NULL) { + return 0; + } + + fpqTDIntData = (EHCI_QTD*)((UINTN)fpQHInt + sizeof(EHCI_QH)); + + // + // Intialize the queue head + // + EHCIInitializeQueueHead(fpQHInt); + + // + // Set the first qTD pointer + // + fpQHInt->fpFirstqTD = fpqTDIntData; + fpQHInt->dNextqTDPtr = (UINT32)(UINTN)fpqTDIntData; + fpQHInt->dLinkPointer = EHCI_TERMINATE; + + // + // Get Device address & Endpoint + // + dTmp = (UINT32)fpDevInfo->bDeviceAddress; + dTmp |= (UINT32)(EndpointAddress & 0xF) << 8; + + // + // Set max packet size + // + dTmp |= (UINT32)(MaxPktSize) << 16; + + // + // Set the device speed, reset the device speed bits + // + dTmp1 = (UINT32)fpDevInfo->bEndpointSpeed; // 00/01/10 for HI/LO/FULL + + // + // Assume as a high speed device + // + dTmp |= QH_HIGH_SPEED; // 10b - High speed + + // + // Check for high speed + // + if (dTmp1) + { + dTmp1 = (dTmp1 & 1) << 12; // Bit 12 = full/low speed flag + dTmp &= ~(QH_ENDPOINT_SPEED); + dTmp |= dTmp1; + // + // Set the hub address and port number + // + dTmp1 = (UINT32)EHCI_GetHiSpeedHubPortNumber(fpDevInfo); + dTmp1 = (dTmp1 << 16) | BIT10 | BIT11 | BIT12; + fpQHInt->dEndPntCap |= dTmp1; // Split complete Xaction + } + // + // Update the endpoint characteristcs field with the data formed + // + fpQHInt->dEndPntCharac = dTmp; + fpQHInt->dEndPntCap |= (BIT0 | QH_ONE_XFER); // Interrupt schedule mask + fpQHInt->Interval = EhciTranslateInterval(fpDevInfo->bEndpointSpeed, + fpDevInfo->bPollInterval); + + // + // Set the data toggle depending on the bDatToggle value + // + fpQHInt->dToken |= (UINT32)bDatToggle << 31; + +// +// Fill the interrupt data qTD with relevant information +// The token field will be set so +// Direction PID = QTD_IN_TOKEN, +// Size = size of the data, +// Data Toggle = bDatToggle, +// Error Count = QTD_THREE_ERRORS, +// Status code = QTD_ACTIVE +// The buffer pointers field will point to the EDX +// which was before initialized to contain a DeviceRequest struc. +// The dNextqTDPtr field will point to the qTDControlSetup +// The dAltNextqTDPtr field will be set to EHCI_TERMINATE +// + fpqTDIntData->dToken = QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_ACTIVE; + if (EndpointAddress & BIT7) { + fpqTDIntData->dToken |= QTD_IN_TOKEN; + } else { + fpqTDIntData->dToken |= QTD_OUT_TOKEN; + } + + // + // Set length + // + fpqTDIntData->dToken |= (UINT32)wLength << 16; + + EhciDmaMap(fpHCStruc, EndpointAddress & BIT7, fpBuffer, wLength, + &BufPhyAddr, &BufferMapping); + // + // Update buffer pointers + // + EHCISetQTDBufferPointers(fpqTDIntData, BufPhyAddr, (UINT32)wLength); + + // + // Update next & alternate next qTD pointers + // + fpqTDIntData->dNextqTDPtr = EHCI_TERMINATE; + fpqTDIntData->dAltNextqTDPtr = EHCI_TERMINATE; + + // + // Schedule the QHInterrupt to 1msec schedule + // + EhciAddPeriodicQh(fpHCStruc,fpQHInt); + + // Set the QH as active + fpQHInt->bActive = TRUE; + + // + // Now wait for interrupt transaction to be complete; + // the EHCIProcessInterrupt will set its active flag to FALSE. + // + Timeout = gUsbData->wTimeOutValue * 100; // makes it number of 10 macrosecond units + + for (Count = 0; Timeout == 0 || Count < Timeout; Count++) { + if (!(fpqTDIntData->dToken & QTD_ACTIVE)) { + break; + } + FixedDelay(10); // 60 microsec + } + //Status = EHCIWaitForTransferComplete(fpHCStruc, fpQHInt, fpDevInfo); + + // Remove the QH from periodic schedule + EhciRemovePeriodicQh(fpHCStruc,(EHCI_QH*)fpQHInt); + + // + // Check whether the QH stopped or timed out + // + BytesTransferred = 0; + if (fpqTDIntData->dToken & QTD_ACTIVE) { + USB_DEBUG (DEBUG_LEVEL_3, "EHCI Time-Out\n"); + gUsbData->dLastCommandStatusExtended |= USB_TRNSFR_TIMEOUT; + } else if (fpqTDIntData->dToken & QTD_HALTED) { + gUsbData->dLastCommandStatusExtended |= USB_TRSFR_STALLED; + } else { + BytesTransferred = (UINT16)(wLength - ((fpqTDIntData->dToken & + ~(QTD_DATA_TOGGLE)) >> 16)); + // + // Update the data toggle value into the mass info structure + // + bDatToggle = (UINT8)(((fpQHInt->dToken & QH_DATA_TOGGLE) >> 31) & 1); + UsbUpdateDataToggle(fpDevInfo, EndpointAddress, bDatToggle); + } + + EhciDmaUnmap(fpHCStruc, BufferMapping); + + // Free the allocated QH and qTD + EhciMemFree(fpHCStruc, fpQHInt, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH)) + + (1 * sizeof(EHCI_QTD)))); + + // Service all interrupts + EHCI_ProcessInterrupt(fpHCStruc); + + return (UINT16)BytesTransferred; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCI_DeactivatePolling +// +// Description: This function de-activates the polling QH for the requested +// device. The device may be a USB keyboard or USB hub +// +// Input: fpHCStruc - Pointer to the HC structure +// fpDevInfo - Pointer to the device information structure +// +// Output: USB_ERROR on error, USB_SUCCESS on success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_DeactivatePolling( + HC_STRUC* fpHCStruc, + DEV_INFO* fpDevInfo) +{ + UINT8 *fpPollED; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + EfiStatus = UsbDevInfoValidation(fpDevInfo); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + if (EhciIsHalted(fpHCStruc)) { + return USB_ERROR; + } + + // + // Get a pointer to the device's QH from the poll QH pointer and remove + // the polling ED from the schedule + // + fpPollED = fpDevInfo->fpPollEDPtr; + if(!fpPollED) return USB_ERROR; + + ((EHCI_QH*)fpPollED)->bActive = FALSE; + + EhciRemovePeriodicQh(fpHCStruc,(EHCI_QH*)fpPollED); + + UsbUpdateDataToggle(fpDevInfo, fpDevInfo->IntInEndpoint, + (UINT8)((((EHCI_QH*)fpPollED)->dToken & QH_DATA_TOGGLE) >> 31)); + + EhciMemFree(fpHCStruc, fpPollED, GET_MEM_BLK_COUNT(sizeof(EHCI_QH)+sizeof(EHCI_QTD))); + fpDevInfo->fpPollEDPtr = NULL; + fpDevInfo->fpPollTDPtr = NULL; + + if(fpDevInfo->fpPollDataBuffer) { + EhciMemFree(fpHCStruc, fpDevInfo->fpPollDataBuffer, + GET_MEM_BLK_COUNT(fpDevInfo->PollingLength)); + fpDevInfo->fpPollDataBuffer = 0; + } + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCI_ActivatePolling +// +// Description: This function activates the polling QH for the requested +// device. The device may be a USB keyboard or USB hub +// +// Input: fpHCStruc - Pointer to the HC structure +// fpDevInfo - Pointer to the device information structure +// +// Output: USB_ERROR on error, USB_SUCCESS on success +// +// Notes: For the keyboard device this routine allocates TDRepeat +// also, if it is not already allocated. This routine allocate +// a polling TD and schedule it to 8ms schedule for keyboards +// and to 1024ms schedule for hubs. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_ActivatePolling( + HC_STRUC* fpHCStruc, + DEV_INFO* fpDevInfo) +{ + EHCI_QH *fpPollED; + EHCI_QTD *fpPollTD; + UINT32 dTmp, dTmp1; + EFI_STATUS EfiStatus; + + EfiStatus = UsbHcStrucValidation(fpHCStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + EfiStatus = UsbDevInfoValidation(fpDevInfo); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(fpHCStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + USB_DEBUG (DEBUG_LEVEL_3, "EHCI_AP dev type %d\n", fpDevInfo->bDeviceType); + + if (EhciIsHalted(fpHCStruc)) { + return USB_ERROR; + } + + // + // Allocate a QH/qTD for polling QH & qTD + // + fpPollED = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT( + sizeof(EHCI_QH)+sizeof(EHCI_QTD))); + if (!fpPollED) + { + return USB_ERROR; // Memory allocation error + } + + // + // Save the pointers in DeviceInfo structure + // + fpDevInfo->fpPollEDPtr = (UINT8*)fpPollED; + fpPollTD = (EHCI_QTD*)((UINTN)fpPollED + sizeof(EHCI_QH)); + fpDevInfo->fpPollTDPtr = (UINT8*)fpPollTD; + +// +// Setup the polling QH +// Set the QH's dNextqTDPtr field to polling qTD and dAltqTDPtr field to +// EHCI_TERMINATE +// + + fpPollED->fpFirstqTD = fpPollTD; + fpPollED->dNextqTDPtr = (UINT32)(UINTN)fpPollTD; + + // + // Intialize the queue head + // + fpPollED->dAltNextqTDPtr = EHCI_TERMINATE; + fpPollED->dLinkPointer = EHCI_TERMINATE; + + // + // Set the device info pointer in the QH + // + fpPollED->fpDevInfoPtr = (UINT8*)fpDevInfo; + + // + // Get Device address & Endpoint + // + dTmp = ((UINT32)fpDevInfo->bDeviceAddress) | + ((UINT32)(fpDevInfo->IntInEndpoint & 0xF) << 8); + + dTmp |= ((UINT32)fpDevInfo->IntInMaxPkt) << 16; // Set max packet size //(EIP54782) + + dTmp1 = (UINT32)fpDevInfo->bEndpointSpeed; // 00/01/10 for HI/LO/FULL + + // + // Assume as a high speed device + // + dTmp |= QH_HIGH_SPEED; // 10b - High speed + + // + // Check for high speed + // + if (dTmp1) + { + dTmp1 = (dTmp1 & 1) << 12; // Bit 12 = full/low speed flag + dTmp &= ~(QH_ENDPOINT_SPEED); + dTmp |= dTmp1; + // + // Set the hub address and port number + // + dTmp1 = (UINT32)EHCI_GetHiSpeedHubPortNumber(fpDevInfo); + dTmp1 = (dTmp1 << 16) | (BIT10 + BIT11 + BIT12); // Split complete Xaction + fpPollED->dEndPntCap |= dTmp1; + } + + // + // Update the endpoint characteristcs field with the data formed + // + fpPollED->dEndPntCharac = dTmp; + + // + // Set a bit in interrupt mask + // + fpPollED->dEndPntCap |= (BIT0 + QH_ONE_XFER); + fpPollED->Interval = EhciTranslateInterval(fpDevInfo->bEndpointSpeed, + fpDevInfo->bPollInterval); + + // + // Set the data toggle + // + fpPollED->dToken |= (UINT32)(UsbGetDataToggle(fpDevInfo, fpDevInfo->IntInEndpoint) << 31); + +// +// Fill the polling qTD with relevant information +// The token field will be set so +// Direction PID = QTD_IN_TOKEN, +// Size = size of the data, +// Data Toggle = QTD_DATA0_TOGGLE, +// Error Count = QTD_THREE_ERRORS, +// Status code = QTD_ACTIVE +// The buffer pointers field will point to the fpBuffer buffer +// which was before initialized to contain a DeviceRequest struc. +// The dNextqTDPtr field will point to the qTDControlSetup +// The dAltNextqTDPtr field will be set to EHCI_TERMINATE +// + fpPollTD->dToken = QTD_IN_TOKEN | + QTD_IOC_BIT | + QTD_THREE_ERRORS | + QTD_ACTIVE; + // + // Set length + // + fpPollTD->dToken |= (UINT32)fpDevInfo->PollingLength << 16; + fpDevInfo->fpPollDataBuffer = EhciMemAlloc(fpHCStruc, + GET_MEM_BLK_COUNT(fpDevInfo->PollingLength)); + ASSERT(fpDevInfo->fpPollDataBuffer); + + // + // Update buffer pointers + // + EHCISetQTDBufferPointers(fpPollTD, + fpDevInfo->fpPollDataBuffer, fpDevInfo->PollingLength); + + // + // Update next & alternate next qTD pointers + // + fpPollTD->dNextqTDPtr = EHCI_TERMINATE; + fpPollTD->dAltNextqTDPtr = EHCI_TERMINATE; + + EhciAddPeriodicQh(fpHCStruc,fpPollED); + + fpPollED->bCallBackIndex = USB_InstallCallBackFunction(EhciPollingTDCallback); + fpPollED->bActive = TRUE; + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIDisableKeyRepeat +// +// Description: This function disables the keyboard repeat rate logic by +// enabling the repeat TD +// +// Input: fpHCStruc - Pointer to the HCStruc structure +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_DisableKeyRepeat ( + HC_STRUC *HcStruc +) +{ + EHCI_DESC_PTRS *DescPtr; + EHCI_QH *RepeatQh; + EFI_STATUS EfiStatus; + UINT8 *MemBlockEnd = gUsbData->fpMemBlockStart + (gUsbData->MemPages << 12); + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + if (EhciIsHalted(HcStruc)) { + return USB_ERROR; + } + + DescPtr = HcStruc->stDescPtrs.fpEHCIDescPtrs; + + if (DescPtr == NULL) { + return USB_ERROR; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (((UINT8*)DescPtr < gUsbData->fpMemBlockStart) || + ((UINT8*)(DescPtr + sizeof(EHCI_DESC_PTRS)) > MemBlockEnd)) { + return USB_ERROR; + } +#endif + + RepeatQh = DescPtr->fpQHRepeat; + + if (RepeatQh == NULL) { + return USB_ERROR; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (((UINT8*)RepeatQh < gUsbData->fpMemBlockStart) || + ((UINT8*)(RepeatQh + sizeof(EHCI_QH)) > MemBlockEnd)) { + return USB_ERROR; + } +#endif + + if (RepeatQh) { + RepeatQh->dTokenReload = ((UINT32)8 << 16) | QTD_IN_TOKEN | QTD_ONE_ERROR; + RepeatQh->bActive = FALSE; + } + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//--------------------------------------------------------------------------- +// +// Procedure: EHCI_EnableKeyRepeat +// +// Description: This function disables the keyboard repeat rate logic by +// enabling the repeat TD +// +// Parameters: fpHCStruc Pointer to the HCStruc structure +// +// Output: USB_SUCCESS or USB_ERROR +// +//--------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCI_EnableKeyRepeat ( + HC_STRUC* HcStruc +) +{ + EHCI_DESC_PTRS *DescPtr; + EHCI_QH *RepeatQh; + EHCI_QTD *RepeatQtd; + EFI_STATUS EfiStatus; + UINT8 *MemBlockEnd = gUsbData->fpMemBlockStart + (gUsbData->MemPages << 12); + + EfiStatus = UsbHcStrucValidation(HcStruc); + + if (EFI_ERROR(EfiStatus)) { + return USB_ERROR; + } + + if (!(HcStruc->dHCFlag & HC_STATE_RUNNING)) { + return USB_ERROR; + } + + if (EhciIsHalted(HcStruc)) { + return USB_ERROR; + } + + DescPtr = HcStruc->stDescPtrs.fpEHCIDescPtrs; + + if (DescPtr == NULL) { + return USB_ERROR; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (((UINT8*)DescPtr < gUsbData->fpMemBlockStart) || + ((UINT8*)(DescPtr + sizeof(EHCI_DESC_PTRS)) > MemBlockEnd)) { + return USB_ERROR; + } +#endif + + RepeatQh = DescPtr->fpQHRepeat; + + if (RepeatQh == NULL) { + return USB_ERROR; + } + +#if USB_RUNTIME_DRIVER_IN_SMM + if (((UINT8*)RepeatQh < gUsbData->fpMemBlockStart) || + (((UINT8*)RepeatQh + sizeof(EHCI_QH)) > MemBlockEnd)) { + return USB_ERROR; + } +#endif + + if ((RepeatQh->dTokenReload & QTD_ACTIVE) == 0) { + RepeatQtd = DescPtr->fpqTDRepeat; + if (RepeatQtd == NULL) { + return USB_ERROR; + } +#if USB_RUNTIME_DRIVER_IN_SMM + if (((UINT8*)RepeatQtd < gUsbData->fpMemBlockStart) || + (((UINT8*)RepeatQtd + sizeof(EHCI_QTD)) > MemBlockEnd)) { + return USB_ERROR; + } +#endif + RepeatQh->dTokenReload = ((UINT32)8 << 16) | QTD_IN_TOKEN | + QTD_ONE_ERROR | QTD_IOC_BIT | QTD_ACTIVE; + + // Update buffer pointers + EHCISetQTDBufferPointers(RepeatQtd, + &RepeatQh->aDataBuffer[0], 8); + + // Re-init the QH pointers + RepeatQh->dCurqTDPtr = 0; + RepeatQh->dAltNextqTDPtr = EHCI_TERMINATE; + RepeatQh->dNextqTDPtr = (UINT32)(UINTN)RepeatQtd; + + // + // Restart the qTD + // + RepeatQh->dToken = 0; + RepeatQtd->dToken = RepeatQh->dTokenReload; + + RepeatQh->bActive = TRUE; + } + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIResetHC +// +// Description: This function resets the EHCI controller +// +// Input: Pointer to the HCStruc structure +// +// Output: USB_SUCCESS HC successfully reset +// USB_ERROR Error, error log is updated +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCIResetHC(HC_STRUC* fpHCStruc) +{ + UINT16 count; + + if (fpHCStruc->DebugPort) return USB_SUCCESS; + // + // Check HC is halted: attempting to reset an actively running HC will + // result in undefined behavior. + // + if (EhciReadOpReg(fpHCStruc, EHCI_USBSTS) & EHCI_HCHALTED) + { + // + // Issue reset + // + EhciSetOpReg(fpHCStruc, EHCI_USBCMD, EHCI_HCRESET); + + // + // EHCI_HCRESET bit is set to zero by the Host Controller when the reset + // process is complete. + // + for (count = 0; count < 500; count++) { + if (!(EhciReadOpReg(fpHCStruc, EHCI_USBCMD) & EHCI_HCRESET)) { + return USB_SUCCESS; + } + FixedDelay(100); // 100 us delay + } + } + + // + // Error - HC reset failed + // + USBLogError(USB_ERR_HC_RESET_FAILED); + + return USB_ERROR; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIInitializesPeriodicSchedule +// +// Description: This function initializes the periodic schedules for the +// EHCI host controller +// +// Input: fpHCStruc - HCStruc for the controller +// dMemAddr - Membase address +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +// Notes: This routine creates 8ms and 32ms schedules +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCIInitializePeriodicSchedule( + HC_STRUC* fpHCStruc, + UINT32 dMemBase) +{ + UINT8 *fpPtr; + EHCI_DESC_PTRS *fpDescPtr; + // + // Allocate descriptor structure and fill it in HCStruc + // + fpDescPtr = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT_STRUC(EHCI_DESC_PTRS)); + if (fpDescPtr == NULL) { + USB_DEBUG(DEBUG_LEVEL_4, "EHCI Descriptor struc alloc failed. %d \n", + GET_MEM_BLK_COUNT_STRUC(EHCI_DESC_PTRS)); + return USB_ERROR; + } + + // + // Save the value in the HC struc + // + fpHCStruc->stDescPtrs.fpEHCIDescPtrs = fpDescPtr; + + // Allocate QH/qTD for PeriodicQh + fpPtr = EhciMemAlloc(fpHCStruc, GET_MEM_BLK_COUNT((1 * sizeof(EHCI_QH))+ + (0 * sizeof(EHCI_QTD)))); + if (fpPtr == NULL) { + USB_DEBUG(DEBUG_LEVEL_4, "Schedule of EHCI QH alloc failed.\n"); + return USB_ERROR; + } + + // Save the 1 QH in appropriate location + fpDescPtr->PeriodicQh = (EHCI_QH*)fpPtr; + + EHCIInitializeQueueHead(fpDescPtr->PeriodicQh); + fpDescPtr->PeriodicQh->dNextqTDPtr = EHCI_TERMINATE; + fpDescPtr->PeriodicQh->dAltNextqTDPtr = EHCI_TERMINATE; + fpDescPtr->PeriodicQh->dEndPntCharac = ((UINT32)0x40 << 16) + QH_HIGH_SPEED; + fpDescPtr->PeriodicQh->dLinkPointer = EHCI_TERMINATE; + fpDescPtr->PeriodicQh->dToken = QTD_HALTED; + fpDescPtr->PeriodicQh->Interval = 1; + + EhciAddPeriodicQh(fpHCStruc, fpDescPtr->PeriodicQh); + + return USB_SUCCESS; +} + + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIInitializeQueueHead +// +// Description: This function initializes the queue head with default values +// +// Input: fpQH Pointer to queue head +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +EHCIInitializeQueueHead(EHCI_QH *fpQH) +{ + fpQH->dNextqTDPtr = 1; + fpQH->dAltNextqTDPtr = 1; + fpQH->dCurqTDPtr = 0; + + fpQH->dEndPntCap = QH_ONE_XFER; + fpQH->dToken = 0; + fpQH->dEndPntCharac = 0; + fpQH->dBufferPtr0 = 0; + fpQH->dBufferPtr1 = 0; + fpQH->dBufferPtr2 = 0; + fpQH->dBufferPtr3 = 0; + fpQH->dBufferPtr4 = 0; + fpQH->bErrorStatus = 0; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIStartPeriodicSchedule +// +// Description: This function starts the periodic schedule for the +// EHCI host controller +// +// Input: Pointer to HcStruc +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCIStartPeriodicSchedule( + HC_STRUC* HcStruc +) +{ + UINT16 i; + // + // Start periodic schedule + // + EhciSetOpReg(HcStruc, EHCI_USBCMD, EHCI_PER_SCHED_ENABLE); + + // + // Make sure the HC started the schedules + // + for (i = 0; i < 1000; i++) { + if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_PER_SCHED_STATUS) { + break; + } + FixedDelay(10); + } + if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_PER_SCHED_STATUS)) { + return USB_ERROR; + } + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIStopPeriodicSchedule +// +// Description: This function stops the periodic schedule for the +// EHCI USB host controller +// +// Input: HcStruc for the controller +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCIStopPeriodicSchedule( + HC_STRUC* HcStruc +) +{ + UINT16 i; + // + // Stop periodic schedule + // + EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_PER_SCHED_ENABLE); + + // + // Make sure the HC stopped the schedules + // + for (i = 0; i < 1000; i++) { + if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_PER_SCHED_STATUS)) { + break; + } + FixedDelay(10); + } + if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_PER_SCHED_STATUS) { + return USB_ERROR; + } + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIStartAsyncSchedule +// +// Description: This function starts the asynchronous schedule +// +// Input: Pointer to HcStruc +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCIStartAsyncSchedule( + HC_STRUC* HcStruc +) +{ + UINT16 i; + // + // Start the Async schedule + // + EhciSetOpReg(HcStruc, EHCI_USBCMD, EHCI_ASYNC_SCHED_ENABLE); + + // + // Make sure the HC started the async. execution + // + for (i = 0; i < 1000; i++) { + if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS) { + break; + } + FixedDelay(10); + } + if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS)) { + // + // Stop the host controller (Reset bit 0) + // + EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_RUNSTOP); + // The Host Controller must halt within 16 micro-frames after + // software clears the Run bit. + for (i = 0; i < 16; i++) { + if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_HCHALTED) { + break; + } + FixedDelay(125); // 125 us delay + } + return USB_ERROR; + } + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIStopAsyncSchedule +// +// Description: This function stops the asynchronous transfer and sets the +// asynchronous pointer to null +// +// Input: Pointer to HcStruc +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCIStopAsyncSchedule( + HC_STRUC* HcStruc +) +{ + UINT16 i; + + if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS)) { + return USB_SUCCESS; + } + + // + // Stop periodic schedule + // + EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_ASYNC_SCHED_ENABLE); + + // + // Make sure the HC stopped the async. execution + // + for (i = 0; i < 1000; i++) { + if (!(EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS)) { + break; + } + FixedDelay(10); + } + if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_ASYNC_SCHED_STATUS) { + // + // Stop the host controller (Reset bit 0) + // + EhciClearOpReg(HcStruc, EHCI_USBCMD, EHCI_RUNSTOP); + // The Host Controller must halt within 16 micro-frames after + // software clears the Run bit. + for (i = 0; i < 16; i++) { + if (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_HCHALTED) { + break; + } + FixedDelay(125); // 125 us delay + } + return USB_ERROR; + } + + return USB_SUCCESS; +} + +/* +#if EHCI_ASYNC_BELL_SUPPORT +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIRemoveQHFromAsyncList +// +// Description: This function stops the asynchronous transfer and sets the +// asynchronous pointer to null +// +// Input: Pointer to HCStruc +// Pointer to the Queue head that has to be removed +// from the asynchronous schedule +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +EHCIRemoveQHFromAsyncList( + HC_STRUC *fpHCStruc, + EHCI_QH *fpQH +) +{ + // + // Stop the Async transfer + // + EHCIStopAsyncSchedule(fpHCStruc); + + // + // Remove the queue head from the Async list + // + gUsbData->fpQHAsyncXfer->dLinkPointer = fpQH->dLinkPointer; + + // + // Pointer is advanced. The queue head is totally removed from the list! + // +} +#endif +*/ + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIAddPeriodicQH +// +// Description: This function adds a QH to the frame list +// +// Input: Pointer to the QH to be added +// Absolute pointer to the frame list +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EhciAddPeriodicQh( + HC_STRUC *HcStruc, + EHCI_QH *NewQh +) +{ + UINT16 Index; + UINT32 *PrevPtr; + UINT32 LinkPtr; + EHCI_QH *Qh; + EFI_STATUS Status = EFI_SUCCESS; + + if (NewQh == NULL || NewQh->Interval == 0) { + return USB_ERROR; + } + + for (Index = HcStruc->SplitPeriodicIndex; Index < HcStruc->wAsyncListSize; Index += NewQh->Interval) { + PrevPtr = &HcStruc->fpFrameList[Index]; + LinkPtr = *PrevPtr; + while (!(LinkPtr & EHCI_TERMINATE)){ + Qh = (EHCI_QH*)(LinkPtr & EHCI_POINTER_MASK); +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)Qh, sizeof(EHCI_QH)); + if (EFI_ERROR(Status)) { + return USB_ERROR; + } +#endif + if (Qh->Interval <= NewQh->Interval) { + break; + } + PrevPtr = &Qh->dLinkPointer; + LinkPtr = *PrevPtr; + } + + if (!(LinkPtr & EHCI_TERMINATE) && (Qh == NewQh)) { + continue; + } +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)PrevPtr, sizeof(UINT32)); + if (EFI_ERROR(Status)) { + return USB_ERROR; + } +#endif + NewQh->dLinkPointer = *PrevPtr; + *PrevPtr = (UINT32)((UINTN)NewQh | EHCI_QUEUE_HEAD); + + } + + if (HcStruc->SplitPeriodicIndex == MAX_SPLIT_PERIODIC_NUMBER) { + HcStruc->SplitPeriodicIndex = 0; + } else { + HcStruc->SplitPeriodicIndex++; + } + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EhciRemovePeriodicQh +// +// Description: This function removes a QH from the frame list +// +// Input: Pointer to the QH to be added +// Absolute pointer to the frame list +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EhciRemovePeriodicQh ( + HC_STRUC *HcStruc, + EHCI_QH *RetiredQh +) +{ + UINT16 Index; + UINT32 *PrevPtr; + UINT32 LinkPtr; + EHCI_QH *Qh; + EFI_STATUS Status = EFI_SUCCESS; + + if (RetiredQh == NULL || RetiredQh->Interval == 0) { + return USB_ERROR; + } + + EHCIStopPeriodicSchedule(HcStruc); + + for (Index = 0; Index < HcStruc->wAsyncListSize; Index++) { + PrevPtr = &HcStruc->fpFrameList[Index]; + LinkPtr = *PrevPtr; + + while (!(LinkPtr & EHCI_TERMINATE)){ + Qh = (EHCI_QH*)(LinkPtr & EHCI_POINTER_MASK); +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)Qh, sizeof(EHCI_QH)); + if (EFI_ERROR(Status)) { + return USB_ERROR; + } +#endif + if (Qh == RetiredQh) { + break; + } + PrevPtr = &Qh->dLinkPointer; + LinkPtr = *PrevPtr; + } + + if (LinkPtr & EHCI_TERMINATE) { + continue; + } +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)PrevPtr, sizeof(UINT32)); + if (EFI_ERROR(Status)) { + return USB_ERROR; + } +#endif + *PrevPtr = RetiredQh->dLinkPointer; + } + + RetiredQh->dLinkPointer = EHCI_TERMINATE; + + EHCIStartPeriodicSchedule(HcStruc); + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCISetQTDBufferPointers +// +// Description: This function will set the 5 buffer pointer in the qTD +// appropriately depending upon the input size +// +// Input: fpQtd - Pointer to the qTD +// fpBuf - 32bit absolute buffer pointer +// wSize - Amount of data to be transferred +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +EHCISetQTDBufferPointers( + EHCI_QTD *fpQtd, + UINT8 *fpBuf, + UINT32 dSize +) +{ + UINT16 wBufSize; + UINT8 *fpBuffer = fpBuf; + UINT32 *fpBufferPtr; + UINT16 w4KRemainder; + + // + // Fill the buffer pointers with 0s + // + fpQtd->dBufferPtr0 = 0; + fpQtd->dBufferPtr1 = 0; + fpQtd->dBufferPtr2 = 0; + fpQtd->dBufferPtr3 = 0; + fpQtd->dBufferPtr4 = 0; + fpQtd->dAltNextqTDPtr = 1; + + // + // If size to transfer is 0 skip updating pointers + // + if (!dSize) + { + return; + } + + // + // Make sure the amount of data to be xferred is 16K or less + // + wBufSize = (UINT16)((dSize > MAX_EHCI_DATA_SIZE) ? MAX_EHCI_DATA_SIZE : dSize); + + fpBufferPtr = &fpQtd->dBufferPtr0; + + for (;;) + { + *fpBufferPtr = (UINT32)(UINTN)fpBuffer; + // + // Calculate the number of bytes that can be transferred using current + // buffer pointer + // + w4KRemainder = (UINT16)((((UINT32)((UINTN)fpBuffer+0x1000)) & 0xFFFFF000) - + (UINT32)(UINTN)fpBuffer); + + // + // Check whether all the bytes can be accomadated in the current buffer + // + if (w4KRemainder >= wBufSize) + { + break; // Yes. Current Buffer is sufficient for the rest of data + } + + // + // We have more data to transfer - adjust data and store it in the next pointer + // + wBufSize = (UINT16)(wBufSize - w4KRemainder); // Amount of data remaining + fpBuffer = fpBuffer + w4KRemainder; // Adjust buffer (4K bound) + fpBufferPtr++; // Next buffer pointer + } +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIWaitForTransferComplete +// +// Description: This function waits until the requested QH completes or +// the transaction time-out +// +// Input: fpHCStruc - Pointer to HCStruc of the host controller +// fpQH - Pointer to the QH which has to be completed +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 +EHCIWaitForTransferComplete( + HC_STRUC *fpHCStruc, + EHCI_QH *fpQH, + DEV_INFO* fpDevInfo + +) +{ + UINT32 dCount ; + UINT32 countLimit = gUsbData->wTimeOutValue * 100; // makes it number of macrosecond units + UINT16 Status = USB_ERROR; + + // + // Check status change loop iteration + // + for(dCount = 0; !countLimit || dCount < countLimit; dCount++) + { + EHCIProcessQH(fpHCStruc, fpQH); + if(fpQH->bActive == FALSE) { + Status = USB_SUCCESS; + break; + } + FixedDelay(10); // 60 microsec + } + + if(fpQH->bActive == TRUE) { + // Set the QH as in-active + fpQH->bActive = FALSE; + Status = USB_ERROR; + USB_DEBUG (DEBUG_LEVEL_3, "EHCI Time-Out\n"); + } + + // Service all interrupts + EHCI_ProcessInterrupt(fpHCStruc); + + return Status; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIProcessQH +// +// Description: This function whether all the TD's in the QH is completed +// +// Input: fpQH - Pointer to the QH which has to be completed +// +// Output: USB_ERROR On error +// USB_SUCCESS On success +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EHCIProcessQH( + HC_STRUC *fpHCStruc, + EHCI_QH *fpQH +) +{ + EHCI_QTD *fpQTD = fpQH->fpFirstqTD; + EFI_STATUS Status = EFI_SUCCESS; + + for (;;) { + if (fpQTD == NULL) { + return USB_ERROR; + } +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)fpQTD, sizeof(EHCI_QTD)); + if (EFI_ERROR(Status)) { + return USB_ERROR; + } +#endif + // + // Check whether the qTD is active, if so. Exit! + // + if (fpQTD->dToken & QTD_ACTIVE) { + return USB_ERROR; + } + + // + // Check for halt condition, if halted - exit + // + if (fpQTD->dToken & QTD_HALTED) { + // + // Set the QH halted status + // + fpQH->bErrorStatus = QTD_HALTED; + // + // Set the QH as in-active + // + fpQH->bActive = FALSE; + break; + } + // + // qTD is not active and not halted. That is it is completed successfully + // Check whether this qTD is the last one in the list + // + if (fpQTD->dNextqTDPtr & EHCI_TERMINATE) { + // + // Set the QH as in-active + // + fpQH->bActive = FALSE; + break; + } + + // + // More qTDs are in the list. Process next qTD + // + fpQTD = (EHCI_QTD*)(UINTN)fpQTD->dNextqTDPtr; + } + + if ((fpQH->bCallBackIndex) && (fpQH->bCallBackIndex <= MAX_CALLBACK_FUNCTION)) { + if (gUsbData->aCallBackFunctionTable[fpQH->bCallBackIndex - 1]) { + if ((gUsbData->aCallBackFunctionTable[fpQH->bCallBackIndex - 1]) + != EhciRepeatTDCallback) { + Status = UsbDevInfoValidation((DEV_INFO*)fpQH->fpDevInfoPtr); + + if (EFI_ERROR(Status)) { + return USB_ERROR; + } + } + (*gUsbData->aCallBackFunctionTable[fpQH->bCallBackIndex - 1]) + (fpHCStruc, (DEV_INFO*)fpQH->fpDevInfoPtr, + (UINT8*)fpQH, NULL, 0); + } + } + + return USB_SUCCESS; +} + + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIProcessPeriodicList +// +// Description: This function parses through the periodic list to find +// completed qTD. If a qTD is done it will call its associated +// call back function (in device info structure) and restarts +// the polling qTD +// +// Input: fpHCStruc - Pointer to HCStruc of the host controller +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +VOID +EHCIProcessPeriodicList( + HC_STRUC *HcStruc +) +{ + UINT16 Index; + UINT32 *PrevPtr; + UINT32 LinkPtr; + EHCI_QH *Qh; + UINT32 OrgQhLinkPointer; + EFI_STATUS Status = EFI_SUCCESS; + + + // + // Get the first entry in the periodic list. This QH list will link to all + // the periodic QH's + // + + for (Index = 0; Index <= MAX_SPLIT_PERIODIC_NUMBER; Index ++) { + PrevPtr = &HcStruc->fpFrameList[Index]; + LinkPtr = *PrevPtr; + + while (!(LinkPtr & EHCI_TERMINATE)) { + Qh = (EHCI_QH*)(LinkPtr & EHCI_POINTER_MASK); +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)Qh, sizeof(EHCI_QH)); + if (EFI_ERROR(Status)) { + return; + } +#endif + // Process only QHeads, skip the other types + if ((Qh->dLinkPointer & 6) == EHCI_QUEUE_HEAD) { + + // Check whether this QH is actived + if (Qh->bActive == TRUE) { + OrgQhLinkPointer = Qh->dLinkPointer; + EHCIProcessQH(HcStruc, Qh); + // Qh is changed, we re-parses through the list. + if (Qh->dLinkPointer != OrgQhLinkPointer) { + LinkPtr = *PrevPtr; + continue; + } + } + } + + LinkPtr = Qh->dLinkPointer; + } + + } + +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EhciPollingTDCallback +// +// Description: This function is called when a polling TD from the TD pool +// completes an interrupt transaction to its assigned device. +// This routine should process any data in the TD's data buffer, +// handle any errors, and then copy the TD's CSReloadValue field +// into its control status field to put the TD back into service. +// +// Input: HcStruc Pointer to the HCStruc structure +// DevInfo NULL (pDevInfo is not valid) +// QueueHead Pointer to the QH that completed +// Buffer Not used +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EhciPollingTDCallback( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo, + UINT8 *QueueHead, + UINT8 *Buffer, + UINT16 DataLength +) +{ + EHCI_QH* PollQh = (EHCI_QH*)QueueHead; + EHCI_QTD *PollQtd = PollQh->fpFirstqTD; + UINT16 BytesTransferred; + EFI_STATUS Status = EFI_SUCCESS; + +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)PollQtd, sizeof(EHCI_QTD)); + if (EFI_ERROR(Status)) { + return USB_ERROR; + } +#endif + + // Update datat toggle value + UsbUpdateDataToggle(DevInfo, DevInfo->IntInEndpoint, + (UINT8)((PollQh->dToken & QH_DATA_TOGGLE) >> 31)); + + if (!(PollQh->dToken & QTD_STATUS_FIELD)) { + if ((DevInfo->bCallBackIndex) && (DevInfo->bCallBackIndex <= MAX_CALLBACK_FUNCTION)) { + if (gUsbData->aCallBackFunctionTable[DevInfo->bCallBackIndex-1]) { + // + // Get the size of data transferred + // + BytesTransferred = DevInfo->PollingLength - (UINT16)((PollQtd->dToken & ~(QTD_DATA_TOGGLE)) >> 16); + (*gUsbData->aCallBackFunctionTable[DevInfo->bCallBackIndex-1]) + (HcStruc, DevInfo, (UINT8*)PollQtd, DevInfo->fpPollDataBuffer, BytesTransferred); + } + } + } + + // Keep the PID code bit in the qTD + PollQtd->dToken &= QTD_DIRECTION_PID; + + // + // Set length + // + PollQtd->dToken |= (UINT32)DevInfo->PollingLength << 16; + + // + // Update buffer pointers + // + EHCISetQTDBufferPointers(PollQtd, + DevInfo->fpPollDataBuffer, DevInfo->PollingLength); + + // + // Re-init the QH pointers + // + PollQh->dToken &= QH_DATA_TOGGLE; + PollQh->dCurqTDPtr = 0; + PollQh->dAltNextqTDPtr = EHCI_TERMINATE; + PollQh->dBufferPtr0 = 0; + PollQh->dBufferPtr1 = 0; + PollQh->dBufferPtr2 = 0; + PollQh->dBufferPtr3 = 0; + PollQh->dBufferPtr4 = 0; + // + // Update next & alternate next qTD pointers + // + PollQtd->dNextqTDPtr = EHCI_TERMINATE; + PollQtd->dAltNextqTDPtr = EHCI_TERMINATE; + PollQh->dNextqTDPtr = (UINT32)(UINTN)PollQtd; + + // + // Restart the qTD + // + PollQtd->dToken |= (QTD_IOC_BIT | QTD_THREE_ERRORS | QTD_ACTIVE); + PollQh->bActive = TRUE; + + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EHCIRepeatTDCallback +// +// Description: This function is called when qTdRepeat completes +// a transaction. This qTD runs a dummy interrupt transaction +// to a non-existant device address for the purpose of +// generating a periodic timeout interrupt which in turn +// is used to generate keyboard repeat or update LED status. +// +// Input: HcStruc Pointer to the HCStruc structure +// DevInfo NULL (pDevInfo is not valid) +// QueueHead Pointer to the QH that completed +// Buffer Not used +// +// Output: None +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT8 +EhciRepeatTDCallback( + HC_STRUC *HcStruc, + DEV_INFO *DevInfo, + UINT8 *QueueHead, + UINT8 *Buffer, + UINT16 DataLength +) +{ + EHCI_QH *RepeatQh = (EHCI_QH*)QueueHead; + EHCI_QTD *RepeatQtd = RepeatQh->fpFirstqTD; + EFI_STATUS Status = EFI_SUCCESS; + +#if USB_RUNTIME_DRIVER_IN_SMM + Status = AmiValidateMemoryBuffer((VOID*)RepeatQtd, sizeof(EHCI_QTD)); + if (EFI_ERROR(Status)) { + return USB_ERROR; + } +#endif + +#if USB_DEV_KBD + USBKBDPeriodicInterruptHandler(HcStruc); +#endif + + if (RepeatQh->dTokenReload & QTD_ACTIVE) { + // + // Update buffer pointers + // + EHCISetQTDBufferPointers(RepeatQtd, + &RepeatQh->aDataBuffer[0], 8); + + // + // Re-init the QH pointers + // + RepeatQh->dToken &= QH_DATA_TOGGLE; + RepeatQh->dCurqTDPtr = 0; + RepeatQh->dAltNextqTDPtr = EHCI_TERMINATE; + RepeatQh->dBufferPtr0 = 0; + RepeatQh->dBufferPtr1 = 0; + RepeatQh->dBufferPtr2 = 0; + RepeatQh->dBufferPtr3 = 0; + RepeatQh->dBufferPtr4 = 0; + RepeatQh->dNextqTDPtr = (UINT32)(UINTN)RepeatQtd; + + // + // Restart the qTD + // + RepeatQh->dToken = 0; + RepeatQtd->dToken = RepeatQh->dTokenReload; + + RepeatQh->bActive = TRUE; + } + + return USB_SUCCESS; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EhciIsHalted +// +// Description: This function check whether HC is halted. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +BOOLEAN +EhciIsHalted ( + HC_STRUC *HcStruc +) +{ + return (EhciReadOpReg(HcStruc, EHCI_USBSTS) & EHCI_HCHALTED) == EHCI_HCHALTED; +} + +//<AMI_PHDR_START> +//---------------------------------------------------------------------------- +// +// Procedure: EhciTranslateInterval +// +// Description: This function calculates the polling rate in frames unit. +// +//---------------------------------------------------------------------------- +//<AMI_PHDR_END> + +UINT16 +EhciTranslateInterval( + UINT8 Speed, + UINT8 Interval +) +{ + UINT8 BitCount = 0; + UINT16 PollingRate = 0; + + if (Speed == USB_PORT_STAT_DEV_HISPEED) { + // For high-speed interrupt endpoints, the Interval value must be + // from 1 to 16 + ASSERT(Interval >= 1 && Interval <= 16); + + PollingRate = (1 << (Interval - 1)) >> 3; + return PollingRate != 0 ? PollingRate : 1; + } + + // For full-/low-speed interrupt endpoints, the Interval value should + // be from 1 to 255 + ASSERT(Interval >= 1 && Interval <= 255); + + for (BitCount = 0; Interval != 0; BitCount++) { + Interval >>= 1; + } + return (1 << (BitCount - 1)); +} + +//========================================================================== + +UINT32 +EhciReadPciReg( + HC_STRUC *HcStruc, + UINT32 Offset +) +{ +#if USB_RUNTIME_DRIVER_IN_SMM + return ReadPCIConfig(HcStruc->wBusDevFuncNum, Offset); +#else + EFI_STATUS Status; + UINT32 Data = 0; + EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; + + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + Offset, + 1, + &Data); + ASSERT_EFI_ERROR(Status); + return Data; +#endif +} + + +VOID +EhciWritePciReg( + HC_STRUC *HcStruc, + UINT32 Offset, + UINT32 Data +) +{ +#if USB_RUNTIME_DRIVER_IN_SMM + DwordWritePCIConfig(HcStruc->wBusDevFuncNum, Offset, Data); + return; +#else + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; + + Status = PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + Offset, + 1, + &Data); + ASSERT_EFI_ERROR(Status); + return; +#endif +} + +UINT32 +EhciReadHcMem( + HC_STRUC *HcStruc, + UINT32 Offset +) +{ +#if USB_RUNTIME_DRIVER_IN_SMM + if (Offset > HcStruc->BaseAddressSize) { + return 0; + } + return DwordReadMem((UINT32)HcStruc->BaseAddress, Offset); +#else + EFI_STATUS Status; + UINT32 Data = 0; + EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; + + Status = PciIo->Mem.Read ( + PciIo, + EfiPciIoWidthUint32, + 0, + Offset, + 1, + &Data); + ASSERT_EFI_ERROR(Status); + return Data; +#endif +} + +VOID +EhciWriteHcMem( + HC_STRUC *HcStruc, + UINT32 Offset, + UINT32 Data +) +{ +#if USB_RUNTIME_DRIVER_IN_SMM + if (Offset > HcStruc->BaseAddressSize) { + return; + } + DwordWriteMem((UINT32)HcStruc->BaseAddress, Offset, Data); + return; +#else + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; + + Status = PciIo->Mem.Write ( + PciIo, + EfiPciIoWidthUint32, + 0, + Offset, + 1, + &Data); + ASSERT_EFI_ERROR(Status); + return; +#endif +} + +UINT32 +EhciReadOpReg( + HC_STRUC *HcStruc, + UINT32 Offset +) +{ + return EhciReadHcMem(HcStruc, HcStruc->bOpRegOffset + Offset); +} + +VOID +EhciWriteOpReg( + HC_STRUC *HcStruc, + UINT32 Offset, + UINT32 Data +) +{ + EhciWriteHcMem(HcStruc, HcStruc->bOpRegOffset + Offset, Data); + return; +} + +VOID +EhciSetOpReg( + HC_STRUC *HcStruc, + UINT32 Offset, + UINT32 Bit +) +{ + UINT32 Data; + + Data = EhciReadOpReg(HcStruc, Offset) | Bit; + EhciWriteOpReg(HcStruc, Offset, Data); + return; +} + +VOID +EhciClearOpReg( + HC_STRUC *HcStruc, + UINT32 Offset, + UINT32 Bit +) +{ + UINT32 Data; + + Data = EhciReadOpReg(HcStruc, Offset) & ~Bit; + EhciWriteOpReg(HcStruc, Offset, Data); + return; +} + +UINT32 +EhciReadDebugReg( + HC_STRUC *HcStruc, + UINT8 BarIndex, + UINT32 Offset +) +{ +#if USB_RUNTIME_DRIVER_IN_SMM + UINT8 BarOffset[6] = {0x10, 0x14, 0x18, 0x1C, 0x20, 0x24}; + UINT32 DebugPortsMem; + + DebugPortsMem = EhciReadPciReg(HcStruc, BarOffset[BarIndex]); + return DwordReadMem(DebugPortsMem, Offset); +#else + EFI_STATUS Status; + UINT32 Data = 0; + EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; + + Status = PciIo->Mem.Read ( + PciIo, + EfiPciIoWidthUint32, + BarIndex, + Offset, + 1, + &Data); + ASSERT_EFI_ERROR(Status); + return Data; +#endif +} + +VOID* +EhciMemAlloc( + HC_STRUC *HcStruc, + UINT16 NumBlks +) +{ +#if USB_RUNTIME_DRIVER_IN_SMM + return USB_MemAlloc(NumBlks); +#else + UINT32 SavedMemPages = gUsbData->MemPages; + UINT8 *SavedMemPool = gUsbData->fpMemBlockStart; + UINT32 SavedMemBlkStsBytes = gUsbData->MemBlkStsBytes; + UINT32 *SavedMemBlsSts = gUsbData->aMemBlkSts; + VOID* MemPtr; + + gUsbData->MemPages = HcStruc->MemPoolPages; + gUsbData->fpMemBlockStart = HcStruc->MemPool; + gUsbData->MemBlkStsBytes = HcStruc->MemBlkStsBytes; + gUsbData->aMemBlkSts = HcStruc->MemBlkSts; + + MemPtr = USB_MemAlloc(NumBlks); + + gUsbData->MemPages = SavedMemPages; + gUsbData->fpMemBlockStart = SavedMemPool; + gUsbData->MemBlkStsBytes = SavedMemBlkStsBytes; + gUsbData->aMemBlkSts = SavedMemBlsSts; + + return MemPtr; +#endif +} + +VOID +EhciMemFree( + HC_STRUC *HcStruc, + VOID* MemPtr, + UINT16 NumBlks +) +{ +#if USB_RUNTIME_DRIVER_IN_SMM + USB_MemFree(MemPtr, NumBlks); +#else + UINT32 SavedMemPages = gUsbData->MemPages; + UINT8 *SavedMemPool = gUsbData->fpMemBlockStart; + UINT32 SavedMemBlkStsBytes = gUsbData->MemBlkStsBytes; + UINT32 *SavedMemBlsSts = gUsbData->aMemBlkSts; + + gUsbData->MemPages = HcStruc->MemPoolPages; + gUsbData->fpMemBlockStart = HcStruc->MemPool; + gUsbData->MemBlkStsBytes = HcStruc->MemBlkStsBytes; + gUsbData->aMemBlkSts = HcStruc->MemBlkSts; + + USB_MemFree(MemPtr, NumBlks); + + gUsbData->MemPages = SavedMemPages; + gUsbData->fpMemBlockStart = SavedMemPool; + gUsbData->MemBlkStsBytes = SavedMemBlkStsBytes; + gUsbData->aMemBlkSts = SavedMemBlsSts; + + return; +#endif +} + +UINT8 +EhciDmaMap( + HC_STRUC *HcStruc, + UINT8 Direction, + UINT8 *BufferAddr, + UINT32 BufferSize, + UINT8 **PhyAddr, + VOID **Mapping +) +{ +#if USB_RUNTIME_DRIVER_IN_SMM + *PhyAddr = BufferAddr; +#else + EFI_PCI_IO_PROTOCOL_OPERATION Operation; + EFI_PHYSICAL_ADDRESS Addr; + EFI_STATUS Status; + UINTN Bytes = BufferSize; + EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; + + if (Direction & BIT7) { + Operation = EfiPciIoOperationBusMasterWrite; + } else { + Operation = EfiPciIoOperationBusMasterRead; + } + + Status = PciIo->Map ( + PciIo, + Operation, + BufferAddr, + &Bytes, + &Addr, + Mapping + ); + ASSERT_EFI_ERROR(Status); + if (EFI_ERROR(Status) || Bytes != BufferSize) { + return USB_ERROR; + } + + *PhyAddr = (UINT8*)Addr; +#endif + return USB_SUCCESS; +} + +UINT8 +EhciDmaUnmap( + HC_STRUC *HcStruc, + VOID *Mapping +) +{ +#if !USB_RUNTIME_DRIVER_IN_SMM + + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo = HcStruc->PciIo; + + Status = PciIo->Unmap(PciIo, Mapping); + ASSERT_EFI_ERROR(Status); + if (EFI_ERROR(Status)) { + return USB_ERROR; + } +#endif + return USB_SUCCESS; +} + +//**************************************************************************** +//**************************************************************************** +//** ** +//** (C)Copyright 1985-2016, American Megatrends, Inc. ** +//** ** +//** All Rights Reserved. ** +//** ** +//** 5555 Oakbrook Pkwy, Norcross, GA 30093 ** +//** ** +//** Phone (770)-246-8600 ** +//** ** +//**************************************************************************** +//**************************************************************************** |