From 058df7b5a9cc7aaa9872eaa916b715544a8f9840 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 8 May 2017 22:10:26 +0200 Subject: [PATCH 028/229] ehci: Split core code from PCI part. On ARM often EHCI is present without PCI and just declared in device tree. So splitcore from PCI part. --- grub-core/Makefile.core.def | 1 + grub-core/bus/usb/ehci-pci.c | 208 +++++++++++++++++++++++++++++++++++++++++++ grub-core/bus/usb/ehci.c | 201 +++-------------------------------------- 3 files changed, 223 insertions(+), 187 deletions(-) create mode 100644 grub-core/bus/usb/ehci-pci.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index e4f253a205e..4745eb4d93e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -593,6 +593,7 @@ module = { module = { name = ehci; common = bus/usb/ehci.c; + pci = bus/usb/ehci-pci.c; enable = pci; }; diff --git a/grub-core/bus/usb/ehci-pci.c b/grub-core/bus/usb/ehci-pci.c new file mode 100644 index 00000000000..65e6cb57438 --- /dev/null +++ b/grub-core/bus/usb/ehci-pci.c @@ -0,0 +1,208 @@ +/* ehci.c - EHCI Support. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * GRUB 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. + * + * GRUB 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 GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define GRUB_EHCI_PCI_SBRN_REG 0x60 +#define GRUB_EHCI_ADDR_MEM_MASK (~0xff) + +/* USBLEGSUP bits and related OS OWNED byte offset */ +enum +{ + GRUB_EHCI_BIOS_OWNED = (1 << 16), + GRUB_EHCI_OS_OWNED = (1 << 24) +}; + +/* PCI iteration function... */ +static int +grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + volatile grub_uint32_t *regs; + grub_uint32_t base, base_h; + grub_uint32_t eecp_offset; + grub_uint32_t usblegsup = 0; + grub_uint64_t maxtime; + grub_uint32_t interf; + grub_uint32_t subclass; + grub_uint32_t class; + grub_uint8_t release; + grub_uint32_t class_code; + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: begin\n"); + + if (pciid == GRUB_CS5536_PCIID) + { + grub_uint64_t basereg; + + basereg = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE); + if (!(basereg & GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE)) + { + /* Shouldn't happen. */ + grub_dprintf ("ehci", "No EHCI address is assigned\n"); + return 0; + } + base = (basereg & GRUB_CS5536_MSR_USB_BASE_ADDR_MASK); + basereg |= GRUB_CS5536_MSR_USB_BASE_BUS_MASTER; + basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_ENABLED; + basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_STATUS; + basereg &= ~GRUB_CS5536_MSR_USB_BASE_SMI_ENABLE; + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE, basereg); + } + else + { + grub_pci_address_t addr; + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class_code = grub_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; + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: class OK\n"); + + /* Check Serial Bus Release Number */ + addr = grub_pci_make_address (dev, GRUB_EHCI_PCI_SBRN_REG); + release = grub_pci_read_byte (addr); + if (release != 0x20) + { + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: Wrong SBRN: %0x\n", + release); + return 0; + } + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: bus rev. num. OK\n"); + + /* Determine EHCI EHCC registers base address. */ + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + base = grub_pci_read (addr); + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG1); + base_h = grub_pci_read (addr); + /* Stop if registers are mapped above 4G - GRUB does not currently + * work with registers mapped above 4G */ + if (((base & GRUB_PCI_ADDR_MEM_TYPE_MASK) != GRUB_PCI_ADDR_MEM_TYPE_32) + && (base_h != 0)) + { + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: registers above 4G are not supported\n"); + return 0; + } + base &= GRUB_PCI_ADDR_MEM_MASK; + if (!base) + { + grub_dprintf ("ehci", + "EHCI: EHCI is not mapped\n"); + return 0; + } + + /* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */ + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word(addr, + GRUB_PCI_COMMAND_MEM_ENABLED + | GRUB_PCI_COMMAND_BUS_MASTER + | grub_pci_read_word(addr)); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: 32-bit EHCI OK\n"); + } + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of EHCC: %08x\n", + (base & GRUB_EHCI_ADDR_MEM_MASK)); + + regs = grub_pci_device_map_range (dev, + (base & GRUB_EHCI_ADDR_MEM_MASK), + 0x100); + + /* Is there EECP ? */ + eecp_offset = (grub_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 != GRUB_CS5536_PCIID && eecp_offset >= 0x40) + { + grub_pci_address_t pciaddr_eecp; + pciaddr_eecp = grub_pci_make_address (dev, eecp_offset); + + usblegsup = grub_pci_read (pciaddr_eecp); + if (usblegsup & GRUB_EHCI_BIOS_OWNED) + { + grub_boot_time ("Taking ownership of EHCI controller"); + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: EHCI owned by: BIOS\n"); + /* Ownership change - set OS_OWNED bit */ + grub_pci_write (pciaddr_eecp, usblegsup | GRUB_EHCI_OS_OWNED); + /* Ensure PCI register is written */ + grub_pci_read (pciaddr_eecp); + + /* Wait for finish of ownership change, EHCI specification + * doesn't say how long it can take... */ + maxtime = grub_get_time_ms () + 1000; + while ((grub_pci_read (pciaddr_eecp) & GRUB_EHCI_BIOS_OWNED) + && (grub_get_time_ms () < maxtime)); + if (grub_pci_read (pciaddr_eecp) & GRUB_EHCI_BIOS_OWNED) + { + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: EHCI change ownership timeout"); + /* Change ownership in "hard way" - reset BIOS ownership */ + grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED); + /* Ensure PCI register is written */ + grub_pci_read (pciaddr_eecp); + } + } + else if (usblegsup & GRUB_EHCI_OS_OWNED) + /* XXX: What to do in this case - nothing ? Can it happen ? */ + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: EHCI owned by: OS\n"); + else + { + grub_dprintf ("ehci", + "EHCI grub_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 */ + grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED); + /* Ensure PCI register is written */ + grub_pci_read (pciaddr_eecp); + } + + /* Disable SMI, just to be sure. */ + pciaddr_eecp = grub_pci_make_address (dev, eecp_offset + 4); + grub_pci_write (pciaddr_eecp, 0); + /* Ensure PCI register is written */ + grub_pci_read (pciaddr_eecp); + } + + grub_dprintf ("ehci", "inithw: EHCI grub_ehci_pci_iter: ownership OK\n"); + + grub_ehci_init_device (regs); + return 0; +} + +void +grub_ehci_pci_scan (void) +{ + grub_pci_iterate (grub_ehci_pci_iter, NULL); +} diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 5f4297bb21e..c772e76546e 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -22,13 +22,10 @@ #include #include #include -#include -#include -#include #include #include -#include #include +#include #include GRUB_MOD_LICENSE ("GPLv3+"); @@ -39,8 +36,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); * - is not supporting interrupt transfers */ -#define GRUB_EHCI_PCI_SBRN_REG 0x60 - /* Capability registers offsets */ enum { @@ -54,7 +49,6 @@ enum #define GRUB_EHCI_EECP_MASK (0xff << 8) #define GRUB_EHCI_EECP_SHIFT 8 -#define GRUB_EHCI_ADDR_MEM_MASK (~0xff) #define GRUB_EHCI_POINTER_MASK (~0x1f) /* Capability register SPARAMS bits */ @@ -85,13 +79,6 @@ enum #define GRUB_EHCI_QH_EMPTY 1 -/* USBLEGSUP bits and related OS OWNED byte offset */ -enum -{ - GRUB_EHCI_BIOS_OWNED = (1 << 16), - GRUB_EHCI_OS_OWNED = (1 << 24) -}; - /* Operational registers offsets */ enum { @@ -455,9 +442,10 @@ grub_ehci_reset (struct grub_ehci *e) sync_all_caches (e); + grub_dprintf ("ehci", "reset\n"); + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, - GRUB_EHCI_CMD_HC_RESET - | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + GRUB_EHCI_CMD_HC_RESET); /* Ensure command is written */ grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); /* XXX: How long time could take reset of HC ? */ @@ -473,116 +461,24 @@ grub_ehci_reset (struct grub_ehci *e) } /* PCI iteration function... */ -static int -grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, - void *data __attribute__ ((unused))) +void +grub_ehci_init_device (volatile void *regs) { - grub_uint8_t release; - grub_uint32_t class_code; - grub_uint32_t interf; - grub_uint32_t subclass; - grub_uint32_t class; - grub_uint32_t base, base_h; struct grub_ehci *e; - grub_uint32_t eecp_offset; grub_uint32_t fp; int i; - grub_uint32_t usblegsup = 0; - grub_uint64_t maxtime; grub_uint32_t n_ports; grub_uint8_t caplen; - grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: begin\n"); - - if (pciid == GRUB_CS5536_PCIID) - { - grub_uint64_t basereg; - - basereg = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE); - if (!(basereg & GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE)) - { - /* Shouldn't happen. */ - grub_dprintf ("ehci", "No EHCI address is assigned\n"); - return 0; - } - base = (basereg & GRUB_CS5536_MSR_USB_BASE_ADDR_MASK); - basereg |= GRUB_CS5536_MSR_USB_BASE_BUS_MASTER; - basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_ENABLED; - basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_STATUS; - basereg &= ~GRUB_CS5536_MSR_USB_BASE_SMI_ENABLE; - grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE, basereg); - } - else - { - grub_pci_address_t addr; - addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); - class_code = grub_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; - - grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: class OK\n"); - - /* Check Serial Bus Release Number */ - addr = grub_pci_make_address (dev, GRUB_EHCI_PCI_SBRN_REG); - release = grub_pci_read_byte (addr); - if (release != 0x20) - { - grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: Wrong SBRN: %0x\n", - release); - return 0; - } - grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: bus rev. num. OK\n"); - - /* Determine EHCI EHCC registers base address. */ - addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); - base = grub_pci_read (addr); - addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG1); - base_h = grub_pci_read (addr); - /* Stop if registers are mapped above 4G - GRUB does not currently - * work with registers mapped above 4G */ - if (((base & GRUB_PCI_ADDR_MEM_TYPE_MASK) != GRUB_PCI_ADDR_MEM_TYPE_32) - && (base_h != 0)) - { - grub_dprintf ("ehci", - "EHCI grub_ehci_pci_iter: registers above 4G are not supported\n"); - return 0; - } - base &= GRUB_PCI_ADDR_MEM_MASK; - if (!base) - { - grub_dprintf ("ehci", - "EHCI: EHCI is not mapped\n"); - return 0; - } - - /* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */ - addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); - grub_pci_write_word(addr, - GRUB_PCI_COMMAND_MEM_ENABLED - | GRUB_PCI_COMMAND_BUS_MASTER - | grub_pci_read_word(addr)); - - grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: 32-bit EHCI OK\n"); - } - /* Allocate memory for the controller and fill basic values. */ e = grub_zalloc (sizeof (*e)); if (!e) - return 1; + return; e->framelist_chunk = NULL; e->td_chunk = NULL; e->qh_chunk = NULL; - e->iobase_ehcc = grub_pci_device_map_range (dev, - (base & GRUB_EHCI_ADDR_MEM_MASK), - 0x100); + e->iobase_ehcc = regs; - grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of EHCC: %08x\n", - (base & GRUB_EHCI_ADDR_MEM_MASK)); grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CAPLEN: %02x\n", grub_ehci_ehcc_read8 (e, GRUB_EHCI_EHCC_CAPLEN)); grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: VERSION: %04x\n", @@ -598,7 +494,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, if (caplen & (sizeof (grub_uint32_t) - 1)) { grub_dprintf ("ehci", "Unaligned caplen\n"); - return 0; + return; } e->iobase = ((volatile grub_uint32_t *) e->iobase_ehcc + (caplen / sizeof (grub_uint32_t))); @@ -609,7 +505,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of oper. regs: %08x\n", - (base & GRUB_EHCI_ADDR_MEM_MASK) + caplen); + (grub_addr_t) e->iobase_ehcc + caplen); grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: COMMAND: %08x\n", grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: STATUS: %08x\n", @@ -625,10 +521,6 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CONFIG_FLAG: %08x\n", grub_ehci_oper_read32 (e, GRUB_EHCI_CONFIG_FLAG)); - /* Is there EECP ? */ - eecp_offset = (grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_CPARAMS) - & GRUB_EHCI_EECP_MASK) >> GRUB_EHCI_EECP_SHIFT; - /* Check format of data structures requested by EHCI */ /* XXX: In fact it is not used at any place, it is prepared for future * This implementation uses 32-bits pointers only */ @@ -732,65 +624,6 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: QH/TD init. OK\n"); - /* Determine and change ownership. */ - /* EECP offset valid in HCCPARAMS */ - /* Ownership can be changed via EECP only */ - if (pciid != GRUB_CS5536_PCIID && eecp_offset >= 0x40) - { - grub_pci_address_t pciaddr_eecp; - pciaddr_eecp = grub_pci_make_address (dev, eecp_offset); - - usblegsup = grub_pci_read (pciaddr_eecp); - if (usblegsup & GRUB_EHCI_BIOS_OWNED) - { - grub_boot_time ("Taking ownership of EHCI controller"); - grub_dprintf ("ehci", - "EHCI grub_ehci_pci_iter: EHCI owned by: BIOS\n"); - /* Ownership change - set OS_OWNED bit */ - grub_pci_write (pciaddr_eecp, usblegsup | GRUB_EHCI_OS_OWNED); - /* Ensure PCI register is written */ - grub_pci_read (pciaddr_eecp); - - /* Wait for finish of ownership change, EHCI specification - * doesn't say how long it can take... */ - maxtime = grub_get_time_ms () + 1000; - while ((grub_pci_read (pciaddr_eecp) & GRUB_EHCI_BIOS_OWNED) - && (grub_get_time_ms () < maxtime)); - if (grub_pci_read (pciaddr_eecp) & GRUB_EHCI_BIOS_OWNED) - { - grub_dprintf ("ehci", - "EHCI grub_ehci_pci_iter: EHCI change ownership timeout"); - /* Change ownership in "hard way" - reset BIOS ownership */ - grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED); - /* Ensure PCI register is written */ - grub_pci_read (pciaddr_eecp); - } - } - else if (usblegsup & GRUB_EHCI_OS_OWNED) - /* XXX: What to do in this case - nothing ? Can it happen ? */ - grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: EHCI owned by: OS\n"); - else - { - grub_dprintf ("ehci", - "EHCI grub_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 */ - grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED); - /* Ensure PCI register is written */ - grub_pci_read (pciaddr_eecp); - } - - /* Disable SMI, just to be sure. */ - pciaddr_eecp = grub_pci_make_address (dev, eecp_offset + 4); - grub_pci_write (pciaddr_eecp, 0); - /* Ensure PCI register is written */ - grub_pci_read (pciaddr_eecp); - - } - - grub_dprintf ("ehci", "inithw: EHCI grub_ehci_pci_iter: ownership OK\n"); - /* Now we can setup EHCI (maybe...) */ /* Check if EHCI is halted and halt it if not */ @@ -864,7 +697,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of oper. regs: %08x\n", - (base & GRUB_EHCI_ADDR_MEM_MASK)); + (grub_addr_t) regs); grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: COMMAND: %08x\n", grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: STATUS: %08x\n", @@ -880,7 +713,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CONFIG_FLAG: %08x\n", grub_ehci_oper_read32 (e, GRUB_EHCI_CONFIG_FLAG)); - return 0; + return; fail: if (e) @@ -894,7 +727,7 @@ fail: } grub_free (e); - return 0; + return; } static int @@ -1891,12 +1724,6 @@ grub_ehci_detect_dev (grub_usb_controller_t dev, int port, int *changed) } } -static void -grub_ehci_inithw (void) -{ - grub_pci_iterate (grub_ehci_pci_iter, NULL); -} - static grub_err_t grub_ehci_restore_hw (void) { @@ -1997,7 +1824,7 @@ GRUB_MOD_INIT (ehci) grub_stop_disk_firmware (); grub_boot_time ("Initing EHCI hardware"); - grub_ehci_inithw (); + grub_ehci_pci_scan (); grub_boot_time ("Registering EHCI driver"); grub_usb_controller_dev_register (&usb_controller); grub_boot_time ("EHCI driver registered"); -- 2.15.0