david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[uhci] Add support for UHCI host controllers

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2015-05-09 23:14:34 +01:00
parent 9ea8a2daa7
commit 6dba29b18f
7 changed files with 1937 additions and 0 deletions

View File

@ -40,3 +40,6 @@ REQUIRE_OBJECT ( xhci );
#ifdef USB_HCD_EHCI
REQUIRE_OBJECT ( ehci );
#endif
#ifdef USB_HCD_UHCI
REQUIRE_OBJECT ( uhci );
#endif

View File

@ -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 */

View File

@ -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)

1573
src/drivers/usb/uhci.c Normal file

File diff suppressed because it is too large Load Diff

348
src/drivers/usb/uhci.h Normal file
View File

@ -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 */

View File

@ -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 )

View File

@ -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 */