#define ehci_is_TDI() (0)
#endif
-int __ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
+__weak int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
{
return PORTSC_PSPD(reg);
}
-int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
- __attribute__((weak, alias("__ehci_get_port_speed")));
-
-void __ehci_set_usbmode(int index)
+__weak void ehci_set_usbmode(int index)
{
uint32_t tmp;
uint32_t *reg_ptr;
ehci_writel(reg_ptr, tmp);
}
-void ehci_set_usbmode(int index)
- __attribute__((weak, alias("__ehci_set_usbmode")));
-
-void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
+__weak void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
{
mdelay(50);
}
-void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)
- __attribute__((weak, alias("__ehci_powerup_fixup")));
-
static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)
{
uint32_t result;
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)
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);
* 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);
}
struct int_queue {
+ int elementsize;
struct QH *first;
struct QH *current;
struct QH *last;
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)
struct int_queue *result = NULL;
int i;
+ /*
+ * Interrupt transfers requiring several transactions are not supported
+ * because bInterval is ignored.
+ *
+ * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2
+ * <= PKT_ALIGN if several qTDs are required, while the USB
+ * specification does not constrain this for interrupt transfers. That
+ * means that ehci_submit_async() would support interrupt transfers
+ * requiring several transactions only as long as the transfer size does
+ * not require more than a single qTD.
+ */
+ if (elementsize > usb_maxpacket(dev, pipe)) {
+ printf("%s: xfers requiring several transactions are not supported.\n",
+ __func__);
+ return NULL;
+ }
+
debug("Enter create_int_queue\n");
if (usb_pipetype(pipe) != PIPE_INTERRUPT) {
debug("non-interrupt pipe (type=%lu)", usb_pipetype(pipe));
debug("ehci intr queue: out of memory\n");
goto fail1;
}
+ result->elementsize = elementsize;
result->first = memalign(USB_DMA_MINALIGN,
sizeof(struct QH) * queuesize);
if (!result->first) {
(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);
ALIGN_END_ADDR(struct qTD, result->tds,
queuesize));
- if (disable_periodic(ctrl) < 0) {
- debug("FATAL: periodic should never fail, but did");
- goto fail3;
+ if (ctrl->periodic_schedules > 0) {
+ if (disable_periodic(ctrl) < 0) {
+ debug("FATAL: periodic should never fail, but did");
+ goto fail3;
+ }
}
/* hook up to periodic list */
debug("FATAL: periodic should never fail, but did");
goto fail3;
}
- periodic_schedules++;
+ ctrl->periodic_schedules++;
debug("Exit create_int_queue\n");
return result;
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) {
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);
+
+ invalidate_dcache_range((uint32_t)cur->buffer,
+ ALIGN_END_ADDR(char, cur->buffer,
+ queue->elementsize));
+
+ 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;
}
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 */
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;
}
}
}
- if (periodic_schedules > 0) {
+ if (ctrl->periodic_schedules > 0) {
result = enable_periodic(ctrl);
if (result < 0)
debug("FATAL: periodic should never fail, but did");
debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",
dev, pipe, buffer, length, interval);
- /*
- * Interrupt transfers requiring several transactions are not supported
- * because bInterval is ignored.
- *
- * Also, ehci_submit_async() relies on wMaxPacketSize being a power of 2
- * <= PKT_ALIGN if several qTDs are required, while the USB
- * specification does not constrain this for interrupt transfers. That
- * means that ehci_submit_async() would support interrupt transfers
- * requiring several transactions only as long as the transfer size does
- * not require more than a single qTD.
- */
- if (length > usb_maxpacket(dev, pipe)) {
- printf("%s: Interrupt transfers requiring several "
- "transactions are not supported.\n", __func__);
- return -1;
- }
-
queue = create_int_queue(dev, pipe, 1, length, buffer);
+ if (!queue)
+ return -1;
timeout = get_timer(0) + USB_TIMEOUT_MS(pipe);
while ((backbuffer = poll_int_queue(dev, queue)) == NULL)
return -EINVAL;
}
- invalidate_dcache_range((uint32_t)buffer,
- ALIGN_END_ADDR(char, buffer, length));
-
ret = destroy_int_queue(dev, queue);
if (ret < 0)
return ret;