aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNikunj A Dadhania <nikunj@linux.vnet.ibm.com>2013-08-01 14:59:42 +0530
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-08-06 16:00:30 +1000
commit2ecfaa4a86fecae3dfa25c33ebf7b4f29c71e129 (patch)
treed399e80f29775ad69f265098f31fd7ffa59ac0e0 /lib
parent69772c33c22416603a136d53a3311d961efaab61 (diff)
downloadSLOF-2ecfaa4a86fecae3dfa25c33ebf7b4f29c71e129.zip
SLOF-2ecfaa4a86fecae3dfa25c33ebf7b4f29c71e129.tar.gz
SLOF-2ecfaa4a86fecae3dfa25c33ebf7b4f29c71e129.tar.bz2
usb-ehci: Add ehci handshake
After removal of the QTD, the device driver needs to make sure that the host controller is really done with this QTD, and does not have any local/cached copy of this. This is achieved by employing a 3-bit handshake as explained in the echi spec 4.8.2 and Fig 4-10 Also add missing memory barrier. Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/libusb/usb-ehci.c46
-rw-r--r--lib/libusb/usb-ehci.h3
2 files changed, 43 insertions, 6 deletions
diff --git a/lib/libusb/usb-ehci.c b/lib/libusb/usb-ehci.c
index ba52a08..d38690c 100644
--- a/lib/libusb/usb-ehci.c
+++ b/lib/libusb/usb-ehci.c
@@ -283,6 +283,29 @@ static void ehci_disconnect(void)
}
+static int ehci_handshake(struct ehci_hcd *ehcd, uint32_t timeout)
+{
+ uint32_t usbsts = 0, time;
+ uint32_t usbcmd;
+ mb();
+ usbcmd = read_reg32(&ehcd->op_regs->usbcmd);
+ /* Ring a doorbell */
+ write_reg32(&ehcd->op_regs->usbcmd, usbcmd | CMD_IAAD);
+ mb();
+ time = SLOF_GetTimer() + timeout;
+ while ((time > SLOF_GetTimer())) {
+ /* Wait for controller to confirm */
+ usbsts = read_reg32(&ehcd->op_regs->usbsts);
+ if (usbsts & STS_IAA) {
+ /* Acknowledge it, for next doorbell to work */
+ write_reg32(&ehcd->op_regs->usbsts, STS_IAA);
+ return true;
+ }
+ cpu_relax();
+ }
+ return false;
+}
+
static int fill_qtd_buff(struct ehci_qtd *qtd, long data, uint32_t size)
{
long i, rem;
@@ -394,11 +417,17 @@ static int ehci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *
}
} while (qtd->next_qtd != QH_PTR_TERM);
+ ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH);
+ mb();
+ if (!ehci_handshake(ehcd, USB_TIMEOUT)) {
+ printf("%s: handshake failed\n", __func__);
+ ret = false;
+ }
+
SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req));
SLOF_dma_map_out(data_phys, data, datalen);
SLOF_dma_map_out(PTR_U32(qtds_phys), qtds, sizeof(*qtds) * 3);
SLOF_dma_free(qtds, sizeof(*qtds) * 3);
- ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH);
return ret;
}
@@ -410,7 +439,7 @@ static int ehci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys,
struct ehci_qtd *qtd, *qtd_phys;
struct ehci_pipe *epipe;
uint32_t pid;
- int i, rem;
+ int i, rem, ret = true;
uint32_t time;
long ptr;
@@ -473,20 +502,25 @@ static int ehci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys,
while ((time > SLOF_GetTimer()) &&
(le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)))
cpu_relax();
+ mb();
if (qtd->next_qtd == QH_PTR_TERM)
break;
if (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)) {
printf("usb-ehci: bulk transfer timed out_\n");
- return false;
+ ret = false;
+ break;
}
-
qtd++;
}
ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH);
-
- return true;
+ mb();
+ if (!ehci_handshake(ehcd, USB_TIMEOUT)) {
+ printf("%s: handshake failed\n", __func__);
+ ret = false;
+ }
+ return ret;
}
static struct usb_pipe *ehci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
diff --git a/lib/libusb/usb-ehci.h b/lib/libusb/usb-ehci.h
index 9b4fa82..2955a9c 100644
--- a/lib/libusb/usb-ehci.h
+++ b/lib/libusb/usb-ehci.h
@@ -101,12 +101,15 @@ struct ehci_pipe {
#define HCS_NPORTS_MASK 0x000f
+#define CMD_IAAD (1 << 6)
#define CMD_ASE (1 << 5)
#define CMD_PSE (1 << 4)
#define CMD_FLS_MASK (3 << 2)
#define CMD_HCRESET (1 << 1)
#define CMD_RUN (1 << 0)
+#define STS_IAA (1 << 5)
+
#define PORT_RESET (1 << 8)
#define PORT_PE (1 << 2)
#define PORT_CSC (1 << 1)