mirror of
https://github.com/smaeul/u-boot.git
synced 2025-10-14 12:56:00 +01:00
usb: ehci-hcd: Keep async schedule running
Profiling the EHCI driver shows a significant performance problem in ehci_submit_async(). Specifically, this function keeps enabling and disabling async schedule back and forth for every single transaction. However, enabling/disabling the async schedule does not take effect immediatelly, but instead may take up to 1 mS (8 uFrames) to complete. This impacts USB storage significantly, esp. since the recent reduction of maximum transfer size to support more USB storage devices. This in turn results in sharp increase in the number of ehci_submit_async() calls. Since one USB storage BBB transfer does three such calls and the maximum transfer size is 120 kiB, the overhead is 6 mS per 120 kiB, which is unacceptable. However, this overhead can be removed simply by keeping the async schedule running. Specifically, the first transfer starts the async schedule and then each and every subsequent transfer only adds a new QH into that schedule, waits until the QH is completed and does NOT disable the async schedule. The async schedule is stopped only by shutting down the controller, which must happen before moving out of U-Boot, otherwise the controller will corrupt memory. Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com> Cc: Bin Meng <bmeng.cn@gmail.com> Cc: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
da3d1c499f
commit
02b0e1a36c
@ -307,7 +307,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||||||
volatile struct qTD *vtd;
|
volatile struct qTD *vtd;
|
||||||
unsigned long ts;
|
unsigned long ts;
|
||||||
uint32_t *tdp;
|
uint32_t *tdp;
|
||||||
uint32_t endpt, maxpacket, token, usbsts;
|
uint32_t endpt, maxpacket, token, usbsts, qhtoken;
|
||||||
uint32_t c, toggle;
|
uint32_t c, toggle;
|
||||||
uint32_t cmd;
|
uint32_t cmd;
|
||||||
int timeout;
|
int timeout;
|
||||||
@ -551,14 +551,12 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||||||
flush_dcache_range((unsigned long)qtd,
|
flush_dcache_range((unsigned long)qtd,
|
||||||
ALIGN_END_ADDR(struct qTD, qtd, qtd_count));
|
ALIGN_END_ADDR(struct qTD, qtd, qtd_count));
|
||||||
|
|
||||||
/* Set async. queue head pointer. */
|
|
||||||
ehci_writel(&ctrl->hcor->or_asynclistaddr, virt_to_phys(&ctrl->qh_list));
|
|
||||||
|
|
||||||
usbsts = ehci_readl(&ctrl->hcor->or_usbsts);
|
usbsts = ehci_readl(&ctrl->hcor->or_usbsts);
|
||||||
ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f));
|
ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f));
|
||||||
|
|
||||||
/* Enable async. schedule. */
|
/* Enable async. schedule. */
|
||||||
cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
|
cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
|
||||||
|
if (!(cmd & CMD_ASE)) {
|
||||||
cmd |= CMD_ASE;
|
cmd |= CMD_ASE;
|
||||||
ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
|
ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
|
||||||
|
|
||||||
@ -568,6 +566,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||||||
printf("EHCI fail timeout STS_ASS set\n");
|
printf("EHCI fail timeout STS_ASS set\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Wait for TDs to be processed. */
|
/* Wait for TDs to be processed. */
|
||||||
ts = get_timer(0);
|
ts = get_timer(0);
|
||||||
@ -587,6 +586,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||||||
break;
|
break;
|
||||||
WATCHDOG_RESET();
|
WATCHDOG_RESET();
|
||||||
} while (get_timer(ts) < timeout);
|
} while (get_timer(ts) < timeout);
|
||||||
|
qhtoken = hc32_to_cpu(qh->qh_overlay.qt_token);
|
||||||
|
|
||||||
|
ctrl->qh_list.qh_link = cpu_to_hc32(virt_to_phys(&ctrl->qh_list) | QH_LINK_TYPE_QH);
|
||||||
|
flush_dcache_range((unsigned long)&ctrl->qh_list,
|
||||||
|
ALIGN_END_ADDR(struct QH, &ctrl->qh_list, 1));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Invalidate the memory area occupied by buffer
|
* Invalidate the memory area occupied by buffer
|
||||||
@ -605,25 +609,12 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||||||
if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)
|
if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)
|
||||||
printf("EHCI timed out on TD - token=%#x\n", token);
|
printf("EHCI timed out on TD - token=%#x\n", token);
|
||||||
|
|
||||||
/* Disable async schedule. */
|
if (!(QT_TOKEN_GET_STATUS(qhtoken) & QT_TOKEN_STATUS_ACTIVE)) {
|
||||||
cmd = ehci_readl(&ctrl->hcor->or_usbcmd);
|
debug("TOKEN=%#x\n", qhtoken);
|
||||||
cmd &= ~CMD_ASE;
|
switch (QT_TOKEN_GET_STATUS(qhtoken) &
|
||||||
ehci_writel(&ctrl->hcor->or_usbcmd, cmd);
|
|
||||||
|
|
||||||
ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, 0,
|
|
||||||
100 * 1000);
|
|
||||||
if (ret < 0) {
|
|
||||||
printf("EHCI fail timeout STS_ASS reset\n");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
token = hc32_to_cpu(qh->qh_overlay.qt_token);
|
|
||||||
if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) {
|
|
||||||
debug("TOKEN=%#x\n", token);
|
|
||||||
switch (QT_TOKEN_GET_STATUS(token) &
|
|
||||||
~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) {
|
~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) {
|
||||||
case 0:
|
case 0:
|
||||||
toggle = QT_TOKEN_GET_DT(token);
|
toggle = QT_TOKEN_GET_DT(qhtoken);
|
||||||
usb_settoggle(dev, usb_pipeendpoint(pipe),
|
usb_settoggle(dev, usb_pipeendpoint(pipe),
|
||||||
usb_pipeout(pipe), toggle);
|
usb_pipeout(pipe), toggle);
|
||||||
dev->status = 0;
|
dev->status = 0;
|
||||||
@ -641,11 +632,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev->status = USB_ST_CRC_ERR;
|
dev->status = USB_ST_CRC_ERR;
|
||||||
if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED)
|
if (QT_TOKEN_GET_STATUS(qhtoken) & QT_TOKEN_STATUS_HALTED)
|
||||||
dev->status |= USB_ST_STALLED;
|
dev->status |= USB_ST_STALLED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token);
|
dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(qhtoken);
|
||||||
} else {
|
} else {
|
||||||
dev->act_len = 0;
|
dev->act_len = 0;
|
||||||
#ifndef CONFIG_USB_EHCI_FARADAY
|
#ifndef CONFIG_USB_EHCI_FARADAY
|
||||||
|
Loading…
x
Reference in New Issue
Block a user