summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/isp1760-hcd.c83
1 files changed, 74 insertions, 9 deletions
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index 11367b45d51..5b08bd743ac 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -114,6 +114,7 @@ struct isp1760_qh {
u32 toggle;
u32 ping;
int slot;
+ int tt_buffer_dirty; /* See USB2.0 spec section 11.17.5 */
};
struct urb_listitem {
@@ -845,6 +846,10 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
return;
}
+ /* Make sure this endpoint's TT buffer is clean before queueing ptds */
+ if (qh->tt_buffer_dirty)
+ return;
+
if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd,
qtd_list)->urb->pipe)) {
ptd_offset = INT_PTD_OFFSET;
@@ -1182,6 +1187,15 @@ static void handle_done_ptds(struct usb_hcd *hcd)
case PTD_STATE_URB_RETIRE:
qtd->status = QTD_RETIRE;
+ if ((qtd->urb->dev->speed != USB_SPEED_HIGH) &&
+ (qtd->urb->status != -EPIPE) &&
+ (qtd->urb->status != -EREMOTEIO)) {
+ qh->tt_buffer_dirty = 1;
+ if (usb_hub_clear_tt_buffer(qtd->urb))
+ /* Clear failed; let's hope things work
+ anyway */
+ qh->tt_buffer_dirty = 0;
+ }
qtd = NULL;
qh->toggle = 0;
qh->ping = 0;
@@ -1611,6 +1625,41 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb,
qh->slot = -1;
}
+/*
+ * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing
+ * any active transfer belonging to the urb in the process.
+ */
+static void dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh,
+ struct isp1760_qtd *qtd)
+{
+ struct urb *urb;
+ int urb_was_running;
+
+ urb = qtd->urb;
+ urb_was_running = 0;
+ list_for_each_entry_from(qtd, &qh->qtd_list, qtd_list) {
+ if (qtd->urb != urb)
+ break;
+
+ if (qtd->status >= QTD_XFER_STARTED)
+ urb_was_running = 1;
+ if (last_qtd_of_urb(qtd, qh) &&
+ (qtd->status >= QTD_XFER_COMPLETE))
+ urb_was_running = 0;
+
+ if (qtd->status == QTD_XFER_STARTED)
+ kill_transfer(hcd, urb, qh);
+ qtd->status = QTD_RETIRE;
+ }
+
+ if ((urb->dev->speed != USB_SPEED_HIGH) && urb_was_running) {
+ qh->tt_buffer_dirty = 1;
+ if (usb_hub_clear_tt_buffer(urb))
+ /* Clear failed; let's hope things work anyway */
+ qh->tt_buffer_dirty = 0;
+ }
+}
+
static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
int status)
{
@@ -1633,9 +1682,8 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
list_for_each_entry(qtd, &qh->qtd_list, qtd_list)
if (qtd->urb == urb) {
- if (qtd->status == QTD_XFER_STARTED)
- kill_transfer(hcd, urb, qh);
- qtd->status = QTD_RETIRE;
+ dequeue_urb_from_qtd(hcd, qh, qtd);
+ break;
}
urb->status = status;
@@ -1660,12 +1708,11 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd,
if (!qh)
goto out;
- list_for_each_entry(qtd, &qh->qtd_list, qtd_list) {
- if (qtd->status == QTD_XFER_STARTED)
- kill_transfer(hcd, qtd->urb, qh);
- qtd->status = QTD_RETIRE;
- qtd->urb->status = -ECONNRESET;
- }
+ list_for_each_entry(qtd, &qh->qtd_list, qtd_list)
+ if (qtd->status != QTD_RETIRE) {
+ dequeue_urb_from_qtd(hcd, qh, qtd);
+ qtd->urb->status = -ECONNRESET;
+ }
ep->hcpriv = NULL;
/* Cannot free qh here since it will be parsed by schedule_ptds() */
@@ -2088,6 +2135,23 @@ static void isp1760_shutdown(struct usb_hcd *hcd)
reg_write32(hcd->regs, HC_USBCMD, command);
}
+static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct isp1760_hcd *priv = hcd_to_priv(hcd);
+ struct isp1760_qh *qh = ep->hcpriv;
+ unsigned long spinflags;
+
+ if (!qh)
+ return;
+
+ spin_lock_irqsave(&priv->lock, spinflags);
+ qh->tt_buffer_dirty = 0;
+ schedule_ptds(hcd);
+ spin_unlock_irqrestore(&priv->lock, spinflags);
+}
+
+
static const struct hc_driver isp1760_hc_driver = {
.description = "isp1760-hcd",
.product_desc = "NXP ISP1760 USB Host Controller",
@@ -2104,6 +2168,7 @@ static const struct hc_driver isp1760_hc_driver = {
.get_frame_number = isp1760_get_frame,
.hub_status_data = isp1760_hub_status_data,
.hub_control = isp1760_hub_control,
+ .clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete,
};
int __init init_kmem_once(void)