From f5960b698eb50a39fce1a066dc19a6a5a1148e16 Mon Sep 17 00:00:00 2001 From: Matt Evans Date: Wed, 1 Jun 2011 10:22:55 +1000 Subject: xhci: Remove some unnecessary casts and tidy some endian swap code Some of the recently-added cpu_to_leXX and leXX_to_cpu made things somewhat messy; this patch neatens some of these areas, removing unnecessary casts in those parts also. In some places (where Y & Z are constants) a comparison of (leXX_to_cpu(X) & Y) == Z has been replaced with (X & cpu_to_leXX(Y)) == cpu_to_leXX(Z). The endian reversal of the constants should wash out at compile time. Signed-off-by: Matt Evans Signed-off-by: Sarah Sharp --- drivers/usb/host/xhci-dbg.c | 22 +++++++++++----------- drivers/usb/host/xhci-mem.c | 26 +++++++++++++------------- drivers/usb/host/xhci-ring.c | 42 +++++++++++++++++------------------------- drivers/usb/host/xhci.c | 10 ++++------ drivers/usb/host/xhci.h | 7 +++++++ 5 files changed, 52 insertions(+), 55 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 2e0486178dbe..17d3e359ca62 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -266,11 +266,11 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) xhci_dbg(xhci, "Interrupter target = 0x%x\n", GET_INTR_TARGET(le32_to_cpu(trb->link.intr_target))); xhci_dbg(xhci, "Cycle bit = %u\n", - (unsigned int) (le32_to_cpu(trb->link.control) & TRB_CYCLE)); + le32_to_cpu(trb->link.control) & TRB_CYCLE); xhci_dbg(xhci, "Toggle cycle bit = %u\n", - (unsigned int) (le32_to_cpu(trb->link.control) & LINK_TOGGLE)); + le32_to_cpu(trb->link.control) & LINK_TOGGLE); xhci_dbg(xhci, "No Snoop bit = %u\n", - (unsigned int) (le32_to_cpu(trb->link.control) & TRB_NO_SNOOP)); + le32_to_cpu(trb->link.control) & TRB_NO_SNOOP); break; case TRB_TYPE(TRB_TRANSFER): address = le64_to_cpu(trb->trans_event.buffer); @@ -284,9 +284,9 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) address = le64_to_cpu(trb->event_cmd.cmd_trb); xhci_dbg(xhci, "Command TRB pointer = %llu\n", address); xhci_dbg(xhci, "Completion status = %u\n", - (unsigned int) GET_COMP_CODE(le32_to_cpu(trb->event_cmd.status))); + GET_COMP_CODE(le32_to_cpu(trb->event_cmd.status))); xhci_dbg(xhci, "Flags = 0x%x\n", - (unsigned int) le32_to_cpu(trb->event_cmd.flags)); + le32_to_cpu(trb->event_cmd.flags)); break; default: xhci_dbg(xhci, "Unknown TRB with TRB type ID %u\n", @@ -318,10 +318,10 @@ void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg) for (i = 0; i < TRBS_PER_SEGMENT; ++i) { trb = &seg->trbs[i]; xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", addr, - (u32)lower_32_bits(le64_to_cpu(trb->link.segment_ptr)), - (u32)upper_32_bits(le64_to_cpu(trb->link.segment_ptr)), - (unsigned int) le32_to_cpu(trb->link.intr_target), - (unsigned int) le32_to_cpu(trb->link.control)); + lower_32_bits(le64_to_cpu(trb->link.segment_ptr)), + upper_32_bits(le64_to_cpu(trb->link.segment_ptr)), + le32_to_cpu(trb->link.intr_target), + le32_to_cpu(trb->link.control)); addr += sizeof(*trb); } } @@ -402,8 +402,8 @@ void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) addr, lower_32_bits(le64_to_cpu(entry->seg_addr)), upper_32_bits(le64_to_cpu(entry->seg_addr)), - (unsigned int) le32_to_cpu(entry->seg_size), - (unsigned int) le32_to_cpu(entry->rsvd)); + le32_to_cpu(entry->seg_size), + le32_to_cpu(entry->rsvd)); addr += sizeof(*entry); } } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 26caba4c1950..596d8fbb9e18 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -89,8 +89,8 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, return; prev->next = next; if (link_trbs) { - prev->trbs[TRBS_PER_SEGMENT-1].link. - segment_ptr = cpu_to_le64(next->dma); + prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = + cpu_to_le64(next->dma); /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); @@ -187,8 +187,8 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, if (link_trbs) { /* See section 4.9.2.1 and 6.4.4.1 */ - prev->trbs[TRBS_PER_SEGMENT-1].link. - control |= cpu_to_le32(LINK_TOGGLE); + prev->trbs[TRBS_PER_SEGMENT-1].link.control |= + cpu_to_le32(LINK_TOGGLE); xhci_dbg(xhci, "Wrote link toggle flag to" " segment %p (virtual), 0x%llx (DMA)\n", prev, (unsigned long long)prev->dma); @@ -549,8 +549,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, addr = cur_ring->first_seg->dma | SCT_FOR_CTX(SCT_PRI_TR) | cur_ring->cycle_state; - stream_info->stream_ctx_array[cur_stream]. - stream_ring = cpu_to_le64(addr); + stream_info->stream_ctx_array[cur_stream].stream_ring = + cpu_to_le64(addr); xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n", cur_stream, (unsigned long long) addr); @@ -786,7 +786,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n", slot_id, &xhci->dcbaa->dev_context_ptrs[slot_id], - (unsigned long long) le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id])); + le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id])); return 1; fail: @@ -890,19 +890,19 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); /* 3) Only the control endpoint is valid - one endpoint context */ - slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | (u32) udev->route); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | udev->route); switch (udev->speed) { case USB_SPEED_SUPER: - slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_SS); + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_SS); break; case USB_SPEED_HIGH: - slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_HS); + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_HS); break; case USB_SPEED_FULL: - slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_FS); + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_FS); break; case USB_SPEED_LOW: - slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_LS); + slot_ctx->dev_info |= cpu_to_le32(SLOT_SPEED_LS); break; case USB_SPEED_WIRELESS: xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); @@ -916,7 +916,7 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud port_num = xhci_find_real_port_number(xhci, udev); if (!port_num) return -EINVAL; - slot_ctx->dev_info2 |= cpu_to_le32((u32) ROOT_HUB_PORT(port_num)); + slot_ctx->dev_info2 |= cpu_to_le32(ROOT_HUB_PORT(port_num)); /* Set the port number in the virtual_device to the faked port number */ for (top_dev = udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index cc1485bfed38..4b40e4c95f94 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -113,15 +113,13 @@ static int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, if (ring == xhci->event_ring) return trb == &seg->trbs[TRBS_PER_SEGMENT]; else - return (le32_to_cpu(trb->link.control) & TRB_TYPE_BITMASK) - == TRB_TYPE(TRB_LINK); + return TRB_TYPE_LINK_LE32(trb->link.control); } static int enqueue_is_link_trb(struct xhci_ring *ring) { struct xhci_link_trb *link = &ring->enqueue->link; - return ((le32_to_cpu(link->control) & TRB_TYPE_BITMASK) == - TRB_TYPE(TRB_LINK)); + return TRB_TYPE_LINK_LE32(link->control); } /* Updates trb to point to the next TRB in the ring, and updates seg if the next @@ -372,7 +370,7 @@ static struct xhci_segment *find_trb_seg( while (cur_seg->trbs > trb || &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) { generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic; - if (le32_to_cpu(generic_trb->field[3]) & LINK_TOGGLE) + if (generic_trb->field[3] & cpu_to_le32(LINK_TOGGLE)) *cycle_state ^= 0x1; cur_seg = cur_seg->next; if (cur_seg == start_seg) @@ -489,8 +487,8 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, } trb = &state->new_deq_ptr->generic; - if ((le32_to_cpu(trb->field[3]) & TRB_TYPE_BITMASK) == - TRB_TYPE(TRB_LINK) && (le32_to_cpu(trb->field[3]) & LINK_TOGGLE)) + if (TRB_TYPE_LINK_LE32(trb->field[3]) && + (trb->field[3] & cpu_to_le32(LINK_TOGGLE))) state->new_cycle_state ^= 0x1; next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr); @@ -525,8 +523,7 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, for (cur_seg = cur_td->start_seg, cur_trb = cur_td->first_trb; true; next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) - == TRB_TYPE(TRB_LINK)) { + if (TRB_TYPE_LINK_LE32(cur_trb->generic.field[3])) { /* Unchain any chained Link TRBs, but * leave the pointers intact. */ @@ -1000,7 +997,7 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci, * but we don't care. */ xhci_dbg(xhci, "Ignoring reset ep completion code of %u\n", - (unsigned int) GET_COMP_CODE(le32_to_cpu(event->status))); + GET_COMP_CODE(le32_to_cpu(event->status))); /* HW with the reset endpoint quirk needs to have a configure endpoint * command complete before the endpoint can be used. Queue that here @@ -1458,7 +1455,8 @@ static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci, * endpoint anyway. Check if a babble halted the * endpoint. */ - if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == EP_STATE_HALTED) + if ((ep_ctx->ep_info & cpu_to_le32(EP_STATE_MASK)) == + cpu_to_le32(EP_STATE_HALTED)) return 1; return 0; @@ -1752,10 +1750,8 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; cur_trb != event_trb; next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((le32_to_cpu(cur_trb->generic.field[3]) & - TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && - (le32_to_cpu(cur_trb->generic.field[3]) & - TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) + if (!TRB_TYPE_NOOP_LE32(cur_trb->generic.field[3]) && + !TRB_TYPE_LINK_LE32(cur_trb->generic.field[3])) len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])); } len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - @@ -1888,10 +1884,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; cur_trb != event_trb; next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((le32_to_cpu(cur_trb->generic.field[3]) & - TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && - (le32_to_cpu(cur_trb->generic.field[3]) & - TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) + if (!TRB_TYPE_NOOP_LE32(cur_trb->generic.field[3]) && + !TRB_TYPE_LINK_LE32(cur_trb->generic.field[3])) td->urb->actual_length += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])); } @@ -2046,8 +2040,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), ep_index); xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", - (unsigned int) (le32_to_cpu(event->flags) - & TRB_TYPE_BITMASK)>>10); + (le32_to_cpu(event->flags) & + TRB_TYPE_BITMASK)>>10); xhci_print_trb_offsets(xhci, (union xhci_trb *) event); if (ep->skip) { ep->skip = false; @@ -2104,9 +2098,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, * corresponding TD has been cancelled. Just ignore * the TD. */ - if ((le32_to_cpu(event_trb->generic.field[3]) - & TRB_TYPE_BITMASK) - == TRB_TYPE(TRB_TR_NOOP)) { + if (TRB_TYPE_NOOP_LE32(event_trb->generic.field[3])) { xhci_dbg(xhci, "event_trb is a no-op TRB. Skip it\n"); goto cleanup; @@ -2432,7 +2424,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, next->link.control |= cpu_to_le32(TRB_CHAIN); wmb(); - next->link.control ^= cpu_to_le32((u32) TRB_CYCLE); + next->link.control ^= cpu_to_le32(TRB_CYCLE); /* Toggle the cycle bit after the last ring segment. */ if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) { diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index d9660eb97eb9..743cf80debb1 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1333,8 +1333,8 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, /* If the HC already knows the endpoint is disabled, * or the HCD has noted it is disabled, ignore this request */ - if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == - EP_STATE_DISABLED || + if (((ep_ctx->ep_info & cpu_to_le32(EP_STATE_MASK)) == + cpu_to_le32(EP_STATE_DISABLED)) || le32_to_cpu(ctrl_ctx->drop_flags) & xhci_get_endpoint_flag(&ep->desc)) { xhci_warn(xhci, "xHCI %s called with disabled ep %p\n", @@ -1725,8 +1725,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, /* Enqueue pointer can be left pointing to the link TRB, * we must handle that */ - if ((le32_to_cpu(command->command_trb->link.control) - & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) + if (TRB_TYPE_LINK_LE32(command->command_trb->link.control)) command->command_trb = xhci->cmd_ring->enq_seg->next->trbs; @@ -2519,8 +2518,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) /* Enqueue pointer can be left pointing to the link TRB, * we must handle that */ - if ((le32_to_cpu(reset_device_cmd->command_trb->link.control) - & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) + if (TRB_TYPE_LINK_LE32(reset_device_cmd->command_trb->link.control)) reset_device_cmd->command_trb = xhci->cmd_ring->enq_seg->next->trbs; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ac0196e7fcf1..f9098a24d38b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1065,6 +1065,13 @@ union xhci_trb { /* Get NEC firmware revision. */ #define TRB_NEC_GET_FW 49 +#define TRB_TYPE_LINK(x) (((x) & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) +/* Above, but for __le32 types -- can avoid work by swapping constants: */ +#define TRB_TYPE_LINK_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \ + cpu_to_le32(TRB_TYPE(TRB_LINK))) +#define TRB_TYPE_NOOP_LE32(x) (((x) & cpu_to_le32(TRB_TYPE_BITMASK)) == \ + cpu_to_le32(TRB_TYPE(TRB_TR_NOOP))) + #define NEC_FW_MINOR(p) (((p) >> 0) & 0xff) #define NEC_FW_MAJOR(p) (((p) >> 8) & 0xff) -- cgit v1.2.3 From 1acb30ef28c4cb08bc6f7bc7f68ee7eebd4b9c84 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 20 May 2011 20:48:33 +0900 Subject: USB: ehci-s5p: add PM support This patch adds power management support such as suspend and resume functions. Signed-off-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-s5p.c | 95 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index e3374c8f7b3f..b3958b3d3163 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -189,6 +189,100 @@ static void s5p_ehci_shutdown(struct platform_device *pdev) hcd->driver->shutdown(hcd); } +#ifdef CONFIG_PM +static int s5p_ehci_suspend(struct device *dev) +{ + struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); + struct usb_hcd *hcd = s5p_ehci->hcd; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct platform_device *pdev = to_platform_device(dev); + struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; + unsigned long flags; + int rc = 0; + + if (time_before(jiffies, ehci->next_statechange)) + msleep(20); + + /* + * Root hub was already suspended. Disable irq emission and + * mark HW unaccessible. The PM and USB cores make sure that + * the root hub is either suspended or stopped. + */ + ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); + spin_lock_irqsave(&ehci->lock, flags); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + (void)ehci_readl(ehci, &ehci->regs->intr_enable); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + spin_unlock_irqrestore(&ehci->lock, flags); + + if (pdata && pdata->phy_exit) + pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + + return rc; +} + +static int s5p_ehci_resume(struct device *dev) +{ + struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); + struct usb_hcd *hcd = s5p_ehci->hcd; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct platform_device *pdev = to_platform_device(dev); + struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; + + if (pdata && pdata->phy_init) + pdata->phy_init(pdev, S5P_USB_PHY_HOST); + + if (time_before(jiffies, ehci->next_statechange)) + msleep(100); + + /* Mark hardware accessible again as we are out of D3 state by now */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { + int mask = INTR_MASK; + + ehci_prepare_ports_for_controller_resume(ehci); + if (!hcd->self.root_hub->do_remote_wakeup) + mask &= ~STS_PCD; + ehci_writel(ehci, mask, &ehci->regs->intr_enable); + ehci_readl(ehci, &ehci->regs->intr_enable); + return 0; + } + + usb_root_hub_lost_power(hcd->self.root_hub); + + (void) ehci_halt(ehci); + (void) ehci_reset(ehci); + + /* emptying the schedule aborts any urbs */ + spin_lock_irq(&ehci->lock); + if (ehci->reclaim) + end_unlink_async(ehci); + ehci_work(ehci); + spin_unlock_irq(&ehci->lock); + + ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ + + /* here we "know" root ports should always stay powered */ + ehci_port_power(ehci, 1); + + hcd->state = HC_STATE_SUSPENDED; + + return 0; +} +#else +#define s5p_ehci_suspend NULL +#define s5p_ehci_resume NULL +#endif + +static const struct dev_pm_ops s5p_ehci_pm_ops = { + .suspend = s5p_ehci_suspend, + .resume = s5p_ehci_resume, +}; + static struct platform_driver s5p_ehci_driver = { .probe = s5p_ehci_probe, .remove = __devexit_p(s5p_ehci_remove), @@ -196,6 +290,7 @@ static struct platform_driver s5p_ehci_driver = { .driver = { .name = "s5p-ehci", .owner = THIS_MODULE, + .pm = &s5p_ehci_pm_ops, } }; -- cgit v1.2.3 From cc55687124426e3a6a5301780c4e6bb36bb531fd Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Tue, 31 May 2011 23:00:10 +0100 Subject: ehci-hcd: remove EOL from MODULE_PARM_DESC for 'hird' option There is no need to have a "\n" on a MODULE_PARM_DESC, remove it Signed-off-by: Niels de Vos Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index b435ed67dd5c..e18862c5b059 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -110,7 +110,7 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); /* for link power management(LPM) feature */ static unsigned int hird; module_param(hird, int, S_IRUGO); -MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n"); +MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); #define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) -- cgit v1.2.3 From 2cb30bb11963907acdd69f77e77b3fabb16cfa62 Mon Sep 17 00:00:00 2001 From: Matthieu CASTET Date: Sat, 2 Jul 2011 20:59:46 +0200 Subject: ehci-msm : use ehci_setup Signed-off-by: Matthieu CASTET Tested-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-msm.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index b5a0bf649c95..592d5f76803e 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -40,27 +40,9 @@ static int ehci_msm_reset(struct usb_hcd *hcd) int retval; ehci->caps = USB_CAPLENGTH; - ehci->regs = USB_CAPLENGTH + - HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); - - /* cache the data to minimize the chip reads*/ - ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - hcd->has_tt = 1; - ehci->sbrn = HCD_USB2; - - retval = ehci_halt(ehci); - if (retval) - return retval; - - /* data structure init */ - retval = ehci_init(hcd); - if (retval) - return retval; - retval = ehci_reset(ehci); + retval = ehci_setup(hcd); if (retval) return retval; -- cgit v1.2.3 From 4c67045bfc2c14a1d3c6040e80eb4a62946282dd Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Sun, 3 Jul 2011 20:36:56 +0400 Subject: USB: EHCI: Move sysfs related bits into ehci-sysfs.c The only sysfs attr implemented so far is "companion" from ehci-hub.c, but in the next patch we are going to add another sysfs file, so prior to that let's structure things and move already-in-there sysfs code to separate file. NOTE: All the code I'm moving into this new file was written by Alan Stern (in 57e06c11 "EHCI: force high-speed devices to run at full speed"; Jan 16 2007), that's why I'm putting Copyright (C) 2007 by Alan Stern there after explicit request from the author. Signed-off-by: Kirill Smelkov Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 5 ++- drivers/usb/host/ehci-hub.c | 75 ---------------------------------- drivers/usb/host/ehci-sysfs.c | 94 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 77 deletions(-) create mode 100644 drivers/usb/host/ehci-sysfs.c (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index e18862c5b059..8306155de9e5 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -336,6 +336,7 @@ static void ehci_work(struct ehci_hcd *ehci); #include "ehci-mem.c" #include "ehci-q.c" #include "ehci-sched.c" +#include "ehci-sysfs.c" /*-------------------------------------------------------------------------*/ @@ -520,7 +521,7 @@ static void ehci_stop (struct usb_hcd *hcd) ehci_reset (ehci); spin_unlock_irq(&ehci->lock); - remove_companion_file(ehci); + remove_sysfs_files(ehci); remove_debug_files (ehci); /* root hub is shut down separately (first, when possible) */ @@ -754,7 +755,7 @@ static int ehci_run (struct usb_hcd *hcd) * since the class device isn't created that early. */ create_debug_files(ehci); - create_companion_file(ehci); + create_sysfs_files(ehci); return 0; } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index ea6184bf48d0..d9e8d713f485 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -471,29 +471,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ -/* Display the ports dedicated to the companion controller */ -static ssize_t show_companion(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct ehci_hcd *ehci; - int nports, index, n; - int count = PAGE_SIZE; - char *ptr = buf; - - ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); - nports = HCS_N_PORTS(ehci->hcs_params); - - for (index = 0; index < nports; ++index) { - if (test_bit(index, &ehci->companion_ports)) { - n = scnprintf(ptr, count, "%d\n", index + 1); - ptr += n; - count -= n; - } - } - return ptr - buf; -} - /* * Sets the owner of a port */ @@ -528,58 +505,6 @@ static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner) } } -/* - * Dedicate or undedicate a port to the companion controller. - * Syntax is "[-]portnum", where a leading '-' sign means - * return control of the port to the EHCI controller. - */ -static ssize_t store_companion(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct ehci_hcd *ehci; - int portnum, new_owner; - - ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); - new_owner = PORT_OWNER; /* Owned by companion */ - if (sscanf(buf, "%d", &portnum) != 1) - return -EINVAL; - if (portnum < 0) { - portnum = - portnum; - new_owner = 0; /* Owned by EHCI */ - } - if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) - return -ENOENT; - portnum--; - if (new_owner) - set_bit(portnum, &ehci->companion_ports); - else - clear_bit(portnum, &ehci->companion_ports); - set_owner(ehci, portnum, new_owner); - return count; -} -static DEVICE_ATTR(companion, 0644, show_companion, store_companion); - -static inline int create_companion_file(struct ehci_hcd *ehci) -{ - int i = 0; - - /* with integrated TT there is no companion! */ - if (!ehci_is_TDI(ehci)) - i = device_create_file(ehci_to_hcd(ehci)->self.controller, - &dev_attr_companion); - return i; -} - -static inline void remove_companion_file(struct ehci_hcd *ehci) -{ - /* with integrated TT there is no companion! */ - if (!ehci_is_TDI(ehci)) - device_remove_file(ehci_to_hcd(ehci)->self.controller, - &dev_attr_companion); -} - - /*-------------------------------------------------------------------------*/ static int check_reset_complete ( diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c new file mode 100644 index 000000000000..29824a98aec9 --- /dev/null +++ b/drivers/usb/host/ehci-sysfs.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2007 by Alan Stern + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* this file is part of ehci-hcd.c */ + + +/* Display the ports dedicated to the companion controller */ +static ssize_t show_companion(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ehci_hcd *ehci; + int nports, index, n; + int count = PAGE_SIZE; + char *ptr = buf; + + ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); + nports = HCS_N_PORTS(ehci->hcs_params); + + for (index = 0; index < nports; ++index) { + if (test_bit(index, &ehci->companion_ports)) { + n = scnprintf(ptr, count, "%d\n", index + 1); + ptr += n; + count -= n; + } + } + return ptr - buf; +} + +/* + * Dedicate or undedicate a port to the companion controller. + * Syntax is "[-]portnum", where a leading '-' sign means + * return control of the port to the EHCI controller. + */ +static ssize_t store_companion(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ehci_hcd *ehci; + int portnum, new_owner; + + ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); + new_owner = PORT_OWNER; /* Owned by companion */ + if (sscanf(buf, "%d", &portnum) != 1) + return -EINVAL; + if (portnum < 0) { + portnum = - portnum; + new_owner = 0; /* Owned by EHCI */ + } + if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) + return -ENOENT; + portnum--; + if (new_owner) + set_bit(portnum, &ehci->companion_ports); + else + clear_bit(portnum, &ehci->companion_ports); + set_owner(ehci, portnum, new_owner); + return count; +} +static DEVICE_ATTR(companion, 0644, show_companion, store_companion); + +static inline int create_sysfs_files(struct ehci_hcd *ehci) +{ + int i = 0; + + /* with integrated TT there is no companion! */ + if (!ehci_is_TDI(ehci)) + i = device_create_file(ehci_to_hcd(ehci)->self.controller, + &dev_attr_companion); + return i; +} + +static inline void remove_sysfs_files(struct ehci_hcd *ehci) +{ + /* with integrated TT there is no companion! */ + if (!ehci_is_TDI(ehci)) + device_remove_file(ehci_to_hcd(ehci)->self.controller, + &dev_attr_companion); +} -- cgit v1.2.3 From cc62a7eb6396e8be95b9a30053ed09191818b99b Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Sun, 3 Jul 2011 20:36:57 +0400 Subject: USB: EHCI: Allow users to override 80% max periodic bandwidth There are cases, when 80% max isochronous bandwidth is too limiting. For example I have two USB video capture cards which stream uncompressed video, and to stream full NTSC + PAL videos we'd need NTSC 640x480 YUV422 @30fps ~17.6 MB/s PAL 720x576 YUV422 @25fps ~19.7 MB/s isoc bandwidth. Now, due to limited alt settings in capture devices NTSC one ends up streaming with max_pkt_size=2688 and PAL with max_pkt_size=2892, both with interval=1. In terms of microframe time allocation this gives NTSC ~53us PAL ~57us and together ~110us > 100us == 80% of 125us uframe time. So those two devices can't work together simultaneously because the'd over allocate isochronous bandwidth. 80% seemed a bit arbitrary to me, and I've tried to raise it to 90% and both devices started to work together, so I though sometimes it would be a good idea for users to override hardcoded default of max 80% isoc bandwidth. After all, isn't it a user who should decide how to load the bus? If I can live with 10% or even 5% bulk bandwidth that should be ok. I'm a USB newcomer, but that 80% set in stone by USB 2.0 specification seems to be chosen pretty arbitrary to me, just to serve as a reasonable default. NOTE 1 ~~~~~~ for two streams with max_pkt_size=3072 (worst case) both time allocation would be 60us+60us=120us which is 96% periodic bandwidth leaving 4% for bulk and control. Alan Stern suggested that bulk then would be problematic (less than 300*8 bittimes left per microframe), but I think that is still enough for control traffic. NOTE 2 ~~~~~~ Sarah Sharp expressed concern that maxing out periodic bandwidth could lead to vendor-specific hardware bugs on host controllers, because > It's entirely possible that you'll run into > vendor-specific bugs if you try to pack the schedule with isochronous > transfers. I don't think any hardware designer would seriously test or > validate their hardware with a schedule that is basically a violation of > the USB bus spec (more than 80% for periodic transfers). So far I've only tested this patch on my HP Mini 5103 with N10 chipset kirr@mini:~$ lspci 00:00.0 Host bridge: Intel Corporation N10 Family DMI Bridge 00:02.0 VGA compatible controller: Intel Corporation N10 Family Integrated Graphics Controller 00:02.1 Display controller: Intel Corporation N10 Family Integrated Graphics Controller 00:1b.0 Audio device: Intel Corporation N10/ICH 7 Family High Definition Audio Controller (rev 02) 00:1c.0 PCI bridge: Intel Corporation N10/ICH 7 Family PCI Express Port 1 (rev 02) 00:1c.3 PCI bridge: Intel Corporation N10/ICH 7 Family PCI Express Port 4 (rev 02) 00:1d.0 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #1 (rev 02) 00:1d.1 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #2 (rev 02) 00:1d.2 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #3 (rev 02) 00:1d.3 USB Controller: Intel Corporation N10/ICH 7 Family USB UHCI Controller #4 (rev 02) 00:1d.7 USB Controller: Intel Corporation N10/ICH 7 Family USB2 EHCI Controller (rev 02) 00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev e2) 00:1f.0 ISA bridge: Intel Corporation NM10 Family LPC Controller (rev 02) 00:1f.2 SATA controller: Intel Corporation N10/ICH7 Family SATA AHCI Controller (rev 02) 01:00.0 Network controller: Broadcom Corporation BCM4313 802.11b/g/n Wireless LAN Controller (rev 01) 02:00.0 Ethernet controller: Marvell Technology Group Ltd. 88E8059 PCI-E Gigabit Ethernet Controller (rev 11) and the system works stable with 110us/uframe (~88%) isoc bandwith allocated for above-mentioned isochronous transfers. NOTE 3 ~~~~~~ This feature is off by default. I mean max periodic bandwidth is set to 100us/uframe by default exactly as it was before the patch. So only those of us who need the extreme settings are taking the risk - normal users who do not alter uframe_periodic_max sysfs attribute should not see any change at all. NOTE 4 ~~~~~~ I've tried to update documentation in Documentation/ABI/ thoroughly, but only "TBD" was put into Documentation/usb/ehci.txt -- the text there seems to be outdated and much needing refreshing, before it could be amended. Cc: Sarah Sharp Signed-off-by: Kirill Smelkov Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 6 +++ drivers/usb/host/ehci-sched.c | 17 +++---- drivers/usb/host/ehci-sysfs.c | 104 ++++++++++++++++++++++++++++++++++++++++-- drivers/usb/host/ehci.h | 2 + 4 files changed, 115 insertions(+), 14 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8306155de9e5..4ee62bef3723 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -571,6 +571,12 @@ static int ehci_init(struct usb_hcd *hcd) hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); + /* + * by default set standard 80% (== 100 usec/uframe) max periodic + * bandwidth as required by USB 2.0 + */ + ehci->uframe_periodic_max = 100; + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 6c9fbe352f73..2abf8543f083 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -172,7 +172,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) } } #ifdef DEBUG - if (usecs > 100) + if (usecs > ehci->uframe_periodic_max) ehci_err (ehci, "uframe %d sched overrun: %d usecs\n", frame * 8 + uframe, usecs); #endif @@ -709,11 +709,8 @@ static int check_period ( if (uframe >= 8) return 0; - /* - * 80% periodic == 100 usec/uframe available - * convert "usecs we need" to "max already claimed" - */ - usecs = 100 - usecs; + /* convert "usecs we need" to "max already claimed" */ + usecs = ehci->uframe_periodic_max - usecs; /* we "know" 2 and 4 uframe intervals were rejected; so * for period 0, check _every_ microframe in the schedule. @@ -1286,9 +1283,9 @@ itd_slot_ok ( { uframe %= period; do { - /* can't commit more than 80% periodic == 100 usec */ + /* can't commit more than uframe_periodic_max usec */ if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7) - > (100 - usecs)) + > (ehci->uframe_periodic_max - usecs)) return 0; /* we know urb->interval is 2^N uframes */ @@ -1345,7 +1342,7 @@ sitd_slot_ok ( #endif /* check starts (OUT uses more than one) */ - max_used = 100 - stream->usecs; + max_used = ehci->uframe_periodic_max - stream->usecs; for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) { if (periodic_usecs (ehci, frame, uf) > max_used) return 0; @@ -1354,7 +1351,7 @@ sitd_slot_ok ( /* for IN, check CSPLIT */ if (stream->c_usecs) { uf = uframe & 7; - max_used = 100 - stream->c_usecs; + max_used = ehci->uframe_periodic_max - stream->c_usecs; do { tmp = 1 << uf; tmp <<= 8; diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c index 29824a98aec9..14ced00ba220 100644 --- a/drivers/usb/host/ehci-sysfs.c +++ b/drivers/usb/host/ehci-sysfs.c @@ -74,21 +74,117 @@ static ssize_t store_companion(struct device *dev, } static DEVICE_ATTR(companion, 0644, show_companion, store_companion); + +/* + * Display / Set uframe_periodic_max + */ +static ssize_t show_uframe_periodic_max(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ehci_hcd *ehci; + int n; + + ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); + n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max); + return n; +} + + +static ssize_t store_uframe_periodic_max(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ehci_hcd *ehci; + unsigned uframe_periodic_max; + unsigned frame, uframe; + unsigned short allocated_max; + unsigned long flags; + ssize_t ret; + + ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); + if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) + return -EINVAL; + + if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { + ehci_info(ehci, "rejecting invalid request for " + "uframe_periodic_max=%u\n", uframe_periodic_max); + return -EINVAL; + } + + ret = -EINVAL; + + /* + * lock, so that our checking does not race with possible periodic + * bandwidth allocation through submitting new urbs. + */ + spin_lock_irqsave (&ehci->lock, flags); + + /* + * for request to decrease max periodic bandwidth, we have to check + * every microframe in the schedule to see whether the decrease is + * possible. + */ + if (uframe_periodic_max < ehci->uframe_periodic_max) { + allocated_max = 0; + + for (frame = 0; frame < ehci->periodic_size; ++frame) + for (uframe = 0; uframe < 7; ++uframe) + allocated_max = max(allocated_max, + periodic_usecs (ehci, frame, uframe)); + + if (allocated_max > uframe_periodic_max) { + ehci_info(ehci, + "cannot decrease uframe_periodic_max becase " + "periodic bandwidth is already allocated " + "(%u > %u)\n", + allocated_max, uframe_periodic_max); + goto out_unlock; + } + } + + /* increasing is always ok */ + + ehci_info(ehci, "setting max periodic bandwidth to %u%% " + "(== %u usec/uframe)\n", + 100*uframe_periodic_max/125, uframe_periodic_max); + + if (uframe_periodic_max != 100) + ehci_warn(ehci, "max periodic bandwidth set is non-standard\n"); + + ehci->uframe_periodic_max = uframe_periodic_max; + ret = count; + +out_unlock: + spin_unlock_irqrestore (&ehci->lock, flags); + return ret; +} +static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max); + + static inline int create_sysfs_files(struct ehci_hcd *ehci) { + struct device *controller = ehci_to_hcd(ehci)->self.controller; int i = 0; /* with integrated TT there is no companion! */ if (!ehci_is_TDI(ehci)) - i = device_create_file(ehci_to_hcd(ehci)->self.controller, - &dev_attr_companion); + i = device_create_file(controller, &dev_attr_companion); + if (i) + goto out; + + i = device_create_file(controller, &dev_attr_uframe_periodic_max); +out: return i; } static inline void remove_sysfs_files(struct ehci_hcd *ehci) { + struct device *controller = ehci_to_hcd(ehci)->self.controller; + /* with integrated TT there is no companion! */ if (!ehci_is_TDI(ehci)) - device_remove_file(ehci_to_hcd(ehci)->self.controller, - &dev_attr_companion); + device_remove_file(controller, &dev_attr_companion); + + device_remove_file(controller, &dev_attr_uframe_periodic_max); } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bd6ff489baf9..fa3129fe1ee0 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -87,6 +87,8 @@ struct ehci_hcd { /* one per controller */ union ehci_shadow *pshadow; /* mirror hw periodic table */ int next_uframe; /* scan periodic, start here */ unsigned periodic_sched; /* periodic activity count */ + unsigned uframe_periodic_max; /* max periodic time per uframe */ + /* list of itds & sitds completed while clock_frame was still active */ struct list_head cached_itd_list; -- cgit v1.2.3 From 03c75362181b0b1d6a330e7cf8def10ba988dfbe Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Tue, 5 Jul 2011 16:38:45 +0200 Subject: ehci: refactor pci quirk to use standard dmi_check_system method In commit 3610ea5397b80822e417aaa0e706fd803fb05680 (ehci: workaround for pci quirk timeout on ExoPC), a workaround was added to skip the negociation for the handoff of the EHCI controller. Refactor the DMI detection code to use standard dmi_check_system function. Signed-off-by: Anisse Astier Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/pci-quirks.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index fd930618c28f..04600a9c6e4a 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -507,20 +507,27 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) iounmap(base); } +static const struct dmi_system_id __initconst ehci_dmi_nohandoff_table[] = { + { + /* Pegatron Lucid (ExoPC) */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "EXOPG06411"), + DMI_MATCH(DMI_BIOS_VERSION, "Lucid-CE-133"), + }, + }, + { } +}; + static void __devinit ehci_bios_handoff(struct pci_dev *pdev, void __iomem *op_reg_base, u32 cap, u8 offset) { int try_handoff = 1, tried_handoff = 0; - /* The Pegatron Lucid (ExoPC) tablet sporadically waits for 90 - * seconds trying the handoff on its unused controller. Skip - * it. */ + /* The Pegatron Lucid tablet sporadically waits for 98 seconds trying + * the handoff on its unused controller. Skip it. */ if (pdev->vendor == 0x8086 && pdev->device == 0x283a) { - const char *dmi_bn = dmi_get_system_info(DMI_BOARD_NAME); - const char *dmi_bv = dmi_get_system_info(DMI_BIOS_VERSION); - if (dmi_bn && !strcmp(dmi_bn, "EXOPG06411") && - dmi_bv && !strcmp(dmi_bv, "Lucid-CE-133")) + if (dmi_check_system(ehci_dmi_nohandoff_table)) try_handoff = 0; } -- cgit v1.2.3 From 0c42a4e84502533ec40544324debe2a62836ae11 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Tue, 5 Jul 2011 16:38:46 +0200 Subject: ehci: add pci quirk for Ordissimo and RM Slate 100 too Add another variant of the Pegatron tablet used by Ordissimo, and apparently RM Slate 100, to the list of models that should skip the negociation for the handoff of the EHCI controller. Signed-off-by: Anisse Astier Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/pci-quirks.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 04600a9c6e4a..b5a7304fcbef 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -515,6 +515,13 @@ static const struct dmi_system_id __initconst ehci_dmi_nohandoff_table[] = { DMI_MATCH(DMI_BIOS_VERSION, "Lucid-CE-133"), }, }, + { + /* Pegatron Lucid (Ordissimo AIRIS) */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "M11JB"), + DMI_MATCH(DMI_BIOS_VERSION, "Lucid-GE-133"), + }, + }, { } }; -- cgit v1.2.3 From 004c19682884d4f40000ce1ded53f4a1d0b18206 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 5 Jul 2011 12:34:05 -0400 Subject: USB: EHCI: go back to using the system clock for QH unlinks This patch (as1477) fixes a problem affecting a few types of EHCI controller. Contrary to what one might expect, these controllers automatically stop their internal frame counter when no ports are enabled. Since ehci-hcd currently relies on the frame counter for determining when it should unlink QHs from the async schedule, those controllers run into trouble: The frame counter stops and the QHs never get unlinked. Some systems have also experienced other problems traced back to commit b963801164618e25fbdc0cd452ce49c3628b46c8 (USB: ehci-hcd unlink speedups), which made the original switch from using the system clock to using the frame counter. It never became clear what the reason was for these problems, but evidently it is related to use of the frame counter. To fix all these problems, this patch more or less reverts that commit and goes back to using the system clock. But this can't be done cleanly because other changes have since been made to the scan_async() subroutine. One of these changes involved the tricky logic that tries to avoid rescanning QHs that have already been seen when the scanning loop is restarted, which happens whenever an URB is given back. Switching back to clock-based unlinks would make this logic even more complicated. Therefore the new code doesn't rescan the entire async list whenever a giveback occurs. Instead it rescans only the current QH and continues on from there. This requires the use of a separate pointer to keep track of the next QH to scan, since the current QH may be unlinked while the scanning is in progress. That new pointer must be global, so that it can be adjusted forward whenever the _next_ QH gets unlinked. (uhci-hcd uses this same trick.) Simplification of the scanning loop removes a level of indentation, which accounts for the size of the patch. The amount of code changed is relatively small, and it isn't exactly a reversion of the b963801164 commit. This fixes Bugzilla #32432. Signed-off-by: Alan Stern CC: Tested-by: Matej Kenda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 8 ++--- drivers/usb/host/ehci-q.c | 82 ++++++++++++++++++++++----------------------- drivers/usb/host/ehci.h | 3 +- 3 files changed, 45 insertions(+), 48 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 4ee62bef3723..2902199fa8ff 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -90,7 +90,8 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ -#define EHCI_SHRINK_FRAMES 5 /* async qh unlink delay */ +#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) + /* 200-ms async qh unlink delay */ /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 @@ -148,10 +149,7 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) break; /* case TIMER_ASYNC_SHRINK: */ default: - /* add a jiffie since we synch against the - * 8 KHz uframe counter. - */ - t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1; + t = EHCI_SHRINK_JIFFIES; break; } mod_timer(&ehci->watchdog, t + jiffies); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 5d6bc624c961..9bf3c0d983c4 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1231,6 +1231,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) prev->hw->hw_next = qh->hw->hw_next; prev->qh_next = qh->qh_next; + if (ehci->qh_scan_next == qh) + ehci->qh_scan_next = qh->qh_next.qh; wmb (); /* If the controller isn't running, we don't have to wait for it */ @@ -1256,53 +1258,49 @@ static void scan_async (struct ehci_hcd *ehci) struct ehci_qh *qh; enum ehci_timer_action action = TIMER_IO_WATCHDOG; - ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index); timer_action_done (ehci, TIMER_ASYNC_SHRINK); -rescan: stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); - qh = ehci->async->qh_next.qh; - if (likely (qh != NULL)) { - do { - /* clean any finished work for this qh */ - if (!list_empty(&qh->qtd_list) && (stopped || - qh->stamp != ehci->stamp)) { - int temp; - - /* unlinks could happen here; completion - * reporting drops the lock. rescan using - * the latest schedule, but don't rescan - * qhs we already finished (no looping) - * unless the controller is stopped. - */ - qh = qh_get (qh); - qh->stamp = ehci->stamp; - temp = qh_completions (ehci, qh); - if (qh->needs_rescan) - unlink_async(ehci, qh); - qh_put (qh); - if (temp != 0) { - goto rescan; - } - } - /* unlink idle entries, reducing DMA usage as well - * as HCD schedule-scanning costs. delay for any qh - * we just scanned, there's a not-unusual case that it - * doesn't stay idle for long. - * (plus, avoids some kind of re-activation race.) + ehci->qh_scan_next = ehci->async->qh_next.qh; + while (ehci->qh_scan_next) { + qh = ehci->qh_scan_next; + ehci->qh_scan_next = qh->qh_next.qh; + rescan: + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list)) { + int temp; + + /* + * Unlinks could happen here; completion reporting + * drops the lock. That's why ehci->qh_scan_next + * always holds the next qh to scan; if the next qh + * gets unlinked then ehci->qh_scan_next is adjusted + * in start_unlink_async(). */ - if (list_empty(&qh->qtd_list) - && qh->qh_state == QH_STATE_LINKED) { - if (!ehci->reclaim && (stopped || - ((ehci->stamp - qh->stamp) & 0x1fff) - >= EHCI_SHRINK_FRAMES * 8)) - start_unlink_async(ehci, qh); - else - action = TIMER_ASYNC_SHRINK; - } + qh = qh_get(qh); + temp = qh_completions(ehci, qh); + if (qh->needs_rescan) + unlink_async(ehci, qh); + qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; + qh_put(qh); + if (temp != 0) + goto rescan; + } - qh = qh->qh_next.qh; - } while (qh); + /* unlink idle entries, reducing DMA usage as well + * as HCD schedule-scanning costs. delay for any qh + * we just scanned, there's a not-unusual case that it + * doesn't stay idle for long. + * (plus, avoids some kind of re-activation race.) + */ + if (list_empty(&qh->qtd_list) + && qh->qh_state == QH_STATE_LINKED) { + if (!ehci->reclaim && (stopped || + time_after_eq(jiffies, qh->unlink_time))) + start_unlink_async(ehci, qh); + else + action = TIMER_ASYNC_SHRINK; + } } if (action == TIMER_ASYNC_SHRINK) timer_action (ehci, TIMER_ASYNC_SHRINK); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index fa3129fe1ee0..e4feec3457fb 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -75,6 +75,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *reclaim; + struct ehci_qh *qh_scan_next; unsigned scanning : 1; /* periodic schedule support */ @@ -119,7 +120,6 @@ struct ehci_hcd { /* one per controller */ struct timer_list iaa_watchdog; struct timer_list watchdog; unsigned long actions; - unsigned stamp; unsigned periodic_stamp; unsigned random_frame; unsigned long next_statechange; @@ -345,6 +345,7 @@ struct ehci_qh { struct ehci_qh *reclaim; /* next to reclaim */ struct ehci_hcd *ehci; + unsigned long unlink_time; /* * Do NOT use atomic operations for QH refcounting. On some CPUs -- cgit v1.2.3 From 81463c1d707186adbbe534016cd1249edeab0dac Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 6 Jul 2011 23:19:38 +0400 Subject: EHCI: only power off port if over-current is active MAX4967 USB power supply chip we use on our boards signals over-current when power is not enabled; once it's enabled, over-current signal returns to normal. That unfortunately caused the endless stream of "over-current change on port" messages. The EHCI root hub code reacts on every over-current signal change with powering off the port -- such change event is generated the moment the port power is enabled, so once enabled the power is immediately cut off. I think we should only cut off power when we're seeing the active over-current signal, so I'm adding such check to that code. I also think that the fact that we've cut off the port power should be reflected in the result of GetPortStatus request immediately, hence I'm adding a PORTSCn register readback after write... Signed-off-by: Sergei Shtylyov Cc: stable@kernel.org Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hub.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index d9e8d713f485..bf2c8f65e1ae 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -816,10 +816,11 @@ static int ehci_hub_control ( * power switching; they're allowed to just limit the * current. khubd will turn the power back on. */ - if (HCS_PPC (ehci->hcs_params)){ + if ((temp & PORT_OC) && HCS_PPC(ehci->hcs_params)) { ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_POWER), status_reg); + temp = ehci_readl(ehci, status_reg); } } -- cgit v1.2.3 From f2e9039a43b01f01cab9dfaea2cad5f304fb3343 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 7 Jul 2011 09:57:10 +0900 Subject: usb: r8a66597-hcd: add function for external controller R8A66597 has the pin of WR0 and WR1. So, if one write-pin of CPU connects to the pins, we have to change the setting of FIFOSEL register in the controller. If we don't change the setting, the controller cannot send the data of odd length. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 2 +- drivers/usb/host/r8a66597.h | 38 +++++++++++++++++++++----------------- 2 files changed, 22 insertions(+), 18 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index db6f8b9c19b6..cc8ba3c220b5 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1438,7 +1438,7 @@ static void packet_write(struct r8a66597 *r8a66597, u16 pipenum) if (pipenum > 0) r8a66597_write(r8a66597, ~(1 << pipenum), BEMPSTS); if (urb->transfer_buffer) { - r8a66597_write_fifo(r8a66597, td->pipe->fifoaddr, buf, size); + r8a66597_write_fifo(r8a66597, td->pipe, buf, size); if (!usb_pipebulk(urb->pipe) || td->maxpacket != size) r8a66597_write(r8a66597, BVAL, td->pipe->fifoctr); } diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index 25563e9a90bc..f28782d20eef 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -201,11 +201,26 @@ static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, iowrite16(val, r8a66597->reg + offset); } +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, - unsigned long offset, u16 *buf, + struct r8a66597_pipe *pipe, u16 *buf, int len) { - void __iomem *fifoaddr = r8a66597->reg + offset; + void __iomem *fifoaddr = r8a66597->reg + pipe->fifoaddr; unsigned long count; unsigned char *pb; int i; @@ -230,26 +245,15 @@ static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, iowrite16_rep(fifoaddr, buf, len); if (unlikely(odd)) { buf = &buf[len]; + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bclr(r8a66597, MBW_16, pipe->fifosel); iowrite8((unsigned char)*buf, fifoaddr); + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bset(r8a66597, MBW_16, pipe->fifosel); } } } -static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, - u16 val, u16 pat, unsigned long offset) -{ - u16 tmp; - tmp = r8a66597_read(r8a66597, offset); - tmp = tmp & (~pat); - tmp = tmp | val; - r8a66597_write(r8a66597, tmp, offset); -} - -#define r8a66597_bclr(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, 0, val, offset) -#define r8a66597_bset(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, val, 0, offset) - static inline unsigned long get_syscfg_reg(int port) { return port == 0 ? SYSCFG0 : SYSCFG1; -- cgit v1.2.3 From 45304e8cd9d9df07e9221551678262b390bdaaa4 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 7 Jul 2011 09:58:56 +0900 Subject: usb: update email address in ohci-sh and r8a66597-hcd Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-sh.c | 2 +- drivers/usb/host/r8a66597-hcd.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c index f47867ff78c7..14cecb52a9fe 100644 --- a/drivers/usb/host/ohci-sh.c +++ b/drivers/usb/host/ohci-sh.c @@ -3,7 +3,7 @@ * * Copyright (C) 2008 Renesas Solutions Corp. * - * Author : Yoshihiro Shimoda + * Author : Yoshihiro Shimoda * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index cc8ba3c220b5..1ba53f010848 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -6,7 +6,7 @@ * Portions Copyright (C) 2004-2005 David Brownell * Portions Copyright (C) 1999 Roman Weissgaerber * - * Author : Yoshihiro Shimoda + * Author : Yoshihiro Shimoda * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -- cgit v1.2.3 From 35da41375caabe5433e6d20ab1ec087bd31d12b1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 7 Jul 2011 02:36:32 -0700 Subject: usb: r8a66597-hcd: fixup USB_PORT_STAT_C_SUSPEND shift This is typo fix of 749da5f8 (USB: straighten out port feature vs. port status usage) Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 1ba53f010848..fd35901861c3 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2306,7 +2306,7 @@ static int r8a66597_bus_resume(struct usb_hcd *hcd) dbg("resume port = %d", port); rh->port &= ~USB_PORT_STAT_SUSPEND; - rh->port |= USB_PORT_STAT_C_SUSPEND < 16; + rh->port |= USB_PORT_STAT_C_SUSPEND << 16; r8a66597_mdfy(r8a66597, RESUME, RESUME | UACT, dvstctr_reg); msleep(50); r8a66597_mdfy(r8a66597, UACT, RESUME | UACT, dvstctr_reg); -- cgit v1.2.3 From 6ea12a04d295235ed67010a09fdea58c949e3eb0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 15 Jul 2011 17:22:15 -0400 Subject: USB: OHCI: fix another regression for NVIDIA controllers The NVIDIA series of OHCI controllers continues to be troublesome. A few people using the MCP67 chipset have reported that even with the most recent kernels, the OHCI controller fails to handle new connections and spams the system log with "unable to enumerate USB port" messages. This is different from the other problems previously reported for NVIDIA OHCI controllers, although it is probably related. It turns out that the MCP67 controller does not like to be kept in the RESET state very long. After only a few seconds, it decides not to work any more. This patch (as1479) changes the PCI initialization quirk code so that NVIDIA controllers are switched into the SUSPEND state after 50 ms of RESET. With no interrupts enabled and all the downstream devices reset, and thus unable to send wakeup requests, this should be perfectly safe (even for non-NVIDIA hardware). The removal code in ohci-hcd hasn't been changed; it will still leave the controller in the RESET state. As a result, if someone unloads ohci-hcd and then reloads it, the controller won't work again until the system is rebooted. If anybody complains about this, the removal code can be updated similarly. This fixes Bugzilla #22052. Tested-by: Larry Finger Cc: stable Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/pci-quirks.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index b5a7304fcbef..a9d315906e3d 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -35,6 +35,8 @@ #define OHCI_INTRSTATUS 0x0c #define OHCI_INTRENABLE 0x10 #define OHCI_INTRDISABLE 0x14 +#define OHCI_FMINTERVAL 0x34 +#define OHCI_HCR (1 << 0) /* host controller reset */ #define OHCI_OCR (1 << 3) /* ownership change request */ #define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ #define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ @@ -497,6 +499,32 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) /* reset controller, preserving RWC (and possibly IR) */ writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL); + readl(base + OHCI_CONTROL); + + /* Some NVIDIA controllers stop working if kept in RESET for too long */ + if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) { + u32 fminterval; + int cnt; + + /* drive reset for at least 50 ms (7.1.7.5) */ + msleep(50); + + /* software reset of the controller, preserving HcFmInterval */ + fminterval = readl(base + OHCI_FMINTERVAL); + writel(OHCI_HCR, base + OHCI_CMDSTATUS); + + /* reset requires max 10 us delay */ + for (cnt = 30; cnt > 0; --cnt) { /* ... allow extra time */ + if ((readl(base + OHCI_CMDSTATUS) & OHCI_HCR) == 0) + break; + udelay(1); + } + writel(fminterval, base + OHCI_FMINTERVAL); + + /* Now we're in the SUSPEND state with all devices reset + * and wakeups and interrupts disabled + */ + } /* * disable interrupts -- cgit v1.2.3 From e04f5f7e423018bcec84c11af2058cdce87816f3 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 19 Jul 2011 14:01:23 -0400 Subject: EHCI: fix direction handling for interrupt data toggles This patch (as1480) fixes a rather obscure bug in ehci-hcd. The qh_update() routine needs to know the number and direction of the endpoint corresponding to its QH argument. The number can be taken directly from the QH data structure, but the direction isn't stored there. The direction is taken instead from the first qTD linked to the QH. However, it turns out that for interrupt transfers, qh_update() gets called before the qTDs are linked to the QH. As a result, qh_update() computes a bogus direction value, which messes up the endpoint toggle handling. Under the right combination of circumstances this causes usb_reset_endpoint() not to work correctly, which causes packets to be dropped and communications to fail. Now, it's silly for the QH structure not to have direct access to all the descriptor information for the corresponding endpoint. Ultimately it may get a pointer to the usb_host_endpoint structure; for now, adding a copy of the direction flag solves the immediate problem. This allows the Spyder2 color-calibration system (a low-speed USB device that sends all its interrupt data packets with the toggle set to 0 and hance requires constant use of usb_reset_endpoint) to work when connected through a high-speed hub. Thanks to Graeme Gill for supplying the hardware that allowed me to track down this bug. Signed-off-by: Alan Stern Reported-by: Graeme Gill CC: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-q.c | 3 ++- drivers/usb/host/ehci.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 9bf3c0d983c4..0917e3a32465 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -103,7 +103,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) { unsigned is_out, epnum; - is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8)); + is_out = qh->is_out; epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f; if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); @@ -946,6 +946,7 @@ done: hw = qh->hw; hw->hw_info1 = cpu_to_hc32(ehci, info1); hw->hw_info2 = cpu_to_hc32(ehci, info2); + qh->is_out = !is_input; usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); qh_refresh (ehci, qh); return qh; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index e4feec3457fb..cc7d337ec355 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -377,6 +377,7 @@ struct ehci_qh { #define NO_FRAME ((unsigned short)~0) /* pick new start */ struct usb_device *dev; /* access to TT */ + unsigned is_out:1; /* bulk or intr OUT */ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ }; -- cgit v1.2.3