mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-13 10:04:04 +01:00
ff1681ad80
Signed-off-by: SergeySlice <sergey.slice@gmail.com>
363 lines
15 KiB
C++
363 lines
15 KiB
C++
/*
|
||
* usbfix.c
|
||
*
|
||
* Created by Slice on 21.09.11.
|
||
*
|
||
* based on works by mackerintel 2008, orByte 2006, Signal64, THeKiNG
|
||
*/
|
||
|
||
#include <Platform.h> // Only use angled for Platform, else, xcode project won't compile
|
||
#include <Efi.h>
|
||
|
||
#ifndef DEBUG_ALL
|
||
#define DEBUG_USB 1
|
||
#else
|
||
#define DEBUG_USB DEBUG_ALL
|
||
#endif
|
||
|
||
#if DEBUG_USB == 0
|
||
#define DBG(...)
|
||
#else
|
||
#define DBG(...) DebugLog(DEBUG_USB, __VA_ARGS__)
|
||
#endif
|
||
|
||
#define PCI_IF_OHCI 0x10
|
||
#define PCI_IF_XHCI 0x30
|
||
#define OHCI_CTRL_MASK (1 << 9)
|
||
#define OHCI_CONTROL 0x04
|
||
#define OHCI_INTRDISABLE 0x14
|
||
#define OHCI_INTRSTATUS 0x0c
|
||
|
||
EFI_STATUS
|
||
FixOwnership(void)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Disable the USB legacy Support in all Ehci and Uhci.
|
||
This function assume all PciIo handles have been created in system.
|
||
Slice - added also OHCI and more advanced algo. Better then known to Intel and Apple :)
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
EFI_SUCCESS
|
||
EFI_NOT_FOUND
|
||
--*/
|
||
{
|
||
DBG("FixOwnership() -> begin\n");
|
||
EFI_STATUS Status;
|
||
EFI_HANDLE *HandleArray = NULL;
|
||
UINTN HandleArrayCount = 0;
|
||
UINTN Index;
|
||
EFI_PCI_IO_PROTOCOL *PciIo;
|
||
PCI_TYPE00 Pci;
|
||
UINT16 Command;
|
||
UINT32 HcCapParams;
|
||
UINT32 ExtendCap;
|
||
UINT32 Value;
|
||
INT32 TimeOut;
|
||
UINT32 Base;
|
||
UINT32 PortBase;
|
||
volatile UINT32 opaddr;
|
||
// UINT8 eecp;
|
||
UINT32 usbcmd, usbsts, usbintr;
|
||
UINT32 usblegsup, usblegctlsts;
|
||
|
||
UINTN isOSowned;
|
||
UINTN isBIOSowned;
|
||
XBool isOwnershipConflict;
|
||
|
||
//
|
||
// Find the usb host controller
|
||
//
|
||
Status = gBS->LocateHandleBuffer (
|
||
ByProtocol,
|
||
&gEfiPciIoProtocolGuid,
|
||
NULL,
|
||
&HandleArrayCount,
|
||
&HandleArray
|
||
);
|
||
DBG("PCI Io found %lld\n", HandleArrayCount);
|
||
if (HandleArrayCount > 50) HandleArrayCount = 50;
|
||
if (!EFI_ERROR(Status)) {
|
||
for (Index = 0; Index < HandleArrayCount; Index++) {
|
||
Status = gBS->HandleProtocol (
|
||
HandleArray[Index],
|
||
&gEfiPciIoProtocolGuid,
|
||
(void **)&PciIo
|
||
);
|
||
if (!EFI_ERROR(Status)) {
|
||
//
|
||
// Find the USB host controller
|
||
//
|
||
Status = PciIo->Pci.Read (
|
||
PciIo,
|
||
EfiPciIoWidthUint32,
|
||
0,
|
||
sizeof (Pci) / sizeof (UINT32),
|
||
&Pci
|
||
);
|
||
|
||
if (!EFI_ERROR(Status)) {
|
||
if ((PCI_CLASS_SERIAL == Pci.Hdr.ClassCode[2]) &&
|
||
(PCI_CLASS_SERIAL_USB == Pci.Hdr.ClassCode[1])) {
|
||
switch (Pci.Hdr.ClassCode[0]) {
|
||
case PCI_IF_UHCI:
|
||
//
|
||
// Found the UHCI, then disable the legacy support
|
||
//
|
||
Base = 0;
|
||
Status = PciIo->Pci.Read(PciIo, EfiPciIoWidthUint32, 0x20, 1, &Base);
|
||
PortBase = (Base >> 5) & 0x07ff;
|
||
DBG("USB UHCI Base=%X PortBase=%X\n", Base, PortBase);
|
||
Command = 0x8f00;
|
||
Status = PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0xC0, 1, &Command);
|
||
if (PortBase && (PortBase & BIT0) == 0) {
|
||
IoWrite16 (PortBase, 0x0002);
|
||
gBS->Stall (500);
|
||
IoWrite16 (PortBase+4, 0);
|
||
gBS->Stall (500);
|
||
IoWrite16 (PortBase, 0);
|
||
}
|
||
|
||
DBG("USB UHCI reset for device %04hX\n", Pci.Hdr.DeviceId);
|
||
break;
|
||
/* case PCI_IF_OHCI:
|
||
|
||
Base = 0;
|
||
Status = PciIo->Pci.Read(PciIo, EfiPciIoWidthUint32, 0x10, 1, &Base);
|
||
|
||
Command = *(UINT32 *)(UINTN)(Base + OHCI_CONTROL);
|
||
*(UINT32 *)(UINTN)(Base + OHCI_CONTROL) = Command & OHCI_CTRL_MASK;
|
||
Command = *(UINT32 *)(UINTN)(Base + OHCI_CONTROL);
|
||
MsgLog("USB OHCI reset for device %04hhX control=0x%hhX\n", Pci.Hdr.DeviceId, Command);
|
||
break;*/
|
||
case PCI_IF_EHCI:
|
||
//Slice - the algo is reworked from Chameleon
|
||
// it looks like redundant but it works so I will not reduce it
|
||
//
|
||
// Found the EHCI, then disable the legacy support
|
||
//
|
||
DBG("EHCI found devID= %04hX\n", Pci.Hdr.DeviceId);
|
||
Value = 0x0002;
|
||
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x04, 1, &Value);
|
||
DBG("EHCI cmd 2 written\n");
|
||
Base = 0;
|
||
Status = PciIo->Pci.Read(PciIo, EfiPciIoWidthUint32, 0x10, 1, &Base);
|
||
if (*((UINT8*)(UINTN)Base) < 0x0C) {
|
||
DBG("Config space too small: no legacy implementation\n");
|
||
break;
|
||
}
|
||
DBG("EHCI Base=%08X\n", Base);
|
||
// opaddr = Operational Registers = capaddr + offset (8bit CAPLENGTH in Capability Registers + offset 0)
|
||
opaddr = Base + *((UINT8*)(UINTN)(Base));
|
||
// eecp = EHCI Extended Capabilities offset = capaddr HCCPARAMS bits 15:8
|
||
//UEFI
|
||
Status = PciIo->Mem.Read (
|
||
PciIo,
|
||
EfiPciIoWidthUint32,
|
||
0, //EHC_BAR_INDEX
|
||
(UINT64) 0x08, //EHC_HCCPARAMS_OFFSET
|
||
1,
|
||
&HcCapParams
|
||
);
|
||
|
||
ExtendCap = (HcCapParams >> 8) & 0xFF;
|
||
DBG("Base=%X Oper=%X eecp=%X\n", Base, opaddr, ExtendCap);
|
||
|
||
usbcmd = *((UINT32*)(UINTN)(opaddr)); // Command Register
|
||
usbsts = *((UINT32*)(UINTN)(opaddr + 4)); // Status Register
|
||
usbintr = *((UINT32*)(UINTN)(opaddr + 8)); // Interrupt Enable Register
|
||
|
||
DBG("usbcmd=%08X usbsts=%08X usbintr=%08X\n", usbcmd, usbsts, usbintr);
|
||
|
||
// read PCI Config 32bit USBLEGSUP (eecp+0)
|
||
Status = PciIo->Pci.Read(PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &usblegsup);
|
||
|
||
// informational only
|
||
isBIOSowned = !!((usblegsup) & (1 << (16)));
|
||
isOSowned = !!((usblegsup) & (1 << (24)));
|
||
|
||
// read PCI Config 32bit USBLEGCTLSTS (eecp+4)
|
||
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &usblegctlsts);
|
||
DBG("usblegsup=%08X isOSowned=%llu isBIOSowned=%llu usblegctlsts=%08X\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
|
||
//
|
||
// Disable the SMI in USBLEGCTLSTS firstly
|
||
//
|
||
|
||
usblegctlsts &= 0xFFFF0000;
|
||
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &usblegctlsts);
|
||
|
||
//double
|
||
// if delay value is in milliseconds it doesn't appear to work.
|
||
// setting value to anything up to 65535 does not add the expected delay here.
|
||
gBS->Stall (500);
|
||
usbcmd = *((UINT32*)(UINTN)(opaddr)); // Command Register
|
||
usbsts = *((UINT32*)(UINTN)(opaddr + 4)); // Status Register
|
||
usbintr = *((UINT32*)(UINTN)(opaddr + 8)); // Interrupt Enable Register
|
||
DBG("usbcmd=%08X usbsts=%08X usbintr=%08X\n", usbcmd, usbsts, usbintr);
|
||
|
||
// clear registers to default
|
||
usbcmd = (usbcmd & 0xffffff00);
|
||
*((UINT32*)(UINTN)(opaddr)) = usbcmd;
|
||
*((UINT32*)(UINTN)(opaddr + 8)) = 0; //usbintr - clear interrupt registers
|
||
*((UINT32*)(UINTN)(opaddr + 4)) = 0x1000; //usbsts - clear status registers
|
||
Value = 1;
|
||
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
|
||
|
||
// get the results
|
||
usbcmd = *((UINT32*)(UINTN)(opaddr)); // Command Register
|
||
usbsts = *((UINT32*)(UINTN)(opaddr + 4)); // Status Register
|
||
usbintr = *((UINT32*)(UINTN)(opaddr + 8)); // Interrupt Enable Register
|
||
MsgLog("usbcmd=%08X usbsts=%08X usbintr=%08X\n", usbcmd, usbsts, usbintr);
|
||
|
||
// read 32bit USBLEGSUP (eecp+0)
|
||
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &usblegsup);
|
||
// informational only
|
||
isBIOSowned = !!((usblegsup) & (1 << (16)));
|
||
isOSowned = !!((usblegsup) & (1 << (24)));
|
||
|
||
// read 32bit USBLEGCTLSTS (eecp+4)
|
||
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &usblegctlsts);
|
||
|
||
DBG("usblegsup=%08X isOSowned=%llu isBIOSowned=%llu usblegctlsts=%08X\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
|
||
MsgLog("Legacy USB Off Done\n");
|
||
|
||
|
||
//
|
||
// Get EHCI Ownership from legacy bios
|
||
//
|
||
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &usblegsup);
|
||
isOwnershipConflict = isBIOSowned && isOSowned;
|
||
if (isOwnershipConflict) {
|
||
DBG("EHCI - Ownership conflict - attempting soft reset ...\n");
|
||
Value = 0;
|
||
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, ExtendCap + 3, 1, &Value);
|
||
TimeOut = 40;
|
||
while (TimeOut--) {
|
||
gBS->Stall (500);
|
||
|
||
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
|
||
|
||
if ((Value & 0x01000000) == 0x0) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
|
||
Value |= (0x1 << 24);
|
||
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
|
||
|
||
TimeOut = 40;
|
||
while (TimeOut--) {
|
||
gBS->Stall (500);
|
||
|
||
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
|
||
|
||
if ((Value & 0x00010000) == 0x0) {
|
||
break;
|
||
}
|
||
}
|
||
isOwnershipConflict = ((Value & 0x00010000) != 0x0);
|
||
if (isOwnershipConflict) {
|
||
// Soft reset has failed. Assume SMI being ignored
|
||
// Hard reset
|
||
DBG("Soft reset has failed - attempting hard reset ...\n");
|
||
Value = 0;
|
||
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, ExtendCap + 2, 1, &Value);
|
||
TimeOut = 40;
|
||
while (TimeOut--) {
|
||
gBS->Stall (500);
|
||
|
||
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap, 1, &Value);
|
||
|
||
if ((Value & 0x00010000) == 0x0) {
|
||
break;
|
||
}
|
||
}
|
||
// Disable further SMI events
|
||
PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &usblegctlsts);
|
||
usblegctlsts &= 0xFFFF0000;
|
||
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, ExtendCap + 0x4, 1, &usblegctlsts);
|
||
}
|
||
if (Value & 0x00010000) {
|
||
MsgLog("EHCI controller unable to take control from BIOS\n");
|
||
Status = EFI_NOT_FOUND; //Slice - why? :)
|
||
break;
|
||
}
|
||
MsgLog("USB EHCI Ownership for device %04hX value=%X\n", Pci.Hdr.DeviceId, Value);
|
||
|
||
break;
|
||
case PCI_IF_XHCI:
|
||
//
|
||
// Found the XHCI, then disable the legacy support, if present
|
||
//
|
||
DBG("USB XHCI reset for device %04hX\n", Pci.Hdr.DeviceId);
|
||
if (Pci.Hdr.VendorId != 0x8086) {
|
||
//ну ее нах эту ВИА, да и прочих, кроме Интел
|
||
DBG("skip XHCI controller Vendor=%04X\n", Pci.Hdr.VendorId);
|
||
break;
|
||
}
|
||
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) 0x10 /* HCCPARAMS1 */, 1, &HcCapParams);
|
||
ExtendCap = EFI_ERROR(Status) ? 0 : ((HcCapParams >> 14) & 0x3FFFC);
|
||
MsgLog("ExtendCap=%08X\n", ExtendCap);
|
||
while (ExtendCap) {
|
||
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) ExtendCap, 1, &Value);
|
||
if (EFI_ERROR(Status))
|
||
break;
|
||
MsgLog(" Value=%08X\n", Value);
|
||
if ((Value & 0xFF) == 1) {
|
||
//
|
||
// Do nothing if Bios Ownership clear
|
||
//
|
||
if (!(Value & (0x1 << 16)))
|
||
break;
|
||
Value |= (0x1 << 24);
|
||
(void) PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) ExtendCap, 1, &Value);
|
||
TimeOut = 40;
|
||
while (TimeOut--) {
|
||
gBS->Stall(500);
|
||
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) ExtendCap, 1, &Value);
|
||
if (EFI_ERROR(Status) || !(Value & (0x1 << 16)))
|
||
break;
|
||
}
|
||
//
|
||
// Disable all SMI in USBLEGCTLSTS
|
||
//
|
||
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) ExtendCap + 4, 1, &Value);
|
||
if (EFI_ERROR(Status))
|
||
break;
|
||
Value &= 0x1F1FEE;
|
||
Value |= 0xE0000000;
|
||
(void) PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) ExtendCap + 4, 1, &Value);
|
||
//
|
||
// Clear all ownership
|
||
//
|
||
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) ExtendCap, 1, &Value);
|
||
if (EFI_ERROR(Status))
|
||
break;
|
||
Value &= ~((0x1 << 24) | (0x1 << 16));
|
||
(void) PciIo->Mem.Write(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) ExtendCap, 1, &Value);
|
||
break;
|
||
} //Value & FF
|
||
if (!(Value & 0xFF00))
|
||
break;
|
||
ExtendCap += ((Value >> 6) & 0x3FC);
|
||
} //while ExtendCap
|
||
break;
|
||
default:
|
||
break;
|
||
} //switch class code
|
||
}
|
||
}
|
||
}
|
||
else break;
|
||
}
|
||
} else {
|
||
return Status;
|
||
}
|
||
gBS->FreePool(HandleArray);
|
||
return Status;
|
||
}
|