mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-28 17:08:18 +01:00
350 lines
14 KiB
C++
350 lines
14 KiB
C++
/*
|
|
* usbfix.c
|
|
*
|
|
* Created by Slice on 21.09.11.
|
|
*
|
|
* based on works by mackerintel 2008, orByte 2006, Signal64, THeKiNG
|
|
*/
|
|
|
|
#include "Platform.h"
|
|
|
|
#ifndef DEBUG_ALL
|
|
#define DEBUG_USB 0
|
|
#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
|
|
--*/
|
|
{
|
|
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;
|
|
BOOLEAN isOwnershipConflict;
|
|
|
|
//
|
|
// Find the usb host controller
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiPciIoProtocolGuid,
|
|
NULL,
|
|
&HandleArrayCount,
|
|
&HandleArray
|
|
);
|
|
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) {
|
|
IoWrite16 (PortBase, 0x0002);
|
|
gBS->Stall (500);
|
|
IoWrite16 (PortBase+4, 0);
|
|
gBS->Stall (500);
|
|
IoWrite16 (PortBase, 0);
|
|
}
|
|
|
|
MsgLog("USB UHCI reset for device %04X\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 %04X control=0x%X\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
|
|
//
|
|
Value = 0x0002;
|
|
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x04, 1, &Value);
|
|
|
|
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;
|
|
}
|
|
|
|
// 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
|
|
DBG("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 %04X value=%X\n", Pci.Hdr.DeviceId, Value);
|
|
|
|
break;
|
|
case PCI_IF_XHCI:
|
|
//
|
|
// Found the XHCI, then disable the legacy support, if present
|
|
//
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) 0x10 /* HCCPARAMS1 */, 1, &HcCapParams);
|
|
ExtendCap = EFI_ERROR(Status) ? 0 : ((HcCapParams >> 14) & 0x3FFFC);
|
|
while (ExtendCap) {
|
|
Status = PciIo->Mem.Read(PciIo, EfiPciIoWidthUint32, 0 /* BAR0 */, (UINT64) ExtendCap, 1, &Value);
|
|
if (EFI_ERROR(Status))
|
|
break;
|
|
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 {
|
|
return Status;
|
|
}
|
|
gBS->FreePool (HandleArray);
|
|
return Status;
|
|
}
|