/* ehci.c - EHCI Support. */
/*
* VAS_EBOOT -- GRand Unified Bootloader
* Copyright (C) 2011 Free Software Foundation, Inc.
*
* VAS_EBOOT is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VAS_EBOOT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VAS_EBOOT. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#define VAS_EBOOT_EHCI_PCI_SBRN_REG 0x60
#define VAS_EBOOT_EHCI_ADDR_MEM_MASK (~0xff)
/* USBLEGSUP bits and related OS OWNED byte offset */
enum
{
VAS_EBOOT_EHCI_BIOS_OWNED = (1 << 16),
VAS_EBOOT_EHCI_OS_OWNED = (1 << 24)
};
/* PCI iteration function... */
static int
VasEBoot_ehci_pci_iter (VasEBoot_pci_device_t dev, VasEBoot_pci_id_t pciid,
void *data __attribute__ ((unused)))
{
volatile VasEBoot_uint32_t *regs;
VasEBoot_uint32_t base, base_h;
VasEBoot_uint32_t eecp_offset;
VasEBoot_uint32_t usblegsup = 0;
VasEBoot_uint64_t maxtime;
VasEBoot_uint32_t interf;
VasEBoot_uint32_t subclass;
VasEBoot_uint32_t class;
VasEBoot_uint8_t release;
VasEBoot_uint32_t class_code;
VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: begin\n");
if (pciid == VAS_EBOOT_CS5536_PCIID)
{
VasEBoot_uint64_t basereg;
basereg = VasEBoot_cs5536_read_msr (dev, VAS_EBOOT_CS5536_MSR_USB_EHCI_BASE);
if (!(basereg & VAS_EBOOT_CS5536_MSR_USB_BASE_MEMORY_ENABLE))
{
/* Shouldn't happen. */
VasEBoot_dprintf ("ehci", "No EHCI address is assigned\n");
return 0;
}
base = (basereg & VAS_EBOOT_CS5536_MSR_USB_BASE_ADDR_MASK);
basereg |= VAS_EBOOT_CS5536_MSR_USB_BASE_BUS_MASTER;
basereg &= ~VAS_EBOOT_CS5536_MSR_USB_BASE_PME_ENABLED;
basereg &= ~VAS_EBOOT_CS5536_MSR_USB_BASE_PME_STATUS;
basereg &= ~VAS_EBOOT_CS5536_MSR_USB_BASE_SMI_ENABLE;
VasEBoot_cs5536_write_msr (dev, VAS_EBOOT_CS5536_MSR_USB_EHCI_BASE, basereg);
}
else
{
VasEBoot_pci_address_t addr;
addr = VasEBoot_pci_make_address (dev, VAS_EBOOT_PCI_REG_CLASS);
class_code = VasEBoot_pci_read (addr) >> 8;
interf = class_code & 0xFF;
subclass = (class_code >> 8) & 0xFF;
class = class_code >> 16;
/* If this is not an EHCI controller, just return. */
if (class != 0x0c || subclass != 0x03 || interf != 0x20)
return 0;
VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: class OK\n");
/* Check Serial Bus Release Number */
addr = VasEBoot_pci_make_address (dev, VAS_EBOOT_EHCI_PCI_SBRN_REG);
release = VasEBoot_pci_read_byte (addr);
if (release != 0x20)
{
VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: Wrong SBRN: %0x\n",
release);
return 0;
}
VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: bus rev. num. OK\n");
/* Determine EHCI EHCC registers base address. */
addr = VasEBoot_pci_make_address (dev, VAS_EBOOT_PCI_REG_ADDRESS_REG0);
base = VasEBoot_pci_read (addr);
addr = VasEBoot_pci_make_address (dev, VAS_EBOOT_PCI_REG_ADDRESS_REG1);
base_h = VasEBoot_pci_read (addr);
/* Stop if registers are mapped above 4G - VAS_EBOOT does not currently
* work with registers mapped above 4G */
if (((base & VAS_EBOOT_PCI_ADDR_MEM_TYPE_MASK) != VAS_EBOOT_PCI_ADDR_MEM_TYPE_32)
&& (base_h != 0))
{
VasEBoot_dprintf ("ehci",
"EHCI VasEBoot_ehci_pci_iter: registers above 4G are not supported\n");
return 0;
}
base &= VAS_EBOOT_PCI_ADDR_MEM_MASK;
if (!base)
{
VasEBoot_dprintf ("ehci",
"EHCI: EHCI is not mapped\n");
return 0;
}
/* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */
addr = VasEBoot_pci_make_address (dev, VAS_EBOOT_PCI_REG_COMMAND);
VasEBoot_pci_write_word(addr,
VAS_EBOOT_PCI_COMMAND_MEM_ENABLED
| VAS_EBOOT_PCI_COMMAND_BUS_MASTER
| VasEBoot_pci_read_word(addr));
VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: 32-bit EHCI OK\n");
}
VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: iobase of EHCC: %08x\n",
(base & VAS_EBOOT_EHCI_ADDR_MEM_MASK));
regs = VasEBoot_pci_device_map_range (dev,
(base & VAS_EBOOT_EHCI_ADDR_MEM_MASK),
0x100);
/* Is there EECP ? */
eecp_offset = (VasEBoot_le_to_cpu32 (regs[2]) >> 8) & 0xff;
/* Determine and change ownership. */
/* EECP offset valid in HCCPARAMS */
/* Ownership can be changed via EECP only */
if (pciid != VAS_EBOOT_CS5536_PCIID && eecp_offset >= 0x40)
{
VasEBoot_pci_address_t pciaddr_eecp;
pciaddr_eecp = VasEBoot_pci_make_address (dev, eecp_offset);
usblegsup = VasEBoot_pci_read (pciaddr_eecp);
if (usblegsup & VAS_EBOOT_EHCI_BIOS_OWNED)
{
VasEBoot_boot_time ("Taking ownership of EHCI controller");
VasEBoot_dprintf ("ehci",
"EHCI VasEBoot_ehci_pci_iter: EHCI owned by: BIOS\n");
/* Ownership change - set OS_OWNED bit */
VasEBoot_pci_write (pciaddr_eecp, usblegsup | VAS_EBOOT_EHCI_OS_OWNED);
/* Ensure PCI register is written */
VasEBoot_pci_read (pciaddr_eecp);
/* Wait for finish of ownership change, EHCI specification
* doesn't say how long it can take... */
maxtime = VasEBoot_get_time_ms () + 1000;
while ((VasEBoot_pci_read (pciaddr_eecp) & VAS_EBOOT_EHCI_BIOS_OWNED)
&& (VasEBoot_get_time_ms () < maxtime));
if (VasEBoot_pci_read (pciaddr_eecp) & VAS_EBOOT_EHCI_BIOS_OWNED)
{
VasEBoot_dprintf ("ehci",
"EHCI VasEBoot_ehci_pci_iter: EHCI change ownership timeout");
/* Change ownership in "hard way" - reset BIOS ownership */
VasEBoot_pci_write (pciaddr_eecp, VAS_EBOOT_EHCI_OS_OWNED);
/* Ensure PCI register is written */
VasEBoot_pci_read (pciaddr_eecp);
}
}
else if (usblegsup & VAS_EBOOT_EHCI_OS_OWNED)
/* XXX: What to do in this case - nothing ? Can it happen ? */
VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: EHCI owned by: OS\n");
else
{
VasEBoot_dprintf ("ehci",
"EHCI VasEBoot_ehci_pci_iter: EHCI owned by: NONE\n");
/* XXX: What to do in this case ? Can it happen ?
* Is code below correct ? */
/* Ownership change - set OS_OWNED bit */
VasEBoot_pci_write (pciaddr_eecp, VAS_EBOOT_EHCI_OS_OWNED);
/* Ensure PCI register is written */
VasEBoot_pci_read (pciaddr_eecp);
}
/* Disable SMI, just to be sure. */
pciaddr_eecp = VasEBoot_pci_make_address (dev, eecp_offset + 4);
VasEBoot_pci_write (pciaddr_eecp, 0);
/* Ensure PCI register is written */
VasEBoot_pci_read (pciaddr_eecp);
}
VasEBoot_dprintf ("ehci", "inithw: EHCI VasEBoot_ehci_pci_iter: ownership OK\n");
VasEBoot_ehci_init_device (regs);
return 0;
}
void
VasEBoot_ehci_pci_scan (void)
{
VasEBoot_pci_iterate (VasEBoot_ehci_pci_iter, NULL);
}