From 2f82ea948d06bfaf90ff208782a16e8d0b017a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Nesrsta?= Date: Sat, 1 Oct 2011 20:18:47 +0200 Subject: [PATCH 01/12] =?UTF-8?q?EHCI=20implementation=20by=20Ale=C5=A1=20?= =?UTF-8?q?Nesrsta.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- grub-core/Makefile.core.def | 6 + grub-core/bus/usb/ehci.c | 1778 +++++++++++++++++++++++++++++++++++ grub-core/bus/usb/usbhub.c | 17 +- include/grub/usb.h | 5 + 4 files changed, 1802 insertions(+), 4 deletions(-) create mode 100644 grub-core/bus/usb/ehci.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index e9e4f06d8..346ec24ee 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -437,6 +437,12 @@ module = { enable = pci; }; +module = { + name = ehci; + common = bus/usb/ehci.c; + enable = pci; +}; + module = { name = pci; noemu = bus/pci.c; diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c new file mode 100644 index 000000000..12b54651b --- /dev/null +++ b/grub-core/bus/usb/ehci.c @@ -0,0 +1,1778 @@ +/* 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 +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* This simple GRUB implementation of EHCI driver: + * - assumes no IRQ + * - is not supporting isochronous transfers (iTD, siTD) + * - is not supporting interrupt transfers + */ + +#define GRUB_EHCI_PCI_SBRN_REG 0x60 + +/* Capability registers offsets */ +#define GRUB_EHCI_EHCC_CAPLEN 0x00 /* byte */ +#define GRUB_EHCI_EHCC_VERSION 0x02 /* word */ +#define GRUB_EHCI_EHCC_SPARAMS 0x04 /* dword */ +#define GRUB_EHCI_EHCC_CPARAMS 0x08 /* dword */ +#define GRUB_EHCI_EHCC_PROUTE 0x0c /* 60 bits */ + +#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 */ +#define GRUB_EHCI_SPARAMS_N_PORTS (0xf <<0) +#define GRUB_EHCI_SPARAMS_PPC (1<<4) /* Power port control */ +#define GRUB_EHCI_SPARAMS_PRR (1<<7) /* Port routing rules */ +#define GRUB_EHCI_SPARAMS_N_PCC (0xf<<8) /* No of ports per comp. */ +#define GRUB_EHCI_SPARAMS_NCC (0xf<<12) /* No of com. controllers */ +#define GRUB_EHCI_SPARAMS_P_IND (1<<16) /* Port indicators present */ +#define GRUB_EHCI_SPARAMS_DEBUG_P (0xf<<20) /* Debug port */ + +#define GRUB_EHCI_MAX_N_PORTS 15 /* Max. number of ports */ + +/* Capability register CPARAMS bits */ +#define GRUB_EHCI_CPARAMS_64BIT (1<<0) +#define GRUB_EHCI_CPARAMS_PROG_FRAMELIST (1<<1) +#define GRUB_EHCI_CPARAMS_PARK_CAP (1<<2) + +#define GRUB_EHCI_N_FRAMELIST 1024 +#define GRUB_EHCI_N_QH 256 +#define GRUB_EHCI_N_TD 640 + +#define GRUB_EHCI_QH_EMPTY 1 + +/* USBLEGSUP bits and related OS OWNED byte offset */ +#define GRUB_EHCI_BIOS_OWNED (1<<16) +#define GRUB_EHCI_OS_OWNED (1<<24) + +/* Operational registers offsets */ +#define GRUB_EHCI_COMMAND 0x00 +#define GRUB_EHCI_STATUS 0x04 +#define GRUB_EHCI_INTERRUPT 0x08 +#define GRUB_EHCI_FRAME_INDEX 0x0c +#define GRUB_EHCI_64BIT_SEL 0x10 +#define GRUB_EHCI_FL_BASE 0x14 +#define GRUB_EHCI_CUR_AL_ADDR 0x18 +#define GRUB_EHCI_CONFIG_FLAG 0x40 +#define GRUB_EHCI_PORT_STAT_CMD 0x44 + +/* Operational register COMMAND bits */ +#define GRUB_EHCI_CMD_RUNSTOP (1<<0) +#define GRUB_EHCI_CMD_HC_RESET (1<<1) +#define GRUB_EHCI_CMD_FL_SIZE (3<<2) +#define GRUB_EHCI_CMD_PS_ENABL (1<<4) +#define GRUB_EHCI_CMD_AS_ENABL (1<<5) +#define GRUB_EHCI_CMD_AS_ADV_D (1<<6) +#define GRUB_EHCI_CMD_L_HC_RES (1<<7) +#define GRUB_EHCI_CMD_AS_PARKM (3<<8) +#define GRUB_EHCI_CMD_AS_PARKE (1<<11) +#define GRUB_EHCI_CMD_INT_THRS (0xff<<16) + +/* Operational register STATUS bits */ +#define GRUB_EHCI_ST_INTERRUPT (1<<0) +#define GRUB_EHCI_ST_ERROR_INT (1<<1) +#define GRUB_EHCI_ST_PORT_CHG (1<<2) +#define GRUB_EHCI_ST_FL_ROLLOVR (1<<3) +#define GRUB_EHCI_ST_HS_ERROR (1<<4) +#define GRUB_EHCI_ST_AS_ADVANCE (1<<5) +#define GRUB_EHCI_ST_HC_HALTED (1<<12) +#define GRUB_EHCI_ST_RECLAM (1<<13) +#define GRUB_EHCI_ST_PS_STATUS (1<<14) +#define GRUB_EHCI_ST_AS_STATUS (1<<15) + +/* Operational register PORT_STAT_CMD bits */ +#define GRUB_EHCI_PORT_CONNECT (1<<0) +#define GRUB_EHCI_PORT_CONNECT_CH (1<<1) +#define GRUB_EHCI_PORT_ENABLED (1<<2) +#define GRUB_EHCI_PORT_ENABLED_CH (1<<3) +#define GRUB_EHCI_PORT_OVERCUR (1<<4) +#define GRUB_EHCI_PORT_OVERCUR_CH (1<<5) +#define GRUB_EHCI_PORT_RESUME (1<<6) +#define GRUB_EHCI_PORT_SUSPEND (1<<7) +#define GRUB_EHCI_PORT_RESET (1<<8) +#define GRUB_EHCI_PORT_LINE_STAT (3<<10) +#define GRUB_EHCI_PORT_POWER (1<<12) +#define GRUB_EHCI_PORT_OWNER (1<<13) +#define GRUB_EHCI_PORT_INDICATOR (3<<14) +#define GRUB_EHCI_PORT_TEST (0xf<<16) +#define GRUB_EHCI_PORT_WON_CONN_E (1<<20) +#define GRUB_EHCI_PORT_WON_DISC_E (1<<21) +#define GRUB_EHCI_PORT_WON_OVER_E (1<<22) + +#define GRUB_EHCI_PORT_LINE_SE0 (0<<10) +#define GRUB_EHCI_PORT_LINE_K (1<<10) +#define GRUB_EHCI_PORT_LINE_J (2<<10) +#define GRUB_EHCI_PORT_LINE_UNDEF (3<<10) +#define GRUB_EHCI_PORT_LINE_LOWSP GRUB_EHCI_PORT_LINE_K /* K state means low speed */ + +#define GRUB_EHCI_PORT_WMASK ~(GRUB_EHCI_PORT_CONNECT_CH | \ + GRUB_EHCI_PORT_ENABLED_CH | \ + GRUB_EHCI_PORT_OVERCUR_CH) + +/* Operational register CONFIGFLAGS bits */ +#define GRUB_EHCI_CF_EHCI_OWNER (1<<0) + +/* Queue Head & Transfer Descriptor constants */ +#define GRUB_EHCI_HPTR_OFF 5 /* Horiz. pointer bit offset */ +#define GRUB_EHCI_HPTR_TYPE_MASK (3<<1) +#define GRUB_EHCI_HPTR_TYPE_ITD (0<<1) +#define GRUB_EHCI_HPTR_TYPE_QH (1<<1) +#define GRUB_EHCI_HPTR_TYPE_SITD (2<<1) +#define GRUB_EHCI_HPTR_TYPE_FSTN (3<<1) + +#define GRUB_EHCI_C (1<<27) +#define GRUB_EHCI_MAXPLEN_MASK (0x7ff<<16) +#define GRUB_EHCI_MAXPLEN_OFF 16 +#define GRUB_EHCI_H (1<<15) +#define GRUB_EHCI_DTC (1<<14) +#define GRUB_EHCI_SPEED_MASK (3<<12) +#define GRUB_EHCI_SPEED_OFF 12 +#define GRUB_EHCI_SPEED_FULL (0<<12) +#define GRUB_EHCI_SPEED_LOW (1<<12) +#define GRUB_EHCI_SPEED_HIGH (2<<12) +#define GRUB_EHCI_SPEED_RESERVED (3<<12) +#define GRUB_EHCI_EP_NUM_MASK (0xf<<8) +#define GRUB_EHCI_EP_NUM_OFF 8 +#define GRUB_EHCI_DEVADDR_MASK 0x7f + +#define GRUB_EHCI_TARGET_MASK (GRUB_EHCI_EP_NUM_MASK \ + | GRUB_EHCI_DEVADDR_MASK) + +#define GRUB_EHCI_MULT_MASK (3<30) +#define GRUB_EHCI_MULT_OFF 30 +#define GRUB_EHCI_MULT_RESERVED (0<<30) +#define GRUB_EHCI_MULT_ONE (0<<30) +#define GRUB_EHCI_MULT_TWO (0<<30) +#define GRUB_EHCI_MULT_THREE (0<<30) +#define GRUB_EHCI_DEVPORT_MASK (0x7f<<23) +#define GRUB_EHCI_DEVPORT_OFF 23 +#define GRUB_EHCI_HUBADDR_MASK (0x7f<<16) +#define GRUB_EHCI_HUBADDR_OFF 16 + +#define GRUB_EHCI_TERMINATE (1<<0) + +#define GRUB_EHCI_TOGGLE (1<<31) + +#define GRUB_EHCI_TOTAL_MASK (0x7fff << 16) +#define GRUB_EHCI_TOTAL_OFF 16 +#define GRUB_EHCI_CERR_MASK (3<<10) +#define GRUB_EHCI_CERR_OFF 10 +#define GRUB_EHCI_CERR_0 (0<<10) +#define GRUB_EHCI_CERR_1 (1<<10) +#define GRUB_EHCI_CERR_2 (2<<10) +#define GRUB_EHCI_CERR_3 (3<<10) +#define GRUB_EHCI_PIDCODE_OUT (0<<8) +#define GRUB_EHCI_PIDCODE_IN (1<<8) +#define GRUB_EHCI_PIDCODE_SETUP (2<<8) +#define GRUB_EHCI_STATUS_MASK 0xff +#define GRUB_EHCI_STATUS_ACTIVE (1<<7) +#define GRUB_EHCI_STATUS_HALTED (1<<6) +#define GRUB_EHCI_STATUS_BUFERR (1<<5) +#define GRUB_EHCI_STATUS_BABBLE (1<<4) +#define GRUB_EHCI_STATUS_TRANERR (1<<3) +#define GRUB_EHCI_STATUS_MISSDMF (1<<2) +#define GRUB_EHCI_STATUS_SPLITST (1<<1) +#define GRUB_EHCI_STATUS_PINGERR (1<<0) + +#define GRUB_EHCI_BUFPTR_MASK (0xfffff<<12) +#define GRUB_EHCI_QHTDPTR_MASK 0xffffffe0 + +#define GRUB_EHCI_TD_BUF_PAGES 5 + +#define GRUB_EHCI_BUFPAGELEN 0x1000 +#define GRUB_EHCI_MAXBUFLEN 0x5000 + +#define GRUB_EHCI_QHPTR_TO_INDEX (qh) \ + ((grub_uint32_t)qh - (grub_uint32_t)e->qh) / \ + sizeof(grub_ehci_qh_t) + +struct grub_ehci_td; +struct grub_ehci_qh; +typedef volatile struct grub_ehci_td *grub_ehci_td_t; +typedef volatile struct grub_ehci_qh *grub_ehci_qh_t; + +/* EHCI Isochronous Transfer Descriptor */ +/* Currently not supported */ + +/* EHCI Split Transaction Isochronous Transfer Descriptor */ +/* Currently not supported */ + +/* EHCI Queue Element Transfer Descriptor (qTD) */ +/* Align to 32-byte boundaries */ +struct grub_ehci_td +{ + /* EHCI HW part */ + grub_uint32_t next_td; /* Pointer to next qTD */ + grub_uint32_t alt_next_td; /* Pointer to alternate next qTD */ + grub_uint32_t token; /* Toggle, Len, Interrupt, Page, Error, PID, Status */ + grub_uint32_t buffer_page[GRUB_EHCI_TD_BUF_PAGES]; /* Buffer pointer (+ cur. offset in page 0 */ + /* 64-bits part */ + grub_uint32_t buffer_page_high[GRUB_EHCI_TD_BUF_PAGES]; + /* EHCI driver part */ + grub_ehci_td_t link_td; /* pointer to next free/chained TD */ + grub_uint32_t size; + grub_uint32_t pad[1]; /* padding to some multiple of 32 bytes */ +} __attribute__ ((packed)); + +/* EHCI Queue Head */ +/* Align to 32-byte boundaries */ +/* QH allocation is made in the similar/same way as in OHCI driver, + * because unlninking QH from the Asynchronous list is not so + * trivial as on UHCI (at least it is time consuming) */ +struct grub_ehci_qh +{ + /* EHCI HW part */ + grub_uint32_t qh_hptr; /* Horiz. pointer & Terminate */ + grub_uint32_t ep_char; /* EP characteristics */ + grub_uint32_t ep_cap; /* EP capabilities */ + grub_uint32_t td_current; /* current TD link pointer */ + struct grub_ehci_td td_overlay; /* TD overlay area = 64 bytes */ + /* EHCI driver part */ + grub_uint32_t pad[4]; /* padding to some multiple of 32 bytes */ +} __attribute__ ((packed)); + +/* EHCI Periodic Frame Span Traversal Node */ +/* Currently not supported */ + +struct grub_ehci +{ + volatile grub_uint32_t *iobase_ehcc; /* Capability registers */ + volatile grub_uint32_t *iobase; /* Operational registers */ + grub_pci_address_t pcibase_eecp; /* PCI extended capability registers base */ + struct grub_pci_dma_chunk *framelist_chunk; /* Currently not used */ + volatile grub_uint32_t *framelist_virt; + grub_uint32_t framelist_phys; + struct grub_pci_dma_chunk *qh_chunk; /* GRUB_EHCI_N_QH Queue Heads */ + grub_ehci_qh_t qh_virt; + grub_uint32_t qh_phys; + struct grub_pci_dma_chunk *td_chunk; /* GRUB_EHCI_N_TD Transfer Descriptors */ + grub_ehci_td_t td_virt; + grub_uint32_t td_phys; + grub_ehci_td_t tdfree_virt; /* Free Transfer Descriptors */ + int flag64; + grub_uint32_t reset; /* bits 1-15 are flags if port was reset from connected time or not */ + struct grub_ehci *next; +}; + +static struct grub_ehci *ehci; + +/* EHCC registers access functions */ +static inline grub_uint32_t +grub_ehci_ehcc_read32 (struct grub_ehci *e, grub_uint32_t addr) +{ + return + grub_le_to_cpu32 (* + ((volatile grub_uint32_t *) ((char *) e->iobase_ehcc + + addr))); +} + +static inline grub_uint16_t +grub_ehci_ehcc_read16 (struct grub_ehci *e, grub_uint32_t addr) +{ + return + grub_le_to_cpu16 (* + ((volatile grub_uint16_t *) ((char *) e->iobase_ehcc + + addr))); +} + +static inline grub_uint8_t +grub_ehci_ehcc_read8 (struct grub_ehci *e, grub_uint32_t addr) +{ + return *((volatile grub_uint8_t *) ((char *) e->iobase_ehcc + addr)); +} + +/* Operational registers access functions */ +static inline grub_uint32_t +grub_ehci_oper_read32 (struct grub_ehci *e, grub_uint32_t addr) +{ + return + grub_le_to_cpu32 (* + ((volatile grub_uint32_t *) ((char *) e->iobase + + addr))); +} + +static inline void +grub_ehci_oper_write32 (struct grub_ehci *e, grub_uint32_t addr, + grub_uint32_t value) +{ + *((volatile grub_uint32_t *) ((char *) e->iobase + addr)) = + grub_cpu_to_le32 (value); +} + +static inline grub_uint32_t +grub_ehci_port_read (struct grub_ehci *e, grub_uint32_t port) +{ + return grub_ehci_oper_read32 (e, GRUB_EHCI_PORT_STAT_CMD + port * 4); +} + +static inline void +grub_ehci_port_resbits (struct grub_ehci *e, grub_uint32_t port, + grub_uint32_t bits) +{ + grub_ehci_oper_write32 (e, GRUB_EHCI_PORT_STAT_CMD + port * 4, + grub_ehci_port_read (e, + port) & GRUB_EHCI_PORT_WMASK & + ~(bits)); + grub_ehci_port_read (e, port); +} + +static inline void +grub_ehci_port_setbits (struct grub_ehci *e, grub_uint32_t port, + grub_uint32_t bits) +{ + grub_ehci_oper_write32 (e, GRUB_EHCI_PORT_STAT_CMD + port * 4, + (grub_ehci_port_read (e, port) & + GRUB_EHCI_PORT_WMASK) | bits); + grub_ehci_port_read (e, port); +} + +static inline void * +grub_ehci_phys2virt (grub_uint32_t phys, struct grub_pci_dma_chunk *chunk) +{ + if (!phys) + return NULL; + return (void *) (phys - grub_dma_get_phys (chunk) + + (grub_uint32_t) grub_dma_get_virt (chunk)); +} + +static inline grub_uint32_t +grub_ehci_virt2phys (void *virt, struct grub_pci_dma_chunk *chunk) +{ + if (!virt) + return 0; + return ((grub_uint32_t) virt - (grub_uint32_t) grub_dma_get_virt (chunk) + + grub_dma_get_phys (chunk)); +} + +/* Halt if EHCI HC not halted */ +static grub_err_t +grub_ehci_halt (struct grub_ehci *e) +{ + grub_uint64_t maxtime; + + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) & GRUB_EHCI_ST_HC_HALTED) == 0) /* EHCI is not halted */ + { + /* Halt EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + ~GRUB_EHCI_CMD_RUNSTOP + & grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + maxtime = grub_get_time_ms () + 1000; /* Fix: Should be 2ms ! */ + while (((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_HC_HALTED) == 0) + && (grub_get_time_ms () < maxtime)); + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_HC_HALTED) == 0) + return GRUB_ERR_TIMEOUT; + } + + return GRUB_ERR_NONE; +} + +/* EHCI HC reset */ +static grub_err_t +grub_ehci_reset (struct grub_ehci *e) +{ + grub_uint64_t maxtime; + + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_HC_RESET + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + /* XXX: How long time could take reset of HC ? */ + maxtime = grub_get_time_ms () + 1000; + while (((grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND) + & GRUB_EHCI_CMD_HC_RESET) != 0) + && (grub_get_time_ms () < maxtime)); + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND) + & GRUB_EHCI_CMD_HC_RESET) != 0) + return GRUB_ERR_TIMEOUT; + + return GRUB_ERR_NONE; +} + +/* PCI iteration function... */ +static int NESTED_FUNC_ATTR +grub_ehci_pci_iter (grub_pci_device_t dev, + grub_pci_id_t pciid __attribute__ ((unused))) +{ + grub_pci_address_t addr; + 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_dprintf ("ehci", "EHCI grub_ehci_pci_iter: begin\n"); + + 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 1; + } + + 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; + e->framelist_chunk = NULL; + e->td_chunk = NULL; + e->qh_chunk = NULL; + e->iobase_ehcc = (grub_uint32_t *) (base & GRUB_EHCI_ADDR_MEM_MASK); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of EHCC: %08x\n", + (grub_uint32_t) e->iobase_ehcc); + 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", + grub_ehci_ehcc_read16 (e, GRUB_EHCI_EHCC_VERSION)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: SPARAMS: %08x\n", + grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CPARAMS: %08x\n", + grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_CPARAMS)); + + /* Determine base address of EHCI operational registers */ + e->iobase = (grub_uint32_t *) ((grub_uint32_t) e->iobase_ehcc + + (grub_uint32_t) grub_ehci_ehcc_read8 (e, + GRUB_EHCI_EHCC_CAPLEN)); + + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: iobase of oper. regs: %08x\n", + (grub_uint32_t) e->iobase); + 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", + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: INTERRUPT: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_INTERRUPT)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: FRAME_INDEX: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_FRAME_INDEX)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: FL_BASE: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_FL_BASE)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CUR_AL_ADDR: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_CUR_AL_ADDR)); + 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; + if (eecp_offset >= 0x40) /* EECP offset valid in HCCPARAMS */ + e->pcibase_eecp = grub_pci_make_address (dev, eecp_offset); + else + e->pcibase_eecp = 0; + + /* 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 */ + e->flag64 = ((grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_CPARAMS) + & GRUB_EHCI_CPARAMS_64BIT) != 0); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: flag64=%d\n", e->flag64); + + /* Reserve a page for the frame list - it is accurate for max. + * possible size of framelist. But currently it is not used. */ + e->framelist_chunk = grub_memalign_dma32 (4096, 4096); + if (!e->framelist_chunk) + goto fail; + e->framelist_virt = grub_dma_get_virt (e->framelist_chunk); + e->framelist_phys = grub_dma_get_phys (e->framelist_chunk); + grub_memset ((void *) e->framelist_virt, 0, 4096); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: framelist mem=0x%08x. OK\n", + (grub_uint32_t) e->framelist_virt); + + /* Allocate memory for the QHs and register it in "e". */ + e->qh_chunk = grub_memalign_dma32 (4096, + sizeof (struct grub_ehci_qh) * + GRUB_EHCI_N_QH); + if (!e->qh_chunk) + goto fail; + e->qh_virt = (grub_ehci_qh_t) grub_dma_get_virt (e->qh_chunk); + e->qh_phys = grub_dma_get_phys (e->qh_chunk); + grub_memset ((void *) e->qh_virt, 0, + sizeof (struct grub_ehci_qh) * GRUB_EHCI_N_QH); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: QH mem=0x%08x. OK\n", + (grub_uint32_t) e->qh_virt); + + /* Allocate memory for the TDs and register it in "e". */ + e->td_chunk = grub_memalign_dma32 (4096, + sizeof (struct grub_ehci_td) * + GRUB_EHCI_N_TD); + if (!e->td_chunk) + goto fail; + e->td_virt = (grub_ehci_td_t) grub_dma_get_virt (e->td_chunk); + e->td_phys = grub_dma_get_phys (e->td_chunk); + grub_memset ((void *) e->td_virt, 0, + sizeof (struct grub_ehci_td) * GRUB_EHCI_N_TD); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: TD mem=0x%08x. OK\n", + (grub_uint32_t) e->td_virt); + + /* Setup all frame list pointers. Since no isochronous transfers + are supported, they all point to the (same!) queue + head with index 0. */ + fp = grub_cpu_to_le32 ((e->qh_phys & GRUB_EHCI_POINTER_MASK) + | GRUB_EHCI_HPTR_TYPE_QH); + for (i = 0; i < GRUB_EHCI_N_FRAMELIST; i++) + e->framelist_virt[i] = fp; + /* Prepare chain of all TDs and set Terminate in all TDs */ + for (i = 0; i < (GRUB_EHCI_N_TD - 1); i++) + { + e->td_virt[i].link_td = &e->td_virt[i + 1]; + e->td_virt[i].next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + e->td_virt[i].alt_next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + } + e->td_virt[GRUB_EHCI_N_TD - 1].next_td = + grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + e->td_virt[GRUB_EHCI_N_TD - 1].alt_next_td = + grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + e->tdfree_virt = e->td_virt; + /* Set Terminate in first QH, which is used in framelist */ + e->qh_virt[0].qh_hptr = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + e->qh_virt[0].td_overlay.next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + e->qh_virt[0].td_overlay.alt_next_td = + grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + /* Also set Halted bit in token */ + e->qh_virt[0].td_overlay.token = grub_cpu_to_le32 (GRUB_EHCI_STATUS_HALTED); + /* Set the H bit in first QH used for AL */ + e->qh_virt[1].ep_char = grub_cpu_to_le32 (GRUB_EHCI_H); + /* Set Terminate into TD in rest of QHs and set horizontal link + * pointer to itself - these QHs will be used for asynchronous + * schedule and they should have valid value in horiz. link */ + for (i = 1; i < GRUB_EHCI_N_QH; i++) + { + e->qh_virt[i].qh_hptr = + grub_cpu_to_le32 ((grub_ehci_virt2phys ((void *) &e->qh_virt[i], + e-> + qh_chunk) & + GRUB_EHCI_POINTER_MASK) | GRUB_EHCI_HPTR_TYPE_QH); + e->qh_virt[i].td_overlay.next_td = + grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + e->qh_virt[i].td_overlay.alt_next_td = + grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + /* Also set Halted bit in token */ + e->qh_virt[i].td_overlay.token = + grub_cpu_to_le32 (GRUB_EHCI_STATUS_HALTED); + } + + /* Note: QH 0 and QH 1 are reserved and must not be used anywhere. + * QH 0 is used as empty QH for framelist + * QH 1 is used as starting empty QH for asynchronous schedule + * QH 1 must exist at any time because at least one QH linked to + * itself must exist in asynchronous schedule + * QH 1 has the H flag set to one */ + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: QH/TD init. OK\n"); + + /* Determine and change ownership. */ + if (e->pcibase_eecp) /* Ownership can be changed via EECP only */ + { + usblegsup = grub_pci_read (e->pcibase_eecp); + if (usblegsup & GRUB_EHCI_BIOS_OWNED) + { + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: EHCI owned by: BIOS\n"); + /* Ownership change - set OS_OWNED bit */ + grub_pci_write (e->pcibase_eecp, usblegsup | GRUB_EHCI_OS_OWNED); + /* Ensure PCI register is written */ + grub_pci_read (e->pcibase_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 (e->pcibase_eecp) & GRUB_EHCI_BIOS_OWNED) + && (grub_get_time_ms () < maxtime)); + if (grub_pci_read (e->pcibase_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 (e->pcibase_eecp, GRUB_EHCI_OS_OWNED); + /* Ensure PCI register is written */ + grub_pci_read (e->pcibase_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 (e->pcibase_eecp, GRUB_EHCI_OS_OWNED); + /* Ensure PCI register is written */ + grub_pci_read (e->pcibase_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 */ + if (grub_ehci_halt (e) != GRUB_USB_ERR_NONE) + { + grub_error (GRUB_ERR_TIMEOUT, + "EHCI grub_ehci_pci_iter: EHCI halt timeout"); + goto fail; + } + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: halted OK\n"); + + /* Reset EHCI */ + if (grub_ehci_reset (e) != GRUB_USB_ERR_NONE) + { + grub_error (GRUB_ERR_TIMEOUT, + "EHCI grub_ehci_pci_iter: EHCI reset timeout"); + goto fail; + } + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: reset OK\n"); + + /* Setup list address registers */ + grub_ehci_oper_write32 (e, GRUB_EHCI_FL_BASE, e->framelist_phys); + grub_ehci_oper_write32 (e, GRUB_EHCI_CUR_AL_ADDR, + grub_ehci_virt2phys ((void *) &e->qh_virt[1], + e->qh_chunk)); + + /* Set ownership of root hub ports to EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_CONFIG_FLAG, GRUB_EHCI_CF_EHCI_OWNER); + + /* Enable asynchronous list */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_AS_ENABL + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + + /* Now should be possible to power-up and enumerate ports etc. */ + if ((grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_PPC) != 0) + { /* EHCI has port powering control */ + /* Power on all ports */ + n_ports = grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_N_PORTS; + for (i = 0; i < (int) n_ports; i++) + grub_ehci_oper_write32 (e, GRUB_EHCI_PORT_STAT_CMD + i * 4, + GRUB_EHCI_PORT_POWER + | grub_ehci_oper_read32 (e, + GRUB_EHCI_PORT_STAT_CMD + + i * 4)); + } + + /* Ensure all commands are written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + + /* Enable EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_RUNSTOP + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + + /* Link to ehci now that initialisation is successful. */ + e->next = ehci; + ehci = e; + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: OK at all\n"); + + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: iobase of oper. regs: %08x\n", + (grub_uint32_t) e->iobase); + 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", + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: INTERRUPT: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_INTERRUPT)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: FRAME_INDEX: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_FRAME_INDEX)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: FL_BASE: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_FL_BASE)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CUR_AL_ADDR: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_CUR_AL_ADDR)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CONFIG_FLAG: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_CONFIG_FLAG)); + + return 1; + +fail: + if (e) + { + if (e->td_chunk) + grub_dma_free ((void *) e->td_chunk); + if (e->qh_chunk) + grub_dma_free ((void *) e->qh_chunk); + if (e->framelist_chunk) + grub_dma_free (e->framelist_chunk); + } + grub_free (e); + + return 1; +} + +static int +grub_ehci_iterate (int (*hook) (grub_usb_controller_t dev)) +{ + struct grub_ehci *e; + struct grub_usb_controller dev; + + for (e = ehci; e; e = e->next) + { + dev.data = e; + if (hook (&dev)) + return 1; + } + + return 0; +} + +static void +grub_ehci_setup_qh (grub_ehci_qh_t qh, grub_usb_transfer_t transfer) +{ + grub_uint32_t ep_char = 0; + grub_uint32_t ep_cap = 0; + + /* Note: Another part of code is responsible to this QH is + * Halted ! But it can be linked in AL, so we cannot erase or + * change qh_hptr ! */ + /* We will not change any TD field because they should/must be + * in safe state from previous use. */ + + /* EP characteristic setup */ + /* Currently not used NAK counter (RL=0), + * C bit set if EP is not HIGH speed and is control, + * Max Packet Length is taken from transfer structure, + * H bit = 0 (because QH[1] has this bit set), + * DTC bit set to 1 because we are using our own toggle bit control, + * SPEED is selected according to value from transfer structure, + * EP number is taken from transfer structure + * "I" bit must not be set, + * Device Address is taken from transfer structure + * */ + if ((transfer->dev->speed != GRUB_USB_SPEED_HIGH) + && (transfer->type == GRUB_USB_TRANSACTION_TYPE_CONTROL)) + ep_char |= GRUB_EHCI_C; + ep_char |= (transfer->max << GRUB_EHCI_MAXPLEN_OFF) + & GRUB_EHCI_MAXPLEN_MASK; + ep_char |= GRUB_EHCI_DTC; + switch (transfer->dev->speed) + { + case GRUB_USB_SPEED_LOW: + ep_char |= GRUB_EHCI_SPEED_LOW; + break; + case GRUB_USB_SPEED_FULL: + ep_char |= GRUB_EHCI_SPEED_FULL; + break; + case GRUB_USB_SPEED_HIGH: + default: + ep_char |= GRUB_EHCI_SPEED_HIGH; + /* XXX: How we will handle unknown value of speed? */ + } + ep_char |= (transfer->endpoint << GRUB_EHCI_EP_NUM_OFF) + & GRUB_EHCI_EP_NUM_MASK; + ep_char |= transfer->devaddr & GRUB_EHCI_DEVADDR_MASK; + qh->ep_char = grub_cpu_to_le32 (ep_char); + /* EP capabilities setup */ + /* MULT field - we try to use max. number + * PortNumber - included now in device structure referenced + * inside transfer structure + * HubAddress - included now in device structure referenced + * inside transfer structure + * SplitCompletionMask - AFAIK it is ignored in asynchronous list, + * InterruptScheduleMask - AFAIK it should be zero in async. list */ + ep_cap |= GRUB_EHCI_MULT_THREE; + ep_cap |= (transfer->dev->port << GRUB_EHCI_DEVPORT_OFF) + & GRUB_EHCI_DEVPORT_MASK; + ep_cap |= (transfer->dev->hubaddr << GRUB_EHCI_HUBADDR_OFF) + & GRUB_EHCI_HUBADDR_MASK; + qh->ep_cap = grub_cpu_to_le32 (ep_cap); + + grub_dprintf ("ehci", "setup_qh: qh=%08x, not changed: qh_hptr=%08x\n", + (grub_uint32_t) qh, grub_le_to_cpu32 (qh->qh_hptr)); + grub_dprintf ("ehci", "setup_qh: ep_char=%08x, ep_cap=%08x\n", + ep_char, ep_cap); + grub_dprintf ("ehci", "setup_qh: end\n"); + grub_dprintf ("ehci", "setup_qh: not changed: td_current=%08x\n", + grub_le_to_cpu32 (qh->td_current)); + grub_dprintf ("ehci", "setup_qh: not changed: next_td=%08x\n", + grub_le_to_cpu32 (qh->td_overlay.next_td)); + grub_dprintf ("ehci", "setup_qh: not changed: alt_next_td=%08x\n", + grub_le_to_cpu32 (qh->td_overlay.alt_next_td)); + grub_dprintf ("ehci", "setup_qh: not changed: token=%08x\n", + grub_le_to_cpu32 (qh->td_overlay.token)); +} + +static grub_ehci_qh_t +grub_ehci_find_qh (struct grub_ehci *e, grub_usb_transfer_t transfer) +{ + grub_uint32_t target, mask; + int i; + grub_ehci_qh_t qh = e->qh_virt; + + /* Prepare part of EP Characteristic to find existing QH */ + target = ((transfer->endpoint << GRUB_EHCI_EP_NUM_OFF) | + transfer->devaddr) & GRUB_EHCI_TARGET_MASK; + target = grub_cpu_to_le32 (target); + mask = grub_cpu_to_le32 (GRUB_EHCI_TARGET_MASK); + + /* First try to find existing QH with proper target */ + for (i = 2; i < GRUB_EHCI_N_QH; i++) /* We ignore zero and first QH */ + { + if (!qh[i].ep_char) + break; /* Found first not-allocated QH, finish */ + if (target == (qh[i].ep_char & mask)) + { /* Found proper existing (and linked) QH, do setup of QH */ + grub_dprintf ("ehci", "find_qh: found, i=%d, QH=%08x\n", + i, (grub_uint32_t) & qh[i]); + grub_ehci_setup_qh (&qh[i], transfer); + return &qh[i]; + } + } + /* QH with target_addr does not exist, we have to add it */ + /* Have we any free QH in array ? */ + if (i >= GRUB_EHCI_N_QH) /* No. */ + { + grub_dprintf ("ehci", "find_qh: end - no free QH\n"); + return NULL; + } + grub_dprintf ("ehci", "find_qh: new, i=%d, QH=%08x\n", + i, (grub_uint32_t) & qh[i]); + /* Currently we simply take next (current) QH in array, no allocation + * function is used. It should be no problem until we will need to + * de-allocate QHs of unplugged devices. */ + /* We should preset new QH and link it into AL */ + grub_ehci_setup_qh (&qh[i], transfer); + /* Linking - this new (last) QH will point to first QH */ + qh[i].qh_hptr = grub_cpu_to_le32 (GRUB_EHCI_HPTR_TYPE_QH + | grub_ehci_virt2phys ((void *) &qh[1], + e->qh_chunk)); + /* Linking - previous last QH will point to this new QH */ + qh[i - 1].qh_hptr = grub_cpu_to_le32 (GRUB_EHCI_HPTR_TYPE_QH + | grub_ehci_virt2phys ((void *) + &qh[i], + e->qh_chunk)); + + return &qh[i]; +} + +static grub_ehci_td_t +grub_ehci_alloc_td (struct grub_ehci *e) +{ + grub_ehci_td_t ret; + + /* Check if there is a Transfer Descriptor available. */ + if (!e->tdfree_virt) + { + grub_dprintf ("ehci", "alloc_td: end - no free TD\n"); + return NULL; + } + + ret = e->tdfree_virt; /* Take current free TD */ + e->tdfree_virt = (grub_ehci_td_t) ret->link_td; /* Advance to next free TD in chain */ + ret->link_td = 0; /* Reset link_td in allocated TD */ + return ret; +} + +static void +grub_ehci_free_td (struct grub_ehci *e, grub_ehci_td_t td) +{ + td->link_td = e->tdfree_virt; /* Chain new free TD & rest */ + e->tdfree_virt = td; /* Change address of first free TD */ +} + +static void +grub_ehci_free_tds (struct grub_ehci *e, grub_ehci_td_t td, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + int i; /* Index of TD in transfer */ + grub_uint32_t token, to_transfer; + + /* Note: Another part of code is responsible to this QH is + * INACTIVE ! */ + *actual = 0; + + /* Free the TDs in this queue and set last_trans. */ + for (i = 0; td; i++) + { + grub_ehci_td_t tdprev; + + token = grub_le_to_cpu32 (td->token); + to_transfer = (token & GRUB_EHCI_TOTAL_MASK) >> GRUB_EHCI_TOTAL_OFF; + + /* Check state of TD - if it did not transfered + * whole data then set last_trans - it should be last executed TD + * in case when something went wrong. */ + if (transfer && (td->size != to_transfer)) + transfer->last_trans = i; + + *actual += td->size - to_transfer; + + /* Unlink the TD */ + tdprev = td; + td = (grub_ehci_td_t) td->link_td; + + /* Free the TD. */ + grub_ehci_free_td (e, tdprev); + } + + /* Check if last_trans was set. If not and something was + * transferred (it should be all data in this case), set it + * to index of last TD, i.e. i-1 */ + if (transfer && (transfer->last_trans < 0) && (*actual != 0)) + transfer->last_trans = i - 1; + + /* XXX: Fix it: last_trans may be set to bad index. + * Probably we should test more error flags to distinguish + * if TD was at least partialy executed or not at all. + * Generaly, we still could have problem with toggling because + * EHCI can probably split transactions into smaller parts then + * we defined in transaction even if we did not exceed MaxFrame + * length - it probably could happen at the end of microframe (?) + * and if the buffer is crossing page boundary (?). */ +} + +static grub_ehci_td_t +grub_ehci_transaction (struct grub_ehci *e, + grub_transfer_type_t type, + unsigned int toggle, grub_size_t size, + grub_uint32_t data, grub_ehci_td_t td_alt) +{ + grub_ehci_td_t td; + grub_uint32_t token; + grub_uint32_t bufadr; + int i; + + /* Test of transfer size, it can be: + * <= GRUB_EHCI_MAXBUFLEN if data aligned to page boundary + * <= GRUB_EHCI_MAXBUFLEN - GRUB_EHCI_BUFPAGELEN if not aligned + * (worst case) + */ + if ((((data % GRUB_EHCI_BUFPAGELEN) == 0) + && (size > GRUB_EHCI_MAXBUFLEN)) + || + (((data % GRUB_EHCI_BUFPAGELEN) != 0) + && (size > (GRUB_EHCI_MAXBUFLEN - GRUB_EHCI_BUFPAGELEN)))) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "too long data buffer for EHCI transaction"); + return 0; + } + + /* Grab a free Transfer Descriptor and initialize it. */ + td = grub_ehci_alloc_td (e); + if (!td) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "no transfer descriptors available for EHCI transfer"); + return 0; + } + + grub_dprintf ("ehci", + "transaction: type=%d, toggle=%d, size=%lu data=0x%x td=%p\n", + type, toggle, (unsigned long) size, data, td); + + /* Fill whole TD by zeros */ + grub_memset ((void *) td, 0, sizeof (struct grub_ehci_td)); + + /* Don't point to any TD yet, just terminate. */ + td->next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + /* Set alternate pointer. When short packet occurs, alternate TD + * will not be really fetched because it is not active. But don't + * forget, EHCI will try to fetch alternate TD every scan of AL + * until QH is halted. */ + td->alt_next_td = + grub_cpu_to_le32 (grub_ehci_virt2phys ((void *) td_alt, e->td_chunk)); + /* token: + * TOGGLE - according to toggle + * TOTAL SIZE = size + * Interrupt On Complete = FALSE, we don't need IRQ + * Current Page = 0 + * Error Counter = max. value = 3 + * PID Code - according to type + * STATUS: + * ACTIVE bit should be set to one + * SPLIT TRANS. STATE bit should be zero. It is ignored + * in HIGH speed transaction, and should be zero for LOW/FULL + * speed to indicate state Do Split Transaction */ + token = toggle ? GRUB_EHCI_TOGGLE : 0; + token |= (size << GRUB_EHCI_TOTAL_OFF) & GRUB_EHCI_TOTAL_MASK; + token |= GRUB_EHCI_CERR_3; + switch (type) + { + case GRUB_USB_TRANSFER_TYPE_IN: + token |= GRUB_EHCI_PIDCODE_IN; + break; + case GRUB_USB_TRANSFER_TYPE_OUT: + token |= GRUB_EHCI_PIDCODE_OUT; + break; + case GRUB_USB_TRANSFER_TYPE_SETUP: + token |= GRUB_EHCI_PIDCODE_SETUP; + break; + default: /* XXX: Should not happen, but what to do if it does ? */ + break; + } + token |= GRUB_EHCI_STATUS_ACTIVE; + td->token = grub_cpu_to_le32 (token); + + /* Fill buffer pointers according to size */ + bufadr = data; + td->buffer_page[0] = grub_cpu_to_le32 (bufadr); + bufadr = ((bufadr / GRUB_EHCI_BUFPAGELEN) + 1) * GRUB_EHCI_BUFPAGELEN; + for (i = 1; ((bufadr - data) < size) && (i < GRUB_EHCI_TD_BUF_PAGES); i++) + { + td->buffer_page[i] = grub_cpu_to_le32 (bufadr & GRUB_EHCI_BUFPTR_MASK); + bufadr = ((bufadr / GRUB_EHCI_BUFPAGELEN) + 1) * GRUB_EHCI_BUFPAGELEN; + } + + /* Remember data size for future use... */ + td->size = (grub_uint32_t) size; + + grub_dprintf ("ehci", "td=%08x\n", (grub_uint32_t) td); + grub_dprintf ("ehci", "HW: next_td=%08x, alt_next_td=%08x\n", + grub_le_to_cpu32 (td->next_td), + grub_le_to_cpu32 (td->alt_next_td)); + grub_dprintf ("ehci", "HW: token=%08x, buffer[0]=%08x\n", + grub_le_to_cpu32 (td->token), + grub_le_to_cpu32 (td->buffer_page[0])); + grub_dprintf ("ehci", "HW: buffer[1]=%08x, buffer[2]=%08x\n", + grub_le_to_cpu32 (td->buffer_page[1]), + grub_le_to_cpu32 (td->buffer_page[2])); + grub_dprintf ("ehci", "HW: buffer[3]=%08x, buffer[4]=%08x\n", + grub_le_to_cpu32 (td->buffer_page[3]), + grub_le_to_cpu32 (td->buffer_page[4])); + grub_dprintf ("ehci", "link_td=%08x, size=%08x\n", + (grub_uint32_t) td->link_td, td->size); + + return td; +} + +struct grub_ehci_transfer_controller_data +{ + grub_ehci_qh_t qh_virt; + grub_ehci_td_t td_first_virt; + grub_ehci_td_t td_alt_virt; + grub_ehci_td_t td_last_virt; +}; + +static grub_usb_err_t +grub_ehci_setup_transfer (grub_usb_controller_t dev, + grub_usb_transfer_t transfer) +{ + struct grub_ehci *e = (struct grub_ehci *) dev->data; + grub_ehci_td_t td = NULL; + grub_ehci_td_t td_prev = NULL; + int i; + struct grub_ehci_transfer_controller_data *cdata; + + /* Check if EHCI is running and AL is enabled */ + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_HC_HALTED) != 0) + /* XXX: Fix it: Currently we don't do anything to restart EHCI */ + return GRUB_USB_ERR_INTERNAL; + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_AS_STATUS) == 0) + /* XXX: Fix it: Currently we don't do anything to restart EHCI */ + return GRUB_USB_ERR_INTERNAL; + + /* Check if transfer is not high speed and connected to root hub. + * It should not happened but... */ + if ((transfer->dev->speed != GRUB_USB_SPEED_HIGH) + && !transfer->dev->hubaddr) + { + grub_error (GRUB_USB_ERR_BADDEVICE, + "FULL/LOW speed device on EHCI port!?!"); + return GRUB_USB_ERR_BADDEVICE; + } + + /* Allocate memory for controller transfer data. */ + cdata = grub_malloc (sizeof (*cdata)); + if (!cdata) + return GRUB_USB_ERR_INTERNAL; + cdata->td_first_virt = NULL; + + /* Allocate a queue head for the transfer queue. */ + cdata->qh_virt = grub_ehci_find_qh (e, transfer); + if (!cdata->qh_virt) + { + grub_free (cdata); + return GRUB_USB_ERR_INTERNAL; + } + + /* To detect short packet we need some additional "alternate" TD, + * allocate it first. */ + cdata->td_alt_virt = grub_ehci_alloc_td (e); + if (!cdata->td_alt_virt) + { + grub_free (cdata); + return GRUB_USB_ERR_INTERNAL; + } + /* Fill whole alternate TD by zeros (= inactive) and set + * Terminate bits and Halt bit */ + grub_memset ((void *) cdata->td_alt_virt, 0, sizeof (struct grub_ehci_td)); + cdata->td_alt_virt->next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + cdata->td_alt_virt->alt_next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + cdata->td_alt_virt->token = grub_cpu_to_le32 (GRUB_EHCI_STATUS_HALTED); + + /* Allocate appropriate number of TDs and set */ + for (i = 0; i < transfer->transcnt; i++) + { + grub_usb_transaction_t tr = &transfer->transactions[i]; + + td = grub_ehci_transaction (e, tr->pid, tr->toggle, tr->size, + tr->data, cdata->td_alt_virt); + + if (!td) /* de-allocate and free all */ + { + grub_size_t actual = 0; + + if (cdata->td_first_virt) + grub_ehci_free_tds (e, cdata->td_first_virt, NULL, &actual); + + grub_free (cdata); + return GRUB_USB_ERR_INTERNAL; + } + + /* Register new TD in cdata or previous TD */ + if (!cdata->td_first_virt) + cdata->td_first_virt = td; + else + { + td_prev->link_td = td; + td_prev->next_td = + grub_cpu_to_le32 (grub_ehci_virt2phys ((void *) td, e->td_chunk)); + } + td_prev = td; + } + + /* Remember last TD */ + cdata->td_last_virt = td; + /* Last TD should not have set alternate TD */ + cdata->td_last_virt->alt_next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + + grub_dprintf ("ehci", "setup_transfer: cdata=%08x, qh=%08x\n", + (grub_uint32_t) cdata, (grub_uint32_t) cdata->qh_virt); + grub_dprintf ("ehci", "setup_transfer: td_first=%08x, td_alt=%08x\n", + (grub_uint32_t) cdata->td_first_virt, + (grub_uint32_t) cdata->td_alt_virt); + grub_dprintf ("ehci", "setup_transfer: td_last=%08x\n", + (grub_uint32_t) cdata->td_last_virt); + + /* Start transfer: */ + /* Unlink possible alternate pointer in QH */ + cdata->qh_virt->td_overlay.alt_next_td = + grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); + /* Link new TDs with QH via next_td */ + cdata->qh_virt->td_overlay.next_td = + grub_cpu_to_le32 (grub_ehci_virt2phys + ((void *) cdata->td_first_virt, e->td_chunk)); + /* Reset Active and Halted bits in QH to activate Advance Queue, + * i.e. reset token */ + cdata->qh_virt->td_overlay.token = grub_cpu_to_le32 (0); + + /* Finito */ + transfer->controller_data = cdata; + + return GRUB_USB_ERR_NONE; +} + +/* This function expects QH is not active. + * Function set Halt bit in QH TD overlay and possibly prints + * necessary debug information. */ +static void +grub_ehci_pre_finish_transfer (grub_usb_transfer_t transfer) +{ + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + + /* Collect debug data here if necessary */ + + /* Set Halt bit in not active QH. AL will not attempt to do + * Advance Queue on QH with Halt bit set, i.e., we can then + * safely manipulate with QH TD part. */ + cdata->qh_virt->td_overlay.token = (cdata->qh_virt->td_overlay.token + | + grub_cpu_to_le32 + (GRUB_EHCI_STATUS_HALTED)) & + grub_cpu_to_le32 (~GRUB_EHCI_STATUS_ACTIVE); + + /* Print debug data here if necessary */ + +} + +static grub_usb_err_t +grub_ehci_parse_notrun (grub_usb_controller_t dev, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + + grub_dprintf ("ehci", "parse_notrun: info\n"); + + /* QH can be in any state in this case. */ + /* But EHCI or AL is not running, so QH is surely not active + * even if it has Active bit set... */ + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + grub_free (cdata); + + /* Additionally, do something with EHCI to make it running (what?) */ + /* Try enable EHCI and AL */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_RUNSTOP | GRUB_EHCI_CMD_AS_ENABL + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + + return GRUB_USB_ERR_UNRECOVERABLE; +} + +static grub_usb_err_t +grub_ehci_parse_halt (grub_usb_controller_t dev, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + grub_uint32_t token; + grub_usb_err_t err = GRUB_USB_ERR_NAK; + + /* QH should be halted and not active in this case. */ + + grub_dprintf ("ehci", "parse_halt: info\n"); + + /* Remember token before call pre-finish function */ + token = grub_le_to_cpu32 (cdata->qh_virt->td_overlay.token); + + /* Do things like in normal finish */ + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + grub_free (cdata); + + /* Evaluation of error code - currently we don't have GRUB USB error + * codes for some EHCI states, GRUB_USB_ERR_DATA is used for them. + * Order of evaluation is critical, specially bubble/stall. */ + if ((token & GRUB_EHCI_STATUS_BABBLE) != 0) + err = GRUB_USB_ERR_BABBLE; + else if ((token & GRUB_EHCI_CERR_MASK) != 0) + err = GRUB_USB_ERR_STALL; + else if ((token & GRUB_EHCI_STATUS_TRANERR) != 0) + err = GRUB_USB_ERR_DATA; + else if ((token & GRUB_EHCI_STATUS_BUFERR) != 0) + err = GRUB_USB_ERR_DATA; + else if ((token & GRUB_EHCI_STATUS_MISSDMF) != 0) + err = GRUB_USB_ERR_DATA; + + return err; +} + +static grub_usb_err_t +grub_ehci_parse_success (grub_usb_controller_t dev, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + + grub_dprintf ("ehci", "parse_success: info\n"); + + /* QH should be not active in this case, but it is not halted. */ + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + grub_free (cdata); + + return GRUB_USB_ERR_NONE; +} + + +static grub_usb_err_t +grub_ehci_check_transfer (grub_usb_controller_t dev, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + grub_uint32_t token; + + grub_dprintf ("ehci", + "check_transfer: EHCI STATUS=%08x, cdata=%08x, qh=%08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS), + (grub_uint32_t) cdata, (grub_uint32_t) cdata->qh_virt); + grub_dprintf ("ehci", "check_transfer: qh_hptr=%08x, ep_char=%08x\n", + grub_le_to_cpu32 (cdata->qh_virt->qh_hptr), + grub_le_to_cpu32 (cdata->qh_virt->ep_char)); + grub_dprintf ("ehci", "check_transfer: ep_cap=%08x, td_current=%08x\n", + grub_le_to_cpu32 (cdata->qh_virt->ep_cap), + grub_le_to_cpu32 (cdata->qh_virt->td_current)); + grub_dprintf ("ehci", "check_transfer: next_td=%08x, alt_next_td=%08x\n", + grub_le_to_cpu32 (cdata->qh_virt->td_overlay.next_td), + grub_le_to_cpu32 (cdata->qh_virt->td_overlay.alt_next_td)); + grub_dprintf ("ehci", "check_transfer: token=%08x, buffer[0]=%08x\n", + grub_le_to_cpu32 (cdata->qh_virt->td_overlay.token), + grub_le_to_cpu32 (cdata->qh_virt->td_overlay.buffer_page[0])); + + /* Check if EHCI is running and AL is enabled */ + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_HC_HALTED) != 0) + return grub_ehci_parse_notrun (dev, transfer, actual); + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_AS_STATUS) == 0) + return grub_ehci_parse_notrun (dev, transfer, actual); + + token = grub_le_to_cpu32 (cdata->qh_virt->td_overlay.token); + + /* Detect QH halted */ + if ((token & GRUB_EHCI_STATUS_HALTED) != 0) + return grub_ehci_parse_halt (dev, transfer, actual); + + /* Detect QH not active - QH is not active and no next TD */ + if ((token & GRUB_EHCI_STATUS_ACTIVE) == 0) + { + /* It could be finish at all or short packet condition */ + if ((grub_le_to_cpu32 (cdata->qh_virt->td_overlay.next_td) + & GRUB_EHCI_TERMINATE) && + ((grub_le_to_cpu32 (cdata->qh_virt->td_current) + & GRUB_EHCI_QHTDPTR_MASK) == (grub_uint32_t) cdata->td_last_virt)) + /* Normal finish */ + return grub_ehci_parse_success (dev, transfer, actual); + else if ((token & GRUB_EHCI_TOTAL_MASK) != 0) + /* Short packet condition */ + /* But currently we don't handle it - higher level will do it */ + return grub_ehci_parse_success (dev, transfer, actual); + } + + return GRUB_USB_ERR_WAIT; +} + +static grub_usb_err_t +grub_ehci_cancel_transfer (grub_usb_controller_t dev, + grub_usb_transfer_t transfer) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + grub_size_t actual; + int i; + grub_uint64_t maxtime; + + /* QH can be active and should be de-activated and halted */ + + grub_dprintf ("ehci", "cancel_transfer: begin\n"); + + /* First check if EHCI is running and AL is enabled and if not, + * there is no problem... */ + if (((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_HC_HALTED) != 0) || + ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_AS_STATUS) == 0)) + { + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, &actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + grub_free (cdata); + grub_dprintf ("ehci", "cancel_transfer: end - EHCI not running\n"); + return GRUB_USB_ERR_NONE; + } + + /* EHCI and AL are running. What to do? + * Try to Halt QH via de-scheduling QH. */ + /* Find index of current QH - we need previous QH, i.e. i-1 */ + i = ((int) (e->qh_virt - cdata->qh_virt)) / sizeof (struct grub_ehci_qh); + /* Unlink QH from AL */ + e->qh_virt[i - 1].qh_hptr = cdata->qh_virt->qh_hptr; + /* Ring the doorbell */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_AS_ADV_D + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + /* Wait answer with timeout */ + maxtime = grub_get_time_ms () + 2; + while (((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_AS_ADVANCE) == 0) + && (grub_get_time_ms () < maxtime)); + + /* We do not detect the timeout because if timeout occurs, it most + * probably means something wrong with EHCI - maybe stopped etc. */ + + /* Shut up the doorbell */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + ~GRUB_EHCI_CMD_AS_ADV_D + & grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + grub_ehci_oper_write32 (e, GRUB_EHCI_STATUS, + GRUB_EHCI_ST_AS_ADVANCE + | grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS)); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS); + + /* Now is QH out of AL and we can do anything with it... */ + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, &actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + + /* Finaly we should return QH back to the AL... */ + e->qh_virt[i - 1].qh_hptr = + grub_cpu_to_le32 (grub_ehci_virt2phys + ((void *) cdata->qh_virt, e->qh_chunk)); + grub_free (cdata); + + grub_dprintf ("ehci", "cancel_transfer: end\n"); + + return GRUB_USB_ERR_NONE; +} + +static int +grub_ehci_hubports (grub_usb_controller_t dev) +{ + struct grub_ehci *e = (struct grub_ehci *) dev->data; + grub_uint32_t portinfo; + + portinfo = grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_N_PORTS; + grub_dprintf ("ehci", "root hub ports=%d\n", portinfo); + return portinfo; +} + +static grub_err_t +grub_ehci_portstatus (grub_usb_controller_t dev, + unsigned int port, unsigned int enable) +{ + struct grub_ehci *e = (struct grub_ehci *) dev->data; + grub_uint64_t endtime; + + grub_dprintf ("ehci", "portstatus: EHCI STATUS: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS)); + grub_dprintf ("ehci", + "portstatus: begin, iobase=0x%02x, port=%d, status=0x%02x\n", + (grub_uint32_t) e->iobase, port, grub_ehci_port_read (e, + port)); + + /* In any case we need to disable port: + * - if enable==false - we should disable port + * - if enable==true we will do the reset and the specification says + * PortEnable should be FALSE in such case */ + /* Disable the port and wait for it. */ + grub_ehci_port_resbits (e, port, GRUB_EHCI_PORT_ENABLED); + endtime = grub_get_time_ms () + 1000; + while (grub_ehci_port_read (e, port) & GRUB_EHCI_PORT_ENABLED) + if (grub_get_time_ms () > endtime) + return grub_error (GRUB_ERR_IO, "portstatus: EHCI Timed out - disable"); + + if (!enable) /* We don't need reset port */ + { + grub_dprintf ("ehci", "portstatus: Disabled.\n"); + grub_dprintf ("ehci", "portstatus: end, status=0x%02x\n", + grub_ehci_port_read (e, port)); + return GRUB_ERR_NONE; + } + + grub_dprintf ("ehci", "portstatus: enable\n"); + + /* Now we will do reset - if HIGH speed device connected, it will + * result in Enabled state, otherwise port remains disabled. */ + /* Set RESET bit for 50ms */ + grub_ehci_port_setbits (e, port, GRUB_EHCI_PORT_RESET); + grub_millisleep (50); + + /* Reset RESET bit and wait for the end of reset */ + grub_ehci_port_resbits (e, port, GRUB_EHCI_PORT_RESET); + endtime = grub_get_time_ms () + 1000; + while (grub_ehci_port_read (e, port) & GRUB_EHCI_PORT_RESET) + if (grub_get_time_ms () > endtime) + return grub_error (GRUB_ERR_IO, + "portstatus: EHCI Timed out - reset port"); + /* Remember "we did the reset" - needed by detect_dev */ + e->reset |= (1 << port); + /* Test if port enabled, i.e. HIGH speed device connected */ + if ((grub_ehci_port_read (e, port) & GRUB_EHCI_PORT_ENABLED) != 0) /* yes! */ + { + grub_dprintf ("ehci", "portstatus: Enabled!\n"); + /* "Reset recovery time" (USB spec.) */ + grub_millisleep (10); + } + else /* no... */ + { + /* FULL speed device connected - change port ownership. + * It results in disconnected state of this EHCI port. */ + grub_ehci_port_setbits (e, port, GRUB_EHCI_PORT_OWNER); + return GRUB_USB_ERR_BADDEVICE; + } + + /* XXX: Fix it! There is possible problem - we can say to calling + * function that we lost device if it is FULL speed onlu via + * return value <> GRUB_ERR_NONE. It (maybe) displays also error + * message on screen - but this situation is not error, it is normal + * state! */ + + grub_dprintf ("ehci", "portstatus: end, status=0x%02x\n", + grub_ehci_port_read (e, port)); + + return GRUB_ERR_NONE; +} + +static grub_usb_speed_t +grub_ehci_detect_dev (grub_usb_controller_t dev, int port, int *changed) +{ + struct grub_ehci *e = (struct grub_ehci *) dev->data; + grub_uint32_t status, line_state; + + status = grub_ehci_port_read (e, port); + + grub_dprintf ("ehci", "detect_dev: EHCI STATUS: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS)); + grub_dprintf ("ehci", "detect_dev: iobase=0x%02x, port=%d, status=0x%02x\n", + (grub_uint32_t) e->iobase, port, status); + + /* Connect Status Change bit - it detects change of connection */ + if (status & GRUB_EHCI_PORT_CONNECT_CH) + { + *changed = 1; + /* Reset bit Connect Status Change */ + grub_ehci_port_setbits (e, port, GRUB_EHCI_PORT_CONNECT_CH); + } + else + *changed = 0; + + if (!(status & GRUB_EHCI_PORT_CONNECT)) + { /* We should reset related "reset" flag in not connected state */ + e->reset &= ~(1 << port); + return GRUB_USB_SPEED_NONE; + } + /* Detected connected state, so we should return speed. + * But we can detect only LOW speed device and only at connection + * time when PortEnabled=FALSE. FULL / HIGH speed detection is made + * later by EHCI-specific reset procedure. + * Another thing - if detected speed is LOW at connection time, + * we should change port ownership to companion controller. + * So: + * 1. If we detect connected and enabled and EHCI-owned port, + * we can say it is HIGH speed. + * 2. If we detect connected and not EHCI-owned port, we can say + * NONE speed, because such devices are not handled by EHCI. + * 3. If we detect connected, not enabled but reset port, we can say + * NONE speed, because it means FULL device connected to port and + * such devices are not handled by EHCI. + * 4. If we detect connected, not enabled and not reset port, which + * has line state != "K", we will say HIGH - it could be FULL or HIGH + * device, we will see it later after end of EHCI-specific reset + * procedure. + * 5. If we detect connected, not enabled and not reset port, which + * has line state == "K", we can say NONE speed, because LOW speed + * device is connected and we should change port ownership. */ + if ((status & GRUB_EHCI_PORT_ENABLED) != 0) /* Port already enabled, return high speed. */ + return GRUB_USB_SPEED_HIGH; + if ((status & GRUB_EHCI_PORT_OWNER) != 0) /* EHCI is not port owner */ + return GRUB_USB_SPEED_NONE; /* EHCI driver is ignoring this port. */ + if ((e->reset & (1 << port)) != 0) /* Port reset was done = FULL speed */ + return GRUB_USB_SPEED_NONE; /* EHCI driver is ignoring this port. */ + else /* Port connected but not enabled - test port speed. */ + { + line_state = status & GRUB_EHCI_PORT_LINE_STAT; + if (line_state != GRUB_EHCI_PORT_LINE_LOWSP) + return GRUB_USB_SPEED_HIGH; + /* Detected LOW speed device, we should change + * port ownership. + * XXX: Fix it!: There should be test if related companion + * controler is available ! And what to do if it does not exist ? */ + grub_ehci_port_setbits (e, port, GRUB_EHCI_PORT_OWNER); + return GRUB_USB_SPEED_NONE; /* Ignore this port */ + /* Note: Reset of PORT_OWNER bit is done by EHCI HW when + * device is really disconnected from port. + * Don't do PORT_OWNER bit reset by SW when not connected signal + * is detected in port register ! */ + } +} + +static void +grub_ehci_inithw (void) +{ + grub_pci_iterate (grub_ehci_pci_iter); +} + +static grub_err_t +grub_ehci_restore_hw (void) +{ + struct grub_ehci *e; + grub_uint32_t n_ports; + int i; + + /* We should re-enable all EHCI HW similarly as on inithw */ + for (e = ehci; e; e = e->next) + { + /* Check if EHCI is halted and halt it if not */ + if (grub_ehci_halt (e) != GRUB_USB_ERR_NONE) + grub_error (GRUB_ERR_TIMEOUT, "restore_hw: EHCI halt timeout"); + + /* Reset EHCI */ + if (grub_ehci_reset (e) != GRUB_USB_ERR_NONE) + grub_error (GRUB_ERR_TIMEOUT, "restore_hw: EHCI reset timeout"); + + /* Setup some EHCI registers and enable EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_FL_BASE, e->framelist_phys); + grub_ehci_oper_write32 (e, GRUB_EHCI_CUR_AL_ADDR, + grub_ehci_virt2phys ((void *) &e->qh_virt[1], + e->qh_chunk)); + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_RUNSTOP | + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + + /* Set ownership of root hub ports to EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_CONFIG_FLAG, + GRUB_EHCI_CF_EHCI_OWNER); + + /* Enable asynchronous list */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_AS_ENABL + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + + /* Now should be possible to power-up and enumerate ports etc. */ + if ((grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_PPC) != 0) + { /* EHCI has port powering control */ + /* Power on all ports */ + n_ports = grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_N_PORTS; + for (i = 0; i < (int) n_ports; i++) + grub_ehci_oper_write32 (e, GRUB_EHCI_PORT_STAT_CMD + i * 4, + GRUB_EHCI_PORT_POWER + | grub_ehci_oper_read32 (e, + GRUB_EHCI_PORT_STAT_CMD + + i * 4)); + } + } + + return GRUB_USB_ERR_NONE; +} + +static grub_err_t +grub_ehci_fini_hw (int noreturn __attribute__ ((unused))) +{ + struct grub_ehci *e; + + /* We should disable all EHCI HW to prevent any DMA access etc. */ + for (e = ehci; e; e = e->next) + { + /* Check if EHCI is halted and halt it if not */ + if (grub_ehci_halt (e) != GRUB_USB_ERR_NONE) + grub_error (GRUB_ERR_TIMEOUT, "restore_hw: EHCI halt timeout"); + + /* Reset EHCI */ + if (grub_ehci_reset (e) != GRUB_USB_ERR_NONE) + grub_error (GRUB_ERR_TIMEOUT, "restore_hw: EHCI reset timeout"); + } + + return GRUB_USB_ERR_NONE; +} + +static struct grub_usb_controller_dev usb_controller = { + .name = "ehci", + .iterate = grub_ehci_iterate, + .setup_transfer = grub_ehci_setup_transfer, + .check_transfer = grub_ehci_check_transfer, + .cancel_transfer = grub_ehci_cancel_transfer, + .hubports = grub_ehci_hubports, + .portstatus = grub_ehci_portstatus, + .detect_dev = grub_ehci_detect_dev +}; + +GRUB_MOD_INIT (ehci) +{ + COMPILE_TIME_ASSERT (sizeof (struct grub_ehci_td) == 64); + COMPILE_TIME_ASSERT (sizeof (struct grub_ehci_qh) == 96); + grub_ehci_inithw (); + grub_usb_controller_dev_register (&usb_controller); + grub_loader_register_preboot_hook (grub_ehci_fini_hw, grub_ehci_restore_hw, + GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); +} + +GRUB_MOD_FINI (ehci) +{ + grub_ehci_fini_hw (0); + grub_usb_controller_dev_unregister (&usb_controller); +} diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c index b59f2f51d..f18aee95f 100644 --- a/grub-core/bus/usb/usbhub.c +++ b/grub-core/bus/usb/usbhub.c @@ -44,7 +44,9 @@ static struct grub_usb_hub *hubs; /* Add a device that currently has device number 0 and resides on CONTROLLER, the Hub reported that the device speed is SPEED. */ static grub_usb_device_t -grub_usb_hub_add_dev (grub_usb_controller_t controller, grub_usb_speed_t speed) +grub_usb_hub_add_dev (grub_usb_controller_t controller, + grub_usb_speed_t speed, + int port, int hubaddr) { grub_usb_device_t dev; int i; @@ -56,6 +58,8 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller, grub_usb_speed_t speed) dev->controller = *controller; dev->speed = speed; + dev->port = port; + dev->hubaddr = hubaddr; err = grub_usb_device_initialize (dev); if (err) @@ -97,6 +101,11 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller, grub_usb_speed_t speed) dev->initialized = 1; grub_usb_devs[i] = dev; + grub_dprintf ("usb", "Added new usb device: %08x, addr=%d\n", + (grub_uint32_t)dev, i); + grub_dprintf ("usb", "speed=%d, port=%d, hubaddr=%d\n", + speed, port, hubaddr); + /* Wait "recovery interval", spec. says 2ms */ grub_millisleep (2); @@ -218,7 +227,7 @@ attach_root_port (struct grub_usb_hub *hub, int portno, grub_millisleep (10); /* Enable the port and create a device. */ - dev = grub_usb_hub_add_dev (hub->controller, speed); + dev = grub_usb_hub_add_dev (hub->controller, speed, portno, 0); hub->controller->dev->pending_reset = 0; if (! dev) return; @@ -353,7 +362,7 @@ poll_nonroot_hub (grub_usb_device_t dev) 0, i, sizeof (status), (char *) &status); grub_dprintf ("usb", "dev = %p, i = %d, status = %08x\n", - dev, i, status); + dev, i, status); if (err) continue; @@ -472,7 +481,7 @@ poll_nonroot_hub (grub_usb_device_t dev) grub_millisleep (10); /* Add the device and assign a device address to it. */ - next_dev = grub_usb_hub_add_dev (&dev->controller, speed); + next_dev = grub_usb_hub_add_dev (&dev->controller, speed, i, dev->addr); dev->controller.dev->pending_reset = 0; if (! next_dev) continue; diff --git a/include/grub/usb.h b/include/grub/usb.h index ee133dbf5..522cc0c9c 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -198,6 +198,11 @@ struct grub_usb_device grub_uint32_t statuschange; struct grub_usb_desc_endp *hub_endpoint; + + /* EHCI Split Transfer information */ + int port; + + int hubaddr; }; From 679cbb40202647f6f3060943ba9f1a449fbda8cb Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 21:03:03 +0200 Subject: [PATCH 02/12] Replace #define with enum --- grub-core/bus/usb/ehci.c | 301 ++++++++++++++++++++++----------------- 1 file changed, 173 insertions(+), 128 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 12b54651b..5666317cc 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -39,11 +39,14 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_EHCI_PCI_SBRN_REG 0x60 /* Capability registers offsets */ -#define GRUB_EHCI_EHCC_CAPLEN 0x00 /* byte */ -#define GRUB_EHCI_EHCC_VERSION 0x02 /* word */ -#define GRUB_EHCI_EHCC_SPARAMS 0x04 /* dword */ -#define GRUB_EHCI_EHCC_CPARAMS 0x08 /* dword */ -#define GRUB_EHCI_EHCC_PROUTE 0x0c /* 60 bits */ +enum + { + GRUB_EHCI_EHCC_CAPLEN = 0x00, /* byte */ + GRUB_EHCI_EHCC_VERSION = 0x02, /* word */ + GRUB_EHCI_EHCC_SPARAMS = 0x04, /* dword */ + GRUB_EHCI_EHCC_CPARAMS = 0x08, /* dword */ + GRUB_EHCI_EHCC_PROUTE = 0x0c, /* 60 bits */ + }; #define GRUB_EHCI_EECP_MASK (0xff << 8) #define GRUB_EHCI_EECP_SHIFT 8 @@ -52,20 +55,26 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_EHCI_POINTER_MASK (~0x1f) /* Capability register SPARAMS bits */ -#define GRUB_EHCI_SPARAMS_N_PORTS (0xf <<0) -#define GRUB_EHCI_SPARAMS_PPC (1<<4) /* Power port control */ -#define GRUB_EHCI_SPARAMS_PRR (1<<7) /* Port routing rules */ -#define GRUB_EHCI_SPARAMS_N_PCC (0xf<<8) /* No of ports per comp. */ -#define GRUB_EHCI_SPARAMS_NCC (0xf<<12) /* No of com. controllers */ -#define GRUB_EHCI_SPARAMS_P_IND (1<<16) /* Port indicators present */ -#define GRUB_EHCI_SPARAMS_DEBUG_P (0xf<<20) /* Debug port */ +enum + { + GRUB_EHCI_SPARAMS_N_PORTS = (0xf << 0), + GRUB_EHCI_SPARAMS_PPC = (1 << 4), /* Power port control */ + GRUB_EHCI_SPARAMS_PRR = (1 << 7), /* Port routing rules */ + GRUB_EHCI_SPARAMS_N_PCC = (0xf << 8), /* No of ports per comp. */ + GRUB_EHCI_SPARAMS_NCC = (0xf << 12), /* No of com. controllers */ + GRUB_EHCI_SPARAMS_P_IND = (1 << 16), /* Port indicators present */ + GRUB_EHCI_SPARAMS_DEBUG_P = (0xf << 20) /* Debug port */ + }; #define GRUB_EHCI_MAX_N_PORTS 15 /* Max. number of ports */ /* Capability register CPARAMS bits */ -#define GRUB_EHCI_CPARAMS_64BIT (1<<0) -#define GRUB_EHCI_CPARAMS_PROG_FRAMELIST (1<<1) -#define GRUB_EHCI_CPARAMS_PARK_CAP (1<<2) +enum + { + GRUB_EHCI_CPARAMS_64BIT = (1 << 0), + GRUB_EHCI_CPARAMS_PROG_FRAMELIST = (1 << 1), + GRUB_EHCI_CPARAMS_PARK_CAP = (1 << 2) + }; #define GRUB_EHCI_N_FRAMELIST 1024 #define GRUB_EHCI_N_QH 256 @@ -74,137 +83,177 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_EHCI_QH_EMPTY 1 /* USBLEGSUP bits and related OS OWNED byte offset */ -#define GRUB_EHCI_BIOS_OWNED (1<<16) -#define GRUB_EHCI_OS_OWNED (1<<24) +enum + { + GRUB_EHCI_BIOS_OWNED = (1 << 16), + GRUB_EHCI_OS_OWNED = (1 << 24) + }; /* Operational registers offsets */ -#define GRUB_EHCI_COMMAND 0x00 -#define GRUB_EHCI_STATUS 0x04 -#define GRUB_EHCI_INTERRUPT 0x08 -#define GRUB_EHCI_FRAME_INDEX 0x0c -#define GRUB_EHCI_64BIT_SEL 0x10 -#define GRUB_EHCI_FL_BASE 0x14 -#define GRUB_EHCI_CUR_AL_ADDR 0x18 -#define GRUB_EHCI_CONFIG_FLAG 0x40 -#define GRUB_EHCI_PORT_STAT_CMD 0x44 +enum + { + GRUB_EHCI_COMMAND = 0x00, + GRUB_EHCI_STATUS = 0x04, + GRUB_EHCI_INTERRUPT = 0x08, + GRUB_EHCI_FRAME_INDEX = 0x0c, + GRUB_EHCI_64BIT_SEL = 0x10, + GRUB_EHCI_FL_BASE = 0x14, + GRUB_EHCI_CUR_AL_ADDR = 0x18, + GRUB_EHCI_CONFIG_FLAG = 0x40, + GRUB_EHCI_PORT_STAT_CMD = 0x44 + }; /* Operational register COMMAND bits */ -#define GRUB_EHCI_CMD_RUNSTOP (1<<0) -#define GRUB_EHCI_CMD_HC_RESET (1<<1) -#define GRUB_EHCI_CMD_FL_SIZE (3<<2) -#define GRUB_EHCI_CMD_PS_ENABL (1<<4) -#define GRUB_EHCI_CMD_AS_ENABL (1<<5) -#define GRUB_EHCI_CMD_AS_ADV_D (1<<6) -#define GRUB_EHCI_CMD_L_HC_RES (1<<7) -#define GRUB_EHCI_CMD_AS_PARKM (3<<8) -#define GRUB_EHCI_CMD_AS_PARKE (1<<11) -#define GRUB_EHCI_CMD_INT_THRS (0xff<<16) +enum + { + GRUB_EHCI_CMD_RUNSTOP = (1 << 0), + GRUB_EHCI_CMD_HC_RESET = (1 << 1), + GRUB_EHCI_CMD_FL_SIZE = (3 << 2), + GRUB_EHCI_CMD_PS_ENABL = (1 << 4), + GRUB_EHCI_CMD_AS_ENABL = (1 << 5), + GRUB_EHCI_CMD_AS_ADV_D = (1 << 6), + GRUB_EHCI_CMD_L_HC_RES = (1 << 7), + GRUB_EHCI_CMD_AS_PARKM = (3 << 8), + GRUB_EHCI_CMD_AS_PARKE = (1 << 11), + GRUB_EHCI_CMD_INT_THRS = (0xff << 16) + }; /* Operational register STATUS bits */ -#define GRUB_EHCI_ST_INTERRUPT (1<<0) -#define GRUB_EHCI_ST_ERROR_INT (1<<1) -#define GRUB_EHCI_ST_PORT_CHG (1<<2) -#define GRUB_EHCI_ST_FL_ROLLOVR (1<<3) -#define GRUB_EHCI_ST_HS_ERROR (1<<4) -#define GRUB_EHCI_ST_AS_ADVANCE (1<<5) -#define GRUB_EHCI_ST_HC_HALTED (1<<12) -#define GRUB_EHCI_ST_RECLAM (1<<13) -#define GRUB_EHCI_ST_PS_STATUS (1<<14) -#define GRUB_EHCI_ST_AS_STATUS (1<<15) +enum + { + GRUB_EHCI_ST_INTERRUPT = (1 << 0), + GRUB_EHCI_ST_ERROR_INT = (1 << 1), + GRUB_EHCI_ST_PORT_CHG = (1 << 2), + GRUB_EHCI_ST_FL_ROLLOVR = (1 << 3), + GRUB_EHCI_ST_HS_ERROR = (1 << 4), + GRUB_EHCI_ST_AS_ADVANCE = (1 << 5), + GRUB_EHCI_ST_HC_HALTED = (1 << 12), + GRUB_EHCI_ST_RECLAM = (1 << 13), + GRUB_EHCI_ST_PS_STATUS = (1 << 14), + GRUB_EHCI_ST_AS_STATUS = (1 << 15) + }; /* Operational register PORT_STAT_CMD bits */ -#define GRUB_EHCI_PORT_CONNECT (1<<0) -#define GRUB_EHCI_PORT_CONNECT_CH (1<<1) -#define GRUB_EHCI_PORT_ENABLED (1<<2) -#define GRUB_EHCI_PORT_ENABLED_CH (1<<3) -#define GRUB_EHCI_PORT_OVERCUR (1<<4) -#define GRUB_EHCI_PORT_OVERCUR_CH (1<<5) -#define GRUB_EHCI_PORT_RESUME (1<<6) -#define GRUB_EHCI_PORT_SUSPEND (1<<7) -#define GRUB_EHCI_PORT_RESET (1<<8) -#define GRUB_EHCI_PORT_LINE_STAT (3<<10) -#define GRUB_EHCI_PORT_POWER (1<<12) -#define GRUB_EHCI_PORT_OWNER (1<<13) -#define GRUB_EHCI_PORT_INDICATOR (3<<14) -#define GRUB_EHCI_PORT_TEST (0xf<<16) -#define GRUB_EHCI_PORT_WON_CONN_E (1<<20) -#define GRUB_EHCI_PORT_WON_DISC_E (1<<21) -#define GRUB_EHCI_PORT_WON_OVER_E (1<<22) +enum + { + GRUB_EHCI_PORT_CONNECT = (1<<0), + GRUB_EHCI_PORT_CONNECT_CH = (1<<1), + GRUB_EHCI_PORT_ENABLED = (1<<2), + GRUB_EHCI_PORT_ENABLED_CH = (1<<3), + GRUB_EHCI_PORT_OVERCUR = (1<<4), + GRUB_EHCI_PORT_OVERCUR_CH = (1<<5), + GRUB_EHCI_PORT_RESUME = (1<<6), + GRUB_EHCI_PORT_SUSPEND = (1<<7), + GRUB_EHCI_PORT_RESET = (1<<8), + GRUB_EHCI_PORT_LINE_STAT = (3<<10), + GRUB_EHCI_PORT_POWER = (1<<12), + GRUB_EHCI_PORT_OWNER = (1<<13), + GRUB_EHCI_PORT_INDICATOR = (3<<14), + GRUB_EHCI_PORT_TEST = (0xf<<16), + GRUB_EHCI_PORT_WON_CONN_E = (1<<20), + GRUB_EHCI_PORT_WON_DISC_E = (1<<21), + GRUB_EHCI_PORT_WON_OVER_E = (1<<22), -#define GRUB_EHCI_PORT_LINE_SE0 (0<<10) -#define GRUB_EHCI_PORT_LINE_K (1<<10) -#define GRUB_EHCI_PORT_LINE_J (2<<10) -#define GRUB_EHCI_PORT_LINE_UNDEF (3<<10) -#define GRUB_EHCI_PORT_LINE_LOWSP GRUB_EHCI_PORT_LINE_K /* K state means low speed */ - -#define GRUB_EHCI_PORT_WMASK ~(GRUB_EHCI_PORT_CONNECT_CH | \ - GRUB_EHCI_PORT_ENABLED_CH | \ - GRUB_EHCI_PORT_OVERCUR_CH) + GRUB_EHCI_PORT_LINE_SE0 = (0<<10), + GRUB_EHCI_PORT_LINE_K = (1<<10), + GRUB_EHCI_PORT_LINE_J = (2<<10), + GRUB_EHCI_PORT_LINE_UNDEF = (3<<10), + GRUB_EHCI_PORT_LINE_LOWSP = GRUB_EHCI_PORT_LINE_K, /* K state means low speed */ + GRUB_EHCI_PORT_WMASK = ~(GRUB_EHCI_PORT_CONNECT_CH + | GRUB_EHCI_PORT_ENABLED_CH + | GRUB_EHCI_PORT_OVERCUR_CH) + }; /* Operational register CONFIGFLAGS bits */ -#define GRUB_EHCI_CF_EHCI_OWNER (1<<0) +enum + { + GRUB_EHCI_CF_EHCI_OWNER = (1<<0) + }; /* Queue Head & Transfer Descriptor constants */ #define GRUB_EHCI_HPTR_OFF 5 /* Horiz. pointer bit offset */ -#define GRUB_EHCI_HPTR_TYPE_MASK (3<<1) -#define GRUB_EHCI_HPTR_TYPE_ITD (0<<1) -#define GRUB_EHCI_HPTR_TYPE_QH (1<<1) -#define GRUB_EHCI_HPTR_TYPE_SITD (2<<1) -#define GRUB_EHCI_HPTR_TYPE_FSTN (3<<1) +enum + { + GRUB_EHCI_HPTR_TYPE_MASK = (3<<1), + GRUB_EHCI_HPTR_TYPE_ITD = (0<<1), + GRUB_EHCI_HPTR_TYPE_QH = (1<<1), + GRUB_EHCI_HPTR_TYPE_SITD = (2<<1), + GRUB_EHCI_HPTR_TYPE_FSTN = (3<<1) + }; -#define GRUB_EHCI_C (1<<27) -#define GRUB_EHCI_MAXPLEN_MASK (0x7ff<<16) -#define GRUB_EHCI_MAXPLEN_OFF 16 -#define GRUB_EHCI_H (1<<15) -#define GRUB_EHCI_DTC (1<<14) -#define GRUB_EHCI_SPEED_MASK (3<<12) -#define GRUB_EHCI_SPEED_OFF 12 -#define GRUB_EHCI_SPEED_FULL (0<<12) -#define GRUB_EHCI_SPEED_LOW (1<<12) -#define GRUB_EHCI_SPEED_HIGH (2<<12) -#define GRUB_EHCI_SPEED_RESERVED (3<<12) -#define GRUB_EHCI_EP_NUM_MASK (0xf<<8) -#define GRUB_EHCI_EP_NUM_OFF 8 -#define GRUB_EHCI_DEVADDR_MASK 0x7f +enum + { + GRUB_EHCI_C = (1<<27), + GRUB_EHCI_MAXPLEN_MASK = (0x7ff<<16), + GRUB_EHCI_H = (1<<15), + GRUB_EHCI_DTC = (1<<14), + GRUB_EHCI_SPEED_MASK = (3<<12), + GRUB_EHCI_SPEED_FULL = (0<<12), + GRUB_EHCI_SPEED_LOW = (1<<12), + GRUB_EHCI_SPEED_HIGH = (2<<12), + GRUB_EHCI_SPEED_RESERVED = (3<<12), + GRUB_EHCI_EP_NUM_MASK = (0xf<<8), + GRUB_EHCI_DEVADDR_MASK = 0x7f, + GRUB_EHCI_TARGET_MASK = (GRUB_EHCI_EP_NUM_MASK + | GRUB_EHCI_DEVADDR_MASK) + }; -#define GRUB_EHCI_TARGET_MASK (GRUB_EHCI_EP_NUM_MASK \ - | GRUB_EHCI_DEVADDR_MASK) +enum + { + GRUB_EHCI_MAXPLEN_OFF = 16, + GRUB_EHCI_SPEED_OFF = 12, + GRUB_EHCI_EP_NUM_OFF = 8 + }; -#define GRUB_EHCI_MULT_MASK (3<30) -#define GRUB_EHCI_MULT_OFF 30 -#define GRUB_EHCI_MULT_RESERVED (0<<30) -#define GRUB_EHCI_MULT_ONE (0<<30) -#define GRUB_EHCI_MULT_TWO (0<<30) -#define GRUB_EHCI_MULT_THREE (0<<30) -#define GRUB_EHCI_DEVPORT_MASK (0x7f<<23) -#define GRUB_EHCI_DEVPORT_OFF 23 -#define GRUB_EHCI_HUBADDR_MASK (0x7f<<16) -#define GRUB_EHCI_HUBADDR_OFF 16 +enum + { + GRUB_EHCI_MULT_MASK = (3<<30), + GRUB_EHCI_MULT_RESERVED = (0<<30), + GRUB_EHCI_MULT_ONE = (0<<30), + GRUB_EHCI_MULT_TWO = (0<<30), + GRUB_EHCI_MULT_THREE = (0<<30), + GRUB_EHCI_DEVPORT_MASK = (0x7f<<23), + GRUB_EHCI_HUBADDR_MASK = (0x7f<<16) + }; + +enum + { + GRUB_EHCI_MULT_OFF = 30, + GRUB_EHCI_DEVPORT_OFF = 23, + GRUB_EHCI_HUBADDR_OFF = 16 + }; #define GRUB_EHCI_TERMINATE (1<<0) #define GRUB_EHCI_TOGGLE (1<<31) -#define GRUB_EHCI_TOTAL_MASK (0x7fff << 16) -#define GRUB_EHCI_TOTAL_OFF 16 -#define GRUB_EHCI_CERR_MASK (3<<10) -#define GRUB_EHCI_CERR_OFF 10 -#define GRUB_EHCI_CERR_0 (0<<10) -#define GRUB_EHCI_CERR_1 (1<<10) -#define GRUB_EHCI_CERR_2 (2<<10) -#define GRUB_EHCI_CERR_3 (3<<10) -#define GRUB_EHCI_PIDCODE_OUT (0<<8) -#define GRUB_EHCI_PIDCODE_IN (1<<8) -#define GRUB_EHCI_PIDCODE_SETUP (2<<8) -#define GRUB_EHCI_STATUS_MASK 0xff -#define GRUB_EHCI_STATUS_ACTIVE (1<<7) -#define GRUB_EHCI_STATUS_HALTED (1<<6) -#define GRUB_EHCI_STATUS_BUFERR (1<<5) -#define GRUB_EHCI_STATUS_BABBLE (1<<4) -#define GRUB_EHCI_STATUS_TRANERR (1<<3) -#define GRUB_EHCI_STATUS_MISSDMF (1<<2) -#define GRUB_EHCI_STATUS_SPLITST (1<<1) -#define GRUB_EHCI_STATUS_PINGERR (1<<0) +enum + { + GRUB_EHCI_TOTAL_MASK = (0x7fff << 16), + GRUB_EHCI_CERR_MASK = (3<<10), + GRUB_EHCI_CERR_0 = (0<<10), + GRUB_EHCI_CERR_1 = (1<<10), + GRUB_EHCI_CERR_2 = (2<<10), + GRUB_EHCI_CERR_3 = (3<<10), + GRUB_EHCI_PIDCODE_OUT = (0<<8), + GRUB_EHCI_PIDCODE_IN = (1<<8), + GRUB_EHCI_PIDCODE_SETUP = (2<<8), + GRUB_EHCI_STATUS_MASK = 0xff, + GRUB_EHCI_STATUS_ACTIVE = (1<<7), + GRUB_EHCI_STATUS_HALTED = (1<<6), + GRUB_EHCI_STATUS_BUFERR = (1<<5), + GRUB_EHCI_STATUS_BABBLE = (1<<4), + GRUB_EHCI_STATUS_TRANERR = (1<<3), + GRUB_EHCI_STATUS_MISSDMF = (1<<2), + GRUB_EHCI_STATUS_SPLITST = (1<<1), + GRUB_EHCI_STATUS_PINGERR = (1<<0) + }; + +enum + { + GRUB_EHCI_TOTAL_OFF = 16, + GRUB_EHCI_CERR_OFF = 10 + }; #define GRUB_EHCI_BUFPTR_MASK (0xfffff<<12) #define GRUB_EHCI_QHTDPTR_MASK 0xffffffe0 @@ -214,10 +263,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_EHCI_BUFPAGELEN 0x1000 #define GRUB_EHCI_MAXBUFLEN 0x5000 -#define GRUB_EHCI_QHPTR_TO_INDEX (qh) \ - ((grub_uint32_t)qh - (grub_uint32_t)e->qh) / \ - sizeof(grub_ehci_qh_t) - struct grub_ehci_td; struct grub_ehci_qh; typedef volatile struct grub_ehci_td *grub_ehci_td_t; From 0e7ebb0569d65ce29d7487d7775d12197d6a6f4f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 21:04:17 +0200 Subject: [PATCH 03/12] run indent on ehci.c --- grub-core/bus/usb/ehci.c | 312 +++++++++++++++++++-------------------- 1 file changed, 155 insertions(+), 157 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 5666317cc..f8b400314 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -40,13 +40,13 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* Capability registers offsets */ enum - { - GRUB_EHCI_EHCC_CAPLEN = 0x00, /* byte */ - GRUB_EHCI_EHCC_VERSION = 0x02, /* word */ - GRUB_EHCI_EHCC_SPARAMS = 0x04, /* dword */ - GRUB_EHCI_EHCC_CPARAMS = 0x08, /* dword */ - GRUB_EHCI_EHCC_PROUTE = 0x0c, /* 60 bits */ - }; +{ + GRUB_EHCI_EHCC_CAPLEN = 0x00, /* byte */ + GRUB_EHCI_EHCC_VERSION = 0x02, /* word */ + GRUB_EHCI_EHCC_SPARAMS = 0x04, /* dword */ + GRUB_EHCI_EHCC_CPARAMS = 0x08, /* dword */ + GRUB_EHCI_EHCC_PROUTE = 0x0c, /* 60 bits */ +}; #define GRUB_EHCI_EECP_MASK (0xff << 8) #define GRUB_EHCI_EECP_SHIFT 8 @@ -56,25 +56,25 @@ enum /* Capability register SPARAMS bits */ enum - { - GRUB_EHCI_SPARAMS_N_PORTS = (0xf << 0), - GRUB_EHCI_SPARAMS_PPC = (1 << 4), /* Power port control */ - GRUB_EHCI_SPARAMS_PRR = (1 << 7), /* Port routing rules */ - GRUB_EHCI_SPARAMS_N_PCC = (0xf << 8), /* No of ports per comp. */ - GRUB_EHCI_SPARAMS_NCC = (0xf << 12), /* No of com. controllers */ - GRUB_EHCI_SPARAMS_P_IND = (1 << 16), /* Port indicators present */ - GRUB_EHCI_SPARAMS_DEBUG_P = (0xf << 20) /* Debug port */ - }; +{ + GRUB_EHCI_SPARAMS_N_PORTS = (0xf << 0), + GRUB_EHCI_SPARAMS_PPC = (1 << 4), /* Power port control */ + GRUB_EHCI_SPARAMS_PRR = (1 << 7), /* Port routing rules */ + GRUB_EHCI_SPARAMS_N_PCC = (0xf << 8), /* No of ports per comp. */ + GRUB_EHCI_SPARAMS_NCC = (0xf << 12), /* No of com. controllers */ + GRUB_EHCI_SPARAMS_P_IND = (1 << 16), /* Port indicators present */ + GRUB_EHCI_SPARAMS_DEBUG_P = (0xf << 20) /* Debug port */ +}; #define GRUB_EHCI_MAX_N_PORTS 15 /* Max. number of ports */ /* Capability register CPARAMS bits */ enum - { - GRUB_EHCI_CPARAMS_64BIT = (1 << 0), - GRUB_EHCI_CPARAMS_PROG_FRAMELIST = (1 << 1), - GRUB_EHCI_CPARAMS_PARK_CAP = (1 << 2) - }; +{ + GRUB_EHCI_CPARAMS_64BIT = (1 << 0), + GRUB_EHCI_CPARAMS_PROG_FRAMELIST = (1 << 1), + GRUB_EHCI_CPARAMS_PARK_CAP = (1 << 2) +}; #define GRUB_EHCI_N_FRAMELIST 1024 #define GRUB_EHCI_N_QH 256 @@ -84,176 +84,175 @@ enum /* USBLEGSUP bits and related OS OWNED byte offset */ enum - { - GRUB_EHCI_BIOS_OWNED = (1 << 16), - GRUB_EHCI_OS_OWNED = (1 << 24) - }; +{ + GRUB_EHCI_BIOS_OWNED = (1 << 16), + GRUB_EHCI_OS_OWNED = (1 << 24) +}; /* Operational registers offsets */ enum - { - GRUB_EHCI_COMMAND = 0x00, - GRUB_EHCI_STATUS = 0x04, - GRUB_EHCI_INTERRUPT = 0x08, - GRUB_EHCI_FRAME_INDEX = 0x0c, - GRUB_EHCI_64BIT_SEL = 0x10, - GRUB_EHCI_FL_BASE = 0x14, - GRUB_EHCI_CUR_AL_ADDR = 0x18, - GRUB_EHCI_CONFIG_FLAG = 0x40, - GRUB_EHCI_PORT_STAT_CMD = 0x44 - }; +{ + GRUB_EHCI_COMMAND = 0x00, + GRUB_EHCI_STATUS = 0x04, + GRUB_EHCI_INTERRUPT = 0x08, + GRUB_EHCI_FRAME_INDEX = 0x0c, + GRUB_EHCI_64BIT_SEL = 0x10, + GRUB_EHCI_FL_BASE = 0x14, + GRUB_EHCI_CUR_AL_ADDR = 0x18, + GRUB_EHCI_CONFIG_FLAG = 0x40, + GRUB_EHCI_PORT_STAT_CMD = 0x44 +}; /* Operational register COMMAND bits */ enum - { - GRUB_EHCI_CMD_RUNSTOP = (1 << 0), - GRUB_EHCI_CMD_HC_RESET = (1 << 1), - GRUB_EHCI_CMD_FL_SIZE = (3 << 2), - GRUB_EHCI_CMD_PS_ENABL = (1 << 4), - GRUB_EHCI_CMD_AS_ENABL = (1 << 5), - GRUB_EHCI_CMD_AS_ADV_D = (1 << 6), - GRUB_EHCI_CMD_L_HC_RES = (1 << 7), - GRUB_EHCI_CMD_AS_PARKM = (3 << 8), - GRUB_EHCI_CMD_AS_PARKE = (1 << 11), - GRUB_EHCI_CMD_INT_THRS = (0xff << 16) - }; +{ + GRUB_EHCI_CMD_RUNSTOP = (1 << 0), + GRUB_EHCI_CMD_HC_RESET = (1 << 1), + GRUB_EHCI_CMD_FL_SIZE = (3 << 2), + GRUB_EHCI_CMD_PS_ENABL = (1 << 4), + GRUB_EHCI_CMD_AS_ENABL = (1 << 5), + GRUB_EHCI_CMD_AS_ADV_D = (1 << 6), + GRUB_EHCI_CMD_L_HC_RES = (1 << 7), + GRUB_EHCI_CMD_AS_PARKM = (3 << 8), + GRUB_EHCI_CMD_AS_PARKE = (1 << 11), + GRUB_EHCI_CMD_INT_THRS = (0xff << 16) +}; /* Operational register STATUS bits */ enum - { - GRUB_EHCI_ST_INTERRUPT = (1 << 0), - GRUB_EHCI_ST_ERROR_INT = (1 << 1), - GRUB_EHCI_ST_PORT_CHG = (1 << 2), - GRUB_EHCI_ST_FL_ROLLOVR = (1 << 3), - GRUB_EHCI_ST_HS_ERROR = (1 << 4), - GRUB_EHCI_ST_AS_ADVANCE = (1 << 5), - GRUB_EHCI_ST_HC_HALTED = (1 << 12), - GRUB_EHCI_ST_RECLAM = (1 << 13), - GRUB_EHCI_ST_PS_STATUS = (1 << 14), - GRUB_EHCI_ST_AS_STATUS = (1 << 15) - }; +{ + GRUB_EHCI_ST_INTERRUPT = (1 << 0), + GRUB_EHCI_ST_ERROR_INT = (1 << 1), + GRUB_EHCI_ST_PORT_CHG = (1 << 2), + GRUB_EHCI_ST_FL_ROLLOVR = (1 << 3), + GRUB_EHCI_ST_HS_ERROR = (1 << 4), + GRUB_EHCI_ST_AS_ADVANCE = (1 << 5), + GRUB_EHCI_ST_HC_HALTED = (1 << 12), + GRUB_EHCI_ST_RECLAM = (1 << 13), + GRUB_EHCI_ST_PS_STATUS = (1 << 14), + GRUB_EHCI_ST_AS_STATUS = (1 << 15) +}; /* Operational register PORT_STAT_CMD bits */ enum - { - GRUB_EHCI_PORT_CONNECT = (1<<0), - GRUB_EHCI_PORT_CONNECT_CH = (1<<1), - GRUB_EHCI_PORT_ENABLED = (1<<2), - GRUB_EHCI_PORT_ENABLED_CH = (1<<3), - GRUB_EHCI_PORT_OVERCUR = (1<<4), - GRUB_EHCI_PORT_OVERCUR_CH = (1<<5), - GRUB_EHCI_PORT_RESUME = (1<<6), - GRUB_EHCI_PORT_SUSPEND = (1<<7), - GRUB_EHCI_PORT_RESET = (1<<8), - GRUB_EHCI_PORT_LINE_STAT = (3<<10), - GRUB_EHCI_PORT_POWER = (1<<12), - GRUB_EHCI_PORT_OWNER = (1<<13), - GRUB_EHCI_PORT_INDICATOR = (3<<14), - GRUB_EHCI_PORT_TEST = (0xf<<16), - GRUB_EHCI_PORT_WON_CONN_E = (1<<20), - GRUB_EHCI_PORT_WON_DISC_E = (1<<21), - GRUB_EHCI_PORT_WON_OVER_E = (1<<22), +{ + GRUB_EHCI_PORT_CONNECT = (1 << 0), + GRUB_EHCI_PORT_CONNECT_CH = (1 << 1), + GRUB_EHCI_PORT_ENABLED = (1 << 2), + GRUB_EHCI_PORT_ENABLED_CH = (1 << 3), + GRUB_EHCI_PORT_OVERCUR = (1 << 4), + GRUB_EHCI_PORT_OVERCUR_CH = (1 << 5), + GRUB_EHCI_PORT_RESUME = (1 << 6), + GRUB_EHCI_PORT_SUSPEND = (1 << 7), + GRUB_EHCI_PORT_RESET = (1 << 8), + GRUB_EHCI_PORT_LINE_STAT = (3 << 10), + GRUB_EHCI_PORT_POWER = (1 << 12), + GRUB_EHCI_PORT_OWNER = (1 << 13), + GRUB_EHCI_PORT_INDICATOR = (3 << 14), + GRUB_EHCI_PORT_TEST = (0xf << 16), + GRUB_EHCI_PORT_WON_CONN_E = (1 << 20), + GRUB_EHCI_PORT_WON_DISC_E = (1 << 21), + GRUB_EHCI_PORT_WON_OVER_E = (1 << 22), - GRUB_EHCI_PORT_LINE_SE0 = (0<<10), - GRUB_EHCI_PORT_LINE_K = (1<<10), - GRUB_EHCI_PORT_LINE_J = (2<<10), - GRUB_EHCI_PORT_LINE_UNDEF = (3<<10), - GRUB_EHCI_PORT_LINE_LOWSP = GRUB_EHCI_PORT_LINE_K, /* K state means low speed */ - GRUB_EHCI_PORT_WMASK = ~(GRUB_EHCI_PORT_CONNECT_CH - | GRUB_EHCI_PORT_ENABLED_CH - | GRUB_EHCI_PORT_OVERCUR_CH) - }; + GRUB_EHCI_PORT_LINE_SE0 = (0 << 10), + GRUB_EHCI_PORT_LINE_K = (1 << 10), + GRUB_EHCI_PORT_LINE_J = (2 << 10), + GRUB_EHCI_PORT_LINE_UNDEF = (3 << 10), + GRUB_EHCI_PORT_LINE_LOWSP = GRUB_EHCI_PORT_LINE_K, /* K state means low speed */ + GRUB_EHCI_PORT_WMASK = ~(GRUB_EHCI_PORT_CONNECT_CH + | GRUB_EHCI_PORT_ENABLED_CH + | GRUB_EHCI_PORT_OVERCUR_CH) +}; /* Operational register CONFIGFLAGS bits */ enum - { - GRUB_EHCI_CF_EHCI_OWNER = (1<<0) - }; +{ + GRUB_EHCI_CF_EHCI_OWNER = (1 << 0) +}; /* Queue Head & Transfer Descriptor constants */ #define GRUB_EHCI_HPTR_OFF 5 /* Horiz. pointer bit offset */ enum - { - GRUB_EHCI_HPTR_TYPE_MASK = (3<<1), - GRUB_EHCI_HPTR_TYPE_ITD = (0<<1), - GRUB_EHCI_HPTR_TYPE_QH = (1<<1), - GRUB_EHCI_HPTR_TYPE_SITD = (2<<1), - GRUB_EHCI_HPTR_TYPE_FSTN = (3<<1) - }; +{ + GRUB_EHCI_HPTR_TYPE_MASK = (3 << 1), + GRUB_EHCI_HPTR_TYPE_ITD = (0 << 1), + GRUB_EHCI_HPTR_TYPE_QH = (1 << 1), + GRUB_EHCI_HPTR_TYPE_SITD = (2 << 1), + GRUB_EHCI_HPTR_TYPE_FSTN = (3 << 1) +}; enum - { - GRUB_EHCI_C = (1<<27), - GRUB_EHCI_MAXPLEN_MASK = (0x7ff<<16), - GRUB_EHCI_H = (1<<15), - GRUB_EHCI_DTC = (1<<14), - GRUB_EHCI_SPEED_MASK = (3<<12), - GRUB_EHCI_SPEED_FULL = (0<<12), - GRUB_EHCI_SPEED_LOW = (1<<12), - GRUB_EHCI_SPEED_HIGH = (2<<12), - GRUB_EHCI_SPEED_RESERVED = (3<<12), - GRUB_EHCI_EP_NUM_MASK = (0xf<<8), - GRUB_EHCI_DEVADDR_MASK = 0x7f, - GRUB_EHCI_TARGET_MASK = (GRUB_EHCI_EP_NUM_MASK - | GRUB_EHCI_DEVADDR_MASK) - }; +{ + GRUB_EHCI_C = (1 << 27), + GRUB_EHCI_MAXPLEN_MASK = (0x7ff << 16), + GRUB_EHCI_H = (1 << 15), + GRUB_EHCI_DTC = (1 << 14), + GRUB_EHCI_SPEED_MASK = (3 << 12), + GRUB_EHCI_SPEED_FULL = (0 << 12), + GRUB_EHCI_SPEED_LOW = (1 << 12), + GRUB_EHCI_SPEED_HIGH = (2 << 12), + GRUB_EHCI_SPEED_RESERVED = (3 << 12), + GRUB_EHCI_EP_NUM_MASK = (0xf << 8), + GRUB_EHCI_DEVADDR_MASK = 0x7f, + GRUB_EHCI_TARGET_MASK = (GRUB_EHCI_EP_NUM_MASK | GRUB_EHCI_DEVADDR_MASK) +}; enum - { - GRUB_EHCI_MAXPLEN_OFF = 16, - GRUB_EHCI_SPEED_OFF = 12, - GRUB_EHCI_EP_NUM_OFF = 8 - }; +{ + GRUB_EHCI_MAXPLEN_OFF = 16, + GRUB_EHCI_SPEED_OFF = 12, + GRUB_EHCI_EP_NUM_OFF = 8 +}; enum - { - GRUB_EHCI_MULT_MASK = (3<<30), - GRUB_EHCI_MULT_RESERVED = (0<<30), - GRUB_EHCI_MULT_ONE = (0<<30), - GRUB_EHCI_MULT_TWO = (0<<30), - GRUB_EHCI_MULT_THREE = (0<<30), - GRUB_EHCI_DEVPORT_MASK = (0x7f<<23), - GRUB_EHCI_HUBADDR_MASK = (0x7f<<16) - }; +{ + GRUB_EHCI_MULT_MASK = (3 << 30), + GRUB_EHCI_MULT_RESERVED = (0 << 30), + GRUB_EHCI_MULT_ONE = (0 << 30), + GRUB_EHCI_MULT_TWO = (0 << 30), + GRUB_EHCI_MULT_THREE = (0 << 30), + GRUB_EHCI_DEVPORT_MASK = (0x7f << 23), + GRUB_EHCI_HUBADDR_MASK = (0x7f << 16) +}; enum - { - GRUB_EHCI_MULT_OFF = 30, - GRUB_EHCI_DEVPORT_OFF = 23, - GRUB_EHCI_HUBADDR_OFF = 16 - }; +{ + GRUB_EHCI_MULT_OFF = 30, + GRUB_EHCI_DEVPORT_OFF = 23, + GRUB_EHCI_HUBADDR_OFF = 16 +}; #define GRUB_EHCI_TERMINATE (1<<0) #define GRUB_EHCI_TOGGLE (1<<31) enum - { - GRUB_EHCI_TOTAL_MASK = (0x7fff << 16), - GRUB_EHCI_CERR_MASK = (3<<10), - GRUB_EHCI_CERR_0 = (0<<10), - GRUB_EHCI_CERR_1 = (1<<10), - GRUB_EHCI_CERR_2 = (2<<10), - GRUB_EHCI_CERR_3 = (3<<10), - GRUB_EHCI_PIDCODE_OUT = (0<<8), - GRUB_EHCI_PIDCODE_IN = (1<<8), - GRUB_EHCI_PIDCODE_SETUP = (2<<8), - GRUB_EHCI_STATUS_MASK = 0xff, - GRUB_EHCI_STATUS_ACTIVE = (1<<7), - GRUB_EHCI_STATUS_HALTED = (1<<6), - GRUB_EHCI_STATUS_BUFERR = (1<<5), - GRUB_EHCI_STATUS_BABBLE = (1<<4), - GRUB_EHCI_STATUS_TRANERR = (1<<3), - GRUB_EHCI_STATUS_MISSDMF = (1<<2), - GRUB_EHCI_STATUS_SPLITST = (1<<1), - GRUB_EHCI_STATUS_PINGERR = (1<<0) - }; +{ + GRUB_EHCI_TOTAL_MASK = (0x7fff << 16), + GRUB_EHCI_CERR_MASK = (3 << 10), + GRUB_EHCI_CERR_0 = (0 << 10), + GRUB_EHCI_CERR_1 = (1 << 10), + GRUB_EHCI_CERR_2 = (2 << 10), + GRUB_EHCI_CERR_3 = (3 << 10), + GRUB_EHCI_PIDCODE_OUT = (0 << 8), + GRUB_EHCI_PIDCODE_IN = (1 << 8), + GRUB_EHCI_PIDCODE_SETUP = (2 << 8), + GRUB_EHCI_STATUS_MASK = 0xff, + GRUB_EHCI_STATUS_ACTIVE = (1 << 7), + GRUB_EHCI_STATUS_HALTED = (1 << 6), + GRUB_EHCI_STATUS_BUFERR = (1 << 5), + GRUB_EHCI_STATUS_BABBLE = (1 << 4), + GRUB_EHCI_STATUS_TRANERR = (1 << 3), + GRUB_EHCI_STATUS_MISSDMF = (1 << 2), + GRUB_EHCI_STATUS_SPLITST = (1 << 1), + GRUB_EHCI_STATUS_PINGERR = (1 << 0) +}; enum - { - GRUB_EHCI_TOTAL_OFF = 16, - GRUB_EHCI_CERR_OFF = 10 - }; +{ + GRUB_EHCI_TOTAL_OFF = 16, + GRUB_EHCI_CERR_OFF = 10 +}; #define GRUB_EHCI_BUFPTR_MASK (0xfffff<<12) #define GRUB_EHCI_QHTDPTR_MASK 0xffffffe0 @@ -668,8 +667,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, { e->qh_virt[i].qh_hptr = grub_cpu_to_le32 ((grub_ehci_virt2phys ((void *) &e->qh_virt[i], - e-> - qh_chunk) & + e->qh_chunk) & GRUB_EHCI_POINTER_MASK) | GRUB_EHCI_HPTR_TYPE_QH); e->qh_virt[i].td_overlay.next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); From 2877805fb090f66bec9c24f83933bdb8e83ce0f3 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 21:13:29 +0200 Subject: [PATCH 04/12] Fix link_td to use grub_uint32_t and not the pointer type --- grub-core/bus/usb/ehci.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index f8b400314..2ce2be1e0 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -285,7 +285,7 @@ struct grub_ehci_td /* 64-bits part */ grub_uint32_t buffer_page_high[GRUB_EHCI_TD_BUF_PAGES]; /* EHCI driver part */ - grub_ehci_td_t link_td; /* pointer to next free/chained TD */ + grub_uint32_t link_td; /* pointer to next free/chained TD */ grub_uint32_t size; grub_uint32_t pad[1]; /* padding to some multiple of 32 bytes */ } __attribute__ ((packed)); @@ -412,7 +412,7 @@ grub_ehci_phys2virt (grub_uint32_t phys, struct grub_pci_dma_chunk *chunk) } static inline grub_uint32_t -grub_ehci_virt2phys (void *virt, struct grub_pci_dma_chunk *chunk) +grub_ehci_virt2phys (volatile void *virt, struct grub_pci_dma_chunk *chunk) { if (!virt) return 0; @@ -642,7 +642,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, /* Prepare chain of all TDs and set Terminate in all TDs */ for (i = 0; i < (GRUB_EHCI_N_TD - 1); i++) { - e->td_virt[i].link_td = &e->td_virt[i + 1]; + e->td_virt[i].link_td = e->td_phys + (i + 1) * sizeof (struct grub_ehci_td); e->td_virt[i].next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); e->td_virt[i].alt_next_td = grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); } @@ -994,7 +994,8 @@ grub_ehci_alloc_td (struct grub_ehci *e) } ret = e->tdfree_virt; /* Take current free TD */ - e->tdfree_virt = (grub_ehci_td_t) ret->link_td; /* Advance to next free TD in chain */ + /* Advance to next free TD in chain */ + e->tdfree_virt = grub_ehci_phys2virt (ret->link_td, e->td_chunk); ret->link_td = 0; /* Reset link_td in allocated TD */ return ret; } @@ -1002,7 +1003,8 @@ grub_ehci_alloc_td (struct grub_ehci *e) static void grub_ehci_free_td (struct grub_ehci *e, grub_ehci_td_t td) { - td->link_td = e->tdfree_virt; /* Chain new free TD & rest */ + /* Chain new free TD & rest */ + td->link_td = grub_ehci_virt2phys (e->tdfree_virt, e->td_chunk); e->tdfree_virt = td; /* Change address of first free TD */ } @@ -1035,7 +1037,7 @@ grub_ehci_free_tds (struct grub_ehci *e, grub_ehci_td_t td, /* Unlink the TD */ tdprev = td; - td = (grub_ehci_td_t) td->link_td; + td = grub_ehci_phys2virt (td->link_td, e->td_chunk); /* Free the TD. */ grub_ehci_free_td (e, tdprev); @@ -1167,7 +1169,7 @@ grub_ehci_transaction (struct grub_ehci *e, grub_le_to_cpu32 (td->buffer_page[3]), grub_le_to_cpu32 (td->buffer_page[4])); grub_dprintf ("ehci", "link_td=%08x, size=%08x\n", - (grub_uint32_t) td->link_td, td->size); + td->link_td, td->size); return td; } @@ -1263,9 +1265,9 @@ grub_ehci_setup_transfer (grub_usb_controller_t dev, cdata->td_first_virt = td; else { - td_prev->link_td = td; + td_prev->link_td = grub_ehci_virt2phys (td, e->td_chunk); td_prev->next_td = - grub_cpu_to_le32 (grub_ehci_virt2phys ((void *) td, e->td_chunk)); + grub_cpu_to_le32 (grub_ehci_virt2phys (td, e->td_chunk)); } td_prev = td; } From 4fbface043999192346cb5cef3418254e752de06 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 21:14:03 +0200 Subject: [PATCH 05/12] Remove packed attribute. --- grub-core/bus/usb/ehci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 2ce2be1e0..fe5cf6eb8 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -288,7 +288,7 @@ struct grub_ehci_td grub_uint32_t link_td; /* pointer to next free/chained TD */ grub_uint32_t size; grub_uint32_t pad[1]; /* padding to some multiple of 32 bytes */ -} __attribute__ ((packed)); +}; /* EHCI Queue Head */ /* Align to 32-byte boundaries */ @@ -305,7 +305,7 @@ struct grub_ehci_qh struct grub_ehci_td td_overlay; /* TD overlay area = 64 bytes */ /* EHCI driver part */ grub_uint32_t pad[4]; /* padding to some multiple of 32 bytes */ -} __attribute__ ((packed)); +}; /* EHCI Periodic Frame Span Traversal Node */ /* Currently not supported */ From e5c0534f0b0d8e03e42b29c207e1eab33fb7f6f5 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 21:31:53 +0200 Subject: [PATCH 06/12] Fix p2v and v2p --- grub-core/bus/usb/ehci.c | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index fe5cf6eb8..3421d630c 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -405,18 +405,14 @@ grub_ehci_port_setbits (struct grub_ehci *e, grub_uint32_t port, static inline void * grub_ehci_phys2virt (grub_uint32_t phys, struct grub_pci_dma_chunk *chunk) { - if (!phys) - return NULL; - return (void *) (phys - grub_dma_get_phys (chunk) - + (grub_uint32_t) grub_dma_get_virt (chunk)); + return ((grub_uint8_t *) grub_dma_get_virt (chunk) + + (phys - grub_dma_get_phys (chunk))); } static inline grub_uint32_t grub_ehci_virt2phys (volatile void *virt, struct grub_pci_dma_chunk *chunk) { - if (!virt) - return 0; - return ((grub_uint32_t) virt - (grub_uint32_t) grub_dma_get_virt (chunk) + return (((grub_uint8_t *) virt - (grub_uint8_t *) grub_dma_get_virt (chunk)) + grub_dma_get_phys (chunk)); } @@ -666,7 +662,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, for (i = 1; i < GRUB_EHCI_N_QH; i++) { e->qh_virt[i].qh_hptr = - grub_cpu_to_le32 ((grub_ehci_virt2phys ((void *) &e->qh_virt[i], + grub_cpu_to_le32 ((grub_ehci_virt2phys (&e->qh_virt[i], e->qh_chunk) & GRUB_EHCI_POINTER_MASK) | GRUB_EHCI_HPTR_TYPE_QH); e->qh_virt[i].td_overlay.next_td = @@ -758,7 +754,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, /* Setup list address registers */ grub_ehci_oper_write32 (e, GRUB_EHCI_FL_BASE, e->framelist_phys); grub_ehci_oper_write32 (e, GRUB_EHCI_CUR_AL_ADDR, - grub_ehci_virt2phys ((void *) &e->qh_virt[1], + grub_ehci_virt2phys (&e->qh_virt[1], e->qh_chunk)); /* Set ownership of root hub ports to EHCI */ @@ -970,12 +966,11 @@ grub_ehci_find_qh (struct grub_ehci *e, grub_usb_transfer_t transfer) grub_ehci_setup_qh (&qh[i], transfer); /* Linking - this new (last) QH will point to first QH */ qh[i].qh_hptr = grub_cpu_to_le32 (GRUB_EHCI_HPTR_TYPE_QH - | grub_ehci_virt2phys ((void *) &qh[1], + | grub_ehci_virt2phys (&qh[1], e->qh_chunk)); /* Linking - previous last QH will point to this new QH */ qh[i - 1].qh_hptr = grub_cpu_to_le32 (GRUB_EHCI_HPTR_TYPE_QH - | grub_ehci_virt2phys ((void *) - &qh[i], + | grub_ehci_virt2phys (&qh[i], e->qh_chunk)); return &qh[i]; @@ -995,7 +990,10 @@ grub_ehci_alloc_td (struct grub_ehci *e) ret = e->tdfree_virt; /* Take current free TD */ /* Advance to next free TD in chain */ - e->tdfree_virt = grub_ehci_phys2virt (ret->link_td, e->td_chunk); + if (ret->link_td) + e->tdfree_virt = grub_ehci_phys2virt (ret->link_td, e->td_chunk); + else + e->tdfree_virt = NULL; ret->link_td = 0; /* Reset link_td in allocated TD */ return ret; } @@ -1004,7 +1002,10 @@ static void grub_ehci_free_td (struct grub_ehci *e, grub_ehci_td_t td) { /* Chain new free TD & rest */ - td->link_td = grub_ehci_virt2phys (e->tdfree_virt, e->td_chunk); + if (e->tdfree_virt) + td->link_td = grub_ehci_virt2phys (e->tdfree_virt, e->td_chunk); + else + td->link_td = 0; e->tdfree_virt = td; /* Change address of first free TD */ } @@ -1037,7 +1038,10 @@ grub_ehci_free_tds (struct grub_ehci *e, grub_ehci_td_t td, /* Unlink the TD */ tdprev = td; - td = grub_ehci_phys2virt (td->link_td, e->td_chunk); + if (td->link_td) + td = grub_ehci_phys2virt (td->link_td, e->td_chunk); + else + td = NULL; /* Free the TD. */ grub_ehci_free_td (e, tdprev); @@ -1108,8 +1112,8 @@ grub_ehci_transaction (struct grub_ehci *e, * will not be really fetched because it is not active. But don't * forget, EHCI will try to fetch alternate TD every scan of AL * until QH is halted. */ - td->alt_next_td = - grub_cpu_to_le32 (grub_ehci_virt2phys ((void *) td_alt, e->td_chunk)); + td->alt_next_td = grub_cpu_to_le32 (grub_ehci_virt2phys (td_alt, + e->td_chunk)); /* token: * TOGGLE - according to toggle * TOTAL SIZE = size @@ -1292,7 +1296,7 @@ grub_ehci_setup_transfer (grub_usb_controller_t dev, /* Link new TDs with QH via next_td */ cdata->qh_virt->td_overlay.next_td = grub_cpu_to_le32 (grub_ehci_virt2phys - ((void *) cdata->td_first_virt, e->td_chunk)); + (cdata->td_first_virt, e->td_chunk)); /* Reset Active and Halted bits in QH to activate Advance Queue, * i.e. reset token */ cdata->qh_virt->td_overlay.token = grub_cpu_to_le32 (0); @@ -1544,7 +1548,7 @@ grub_ehci_cancel_transfer (grub_usb_controller_t dev, /* Finaly we should return QH back to the AL... */ e->qh_virt[i - 1].qh_hptr = grub_cpu_to_le32 (grub_ehci_virt2phys - ((void *) cdata->qh_virt, e->qh_chunk)); + (cdata->qh_virt, e->qh_chunk)); grub_free (cdata); grub_dprintf ("ehci", "cancel_transfer: end\n"); @@ -1741,7 +1745,7 @@ grub_ehci_restore_hw (void) /* Setup some EHCI registers and enable EHCI */ grub_ehci_oper_write32 (e, GRUB_EHCI_FL_BASE, e->framelist_phys); grub_ehci_oper_write32 (e, GRUB_EHCI_CUR_AL_ADDR, - grub_ehci_virt2phys ((void *) &e->qh_virt[1], + grub_ehci_virt2phys (&e->qh_virt[1], e->qh_chunk)); grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, GRUB_EHCI_CMD_RUNSTOP | From 0a213f4cdb25e5324c69ddf2d94241335207d910 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 21:37:02 +0200 Subject: [PATCH 07/12] Move p2v and v2p to pci.h --- grub-core/bus/usb/ehci.c | 44 ++++++++++++++-------------------------- include/grub/pci.h | 15 ++++++++++++++ 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 3421d630c..6ceb2dfc3 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -402,20 +402,6 @@ grub_ehci_port_setbits (struct grub_ehci *e, grub_uint32_t port, grub_ehci_port_read (e, port); } -static inline void * -grub_ehci_phys2virt (grub_uint32_t phys, struct grub_pci_dma_chunk *chunk) -{ - return ((grub_uint8_t *) grub_dma_get_virt (chunk) - + (phys - grub_dma_get_phys (chunk))); -} - -static inline grub_uint32_t -grub_ehci_virt2phys (volatile void *virt, struct grub_pci_dma_chunk *chunk) -{ - return (((grub_uint8_t *) virt - (grub_uint8_t *) grub_dma_get_virt (chunk)) - + grub_dma_get_phys (chunk)); -} - /* Halt if EHCI HC not halted */ static grub_err_t grub_ehci_halt (struct grub_ehci *e) @@ -662,7 +648,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, for (i = 1; i < GRUB_EHCI_N_QH; i++) { e->qh_virt[i].qh_hptr = - grub_cpu_to_le32 ((grub_ehci_virt2phys (&e->qh_virt[i], + grub_cpu_to_le32 ((grub_dma_virt2phys (&e->qh_virt[i], e->qh_chunk) & GRUB_EHCI_POINTER_MASK) | GRUB_EHCI_HPTR_TYPE_QH); e->qh_virt[i].td_overlay.next_td = @@ -754,7 +740,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, /* Setup list address registers */ grub_ehci_oper_write32 (e, GRUB_EHCI_FL_BASE, e->framelist_phys); grub_ehci_oper_write32 (e, GRUB_EHCI_CUR_AL_ADDR, - grub_ehci_virt2phys (&e->qh_virt[1], + grub_dma_virt2phys (&e->qh_virt[1], e->qh_chunk)); /* Set ownership of root hub ports to EHCI */ @@ -966,12 +952,12 @@ grub_ehci_find_qh (struct grub_ehci *e, grub_usb_transfer_t transfer) grub_ehci_setup_qh (&qh[i], transfer); /* Linking - this new (last) QH will point to first QH */ qh[i].qh_hptr = grub_cpu_to_le32 (GRUB_EHCI_HPTR_TYPE_QH - | grub_ehci_virt2phys (&qh[1], - e->qh_chunk)); + | grub_dma_virt2phys (&qh[1], + e->qh_chunk)); /* Linking - previous last QH will point to this new QH */ qh[i - 1].qh_hptr = grub_cpu_to_le32 (GRUB_EHCI_HPTR_TYPE_QH - | grub_ehci_virt2phys (&qh[i], - e->qh_chunk)); + | grub_dma_virt2phys (&qh[i], + e->qh_chunk)); return &qh[i]; } @@ -991,7 +977,7 @@ grub_ehci_alloc_td (struct grub_ehci *e) ret = e->tdfree_virt; /* Take current free TD */ /* Advance to next free TD in chain */ if (ret->link_td) - e->tdfree_virt = grub_ehci_phys2virt (ret->link_td, e->td_chunk); + e->tdfree_virt = grub_dma_phys2virt (ret->link_td, e->td_chunk); else e->tdfree_virt = NULL; ret->link_td = 0; /* Reset link_td in allocated TD */ @@ -1003,7 +989,7 @@ grub_ehci_free_td (struct grub_ehci *e, grub_ehci_td_t td) { /* Chain new free TD & rest */ if (e->tdfree_virt) - td->link_td = grub_ehci_virt2phys (e->tdfree_virt, e->td_chunk); + td->link_td = grub_dma_virt2phys (e->tdfree_virt, e->td_chunk); else td->link_td = 0; e->tdfree_virt = td; /* Change address of first free TD */ @@ -1039,7 +1025,7 @@ grub_ehci_free_tds (struct grub_ehci *e, grub_ehci_td_t td, /* Unlink the TD */ tdprev = td; if (td->link_td) - td = grub_ehci_phys2virt (td->link_td, e->td_chunk); + td = grub_dma_phys2virt (td->link_td, e->td_chunk); else td = NULL; @@ -1112,7 +1098,7 @@ grub_ehci_transaction (struct grub_ehci *e, * will not be really fetched because it is not active. But don't * forget, EHCI will try to fetch alternate TD every scan of AL * until QH is halted. */ - td->alt_next_td = grub_cpu_to_le32 (grub_ehci_virt2phys (td_alt, + td->alt_next_td = grub_cpu_to_le32 (grub_dma_virt2phys (td_alt, e->td_chunk)); /* token: * TOGGLE - according to toggle @@ -1269,9 +1255,9 @@ grub_ehci_setup_transfer (grub_usb_controller_t dev, cdata->td_first_virt = td; else { - td_prev->link_td = grub_ehci_virt2phys (td, e->td_chunk); + td_prev->link_td = grub_dma_virt2phys (td, e->td_chunk); td_prev->next_td = - grub_cpu_to_le32 (grub_ehci_virt2phys (td, e->td_chunk)); + grub_cpu_to_le32 (grub_dma_virt2phys (td, e->td_chunk)); } td_prev = td; } @@ -1295,7 +1281,7 @@ grub_ehci_setup_transfer (grub_usb_controller_t dev, grub_cpu_to_le32 (GRUB_EHCI_TERMINATE); /* Link new TDs with QH via next_td */ cdata->qh_virt->td_overlay.next_td = - grub_cpu_to_le32 (grub_ehci_virt2phys + grub_cpu_to_le32 (grub_dma_virt2phys (cdata->td_first_virt, e->td_chunk)); /* Reset Active and Halted bits in QH to activate Advance Queue, * i.e. reset token */ @@ -1547,7 +1533,7 @@ grub_ehci_cancel_transfer (grub_usb_controller_t dev, /* Finaly we should return QH back to the AL... */ e->qh_virt[i - 1].qh_hptr = - grub_cpu_to_le32 (grub_ehci_virt2phys + grub_cpu_to_le32 (grub_dma_virt2phys (cdata->qh_virt, e->qh_chunk)); grub_free (cdata); @@ -1745,7 +1731,7 @@ grub_ehci_restore_hw (void) /* Setup some EHCI registers and enable EHCI */ grub_ehci_oper_write32 (e, GRUB_EHCI_FL_BASE, e->framelist_phys); grub_ehci_oper_write32 (e, GRUB_EHCI_CUR_AL_ADDR, - grub_ehci_virt2phys (&e->qh_virt[1], + grub_dma_virt2phys (&e->qh_virt[1], e->qh_chunk)); grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, GRUB_EHCI_CMD_RUNSTOP | diff --git a/include/grub/pci.h b/include/grub/pci.h index f34e3d907..2c2806889 100644 --- a/include/grub/pci.h +++ b/include/grub/pci.h @@ -132,6 +132,21 @@ void EXPORT_FUNC(grub_dma_free) (struct grub_pci_dma_chunk *ch); volatile void *EXPORT_FUNC(grub_dma_get_virt) (struct grub_pci_dma_chunk *ch); grub_uint32_t EXPORT_FUNC(grub_dma_get_phys) (struct grub_pci_dma_chunk *ch); +static inline void * +grub_dma_phys2virt (grub_uint32_t phys, struct grub_pci_dma_chunk *chunk) +{ + return ((grub_uint8_t *) grub_dma_get_virt (chunk) + + (phys - grub_dma_get_phys (chunk))); +} + +static inline grub_uint32_t +grub_dma_virt2phys (volatile void *virt, struct grub_pci_dma_chunk *chunk) +{ + return (((grub_uint8_t *) virt - (grub_uint8_t *) grub_dma_get_virt (chunk)) + + grub_dma_get_phys (chunk)); +} + + #endif #endif /* GRUB_PCI_H */ From 2d523c70e5854d6ebeae4068b0e8077e703ee9a2 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 21:42:44 +0200 Subject: [PATCH 08/12] Use map range --- grub-core/bus/usb/ehci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 6ceb2dfc3..28521b644 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -522,7 +522,9 @@ grub_ehci_pci_iter (grub_pci_device_t dev, e->framelist_chunk = NULL; e->td_chunk = NULL; e->qh_chunk = NULL; - e->iobase_ehcc = (grub_uint32_t *) (base & GRUB_EHCI_ADDR_MEM_MASK); + e->iobase_ehcc = grub_pci_device_map_range (dev, + (base & GRUB_EHCI_ADDR_MEM_MASK), + 0x10); grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of EHCC: %08x\n", (grub_uint32_t) e->iobase_ehcc); From 81dbdc1d0a64201d5d7fee8dfff54aea399008d6 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 21:53:43 +0200 Subject: [PATCH 09/12] Several small fixes --- grub-core/bus/usb/ehci.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 28521b644..217897005 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -470,6 +470,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, 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"); @@ -524,10 +525,10 @@ grub_ehci_pci_iter (grub_pci_device_t dev, e->qh_chunk = NULL; e->iobase_ehcc = grub_pci_device_map_range (dev, (base & GRUB_EHCI_ADDR_MEM_MASK), - 0x10); + 0x100); grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of EHCC: %08x\n", - (grub_uint32_t) e->iobase_ehcc); + (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", @@ -538,13 +539,13 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_CPARAMS)); /* Determine base address of EHCI operational registers */ - e->iobase = (grub_uint32_t *) ((grub_uint32_t) e->iobase_ehcc + - (grub_uint32_t) grub_ehci_ehcc_read8 (e, - GRUB_EHCI_EHCC_CAPLEN)); + caplen = grub_ehci_ehcc_read8 (e, GRUB_EHCI_EHCC_CAPLEN); + e->iobase = (volatile grub_uint32_t *) + ((grub_uint8_t *) e->iobase_ehcc + caplen); grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of oper. regs: %08x\n", - (grub_uint32_t) e->iobase); + (base & GRUB_EHCI_ADDR_MEM_MASK) + 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", @@ -787,7 +788,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of oper. regs: %08x\n", - (grub_uint32_t) e->iobase); + (base & GRUB_EHCI_ADDR_MEM_MASK)); 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", From 1333929060bcb9aa0d9def4a7a230cac4458e36b Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 22:01:31 +0200 Subject: [PATCH 10/12] Disable SMI on OS ownership failure --- grub-core/bus/usb/ehci.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 217897005..3784871a9 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -314,7 +314,6 @@ struct grub_ehci { volatile grub_uint32_t *iobase_ehcc; /* Capability registers */ volatile grub_uint32_t *iobase; /* Operational registers */ - grub_pci_address_t pcibase_eecp; /* PCI extended capability registers base */ struct grub_pci_dma_chunk *framelist_chunk; /* Currently not used */ volatile grub_uint32_t *framelist_virt; grub_uint32_t framelist_phys; @@ -564,10 +563,6 @@ grub_ehci_pci_iter (grub_pci_device_t dev, /* Is there EECP ? */ eecp_offset = (grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_CPARAMS) & GRUB_EHCI_EECP_MASK) >> GRUB_EHCI_EECP_SHIFT; - if (eecp_offset >= 0x40) /* EECP offset valid in HCCPARAMS */ - e->pcibase_eecp = grub_pci_make_address (dev, eecp_offset); - else - e->pcibase_eecp = 0; /* Check format of data structures requested by EHCI */ /* XXX: In fact it is not used at any place, it is prepared for future @@ -673,31 +668,41 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: QH/TD init. OK\n"); /* Determine and change ownership. */ - if (e->pcibase_eecp) /* Ownership can be changed via EECP only */ + /* EECP offset valid in HCCPARAMS */ + /* Ownership can be changed via EECP only */ + if (eecp_offset >= 0x40) { - usblegsup = grub_pci_read (e->pcibase_eecp); + 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_dprintf ("ehci", "EHCI grub_ehci_pci_iter: EHCI owned by: BIOS\n"); /* Ownership change - set OS_OWNED bit */ - grub_pci_write (e->pcibase_eecp, usblegsup | GRUB_EHCI_OS_OWNED); + grub_pci_write (pciaddr_eecp, usblegsup | GRUB_EHCI_OS_OWNED); /* Ensure PCI register is written */ - grub_pci_read (e->pcibase_eecp); + 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 (e->pcibase_eecp) & GRUB_EHCI_BIOS_OWNED) + while ((grub_pci_read (pciaddr_eecp) & GRUB_EHCI_BIOS_OWNED) && (grub_get_time_ms () < maxtime)); - if (grub_pci_read (e->pcibase_eecp) & GRUB_EHCI_BIOS_OWNED) + 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 (e->pcibase_eecp, GRUB_EHCI_OS_OWNED); + grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED); /* Ensure PCI register is written */ - grub_pci_read (e->pcibase_eecp); + grub_pci_read (pciaddr_eecp); + /* Disable SMI. */ + 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); } } else if (usblegsup & GRUB_EHCI_OS_OWNED) @@ -710,9 +715,14 @@ grub_ehci_pci_iter (grub_pci_device_t dev, /* XXX: What to do in this case ? Can it happen ? * Is code below correct ? */ /* Ownership change - set OS_OWNED bit */ - grub_pci_write (e->pcibase_eecp, GRUB_EHCI_OS_OWNED); + grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED); /* Ensure PCI register is written */ - grub_pci_read (e->pcibase_eecp); + 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); } } From 9305dd1a8207373bb687537208c4d6d39faebd0a Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 1 Oct 2011 22:51:12 +0200 Subject: [PATCH 11/12] Some CS5536 code --- grub-core/bus/usb/ehci.c | 98 ++++++++++++++++++++++++---------------- include/grub/cs5536.h | 1 + 2 files changed, 60 insertions(+), 39 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 3784871a9..afcd08e69 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -27,6 +27,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -455,7 +456,6 @@ static int NESTED_FUNC_ATTR grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid __attribute__ ((unused))) { - grub_pci_address_t addr; grub_uint8_t release; grub_uint32_t class_code; grub_uint32_t interf; @@ -473,48 +473,68 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: begin\n"); - 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) + if (pciid == GRUB_CS5536_PCIID) { - grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: Wrong SBRN: %0x\n", - release); - return 0; + 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); } - - 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)) + else { - grub_dprintf ("ehci", - "EHCI grub_ehci_pci_iter: registers above 4G are not supported\n"); - return 1; + 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 1; + } + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: 32-bit EHCI OK\n"); } - 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) @@ -670,7 +690,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, /* Determine and change ownership. */ /* EECP offset valid in HCCPARAMS */ /* Ownership can be changed via EECP only */ - if (eecp_offset >= 0x40) + if (pciid != GRUB_CS5536_PCIID && eecp_offset >= 0x40) { grub_pci_address_t pciaddr_eecp; pciaddr_eecp = grub_pci_make_address (dev, eecp_offset); diff --git a/include/grub/cs5536.h b/include/grub/cs5536.h index fcacb23e8..6b5b424c8 100644 --- a/include/grub/cs5536.h +++ b/include/grub/cs5536.h @@ -91,6 +91,7 @@ #define GRUB_CS5536_MSR_USB_CONTROLLER_BASE 0x4000000a #define GRUB_CS5536_MSR_USB_OPTION_CONTROLLER_BASE 0x4000000b #define GRUB_CS5536_MSR_USB_BASE_ADDR_MASK 0x00ffffff00ULL +#define GRUB_CS5536_MSR_USB_BASE_SMI_ENABLE 0x3f000000000000ULL #define GRUB_CS5536_MSR_USB_BASE_BUS_MASTER 0x0400000000ULL #define GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE 0x0200000000ULL #define GRUB_CS5536_MSR_USB_BASE_PME_ENABLED 0x0800000000ULL From 08491b6a5a6fc6f64910dd7a1a648733b67922da Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 1 Feb 2012 14:08:58 +0100 Subject: [PATCH 12/12] Fix type errors. Don't stop after first EHCI controller is found. --- grub-core/bus/usb/ehci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index afcd08e69..357f7578f 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -403,7 +403,7 @@ grub_ehci_port_setbits (struct grub_ehci *e, grub_uint32_t port, } /* Halt if EHCI HC not halted */ -static grub_err_t +static grub_usb_err_t grub_ehci_halt (struct grub_ehci *e) { grub_uint64_t maxtime; @@ -422,14 +422,14 @@ grub_ehci_halt (struct grub_ehci *e) && (grub_get_time_ms () < maxtime)); if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) & GRUB_EHCI_ST_HC_HALTED) == 0) - return GRUB_ERR_TIMEOUT; + return GRUB_USB_ERR_TIMEOUT; } - return GRUB_ERR_NONE; + return GRUB_USB_ERR_NONE; } /* EHCI HC reset */ -static grub_err_t +static grub_usb_err_t grub_ehci_reset (struct grub_ehci *e) { grub_uint64_t maxtime; @@ -446,9 +446,9 @@ grub_ehci_reset (struct grub_ehci *e) && (grub_get_time_ms () < maxtime)); if ((grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND) & GRUB_EHCI_CMD_HC_RESET) != 0) - return GRUB_ERR_TIMEOUT; + return GRUB_USB_ERR_TIMEOUT; - return GRUB_ERR_NONE; + return GRUB_USB_ERR_NONE; } /* PCI iteration function... */ @@ -529,7 +529,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, { grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: registers above 4G are not supported\n"); - return 1; + return 0; } grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: 32-bit EHCI OK\n"); @@ -834,7 +834,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CONFIG_FLAG: %08x\n", grub_ehci_oper_read32 (e, GRUB_EHCI_CONFIG_FLAG)); - return 1; + return 0; fail: if (e) @@ -848,7 +848,7 @@ fail: } grub_free (e); - return 1; + return 0; } static int