mirror of https://github.com/ipxe/ipxe.git
[uhci] Add support for UHCI host controllers
Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/36/head
parent
9ea8a2daa7
commit
6dba29b18f
|
@ -40,3 +40,6 @@ REQUIRE_OBJECT ( xhci );
|
|||
#ifdef USB_HCD_EHCI
|
||||
REQUIRE_OBJECT ( ehci );
|
||||
#endif
|
||||
#ifdef USB_HCD_UHCI
|
||||
REQUIRE_OBJECT ( uhci );
|
||||
#endif
|
||||
|
|
|
@ -38,6 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
|
||||
#define USB_HCD_XHCI /* xHCI USB host controller */
|
||||
#define USB_HCD_EHCI /* EHCI USB host controller */
|
||||
#define USB_HCD_UHCI /* UHCI USB host controller */
|
||||
|
||||
#define REBOOT_CMD /* Reboot command */
|
||||
#define CPUID_CMD /* x86 CPU feature detection command */
|
||||
|
|
|
@ -17,6 +17,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
*/
|
||||
//#undef USB_HCD_XHCI /* xHCI USB host controller */
|
||||
//#undef USB_HCD_EHCI /* EHCI USB host controller */
|
||||
//#undef USB_HCD_UHCI /* UHCI USB host controller */
|
||||
|
||||
#include <config/named.h>
|
||||
#include NAMED_CONFIG(usb.h)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,348 @@
|
|||
#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;
|
||||
|
||||
/** 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 */
|
|
@ -80,6 +80,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
#define ERRFILE_usbhub ( ERRFILE_DRIVER | 0x00080000 )
|
||||
#define ERRFILE_xhci ( ERRFILE_DRIVER | 0x00090000 )
|
||||
#define ERRFILE_ehci ( ERRFILE_DRIVER | 0x000a0000 )
|
||||
#define ERRFILE_uhci ( ERRFILE_DRIVER | 0x000b0000 )
|
||||
|
||||
#define ERRFILE_nvs ( ERRFILE_DRIVER | 0x00100000 )
|
||||
#define ERRFILE_spi ( ERRFILE_DRIVER | 0x00110000 )
|
||||
|
|
|
@ -54,6 +54,16 @@ enum usb_speed {
|
|||
USB_SPEED_SUPER = USB_SPEED ( 5, 3 ),
|
||||
};
|
||||
|
||||
/** USB packet IDs */
|
||||
enum usb_pid {
|
||||
/** IN PID */
|
||||
USB_PID_IN = 0x69,
|
||||
/** OUT PID */
|
||||
USB_PID_OUT = 0xe1,
|
||||
/** SETUP PID */
|
||||
USB_PID_SETUP = 0x2d,
|
||||
};
|
||||
|
||||
/** A USB setup data packet */
|
||||
struct usb_setup_packet {
|
||||
/** Request */
|
||||
|
|
Loading…
Reference in New Issue