351 lines
7.5 KiB
C
351 lines
7.5 KiB
C
#ifndef _IPXE_UHCI_H
|
|
#define _IPXE_UHCI_H
|
|
|
|
/** @file
|
|
*
|
|
* USB Universal Host Controller Interface (UHCI) driver
|
|
*
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|
|
|
#include <assert.h>
|
|
#include <ipxe/pci.h>
|
|
#include <ipxe/usb.h>
|
|
|
|
/** Minimum alignment required for data structures
|
|
*
|
|
* With the exception of the frame list (which is page-aligned), data
|
|
* structures used by UHCI generally require 16-byte alignment.
|
|
*/
|
|
#define UHCI_ALIGN 16
|
|
|
|
/** Number of ports */
|
|
#define UHCI_PORTS 2
|
|
|
|
/** Maximum transfer size */
|
|
#define UHCI_MTU 1280
|
|
|
|
/** I/O BAR size */
|
|
#define UHCI_BAR_SIZE 0x14
|
|
|
|
/** USB command register */
|
|
#define UHCI_USBCMD 0x00
|
|
|
|
/** Max packet is 64 bytes */
|
|
#define UHCI_USBCMD_MAX64 0x0080
|
|
|
|
/** Host controller reset */
|
|
#define UHCI_USBCMD_HCRESET 0x0002
|
|
|
|
/** Run/stop */
|
|
#define UHCI_USBCMD_RUN 0x0001
|
|
|
|
/** USB status register */
|
|
#define UHCI_USBSTS 0x02
|
|
|
|
/** Host controller halted */
|
|
#define UHCI_USBSTS_HCHALTED 0x0020
|
|
|
|
/** USB interrupt */
|
|
#define UHCI_USBSTS_USBINT 0x0001
|
|
|
|
/** Frame list base address register */
|
|
#define UHCI_FLBASEADD 0x08
|
|
|
|
/** Port status and control register */
|
|
#define UHCI_PORTSC(port) ( 0x0e + ( (port) << 1 ) )
|
|
|
|
/** Port reset */
|
|
#define UHCI_PORTSC_PR 0x0200
|
|
|
|
/** Low-speed device attached */
|
|
#define UHCI_PORTSC_LS 0x0100
|
|
|
|
/** Port enabled/disabled change */
|
|
#define UHCI_PORTSC_PEC 0x0008
|
|
|
|
/** Port enabled */
|
|
#define UHCI_PORTSC_PED 0x0004
|
|
|
|
/** Connect status change */
|
|
#define UHCI_PORTSC_CSC 0x0002
|
|
|
|
/** Current connect status */
|
|
#define UHCI_PORTSC_CCS 0x0001
|
|
|
|
/** Port status change mask */
|
|
#define UHCI_PORTSC_CHANGE ( UHCI_PORTSC_CSC | UHCI_PORTSC_PEC )
|
|
|
|
/** Depth-first processing */
|
|
#define UHCI_LINK_DEPTH_FIRST 0x00000004UL
|
|
|
|
/** Queue head type */
|
|
#define UHCI_LINK_TYPE_QH 0x00000002UL
|
|
|
|
/** List terminator */
|
|
#define UHCI_LINK_TERMINATE 0x00000001UL
|
|
|
|
/** Number of frames in frame list */
|
|
#define UHCI_FRAMES 1024
|
|
|
|
/** A frame list */
|
|
struct uhci_frame_list {
|
|
/** Link pointer */
|
|
uint32_t link[UHCI_FRAMES];
|
|
} __attribute__ (( packed ));
|
|
|
|
/** A transfer descriptor */
|
|
struct uhci_transfer_descriptor {
|
|
/** Link pointer */
|
|
uint32_t link;
|
|
/** Actual length */
|
|
uint16_t actual;
|
|
/** Status */
|
|
uint8_t status;
|
|
/** Flags */
|
|
uint8_t flags;
|
|
/** Control */
|
|
uint32_t control;
|
|
/** Buffer pointer */
|
|
uint32_t data;
|
|
} __attribute__ (( packed ));
|
|
|
|
/** Length mask */
|
|
#define UHCI_LEN_MASK 0x7ff
|
|
|
|
/** Actual length */
|
|
#define UHCI_ACTUAL_LEN( actual ) ( ( (actual) + 1 ) & UHCI_LEN_MASK )
|
|
|
|
/** Active */
|
|
#define UHCI_STATUS_ACTIVE 0x80
|
|
|
|
/** Stalled */
|
|
#define UHCI_STATUS_STALLED 0x40
|
|
|
|
/** Data buffer error */
|
|
#define UHCI_STATUS_BUFFER 0x20
|
|
|
|
/** Babble detected */
|
|
#define UHCI_STATUS_BABBLE 0x10
|
|
|
|
/** NAK received */
|
|
#define UHCI_STATUS_NAK 0x08
|
|
|
|
/** CRC/timeout error */
|
|
#define UHCI_STATUS_CRC_TIMEOUT 0x04
|
|
|
|
/** Bitstuff error */
|
|
#define UHCI_STATUS_BITSTUFF 0x02
|
|
|
|
/** Short packet detect */
|
|
#define UHCI_FL_SPD 0x20
|
|
|
|
/** Error counter */
|
|
#define UHCI_FL_CERR( count ) ( (count) << 3 )
|
|
|
|
/** Error counter maximum value */
|
|
#define UHCI_FL_CERR_MAX UHCI_FL_CERR ( 3 )
|
|
|
|
/** Low speed device */
|
|
#define UHCI_FL_LS 0x04
|
|
|
|
/** Interrupt on completion */
|
|
#define UHCI_FL_IOC 0x01
|
|
|
|
/** Packet ID */
|
|
#define UHCI_CONTROL_PID( pid ) ( (pid) << 0 )
|
|
|
|
/** Packet ID mask */
|
|
#define UHCI_CONTROL_PID_MASK UHCI_CONTROL_PID ( 0xff )
|
|
|
|
/** Device address */
|
|
#define UHCI_CONTROL_DEVICE( address ) ( (address) << 8 )
|
|
|
|
/** Endpoint address */
|
|
#define UHCI_CONTROL_ENDPOINT( address ) ( (address) << 15 )
|
|
|
|
/** Data toggle */
|
|
#define UHCI_CONTROL_TOGGLE ( 1 << 19 )
|
|
|
|
/** Data length */
|
|
#define UHCI_CONTROL_LEN( len ) ( ( ( (len) - 1 ) & UHCI_LEN_MASK ) << 21 )
|
|
|
|
/** Check for data packet
|
|
*
|
|
* This check is based on the fact that only USB_PID_SETUP has bit 2
|
|
* set.
|
|
*/
|
|
#define UHCI_DATA_PACKET( control ) ( ! ( control & 0x04 ) )
|
|
|
|
/** Check for short packet */
|
|
#define UHCI_SHORT_PACKET( control, actual ) \
|
|
( ( ( (control) >> 21 ) ^ (actual) ) & UHCI_LEN_MASK )
|
|
|
|
/** USB legacy support register (in PCI configuration space) */
|
|
#define UHCI_USBLEGSUP 0xc0
|
|
|
|
/** USB legacy support default value */
|
|
#define UHCI_USBLEGSUP_DEFAULT 0x2000
|
|
|
|
/** A queue head */
|
|
struct uhci_queue_head {
|
|
/** Horizontal link pointer */
|
|
uint32_t link;
|
|
/** Current transfer descriptor */
|
|
uint32_t current;
|
|
} __attribute__ (( packed ));
|
|
|
|
/** A single UHCI transfer
|
|
*
|
|
* UHCI hardware is extremely simple, and requires software to build
|
|
* the entire packet schedule (including manually handling all of the
|
|
* data toggles). The hardware requires at least 16 bytes of transfer
|
|
* descriptors per 64 bytes of transmitted/received data. We allocate
|
|
* the transfer descriptors at the time that the transfer is enqueued,
|
|
* to avoid the need to allocate unreasonably large blocks when the
|
|
* endpoint is opened.
|
|
*/
|
|
struct uhci_transfer {
|
|
/** Producer counter */
|
|
unsigned int prod;
|
|
/** Consumer counter */
|
|
unsigned int cons;
|
|
/** Completed data length */
|
|
size_t len;
|
|
|
|
/** Transfer descriptors */
|
|
struct uhci_transfer_descriptor *desc;
|
|
|
|
/** I/O buffer */
|
|
struct io_buffer *iobuf;
|
|
};
|
|
|
|
/** Number of transfer descriptors in a ring
|
|
*
|
|
* This is a policy decision.
|
|
*/
|
|
#define UHCI_RING_COUNT 16
|
|
|
|
/** A transfer ring */
|
|
struct uhci_ring {
|
|
/** Producer counter */
|
|
unsigned int prod;
|
|
/** Consumer counter */
|
|
unsigned int cons;
|
|
|
|
/** Maximum packet length */
|
|
size_t mtu;
|
|
/** Base flags
|
|
*
|
|
* This incorporates the CERR and LS bits
|
|
*/
|
|
uint8_t flags;
|
|
/** Base control word
|
|
*
|
|
* This incorporates the device address, the endpoint address,
|
|
* and the data toggle for the next descriptor to be enqueued.
|
|
*/
|
|
uint32_t control;
|
|
|
|
/** Transfers */
|
|
struct uhci_transfer *xfer[UHCI_RING_COUNT];
|
|
/** End of transfer ring (if non-empty) */
|
|
struct uhci_transfer *end;
|
|
|
|
/** Queue head */
|
|
struct uhci_queue_head *head;
|
|
};
|
|
|
|
/**
|
|
* Calculate space used in transfer ring
|
|
*
|
|
* @v ring Transfer ring
|
|
* @ret fill Number of entries used
|
|
*/
|
|
static inline __attribute__ (( always_inline )) unsigned int
|
|
uhci_ring_fill ( struct uhci_ring *ring ) {
|
|
unsigned int fill;
|
|
|
|
fill = ( ring->prod - ring->cons );
|
|
assert ( fill <= UHCI_RING_COUNT );
|
|
return fill;
|
|
}
|
|
|
|
/**
|
|
* Calculate space remaining in transfer ring
|
|
*
|
|
* @v ring Transfer ring
|
|
* @ret remaining Number of entries remaining
|
|
*/
|
|
static inline __attribute__ (( always_inline )) unsigned int
|
|
uhci_ring_remaining ( struct uhci_ring *ring ) {
|
|
unsigned int fill = uhci_ring_fill ( ring );
|
|
|
|
return ( UHCI_RING_COUNT - fill );
|
|
}
|
|
|
|
/** Maximum time to wait for host controller to stop
|
|
*
|
|
* This is a policy decision.
|
|
*/
|
|
#define UHCI_STOP_MAX_WAIT_MS 100
|
|
|
|
/** Maximum time to wait for reset to complete
|
|
*
|
|
* This is a policy decision.
|
|
*/
|
|
#define UHCI_RESET_MAX_WAIT_MS 500
|
|
|
|
/** Maximum time to wait for a port to be enabled
|
|
*
|
|
* This is a policy decision.
|
|
*/
|
|
#define UHCI_PORT_ENABLE_MAX_WAIT_MS 500
|
|
|
|
/** A UHCI device */
|
|
struct uhci_device {
|
|
/** Registers */
|
|
unsigned long regs;
|
|
/** Name */
|
|
const char *name;
|
|
|
|
/** EHCI companion controller bus:dev.fn address (if any) */
|
|
unsigned int companion;
|
|
|
|
/** Asynchronous queue head */
|
|
struct uhci_queue_head *head;
|
|
/** Frame list */
|
|
struct uhci_frame_list *frame;
|
|
|
|
/** List of all endpoints */
|
|
struct list_head endpoints;
|
|
/** Asynchronous schedule */
|
|
struct list_head async;
|
|
/** Periodic schedule
|
|
*
|
|
* Listed in decreasing order of endpoint interval.
|
|
*/
|
|
struct list_head periodic;
|
|
|
|
/** USB bus */
|
|
struct usb_bus *bus;
|
|
};
|
|
|
|
/** A UHCI endpoint */
|
|
struct uhci_endpoint {
|
|
/** UHCI device */
|
|
struct uhci_device *uhci;
|
|
/** USB endpoint */
|
|
struct usb_endpoint *ep;
|
|
/** List of all endpoints */
|
|
struct list_head list;
|
|
/** Endpoint schedule */
|
|
struct list_head schedule;
|
|
|
|
/** Transfer ring */
|
|
struct uhci_ring ring;
|
|
};
|
|
|
|
#endif /* _IPXE_UHCI_H */
|