From patchwork Sun Mar 22 13:00:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 244095 List-Id: U-Boot discussion From: lukma at denx.de (Lukasz Majewski) Date: Sun, 22 Mar 2020 14:00:27 +0100 Subject: [RFT PATCH v1 1/5] Revert "usb: ehci-hcd: Keep async schedule running" In-Reply-To: <20200322130031.10455-1-lukma@denx.de> References: <20200322130031.10455-1-lukma@denx.de> Message-ID: <20200322130031.10455-2-lukma@denx.de> This reverts commit 02b0e1a36c5bc20174299312556ec4e266872bd6. Signed-off-by: Lukasz Majewski --- drivers/usb/host/ehci-hcd.c | 51 ++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 1cc02052f5..0a77111f80 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -309,7 +309,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, volatile struct qTD *vtd; unsigned long ts; uint32_t *tdp; - uint32_t endpt, maxpacket, token, usbsts, qhtoken; + uint32_t endpt, maxpacket, token, usbsts; uint32_t c, toggle; uint32_t cmd; int timeout; @@ -553,21 +553,22 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, flush_dcache_range((unsigned long)qtd, 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); ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ cmd = ehci_readl(&ctrl->hcor->or_usbcmd); - if (!(cmd & CMD_ASE)) { - cmd |= CMD_ASE; - ehci_writel(&ctrl->hcor->or_usbcmd, cmd); + cmd |= CMD_ASE; + ehci_writel(&ctrl->hcor->or_usbcmd, cmd); - ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS, - 100 * 1000); - if (ret < 0) { - printf("EHCI fail timeout STS_ASS set\n"); - goto fail; - } + ret = handshake((uint32_t *)&ctrl->hcor->or_usbsts, STS_ASS, STS_ASS, + 100 * 1000); + if (ret < 0) { + printf("EHCI fail timeout STS_ASS set\n"); + goto fail; } /* Wait for TDs to be processed. */ @@ -588,11 +589,6 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, break; WATCHDOG_RESET(); } 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 @@ -611,12 +607,25 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE) printf("EHCI timed out on TD - token=%#x\n", token); - if (!(QT_TOKEN_GET_STATUS(qhtoken) & QT_TOKEN_STATUS_ACTIVE)) { - debug("TOKEN=%#x\n", qhtoken); - switch (QT_TOKEN_GET_STATUS(qhtoken) & + /* Disable async schedule. */ + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); + cmd &= ~CMD_ASE; + 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)) { case 0: - toggle = QT_TOKEN_GET_DT(qhtoken); + toggle = QT_TOKEN_GET_DT(token); usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), toggle); dev->status = 0; @@ -634,11 +643,11 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, break; default: dev->status = USB_ST_CRC_ERR; - if (QT_TOKEN_GET_STATUS(qhtoken) & QT_TOKEN_STATUS_HALTED) + if (QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_HALTED) dev->status |= USB_ST_STALLED; break; } - dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(qhtoken); + dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token); } else { dev->act_len = 0; #ifndef CONFIG_USB_EHCI_FARADAY From patchwork Sun Mar 22 13:00:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 244096 List-Id: U-Boot discussion From: lukma at denx.de (Lukasz Majewski) Date: Sun, 22 Mar 2020 14:00:28 +0100 Subject: [RFT PATCH v1 2/5] usb: Handle XACTERR error in DATA phase of USB storage In-Reply-To: <20200322130031.10455-1-lukma@denx.de> References: <20200322130031.10455-1-lukma@denx.de> Message-ID: <20200322130031.10455-3-lukma@denx.de> This change brings support for handling XACTERR error during DATA phase of USB BBB (bulk) transmission. The fix is to clear stall'ed endpoint and reset recovery on the USB mass storage class. This code is the port to newest U-Boot of the fix from - "rayvt" (from [1]). Links: [1] - https://forum.doozan.com/read.php?3,35295,35295#msg-35295 [2] - https://www.dropbox.com/s/nrkrd1no63viuu8/uboot-bodhi-2016.05-timeoutTD.patch?dl=0 Signed-off-by: Lukasz Majewski [Unfortunately, the original patch [2] did not contain S-o-B from the original author - "rayvt"] --- common/usb_storage.c | 10 ++++++++++ include/usb_defs.h | 1 + 2 files changed, 11 insertions(+) diff --git a/common/usb_storage.c b/common/usb_storage.c index 097b6729c1..92e1e54d1c 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -740,6 +740,16 @@ static int usb_stor_BBB_transport(struct scsi_cmd *srb, struct us_data *us) result = usb_bulk_msg(us->pusb_dev, pipe, srb->pdata, srb->datalen, &data_actlen, USB_CNTL_TIMEOUT * 5); + + /* special handling of XACTERR in DATA phase */ + if (result < 0 && (us->pusb_dev->status & USB_ST_XACTERR)) { + debug("XACTERR in data phase - clr, reset, and return fail.\n"); + usb_stor_BBB_clear_endpt_stall(us, dir_in ? + us->ep_in : us->ep_out); + usb_stor_BBB_reset(us); + return USB_STOR_TRANSPORT_FAILED; + } + /* special handling of STALL in DATA phase */ if ((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) { debug("DATA:stall\n"); diff --git a/include/usb_defs.h b/include/usb_defs.h index 6dd2c997f9..572f6ab296 100644 --- a/include/usb_defs.h +++ b/include/usb_defs.h @@ -197,6 +197,7 @@ #define USB_ST_NAK_REC 0x10 /* NAK Received*/ #define USB_ST_CRC_ERR 0x20 /* CRC/timeout Error */ #define USB_ST_BIT_ERR 0x40 /* Bitstuff error */ +#define USB_ST_XACTERR 0x80 /* XACTERR error */ #define USB_ST_NOT_PROC 0x80000000L /* Not yet processed */ From patchwork Sun Mar 22 13:00:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 244097 List-Id: U-Boot discussion From: lukma at denx.de (Lukasz Majewski) Date: Sun, 22 Mar 2020 14:00:29 +0100 Subject: [RFT PATCH v1 3/5] usb: Add some delay to wait for slow USB devices to be operational In-Reply-To: <20200322130031.10455-1-lukma@denx.de> References: <20200322130031.10455-1-lukma@denx.de> Message-ID: <20200322130031.10455-4-lukma@denx.de> This change provides some extra time for some slow (or degraded) USB devices to become fully operational. This code is the port to newest U-Boot of the fix from - "rayvt" (from [1]). Links: [1] - https://forum.doozan.com/read.php?3,35295,35295#msg-35295 [2] - https://www.dropbox.com/s/nrkrd1no63viuu8/uboot-bodhi-2016.05-timeoutTD.patch?dl=0 Signed-off-by: Lukasz Majewski [Unfortunately, the original patch [2] did not contain S-o-B from the original author - "rayvt"] --- common/usb.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/common/usb.c b/common/usb.c index 349e838f1d..305482b5bb 100644 --- a/common/usb.c +++ b/common/usb.c @@ -925,14 +925,20 @@ static int get_descriptor_len(struct usb_device *dev, int len, int expect_len) __maybe_unused struct usb_device_descriptor *desc; ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ); int err; + int retry = 5; desc = (struct usb_device_descriptor *)tmpbuf; +again: err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, len); if (err < expect_len) { if (err < 0) { - printf("unable to get device descriptor (error=%d)\n", - err); + printf("unable to get device descriptor (error=%d) retry: %d\n", + err, retry); + mdelay(50); + if (--retry >= 0) + /* Some drives are just slow to wake up. */ + goto again; return err; } else { printf("USB device descriptor short read (expected %i, got %i)\n", From patchwork Sun Mar 22 13:00:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 244098 List-Id: U-Boot discussion From: lukma at denx.de (Lukasz Majewski) Date: Sun, 22 Mar 2020 14:00:30 +0100 Subject: [RFT PATCH v1 4/5] usb: Provide code to handle spinup of USB usb devices (mostly HDDs) In-Reply-To: <20200322130031.10455-1-lukma@denx.de> References: <20200322130031.10455-1-lukma@denx.de> Message-ID: <20200322130031.10455-5-lukma@denx.de> After this change USB mass storage devices - mostly HDDs connected via USB - will gain handling of extra time needed for their spin up. This operation is realized with issuing SCSI start/stop unit (0x1B) command. This code is the port to newest U-Boot of the fix from - "rayvt" (from [1]). Links: [1] - https://forum.doozan.com/read.php?3,35295,35295#msg-35295 [2] - https://www.dropbox.com/s/nrkrd1no63viuu8/uboot-bodhi-2016.05-timeoutTD.patch?dl=0 Signed-off-by: Lukasz Majewski [Unfortunately, the original patch [2] did not contain S-o-B from the original author - "rayvt"] --- common/usb_storage.c | 36 ++++++++++++++++++++++++++++++++++++ include/usb.h | 1 + 2 files changed, 37 insertions(+) diff --git a/common/usb_storage.c b/common/usb_storage.c index 92e1e54d1c..3c2324fa1a 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -729,6 +729,7 @@ static int usb_stor_BBB_transport(struct scsi_cmd *srb, struct us_data *us) pipeout = usb_sndbulkpipe(us->pusb_dev, us->ep_out); /* DATA phase + error handling */ data_actlen = 0; + mdelay(10); /* Like linux does. */ /* no data, go immediately to the STATUS phase */ if (srb->datalen == 0) goto st; @@ -1023,9 +1024,32 @@ static int usb_request_sense(struct scsi_cmd *srb, struct us_data *ss) return 0; } +/* + * This spins up the disk and also consumes the time that the + * disk takes to become active and ready to read data. + * Some drives (like Western Digital) can take more than 5 seconds. + * The delay occurs on the 1st data read from the disk. + * Extending the timeout here works better than handling the timeout + * as an error on a "real" read. + */ +static int usb_spinup(struct scsi_cmd *srb, struct us_data *ss) +{ + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_START_STP; + srb->cmd[1] = srb->lun << 5; + srb->cmd[4] = 1; /* Start spinup. */ + srb->datalen = 0; + srb->cmdlen = 6; + ss->pusb_dev->extra_timeout = 9876; + ss->transport(srb, ss); + ss->pusb_dev->extra_timeout = 0; + return 0; +} + static int usb_test_unit_ready(struct scsi_cmd *srb, struct us_data *ss) { int retries = 10; + int gave_extra_time = 0; do { memset(&srb->cmd[0], 0, 12); @@ -1048,6 +1072,17 @@ static int usb_test_unit_ready(struct scsi_cmd *srb, struct us_data *ss) if ((srb->sense_buf[2] == 0x02) && (srb->sense_buf[12] == 0x3a)) return -1; + /* + * If the status is "Not Ready - becoming ready", give it + * more time. Linux issues a spinup command (once) and gives + * it 100 seconds. + */ + if (srb->sense_buf[2] == 0x02 && + srb->sense_buf[12] == 0x04 && + gave_extra_time == 0) { + retries = 100; /* Allow 10 seconds. */ + gave_extra_time = retries; + } mdelay(100); } while (retries--); @@ -1502,6 +1537,7 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, dev_desc->log2blksz = LOG2(dev_desc->blksz); dev_desc->type = perq; debug(" address %d\n", dev_desc->target); + usb_spinup(pccb, ss); return 1; } diff --git a/include/usb.h b/include/usb.h index efb67ea33f..5b0f5ce5d6 100644 --- a/include/usb.h +++ b/include/usb.h @@ -140,6 +140,7 @@ struct usb_device { int act_len; /* transferred bytes */ int maxchild; /* Number of ports if hub */ int portnr; /* Port number, 1=first */ + int extra_timeout; /* Add to timeout in ehci_submit_async or spinup */ #if !CONFIG_IS_ENABLED(DM_USB) /* parent hub, or NULL if this is the root hub */ struct usb_device *parent; From patchwork Sun Mar 22 13:00:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 244099 List-Id: U-Boot discussion From: lukma at denx.de (Lukasz Majewski) Date: Sun, 22 Mar 2020 14:00:31 +0100 Subject: [RFT PATCH v1 5/5] usb: Handle QT_TOKEN_STATUS_XACTERR error when sending data In-Reply-To: <20200322130031.10455-1-lukma@denx.de> References: <20200322130031.10455-1-lukma@denx.de> Message-ID: <20200322130031.10455-6-lukma@denx.de> This code adds check if QT_TOKEN_STATUS_XACTERR error occurred. When it is detected the token is reconfigured and transmission is retried. This code is the port to newest U-Boot of the fix from - "rayvt" (from [1]). Links: [1] - https://forum.doozan.com/read.php?3,35295,35295#msg-35295 [2] - https://www.dropbox.com/s/nrkrd1no63viuu8/uboot-bodhi-2016.05-timeoutTD.patch?dl=0 Signed-off-by: Lukasz Majewski [Unfortunately, the original patch [2] did not contain S-o-B from the original author - "rayvt"] --- drivers/usb/host/ehci-hcd.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 0a77111f80..45eda7ad24 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -315,6 +315,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, int timeout; int ret = 0; struct ehci_ctrl *ctrl = ehci_get_ctrl(dev); + int trynum; debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe, buffer, length, req); @@ -560,6 +561,10 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, ehci_writel(&ctrl->hcor->or_usbsts, (usbsts & 0x3f)); /* Enable async. schedule. */ + trynum = 1; /* No more than 2 tries, in case of XACTERR. */ +retry_xacterr:; + vtd = &qtd[qtd_counter - 1]; + cmd = ehci_readl(&ctrl->hcor->or_usbcmd); cmd |= CMD_ASE; ehci_writel(&ctrl->hcor->or_usbcmd, cmd); @@ -573,8 +578,8 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, /* Wait for TDs to be processed. */ ts = get_timer(0); - vtd = &qtd[qtd_counter - 1]; timeout = USB_TIMEOUT_MS(pipe); + timeout += dev->extra_timeout; do { /* Invalidate dcache */ invalidate_dcache_range((unsigned long)&ctrl->qh_list, @@ -589,6 +594,7 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, break; WATCHDOG_RESET(); } while (get_timer(ts) < timeout); + debug("took %4lu ms of %4d\n", get_timer(ts), timeout); /* * Invalidate the memory area occupied by buffer @@ -622,6 +628,25 @@ ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer, token = hc32_to_cpu(qh->qh_overlay.qt_token); if (!(QT_TOKEN_GET_STATUS(token) & QT_TOKEN_STATUS_ACTIVE)) { debug("TOKEN=%#x\n", token); + if (token & QT_TOKEN_STATUS_XACTERR) { + if (--trynum >= 0) { + /* + * It is necessary to do this, otherwise the + * disk is clagged. + */ + debug("reset the TD and redo, because of XACTERR\n"); + token &= ~QT_TOKEN_STATUS_HALTED; + token |= QT_TOKEN_STATUS_ACTIVE | + QT_TOKEN_CERR(2); + vtd->qt_token = cpu_to_hc32(token); + qh->qh_overlay.qt_token = cpu_to_hc32(token); + goto retry_xacterr; + } + dev->status = USB_ST_XACTERR; + dev->act_len = length - QT_TOKEN_GET_TOTALBYTES(token); + goto fail; + } + switch (QT_TOKEN_GET_STATUS(token) & ~(QT_TOKEN_STATUS_SPLITXSTATE | QT_TOKEN_STATUS_PERR)) { case 0: