/* ehci.c - EHCI Support. */ /* * VasEBoot -- GRand Unified Bootloader * Copyright (C) 2011 Free Software Foundation, Inc. * * VasEBoot 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. * * VasEBoot 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 VasEBoot. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include VasEBoot_MOD_LICENSE ("GPLv3+"); /* This simple VasEBoot implementation of EHCI driver: * - assumes no IRQ * - is not supporting isochronous transfers (iTD, siTD) * - is not supporting interrupt transfers */ #define VasEBoot_EHCI_PCI_SBRN_REG 0x60 /* Capability registers offsets */ enum { VasEBoot_EHCI_EHCC_CAPLEN = 0x00, /* byte */ VasEBoot_EHCI_EHCC_VERSION = 0x02, /* word */ VasEBoot_EHCI_EHCC_SPARAMS = 0x04, /* dword */ VasEBoot_EHCI_EHCC_CPARAMS = 0x08, /* dword */ VasEBoot_EHCI_EHCC_PROUTE = 0x0c, /* 60 bits */ }; #define VasEBoot_EHCI_EECP_MASK (0xff << 8) #define VasEBoot_EHCI_EECP_SHIFT 8 #define VasEBoot_EHCI_ADDR_MEM_MASK (~0xff) #define VasEBoot_EHCI_POINTER_MASK (~0x1f) /* Capability register SPARAMS bits */ enum { VasEBoot_EHCI_SPARAMS_N_PORTS = (0xf << 0), VasEBoot_EHCI_SPARAMS_PPC = (1 << 4), /* Power port control */ VasEBoot_EHCI_SPARAMS_PRR = (1 << 7), /* Port routing rules */ VasEBoot_EHCI_SPARAMS_N_PCC = (0xf << 8), /* No of ports per comp. */ VasEBoot_EHCI_SPARAMS_NCC = (0xf << 12), /* No of com. controllers */ VasEBoot_EHCI_SPARAMS_P_IND = (1 << 16), /* Port indicators present */ VasEBoot_EHCI_SPARAMS_DEBUG_P = (0xf << 20) /* Debug port */ }; #define VasEBoot_EHCI_MAX_N_PORTS 15 /* Max. number of ports */ /* Capability register CPARAMS bits */ enum { VasEBoot_EHCI_CPARAMS_64BIT = (1 << 0), VasEBoot_EHCI_CPARAMS_PROG_FRAMELIST = (1 << 1), VasEBoot_EHCI_CPARAMS_PARK_CAP = (1 << 2) }; #define VasEBoot_EHCI_N_FRAMELIST 1024 #define VasEBoot_EHCI_N_QH 256 #define VasEBoot_EHCI_N_TD 640 #define VasEBoot_EHCI_QH_EMPTY 1 /* USBLEGSUP bits and related OS OWNED byte offset */ enum { VasEBoot_EHCI_BIOS_OWNED = (1 << 16), VasEBoot_EHCI_OS_OWNED = (1 << 24) }; /* Operational registers offsets */ enum { VasEBoot_EHCI_COMMAND = 0x00, VasEBoot_EHCI_STATUS = 0x04, VasEBoot_EHCI_INTERRUPT = 0x08, VasEBoot_EHCI_FRAME_INDEX = 0x0c, VasEBoot_EHCI_64BIT_SEL = 0x10, VasEBoot_EHCI_FL_BASE = 0x14, VasEBoot_EHCI_CUR_AL_ADDR = 0x18, VasEBoot_EHCI_CONFIG_FLAG = 0x40, VasEBoot_EHCI_PORT_STAT_CMD = 0x44 }; /* Operational register COMMAND bits */ enum { VasEBoot_EHCI_CMD_RUNSTOP = (1 << 0), VasEBoot_EHCI_CMD_HC_RESET = (1 << 1), VasEBoot_EHCI_CMD_FL_SIZE = (3 << 2), VasEBoot_EHCI_CMD_PS_ENABL = (1 << 4), VasEBoot_EHCI_CMD_AS_ENABL = (1 << 5), VasEBoot_EHCI_CMD_AS_ADV_D = (1 << 6), VasEBoot_EHCI_CMD_L_HC_RES = (1 << 7), VasEBoot_EHCI_CMD_AS_PARKM = (3 << 8), VasEBoot_EHCI_CMD_AS_PARKE = (1 << 11), VasEBoot_EHCI_CMD_INT_THRS = (0xff << 16) }; /* Operational register STATUS bits */ enum { VasEBoot_EHCI_ST_INTERRUPT = (1 << 0), VasEBoot_EHCI_ST_ERROR_INT = (1 << 1), VasEBoot_EHCI_ST_PORT_CHG = (1 << 2), VasEBoot_EHCI_ST_FL_ROLLOVR = (1 << 3), VasEBoot_EHCI_ST_HS_ERROR = (1 << 4), VasEBoot_EHCI_ST_AS_ADVANCE = (1 << 5), VasEBoot_EHCI_ST_HC_HALTED = (1 << 12), VasEBoot_EHCI_ST_RECLAM = (1 << 13), VasEBoot_EHCI_ST_PS_STATUS = (1 << 14), VasEBoot_EHCI_ST_AS_STATUS = (1 << 15) }; /* Operational register PORT_STAT_CMD bits */ enum { VasEBoot_EHCI_PORT_CONNECT = (1 << 0), VasEBoot_EHCI_PORT_CONNECT_CH = (1 << 1), VasEBoot_EHCI_PORT_ENABLED = (1 << 2), VasEBoot_EHCI_PORT_ENABLED_CH = (1 << 3), VasEBoot_EHCI_PORT_OVERCUR = (1 << 4), VasEBoot_EHCI_PORT_OVERCUR_CH = (1 << 5), VasEBoot_EHCI_PORT_RESUME = (1 << 6), VasEBoot_EHCI_PORT_SUSPEND = (1 << 7), VasEBoot_EHCI_PORT_RESET = (1 << 8), VasEBoot_EHCI_PORT_LINE_STAT = (3 << 10), VasEBoot_EHCI_PORT_POWER = (1 << 12), VasEBoot_EHCI_PORT_OWNER = (1 << 13), VasEBoot_EHCI_PORT_INDICATOR = (3 << 14), VasEBoot_EHCI_PORT_TEST = (0xf << 16), VasEBoot_EHCI_PORT_WON_CONN_E = (1 << 20), VasEBoot_EHCI_PORT_WON_DISC_E = (1 << 21), VasEBoot_EHCI_PORT_WON_OVER_E = (1 << 22), VasEBoot_EHCI_PORT_LINE_SE0 = (0 << 10), VasEBoot_EHCI_PORT_LINE_K = (1 << 10), VasEBoot_EHCI_PORT_LINE_J = (2 << 10), VasEBoot_EHCI_PORT_LINE_UNDEF = (3 << 10), VasEBoot_EHCI_PORT_LINE_LOWSP = VasEBoot_EHCI_PORT_LINE_K, /* K state means low speed */ VasEBoot_EHCI_PORT_WMASK = ~(VasEBoot_EHCI_PORT_CONNECT_CH | VasEBoot_EHCI_PORT_ENABLED_CH | VasEBoot_EHCI_PORT_OVERCUR_CH) }; /* Operational register CONFIGFLAGS bits */ enum { VasEBoot_EHCI_CF_EHCI_OWNER = (1 << 0) }; /* Queue Head & Transfer Descriptor constants */ #define VasEBoot_EHCI_HPTR_OFF 5 /* Horiz. pointer bit offset */ enum { VasEBoot_EHCI_HPTR_TYPE_MASK = (3 << 1), VasEBoot_EHCI_HPTR_TYPE_ITD = (0 << 1), VasEBoot_EHCI_HPTR_TYPE_QH = (1 << 1), VasEBoot_EHCI_HPTR_TYPE_SITD = (2 << 1), VasEBoot_EHCI_HPTR_TYPE_FSTN = (3 << 1) }; enum { VasEBoot_EHCI_C = (1 << 27), VasEBoot_EHCI_MAXPLEN_MASK = (0x7ff << 16), VasEBoot_EHCI_H = (1 << 15), VasEBoot_EHCI_DTC = (1 << 14), VasEBoot_EHCI_SPEED_MASK = (3 << 12), VasEBoot_EHCI_SPEED_FULL = (0 << 12), VasEBoot_EHCI_SPEED_LOW = (1 << 12), VasEBoot_EHCI_SPEED_HIGH = (2 << 12), VasEBoot_EHCI_SPEED_RESERVED = (3 << 12), VasEBoot_EHCI_EP_NUM_MASK = (0xf << 8), VasEBoot_EHCI_DEVADDR_MASK = 0x7f, VasEBoot_EHCI_TARGET_MASK = (VasEBoot_EHCI_EP_NUM_MASK | VasEBoot_EHCI_DEVADDR_MASK) }; enum { VasEBoot_EHCI_MAXPLEN_OFF = 16, VasEBoot_EHCI_SPEED_OFF = 12, VasEBoot_EHCI_EP_NUM_OFF = 8 }; enum { VasEBoot_EHCI_MULT_MASK = (3 << 30), VasEBoot_EHCI_MULT_RESERVED = (0 << 30), VasEBoot_EHCI_MULT_ONE = (1 << 30), VasEBoot_EHCI_MULT_TWO = (2 << 30), VasEBoot_EHCI_MULT_THREE = (3 << 30), VasEBoot_EHCI_DEVPORT_MASK = (0x7f << 23), VasEBoot_EHCI_HUBADDR_MASK = (0x7f << 16), VasEBoot_EHCI_CMASK_MASK = (0xff << 8), VasEBoot_EHCI_SMASK_MASK = (0xff << 0), }; enum { VasEBoot_EHCI_MULT_OFF = 30, VasEBoot_EHCI_DEVPORT_OFF = 23, VasEBoot_EHCI_HUBADDR_OFF = 16, VasEBoot_EHCI_CMASK_OFF = 8, VasEBoot_EHCI_SMASK_OFF = 0, }; #define VasEBoot_EHCI_TERMINATE (1<<0) #define VasEBoot_EHCI_TOGGLE (1<<31) enum { VasEBoot_EHCI_TOTAL_MASK = (0x7fff << 16), VasEBoot_EHCI_CERR_MASK = (3 << 10), VasEBoot_EHCI_CERR_0 = (0 << 10), VasEBoot_EHCI_CERR_1 = (1 << 10), VasEBoot_EHCI_CERR_2 = (2 << 10), VasEBoot_EHCI_CERR_3 = (3 << 10), VasEBoot_EHCI_PIDCODE_OUT = (0 << 8), VasEBoot_EHCI_PIDCODE_IN = (1 << 8), VasEBoot_EHCI_PIDCODE_SETUP = (2 << 8), VasEBoot_EHCI_STATUS_MASK = 0xff, VasEBoot_EHCI_STATUS_ACTIVE = (1 << 7), VasEBoot_EHCI_STATUS_HALTED = (1 << 6), VasEBoot_EHCI_STATUS_BUFERR = (1 << 5), VasEBoot_EHCI_STATUS_BABBLE = (1 << 4), VasEBoot_EHCI_STATUS_TRANERR = (1 << 3), VasEBoot_EHCI_STATUS_MISSDMF = (1 << 2), VasEBoot_EHCI_STATUS_SPLITST = (1 << 1), VasEBoot_EHCI_STATUS_PINGERR = (1 << 0) }; enum { VasEBoot_EHCI_TOTAL_OFF = 16, VasEBoot_EHCI_CERR_OFF = 10 }; #define VasEBoot_EHCI_BUFPTR_MASK (0xfffff<<12) #define VasEBoot_EHCI_QHTDPTR_MASK 0xffffffe0 #define VasEBoot_EHCI_TD_BUF_PAGES 5 #define VasEBoot_EHCI_BUFPAGELEN 0x1000 #define VasEBoot_EHCI_MAXBUFLEN 0x5000 struct VasEBoot_ehci_td; struct VasEBoot_ehci_qh; typedef volatile struct VasEBoot_ehci_td *VasEBoot_ehci_td_t; typedef volatile struct VasEBoot_ehci_qh *VasEBoot_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 VasEBoot_ehci_td { /* EHCI HW part */ VasEBoot_uint32_t next_td; /* Pointer to next qTD */ VasEBoot_uint32_t alt_next_td; /* Pointer to alternate next qTD */ VasEBoot_uint32_t token; /* Toggle, Len, Interrupt, Page, Error, PID, Status */ VasEBoot_uint32_t buffer_page[VasEBoot_EHCI_TD_BUF_PAGES]; /* Buffer pointer (+ cur. offset in page 0 */ /* 64-bits part */ VasEBoot_uint32_t buffer_page_high[VasEBoot_EHCI_TD_BUF_PAGES]; /* EHCI driver part */ VasEBoot_uint32_t link_td; /* pointer to next free/chained TD */ VasEBoot_uint32_t size; VasEBoot_uint32_t pad[1]; /* padding to some multiple of 32 bytes */ }; /* 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 VasEBoot_ehci_qh { /* EHCI HW part */ VasEBoot_uint32_t qh_hptr; /* Horiz. pointer & Terminate */ VasEBoot_uint32_t ep_char; /* EP characteristics */ VasEBoot_uint32_t ep_cap; /* EP capabilities */ VasEBoot_uint32_t td_current; /* current TD link pointer */ struct VasEBoot_ehci_td td_overlay; /* TD overlay area = 64 bytes */ /* EHCI driver part */ VasEBoot_uint32_t pad[4]; /* padding to some multiple of 32 bytes */ }; /* EHCI Periodic Frame Span Traversal Node */ /* Currently not supported */ struct VasEBoot_ehci { volatile VasEBoot_uint32_t *iobase_ehcc; /* Capability registers */ volatile VasEBoot_uint32_t *iobase; /* Operational registers */ struct VasEBoot_pci_dma_chunk *framelist_chunk; /* Currently not used */ volatile VasEBoot_uint32_t *framelist_virt; VasEBoot_uint32_t framelist_phys; struct VasEBoot_pci_dma_chunk *qh_chunk; /* VasEBoot_EHCI_N_QH Queue Heads */ VasEBoot_ehci_qh_t qh_virt; VasEBoot_uint32_t qh_phys; struct VasEBoot_pci_dma_chunk *td_chunk; /* VasEBoot_EHCI_N_TD Transfer Descriptors */ VasEBoot_ehci_td_t td_virt; VasEBoot_uint32_t td_phys; VasEBoot_ehci_td_t tdfree_virt; /* Free Transfer Descriptors */ int flag64; VasEBoot_uint32_t reset; /* bits 1-15 are flags if port was reset from connected time or not */ struct VasEBoot_ehci *next; }; static struct VasEBoot_ehci *ehci; static void sync_all_caches (struct VasEBoot_ehci *e) { if (!e) return; if (e->td_virt) VasEBoot_arch_sync_dma_caches (e->td_virt, sizeof (struct VasEBoot_ehci_td) * VasEBoot_EHCI_N_TD); if (e->qh_virt) VasEBoot_arch_sync_dma_caches (e->qh_virt, sizeof (struct VasEBoot_ehci_qh) * VasEBoot_EHCI_N_QH); if (e->framelist_virt) VasEBoot_arch_sync_dma_caches (e->framelist_virt, 4096); } /* EHCC registers access functions */ static inline VasEBoot_uint32_t VasEBoot_ehci_ehcc_read32 (struct VasEBoot_ehci *e, VasEBoot_uint32_t addr) { return VasEBoot_le_to_cpu32 (*((volatile VasEBoot_uint32_t *) e->iobase_ehcc + (addr / sizeof (VasEBoot_uint32_t)))); } static inline VasEBoot_uint16_t VasEBoot_ehci_ehcc_read16 (struct VasEBoot_ehci *e, VasEBoot_uint32_t addr) { return VasEBoot_le_to_cpu16 (*((volatile VasEBoot_uint16_t *) e->iobase_ehcc + (addr / sizeof (VasEBoot_uint16_t)))); } static inline VasEBoot_uint8_t VasEBoot_ehci_ehcc_read8 (struct VasEBoot_ehci *e, VasEBoot_uint32_t addr) { return *((volatile VasEBoot_uint8_t *) e->iobase_ehcc + addr); } /* Operational registers access functions */ static inline VasEBoot_uint32_t VasEBoot_ehci_oper_read32 (struct VasEBoot_ehci *e, VasEBoot_uint32_t addr) { return VasEBoot_le_to_cpu32 (* ((volatile VasEBoot_uint32_t *) e->iobase + (addr / sizeof (VasEBoot_uint32_t)))); } static inline void VasEBoot_ehci_oper_write32 (struct VasEBoot_ehci *e, VasEBoot_uint32_t addr, VasEBoot_uint32_t value) { *((volatile VasEBoot_uint32_t *) e->iobase + (addr / sizeof (VasEBoot_uint32_t))) = VasEBoot_cpu_to_le32 (value); } static inline VasEBoot_uint32_t VasEBoot_ehci_port_read (struct VasEBoot_ehci *e, VasEBoot_uint32_t port) { return VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_PORT_STAT_CMD + port * 4); } static inline void VasEBoot_ehci_port_resbits (struct VasEBoot_ehci *e, VasEBoot_uint32_t port, VasEBoot_uint32_t bits) { VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_PORT_STAT_CMD + port * 4, VasEBoot_ehci_port_read (e, port) & VasEBoot_EHCI_PORT_WMASK & ~(bits)); VasEBoot_ehci_port_read (e, port); } static inline void VasEBoot_ehci_port_setbits (struct VasEBoot_ehci *e, VasEBoot_uint32_t port, VasEBoot_uint32_t bits) { VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_PORT_STAT_CMD + port * 4, (VasEBoot_ehci_port_read (e, port) & VasEBoot_EHCI_PORT_WMASK) | bits); VasEBoot_ehci_port_read (e, port); } /* Halt if EHCI HC not halted */ static VasEBoot_usb_err_t VasEBoot_ehci_halt (struct VasEBoot_ehci *e) { VasEBoot_uint64_t maxtime; if ((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS) & VasEBoot_EHCI_ST_HC_HALTED) == 0) /* EHCI is not halted */ { /* Halt EHCI */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, ~VasEBoot_EHCI_CMD_RUNSTOP & VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); /* Ensure command is written */ VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND); maxtime = VasEBoot_get_time_ms () + 1000; /* Fix: Should be 2ms ! */ while (((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS) & VasEBoot_EHCI_ST_HC_HALTED) == 0) && (VasEBoot_get_time_ms () < maxtime)); if ((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS) & VasEBoot_EHCI_ST_HC_HALTED) == 0) return VasEBoot_USB_ERR_TIMEOUT; } return VasEBoot_USB_ERR_NONE; } /* EHCI HC reset */ static VasEBoot_usb_err_t VasEBoot_ehci_reset (struct VasEBoot_ehci *e) { VasEBoot_uint64_t maxtime; sync_all_caches (e); VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, VasEBoot_EHCI_CMD_HC_RESET | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); /* Ensure command is written */ VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND); /* XXX: How long time could take reset of HC ? */ maxtime = VasEBoot_get_time_ms () + 1000; while (((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND) & VasEBoot_EHCI_CMD_HC_RESET) != 0) && (VasEBoot_get_time_ms () < maxtime)); if ((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND) & VasEBoot_EHCI_CMD_HC_RESET) != 0) return VasEBoot_USB_ERR_TIMEOUT; return VasEBoot_USB_ERR_NONE; } /* PCI iteration function... */ static int VasEBoot_ehci_pci_iter (VasEBoot_pci_device_t dev, VasEBoot_pci_id_t pciid, void *data __attribute__ ((unused))) { VasEBoot_uint8_t release; VasEBoot_uint32_t class_code; VasEBoot_uint32_t interf; VasEBoot_uint32_t subclass; VasEBoot_uint32_t class; VasEBoot_uint32_t base, base_h; struct VasEBoot_ehci *e; VasEBoot_uint32_t eecp_offset; VasEBoot_uint32_t fp; int i; VasEBoot_uint32_t usblegsup = 0; VasEBoot_uint64_t maxtime; VasEBoot_uint32_t n_ports; VasEBoot_uint8_t caplen; VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: begin\n"); if (pciid == VasEBoot_CS5536_PCIID) { VasEBoot_uint64_t basereg; basereg = VasEBoot_cs5536_read_msr (dev, VasEBoot_CS5536_MSR_USB_EHCI_BASE); if (!(basereg & VasEBoot_CS5536_MSR_USB_BASE_MEMORY_ENABLE)) { /* Shouldn't happen. */ VasEBoot_dprintf ("ehci", "No EHCI address is assigned\n"); return 0; } base = (basereg & VasEBoot_CS5536_MSR_USB_BASE_ADDR_MASK); basereg |= VasEBoot_CS5536_MSR_USB_BASE_BUS_MASTER; basereg &= ~VasEBoot_CS5536_MSR_USB_BASE_PME_ENABLED; basereg &= ~VasEBoot_CS5536_MSR_USB_BASE_PME_STATUS; basereg &= ~VasEBoot_CS5536_MSR_USB_BASE_SMI_ENABLE; VasEBoot_cs5536_write_msr (dev, VasEBoot_CS5536_MSR_USB_EHCI_BASE, basereg); } else { VasEBoot_pci_address_t addr; addr = VasEBoot_pci_make_address (dev, VasEBoot_PCI_REG_CLASS); class_code = VasEBoot_pci_read (addr) >> 8; interf = class_code & 0xFF; subclass = (class_code >> 8) & 0xFF; class = class_code >> 16; /* If this is not an EHCI controller, just return. */ if (class != 0x0c || subclass != 0x03 || interf != 0x20) return 0; VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: class OK\n"); /* Check Serial Bus Release Number */ addr = VasEBoot_pci_make_address (dev, VasEBoot_EHCI_PCI_SBRN_REG); release = VasEBoot_pci_read_byte (addr); if (release != 0x20) { VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: Wrong SBRN: %0x\n", release); return 0; } VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: bus rev. num. OK\n"); /* Determine EHCI EHCC registers base address. */ addr = VasEBoot_pci_make_address (dev, VasEBoot_PCI_REG_ADDRESS_REG0); base = VasEBoot_pci_read (addr); addr = VasEBoot_pci_make_address (dev, VasEBoot_PCI_REG_ADDRESS_REG1); base_h = VasEBoot_pci_read (addr); /* Stop if registers are mapped above 4G - VasEBoot does not currently * work with registers mapped above 4G */ if (((base & VasEBoot_PCI_ADDR_MEM_TYPE_MASK) != VasEBoot_PCI_ADDR_MEM_TYPE_32) && (base_h != 0)) { VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: registers above 4G are not supported\n"); return 0; } base &= VasEBoot_PCI_ADDR_MEM_MASK; if (!base) { VasEBoot_dprintf ("ehci", "EHCI: EHCI is not mapped\n"); return 0; } /* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */ addr = VasEBoot_pci_make_address (dev, VasEBoot_PCI_REG_COMMAND); VasEBoot_pci_write_word(addr, VasEBoot_PCI_COMMAND_MEM_ENABLED | VasEBoot_PCI_COMMAND_BUS_MASTER | VasEBoot_pci_read_word(addr)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: 32-bit EHCI OK\n"); } /* Allocate memory for the controller and fill basic values. */ e = VasEBoot_zalloc (sizeof (*e)); if (!e) return 1; e->framelist_chunk = NULL; e->td_chunk = NULL; e->qh_chunk = NULL; e->iobase_ehcc = VasEBoot_pci_device_map_range (dev, (base & VasEBoot_EHCI_ADDR_MEM_MASK), 0x100); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: iobase of EHCC: %08x\n", (base & VasEBoot_EHCI_ADDR_MEM_MASK)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: CAPLEN: %02x\n", VasEBoot_ehci_ehcc_read8 (e, VasEBoot_EHCI_EHCC_CAPLEN)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: VERSION: %04x\n", VasEBoot_ehci_ehcc_read16 (e, VasEBoot_EHCI_EHCC_VERSION)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: SPARAMS: %08x\n", VasEBoot_ehci_ehcc_read32 (e, VasEBoot_EHCI_EHCC_SPARAMS)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: CPARAMS: %08x\n", VasEBoot_ehci_ehcc_read32 (e, VasEBoot_EHCI_EHCC_CPARAMS)); /* Determine base address of EHCI operational registers */ caplen = VasEBoot_ehci_ehcc_read8 (e, VasEBoot_EHCI_EHCC_CAPLEN); #ifndef VasEBoot_HAVE_UNALIGNED_ACCESS if (caplen & (sizeof (VasEBoot_uint32_t) - 1)) { VasEBoot_dprintf ("ehci", "Unaligned caplen\n"); return 0; } e->iobase = ((volatile VasEBoot_uint32_t *) e->iobase_ehcc + (caplen / sizeof (VasEBoot_uint32_t))); #else e->iobase = (volatile VasEBoot_uint32_t *) ((VasEBoot_uint8_t *) e->iobase_ehcc + caplen); #endif VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: iobase of oper. regs: %08x\n", (base & VasEBoot_EHCI_ADDR_MEM_MASK) + caplen); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: COMMAND: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: STATUS: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: INTERRUPT: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_INTERRUPT)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: FRAME_INDEX: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_FRAME_INDEX)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: FL_BASE: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_FL_BASE)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: CUR_AL_ADDR: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_CUR_AL_ADDR)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: CONFIG_FLAG: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_CONFIG_FLAG)); /* Is there EECP ? */ eecp_offset = (VasEBoot_ehci_ehcc_read32 (e, VasEBoot_EHCI_EHCC_CPARAMS) & VasEBoot_EHCI_EECP_MASK) >> VasEBoot_EHCI_EECP_SHIFT; /* Check format of data structures requested by EHCI */ /* XXX: In fact it is not used at any place, it is prepared for future * This implementation uses 32-bits pointers only */ e->flag64 = ((VasEBoot_ehci_ehcc_read32 (e, VasEBoot_EHCI_EHCC_CPARAMS) & VasEBoot_EHCI_CPARAMS_64BIT) != 0); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_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 = VasEBoot_memalign_dma32 (4096, 4096); if (!e->framelist_chunk) goto fail; e->framelist_virt = VasEBoot_dma_get_virt (e->framelist_chunk); e->framelist_phys = VasEBoot_dma_get_phys (e->framelist_chunk); VasEBoot_memset ((void *) e->framelist_virt, 0, 4096); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: framelist mem=%p. OK\n", e->framelist_virt); /* Allocate memory for the QHs and register it in "e". */ e->qh_chunk = VasEBoot_memalign_dma32 (4096, sizeof (struct VasEBoot_ehci_qh) * VasEBoot_EHCI_N_QH); if (!e->qh_chunk) goto fail; e->qh_virt = (VasEBoot_ehci_qh_t) VasEBoot_dma_get_virt (e->qh_chunk); e->qh_phys = VasEBoot_dma_get_phys (e->qh_chunk); VasEBoot_memset ((void *) e->qh_virt, 0, sizeof (struct VasEBoot_ehci_qh) * VasEBoot_EHCI_N_QH); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: QH mem=%p. OK\n", e->qh_virt); /* Allocate memory for the TDs and register it in "e". */ e->td_chunk = VasEBoot_memalign_dma32 (4096, sizeof (struct VasEBoot_ehci_td) * VasEBoot_EHCI_N_TD); if (!e->td_chunk) goto fail; e->td_virt = (VasEBoot_ehci_td_t) VasEBoot_dma_get_virt (e->td_chunk); e->td_phys = VasEBoot_dma_get_phys (e->td_chunk); VasEBoot_memset ((void *) e->td_virt, 0, sizeof (struct VasEBoot_ehci_td) * VasEBoot_EHCI_N_TD); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: TD mem=%p. OK\n", 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 = VasEBoot_cpu_to_le32 ((e->qh_phys & VasEBoot_EHCI_POINTER_MASK) | VasEBoot_EHCI_HPTR_TYPE_QH); for (i = 0; i < VasEBoot_EHCI_N_FRAMELIST; i++) e->framelist_virt[i] = fp; /* Prepare chain of all TDs and set Terminate in all TDs */ for (i = 0; i < (VasEBoot_EHCI_N_TD - 1); i++) { e->td_virt[i].link_td = e->td_phys + (i + 1) * sizeof (struct VasEBoot_ehci_td); e->td_virt[i].next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); e->td_virt[i].alt_next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); } e->td_virt[VasEBoot_EHCI_N_TD - 1].next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); e->td_virt[VasEBoot_EHCI_N_TD - 1].alt_next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); e->tdfree_virt = e->td_virt; /* Set Terminate in first QH, which is used in framelist */ e->qh_virt[0].qh_hptr = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE | VasEBoot_EHCI_HPTR_TYPE_QH); e->qh_virt[0].td_overlay.next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); e->qh_virt[0].td_overlay.alt_next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); /* Also set Halted bit in token */ e->qh_virt[0].td_overlay.token = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_STATUS_HALTED); /* Set the H bit in first QH used for AL */ e->qh_virt[1].ep_char = VasEBoot_cpu_to_le32_compile_time (VasEBoot_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 < VasEBoot_EHCI_N_QH; i++) { e->qh_virt[i].qh_hptr = VasEBoot_cpu_to_le32 ((VasEBoot_dma_virt2phys (&e->qh_virt[i], e->qh_chunk) & VasEBoot_EHCI_POINTER_MASK) | VasEBoot_EHCI_HPTR_TYPE_QH); e->qh_virt[i].td_overlay.next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); e->qh_virt[i].td_overlay.alt_next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); /* Also set Halted bit in token */ e->qh_virt[i].td_overlay.token = VasEBoot_cpu_to_le32_compile_time (VasEBoot_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 */ VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: QH/TD init. OK\n"); /* Determine and change ownership. */ /* EECP offset valid in HCCPARAMS */ /* Ownership can be changed via EECP only */ if (pciid != VasEBoot_CS5536_PCIID && eecp_offset >= 0x40) { VasEBoot_pci_address_t pciaddr_eecp; pciaddr_eecp = VasEBoot_pci_make_address (dev, eecp_offset); usblegsup = VasEBoot_pci_read (pciaddr_eecp); if (usblegsup & VasEBoot_EHCI_BIOS_OWNED) { VasEBoot_boot_time ("Taking ownership of EHCI controller"); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: EHCI owned by: BIOS\n"); /* Ownership change - set OS_OWNED bit */ VasEBoot_pci_write (pciaddr_eecp, usblegsup | VasEBoot_EHCI_OS_OWNED); /* Ensure PCI register is written */ VasEBoot_pci_read (pciaddr_eecp); /* Wait for finish of ownership change, EHCI specification * doesn't say how long it can take... */ maxtime = VasEBoot_get_time_ms () + 1000; while ((VasEBoot_pci_read (pciaddr_eecp) & VasEBoot_EHCI_BIOS_OWNED) && (VasEBoot_get_time_ms () < maxtime)); if (VasEBoot_pci_read (pciaddr_eecp) & VasEBoot_EHCI_BIOS_OWNED) { VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: EHCI change ownership timeout"); /* Change ownership in "hard way" - reset BIOS ownership */ VasEBoot_pci_write (pciaddr_eecp, VasEBoot_EHCI_OS_OWNED); /* Ensure PCI register is written */ VasEBoot_pci_read (pciaddr_eecp); } } else if (usblegsup & VasEBoot_EHCI_OS_OWNED) /* XXX: What to do in this case - nothing ? Can it happen ? */ VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: EHCI owned by: OS\n"); else { VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: EHCI owned by: NONE\n"); /* XXX: What to do in this case ? Can it happen ? * Is code below correct ? */ /* Ownership change - set OS_OWNED bit */ VasEBoot_pci_write (pciaddr_eecp, VasEBoot_EHCI_OS_OWNED); /* Ensure PCI register is written */ VasEBoot_pci_read (pciaddr_eecp); } /* Disable SMI, just to be sure. */ pciaddr_eecp = VasEBoot_pci_make_address (dev, eecp_offset + 4); VasEBoot_pci_write (pciaddr_eecp, 0); /* Ensure PCI register is written */ VasEBoot_pci_read (pciaddr_eecp); } VasEBoot_dprintf ("ehci", "inithw: EHCI VasEBoot_ehci_pci_iter: ownership OK\n"); /* Now we can setup EHCI (maybe...) */ /* Check if EHCI is halted and halt it if not */ if (VasEBoot_ehci_halt (e) != VasEBoot_USB_ERR_NONE) { VasEBoot_error (VasEBoot_ERR_TIMEOUT, "EHCI VasEBoot_ehci_pci_iter: EHCI halt timeout"); goto fail; } VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: halted OK\n"); /* Reset EHCI */ if (VasEBoot_ehci_reset (e) != VasEBoot_USB_ERR_NONE) { VasEBoot_error (VasEBoot_ERR_TIMEOUT, "EHCI VasEBoot_ehci_pci_iter: EHCI reset timeout"); goto fail; } VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: reset OK\n"); /* Setup list address registers */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_FL_BASE, e->framelist_phys); VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_CUR_AL_ADDR, VasEBoot_dma_virt2phys (&e->qh_virt[1], e->qh_chunk)); /* Set ownership of root hub ports to EHCI */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_CONFIG_FLAG, VasEBoot_EHCI_CF_EHCI_OWNER); /* Enable both lists */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, VasEBoot_EHCI_CMD_AS_ENABL | VasEBoot_EHCI_CMD_PS_ENABL | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); /* Now should be possible to power-up and enumerate ports etc. */ if ((VasEBoot_ehci_ehcc_read32 (e, VasEBoot_EHCI_EHCC_SPARAMS) & VasEBoot_EHCI_SPARAMS_PPC) != 0) { /* EHCI has port powering control */ /* Power on all ports */ n_ports = VasEBoot_ehci_ehcc_read32 (e, VasEBoot_EHCI_EHCC_SPARAMS) & VasEBoot_EHCI_SPARAMS_N_PORTS; for (i = 0; i < (int) n_ports; i++) VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_PORT_STAT_CMD + i * 4, VasEBoot_EHCI_PORT_POWER | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_PORT_STAT_CMD + i * 4)); } /* Ensure all commands are written */ VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND); /* Enable EHCI */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, VasEBoot_EHCI_CMD_RUNSTOP | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); /* Ensure command is written */ VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND); /* Link to ehci now that initialisation is successful. */ e->next = ehci; ehci = e; sync_all_caches (e); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: OK at all\n"); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: iobase of oper. regs: %08x\n", (base & VasEBoot_EHCI_ADDR_MEM_MASK)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: COMMAND: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: STATUS: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: INTERRUPT: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_INTERRUPT)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: FRAME_INDEX: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_FRAME_INDEX)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: FL_BASE: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_FL_BASE)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: CUR_AL_ADDR: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_CUR_AL_ADDR)); VasEBoot_dprintf ("ehci", "EHCI VasEBoot_ehci_pci_iter: CONFIG_FLAG: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_CONFIG_FLAG)); return 0; fail: if (e) { if (e->td_chunk) VasEBoot_dma_free ((void *) e->td_chunk); if (e->qh_chunk) VasEBoot_dma_free ((void *) e->qh_chunk); if (e->framelist_chunk) VasEBoot_dma_free (e->framelist_chunk); } VasEBoot_free (e); return 0; } static int VasEBoot_ehci_iterate (VasEBoot_usb_controller_iterate_hook_t hook, void *hook_data) { struct VasEBoot_ehci *e; struct VasEBoot_usb_controller dev; for (e = ehci; e; e = e->next) { dev.data = e; if (hook (&dev, hook_data)) return 1; } return 0; } static void VasEBoot_ehci_setup_qh (VasEBoot_ehci_qh_t qh, VasEBoot_usb_transfer_t transfer) { VasEBoot_uint32_t ep_char = 0; VasEBoot_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 != VasEBoot_USB_SPEED_HIGH) && (transfer->type == VasEBoot_USB_TRANSACTION_TYPE_CONTROL)) ep_char |= VasEBoot_EHCI_C; ep_char |= (transfer->max << VasEBoot_EHCI_MAXPLEN_OFF) & VasEBoot_EHCI_MAXPLEN_MASK; ep_char |= VasEBoot_EHCI_DTC; switch (transfer->dev->speed) { case VasEBoot_USB_SPEED_LOW: ep_char |= VasEBoot_EHCI_SPEED_LOW; break; case VasEBoot_USB_SPEED_FULL: ep_char |= VasEBoot_EHCI_SPEED_FULL; break; case VasEBoot_USB_SPEED_HIGH: default: ep_char |= VasEBoot_EHCI_SPEED_HIGH; /* XXX: How we will handle unknown value of speed? */ } ep_char |= (transfer->endpoint << VasEBoot_EHCI_EP_NUM_OFF) & VasEBoot_EHCI_EP_NUM_MASK; ep_char |= transfer->devaddr & VasEBoot_EHCI_DEVADDR_MASK; qh->ep_char = VasEBoot_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 |= VasEBoot_EHCI_MULT_THREE; ep_cap |= (transfer->dev->split_hubport << VasEBoot_EHCI_DEVPORT_OFF) & VasEBoot_EHCI_DEVPORT_MASK; ep_cap |= (transfer->dev->split_hubaddr << VasEBoot_EHCI_HUBADDR_OFF) & VasEBoot_EHCI_HUBADDR_MASK; if (transfer->dev->speed == VasEBoot_USB_SPEED_LOW && transfer->type != VasEBoot_USB_TRANSACTION_TYPE_CONTROL) { ep_cap |= (1<<0) << VasEBoot_EHCI_SMASK_OFF; ep_cap |= (7<<2) << VasEBoot_EHCI_CMASK_OFF; } qh->ep_cap = VasEBoot_cpu_to_le32 (ep_cap); VasEBoot_dprintf ("ehci", "setup_qh: qh=%p, not changed: qh_hptr=%08x\n", qh, VasEBoot_le_to_cpu32 (qh->qh_hptr)); VasEBoot_dprintf ("ehci", "setup_qh: ep_char=%08x, ep_cap=%08x\n", ep_char, ep_cap); VasEBoot_dprintf ("ehci", "setup_qh: end\n"); VasEBoot_dprintf ("ehci", "setup_qh: not changed: td_current=%08x\n", VasEBoot_le_to_cpu32 (qh->td_current)); VasEBoot_dprintf ("ehci", "setup_qh: not changed: next_td=%08x\n", VasEBoot_le_to_cpu32 (qh->td_overlay.next_td)); VasEBoot_dprintf ("ehci", "setup_qh: not changed: alt_next_td=%08x\n", VasEBoot_le_to_cpu32 (qh->td_overlay.alt_next_td)); VasEBoot_dprintf ("ehci", "setup_qh: not changed: token=%08x\n", VasEBoot_le_to_cpu32 (qh->td_overlay.token)); } static VasEBoot_ehci_qh_t VasEBoot_ehci_find_qh (struct VasEBoot_ehci *e, VasEBoot_usb_transfer_t transfer) { VasEBoot_uint32_t target, mask; int i; VasEBoot_ehci_qh_t qh = e->qh_virt; VasEBoot_ehci_qh_t head; VasEBoot_uint32_t qh_phys; VasEBoot_uint32_t qh_terminate = VasEBoot_EHCI_TERMINATE | VasEBoot_EHCI_HPTR_TYPE_QH; VasEBoot_ehci_qh_t qh_iter; /* Prepare part of EP Characteristic to find existing QH */ target = ((transfer->endpoint << VasEBoot_EHCI_EP_NUM_OFF) | transfer->devaddr) & VasEBoot_EHCI_TARGET_MASK; target = VasEBoot_cpu_to_le32 (target); mask = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TARGET_MASK); /* low speed interrupt transfers are linked to the periodic */ /* schedule, everything else to the asynchronous schedule */ if (transfer->dev->speed == VasEBoot_USB_SPEED_LOW && transfer->type != VasEBoot_USB_TRANSACTION_TYPE_CONTROL) head = &qh[0]; else head = &qh[1]; /* First try to find existing QH with proper target in proper list */ qh_phys = VasEBoot_le_to_cpu32( head->qh_hptr ); if (qh_phys != qh_terminate) qh_iter = VasEBoot_dma_phys2virt ( qh_phys & VasEBoot_EHCI_QHTDPTR_MASK, e->qh_chunk ); else qh_iter = NULL; for ( i = 0; (qh_phys != qh_terminate) && (qh_iter != NULL) && (qh_iter != head) && (i < VasEBoot_EHCI_N_QH); i++ ) { if (target == (qh_iter->ep_char & mask)) { /* Found proper existing (and linked) QH, do setup of QH */ VasEBoot_dprintf ("ehci", "find_qh: found, QH=%p\n", qh_iter); VasEBoot_ehci_setup_qh (qh_iter, transfer); sync_all_caches (e); return qh_iter; } qh_phys = VasEBoot_le_to_cpu32( qh_iter->qh_hptr ); if (qh_phys != qh_terminate) qh_iter = VasEBoot_dma_phys2virt ( qh_phys & VasEBoot_EHCI_QHTDPTR_MASK, e->qh_chunk ); else qh_iter = NULL; } /* variable "i" should be never equal to VasEBoot_EHCI_N_QH here */ if (i >= VasEBoot_EHCI_N_QH) { /* Something very bad happened in QH list(s) ! */ VasEBoot_dprintf ("ehci", "find_qh: Mismatch in QH list! head=%p\n", head); } /* QH with target_addr does not exist, we have to find and add it */ for (i = 2; i < VasEBoot_EHCI_N_QH; i++) /* We ignore zero and first QH */ { if (!qh[i].ep_char) break; /* Found first not-allocated QH, finish */ } /* Have we any free QH in array ? */ if (i >= VasEBoot_EHCI_N_QH) /* No. */ { VasEBoot_dprintf ("ehci", "find_qh: end - no free QH\n"); return NULL; } VasEBoot_dprintf ("ehci", "find_qh: new, i=%d, QH=%p\n", i, &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 */ VasEBoot_ehci_setup_qh (&qh[i], transfer); /* Linking - this new (last) QH will copy the QH from the head QH */ qh[i].qh_hptr = head->qh_hptr; /* Linking - the head QH will point to this new QH */ head->qh_hptr = VasEBoot_cpu_to_le32 (VasEBoot_EHCI_HPTR_TYPE_QH | VasEBoot_dma_virt2phys (&qh[i], e->qh_chunk)); return &qh[i]; } static VasEBoot_ehci_td_t VasEBoot_ehci_alloc_td (struct VasEBoot_ehci *e) { VasEBoot_ehci_td_t ret; /* Check if there is a Transfer Descriptor available. */ if (!e->tdfree_virt) { VasEBoot_dprintf ("ehci", "alloc_td: end - no free TD\n"); return NULL; } ret = e->tdfree_virt; /* Take current free TD */ /* Advance to next free TD in chain */ if (ret->link_td) e->tdfree_virt = VasEBoot_dma_phys2virt (ret->link_td, e->td_chunk); else e->tdfree_virt = NULL; ret->link_td = 0; /* Reset link_td in allocated TD */ return ret; } static void VasEBoot_ehci_free_td (struct VasEBoot_ehci *e, VasEBoot_ehci_td_t td) { /* Chain new free TD & rest */ if (e->tdfree_virt) td->link_td = VasEBoot_dma_virt2phys (e->tdfree_virt, e->td_chunk); else td->link_td = 0; e->tdfree_virt = td; /* Change address of first free TD */ } static void VasEBoot_ehci_free_tds (struct VasEBoot_ehci *e, VasEBoot_ehci_td_t td, VasEBoot_usb_transfer_t transfer, VasEBoot_size_t * actual) { int i; /* Index of TD in transfer */ VasEBoot_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++) { VasEBoot_ehci_td_t tdprev; token = VasEBoot_le_to_cpu32 (td->token); to_transfer = (token & VasEBoot_EHCI_TOTAL_MASK) >> VasEBoot_EHCI_TOTAL_OFF; /* Check state of TD - if it did not transfer * 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; if (td->link_td) td = VasEBoot_dma_phys2virt (td->link_td, e->td_chunk); else td = NULL; /* Free the TD. */ VasEBoot_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 VasEBoot_ehci_td_t VasEBoot_ehci_transaction (struct VasEBoot_ehci *e, VasEBoot_transfer_type_t type, unsigned int toggle, VasEBoot_size_t size, VasEBoot_uint32_t data, VasEBoot_ehci_td_t td_alt) { VasEBoot_ehci_td_t td; VasEBoot_uint32_t token; VasEBoot_uint32_t bufadr; int i; /* Test of transfer size, it can be: * <= VasEBoot_EHCI_MAXBUFLEN if data aligned to page boundary * <= VasEBoot_EHCI_MAXBUFLEN - VasEBoot_EHCI_BUFPAGELEN if not aligned * (worst case) */ if ((((data % VasEBoot_EHCI_BUFPAGELEN) == 0) && (size > VasEBoot_EHCI_MAXBUFLEN)) || (((data % VasEBoot_EHCI_BUFPAGELEN) != 0) && (size > (VasEBoot_EHCI_MAXBUFLEN - VasEBoot_EHCI_BUFPAGELEN)))) { VasEBoot_error (VasEBoot_ERR_OUT_OF_MEMORY, "too long data buffer for EHCI transaction"); return 0; } /* Grab a free Transfer Descriptor and initialize it. */ td = VasEBoot_ehci_alloc_td (e); if (!td) { VasEBoot_error (VasEBoot_ERR_OUT_OF_MEMORY, "no transfer descriptors available for EHCI transfer"); return 0; } VasEBoot_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 */ VasEBoot_memset ((void *) td, 0, sizeof (struct VasEBoot_ehci_td)); /* Don't point to any TD yet, just terminate. */ td->next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_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 = VasEBoot_cpu_to_le32 (VasEBoot_dma_virt2phys (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 ? VasEBoot_EHCI_TOGGLE : 0; token |= (size << VasEBoot_EHCI_TOTAL_OFF) & VasEBoot_EHCI_TOTAL_MASK; token |= VasEBoot_EHCI_CERR_3; switch (type) { case VasEBoot_USB_TRANSFER_TYPE_IN: token |= VasEBoot_EHCI_PIDCODE_IN; break; case VasEBoot_USB_TRANSFER_TYPE_OUT: token |= VasEBoot_EHCI_PIDCODE_OUT; break; case VasEBoot_USB_TRANSFER_TYPE_SETUP: token |= VasEBoot_EHCI_PIDCODE_SETUP; break; default: /* XXX: Should not happen, but what to do if it does ? */ break; } token |= VasEBoot_EHCI_STATUS_ACTIVE; td->token = VasEBoot_cpu_to_le32 (token); /* Fill buffer pointers according to size */ bufadr = data; td->buffer_page[0] = VasEBoot_cpu_to_le32 (bufadr); bufadr = ((bufadr / VasEBoot_EHCI_BUFPAGELEN) + 1) * VasEBoot_EHCI_BUFPAGELEN; for (i = 1; ((bufadr - data) < size) && (i < VasEBoot_EHCI_TD_BUF_PAGES); i++) { td->buffer_page[i] = VasEBoot_cpu_to_le32 (bufadr & VasEBoot_EHCI_BUFPTR_MASK); bufadr = ((bufadr / VasEBoot_EHCI_BUFPAGELEN) + 1) * VasEBoot_EHCI_BUFPAGELEN; } /* Remember data size for future use... */ td->size = (VasEBoot_uint32_t) size; VasEBoot_dprintf ("ehci", "td=%p\n", td); VasEBoot_dprintf ("ehci", "HW: next_td=%08x, alt_next_td=%08x\n", VasEBoot_le_to_cpu32 (td->next_td), VasEBoot_le_to_cpu32 (td->alt_next_td)); VasEBoot_dprintf ("ehci", "HW: token=%08x, buffer[0]=%08x\n", VasEBoot_le_to_cpu32 (td->token), VasEBoot_le_to_cpu32 (td->buffer_page[0])); VasEBoot_dprintf ("ehci", "HW: buffer[1]=%08x, buffer[2]=%08x\n", VasEBoot_le_to_cpu32 (td->buffer_page[1]), VasEBoot_le_to_cpu32 (td->buffer_page[2])); VasEBoot_dprintf ("ehci", "HW: buffer[3]=%08x, buffer[4]=%08x\n", VasEBoot_le_to_cpu32 (td->buffer_page[3]), VasEBoot_le_to_cpu32 (td->buffer_page[4])); VasEBoot_dprintf ("ehci", "link_td=%08x, size=%08x\n", td->link_td, td->size); return td; } struct VasEBoot_ehci_transfer_controller_data { VasEBoot_ehci_qh_t qh_virt; VasEBoot_ehci_td_t td_first_virt; VasEBoot_ehci_td_t td_alt_virt; VasEBoot_ehci_td_t td_last_virt; VasEBoot_uint32_t td_last_phys; }; static VasEBoot_usb_err_t VasEBoot_ehci_setup_transfer (VasEBoot_usb_controller_t dev, VasEBoot_usb_transfer_t transfer) { struct VasEBoot_ehci *e = (struct VasEBoot_ehci *) dev->data; VasEBoot_ehci_td_t td = NULL; VasEBoot_ehci_td_t td_prev = NULL; int i; struct VasEBoot_ehci_transfer_controller_data *cdata; VasEBoot_uint32_t status; sync_all_caches (e); /* Check if EHCI is running and AL is enabled */ status = VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS); if ((status & VasEBoot_EHCI_ST_HC_HALTED) != 0) /* XXX: Fix it: Currently we don't do anything to restart EHCI */ { VasEBoot_dprintf ("ehci", "setup_transfer: halted, status = 0x%x\n", status); return VasEBoot_USB_ERR_INTERNAL; } status = VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS); if ((status & (VasEBoot_EHCI_ST_AS_STATUS | VasEBoot_EHCI_ST_PS_STATUS)) == 0) /* XXX: Fix it: Currently we don't do anything to restart EHCI */ { VasEBoot_dprintf ("ehci", "setup_transfer: no AS/PS, status = 0x%x\n", status); return VasEBoot_USB_ERR_INTERNAL; } /* Allocate memory for controller transfer data. */ cdata = VasEBoot_malloc (sizeof (*cdata)); if (!cdata) return VasEBoot_USB_ERR_INTERNAL; cdata->td_first_virt = NULL; /* Allocate a queue head for the transfer queue. */ cdata->qh_virt = VasEBoot_ehci_find_qh (e, transfer); if (!cdata->qh_virt) { VasEBoot_dprintf ("ehci", "setup_transfer: no QH\n"); VasEBoot_free (cdata); return VasEBoot_USB_ERR_INTERNAL; } /* To detect short packet we need some additional "alternate" TD, * allocate it first. */ cdata->td_alt_virt = VasEBoot_ehci_alloc_td (e); if (!cdata->td_alt_virt) { VasEBoot_dprintf ("ehci", "setup_transfer: no TDs\n"); VasEBoot_free (cdata); return VasEBoot_USB_ERR_INTERNAL; } /* Fill whole alternate TD by zeros (= inactive) and set * Terminate bits and Halt bit */ VasEBoot_memset ((void *) cdata->td_alt_virt, 0, sizeof (struct VasEBoot_ehci_td)); cdata->td_alt_virt->next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); cdata->td_alt_virt->alt_next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); cdata->td_alt_virt->token = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_STATUS_HALTED); /* Allocate appropriate number of TDs and set */ for (i = 0; i < transfer->transcnt; i++) { VasEBoot_usb_transaction_t tr = &transfer->transactions[i]; td = VasEBoot_ehci_transaction (e, tr->pid, tr->toggle, tr->size, tr->data, cdata->td_alt_virt); if (!td) /* de-allocate and free all */ { VasEBoot_size_t actual = 0; if (cdata->td_first_virt) VasEBoot_ehci_free_tds (e, cdata->td_first_virt, NULL, &actual); VasEBoot_free (cdata); VasEBoot_dprintf ("ehci", "setup_transfer: no TD\n"); return VasEBoot_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 = VasEBoot_dma_virt2phys (td, e->td_chunk); td_prev->next_td = VasEBoot_cpu_to_le32 (VasEBoot_dma_virt2phys (td, e->td_chunk)); } td_prev = td; } /* Remember last TD */ cdata->td_last_virt = td; cdata->td_last_phys = VasEBoot_dma_virt2phys (td, e->td_chunk); /* Last TD should not have set alternate TD */ cdata->td_last_virt->alt_next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); VasEBoot_dprintf ("ehci", "setup_transfer: cdata=%p, qh=%p\n", cdata,cdata->qh_virt); VasEBoot_dprintf ("ehci", "setup_transfer: td_first=%p, td_alt=%p\n", cdata->td_first_virt, cdata->td_alt_virt); VasEBoot_dprintf ("ehci", "setup_transfer: td_last=%p\n", cdata->td_last_virt); /* Start transfer: */ /* Unlink possible alternate pointer in QH */ cdata->qh_virt->td_overlay.alt_next_td = VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_TERMINATE); /* Link new TDs with QH via next_td */ cdata->qh_virt->td_overlay.next_td = VasEBoot_cpu_to_le32 (VasEBoot_dma_virt2phys (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 = VasEBoot_cpu_to_le32_compile_time (0); sync_all_caches (e); /* Finito */ transfer->controller_data = cdata; return VasEBoot_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 VasEBoot_ehci_pre_finish_transfer (VasEBoot_usb_transfer_t transfer) { struct VasEBoot_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 | VasEBoot_cpu_to_le32_compile_time (VasEBoot_EHCI_STATUS_HALTED)) & VasEBoot_cpu_to_le32_compile_time (~VasEBoot_EHCI_STATUS_ACTIVE); /* Print debug data here if necessary */ } static VasEBoot_usb_err_t VasEBoot_ehci_parse_notrun (VasEBoot_usb_controller_t dev, VasEBoot_usb_transfer_t transfer, VasEBoot_size_t * actual) { struct VasEBoot_ehci *e = dev->data; struct VasEBoot_ehci_transfer_controller_data *cdata = transfer->controller_data; VasEBoot_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... */ VasEBoot_ehci_pre_finish_transfer (transfer); VasEBoot_ehci_free_tds (e, cdata->td_first_virt, transfer, actual); VasEBoot_ehci_free_td (e, cdata->td_alt_virt); VasEBoot_free (cdata); sync_all_caches (e); /* Additionally, do something with EHCI to make it running (what?) */ /* Try enable EHCI and AL */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, VasEBoot_EHCI_CMD_RUNSTOP | VasEBoot_EHCI_CMD_AS_ENABL | VasEBoot_EHCI_CMD_PS_ENABL | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); /* Ensure command is written */ VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND); return VasEBoot_USB_ERR_UNRECOVERABLE; } static VasEBoot_usb_err_t VasEBoot_ehci_parse_halt (VasEBoot_usb_controller_t dev, VasEBoot_usb_transfer_t transfer, VasEBoot_size_t * actual) { struct VasEBoot_ehci *e = dev->data; struct VasEBoot_ehci_transfer_controller_data *cdata = transfer->controller_data; VasEBoot_uint32_t token; VasEBoot_usb_err_t err = VasEBoot_USB_ERR_NAK; /* QH should be halted and not active in this case. */ VasEBoot_dprintf ("ehci", "parse_halt: info\n"); /* Remember token before call pre-finish function */ token = VasEBoot_le_to_cpu32 (cdata->qh_virt->td_overlay.token); /* Do things like in normal finish */ VasEBoot_ehci_pre_finish_transfer (transfer); VasEBoot_ehci_free_tds (e, cdata->td_first_virt, transfer, actual); VasEBoot_ehci_free_td (e, cdata->td_alt_virt); VasEBoot_free (cdata); sync_all_caches (e); /* Evaluation of error code - currently we don't have VasEBoot USB error * codes for some EHCI states, VasEBoot_USB_ERR_DATA is used for them. * Order of evaluation is critical, specially bubble/stall. */ if ((token & VasEBoot_EHCI_STATUS_BABBLE) != 0) err = VasEBoot_USB_ERR_BABBLE; else if ((token & VasEBoot_EHCI_CERR_MASK) != 0) err = VasEBoot_USB_ERR_STALL; else if ((token & VasEBoot_EHCI_STATUS_TRANERR) != 0) err = VasEBoot_USB_ERR_DATA; else if ((token & VasEBoot_EHCI_STATUS_BUFERR) != 0) err = VasEBoot_USB_ERR_DATA; else if ((token & VasEBoot_EHCI_STATUS_MISSDMF) != 0) err = VasEBoot_USB_ERR_DATA; return err; } static VasEBoot_usb_err_t VasEBoot_ehci_parse_success (VasEBoot_usb_controller_t dev, VasEBoot_usb_transfer_t transfer, VasEBoot_size_t * actual) { struct VasEBoot_ehci *e = dev->data; struct VasEBoot_ehci_transfer_controller_data *cdata = transfer->controller_data; VasEBoot_dprintf ("ehci", "parse_success: info\n"); /* QH should be not active in this case, but it is not halted. */ VasEBoot_ehci_pre_finish_transfer (transfer); VasEBoot_ehci_free_tds (e, cdata->td_first_virt, transfer, actual); VasEBoot_ehci_free_td (e, cdata->td_alt_virt); VasEBoot_free (cdata); sync_all_caches (e); return VasEBoot_USB_ERR_NONE; } static VasEBoot_usb_err_t VasEBoot_ehci_check_transfer (VasEBoot_usb_controller_t dev, VasEBoot_usb_transfer_t transfer, VasEBoot_size_t * actual) { struct VasEBoot_ehci *e = dev->data; struct VasEBoot_ehci_transfer_controller_data *cdata = transfer->controller_data; VasEBoot_uint32_t token, token_ftd; sync_all_caches (e); VasEBoot_dprintf ("ehci", "check_transfer: EHCI STATUS=%08x, cdata=%p, qh=%p\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS), cdata, cdata->qh_virt); VasEBoot_dprintf ("ehci", "check_transfer: qh_hptr=%08x, ep_char=%08x\n", VasEBoot_le_to_cpu32 (cdata->qh_virt->qh_hptr), VasEBoot_le_to_cpu32 (cdata->qh_virt->ep_char)); VasEBoot_dprintf ("ehci", "check_transfer: ep_cap=%08x, td_current=%08x\n", VasEBoot_le_to_cpu32 (cdata->qh_virt->ep_cap), VasEBoot_le_to_cpu32 (cdata->qh_virt->td_current)); VasEBoot_dprintf ("ehci", "check_transfer: next_td=%08x, alt_next_td=%08x\n", VasEBoot_le_to_cpu32 (cdata->qh_virt->td_overlay.next_td), VasEBoot_le_to_cpu32 (cdata->qh_virt->td_overlay.alt_next_td)); VasEBoot_dprintf ("ehci", "check_transfer: token=%08x, buffer[0]=%08x\n", VasEBoot_le_to_cpu32 (cdata->qh_virt->td_overlay.token), VasEBoot_le_to_cpu32 (cdata->qh_virt->td_overlay.buffer_page[0])); /* Check if EHCI is running and AL is enabled */ if ((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS) & VasEBoot_EHCI_ST_HC_HALTED) != 0) return VasEBoot_ehci_parse_notrun (dev, transfer, actual); if ((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS) & (VasEBoot_EHCI_ST_AS_STATUS | VasEBoot_EHCI_ST_PS_STATUS)) == 0) return VasEBoot_ehci_parse_notrun (dev, transfer, actual); token = VasEBoot_le_to_cpu32 (cdata->qh_virt->td_overlay.token); /* If the transfer consist from only one TD, we should check */ /* if the TD was really executed and deactivated - to prevent */ /* false detection of transfer finish. */ token_ftd = VasEBoot_le_to_cpu32 (cdata->td_first_virt->token); /* Detect QH halted */ if ((token & VasEBoot_EHCI_STATUS_HALTED) != 0) return VasEBoot_ehci_parse_halt (dev, transfer, actual); /* Detect QH not active - QH is not active and no next TD */ if (token && ((token & VasEBoot_EHCI_STATUS_ACTIVE) == 0) && ((token_ftd & VasEBoot_EHCI_STATUS_ACTIVE) == 0)) { /* It could be finish at all or short packet condition */ if ((VasEBoot_le_to_cpu32 (cdata->qh_virt->td_overlay.next_td) & VasEBoot_EHCI_TERMINATE) && ((VasEBoot_le_to_cpu32 (cdata->qh_virt->td_current) & VasEBoot_EHCI_QHTDPTR_MASK) == cdata->td_last_phys)) /* Normal finish */ return VasEBoot_ehci_parse_success (dev, transfer, actual); else if ((token & VasEBoot_EHCI_TOTAL_MASK) != 0) /* Short packet condition */ /* But currently we don't handle it - higher level will do it */ return VasEBoot_ehci_parse_success (dev, transfer, actual); } return VasEBoot_USB_ERR_WAIT; } static VasEBoot_usb_err_t VasEBoot_ehci_cancel_transfer (VasEBoot_usb_controller_t dev, VasEBoot_usb_transfer_t transfer) { struct VasEBoot_ehci *e = dev->data; struct VasEBoot_ehci_transfer_controller_data *cdata = transfer->controller_data; VasEBoot_size_t actual; int i; VasEBoot_uint64_t maxtime; VasEBoot_uint32_t qh_phys; sync_all_caches (e); VasEBoot_uint32_t interrupt = cdata->qh_virt->ep_cap & VasEBoot_EHCI_SMASK_MASK; /* QH can be active and should be de-activated and halted */ VasEBoot_dprintf ("ehci", "cancel_transfer: begin\n"); /* First check if EHCI is running - if not, there is no problem */ /* to cancel any transfer. Or, if transfer is asynchronous, check */ /* if AL is enabled - if not, transfer can be canceled also. */ if (((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS) & VasEBoot_EHCI_ST_HC_HALTED) != 0) || (!interrupt && ((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS) & (VasEBoot_EHCI_ST_AS_STATUS | VasEBoot_EHCI_ST_PS_STATUS)) == 0))) { VasEBoot_ehci_pre_finish_transfer (transfer); VasEBoot_ehci_free_tds (e, cdata->td_first_virt, transfer, &actual); VasEBoot_ehci_free_td (e, cdata->td_alt_virt); VasEBoot_free (cdata); sync_all_caches (e); VasEBoot_dprintf ("ehci", "cancel_transfer: end - EHCI not running\n"); return VasEBoot_USB_ERR_NONE; } /* EHCI and (AL or SL) are running. What to do? */ /* Try to Halt QH via de-scheduling QH. */ /* Find index of previous QH */ qh_phys = VasEBoot_dma_virt2phys(cdata->qh_virt, e->qh_chunk); for (i = 0; i < VasEBoot_EHCI_N_QH; i++) { if ((VasEBoot_le_to_cpu32(e->qh_virt[i].qh_hptr) & VasEBoot_EHCI_QHTDPTR_MASK) == qh_phys) break; } if (i == VasEBoot_EHCI_N_QH) { VasEBoot_printf ("%s: prev not found, queues are corrupt\n", __func__); return VasEBoot_USB_ERR_UNRECOVERABLE; } /* Unlink QH from AL */ e->qh_virt[i].qh_hptr = cdata->qh_virt->qh_hptr; sync_all_caches (e); /* If this is an interrupt transfer, we just wait for the periodic * schedule to advance a few times and then assume that the EHCI * controller has read the updated QH. */ if (cdata->qh_virt->ep_cap & VasEBoot_EHCI_SMASK_MASK) { VasEBoot_millisleep(20); } else { /* For the asynchronous schedule we use the advance doorbell to find * out when the EHCI controller has read the updated QH. */ /* Ring the doorbell */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, VasEBoot_EHCI_CMD_AS_ADV_D | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); /* Ensure command is written */ VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND); /* Wait answer with timeout */ maxtime = VasEBoot_get_time_ms () + 2; while (((VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS) & VasEBoot_EHCI_ST_AS_ADVANCE) == 0) && (VasEBoot_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 */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, ~VasEBoot_EHCI_CMD_AS_ADV_D & VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_STATUS, VasEBoot_EHCI_ST_AS_ADVANCE | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS)); /* Ensure command is written */ VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS); } /* Now is QH out of AL and we can do anything with it... */ VasEBoot_ehci_pre_finish_transfer (transfer); VasEBoot_ehci_free_tds (e, cdata->td_first_virt, transfer, &actual); VasEBoot_ehci_free_td (e, cdata->td_alt_virt); /* "Free" the QH - link it to itself */ cdata->qh_virt->ep_char = 0; cdata->qh_virt->qh_hptr = VasEBoot_cpu_to_le32 ((VasEBoot_dma_virt2phys (cdata->qh_virt, e->qh_chunk) & VasEBoot_EHCI_POINTER_MASK) | VasEBoot_EHCI_HPTR_TYPE_QH); VasEBoot_free (cdata); VasEBoot_dprintf ("ehci", "cancel_transfer: end\n"); sync_all_caches (e); return VasEBoot_USB_ERR_NONE; } static int VasEBoot_ehci_hubports (VasEBoot_usb_controller_t dev) { struct VasEBoot_ehci *e = (struct VasEBoot_ehci *) dev->data; VasEBoot_uint32_t portinfo; portinfo = VasEBoot_ehci_ehcc_read32 (e, VasEBoot_EHCI_EHCC_SPARAMS) & VasEBoot_EHCI_SPARAMS_N_PORTS; VasEBoot_dprintf ("ehci", "root hub ports=%d\n", portinfo); return portinfo; } static VasEBoot_usb_err_t VasEBoot_ehci_portstatus (VasEBoot_usb_controller_t dev, unsigned int port, unsigned int enable) { struct VasEBoot_ehci *e = (struct VasEBoot_ehci *) dev->data; VasEBoot_uint64_t endtime; VasEBoot_dprintf ("ehci", "portstatus: EHCI STATUS: %08x\n", VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_STATUS)); VasEBoot_dprintf ("ehci", "portstatus: begin, iobase=%p, port=%d, status=0x%02x\n", e->iobase, port, VasEBoot_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. */ VasEBoot_ehci_port_resbits (e, port, VasEBoot_EHCI_PORT_ENABLED); endtime = VasEBoot_get_time_ms () + 1000; while (VasEBoot_ehci_port_read (e, port) & VasEBoot_EHCI_PORT_ENABLED) if (VasEBoot_get_time_ms () > endtime) return VasEBoot_USB_ERR_TIMEOUT; if (!enable) /* We don't need reset port */ { VasEBoot_dprintf ("ehci", "portstatus: Disabled.\n"); VasEBoot_dprintf ("ehci", "portstatus: end, status=0x%02x\n", VasEBoot_ehci_port_read (e, port)); return VasEBoot_USB_ERR_NONE; } VasEBoot_dprintf ("ehci", "portstatus: enable\n"); VasEBoot_boot_time ("Resetting port %d", port); /* 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 */ VasEBoot_ehci_port_setbits (e, port, VasEBoot_EHCI_PORT_RESET); VasEBoot_millisleep (50); /* Reset RESET bit and wait for the end of reset */ VasEBoot_ehci_port_resbits (e, port, VasEBoot_EHCI_PORT_RESET); endtime = VasEBoot_get_time_ms () + 1000; while (VasEBoot_ehci_port_read (e, port) & VasEBoot_EHCI_PORT_RESET) if (VasEBoot_get_time_ms () > endtime) return VasEBoot_USB_ERR_TIMEOUT; VasEBoot_boot_time ("Port %d 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 ((VasEBoot_ehci_port_read (e, port) & VasEBoot_EHCI_PORT_ENABLED) != 0) /* yes! */ { VasEBoot_dprintf ("ehci", "portstatus: Enabled!\n"); /* "Reset recovery time" (USB spec.) */ VasEBoot_millisleep (10); } else /* no... */ { /* FULL speed device connected - change port ownership. * It results in disconnected state of this EHCI port. */ VasEBoot_ehci_port_setbits (e, port, VasEBoot_EHCI_PORT_OWNER); return VasEBoot_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 <> VasEBoot_ERR_NONE. It (maybe) displays also error * message on screen - but this situation is not error, it is normal * state! */ VasEBoot_dprintf ("ehci", "portstatus: end, status=0x%02x\n", VasEBoot_ehci_port_read (e, port)); return VasEBoot_USB_ERR_NONE; } static VasEBoot_usb_speed_t VasEBoot_ehci_detect_dev (VasEBoot_usb_controller_t dev, int port, int *changed) { struct VasEBoot_ehci *e = (struct VasEBoot_ehci *) dev->data; VasEBoot_uint32_t status, line_state; status = VasEBoot_ehci_port_read (e, port); /* Connect Status Change bit - it detects change of connection */ if (status & VasEBoot_EHCI_PORT_CONNECT_CH) { *changed = 1; /* Reset bit Connect Status Change */ VasEBoot_ehci_port_setbits (e, port, VasEBoot_EHCI_PORT_CONNECT_CH); } else *changed = 0; if (!(status & VasEBoot_EHCI_PORT_CONNECT)) { /* We should reset related "reset" flag in not connected state */ e->reset &= ~(1 << port); return VasEBoot_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 & VasEBoot_EHCI_PORT_ENABLED) != 0) /* Port already enabled, return high speed. */ return VasEBoot_USB_SPEED_HIGH; if ((status & VasEBoot_EHCI_PORT_OWNER) != 0) /* EHCI is not port owner */ return VasEBoot_USB_SPEED_NONE; /* EHCI driver is ignoring this port. */ if ((e->reset & (1 << port)) != 0) /* Port reset was done = FULL speed */ return VasEBoot_USB_SPEED_NONE; /* EHCI driver is ignoring this port. */ else /* Port connected but not enabled - test port speed. */ { line_state = status & VasEBoot_EHCI_PORT_LINE_STAT; if (line_state != VasEBoot_EHCI_PORT_LINE_LOWSP) return VasEBoot_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 ? */ VasEBoot_ehci_port_setbits (e, port, VasEBoot_EHCI_PORT_OWNER); return VasEBoot_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 VasEBoot_ehci_inithw (void) { VasEBoot_pci_iterate (VasEBoot_ehci_pci_iter, NULL); } static VasEBoot_err_t VasEBoot_ehci_restore_hw (void) { struct VasEBoot_ehci *e; VasEBoot_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 (VasEBoot_ehci_halt (e) != VasEBoot_USB_ERR_NONE) VasEBoot_error (VasEBoot_ERR_TIMEOUT, "restore_hw: EHCI halt timeout"); /* Reset EHCI */ if (VasEBoot_ehci_reset (e) != VasEBoot_USB_ERR_NONE) VasEBoot_error (VasEBoot_ERR_TIMEOUT, "restore_hw: EHCI reset timeout"); /* Setup some EHCI registers and enable EHCI */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_FL_BASE, e->framelist_phys); VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_CUR_AL_ADDR, VasEBoot_dma_virt2phys (&e->qh_virt[1], e->qh_chunk)); VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, VasEBoot_EHCI_CMD_RUNSTOP | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); /* Set ownership of root hub ports to EHCI */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_CONFIG_FLAG, VasEBoot_EHCI_CF_EHCI_OWNER); /* Enable asynchronous list */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, VasEBoot_EHCI_CMD_AS_ENABL | VasEBoot_EHCI_CMD_PS_ENABL | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); /* Now should be possible to power-up and enumerate ports etc. */ if ((VasEBoot_ehci_ehcc_read32 (e, VasEBoot_EHCI_EHCC_SPARAMS) & VasEBoot_EHCI_SPARAMS_PPC) != 0) { /* EHCI has port powering control */ /* Power on all ports */ n_ports = VasEBoot_ehci_ehcc_read32 (e, VasEBoot_EHCI_EHCC_SPARAMS) & VasEBoot_EHCI_SPARAMS_N_PORTS; for (i = 0; i < (int) n_ports; i++) VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_PORT_STAT_CMD + i * 4, VasEBoot_EHCI_PORT_POWER | VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_PORT_STAT_CMD + i * 4)); } } return VasEBoot_ERR_NONE; } static VasEBoot_err_t VasEBoot_ehci_fini_hw (int noreturn __attribute__ ((unused))) { struct VasEBoot_ehci *e; /* We should disable all EHCI HW to prevent any DMA access etc. */ for (e = ehci; e; e = e->next) { /* Disable both lists */ VasEBoot_ehci_oper_write32 (e, VasEBoot_EHCI_COMMAND, ~(VasEBoot_EHCI_CMD_AS_ENABL | VasEBoot_EHCI_CMD_PS_ENABL) & VasEBoot_ehci_oper_read32 (e, VasEBoot_EHCI_COMMAND)); /* Check if EHCI is halted and halt it if not */ VasEBoot_ehci_halt (e); /* Reset EHCI */ VasEBoot_ehci_reset (e); } return VasEBoot_ERR_NONE; } static struct VasEBoot_usb_controller_dev usb_controller = { .name = "ehci", .iterate = VasEBoot_ehci_iterate, .setup_transfer = VasEBoot_ehci_setup_transfer, .check_transfer = VasEBoot_ehci_check_transfer, .cancel_transfer = VasEBoot_ehci_cancel_transfer, .hubports = VasEBoot_ehci_hubports, .portstatus = VasEBoot_ehci_portstatus, .detect_dev = VasEBoot_ehci_detect_dev, /* estimated max. count of TDs for one bulk transfer */ .max_bulk_tds = VasEBoot_EHCI_N_TD * 3 / 4 }; VasEBoot_MOD_INIT (ehci) { COMPILE_TIME_ASSERT (sizeof (struct VasEBoot_ehci_td) == 64); COMPILE_TIME_ASSERT (sizeof (struct VasEBoot_ehci_qh) == 96); VasEBoot_stop_disk_firmware (); VasEBoot_boot_time ("Initing EHCI hardware"); VasEBoot_ehci_inithw (); VasEBoot_boot_time ("Registering EHCI driver"); VasEBoot_usb_controller_dev_register (&usb_controller); VasEBoot_boot_time ("EHCI driver registered"); VasEBoot_loader_register_preboot_hook (VasEBoot_ehci_fini_hw, VasEBoot_ehci_restore_hw, VasEBoot_LOADER_PREBOOT_HOOK_PRIO_DISK); } VasEBoot_MOD_FINI (ehci) { VasEBoot_ehci_fini_hw (0); VasEBoot_usb_controller_dev_unregister (&usb_controller); }