diff options
author | Michael Brown | 2015-03-23 14:34:16 +0100 |
---|---|---|
committer | Michael Brown | 2015-03-23 17:23:08 +0100 |
commit | b418af26d961dd465ce0667575ffc6d82a1a8987 (patch) | |
tree | 2a1bb351244ede637bbc74e1a64f255ae09f76a6 /src/drivers/usb | |
parent | [usb] Clear transaction translator buffers when applicable (diff) | |
download | ipxe-b418af26d961dd465ce0667575ffc6d82a1a8987.tar.gz ipxe-b418af26d961dd465ce0667575ffc6d82a1a8987.tar.xz ipxe-b418af26d961dd465ce0667575ffc6d82a1a8987.zip |
[ehci] Support USB1 devices attached via transaction translators
Support low-speed and full-speed devices attached to a USB2 hub. Such
devices use a transaction translator (TT) within the USB2 hub, which
asynchronously initiates transactions on the lower-speed bus and
returns the result via a split completion on the high-speed bus.
We make the simplifying assumption that there will never be more than
sixteen active interrupt endpoints behind a single transaction
translator; this assumption allows us to schedule all periodic start
splits in microframe 0 and all periodic split completions in
microframes 2 and 3. (We do not handle isochronous endpoints.)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/drivers/usb')
-rw-r--r-- | src/drivers/usb/ehci.c | 30 | ||||
-rw-r--r-- | src/drivers/usb/ehci.h | 34 |
2 files changed, 53 insertions, 11 deletions
diff --git a/src/drivers/usb/ehci.c b/src/drivers/usb/ehci.c index 83635f45..f912de35 100644 --- a/src/drivers/usb/ehci.c +++ b/src/drivers/usb/ehci.c @@ -545,7 +545,7 @@ static int ehci_enqueue ( struct ehci_device *ehci, struct ehci_ring *ring, 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; + desc->flags = ( xfer->flags | EHCI_FL_CERR_MAX ); /* Copy data to immediate data buffer (if requested) */ data = xfer->data; @@ -902,19 +902,16 @@ static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) { chr |= EHCI_CHR_TOGGLE; /* Determine endpoint speed */ - switch ( usb->port->speed ) { - case USB_SPEED_HIGH : + if ( usb->port->speed == USB_SPEED_HIGH ) { chr |= EHCI_CHR_EPS_HIGH; - break; - case USB_SPEED_FULL : - chr |= EHCI_CHR_EPS_FULL; - break; - default: - assert ( usb->port->speed == USB_SPEED_LOW ); - chr |= EHCI_CHR_EPS_LOW; + } else { + if ( usb->port->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; - break; } return chr; @@ -927,6 +924,8 @@ static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) { * @ret cap Endpoint capabilities */ static uint32_t ehci_endpoint_capabilities ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct usb_port *tt = usb_transaction_translator ( usb ); unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); uint32_t cap; unsigned int i; @@ -943,6 +942,15 @@ static uint32_t ehci_endpoint_capabilities ( struct usb_endpoint *ep ) { } } + /* 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; } diff --git a/src/drivers/usb/ehci.h b/src/drivers/usb/ehci.h index e9437d40..d575727d 100644 --- a/src/drivers/usb/ehci.h +++ b/src/drivers/usb/ehci.h @@ -285,6 +285,12 @@ struct ehci_transfer_descriptor { /** SETUP token */ #define EHCI_FL_PID_SETUP EHCI_FL_PID ( 2 ) +/** Error counter */ +#define EHCI_FL_CERR( count ) ( (count) << 2 ) + +/** Error counter maximum value */ +#define EHCI_FL_CERR_MAX EHCI_FL_CERR ( 3 ) + /** Interrupt on completion */ #define EHCI_FL_IOC 0x80 @@ -341,6 +347,34 @@ struct ehci_queue_head { /** Interrupt schedule mask */ #define EHCI_CAP_INTR_SCHED( uframe ) ( 1 << ( (uframe) + 0 ) ) +/** Split completion schedule mask */ +#define EHCI_CAP_SPLIT_SCHED( uframe ) ( 1 << ( (uframe) + 8 ) ) + +/** Default split completion schedule mask + * + * We schedule all split starts in microframe 0, on the assumption + * that we will never have to deal with more than sixteen actively + * interrupting devices via the same transaction translator. We + * schedule split completions for all remaining microframes after + * microframe 1 (in which the low-speed or full-speed transaction is + * assumed to execute). This is a very crude approximation designed + * to avoid the need for calculating exactly when low-speed and + * full-speed transactions will execute. Since we only ever deal with + * interrupt endpoints (rather than isochronous endpoints), the volume + * of periodic traffic is extremely low, and this approximation should + * remain valid. + */ +#define EHCI_CAP_SPLIT_SCHED_DEFAULT \ + ( EHCI_CAP_SPLIT_SCHED ( 2 ) | EHCI_CAP_SPLIT_SCHED ( 3 ) | \ + EHCI_CAP_SPLIT_SCHED ( 4 ) | EHCI_CAP_SPLIT_SCHED ( 5 ) | \ + EHCI_CAP_SPLIT_SCHED ( 6 ) | EHCI_CAP_SPLIT_SCHED ( 7 ) ) + +/** Transaction translator hub address */ +#define EHCI_CAP_TT_HUB( address ) ( (address) << 16 ) + +/** Transaction translator port number */ +#define EHCI_CAP_TT_PORT( port ) ( (port) << 23 ) + /** High-bandwidth pipe multiplier */ #define EHCI_CAP_MULT( mult ) ( (mult) << 30 ) |