|
|
|
@ -273,6 +273,29 @@ static inline u8 ehci_encode_speed(enum usb_device_speed speed) |
|
|
|
|
return QH_FULL_SPEED; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void ehci_update_endpt2_dev_n_port(struct usb_device *dev, |
|
|
|
|
struct QH *qh) |
|
|
|
|
{ |
|
|
|
|
struct usb_device *ttdev; |
|
|
|
|
|
|
|
|
|
if (dev->speed != USB_SPEED_LOW && dev->speed != USB_SPEED_FULL) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* For full / low speed devices we need to get the devnum and portnr of |
|
|
|
|
* the tt, so of the first upstream usb-2 hub, there may be usb-1 hubs |
|
|
|
|
* in the tree before that one! |
|
|
|
|
*/ |
|
|
|
|
ttdev = dev; |
|
|
|
|
while (ttdev->parent && ttdev->parent->speed != USB_SPEED_HIGH) |
|
|
|
|
ttdev = ttdev->parent; |
|
|
|
|
if (!ttdev->parent) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
qh->qh_endpt2 |= cpu_to_hc32(QH_ENDPT2_PORTNUM(ttdev->portnr) | |
|
|
|
|
QH_ENDPT2_HUBADDR(ttdev->parent->devnum)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int |
|
|
|
|
ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, |
|
|
|
|
int length, struct devrequest *req) |
|
|
|
@ -390,10 +413,9 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, |
|
|
|
|
QH_ENDPT1_ENDPT(usb_pipeendpoint(pipe)) | QH_ENDPT1_I(0) | |
|
|
|
|
QH_ENDPT1_DEVADDR(usb_pipedevice(pipe)); |
|
|
|
|
qh->qh_endpt1 = cpu_to_hc32(endpt); |
|
|
|
|
endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_PORTNUM(dev->portnr) | |
|
|
|
|
QH_ENDPT2_HUBADDR(dev->parent->devnum) | |
|
|
|
|
QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0); |
|
|
|
|
endpt = QH_ENDPT2_MULT(1) | QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0); |
|
|
|
|
qh->qh_endpt2 = cpu_to_hc32(endpt); |
|
|
|
|
ehci_update_endpt2_dev_n_port(dev, qh); |
|
|
|
|
qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); |
|
|
|
|
qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); |
|
|
|
|
|
|
|
|
@ -974,6 +996,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) |
|
|
|
|
* Set up periodic list |
|
|
|
|
* Step 1: Parent QH for all periodic transfers. |
|
|
|
|
*/ |
|
|
|
|
ehcic[index].periodic_schedules = 0; |
|
|
|
|
periodic = &ehcic[index].periodic_queue; |
|
|
|
|
memset(periodic, 0, sizeof(*periodic)); |
|
|
|
|
periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); |
|
|
|
@ -1132,8 +1155,6 @@ disable_periodic(struct ehci_ctrl *ctrl) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int periodic_schedules; |
|
|
|
|
|
|
|
|
|
struct int_queue * |
|
|
|
|
create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, |
|
|
|
|
int elementsize, void *buffer) |
|
|
|
@ -1201,12 +1222,10 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, |
|
|
|
|
(1 << 0)); /* S-mask: microframe 0 */ |
|
|
|
|
if (dev->speed == USB_SPEED_LOW || |
|
|
|
|
dev->speed == USB_SPEED_FULL) { |
|
|
|
|
debug("TT: port: %d, hub address: %d\n", |
|
|
|
|
dev->portnr, dev->parent->devnum); |
|
|
|
|
qh->qh_endpt2 |= cpu_to_hc32((dev->portnr << 23) | |
|
|
|
|
(dev->parent->devnum << 16) | |
|
|
|
|
(0x1c << 8)); /* C-mask: microframes 2-4 */ |
|
|
|
|
/* C-mask: microframes 2-4 */ |
|
|
|
|
qh->qh_endpt2 |= cpu_to_hc32((0x1c << 8)); |
|
|
|
|
} |
|
|
|
|
ehci_update_endpt2_dev_n_port(dev, qh); |
|
|
|
|
|
|
|
|
|
td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); |
|
|
|
|
td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); |
|
|
|
@ -1258,7 +1277,7 @@ create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, |
|
|
|
|
debug("FATAL: periodic should never fail, but did"); |
|
|
|
|
goto fail3; |
|
|
|
|
} |
|
|
|
|
periodic_schedules++; |
|
|
|
|
ctrl->periodic_schedules++; |
|
|
|
|
|
|
|
|
|
debug("Exit create_int_queue\n"); |
|
|
|
|
return result; |
|
|
|
@ -1277,6 +1296,7 @@ fail1: |
|
|
|
|
void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) |
|
|
|
|
{ |
|
|
|
|
struct QH *cur = queue->current; |
|
|
|
|
struct qTD *cur_td; |
|
|
|
|
|
|
|
|
|
/* depleted queue */ |
|
|
|
|
if (cur == NULL) { |
|
|
|
@ -1284,20 +1304,21 @@ void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
/* still active */ |
|
|
|
|
invalidate_dcache_range((uint32_t)cur, |
|
|
|
|
ALIGN_END_ADDR(struct QH, cur, 1)); |
|
|
|
|
if (cur->qh_overlay.qt_token & cpu_to_hc32(0x80)) { |
|
|
|
|
debug("Exit poll_int_queue with no completed intr transfer. " |
|
|
|
|
"token is %x\n", cur->qh_overlay.qt_token); |
|
|
|
|
cur_td = &queue->tds[queue->current - queue->first]; |
|
|
|
|
invalidate_dcache_range((uint32_t)cur_td, |
|
|
|
|
ALIGN_END_ADDR(struct qTD, cur_td, 1)); |
|
|
|
|
if (QT_TOKEN_GET_STATUS(hc32_to_cpu(cur_td->qt_token)) & |
|
|
|
|
QT_TOKEN_STATUS_ACTIVE) { |
|
|
|
|
debug("Exit poll_int_queue with no completed intr transfer. token is %x\n", |
|
|
|
|
hc32_to_cpu(cur_td->qt_token)); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
if (!(cur->qh_link & QH_LINK_TERMINATE)) |
|
|
|
|
queue->current++; |
|
|
|
|
else |
|
|
|
|
queue->current = NULL; |
|
|
|
|
debug("Exit poll_int_queue with completed intr transfer. " |
|
|
|
|
"token is %x at %p (first at %p)\n", cur->qh_overlay.qt_token, |
|
|
|
|
&cur->qh_overlay.qt_token, queue->first); |
|
|
|
|
debug("Exit poll_int_queue with completed intr transfer. token is %x at %p (first at %p)\n", |
|
|
|
|
hc32_to_cpu(cur_td->qt_token), cur, queue->first); |
|
|
|
|
return cur->buffer; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1313,7 +1334,7 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue) |
|
|
|
|
debug("FATAL: periodic should never fail, but did"); |
|
|
|
|
goto out; |
|
|
|
|
} |
|
|
|
|
periodic_schedules--; |
|
|
|
|
ctrl->periodic_schedules--; |
|
|
|
|
|
|
|
|
|
struct QH *cur = &ctrl->periodic_queue; |
|
|
|
|
timeout = get_timer(0) + 500; /* abort after 500ms */ |
|
|
|
@ -1322,6 +1343,8 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue) |
|
|
|
|
if (NEXT_QH(cur) == queue->first) { |
|
|
|
|
debug("found candidate. removing from chain\n"); |
|
|
|
|
cur->qh_link = queue->last->qh_link; |
|
|
|
|
flush_dcache_range((uint32_t)cur, |
|
|
|
|
ALIGN_END_ADDR(struct QH, cur, 1)); |
|
|
|
|
result = 0; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
@ -1333,7 +1356,7 @@ destroy_int_queue(struct usb_device *dev, struct int_queue *queue) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (periodic_schedules > 0) { |
|
|
|
|
if (ctrl->periodic_schedules > 0) { |
|
|
|
|
result = enable_periodic(ctrl); |
|
|
|
|
if (result < 0) |
|
|
|
|
debug("FATAL: periodic should never fail, but did"); |
|
|
|
|