/* 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); }