iPXE
Defines | Functions | Variables
ehci.c File Reference

USB Enhanced Host Controller Interface (EHCI) driver. More...

#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"

Go to the source code of this file.

Defines

#define EIO_STATUS(status)   EUNIQ ( EINFO_EIO, ( ( (status) >> 2 ) & 0xf ) )
 Construct error code from transfer descriptor status.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static void ehci_init (struct ehci_device *ehci, void *regs)
 Initialise device.
static unsigned int ehci_extended_capability (struct ehci_device *ehci, struct pci_device *pci, unsigned int id, unsigned int offset)
 Find extended capability.
static size_t ehci_align (size_t len)
 Calculate buffer alignment.
static int ehci_ctrl_reachable (struct ehci_device *ehci, void *ptr)
 Check control data structure reachability.
static __unused void ehci_dump (struct ehci_device *ehci)
 Dump host controller registers.
static void ehci_legacy_init (struct ehci_device *ehci, struct pci_device *pci)
 Initialise USB legacy support.
static void ehci_legacy_claim (struct ehci_device *ehci, struct pci_device *pci)
 Claim ownership from BIOS.
static void ehci_legacy_release (struct ehci_device *ehci, struct pci_device *pci)
 Release ownership back to BIOS.
static void ehci_poll_companions (struct ehci_device *ehci)
 Poll child companion controllers.
unsigned int ehci_companion (struct pci_device *pci)
 Locate EHCI companion controller.
static void ehci_run (struct ehci_device *ehci)
 Start EHCI device.
static int ehci_stop (struct ehci_device *ehci)
 Stop EHCI device.
static int ehci_reset (struct ehci_device *ehci)
 Reset EHCI device.
static int ehci_ring_alloc (struct ehci_device *ehci, struct ehci_ring *ring)
 Allocate transfer descriptor ring.
static void ehci_ring_free (struct ehci_ring *ring)
 Free transfer descriptor ring.
static int ehci_enqueue (struct ehci_device *ehci, struct ehci_ring *ring, struct io_buffer *iobuf, const struct ehci_transfer *xfer, unsigned int count)
 Enqueue transfer descriptors.
static struct io_bufferehci_dequeue (struct ehci_ring *ring)
 Dequeue a transfer descriptor.
static uint32_t ehci_link_qh (struct ehci_queue_head *queue)
 Get link value for a queue head.
static void ehci_async_schedule (struct ehci_device *ehci)
 (Re)build asynchronous schedule
static void ehci_async_add (struct ehci_endpoint *endpoint)
 Add endpoint to asynchronous schedule.
static int ehci_async_del (struct ehci_endpoint *endpoint)
 Remove endpoint from asynchronous schedule.
static void ehci_periodic_schedule (struct ehci_device *ehci)
 (Re)build periodic schedule
static void ehci_periodic_add (struct ehci_endpoint *endpoint)
 Add endpoint to periodic schedule.
static int ehci_periodic_del (struct ehci_endpoint *endpoint)
 Remove endpoint from periodic schedule.
static void ehci_schedule_add (struct ehci_endpoint *endpoint)
 Add endpoint to appropriate schedule.
static int ehci_schedule_del (struct ehci_endpoint *endpoint)
 Remove endpoint from appropriate schedule.
static uint32_t ehci_endpoint_characteristics (struct usb_endpoint *ep)
 Determine endpoint characteristics.
static uint32_t ehci_endpoint_capabilities (struct usb_endpoint *ep)
 Determine endpoint capabilities.
static void ehci_endpoint_update (struct usb_endpoint *ep)
 Update endpoint characteristics and capabilities.
static int ehci_endpoint_open (struct usb_endpoint *ep)
 Open endpoint.
static void ehci_endpoint_close (struct usb_endpoint *ep)
 Close endpoint.
static int ehci_endpoint_reset (struct usb_endpoint *ep)
 Reset endpoint.
static int ehci_endpoint_mtu (struct usb_endpoint *ep)
 Update MTU.
static int ehci_endpoint_message (struct usb_endpoint *ep, struct io_buffer *iobuf)
 Enqueue message transfer.
static unsigned int ehci_endpoint_count (size_t len, int zlp)
 Calculate number of transfer descriptors.
static int ehci_endpoint_stream (struct usb_endpoint *ep, struct io_buffer *iobuf, int zlp)
 Enqueue stream transfer.
static void ehci_endpoint_poll (struct ehci_endpoint *endpoint)
 Poll for completions.
static int ehci_device_open (struct usb_device *usb)
 Open device.
static void ehci_device_close (struct usb_device *usb)
 Close device.
static int ehci_device_address (struct usb_device *usb)
 Assign device address.
static int ehci_hub_open (struct usb_hub *hub __unused)
 Open hub.
static void ehci_hub_close (struct usb_hub *hub __unused)
 Close hub.
static int ehci_root_open (struct usb_hub *hub)
 Open root hub.
static void ehci_root_close (struct usb_hub *hub)
 Close root hub.
static int ehci_root_enable (struct usb_hub *hub, struct usb_port *port)
 Enable port.
static int ehci_root_disable (struct usb_hub *hub, struct usb_port *port)
 Disable port.
static int ehci_root_speed (struct usb_hub *hub, struct usb_port *port)
 Update root hub port speed.
static int ehci_root_clear_tt (struct usb_hub *hub, struct usb_port *port, struct usb_endpoint *ep)
 Clear transaction translator buffer.
static void ehci_root_poll (struct usb_hub *hub, struct usb_port *port)
 Poll for port status changes.
static int ehci_bus_open (struct usb_bus *bus)
 Open USB bus.
static void ehci_bus_close (struct usb_bus *bus)
 Close USB bus.
static void ehci_bus_poll (struct usb_bus *bus)
 Poll USB bus.
static int ehci_probe (struct pci_device *pci)
 Probe PCI device.
static void ehci_remove (struct pci_device *pci)
 Remove PCI device.
static void ehci_shutdown (int booting)
 Prepare for exit.
struct startup_fn ehci_startup __startup_fn (STARTUP_LATE)
 Startup/shutdown function.

Variables

static int ehci_legacy_prevent_release
 Prevent the release of ownership back to BIOS.
static struct usb_host_operations ehci_operations
 USB host controller operations.
static struct pci_device_id ehci_ids []
 EHCI PCI device IDs.
struct pci_driver ehci_driver __pci_driver
 EHCI PCI driver.

Detailed Description

USB Enhanced Host Controller Interface (EHCI) driver.

Definition in file ehci.c.


Define Documentation

#define EIO_STATUS (   status)    EUNIQ ( EINFO_EIO, ( ( (status) >> 2 ) & 0xf ) )

Construct error code from transfer descriptor status.

Parameters:
statusTransfer descriptor status
Return values:
rcError 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().

Definition at line 54 of file ehci.c.

Referenced by ehci_endpoint_poll().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static void ehci_init ( struct ehci_device ehci,
void *  regs 
) [static]

Initialise device.

Parameters:
ehciEHCI device
regsMMIO registers

Definition at line 69 of file ehci.c.

References ehci_device::addr64, ehci_device::cap, DBGC, DBGC2, ehci_device::eecp, EHCI_CAP_CAPLENGTH, EHCI_CAP_HCCPARAMS, EHCI_CAP_HCSPARAMS, EHCI_FLSIZE_DEFAULT, EHCI_FLSIZE_SMALL, EHCI_HCCPARAMS_ADDR64, EHCI_HCCPARAMS_EECP, EHCI_HCCPARAMS_FLSIZE, EHCI_HCSPARAMS_PORTS, ehci_device::flsize, ehci_device::name, ehci_device::op, ehci_device::ports, readb(), readl(), regs, and virt_to_phys().

Referenced by ehci_probe().

                                                               {
        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 );
}
static unsigned int ehci_extended_capability ( struct ehci_device ehci,
struct pci_device pci,
unsigned int  id,
unsigned int  offset 
) [static]

Find extended capability.

Parameters:
ehciEHCI device
pciPCI device
idCapability ID
offsetOffset to previous extended capability instance, or zero
Return values:
offsetOffset to extended capability, or zero if not found

Definition at line 105 of file ehci.c.

References ehci_device::eecp, EHCI_EECP_ID, EHCI_EECP_NEXT, offset, and pci_read_config_dword().

Referenced by ehci_legacy_init().

                                                                     {
        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;
        }
}
static size_t ehci_align ( size_t  len) [inline, static]

Calculate buffer alignment.

Parameters:
lenLength
Return values:
alignBuffer 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.

Definition at line 140 of file ehci.c.

References EHCI_MIN_ALIGN, and fls.

Referenced by ehci_bus_open(), and ehci_ring_alloc().

                                               {
        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;
}
static int ehci_ctrl_reachable ( struct ehci_device ehci,
void *  ptr 
) [static]

Check control data structure reachability.

Parameters:
ehciEHCI device
ptrData structure pointer
Return values:
rcReturn status code

Definition at line 160 of file ehci.c.

References ehci_device::ctrldssegment, ENOTSUP, phys, segment, and virt_to_phys().

Referenced by ehci_bus_open(), and ehci_ring_alloc().

                                                                       {
        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;
}
static __unused void ehci_dump ( struct ehci_device ehci) [static]

Dump host controller registers.

Parameters:
ehciEHCI device

Definition at line 188 of file ehci.c.

References ehci_device::cap, DBG_LOG, DBGC, EHCI_CAP_CAPLENGTH, EHCI_CAP_HCCPARAMS, EHCI_CAP_HCIVERSION, EHCI_CAP_HCSPARAMS, EHCI_OP_ASYNCLISTADDR, EHCI_OP_CONFIGFLAG, EHCI_OP_CTRLDSSEGMENT, EHCI_OP_FRINDEX, EHCI_OP_PERIODICLISTBASE, EHCI_OP_USBCMD, EHCI_OP_USBINTR, EHCI_OP_USBSTS, ehci_device::name, ehci_device::op, readb(), readl(), and readw().

                                                            {
        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 );
}
static void ehci_legacy_init ( struct ehci_device ehci,
struct pci_device pci 
) [static]

Initialise USB legacy support.

Parameters:
ehciEHCI device
pciPCI device

Definition at line 247 of file ehci.c.

References DBGC, EHCI_EECP_ID_LEGACY, ehci_extended_capability(), EHCI_USBLEGSUP_BIOS, EHCI_USBLEGSUP_BIOS_OWNED, ehci_device::legacy, ehci_device::name, and pci_read_config_byte().

Referenced by ehci_probe().

                                                        {
        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;
}
static void ehci_legacy_claim ( struct ehci_device ehci,
struct pci_device pci 
) [static]

Claim ownership from BIOS.

Parameters:
ehciEHCI device
pciPCI device

Definition at line 280 of file ehci.c.

References DBGC, EHCI_USBLEGSUP_BIOS, EHCI_USBLEGSUP_BIOS_OWNED, EHCI_USBLEGSUP_CTLSTS, EHCI_USBLEGSUP_MAX_WAIT_MS, EHCI_USBLEGSUP_OS, EHCI_USBLEGSUP_OS_OWNED, ehci_device::legacy, mdelay(), ehci_device::name, pci_read_config_byte(), pci_read_config_dword(), pci_write_config_byte(), and pci_write_config_dword().

Referenced by ehci_probe().

                                                         {
        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 );
}
static void ehci_legacy_release ( struct ehci_device ehci,
struct pci_device pci 
) [static]

Release ownership back to BIOS.

Parameters:
ehciEHCI device
pciPCI device

Definition at line 340 of file ehci.c.

References DBGC, ehci_legacy_prevent_release, EHCI_USBLEGSUP_CTLSTS, EHCI_USBLEGSUP_OS, ehci_device::legacy, ehci_device::name, pci_read_config_dword(), and pci_write_config_byte().

Referenced by ehci_probe(), and ehci_remove().

                                                           {
        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 );
}
static void ehci_poll_companions ( struct ehci_device ehci) [static]

Poll child companion controllers.

Parameters:
ehciEHCI device

Definition at line 379 of file ehci.c.

References bus, ehci_device::bus, device_description::bus_type, BUS_TYPE_PCI, device_description::class, DBGC2, device::desc, usb_bus::dev, for_each_usb_bus, device_description::location, ehci_device::name, usb_bus::name, PCI_CLASS, PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, PCI_CLASS_SERIAL_USB_OHCI, PCI_CLASS_SERIAL_USB_UHCI, PCI_FIRST_FUNC, and usb_poll().

Referenced by ehci_root_enable().

                                                              {
        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 );
        }
}
unsigned int ehci_companion ( struct pci_device pci)

Locate EHCI companion controller.

Parameters:
pciPCI device
Return values:
busdevfnEHCI companion controller bus:dev.fn (if any)

Definition at line 420 of file ehci.c.

References pci_device::busdevfn, pci_device::class, PCI_CLASS, PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, PCI_CLASS_SERIAL_USB_EHCI, pci_init(), PCI_LAST_FUNC, pci_read_config(), and rc.

Referenced by uhci_probe().

                                                       {
        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;
}
static void ehci_run ( struct ehci_device ehci) [static]

Start EHCI device.

Parameters:
ehciEHCI device

Definition at line 452 of file ehci.c.

References EHCI_OP_USBCMD, EHCI_USBCMD_ASYNC, EHCI_USBCMD_FLSIZE, EHCI_USBCMD_FLSIZE_MASK, EHCI_USBCMD_PERIODIC, EHCI_USBCMD_RUN, ehci_device::flsize, ehci_device::op, readl(), and writel().

Referenced by ehci_bus_open().

                                                  {
        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 );
}
static int ehci_stop ( struct ehci_device ehci) [static]

Stop EHCI device.

Parameters:
ehciEHCI device
Return values:
rcReturn status code

Definition at line 469 of file ehci.c.

References DBGC, EHCI_OP_USBCMD, EHCI_OP_USBSTS, EHCI_STOP_MAX_WAIT_MS, EHCI_USBCMD_ASYNC, EHCI_USBCMD_PERIODIC, EHCI_USBCMD_RUN, EHCI_USBSTS_HCH, ETIMEDOUT, mdelay(), ehci_device::name, ehci_device::op, readl(), and writel().

Referenced by ehci_bus_close(), ehci_bus_open(), and ehci_reset().

                                                  {
        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;
}
static int ehci_reset ( struct ehci_device ehci) [static]

Reset EHCI device.

Parameters:
ehciEHCI device
Return values:
rcReturn status code

Definition at line 502 of file ehci.c.

References DBGC, EHCI_OP_USBCMD, EHCI_RESET_MAX_WAIT_MS, ehci_stop(), EHCI_USBCMD_HCRST, ETIMEDOUT, mdelay(), ehci_device::name, ehci_device::op, rc, readl(), and writel().

Referenced by ehci_probe(), and ehci_remove().

                                                   {
        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;
}
static int ehci_ring_alloc ( struct ehci_device ehci,
struct ehci_ring ring 
) [static]

Allocate transfer descriptor ring.

Parameters:
ehciEHCI device
ringTransfer descriptor ring
Return values:
rcReturn status code

Definition at line 548 of file ehci.c.

References ehci_transfer_descriptor::alt, ehci_queue_head::cache, cpu_to_le32, DBGC, ehci_ring::desc, ehci_align(), ehci_ctrl_reachable(), EHCI_RING_COUNT, ENOMEM, free, free_dma(), ehci_ring::head, ehci_ring::iobuf, len, link, malloc_dma(), memset(), ehci_device::name, next, ehci_transfer_descriptor::next, rc, virt_to_phys(), and zalloc().

Referenced by ehci_endpoint_open().

                                                      {
        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;
}
static void ehci_ring_free ( struct ehci_ring ring) [static]

Free transfer descriptor ring.

Parameters:
ringTransfer descriptor ring

Definition at line 625 of file ehci.c.

References assert, ehci_ring::desc, EHCI_RING_COUNT, ehci_ring_fill(), free, free_dma(), ehci_ring::head, ehci_ring::iobuf, and NULL.

Referenced by ehci_endpoint_close(), and ehci_endpoint_open().

                                                      {
        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 );
}
static int ehci_enqueue ( struct ehci_device ehci,
struct ehci_ring ring,
struct io_buffer iobuf,
const struct ehci_transfer xfer,
unsigned int  count 
) [static]

Enqueue transfer descriptors.

Parameters:
ehciEHCI device
ringTransfer descriptor ring
iobufI/O buffer
xfersTransfers
countNumber of transfers
Return values:
rcReturn status code

Definition at line 653 of file ehci.c.

References ehci_device::addr64, assert, count, cpu_to_le16, cpu_to_le32, data, ehci_transfer::data, ehci_ring::desc, EHCI_FL_CERR_MAX, EHCI_FL_TOGGLE, EHCI_LEN_MASK, EHCI_LEN_TOGGLE, EHCI_PAGE_ALIGN, EHCI_RING_COUNT, ehci_ring_remaining(), EHCI_STATUS_ACTIVE, ENOBUFS, ENOTSUP, ehci_transfer_descriptor::flags, ehci_transfer::flags, ehci_transfer_descriptor::high, index, ehci_ring::iobuf, ehci_transfer_descriptor::len, len, ehci_transfer::len, ehci_transfer_descriptor::low, NULL, offset, phys, ehci_ring::prod, ehci_transfer_descriptor::status, virt_to_phys(), and wmb.

Referenced by ehci_endpoint_message(), and ehci_endpoint_stream().

                                               {
        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;
}
static struct io_buffer* ehci_dequeue ( struct ehci_ring ring) [static, read]

Dequeue a transfer descriptor.

Parameters:
ringTransfer descriptor ring
Return values:
iobufI/O buffer (or NULL)

Definition at line 743 of file ehci.c.

References assert, ehci_ring::cons, ehci_ring::desc, EHCI_RING_COUNT, ehci_ring_fill(), index, ehci_ring::iobuf, NULL, and ehci_transfer_descriptor::status.

Referenced by ehci_endpoint_close(), and ehci_endpoint_poll().

                                                                  {
        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;
}
static uint32_t ehci_link_qh ( struct ehci_queue_head queue) [inline, static]

Get link value for a queue head.

Parameters:
queueQueue head
Return values:
linkLink value

Definition at line 778 of file ehci.c.

References EHCI_LINK_TYPE_QH, and virt_to_phys().

Referenced by ehci_async_schedule(), and ehci_periodic_schedule().

                                                                      {

        return ( virt_to_phys ( queue ) | EHCI_LINK_TYPE_QH );
}
static void ehci_async_schedule ( struct ehci_device ehci) [static]

(Re)build asynchronous schedule

Parameters:
ehciEHCI device

Definition at line 788 of file ehci.c.

References ehci_device::async, cpu_to_le32, ehci_link_qh(), ehci_ring::head, ehci_device::head, link, ehci_queue_head::link, list_for_each_entry_reverse, queue, ehci_endpoint::ring, and wmb.

Referenced by ehci_async_add(), ehci_async_del(), and ehci_bus_open().

                                                             {
        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();
}
static void ehci_async_add ( struct ehci_endpoint endpoint) [static]

Add endpoint to asynchronous schedule.

Parameters:
endpointEndpoint

Definition at line 814 of file ehci.c.

References ehci_device::async, ehci_endpoint::ehci, ehci_async_schedule(), list_add_tail, and ehci_endpoint::schedule.

Referenced by ehci_schedule_add().

                                                              {
        struct ehci_device *ehci = endpoint->ehci;

        /* Add to end of schedule */
        list_add_tail ( &endpoint->schedule, &ehci->async );

        /* Rebuild schedule */
        ehci_async_schedule ( ehci );
}
static int ehci_async_del ( struct ehci_endpoint endpoint) [static]

Remove endpoint from asynchronous schedule.

Parameters:
endpointEndpoint
Return values:
rcReturn status code

Definition at line 830 of file ehci.c.

References ehci_device::async, DBGC, ehci_endpoint::ehci, EHCI_ASYNC_ADVANCE_MAX_WAIT_MS, ehci_async_schedule(), EHCI_OP_USBCMD, EHCI_OP_USBSTS, EHCI_USBCMD_ASYNC_ADVANCE, EHCI_USBSTS_ASYNC_ADVANCE, EHCI_USBSTS_CHANGE, ETIMEDOUT, list_check_contains_entry, list_del, mdelay(), ehci_device::name, ehci_device::op, readl(), ehci_endpoint::schedule, and writel().

Referenced by ehci_schedule_del().

                                                             {
        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;
}
static void ehci_periodic_schedule ( struct ehci_device ehci) [static]

(Re)build periodic schedule

Parameters:
ehciEHCI device

Definition at line 875 of file ehci.c.

References cpu_to_le32, DBGCP, ehci_link_qh(), EHCI_LINK_TERMINATE, EHCI_PERIODIC_FRAMES, ehci_endpoint::ep, ffs, ehci_device::flsize, ehci_device::frame, ehci_ring::head, usb_endpoint::interval, link, ehci_periodic_frame::link, ehci_queue_head::link, list_for_each_entry, list_for_each_entry_reverse, ehci_device::name, ehci_device::periodic, queue, ehci_endpoint::ring, and wmb.

Referenced by ehci_bus_open(), ehci_periodic_add(), and ehci_periodic_del().

                                                                {
        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" );
}
static void ehci_periodic_add ( struct ehci_endpoint endpoint) [static]

Add endpoint to periodic schedule.

Parameters:
endpointEndpoint

Definition at line 945 of file ehci.c.

References ehci_endpoint::ehci, ehci_periodic_schedule(), ehci_endpoint::ep, usb_endpoint::interval, list_add_tail, list_for_each_entry, ehci_device::periodic, and ehci_endpoint::schedule.

Referenced by ehci_schedule_add().

                                                                 {
        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 );
}
static int ehci_periodic_del ( struct ehci_endpoint endpoint) [static]

Remove endpoint from periodic schedule.

Parameters:
endpointEndpoint
Return values:
rcReturn status code

Definition at line 967 of file ehci.c.

References ehci_endpoint::ehci, ehci_periodic_schedule(), list_check_contains_entry, list_del, mdelay(), ehci_device::periodic, and ehci_endpoint::schedule.

Referenced by ehci_schedule_del().

                                                                {
        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;
}
static void ehci_schedule_add ( struct ehci_endpoint endpoint) [static]

Add endpoint to appropriate schedule.

Parameters:
endpointEndpoint

Definition at line 988 of file ehci.c.

References attr, usb_endpoint::attributes, ehci_async_add(), ehci_periodic_add(), ehci_endpoint::ep, USB_ENDPOINT_ATTR_INTERRUPT, and USB_ENDPOINT_ATTR_TYPE_MASK.

Referenced by ehci_endpoint_open().

                                                                 {
        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 );
        }
}
static int ehci_schedule_del ( struct ehci_endpoint endpoint) [static]

Remove endpoint from appropriate schedule.

Parameters:
endpointEndpoint
Return values:
rcReturn status code

Definition at line 1005 of file ehci.c.

References attr, usb_endpoint::attributes, ehci_async_del(), ehci_periodic_del(), ehci_endpoint::ep, USB_ENDPOINT_ATTR_INTERRUPT, and USB_ENDPOINT_ATTR_TYPE_MASK.

Referenced by ehci_endpoint_close().

                                                                {
        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 );
        }
}
static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint ep) [static]

Determine endpoint characteristics.

Parameters:
epUSB endpoint
Return values:
chrEndpoint characteristics

Definition at line 1029 of file ehci.c.

References usb_endpoint::address, usb_device::address, attr, usb_endpoint::attributes, EHCI_CHR_ADDRESS, EHCI_CHR_CONTROL, EHCI_CHR_ENDPOINT, EHCI_CHR_EPS_FULL, EHCI_CHR_EPS_HIGH, EHCI_CHR_EPS_LOW, EHCI_CHR_MAX_LEN, EHCI_CHR_TOGGLE, usb_endpoint::mtu, usb_device::speed, usb_endpoint::usb, USB_ENDPOINT_ATTR_CONTROL, USB_ENDPOINT_ATTR_TYPE_MASK, USB_SPEED_FULL, and USB_SPEED_HIGH.

Referenced by ehci_endpoint_update().

                                                                          {
        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;
}
static uint32_t ehci_endpoint_capabilities ( struct usb_endpoint ep) [static]

Determine endpoint capabilities.

Parameters:
epUSB endpoint
Return values:
capEndpoint capabilities

Definition at line 1065 of file ehci.c.

References usb_device::address, usb_port::address, assert, attr, usb_endpoint::attributes, usb_endpoint::burst, EHCI_CAP_INTR_SCHED, EHCI_CAP_MULT, EHCI_CAP_SPLIT_SCHED_DEFAULT, EHCI_CAP_TT_HUB, EHCI_CAP_TT_PORT, usb_port::hub, usb_endpoint::interval, usb_endpoint::usb, usb_hub::usb, USB_ENDPOINT_ATTR_INTERRUPT, USB_ENDPOINT_ATTR_TYPE_MASK, and usb_transaction_translator().

Referenced by ehci_endpoint_update().

                                                                       {
        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;
}
static void ehci_endpoint_update ( struct usb_endpoint ep) [static]

Update endpoint characteristics and capabilities.

Parameters:
epUSB endpoint

Definition at line 1101 of file ehci.c.

References ehci_queue_head::cap, ehci_queue_head::chr, cpu_to_le32, ehci_endpoint_capabilities(), ehci_endpoint_characteristics(), ehci_ring::head, head, ehci_endpoint::ring, and usb_endpoint_get_hostdata().

Referenced by ehci_device_address(), ehci_endpoint_mtu(), and ehci_endpoint_open().

                                                             {
        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 ) );
}
static int ehci_endpoint_open ( struct usb_endpoint ep) [static]

Open endpoint.

Parameters:
epUSB endpoint
Return values:
rcReturn status code

Definition at line 1117 of file ehci.c.

References ehci_endpoint::ehci, ehci_endpoint_update(), ehci_ring_alloc(), ehci_ring_free(), ehci_schedule_add(), ehci_device::endpoints, ENOMEM, ehci_endpoint::ep, free, ehci_endpoint::list, list_add_tail, rc, ehci_endpoint::ring, usb_endpoint::usb, usb_endpoint_set_hostdata(), usb_get_hostdata(), and zalloc().

                                                          {
        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;
}
static void ehci_endpoint_close ( struct usb_endpoint ep) [static]

Close endpoint.

Parameters:
epUSB endpoint

Definition at line 1160 of file ehci.c.

References DBGC, ECANCELED, ehci_endpoint::ehci, ehci_dequeue(), ehci_ring_fill(), ehci_ring_free(), ehci_schedule_del(), free, ehci_endpoint::list, list_del, usb_device::name, rc, ehci_endpoint::ring, strerror(), usb_endpoint::usb, usb_complete_err(), usb_endpoint_get_hostdata(), and usb_endpoint_name().

                                                            {
        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 );
}
static int ehci_endpoint_reset ( struct usb_endpoint ep) [static]

Reset endpoint.

Parameters:
epUSB endpoint
Return values:
rcReturn status code

Definition at line 1200 of file ehci.c.

References assert, ehci_queue_head::cache, ehci_ring::cons, cpu_to_le32, ehci_ring::desc, EHCI_RING_COUNT, EHCI_STATUS_ACTIVE, EHCI_STATUS_HALTED, ehci_ring::head, ehci_transfer_descriptor::len, link, ehci_transfer_descriptor::next, ehci_ring::residual, ehci_endpoint::ring, ehci_transfer_descriptor::status, usb_endpoint_get_hostdata(), virt_to_phys(), and wmb.

                                                           {
        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;
}
static int ehci_endpoint_mtu ( struct usb_endpoint ep) [static]

Update MTU.

Parameters:
epUSB endpoint
Return values:
rcReturn status code

Definition at line 1233 of file ehci.c.

References ehci_endpoint_update().

                                                         {

        /* Update endpoint characteristics and capabilities */
        ehci_endpoint_update ( ep );

        return 0;
}
static int ehci_endpoint_message ( struct usb_endpoint ep,
struct io_buffer iobuf 
) [static]

Enqueue message transfer.

Parameters:
epUSB endpoint
iobufI/O buffer
Return values:
rcReturn status code

Definition at line 1248 of file ehci.c.

References assert, cpu_to_le16, io_buffer::data, ehci_transfer::data, ehci_endpoint::ehci, ehci_enqueue(), EHCI_FL_IOC, EHCI_FL_PID_IN, EHCI_FL_PID_OUT, EHCI_FL_PID_SETUP, EHCI_FL_TOGGLE, ehci_transfer::flags, iob_len(), iob_pull, len, ehci_transfer::len, NULL, rc, usb_setup_packet::request, ehci_endpoint::ring, USB_DIR_IN, and usb_endpoint_get_hostdata().

                                                             {
        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;
}
static unsigned int ehci_endpoint_count ( size_t  len,
int  zlp 
) [static]

Calculate number of transfer descriptors.

Parameters:
lenLength of data
zlpAppend a zero-length packet
Return values:
countNumber of transfer descriptors

Definition at line 1301 of file ehci.c.

References count, and EHCI_MTU.

Referenced by ehci_endpoint_stream().

                                                                {
        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;
}
static int ehci_endpoint_stream ( struct usb_endpoint ep,
struct io_buffer iobuf,
int  zlp 
) [static]

Enqueue stream transfer.

Parameters:
epUSB endpoint
iobufI/O buffer
zlpAppend a zero-length packet
Return values:
rcReturn status code

Definition at line 1326 of file ehci.c.

References usb_endpoint::address, count, io_buffer::data, data, ehci_transfer::data, ehci_endpoint::ehci, ehci_endpoint_count(), ehci_enqueue(), EHCI_FL_IOC, EHCI_FL_PID_IN, EHCI_FL_PID_OUT, EHCI_MTU, flags, ehci_transfer::flags, iob_len(), len, ehci_transfer::len, rc, ehci_endpoint::ring, USB_DIR_IN, and usb_endpoint_get_hostdata().

                                                                     {
        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;
}
static void ehci_endpoint_poll ( struct ehci_endpoint endpoint) [static]

Poll for completions.

Parameters:
endpointEndpoint

Definition at line 1374 of file ehci.c.

References ehci_ring::cons, DBGC, ehci_ring::desc, ehci_endpoint::ehci, ehci_dequeue(), EHCI_LEN_MASK, EHCI_RING_COUNT, ehci_ring_fill(), EHCI_STATUS_ACTIVE, EHCI_STATUS_HALTED, EIO_STATUS, ehci_endpoint::ep, index, iob_unput, le16_to_cpu, ehci_transfer_descriptor::len, usb_device::name, rc, ehci_ring::residual, ehci_endpoint::ring, rmb, status, ehci_transfer_descriptor::status, strerror(), usb_endpoint::usb, usb_complete(), usb_complete_err(), and usb_endpoint_name().

Referenced by ehci_bus_poll().

                                                                  {
        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 );
        }
}
static int ehci_device_open ( struct usb_device usb) [static]

Open device.

Parameters:
usbUSB device
Return values:
rcReturn status code

Definition at line 1447 of file ehci.c.

References usb_hub::bus, usb_port::hub, usb_device::port, usb_bus_get_hostdata(), and usb_set_hostdata().

                                                       {
        struct ehci_device *ehci = usb_bus_get_hostdata ( usb->port->hub->bus );

        usb_set_hostdata ( usb, ehci );
        return 0;
}
static void ehci_device_close ( struct usb_device usb) [static]

Close device.

Parameters:
usbUSB device

Definition at line 1459 of file ehci.c.

References usb_device::address, bus, ehci_device::bus, usb_free_address(), and usb_get_hostdata().

                                                         {
        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 );
}
static int ehci_device_address ( struct usb_device usb) [static]

Assign device address.

Parameters:
usbUSB device
Return values:
rcReturn status code

Definition at line 1474 of file ehci.c.

References address, usb_device::address, assert, bus, ehci_device::bus, DBGC, ehci_endpoint_update(), usb_device::name, NULL, rc, strerror(), usb_alloc_address(), usb_endpoint(), USB_EP0_ADDRESS, usb_free_address(), usb_get_hostdata(), and usb_set_address().

                                                          {
        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;
}
static int ehci_hub_open ( struct usb_hub *hub  __unused) [static]

Open hub.

Parameters:
hubUSB hub
Return values:
rcReturn status code

Definition at line 1525 of file ehci.c.

                                                          {

        /* Nothing to do */
        return 0;
}
static void ehci_hub_close ( struct usb_hub *hub  __unused) [static]

Close hub.

Parameters:
hubUSB hub

Definition at line 1536 of file ehci.c.

                                                            {

        /* Nothing to do */
}
static int ehci_root_open ( struct usb_hub hub) [static]

Open root hub.

Parameters:
hubUSB hub
Return values:
rcReturn status code

Definition at line 1554 of file ehci.c.

References bus, usb_hub::bus, EHCI_CONFIGFLAG_CF, EHCI_OP_CONFIGFLAG, EHCI_OP_PORTSC, EHCI_PORT_POWER_DELAY_MS, EHCI_PORTSC_CHANGE, EHCI_PORTSC_PP, mdelay(), ehci_device::op, ehci_device::ports, readl(), usb_bus_get_hostdata(), usb_hub_set_drvdata(), and writel().

                                                  {
        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;
}
static void ehci_root_close ( struct usb_hub hub) [static]

Close root hub.

Parameters:
hubUSB hub

Definition at line 1585 of file ehci.c.

References EHCI_OP_CONFIGFLAG, NULL, ehci_device::op, usb_hub_get_drvdata(), usb_hub_set_drvdata(), and writel().

                                                    {
        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 );
}
static int ehci_root_enable ( struct usb_hub hub,
struct usb_port port 
) [static]

Enable port.

Parameters:
hubUSB hub
portUSB port
Return values:
rcReturn status code

Definition at line 1602 of file ehci.c.

References usb_port::address, DBGC, EHCI_DISOWN_DELAY_MS, EHCI_OP_PORTSC, ehci_poll_companions(), EHCI_PORT_RESET_MAX_WAIT_MS, EHCI_PORTSC_CHANGE, EHCI_PORTSC_LINE_STATUS, EHCI_PORTSC_LINE_STATUS_LOW, EHCI_PORTSC_OWNER, EHCI_PORTSC_PED, EHCI_PORTSC_PR, ENODEV, ETIMEDOUT, mdelay(), ehci_device::name, ehci_device::op, readl(), usb_hub_get_drvdata(), USB_RESET_DELAY_MS, and writel().

                                                                           {
        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;
}
static int ehci_root_disable ( struct usb_hub hub,
struct usb_port port 
) [static]

Disable port.

Parameters:
hubUSB hub
portUSB port
Return values:
rcReturn status code

Definition at line 1668 of file ehci.c.

References usb_port::address, EHCI_OP_PORTSC, EHCI_PORTSC_CHANGE, EHCI_PORTSC_PED, ehci_device::op, readl(), usb_hub_get_drvdata(), and writel().

                                                                            {
        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;
}
static int ehci_root_speed ( struct usb_hub hub,
struct usb_port port 
) [static]

Update root hub port speed.

Parameters:
hubUSB hub
portUSB port
Return values:
rcReturn status code

Definition at line 1687 of file ehci.c.

References usb_port::address, DBGC2, usb_port::disconnected, EHCI_OP_PORTSC, EHCI_PORTSC_CCS, EHCI_PORTSC_CSC, EHCI_PORTSC_LINE_STATUS, EHCI_PORTSC_LINE_STATUS_LOW, EHCI_PORTSC_PED, ehci_device::name, ehci_device::op, readl(), speed, usb_port::speed, usb_hub_get_drvdata(), USB_SPEED_FULL, USB_SPEED_HIGH, USB_SPEED_LOW, USB_SPEED_NONE, and writel().

                                                                          {
        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;
}
static int ehci_root_clear_tt ( struct usb_hub hub,
struct usb_port port,
struct usb_endpoint ep 
) [static]

Clear transaction translator buffer.

Parameters:
hubUSB hub
portUSB port
epUSB endpoint
Return values:
rcReturn status code

Definition at line 1737 of file ehci.c.

References usb_port::address, DBGC, ENOTSUP, ehci_device::name, usb_device::name, usb_endpoint::usb, usb_endpoint_name(), and usb_hub_get_drvdata().

                                                          {
        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;
}
static void ehci_root_poll ( struct usb_hub hub,
struct usb_port port 
) [static]

Poll for port status changes.

Parameters:
hubUSB hub
portUSB port

Definition at line 1754 of file ehci.c.

References usb_port::address, usb_port::disconnected, EHCI_OP_PORTSC, EHCI_PORTSC_CHANGE, EHCI_PORTSC_CSC, ehci_device::op, readl(), usb_hub_get_drvdata(), usb_port_changed(), and writel().

Referenced by ehci_bus_poll().

                                                                          {
        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 );
}
static int ehci_bus_open ( struct usb_bus bus) [static]

Open USB bus.

Parameters:
busUSB bus
Return values:
rcReturn status code

Definition at line 1786 of file ehci.c.

References ehci_device::addr64, assert, ehci_device::async, ehci_queue_head::cache, ehci_queue_head::chr, cpu_to_le32, ehci_device::ctrldssegment, DBGC, ehci_align(), ehci_async_schedule(), EHCI_CHR_HEAD, ehci_ctrl_reachable(), EHCI_LINK_TERMINATE, EHCI_OP_ASYNCLISTADDR, EHCI_OP_CTRLDSSEGMENT, EHCI_OP_PERIODICLISTBASE, EHCI_PAGE_ALIGN, EHCI_PERIODIC_FRAMES, ehci_periodic_schedule(), ehci_run(), EHCI_STATUS_HALTED, ehci_stop(), ENOMEM, ENOTSUP, ehci_device::flsize, ehci_device::frame, free_dma(), ehci_device::head, len, list_empty, malloc_dma(), memset(), ehci_device::name, ehci_transfer_descriptor::next, ehci_device::op, ehci_device::periodic, rc, ehci_transfer_descriptor::status, usb_bus_get_hostdata(), virt_to_phys(), and writel().

                                                 {
        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;
}
static void ehci_bus_close ( struct usb_bus bus) [static]

Close USB bus.

Parameters:
busUSB bus

Definition at line 1859 of file ehci.c.

References assert, ehci_device::async, EHCI_PERIODIC_FRAMES, ehci_stop(), ehci_device::flsize, ehci_device::frame, free_dma(), ehci_device::head, list_empty, ehci_device::periodic, and usb_bus_get_hostdata().

                                                   {
        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 ) );
}
static void ehci_bus_poll ( struct usb_bus bus) [static]

Poll USB bus.

Parameters:
busUSB bus

Definition at line 1882 of file ehci.c.

References assert, DBGC, ehci_endpoint_poll(), EHCI_OP_USBSTS, ehci_root_poll(), EHCI_USBSTS_ASYNC, EHCI_USBSTS_CHANGE, EHCI_USBSTS_HCH, EHCI_USBSTS_PERIODIC, EHCI_USBSTS_PORT, EHCI_USBSTS_SYSERR, EHCI_USBSTS_USBERRINT, EHCI_USBSTS_USBINT, ehci_device::endpoints, usb_bus::hub, ehci_endpoint::list, list_for_each_entry, ehci_device::name, ehci_device::op, ehci_device::ports, readl(), usb_bus_get_hostdata(), and writel().

                                                  {
        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 );
}
static int ehci_probe ( struct pci_device pci) [static]

Probe PCI device.

Parameters:
pciPCI device
Return values:
rcReturn status code

Definition at line 1974 of file ehci.c.

References adjust_pci_device(), alloc_usb_bus(), ehci_device::async, ehci_device::bus, pci_device::dev, EHCI_BAR, ehci_init(), ehci_legacy_claim(), ehci_legacy_init(), ehci_legacy_release(), EHCI_MTU, ehci_reset(), ehci_device::endpoints, ENODEV, ENOMEM, free, free_usb_bus(), usb_bus::hub, INIT_LIST_HEAD, ioremap(), iounmap(), device::name, ehci_device::name, pci_bar_size(), pci_bar_start(), pci_set_drvdata(), ehci_device::periodic, port, ehci_device::ports, usb_port::protocol, rc, register_usb_bus(), ehci_device::regs, unregister_usb_bus(), usb_bus_set_hostdata(), usb_hub_set_drvdata(), usb_port(), USB_PROTO_2_0, and zalloc().

                                                 {
        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;
}
static void ehci_remove ( struct pci_device pci) [static]

Remove PCI device.

Parameters:
pciPCI device

Definition at line 2058 of file ehci.c.

References assert, ehci_device::async, bus, ehci_device::bus, ehci_legacy_release(), ehci_reset(), free, free_usb_bus(), iounmap(), list_empty, pci_get_drvdata(), ehci_device::periodic, ehci_device::regs, and unregister_usb_bus().

                                                   {
        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 );
}
static void ehci_shutdown ( int  booting) [static]

Prepare for exit.

Parameters:
bootingSystem is shutting down for OS boot

Definition at line 2092 of file ehci.c.

References ehci_legacy_prevent_release.

                                          {
        /* If we are shutting down to boot an OS, then prevent the
         * release of ownership back to BIOS.
         */
        ehci_legacy_prevent_release = booting;
}
struct startup_fn ehci_startup __startup_fn ( STARTUP_LATE  ) [read]

Startup/shutdown function.


Variable Documentation

Prevent the release of ownership back to BIOS.

Definition at line 239 of file ehci.c.

Referenced by ehci_legacy_release(), and ehci_shutdown().

USB host controller operations.

Definition at line 1935 of file ehci.c.

struct pci_device_id ehci_ids[] [static]
Initial value:
 {
        PCI_ROM ( 0xffff, 0xffff, "ehci", "EHCI", 0 ),
}

EHCI PCI device IDs.

Definition at line 2073 of file ehci.c.

struct pci_driver ehci_driver __pci_driver
Initial value:
 {
        .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,
}

EHCI PCI driver.

Definition at line 2078 of file ehci.c.