12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102 |
- /*
- * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program 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 2 of the
- * License, or (at your option) any later version.
- *
- * This program 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <strings.h>
- #include <errno.h>
- #include <byteswap.h>
- #include <ipxe/malloc.h>
- #include <ipxe/pci.h>
- #include <ipxe/usb.h>
- #include <ipxe/init.h>
- #include "ehci.h"
-
- /** @file
- *
- * USB Enhanced Host Controller Interface (EHCI) driver
- *
- */
-
- /**
- * Construct error code from transfer descriptor status
- *
- * @v status Transfer descriptor status
- * @ret rc Error code
- *
- * Bits 2-5 of the status code provide some indication as to the root
- * cause of the error. We incorporate these into the error code as
- * reported to usb_complete_err().
- */
- #define EIO_STATUS( status ) EUNIQ ( EINFO_EIO, ( ( (status) >> 2 ) & 0xf ) )
-
- /******************************************************************************
- *
- * Register access
- *
- ******************************************************************************
- */
-
- /**
- * Initialise device
- *
- * @v ehci EHCI device
- * @v regs MMIO registers
- */
- static void ehci_init ( struct ehci_device *ehci, void *regs ) {
- uint32_t hcsparams;
- uint32_t hccparams;
- size_t caplength;
-
- /* Locate capability and operational registers */
- ehci->cap = regs;
- caplength = readb ( ehci->cap + EHCI_CAP_CAPLENGTH );
- ehci->op = ( ehci->cap + caplength );
- DBGC2 ( ehci, "EHCI %s cap %08lx op %08lx\n", ehci->name,
- virt_to_phys ( ehci->cap ), virt_to_phys ( ehci->op ) );
-
- /* Read structural parameters */
- hcsparams = readl ( ehci->cap + EHCI_CAP_HCSPARAMS );
- ehci->ports = EHCI_HCSPARAMS_PORTS ( hcsparams );
- DBGC ( ehci, "EHCI %s has %d ports\n", ehci->name, ehci->ports );
-
- /* Read capability parameters 1 */
- hccparams = readl ( ehci->cap + EHCI_CAP_HCCPARAMS );
- ehci->addr64 = EHCI_HCCPARAMS_ADDR64 ( hccparams );
- ehci->flsize = ( EHCI_HCCPARAMS_FLSIZE ( hccparams ) ?
- EHCI_FLSIZE_SMALL : EHCI_FLSIZE_DEFAULT );
- ehci->eecp = EHCI_HCCPARAMS_EECP ( hccparams );
- DBGC2 ( ehci, "EHCI %s %d-bit flsize %d\n", ehci->name,
- ( ehci->addr64 ? 64 : 32 ), ehci->flsize );
- }
-
- /**
- * Find extended capability
- *
- * @v ehci EHCI device
- * @v pci PCI device
- * @v id Capability ID
- * @v offset Offset to previous extended capability instance, or zero
- * @ret offset Offset to extended capability, or zero if not found
- */
- static unsigned int ehci_extended_capability ( struct ehci_device *ehci,
- struct pci_device *pci,
- unsigned int id,
- unsigned int offset ) {
- uint32_t eecp;
-
- /* Locate the extended capability */
- while ( 1 ) {
-
- /* Locate first or next capability as applicable */
- if ( offset ) {
- pci_read_config_dword ( pci, offset, &eecp );
- offset = EHCI_EECP_NEXT ( eecp );
- } else {
- offset = ehci->eecp;
- }
- if ( ! offset )
- return 0;
-
- /* Check if this is the requested capability */
- pci_read_config_dword ( pci, offset, &eecp );
- if ( EHCI_EECP_ID ( eecp ) == id )
- return offset;
- }
- }
-
- /**
- * Calculate buffer alignment
- *
- * @v len Length
- * @ret align Buffer alignment
- *
- * Determine alignment required for a buffer which must be aligned to
- * at least EHCI_MIN_ALIGN and which must not cross a page boundary.
- */
- static inline size_t ehci_align ( size_t len ) {
- size_t align;
-
- /* Align to own length (rounded up to a power of two) */
- align = ( 1 << fls ( len - 1 ) );
-
- /* Round up to EHCI_MIN_ALIGN if needed */
- if ( align < EHCI_MIN_ALIGN )
- align = EHCI_MIN_ALIGN;
-
- return align;
- }
-
- /**
- * Check control data structure reachability
- *
- * @v ehci EHCI device
- * @v ptr Data structure pointer
- * @ret rc Return status code
- */
- static int ehci_ctrl_reachable ( struct ehci_device *ehci, void *ptr ) {
- physaddr_t phys = virt_to_phys ( ptr );
- uint32_t segment;
-
- /* Always reachable in a 32-bit build */
- if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) )
- return 0;
-
- /* Reachable only if control segment matches in a 64-bit build */
- segment = ( ( ( uint64_t ) phys ) >> 32 );
- if ( segment == ehci->ctrldssegment )
- return 0;
-
- return -ENOTSUP;
- }
-
- /******************************************************************************
- *
- * Diagnostics
- *
- ******************************************************************************
- */
-
- /**
- * Dump host controller registers
- *
- * @v ehci EHCI device
- */
- static __unused void ehci_dump ( struct ehci_device *ehci ) {
- uint8_t caplength;
- uint16_t hciversion;
- uint32_t hcsparams;
- uint32_t hccparams;
- uint32_t usbcmd;
- uint32_t usbsts;
- uint32_t usbintr;
- uint32_t frindex;
- uint32_t ctrldssegment;
- uint32_t periodiclistbase;
- uint32_t asynclistaddr;
- uint32_t configflag;
-
- /* Do nothing unless debugging is enabled */
- if ( ! DBG_LOG )
- return;
-
- /* Dump capability registers */
- caplength = readb ( ehci->cap + EHCI_CAP_CAPLENGTH );
- hciversion = readw ( ehci->cap + EHCI_CAP_HCIVERSION );
- hcsparams = readl ( ehci->cap + EHCI_CAP_HCSPARAMS );
- hccparams = readl ( ehci->cap + EHCI_CAP_HCCPARAMS );
- DBGC ( ehci, "EHCI %s caplen %02x hciversion %04x hcsparams %08x "
- "hccparams %08x\n", ehci->name, caplength, hciversion,
- hcsparams, hccparams );
-
- /* Dump operational registers */
- usbcmd = readl ( ehci->op + EHCI_OP_USBCMD );
- usbsts = readl ( ehci->op + EHCI_OP_USBSTS );
- usbintr = readl ( ehci->op + EHCI_OP_USBINTR );
- frindex = readl ( ehci->op + EHCI_OP_FRINDEX );
- ctrldssegment = readl ( ehci->op + EHCI_OP_CTRLDSSEGMENT );
- periodiclistbase = readl ( ehci->op + EHCI_OP_PERIODICLISTBASE );
- asynclistaddr = readl ( ehci->op + EHCI_OP_ASYNCLISTADDR );
- configflag = readl ( ehci->op + EHCI_OP_CONFIGFLAG );
- DBGC ( ehci, "EHCI %s usbcmd %08x usbsts %08x usbint %08x frindx "
- "%08x\n", ehci->name, usbcmd, usbsts, usbintr, frindex );
- DBGC ( ehci, "EHCI %s ctrlds %08x period %08x asyncl %08x cfgflg "
- "%08x\n", ehci->name, ctrldssegment, periodiclistbase,
- asynclistaddr, configflag );
- }
-
- /******************************************************************************
- *
- * USB legacy support
- *
- ******************************************************************************
- */
-
- /** Prevent the release of ownership back to BIOS */
- static int ehci_legacy_prevent_release;
-
- /**
- * Initialise USB legacy support
- *
- * @v ehci EHCI device
- * @v pci PCI device
- */
- static void ehci_legacy_init ( struct ehci_device *ehci,
- struct pci_device *pci ) {
- unsigned int legacy;
- uint8_t bios;
-
- /* Locate USB legacy support capability (if present) */
- legacy = ehci_extended_capability ( ehci, pci, EHCI_EECP_ID_LEGACY, 0 );
- if ( ! legacy ) {
- /* Not an error; capability may not be present */
- DBGC ( ehci, "EHCI %s has no USB legacy support capability\n",
- ehci->name );
- return;
- }
-
- /* Check if legacy USB support is enabled */
- pci_read_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_BIOS ), &bios );
- if ( ! ( bios & EHCI_USBLEGSUP_BIOS_OWNED ) ) {
- /* Not an error; already owned by OS */
- DBGC ( ehci, "EHCI %s USB legacy support already disabled\n",
- ehci->name );
- return;
- }
-
- /* Record presence of USB legacy support capability */
- ehci->legacy = legacy;
- }
-
- /**
- * Claim ownership from BIOS
- *
- * @v ehci EHCI device
- * @v pci PCI device
- */
- static void ehci_legacy_claim ( struct ehci_device *ehci,
- struct pci_device *pci ) {
- unsigned int legacy = ehci->legacy;
- uint32_t ctlsts;
- uint8_t bios;
- unsigned int i;
-
- /* Do nothing unless legacy support capability is present */
- if ( ! legacy )
- return;
-
- /* Dump original SMI usage */
- pci_read_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ),
- &ctlsts );
- if ( ctlsts ) {
- DBGC ( ehci, "EHCI %s BIOS using SMIs: %08x\n",
- ehci->name, ctlsts );
- }
-
- /* Claim ownership */
- pci_write_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_OS ),
- EHCI_USBLEGSUP_OS_OWNED );
-
- /* Wait for BIOS to release ownership */
- for ( i = 0 ; i < EHCI_USBLEGSUP_MAX_WAIT_MS ; i++ ) {
-
- /* Check if BIOS has released ownership */
- pci_read_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_BIOS ),
- &bios );
- if ( ! ( bios & EHCI_USBLEGSUP_BIOS_OWNED ) ) {
- DBGC ( ehci, "EHCI %s claimed ownership from BIOS\n",
- ehci->name );
- pci_read_config_dword ( pci, ( legacy +
- EHCI_USBLEGSUP_CTLSTS ),
- &ctlsts );
- if ( ctlsts ) {
- DBGC ( ehci, "EHCI %s warning: BIOS retained "
- "SMIs: %08x\n", ehci->name, ctlsts );
- }
- return;
- }
-
- /* Delay */
- mdelay ( 1 );
- }
-
- /* BIOS did not release ownership. Claim it forcibly by
- * disabling all SMIs.
- */
- DBGC ( ehci, "EHCI %s could not claim ownership from BIOS: forcibly "
- "disabling SMIs\n", ehci->name );
- pci_write_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ), 0 );
- }
-
- /**
- * Release ownership back to BIOS
- *
- * @v ehci EHCI device
- * @v pci PCI device
- */
- static void ehci_legacy_release ( struct ehci_device *ehci,
- struct pci_device *pci ) {
- unsigned int legacy = ehci->legacy;
- uint32_t ctlsts;
-
- /* Do nothing unless legacy support capability is present */
- if ( ! legacy )
- return;
-
- /* Do nothing if releasing ownership is prevented */
- if ( ehci_legacy_prevent_release ) {
- DBGC ( ehci, "EHCI %s not releasing ownership to BIOS\n",
- ehci->name );
- return;
- }
-
- /* Release ownership */
- pci_write_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_OS ), 0 );
- DBGC ( ehci, "EHCI %s released ownership to BIOS\n", ehci->name );
-
- /* Dump restored SMI usage */
- pci_read_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ),
- &ctlsts );
- DBGC ( ehci, "EHCI %s BIOS reclaimed SMIs: %08x\n",
- ehci->name, ctlsts );
- }
-
- /******************************************************************************
- *
- * Companion controllers
- *
- ******************************************************************************
- */
-
- /**
- * Poll child companion controllers
- *
- * @v ehci EHCI device
- */
- static void ehci_poll_companions ( struct ehci_device *ehci ) {
- struct usb_bus *bus;
- struct device_description *desc;
-
- /* Poll any USB buses belonging to child companion controllers */
- for_each_usb_bus ( bus ) {
-
- /* Get underlying devices description */
- desc = &bus->dev->desc;
-
- /* Skip buses that are not PCI devices */
- if ( desc->bus_type != BUS_TYPE_PCI )
- continue;
-
- /* Skip buses that are not part of the same PCI device */
- if ( PCI_FIRST_FUNC ( desc->location ) !=
- PCI_FIRST_FUNC ( ehci->bus->dev->desc.location ) )
- continue;
-
- /* Skip buses that are not UHCI or OHCI PCI devices */
- if ( ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL,
- PCI_CLASS_SERIAL_USB,
- PCI_CLASS_SERIAL_USB_UHCI ))&&
- ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL,
- PCI_CLASS_SERIAL_USB,
- PCI_CLASS_SERIAL_USB_OHCI ) ))
- continue;
-
- /* Poll child companion controller bus */
- DBGC2 ( ehci, "EHCI %s polling companion %s\n",
- ehci->name, bus->name );
- usb_poll ( bus );
- }
- }
-
- /**
- * Locate EHCI companion controller
- *
- * @v pci PCI device
- * @ret busdevfn EHCI companion controller bus:dev.fn (if any)
- */
- unsigned int ehci_companion ( struct pci_device *pci ) {
- struct pci_device tmp;
- unsigned int busdevfn;
- int rc;
-
- /* Look for an EHCI function on the same PCI device */
- busdevfn = pci->busdevfn;
- while ( ++busdevfn <= PCI_LAST_FUNC ( pci->busdevfn ) ) {
- pci_init ( &tmp, busdevfn );
- if ( ( rc = pci_read_config ( &tmp ) ) != 0 )
- continue;
- if ( tmp.class == PCI_CLASS ( PCI_CLASS_SERIAL,
- PCI_CLASS_SERIAL_USB,
- PCI_CLASS_SERIAL_USB_EHCI ) )
- return busdevfn;
- }
-
- return 0;
- }
-
- /******************************************************************************
- *
- * Run / stop / reset
- *
- ******************************************************************************
- */
-
- /**
- * Start EHCI device
- *
- * @v ehci EHCI device
- */
- static void ehci_run ( struct ehci_device *ehci ) {
- uint32_t usbcmd;
-
- /* Set run/stop bit */
- usbcmd = readl ( ehci->op + EHCI_OP_USBCMD );
- usbcmd &= ~EHCI_USBCMD_FLSIZE_MASK;
- usbcmd |= ( EHCI_USBCMD_RUN | EHCI_USBCMD_FLSIZE ( ehci->flsize ) |
- EHCI_USBCMD_PERIODIC | EHCI_USBCMD_ASYNC );
- writel ( usbcmd, ehci->op + EHCI_OP_USBCMD );
- }
-
- /**
- * Stop EHCI device
- *
- * @v ehci EHCI device
- * @ret rc Return status code
- */
- static int ehci_stop ( struct ehci_device *ehci ) {
- uint32_t usbcmd;
- uint32_t usbsts;
- unsigned int i;
-
- /* Clear run/stop bit */
- usbcmd = readl ( ehci->op + EHCI_OP_USBCMD );
- usbcmd &= ~( EHCI_USBCMD_RUN | EHCI_USBCMD_PERIODIC |
- EHCI_USBCMD_ASYNC );
- writel ( usbcmd, ehci->op + EHCI_OP_USBCMD );
-
- /* Wait for device to stop */
- for ( i = 0 ; i < EHCI_STOP_MAX_WAIT_MS ; i++ ) {
-
- /* Check if device is stopped */
- usbsts = readl ( ehci->op + EHCI_OP_USBSTS );
- if ( usbsts & EHCI_USBSTS_HCH )
- return 0;
-
- /* Delay */
- mdelay ( 1 );
- }
-
- DBGC ( ehci, "EHCI %s timed out waiting for stop\n", ehci->name );
- return -ETIMEDOUT;
- }
-
- /**
- * Reset EHCI device
- *
- * @v ehci EHCI device
- * @ret rc Return status code
- */
- static int ehci_reset ( struct ehci_device *ehci ) {
- uint32_t usbcmd;
- unsigned int i;
- int rc;
-
- /* The EHCI specification states that resetting a running
- * device may result in undefined behaviour, so try stopping
- * it first.
- */
- if ( ( rc = ehci_stop ( ehci ) ) != 0 ) {
- /* Ignore errors and attempt to reset the device anyway */
- }
-
- /* Reset device */
- writel ( EHCI_USBCMD_HCRST, ehci->op + EHCI_OP_USBCMD );
-
- /* Wait for reset to complete */
- for ( i = 0 ; i < EHCI_RESET_MAX_WAIT_MS ; i++ ) {
-
- /* Check if reset is complete */
- usbcmd = readl ( ehci->op + EHCI_OP_USBCMD );
- if ( ! ( usbcmd & EHCI_USBCMD_HCRST ) )
- return 0;
-
- /* Delay */
- mdelay ( 1 );
- }
-
- DBGC ( ehci, "EHCI %s timed out waiting for reset\n", ehci->name );
- return -ETIMEDOUT;
- }
-
- /******************************************************************************
- *
- * Transfer descriptor rings
- *
- ******************************************************************************
- */
-
- /**
- * Allocate transfer descriptor ring
- *
- * @v ehci EHCI device
- * @v ring Transfer descriptor ring
- * @ret rc Return status code
- */
- static int ehci_ring_alloc ( struct ehci_device *ehci,
- struct ehci_ring *ring ) {
- struct ehci_transfer_descriptor *desc;
- struct ehci_transfer_descriptor *next;
- unsigned int i;
- size_t len;
- uint32_t link;
- int rc;
-
- /* Initialise structure */
- memset ( ring, 0, sizeof ( *ring ) );
-
- /* Allocate I/O buffers */
- ring->iobuf = zalloc ( EHCI_RING_COUNT * sizeof ( ring->iobuf[0] ) );
- if ( ! ring->iobuf ) {
- rc = -ENOMEM;
- goto err_alloc_iobuf;
- }
-
- /* Allocate queue head */
- ring->head = malloc_dma ( sizeof ( *ring->head ),
- ehci_align ( sizeof ( *ring->head ) ) );
- if ( ! ring->head ) {
- rc = -ENOMEM;
- goto err_alloc_queue;
- }
- if ( ( rc = ehci_ctrl_reachable ( ehci, ring->head ) ) != 0 ) {
- DBGC ( ehci, "EHCI %s queue head unreachable\n", ehci->name );
- goto err_unreachable_queue;
- }
- memset ( ring->head, 0, sizeof ( *ring->head ) );
-
- /* Allocate transfer descriptors */
- len = ( EHCI_RING_COUNT * sizeof ( ring->desc[0] ) );
- ring->desc = malloc_dma ( len, sizeof ( ring->desc[0] ) );
- if ( ! ring->desc ) {
- rc = -ENOMEM;
- goto err_alloc_desc;
- }
- memset ( ring->desc, 0, len );
-
- /* Initialise transfer descriptors */
- for ( i = 0 ; i < EHCI_RING_COUNT ; i++ ) {
- desc = &ring->desc[i];
- if ( ( rc = ehci_ctrl_reachable ( ehci, desc ) ) != 0 ) {
- DBGC ( ehci, "EHCI %s descriptor unreachable\n",
- ehci->name );
- goto err_unreachable_desc;
- }
- next = &ring->desc[ ( i + 1 ) % EHCI_RING_COUNT ];
- link = virt_to_phys ( next );
- desc->next = cpu_to_le32 ( link );
- desc->alt = cpu_to_le32 ( link );
- }
-
- /* Initialise queue head */
- link = virt_to_phys ( &ring->desc[0] );
- ring->head->cache.next = cpu_to_le32 ( link );
-
- return 0;
-
- err_unreachable_desc:
- free_dma ( ring->desc, len );
- err_alloc_desc:
- err_unreachable_queue:
- free_dma ( ring->head, sizeof ( *ring->head ) );
- err_alloc_queue:
- free ( ring->iobuf );
- err_alloc_iobuf:
- return rc;
- }
-
- /**
- * Free transfer descriptor ring
- *
- * @v ring Transfer descriptor ring
- */
- static void ehci_ring_free ( struct ehci_ring *ring ) {
- unsigned int i;
-
- /* Sanity checks */
- assert ( ehci_ring_fill ( ring ) == 0 );
- for ( i = 0 ; i < EHCI_RING_COUNT ; i++ )
- assert ( ring->iobuf[i] == NULL );
-
- /* Free transfer descriptors */
- free_dma ( ring->desc, ( EHCI_RING_COUNT * sizeof ( ring->desc[0] ) ) );
-
- /* Free queue head */
- free_dma ( ring->head, sizeof ( *ring->head ) );
-
- /* Free I/O buffers */
- free ( ring->iobuf );
- }
-
- /**
- * Enqueue transfer descriptors
- *
- * @v ehci EHCI device
- * @v ring Transfer descriptor ring
- * @v iobuf I/O buffer
- * @v xfers Transfers
- * @v count Number of transfers
- * @ret rc Return status code
- */
- static int ehci_enqueue ( struct ehci_device *ehci, struct ehci_ring *ring,
- struct io_buffer *iobuf,
- const struct ehci_transfer *xfer,
- unsigned int count ) {
- struct ehci_transfer_descriptor *desc;
- physaddr_t phys;
- void *data;
- size_t len;
- size_t offset;
- size_t frag_len;
- unsigned int toggle;
- unsigned int index;
- unsigned int i;
-
- /* Sanity check */
- assert ( iobuf != NULL );
- assert ( count > 0 );
-
- /* Fail if ring does not have sufficient space */
- if ( ehci_ring_remaining ( ring ) < count )
- return -ENOBUFS;
-
- /* Fail if any portion is unreachable */
- for ( i = 0 ; i < count ; i++ ) {
- if ( ! xfer[i].len )
- continue;
- phys = ( virt_to_phys ( xfer[i].data ) + xfer[i].len - 1 );
- if ( ( phys > 0xffffffffUL ) && ( ! ehci->addr64 ) )
- return -ENOTSUP;
- }
-
- /* Enqueue each transfer, recording the I/O buffer with the last */
- for ( ; count ; ring->prod++, xfer++ ) {
-
- /* Populate descriptor header */
- index = ( ring->prod % EHCI_RING_COUNT );
- desc = &ring->desc[index];
- toggle = ( xfer->flags & EHCI_FL_TOGGLE );
- assert ( xfer->len <= EHCI_LEN_MASK );
- assert ( EHCI_FL_TOGGLE == EHCI_LEN_TOGGLE );
- desc->len = cpu_to_le16 ( xfer->len | toggle );
- desc->flags = ( xfer->flags | EHCI_FL_CERR_MAX );
-
- /* Populate buffer pointers */
- data = xfer->data;
- len = xfer->len;
- for ( i = 0 ; len ; i++ ) {
-
- /* Calculate length of this fragment */
- phys = virt_to_phys ( data );
- offset = ( phys & ( EHCI_PAGE_ALIGN - 1 ) );
- frag_len = ( EHCI_PAGE_ALIGN - offset );
- if ( frag_len > len )
- frag_len = len;
-
- /* Sanity checks */
- assert ( ( i == 0 ) || ( offset == 0 ) );
- assert ( i < ( sizeof ( desc->low ) /
- sizeof ( desc->low[0] ) ) );
-
- /* Populate buffer pointer */
- desc->low[i] = cpu_to_le32 ( phys );
- if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) {
- desc->high[i] =
- cpu_to_le32 ( ((uint64_t) phys) >> 32 );
- }
-
- /* Move to next fragment */
- data += frag_len;
- len -= frag_len;
- }
-
- /* Ensure everything is valid before activating descriptor */
- wmb();
- desc->status = EHCI_STATUS_ACTIVE;
-
- /* Record I/O buffer against last ring index */
- if ( --count == 0 )
- ring->iobuf[index] = iobuf;
- }
-
- return 0;
- }
-
- /**
- * Dequeue a transfer descriptor
- *
- * @v ring Transfer descriptor ring
- * @ret iobuf I/O buffer (or NULL)
- */
- static struct io_buffer * ehci_dequeue ( struct ehci_ring *ring ) {
- struct ehci_transfer_descriptor *desc;
- struct io_buffer *iobuf;
- unsigned int index = ( ring->cons % EHCI_RING_COUNT );
-
- /* Sanity check */
- assert ( ehci_ring_fill ( ring ) > 0 );
-
- /* Mark descriptor as inactive (and not halted) */
- desc = &ring->desc[index];
- desc->status = 0;
-
- /* Retrieve I/O buffer */
- iobuf = ring->iobuf[index];
- ring->iobuf[index] = NULL;
-
- /* Update consumer counter */
- ring->cons++;
-
- return iobuf;
- }
-
- /******************************************************************************
- *
- * Schedule management
- *
- ******************************************************************************
- */
-
- /**
- * Get link value for a queue head
- *
- * @v queue Queue head
- * @ret link Link value
- */
- static inline uint32_t ehci_link_qh ( struct ehci_queue_head *queue ) {
-
- return ( virt_to_phys ( queue ) | EHCI_LINK_TYPE_QH );
- }
-
- /**
- * (Re)build asynchronous schedule
- *
- * @v ehci EHCI device
- */
- static void ehci_async_schedule ( struct ehci_device *ehci ) {
- struct ehci_endpoint *endpoint;
- struct ehci_queue_head *queue;
- uint32_t link;
-
- /* Build schedule in reverse order of execution. Provided
- * that we only ever add or remove single endpoints, this can
- * safely run concurrently with hardware execution of the
- * schedule.
- */
- link = ehci_link_qh ( ehci->head );
- list_for_each_entry_reverse ( endpoint, &ehci->async, schedule ) {
- queue = endpoint->ring.head;
- queue->link = cpu_to_le32 ( link );
- wmb();
- link = ehci_link_qh ( queue );
- }
- ehci->head->link = cpu_to_le32 ( link );
- wmb();
- }
-
- /**
- * Add endpoint to asynchronous schedule
- *
- * @v endpoint Endpoint
- */
- static void ehci_async_add ( struct ehci_endpoint *endpoint ) {
- struct ehci_device *ehci = endpoint->ehci;
-
- /* Add to end of schedule */
- list_add_tail ( &endpoint->schedule, &ehci->async );
-
- /* Rebuild schedule */
- ehci_async_schedule ( ehci );
- }
-
- /**
- * Remove endpoint from asynchronous schedule
- *
- * @v endpoint Endpoint
- * @ret rc Return status code
- */
- static int ehci_async_del ( struct ehci_endpoint *endpoint ) {
- struct ehci_device *ehci = endpoint->ehci;
- uint32_t usbcmd;
- uint32_t usbsts;
- unsigned int i;
-
- /* Remove from schedule */
- list_check_contains_entry ( endpoint, &ehci->async, schedule );
- list_del ( &endpoint->schedule );
-
- /* Rebuild schedule */
- ehci_async_schedule ( ehci );
-
- /* Request notification when asynchronous schedule advances */
- usbcmd = readl ( ehci->op + EHCI_OP_USBCMD );
- usbcmd |= EHCI_USBCMD_ASYNC_ADVANCE;
- writel ( usbcmd, ehci->op + EHCI_OP_USBCMD );
-
- /* Wait for asynchronous schedule to advance */
- for ( i = 0 ; i < EHCI_ASYNC_ADVANCE_MAX_WAIT_MS ; i++ ) {
-
- /* Check for asynchronous schedule advancing */
- usbsts = readl ( ehci->op + EHCI_OP_USBSTS );
- if ( usbsts & EHCI_USBSTS_ASYNC_ADVANCE ) {
- usbsts &= ~EHCI_USBSTS_CHANGE;
- usbsts |= EHCI_USBSTS_ASYNC_ADVANCE;
- writel ( usbsts, ehci->op + EHCI_OP_USBSTS );
- return 0;
- }
-
- /* Delay */
- mdelay ( 1 );
- }
-
- /* Bad things will probably happen now */
- DBGC ( ehci, "EHCI %s timed out waiting for asynchronous schedule "
- "to advance\n", ehci->name );
- return -ETIMEDOUT;
- }
-
- /**
- * (Re)build periodic schedule
- *
- * @v ehci EHCI device
- */
- static void ehci_periodic_schedule ( struct ehci_device *ehci ) {
- struct ehci_endpoint *endpoint;
- struct ehci_queue_head *queue;
- uint32_t link;
- unsigned int frames;
- unsigned int max_interval;
- unsigned int i;
-
- /* Build schedule in reverse order of execution. Provided
- * that we only ever add or remove single endpoints, this can
- * safely run concurrently with hardware execution of the
- * schedule.
- */
- DBGCP ( ehci, "EHCI %s periodic schedule: ", ehci->name );
- link = EHCI_LINK_TERMINATE;
- list_for_each_entry_reverse ( endpoint, &ehci->periodic, schedule ) {
- queue = endpoint->ring.head;
- queue->link = cpu_to_le32 ( link );
- wmb();
- DBGCP ( ehci, "%s%d",
- ( ( link == EHCI_LINK_TERMINATE ) ? "" : "<-" ),
- endpoint->ep->interval );
- link = ehci_link_qh ( queue );
- }
- DBGCP ( ehci, "\n" );
-
- /* Populate periodic frame list */
- DBGCP ( ehci, "EHCI %s periodic frame list:", ehci->name );
- frames = EHCI_PERIODIC_FRAMES ( ehci->flsize );
- for ( i = 0 ; i < frames ; i++ ) {
-
- /* Calculate maximum interval (in microframes) which
- * may appear as part of this frame list.
- */
- if ( i == 0 ) {
- /* Start of list: include all endpoints */
- max_interval = -1U;
- } else {
- /* Calculate highest power-of-two frame interval */
- max_interval = ( 1 << ( ffs ( i ) - 1 ) );
- /* Convert to microframes */
- max_interval <<= 3;
- /* Round up to nearest 2^n-1 */
- max_interval = ( ( max_interval << 1 ) - 1 );
- }
-
- /* Find first endpoint in schedule satisfying this
- * maximum interval constraint.
- */
- link = EHCI_LINK_TERMINATE;
- list_for_each_entry ( endpoint, &ehci->periodic, schedule ) {
- if ( endpoint->ep->interval <= max_interval ) {
- queue = endpoint->ring.head;
- link = ehci_link_qh ( queue );
- DBGCP ( ehci, " %d:%d",
- i, endpoint->ep->interval );
- break;
- }
- }
- ehci->frame[i].link = cpu_to_le32 ( link );
- }
- wmb();
- DBGCP ( ehci, "\n" );
- }
-
- /**
- * Add endpoint to periodic schedule
- *
- * @v endpoint Endpoint
- */
- static void ehci_periodic_add ( struct ehci_endpoint *endpoint ) {
- struct ehci_device *ehci = endpoint->ehci;
- struct ehci_endpoint *before;
- unsigned int interval = endpoint->ep->interval;
-
- /* Find first endpoint with a smaller interval */
- list_for_each_entry ( before, &ehci->periodic, schedule ) {
- if ( before->ep->interval < interval )
- break;
- }
- list_add_tail ( &endpoint->schedule, &before->schedule );
-
- /* Rebuild schedule */
- ehci_periodic_schedule ( ehci );
- }
-
- /**
- * Remove endpoint from periodic schedule
- *
- * @v endpoint Endpoint
- * @ret rc Return status code
- */
- static int ehci_periodic_del ( struct ehci_endpoint *endpoint ) {
- struct ehci_device *ehci = endpoint->ehci;
-
- /* Remove from schedule */
- list_check_contains_entry ( endpoint, &ehci->periodic, schedule );
- list_del ( &endpoint->schedule );
-
- /* Rebuild schedule */
- ehci_periodic_schedule ( ehci );
-
- /* Delay for a whole USB frame (with a 100% safety margin) */
- mdelay ( 2 );
-
- return 0;
- }
-
- /**
- * Add endpoint to appropriate schedule
- *
- * @v endpoint Endpoint
- */
- static void ehci_schedule_add ( struct ehci_endpoint *endpoint ) {
- struct usb_endpoint *ep = endpoint->ep;
- unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK );
-
- if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) {
- ehci_periodic_add ( endpoint );
- } else {
- ehci_async_add ( endpoint );
- }
- }
-
- /**
- * Remove endpoint from appropriate schedule
- *
- * @v endpoint Endpoint
- * @ret rc Return status code
- */
- static int ehci_schedule_del ( struct ehci_endpoint *endpoint ) {
- struct usb_endpoint *ep = endpoint->ep;
- unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK );
-
- if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) {
- return ehci_periodic_del ( endpoint );
- } else {
- return ehci_async_del ( endpoint );
- }
- }
-
- /******************************************************************************
- *
- * Endpoint operations
- *
- ******************************************************************************
- */
-
- /**
- * Determine endpoint characteristics
- *
- * @v ep USB endpoint
- * @ret chr Endpoint characteristics
- */
- static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) {
- struct usb_device *usb = ep->usb;
- unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK );
- uint32_t chr;
-
- /* Determine basic characteristics */
- chr = ( EHCI_CHR_ADDRESS ( usb->address ) |
- EHCI_CHR_ENDPOINT ( ep->address ) |
- EHCI_CHR_MAX_LEN ( ep->mtu ) );
-
- /* Control endpoints require manual control of the data toggle */
- if ( attr == USB_ENDPOINT_ATTR_CONTROL )
- chr |= EHCI_CHR_TOGGLE;
-
- /* Determine endpoint speed */
- if ( usb->speed == USB_SPEED_HIGH ) {
- chr |= EHCI_CHR_EPS_HIGH;
- } else {
- if ( usb->speed == USB_SPEED_FULL ) {
- chr |= EHCI_CHR_EPS_FULL;
- } else {
- chr |= EHCI_CHR_EPS_LOW;
- }
- if ( attr == USB_ENDPOINT_ATTR_CONTROL )
- chr |= EHCI_CHR_CONTROL;
- }
-
- return chr;
- }
-
- /**
- * Determine endpoint capabilities
- *
- * @v ep USB endpoint
- * @ret cap Endpoint capabilities
- */
- static uint32_t ehci_endpoint_capabilities ( struct usb_endpoint *ep ) {
- struct usb_device *usb = ep->usb;
- struct usb_port *tt = usb_transaction_translator ( usb );
- unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK );
- uint32_t cap;
- unsigned int i;
-
- /* Determine basic capabilities */
- cap = EHCI_CAP_MULT ( ep->burst + 1 );
-
- /* Determine interrupt schedule mask, if applicable */
- if ( ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) &&
- ( ( ep->interval != 0 ) /* avoid infinite loop */ ) ) {
- for ( i = 0 ; i < 8 /* microframes per frame */ ;
- i += ep->interval ) {
- cap |= EHCI_CAP_INTR_SCHED ( i );
- }
- }
-
- /* Set transaction translator hub address and port, if applicable */
- if ( tt ) {
- assert ( tt->hub->usb );
- cap |= ( EHCI_CAP_TT_HUB ( tt->hub->usb->address ) |
- EHCI_CAP_TT_PORT ( tt->address ) );
- if ( attr == USB_ENDPOINT_ATTR_INTERRUPT )
- cap |= EHCI_CAP_SPLIT_SCHED_DEFAULT;
- }
-
- return cap;
- }
-
- /**
- * Update endpoint characteristics and capabilities
- *
- * @v ep USB endpoint
- */
- static void ehci_endpoint_update ( struct usb_endpoint *ep ) {
- struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
- struct ehci_queue_head *head;
-
- /* Update queue characteristics and capabilities */
- head = endpoint->ring.head;
- head->chr = cpu_to_le32 ( ehci_endpoint_characteristics ( ep ) );
- head->cap = cpu_to_le32 ( ehci_endpoint_capabilities ( ep ) );
- }
-
- /**
- * Open endpoint
- *
- * @v ep USB endpoint
- * @ret rc Return status code
- */
- static int ehci_endpoint_open ( struct usb_endpoint *ep ) {
- struct usb_device *usb = ep->usb;
- struct ehci_device *ehci = usb_get_hostdata ( usb );
- struct ehci_endpoint *endpoint;
- int rc;
-
- /* Allocate and initialise structure */
- endpoint = zalloc ( sizeof ( *endpoint ) );
- if ( ! endpoint ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- endpoint->ehci = ehci;
- endpoint->ep = ep;
- usb_endpoint_set_hostdata ( ep, endpoint );
-
- /* Initialise descriptor ring */
- if ( ( rc = ehci_ring_alloc ( ehci, &endpoint->ring ) ) != 0 )
- goto err_ring_alloc;
-
- /* Update queue characteristics and capabilities */
- ehci_endpoint_update ( ep );
-
- /* Add to list of endpoints */
- list_add_tail ( &endpoint->list, &ehci->endpoints );
-
- /* Add to schedule */
- ehci_schedule_add ( endpoint );
-
- return 0;
-
- ehci_ring_free ( &endpoint->ring );
- err_ring_alloc:
- free ( endpoint );
- err_alloc:
- return rc;
- }
-
- /**
- * Close endpoint
- *
- * @v ep USB endpoint
- */
- static void ehci_endpoint_close ( struct usb_endpoint *ep ) {
- struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
- struct ehci_device *ehci = endpoint->ehci;
- struct usb_device *usb = ep->usb;
- struct io_buffer *iobuf;
- int rc;
-
- /* Remove from schedule */
- if ( ( rc = ehci_schedule_del ( endpoint ) ) != 0 ) {
- /* No way to prevent hardware from continuing to
- * access the memory, so leak it.
- */
- DBGC ( ehci, "EHCI %s %s could not unschedule: %s\n",
- usb->name, usb_endpoint_name ( ep ), strerror ( rc ) );
- return;
- }
-
- /* Cancel any incomplete transfers */
- while ( ehci_ring_fill ( &endpoint->ring ) ) {
- iobuf = ehci_dequeue ( &endpoint->ring );
- if ( iobuf )
- usb_complete_err ( ep, iobuf, -ECANCELED );
- }
-
- /* Remove from list of endpoints */
- list_del ( &endpoint->list );
-
- /* Free descriptor ring */
- ehci_ring_free ( &endpoint->ring );
-
- /* Free endpoint */
- free ( endpoint );
- }
-
- /**
- * Reset endpoint
- *
- * @v ep USB endpoint
- * @ret rc Return status code
- */
- static int ehci_endpoint_reset ( struct usb_endpoint *ep ) {
- struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
- struct ehci_ring *ring = &endpoint->ring;
- struct ehci_transfer_descriptor *cache = &ring->head->cache;
- uint32_t link;
-
- /* Sanity checks */
- assert ( ! ( cache->status & EHCI_STATUS_ACTIVE ) );
- assert ( cache->status & EHCI_STATUS_HALTED );
-
- /* Reset residual count */
- ring->residual = 0;
-
- /* Reset data toggle */
- cache->len = 0;
-
- /* Prepare to restart at next unconsumed descriptor */
- link = virt_to_phys ( &ring->desc[ ring->cons % EHCI_RING_COUNT ] );
- cache->next = cpu_to_le32 ( link );
-
- /* Restart ring */
- wmb();
- cache->status = 0;
-
- return 0;
- }
-
- /**
- * Update MTU
- *
- * @v ep USB endpoint
- * @ret rc Return status code
- */
- static int ehci_endpoint_mtu ( struct usb_endpoint *ep ) {
-
- /* Update endpoint characteristics and capabilities */
- ehci_endpoint_update ( ep );
-
- return 0;
- }
-
- /**
- * Enqueue message transfer
- *
- * @v ep USB endpoint
- * @v iobuf I/O buffer
- * @ret rc Return status code
- */
- static int ehci_endpoint_message ( struct usb_endpoint *ep,
- struct io_buffer *iobuf ) {
- struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
- struct ehci_device *ehci = endpoint->ehci;
- struct usb_setup_packet *packet;
- unsigned int input;
- struct ehci_transfer xfers[3];
- struct ehci_transfer *xfer = xfers;
- size_t len;
- int rc;
-
- /* Construct setup stage */
- assert ( iob_len ( iobuf ) >= sizeof ( *packet ) );
- packet = iobuf->data;
- iob_pull ( iobuf, sizeof ( *packet ) );
- xfer->data = packet;
- xfer->len = sizeof ( *packet );
- xfer->flags = EHCI_FL_PID_SETUP;
- xfer++;
-
- /* Construct data stage, if applicable */
- len = iob_len ( iobuf );
- input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) );
- if ( len ) {
- xfer->data = iobuf->data;
- xfer->len = len;
- xfer->flags = ( EHCI_FL_TOGGLE |
- ( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT ) );
- xfer++;
- }
-
- /* Construct status stage */
- xfer->data = NULL;
- xfer->len = 0;
- xfer->flags = ( EHCI_FL_TOGGLE | EHCI_FL_IOC |
- ( ( len && input ) ? EHCI_FL_PID_OUT : EHCI_FL_PID_IN));
- xfer++;
-
- /* Enqueue transfer */
- if ( ( rc = ehci_enqueue ( ehci, &endpoint->ring, iobuf, xfers,
- ( xfer - xfers ) ) ) != 0 )
- return rc;
-
- return 0;
- }
-
- /**
- * Calculate number of transfer descriptors
- *
- * @v len Length of data
- * @v zlp Append a zero-length packet
- * @ret count Number of transfer descriptors
- */
- static unsigned int ehci_endpoint_count ( size_t len, int zlp ) {
- unsigned int count;
-
- /* Split into 16kB transfers. A single transfer can handle up
- * to 20kB if it happens to be page-aligned, or up to 16kB
- * with arbitrary alignment. We simplify the code by assuming
- * that we can fit only 16kB into each transfer.
- */
- count = ( ( len + EHCI_MTU - 1 ) / EHCI_MTU );
-
- /* Append a zero-length transfer if applicable */
- if ( zlp || ( count == 0 ) )
- count++;
-
- return count;
- }
-
- /**
- * Enqueue stream transfer
- *
- * @v ep USB endpoint
- * @v iobuf I/O buffer
- * @v zlp Append a zero-length packet
- * @ret rc Return status code
- */
- static int ehci_endpoint_stream ( struct usb_endpoint *ep,
- struct io_buffer *iobuf, int zlp ) {
- struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
- struct ehci_device *ehci = endpoint->ehci;
- void *data = iobuf->data;
- size_t len = iob_len ( iobuf );
- unsigned int count = ehci_endpoint_count ( len, zlp );
- unsigned int input = ( ep->address & USB_DIR_IN );
- unsigned int flags = ( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT );
- struct ehci_transfer xfers[count];
- struct ehci_transfer *xfer = xfers;
- size_t xfer_len;
- unsigned int i;
- int rc;
-
- /* Create transfers */
- for ( i = 0 ; i < count ; i++ ) {
-
- /* Calculate transfer length */
- xfer_len = EHCI_MTU;
- if ( xfer_len > len )
- xfer_len = len;
-
- /* Create transfer */
- xfer->data = data;
- xfer->len = xfer_len;
- xfer->flags = flags;
-
- /* Move to next transfer */
- data += xfer_len;
- len -= xfer_len;
- xfer++;
- }
- xfer[-1].flags |= EHCI_FL_IOC;
-
- /* Enqueue transfer */
- if ( ( rc = ehci_enqueue ( ehci, &endpoint->ring, iobuf, xfers,
- count ) ) != 0 )
- return rc;
-
- return 0;
- }
-
- /**
- * Poll for completions
- *
- * @v endpoint Endpoint
- */
- static void ehci_endpoint_poll ( struct ehci_endpoint *endpoint ) {
- struct ehci_device *ehci = endpoint->ehci;
- struct ehci_ring *ring = &endpoint->ring;
- struct ehci_transfer_descriptor *desc;
- struct usb_endpoint *ep = endpoint->ep;
- struct usb_device *usb = ep->usb;
- struct io_buffer *iobuf;
- unsigned int index;
- unsigned int status;
- int rc;
-
- /* Consume all completed descriptors */
- while ( ehci_ring_fill ( &endpoint->ring ) ) {
-
- /* Stop if we reach an uncompleted descriptor */
- rmb();
- index = ( ring->cons % EHCI_RING_COUNT );
- desc = &ring->desc[index];
- status = desc->status;
- if ( status & EHCI_STATUS_ACTIVE )
- break;
-
- /* Consume this descriptor */
- iobuf = ehci_dequeue ( ring );
-
- /* If we have encountered an error, then consume all
- * remaining descriptors in this transaction, report
- * the error to the USB core, and stop further
- * processing.
- */
- if ( status & EHCI_STATUS_HALTED ) {
- rc = -EIO_STATUS ( status );
- DBGC ( ehci, "EHCI %s %s completion %d failed (status "
- "%02x): %s\n", usb->name,
- usb_endpoint_name ( ep ), index, status,
- strerror ( rc ) );
- while ( ! iobuf )
- iobuf = ehci_dequeue ( ring );
- usb_complete_err ( endpoint->ep, iobuf, rc );
- return;
- }
-
- /* Accumulate residual data count */
- ring->residual += ( le16_to_cpu ( desc->len ) & EHCI_LEN_MASK );
-
- /* If this is not the end of a transaction (i.e. has
- * no I/O buffer), then continue to next descriptor.
- */
- if ( ! iobuf )
- continue;
-
- /* Update I/O buffer length */
- iob_unput ( iobuf, ring->residual );
- ring->residual = 0;
-
- /* Report completion to USB core */
- usb_complete ( endpoint->ep, iobuf );
- }
- }
-
- /******************************************************************************
- *
- * Device operations
- *
- ******************************************************************************
- */
-
- /**
- * Open device
- *
- * @v usb USB device
- * @ret rc Return status code
- */
- static int ehci_device_open ( struct usb_device *usb ) {
- struct ehci_device *ehci = usb_bus_get_hostdata ( usb->port->hub->bus );
-
- usb_set_hostdata ( usb, ehci );
- return 0;
- }
-
- /**
- * Close device
- *
- * @v usb USB device
- */
- static void ehci_device_close ( struct usb_device *usb ) {
- struct ehci_device *ehci = usb_get_hostdata ( usb );
- struct usb_bus *bus = ehci->bus;
-
- /* Free device address, if assigned */
- if ( usb->address )
- usb_free_address ( bus, usb->address );
- }
-
- /**
- * Assign device address
- *
- * @v usb USB device
- * @ret rc Return status code
- */
- static int ehci_device_address ( struct usb_device *usb ) {
- struct ehci_device *ehci = usb_get_hostdata ( usb );
- struct usb_bus *bus = ehci->bus;
- struct usb_endpoint *ep0 = usb_endpoint ( usb, USB_EP0_ADDRESS );
- int address;
- int rc;
-
- /* Sanity checks */
- assert ( usb->address == 0 );
- assert ( ep0 != NULL );
-
- /* Allocate device address */
- address = usb_alloc_address ( bus );
- if ( address < 0 ) {
- rc = address;
- DBGC ( ehci, "EHCI %s could not allocate address: %s\n",
- usb->name, strerror ( rc ) );
- goto err_alloc_address;
- }
-
- /* Set address */
- if ( ( rc = usb_set_address ( usb, address ) ) != 0 )
- goto err_set_address;
-
- /* Update device address */
- usb->address = address;
-
- /* Update control endpoint characteristics and capabilities */
- ehci_endpoint_update ( ep0 );
-
- return 0;
-
- err_set_address:
- usb_free_address ( bus, address );
- err_alloc_address:
- return rc;
- }
-
- /******************************************************************************
- *
- * Hub operations
- *
- ******************************************************************************
- */
-
- /**
- * Open hub
- *
- * @v hub USB hub
- * @ret rc Return status code
- */
- static int ehci_hub_open ( struct usb_hub *hub __unused ) {
-
- /* Nothing to do */
- return 0;
- }
-
- /**
- * Close hub
- *
- * @v hub USB hub
- */
- static void ehci_hub_close ( struct usb_hub *hub __unused ) {
-
- /* Nothing to do */
- }
-
- /******************************************************************************
- *
- * Root hub operations
- *
- ******************************************************************************
- */
-
- /**
- * Open root hub
- *
- * @v hub USB hub
- * @ret rc Return status code
- */
- static int ehci_root_open ( struct usb_hub *hub ) {
- struct usb_bus *bus = hub->bus;
- struct ehci_device *ehci = usb_bus_get_hostdata ( bus );
- uint32_t portsc;
- unsigned int i;
-
- /* Route all ports to EHCI controller */
- writel ( EHCI_CONFIGFLAG_CF, ehci->op + EHCI_OP_CONFIGFLAG );
-
- /* Enable power to all ports */
- for ( i = 1 ; i <= ehci->ports ; i++ ) {
- portsc = readl ( ehci->op + EHCI_OP_PORTSC ( i ) );
- portsc &= ~EHCI_PORTSC_CHANGE;
- portsc |= EHCI_PORTSC_PP;
- writel ( portsc, ehci->op + EHCI_OP_PORTSC ( i ) );
- }
-
- /* Wait 20ms after potentially enabling power to a port */
- mdelay ( EHCI_PORT_POWER_DELAY_MS );
-
- /* Record hub driver private data */
- usb_hub_set_drvdata ( hub, ehci );
-
- return 0;
- }
-
- /**
- * Close root hub
- *
- * @v hub USB hub
- */
- static void ehci_root_close ( struct usb_hub *hub ) {
- struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
-
- /* Route all ports back to companion controllers */
- writel ( 0, ehci->op + EHCI_OP_CONFIGFLAG );
-
- /* Clear hub driver private data */
- usb_hub_set_drvdata ( hub, NULL );
- }
-
- /**
- * Enable port
- *
- * @v hub USB hub
- * @v port USB port
- * @ret rc Return status code
- */
- static int ehci_root_enable ( struct usb_hub *hub, struct usb_port *port ) {
- struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
- uint32_t portsc;
- unsigned int line;
- unsigned int i;
-
- /* Check for a low-speed device */
- portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) );
- line = EHCI_PORTSC_LINE_STATUS ( portsc );
- if ( line == EHCI_PORTSC_LINE_STATUS_LOW ) {
- DBGC ( ehci, "EHCI %s-%d detected low-speed device: "
- "disowning\n", ehci->name, port->address );
- goto disown;
- }
-
- /* Reset port */
- portsc &= ~( EHCI_PORTSC_PED | EHCI_PORTSC_CHANGE );
- portsc |= EHCI_PORTSC_PR;
- writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) );
- mdelay ( USB_RESET_DELAY_MS );
- portsc &= ~EHCI_PORTSC_PR;
- writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) );
-
- /* Wait for reset to complete */
- for ( i = 0 ; i < EHCI_PORT_RESET_MAX_WAIT_MS ; i++ ) {
-
- /* Check port status */
- portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) );
- if ( ! ( portsc & EHCI_PORTSC_PR ) ) {
- if ( portsc & EHCI_PORTSC_PED )
- return 0;
- DBGC ( ehci, "EHCI %s-%d not enabled after reset: "
- "disowning\n", ehci->name, port->address );
- goto disown;
- }
-
- /* Delay */
- mdelay ( 1 );
- }
-
- DBGC ( ehci, "EHCI %s-%d timed out waiting for port to reset\n",
- ehci->name, port->address );
- return -ETIMEDOUT;
-
- disown:
- /* Disown port */
- portsc &= ~EHCI_PORTSC_CHANGE;
- portsc |= EHCI_PORTSC_OWNER;
- writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) );
-
- /* Delay to allow child companion controllers to settle */
- mdelay ( EHCI_DISOWN_DELAY_MS );
-
- /* Poll child companion controllers */
- ehci_poll_companions ( ehci );
-
- return -ENODEV;
- }
-
- /**
- * Disable port
- *
- * @v hub USB hub
- * @v port USB port
- * @ret rc Return status code
- */
- static int ehci_root_disable ( struct usb_hub *hub, struct usb_port *port ) {
- struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
- uint32_t portsc;
-
- /* Disable port */
- portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) );
- portsc &= ~( EHCI_PORTSC_PED | EHCI_PORTSC_CHANGE );
- writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) );
-
- return 0;
- }
-
- /**
- * Update root hub port speed
- *
- * @v hub USB hub
- * @v port USB port
- * @ret rc Return status code
- */
- static int ehci_root_speed ( struct usb_hub *hub, struct usb_port *port ) {
- struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
- uint32_t portsc;
- unsigned int speed;
- unsigned int line;
- int ccs;
- int csc;
- int ped;
-
- /* Read port status */
- portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) );
- DBGC2 ( ehci, "EHCI %s-%d status is %08x\n",
- ehci->name, port->address, portsc );
- ccs = ( portsc & EHCI_PORTSC_CCS );
- csc = ( portsc & EHCI_PORTSC_CSC );
- ped = ( portsc & EHCI_PORTSC_PED );
- line = EHCI_PORTSC_LINE_STATUS ( portsc );
-
- /* Record disconnections and clear changes */
- port->disconnected |= csc;
- writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) );
-
- /* Determine port speed */
- if ( ! ccs ) {
- /* Port not connected */
- speed = USB_SPEED_NONE;
- } else if ( line == EHCI_PORTSC_LINE_STATUS_LOW ) {
- /* Detected as low-speed */
- speed = USB_SPEED_LOW;
- } else if ( ped ) {
- /* Port already enabled: must be high-speed */
- speed = USB_SPEED_HIGH;
- } else {
- /* Not low-speed and not yet enabled. Could be either
- * full-speed or high-speed; we can't yet tell.
- */
- speed = USB_SPEED_FULL;
- }
- port->speed = speed;
- return 0;
- }
-
- /**
- * Clear transaction translator buffer
- *
- * @v hub USB hub
- * @v port USB port
- * @v ep USB endpoint
- * @ret rc Return status code
- */
- static int ehci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port,
- struct usb_endpoint *ep ) {
- struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
-
- /* Should never be called; this is a root hub */
- DBGC ( ehci, "EHCI %s-%d nonsensical CLEAR_TT for %s %s\n", ehci->name,
- port->address, ep->usb->name, usb_endpoint_name ( ep ) );
-
- return -ENOTSUP;
- }
-
- /**
- * Poll for port status changes
- *
- * @v hub USB hub
- * @v port USB port
- */
- static void ehci_root_poll ( struct usb_hub *hub, struct usb_port *port ) {
- struct ehci_device *ehci = usb_hub_get_drvdata ( hub );
- uint32_t portsc;
- uint32_t change;
-
- /* Do nothing unless something has changed */
- portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) );
- change = ( portsc & EHCI_PORTSC_CHANGE );
- if ( ! change )
- return;
-
- /* Record disconnections and clear changes */
- port->disconnected |= ( portsc & EHCI_PORTSC_CSC );
- writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) );
-
- /* Report port status change */
- usb_port_changed ( port );
- }
-
- /******************************************************************************
- *
- * Bus operations
- *
- ******************************************************************************
- */
-
- /**
- * Open USB bus
- *
- * @v bus USB bus
- * @ret rc Return status code
- */
- static int ehci_bus_open ( struct usb_bus *bus ) {
- struct ehci_device *ehci = usb_bus_get_hostdata ( bus );
- unsigned int frames;
- size_t len;
- int rc;
-
- /* Sanity checks */
- assert ( list_empty ( &ehci->async ) );
- assert ( list_empty ( &ehci->periodic ) );
-
- /* Allocate and initialise asynchronous queue head */
- ehci->head = malloc_dma ( sizeof ( *ehci->head ),
- ehci_align ( sizeof ( *ehci->head ) ) );
- if ( ! ehci->head ) {
- rc = -ENOMEM;
- goto err_alloc_head;
- }
- memset ( ehci->head, 0, sizeof ( *ehci->head ) );
- ehci->head->chr = cpu_to_le32 ( EHCI_CHR_HEAD );
- ehci->head->cache.next = cpu_to_le32 ( EHCI_LINK_TERMINATE );
- ehci->head->cache.status = EHCI_STATUS_HALTED;
- ehci_async_schedule ( ehci );
- writel ( virt_to_phys ( ehci->head ),
- ehci->op + EHCI_OP_ASYNCLISTADDR );
-
- /* Use async queue head to determine control data structure segment */
- ehci->ctrldssegment =
- ( ( ( uint64_t ) virt_to_phys ( ehci->head ) ) >> 32 );
- if ( ehci->addr64 ) {
- writel ( ehci->ctrldssegment, ehci->op + EHCI_OP_CTRLDSSEGMENT);
- } else if ( ehci->ctrldssegment ) {
- DBGC ( ehci, "EHCI %s CTRLDSSEGMENT not supported\n",
- ehci->name );
- rc = -ENOTSUP;
- goto err_ctrldssegment;
- }
-
- /* Allocate periodic frame list */
- frames = EHCI_PERIODIC_FRAMES ( ehci->flsize );
- len = ( frames * sizeof ( ehci->frame[0] ) );
- ehci->frame = malloc_dma ( len, EHCI_PAGE_ALIGN );
- if ( ! ehci->frame ) {
- rc = -ENOMEM;
- goto err_alloc_frame;
- }
- if ( ( rc = ehci_ctrl_reachable ( ehci, ehci->frame ) ) != 0 ) {
- DBGC ( ehci, "EHCI %s frame list unreachable\n", ehci->name );
- goto err_unreachable_frame;
- }
- ehci_periodic_schedule ( ehci );
- writel ( virt_to_phys ( ehci->frame ),
- ehci->op + EHCI_OP_PERIODICLISTBASE );
-
- /* Start controller */
- ehci_run ( ehci );
-
- return 0;
-
- ehci_stop ( ehci );
- err_unreachable_frame:
- free_dma ( ehci->frame, len );
- err_alloc_frame:
- err_ctrldssegment:
- free_dma ( ehci->head, sizeof ( *ehci->head ) );
- err_alloc_head:
- return rc;
- }
-
- /**
- * Close USB bus
- *
- * @v bus USB bus
- */
- static void ehci_bus_close ( struct usb_bus *bus ) {
- struct ehci_device *ehci = usb_bus_get_hostdata ( bus );
- unsigned int frames = EHCI_PERIODIC_FRAMES ( ehci->flsize );
-
- /* Sanity checks */
- assert ( list_empty ( &ehci->async ) );
- assert ( list_empty ( &ehci->periodic ) );
-
- /* Stop controller */
- ehci_stop ( ehci );
-
- /* Free periodic frame list */
- free_dma ( ehci->frame, ( frames * sizeof ( ehci->frame[0] ) ) );
-
- /* Free asynchronous schedule */
- free_dma ( ehci->head, sizeof ( *ehci->head ) );
- }
-
- /**
- * Poll USB bus
- *
- * @v bus USB bus
- */
- static void ehci_bus_poll ( struct usb_bus *bus ) {
- struct ehci_device *ehci = usb_bus_get_hostdata ( bus );
- struct usb_hub *hub = bus->hub;
- struct ehci_endpoint *endpoint;
- unsigned int i;
- uint32_t usbsts;
- uint32_t change;
-
- /* Do nothing unless something has changed */
- usbsts = readl ( ehci->op + EHCI_OP_USBSTS );
- assert ( usbsts & EHCI_USBSTS_ASYNC );
- assert ( usbsts & EHCI_USBSTS_PERIODIC );
- assert ( ! ( usbsts & EHCI_USBSTS_HCH ) );
- change = ( usbsts & EHCI_USBSTS_CHANGE );
- if ( ! change )
- return;
-
- /* Acknowledge changes */
- writel ( usbsts, ehci->op + EHCI_OP_USBSTS );
-
- /* Process completions, if applicable */
- if ( change & ( EHCI_USBSTS_USBINT | EHCI_USBSTS_USBERRINT ) ) {
-
- /* Iterate over all endpoints looking for completed
- * descriptors. We trust that completion handlers are
- * minimal and will not do anything that could
- * plausibly affect the endpoint list itself.
- */
- list_for_each_entry ( endpoint, &ehci->endpoints, list )
- ehci_endpoint_poll ( endpoint );
- }
-
- /* Process port status changes, if applicable */
- if ( change & EHCI_USBSTS_PORT ) {
-
- /* Iterate over all ports looking for status changes */
- for ( i = 1 ; i <= ehci->ports ; i++ )
- ehci_root_poll ( hub, usb_port ( hub, i ) );
- }
-
- /* Report fatal errors */
- if ( change & EHCI_USBSTS_SYSERR )
- DBGC ( ehci, "EHCI %s host system error\n", ehci->name );
- }
-
- /******************************************************************************
- *
- * PCI interface
- *
- ******************************************************************************
- */
-
- /** USB host controller operations */
- static struct usb_host_operations ehci_operations = {
- .endpoint = {
- .open = ehci_endpoint_open,
- .close = ehci_endpoint_close,
- .reset = ehci_endpoint_reset,
- .mtu = ehci_endpoint_mtu,
- .message = ehci_endpoint_message,
- .stream = ehci_endpoint_stream,
- },
- .device = {
- .open = ehci_device_open,
- .close = ehci_device_close,
- .address = ehci_device_address,
- },
- .bus = {
- .open = ehci_bus_open,
- .close = ehci_bus_close,
- .poll = ehci_bus_poll,
- },
- .hub = {
- .open = ehci_hub_open,
- .close = ehci_hub_close,
- },
- .root = {
- .open = ehci_root_open,
- .close = ehci_root_close,
- .enable = ehci_root_enable,
- .disable = ehci_root_disable,
- .speed = ehci_root_speed,
- .clear_tt = ehci_root_clear_tt,
- },
- };
-
- /**
- * Probe PCI device
- *
- * @v pci PCI device
- * @ret rc Return status code
- */
- static int ehci_probe ( struct pci_device *pci ) {
- struct ehci_device *ehci;
- struct usb_port *port;
- unsigned long bar_start;
- size_t bar_size;
- unsigned int i;
- int rc;
-
- /* Allocate and initialise structure */
- ehci = zalloc ( sizeof ( *ehci ) );
- if ( ! ehci ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- ehci->name = pci->dev.name;
- INIT_LIST_HEAD ( &ehci->endpoints );
- INIT_LIST_HEAD ( &ehci->async );
- INIT_LIST_HEAD ( &ehci->periodic );
-
- /* Fix up PCI device */
- adjust_pci_device ( pci );
-
- /* Map registers */
- bar_start = pci_bar_start ( pci, EHCI_BAR );
- bar_size = pci_bar_size ( pci, EHCI_BAR );
- ehci->regs = ioremap ( bar_start, bar_size );
- if ( ! ehci->regs ) {
- rc = -ENODEV;
- goto err_ioremap;
- }
-
- /* Initialise EHCI device */
- ehci_init ( ehci, ehci->regs );
-
- /* Initialise USB legacy support and claim ownership */
- ehci_legacy_init ( ehci, pci );
- ehci_legacy_claim ( ehci, pci );
-
- /* Reset device */
- if ( ( rc = ehci_reset ( ehci ) ) != 0 )
- goto err_reset;
-
- /* Allocate USB bus */
- ehci->bus = alloc_usb_bus ( &pci->dev, ehci->ports, EHCI_MTU,
- &ehci_operations );
- if ( ! ehci->bus ) {
- rc = -ENOMEM;
- goto err_alloc_bus;
- }
- usb_bus_set_hostdata ( ehci->bus, ehci );
- usb_hub_set_drvdata ( ehci->bus->hub, ehci );
-
- /* Set port protocols */
- for ( i = 1 ; i <= ehci->ports ; i++ ) {
- port = usb_port ( ehci->bus->hub, i );
- port->protocol = USB_PROTO_2_0;
- }
-
- /* Register USB bus */
- if ( ( rc = register_usb_bus ( ehci->bus ) ) != 0 )
- goto err_register;
-
- pci_set_drvdata ( pci, ehci );
- return 0;
-
- unregister_usb_bus ( ehci->bus );
- err_register:
- free_usb_bus ( ehci->bus );
- err_alloc_bus:
- ehci_reset ( ehci );
- err_reset:
- ehci_legacy_release ( ehci, pci );
- iounmap ( ehci->regs );
- err_ioremap:
- free ( ehci );
- err_alloc:
- return rc;
- }
-
- /**
- * Remove PCI device
- *
- * @v pci PCI device
- */
- static void ehci_remove ( struct pci_device *pci ) {
- struct ehci_device *ehci = pci_get_drvdata ( pci );
- struct usb_bus *bus = ehci->bus;
-
- unregister_usb_bus ( bus );
- assert ( list_empty ( &ehci->async ) );
- assert ( list_empty ( &ehci->periodic ) );
- free_usb_bus ( bus );
- ehci_reset ( ehci );
- ehci_legacy_release ( ehci, pci );
- iounmap ( ehci->regs );
- free ( ehci );
- }
-
- /** EHCI PCI device IDs */
- static struct pci_device_id ehci_ids[] = {
- PCI_ROM ( 0xffff, 0xffff, "ehci", "EHCI", 0 ),
- };
-
- /** EHCI PCI driver */
- struct pci_driver ehci_driver __pci_driver = {
- .ids = ehci_ids,
- .id_count = ( sizeof ( ehci_ids ) / sizeof ( ehci_ids[0] ) ),
- .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB,
- PCI_CLASS_SERIAL_USB_EHCI ),
- .probe = ehci_probe,
- .remove = ehci_remove,
- };
-
- /**
- * Prepare for exit
- *
- * @v booting System is shutting down for OS boot
- */
- static void ehci_shutdown ( int booting ) {
- /* If we are shutting down to boot an OS, then prevent the
- * release of ownership back to BIOS.
- */
- ehci_legacy_prevent_release = booting;
- }
-
- /** Startup/shutdown function */
- struct startup_fn ehci_startup __startup_fn ( STARTUP_LATE ) = {
- .shutdown = ehci_shutdown,
- };
|