#define CONFIG_USB_MAX_CONTROLLER_COUNT 1
#endif
+/*
+ * EHCI spec page 20 says that the HC may take up to 16 uFrames (= 4ms) to halt.
+ * Let's time out after 8 to have a little safety margin on top of that.
+ */
+#define HCHALT_TIMEOUT (8 * 1000)
+
static struct ehci_ctrl ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];
#define ALIGN_END_ADDR(type, ptr, size) \
return ret;
}
+static int ehci_shutdown(struct ehci_ctrl *ctrl)
+{
+ int i, ret = 0;
+ uint32_t cmd, reg;
+
+ if (!ctrl || !ctrl->hcor)
+ return -EINVAL;
+
+ cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
+ cmd &= ~(CMD_PSE | CMD_ASE);
+ ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+ ret = handshake(&ctrl->hcor->or_usbsts, STS_ASS | STS_PSS, 0,
+ 100 * 1000);
+
+ if (!ret) {
+ for (i = 0; i < CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS; i++) {
+ reg = ehci_readl(&ctrl->hcor->or_portsc[i]);
+ reg |= EHCI_PS_SUSP;
+ ehci_writel(&ctrl->hcor->or_portsc[i], reg);
+ }
+
+ cmd &= ~CMD_RUN;
+ ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
+ ret = handshake(&ctrl->hcor->or_usbsts, STS_HALT, STS_HALT,
+ HCHALT_TIMEOUT);
+ }
+
+ if (ret)
+ puts("EHCI failed to shut down host controller.\n");
+
+ return ret;
+}
+
static int ehci_td_buffer(struct qTD *td, void *buf, size_t sz)
{
uint32_t delta, next;
QH_ENDPT2_UFCMASK(0) | QH_ENDPT2_UFSMASK(0);
qh->qh_endpt2 = cpu_to_hc32(endpt);
qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+ qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
tdp = &qh->qh_overlay.qt_next;
}
break;
case USB_PORT_FEAT_TEST:
+ ehci_shutdown(ctrl);
reg &= ~(0xf << 16);
reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16;
ehci_writel(status_reg, reg);
int usb_lowlevel_stop(int index)
{
+ ehci_shutdown(&ehcic[index]);
return ehci_hcd_stop(index);
}
-int usb_lowlevel_init(int index, void **controller)
+int usb_lowlevel_init(int index, enum usb_init_type init, void **controller)
{
uint32_t reg;
uint32_t cmd;
struct QH *qh_list;
struct QH *periodic;
int i;
+ int rc;
- if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))
- return -1;
+ rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor);
+ if (rc)
+ return rc;
+ if (init == USB_INIT_DEVICE)
+ goto done;
/* EHCI spec section 4.1 */
if (ehci_reset(index))
return -1;
#if defined(CONFIG_EHCI_HCD_INIT_AFTER_RESET)
- if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))
- return -1;
+ rc = ehci_hcd_init(index, init, &ehcic[index].hccr, &ehcic[index].hcor);
+ if (rc)
+ return rc;
#endif
/* Set the high address word (aka segment) for 64-bit controller */
if (ehci_readl(&ehcic[index].hccr->cr_hccparams) & 1)
- ehci_writel(ehcic[index].hcor->or_ctrldssegment, 0);
+ ehci_writel(&ehcic[index].hcor->or_ctrldssegment, 0);
qh_list = &ehcic[index].qh_list;
if (!ehcic[index].periodic_list)
return -ENOMEM;
for (i = 0; i < 1024; i++) {
- ehcic[index].periodic_list[i] = (uint32_t)periodic
- | QH_LINK_TYPE_QH;
+ ehcic[index].periodic_list[i] = cpu_to_hc32((uint32_t)periodic
+ | QH_LINK_TYPE_QH);
}
flush_dcache_range((uint32_t)ehcic[index].periodic_list,
printf("USB EHCI %x.%02x\n", reg >> 8, reg & 0xff);
ehcic[index].rootdev = 0;
-
+done:
*controller = &ehcic[index];
return 0;
}
struct qTD *tds;
};
-#define NEXT_QH(qh) (struct QH *)((qh)->qh_link & ~0x1f)
+#define NEXT_QH(qh) (struct QH *)(hc32_to_cpu((qh)->qh_link) & ~0x1f)
static int
enable_periodic(struct ehci_ctrl *ctrl)
debug("ehci intr queue: out of memory\n");
goto fail1;
}
- result->first = memalign(32, sizeof(struct QH) * queuesize);
+ result->first = memalign(USB_DMA_MINALIGN,
+ sizeof(struct QH) * queuesize);
if (!result->first) {
debug("ehci intr queue: out of memory\n");
goto fail2;
}
result->current = result->first;
result->last = result->first + queuesize - 1;
- result->tds = memalign(32, sizeof(struct qTD) * queuesize);
+ result->tds = memalign(USB_DMA_MINALIGN,
+ sizeof(struct qTD) * queuesize);
if (!result->tds) {
debug("ehci intr queue: out of memory\n");
goto fail3;
struct qTD *td = result->tds + i;
void **buf = &qh->buffer;
- qh->qh_link = (uint32_t)(qh+1) | QH_LINK_TYPE_QH;
+ qh->qh_link = cpu_to_hc32((uint32_t)(qh+1) | QH_LINK_TYPE_QH);
if (i == queuesize - 1)
- qh->qh_link = QH_LINK_TERMINATE;
+ qh->qh_link = cpu_to_hc32(QH_LINK_TERMINATE);
- qh->qh_overlay.qt_next = (uint32_t)td;
- qh->qh_endpt1 = (0 << 28) | /* No NAK reload (ehci 4.9) */
+ qh->qh_overlay.qt_next = cpu_to_hc32((uint32_t)td);
+ qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
+ qh->qh_endpt1 =
+ cpu_to_hc32((0 << 28) | /* No NAK reload (ehci 4.9) */
(usb_maxpacket(dev, pipe) << 16) | /* MPS */
(1 << 14) |
QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) |
(usb_pipeendpoint(pipe) << 8) | /* Endpoint Number */
- (usb_pipedevice(pipe) << 0);
- qh->qh_endpt2 = (1 << 30) | /* 1 Tx per mframe */
- (1 << 0); /* S-mask: microframe 0 */
+ (usb_pipedevice(pipe) << 0));
+ qh->qh_endpt2 = cpu_to_hc32((1 << 30) | /* 1 Tx per mframe */
+ (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 |= (dev->portnr << 23) |
+ qh->qh_endpt2 |= cpu_to_hc32((dev->portnr << 23) |
(dev->parent->devnum << 16) |
- (0x1c << 8); /* C-mask: microframes 2-4 */
+ (0x1c << 8)); /* C-mask: microframes 2-4 */
}
- td->qt_next = QT_NEXT_TERMINATE;
- td->qt_altnext = QT_NEXT_TERMINATE;
+ td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
+ td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
debug("communication direction is '%s'\n",
usb_pipein(pipe) ? "in" : "out");
- td->qt_token = (elementsize << 16) |
+ td->qt_token = cpu_to_hc32((elementsize << 16) |
((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */
- 0x80; /* active */
- td->qt_buffer[0] = (uint32_t)buffer + i * elementsize;
- td->qt_buffer[1] = (td->qt_buffer[0] + 0x1000) & ~0xfff;
- td->qt_buffer[2] = (td->qt_buffer[0] + 0x2000) & ~0xfff;
- td->qt_buffer[3] = (td->qt_buffer[0] + 0x3000) & ~0xfff;
- td->qt_buffer[4] = (td->qt_buffer[0] + 0x4000) & ~0xfff;
+ 0x80); /* active */
+ td->qt_buffer[0] =
+ cpu_to_hc32((uint32_t)buffer + i * elementsize);
+ td->qt_buffer[1] =
+ cpu_to_hc32((td->qt_buffer[0] + 0x1000) & ~0xfff);
+ td->qt_buffer[2] =
+ cpu_to_hc32((td->qt_buffer[0] + 0x2000) & ~0xfff);
+ td->qt_buffer[3] =
+ cpu_to_hc32((td->qt_buffer[0] + 0x3000) & ~0xfff);
+ td->qt_buffer[4] =
+ cpu_to_hc32((td->qt_buffer[0] + 0x4000) & ~0xfff);
*buf = buffer + i * elementsize;
}
/* hook up to periodic list */
struct QH *list = &ctrl->periodic_queue;
result->last->qh_link = list->qh_link;
- list->qh_link = (uint32_t)result->first | QH_LINK_TYPE_QH;
+ list->qh_link = cpu_to_hc32((uint32_t)result->first | QH_LINK_TYPE_QH);
flush_dcache_range((uint32_t)result->last,
ALIGN_END_ADDR(struct QH, result->last, 1));
/* still active */
invalidate_dcache_range((uint32_t)cur,
ALIGN_END_ADDR(struct QH, cur, 1));
- if (cur->qh_overlay.qt_token & 0x80) {
+ 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);
return NULL;
struct QH *cur = &ctrl->periodic_queue;
timeout = get_timer(0) + 500; /* abort after 500ms */
- while (!(cur->qh_link & QH_LINK_TERMINATE)) {
+ while (!(cur->qh_link & cpu_to_hc32(QH_LINK_TERMINATE))) {
debug("considering %p, with qh_link %x\n", cur, cur->qh_link);
if (NEXT_QH(cur) == queue->first) {
debug("found candidate. removing from chain\n");