diff options
Diffstat (limited to 'drivers/usb/host')
| -rw-r--r-- | drivers/usb/host/ehci-dbg.c | 4 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-hcd.c | 34 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-hub.c | 27 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-q.c | 18 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-sched.c | 38 | ||||
| -rw-r--r-- | drivers/usb/host/ehci.h | 8 | ||||
| -rw-r--r-- | drivers/usb/host/hc_crisv10.c | 14 | ||||
| -rw-r--r-- | drivers/usb/host/isp116x-hcd.c | 110 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-dbg.c | 11 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-hcd.c | 25 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-hub.c | 25 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-mem.c | 4 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-omap.c | 53 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-ppc-soc.c | 24 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-pxa27x.c | 48 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-s3c2410.c | 496 | ||||
| -rw-r--r-- | drivers/usb/host/ohci.h | 1 | ||||
| -rw-r--r-- | drivers/usb/host/sl811-hcd.c | 10 | ||||
| -rw-r--r-- | drivers/usb/host/sl811_cs.c | 7 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-hcd.c | 62 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-hcd.h | 11 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-hub.c | 11 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-q.c | 4 |
23 files changed, 745 insertions, 300 deletions
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 50cb01831075..65ac9fef3a7c 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -527,7 +527,7 @@ show_periodic (struct class_device *class_dev, char *buf) p.qh->period, le32_to_cpup (&p.qh->hw_info2) /* uframe masks */ - & 0xffff, + & (QH_CMASK | QH_SMASK), p.qh); size -= temp; next += temp; @@ -641,7 +641,7 @@ show_registers (struct class_device *class_dev, char *buf) spin_lock_irqsave (&ehci->lock, flags); - if (bus->controller->power.power_state) { + if (bus->controller->power.power_state.event) { size = scnprintf (next, size, "bus %s, device %s (driver " DRIVER_VERSION ")\n" "%s\n" diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 35248a37b717..b948ffd94f45 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -400,6 +400,23 @@ static int ehci_hc_reset (struct usb_hcd *hcd) return -EIO; } break; + case PCI_VENDOR_ID_NVIDIA: + /* NVidia reports that certain chips don't handle + * QH, ITD, or SITD addresses above 2GB. (But TD, + * data buffer, and periodic schedule are normal.) + */ + switch (pdev->device) { + case 0x003c: /* MCP04 */ + case 0x005b: /* CK804 */ + case 0x00d8: /* CK8 */ + case 0x00e8: /* CK8S */ + if (pci_set_consistent_dma_mask(pdev, + DMA_31BIT_MASK) < 0) + ehci_warn (ehci, "can't enable NVidia " + "workaround for >2GB RAM\n"); + break; + } + break; } /* optional debug port, normally in the first BAR */ @@ -549,7 +566,9 @@ static int ehci_start (struct usb_hcd *hcd) hcd->can_wakeup = (port_wake & 1) != 0; /* help hc dma work well with cachelines */ - pci_set_mwi (pdev); + retval = pci_set_mwi(pdev); + if (retval) + ehci_dbg(ehci, "unable to enable MWI - not fatal.\n"); } #endif @@ -757,12 +776,16 @@ static int ehci_resume (struct usb_hcd *hcd) if (time_before (jiffies, ehci->next_statechange)) msleep (100); - /* If any port is suspended, we know we can/must resume the HC. */ + /* If any port is suspended (or owned by the companion), + * we know we can/must resume the HC (and mustn't reset it). + */ for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { u32 status; port--; status = readl (&ehci->regs->port_status [port]); - if (status & PORT_SUSPEND) { + if (!(status & PORT_POWER)) + continue; + if (status & (PORT_SUSPEND | PORT_OWNER)) { down (&hcd->self.root_hub->serialize); retval = ehci_hub_resume (hcd); up (&hcd->self.root_hub->serialize); @@ -960,7 +983,7 @@ static int ehci_urb_enqueue ( struct usb_hcd *hcd, struct usb_host_endpoint *ep, struct urb *urb, - int mem_flags + unsigned mem_flags ) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct list_head qtd_list; @@ -1124,8 +1147,7 @@ rescan: case QH_STATE_UNLINK: /* wait for hw to finish? */ idle_timeout: spin_unlock_irqrestore (&ehci->lock, flags); - set_current_state (TASK_UNINTERRUPTIBLE); - schedule_timeout (1); + schedule_timeout_uninterruptible(1); goto rescan; case QH_STATE_IDLE: /* fully unlinked */ if (list_empty (&qh->qtd_list)) { diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 36cc1f2218d5..18d3f2270316 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -54,7 +54,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) /* suspend any active/unsuspended ports, maybe allow wakeup */ while (port--) { u32 __iomem *reg = &ehci->regs->port_status [port]; - u32 t1 = readl (reg); + u32 t1 = readl (reg) & ~PORT_RWC_BITS; u32 t2 = t1; if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) @@ -115,7 +115,8 @@ static int ehci_hub_resume (struct usb_hcd *hcd) i = HCS_N_PORTS (ehci->hcs_params); while (i--) { temp = readl (&ehci->regs->port_status [i]); - temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); + temp &= ~(PORT_RWC_BITS + | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); if (temp & PORT_SUSPEND) { ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); temp |= PORT_RESUME; @@ -128,7 +129,7 @@ static int ehci_hub_resume (struct usb_hcd *hcd) temp = readl (&ehci->regs->port_status [i]); if ((temp & PORT_SUSPEND) == 0) continue; - temp &= ~PORT_RESUME; + temp &= ~(PORT_RWC_BITS | PORT_RESUME); writel (temp, &ehci->regs->port_status [i]); ehci_vdbg (ehci, "resumed port %d\n", i + 1); } @@ -191,6 +192,7 @@ static int check_reset_complete ( // what happens if HCS_N_CC(params) == 0 ? port_status |= PORT_OWNER; + port_status &= ~PORT_RWC_BITS; writel (port_status, &ehci->regs->port_status [index]); } else @@ -233,7 +235,8 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) if (temp & PORT_OWNER) { /* don't report this in GetPortStatus */ if (temp & PORT_CSC) { - temp &= ~PORT_CSC; + temp &= ~PORT_RWC_BITS; + temp |= PORT_CSC; writel (temp, &ehci->regs->port_status [i]); } continue; @@ -343,7 +346,7 @@ static int ehci_hub_control ( &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_ENABLE: - writel (temp | PORT_PEC, + writel((temp & ~PORT_RWC_BITS) | PORT_PEC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_SUSPEND: @@ -353,7 +356,8 @@ static int ehci_hub_control ( if ((temp & PORT_PE) == 0) goto error; /* resume signaling for 20 msec */ - writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME, + temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); + writel (temp | PORT_RESUME, &ehci->regs->port_status [wIndex]); ehci->reset_done [wIndex] = jiffies + msecs_to_jiffies (20); @@ -364,15 +368,15 @@ static int ehci_hub_control ( break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) - writel (temp & ~PORT_POWER, + writel (temp & ~(PORT_RWC_BITS | PORT_POWER), &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_CONNECTION: - writel (temp | PORT_CSC, + writel((temp & ~PORT_RWC_BITS) | PORT_CSC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_OVER_CURRENT: - writel (temp | PORT_OCC, + writel((temp & ~PORT_RWC_BITS) | PORT_OCC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_RESET: @@ -416,7 +420,7 @@ static int ehci_hub_control ( /* stop resume signaling */ temp = readl (&ehci->regs->port_status [wIndex]); - writel (temp & ~PORT_RESUME, + writel (temp & ~(PORT_RWC_BITS | PORT_RESUME), &ehci->regs->port_status [wIndex]); retval = handshake ( &ehci->regs->port_status [wIndex], @@ -437,7 +441,7 @@ static int ehci_hub_control ( ehci->reset_done [wIndex] = 0; /* force reset to complete */ - writel (temp & ~PORT_RESET, + writel (temp & ~(PORT_RWC_BITS | PORT_RESET), &ehci->regs->port_status [wIndex]); /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... @@ -500,6 +504,7 @@ static int ehci_hub_control ( if (temp & PORT_OWNER) break; + temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if ((temp & PORT_PE) == 0 diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 45d89a7083b1..940d38ca7d91 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -222,7 +222,7 @@ __acquires(ehci->lock) struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; /* S-mask in a QH means it's an interrupt urb */ - if ((qh->hw_info2 & __constant_cpu_to_le32 (0x00ff)) != 0) { + if ((qh->hw_info2 & __constant_cpu_to_le32 (QH_SMASK)) != 0) { /* ... update hc-wide periodic stats (for usbfs) */ ehci_to_hcd(ehci)->self.bandwidth_int_reqs--; @@ -428,7 +428,8 @@ halt: /* should be rare for periodic transfers, * except maybe high bandwidth ... */ - if (qh->period) { + if ((__constant_cpu_to_le32 (QH_SMASK) + & qh->hw_info2) != 0) { intr_deschedule (ehci, qh); (void) qh_schedule (ehci, qh); } else @@ -657,8 +658,8 @@ qh_make ( * For control/bulk requests, the HC or TT handles these. */ if (type == PIPE_INTERRUPT) { - qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, - hb_mult (maxp) * max_packet (maxp)); + qh->usecs = NS_TO_US (usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, + hb_mult (maxp) * max_packet (maxp))); qh->start = NO_FRAME; if (urb->dev->speed == USB_SPEED_HIGH) { @@ -676,6 +677,9 @@ qh_make ( goto done; } } else { + struct usb_tt *tt = urb->dev->tt; + int think_time; + /* gap is f(FS/LS transfer times) */ qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, is_input, 0, maxp) / (125 * 1000); @@ -689,6 +693,10 @@ qh_make ( qh->c_usecs = HS_USECS (0); } + think_time = tt ? tt->think_time : 0; + qh->tt_usecs = NS_TO_US (think_time + + usb_calc_bus_time (urb->dev->speed, + is_input, 0, max_packet (maxp))); qh->period = urb->interval; } } @@ -898,7 +906,7 @@ submit_async ( struct usb_host_endpoint *ep, struct urb *urb, struct list_head *qtd_list, - int mem_flags + unsigned mem_flags ) { struct ehci_qtd *qtd; int epnum; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index c2104cad4033..ccc7300baa6d 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -301,7 +301,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) dev_dbg (&qh->dev->dev, "link qh%d-%04x/%p start %d [%d/%d us]\n", - period, le32_to_cpup (&qh->hw_info2) & 0xffff, + period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); /* high bandwidth, or otherwise every microframe */ @@ -385,7 +385,8 @@ static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) dev_dbg (&qh->dev->dev, "unlink qh%d-%04x/%p start %d [%d/%d us]\n", - qh->period, le32_to_cpup (&qh->hw_info2) & 0xffff, + qh->period, + le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); /* qh->qh_next still "live" to HC */ @@ -411,7 +412,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) * active high speed queues may need bigger delays... */ if (list_empty (&qh->qtd_list) - || (__constant_cpu_to_le32 (0x0ff << 8) + || (__constant_cpu_to_le32 (QH_CMASK) & qh->hw_info2) != 0) wait = 2; else @@ -533,7 +534,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) /* reuse the previous schedule slots, if we can */ if (frame < qh->period) { - uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff); + uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK); status = check_intr_schedule (ehci, frame, --uframe, qh, &c_mask); } else { @@ -569,10 +570,10 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) qh->start = frame; /* reset S-frame and (maybe) C-frame masks */ - qh->hw_info2 &= __constant_cpu_to_le32 (~0xffff); + qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK)); qh->hw_info2 |= qh->period ? cpu_to_le32 (1 << uframe) - : __constant_cpu_to_le32 (0xff); + : __constant_cpu_to_le32 (QH_SMASK); qh->hw_info2 |= c_mask; } else ehci_dbg (ehci, "reused qh %p schedule\n", qh); @@ -588,7 +589,7 @@ static int intr_submit ( struct usb_host_endpoint *ep, struct urb *urb, struct list_head *qtd_list, - int mem_flags + unsigned mem_flags ) { unsigned epnum; unsigned long flags; @@ -633,11 +634,11 @@ done: /* ehci_iso_stream ops work with both ITD and SITD */ static struct ehci_iso_stream * -iso_stream_alloc (int mem_flags) +iso_stream_alloc (unsigned mem_flags) { struct ehci_iso_stream *stream; - stream = kcalloc(1, sizeof *stream, mem_flags); + stream = kzalloc(sizeof *stream, mem_flags); if (likely (stream != NULL)) { INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); @@ -699,6 +700,7 @@ iso_stream_init ( } else { u32 addr; + int think_time; addr = dev->ttport << 24; if (!ehci_is_TDI(ehci) @@ -708,6 +710,9 @@ iso_stream_init ( addr |= epnum << 8; addr |= dev->devnum; stream->usecs = HS_USECS_ISO (maxp); + think_time = dev->tt ? dev->tt->think_time : 0; + stream->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time ( + dev->speed, is_input, 1, maxp)); if (is_input) { u32 tmp; @@ -846,7 +851,7 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb) /* ehci_iso_sched ops can be ITD-only or SITD-only */ static struct ehci_iso_sched * -iso_sched_alloc (unsigned packets, int mem_flags) +iso_sched_alloc (unsigned packets, unsigned mem_flags) { struct ehci_iso_sched *iso_sched; int size = sizeof *iso_sched; @@ -919,7 +924,7 @@ itd_urb_transaction ( struct ehci_iso_stream *stream, struct ehci_hcd *ehci, struct urb *urb, - int mem_flags + unsigned mem_flags ) { struct ehci_itd *itd; @@ -1412,7 +1417,8 @@ itd_complete ( /*-------------------------------------------------------------------------*/ -static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) +static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, + unsigned mem_flags) { int status = -EINVAL; unsigned long flags; @@ -1523,7 +1529,7 @@ sitd_urb_transaction ( struct ehci_iso_stream *stream, struct ehci_hcd *ehci, struct urb *urb, - int mem_flags + unsigned mem_flags ) { struct ehci_sitd *sitd; @@ -1772,7 +1778,8 @@ sitd_complete ( } -static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) +static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, + unsigned mem_flags) { int status = -EINVAL; unsigned long flags; @@ -1822,7 +1829,8 @@ done: #else static inline int -sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags) +sitd_submit (struct ehci_hcd *ehci, struct urb *urb, + unsigned mem_flags) { ehci_dbg (ehci, "split iso support is disabled\n"); return -ENOSYS; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 4df498231752..f34a0516d35f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -263,6 +263,7 @@ struct ehci_regs { #define PORT_PE (1<<2) /* port enable */ #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) } __attribute__ ((packed)); /* Appendix C, Debug port ... intended for use with special "debug devices" @@ -385,6 +386,11 @@ struct ehci_qh { __le32 hw_info1; /* see EHCI 3.6.2 */ #define QH_HEAD 0x00008000 __le32 hw_info2; /* see EHCI 3.6.2 */ +#define QH_SMASK 0x000000ff +#define QH_CMASK 0x0000ff00 +#define QH_HUBADDR 0x007f0000 +#define QH_HUBPORT 0x3f800000 +#define QH_MULT 0xc0000000 __le32 hw_current; /* qtd list - see EHCI 3.6.4 */ /* qtd overlay (hardware parts of a struct ehci_qtd) */ @@ -416,6 +422,7 @@ struct ehci_qh { u8 usecs; /* intr bandwidth */ u8 gap_uf; /* uframes split/csplit gap */ u8 c_usecs; /* ... split completion bw */ + u16 tt_usecs; /* tt downstream bandwidth */ unsigned short period; /* polling interval */ unsigned short start; /* where polling starts */ #define NO_FRAME ((unsigned short)~0) /* pick new start */ @@ -474,6 +481,7 @@ struct ehci_iso_stream { */ u8 interval; u8 usecs, c_usecs; + u16 tt_usecs; u16 maxp; u16 raw_mask; unsigned bandwidth; diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c index d9883d774d3a..a8267cf17db4 100644 --- a/drivers/usb/host/hc_crisv10.c +++ b/drivers/usb/host/hc_crisv10.c @@ -178,8 +178,8 @@ static __u8 root_hub_hub_des[] = 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ }; -static struct timer_list bulk_start_timer = TIMER_INITIALIZER(NULL, 0, 0); -static struct timer_list bulk_eot_timer = TIMER_INITIALIZER(NULL, 0, 0); +static DEFINE_TIMER(bulk_start_timer, NULL, 0, 0); +static DEFINE_TIMER(bulk_eot_timer, NULL, 0, 0); /* We want the start timer to expire before the eot timer, because the former might start traffic, thus making it unnecessary for the latter to time out. */ @@ -463,7 +463,8 @@ static void etrax_usb_free_epid(int epid); static int etrax_remove_from_sb_list(struct urb *urb); -static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, int mem_flags, dma_addr_t *dma); +static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, + unsigned mem_flags, dma_addr_t *dma); static void etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma); static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid); @@ -476,7 +477,7 @@ static int etrax_usb_submit_ctrl_urb(struct urb *urb); static int etrax_usb_submit_intr_urb(struct urb *urb); static int etrax_usb_submit_isoc_urb(struct urb *urb); -static int etrax_usb_submit_urb(struct urb *urb, int mem_flags); +static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags); static int etrax_usb_unlink_urb(struct urb *urb, int status); static int etrax_usb_get_frame_number(struct usb_device *usb_dev); @@ -1262,7 +1263,7 @@ static int etrax_usb_allocate_epid(void) return -1; } -static int etrax_usb_submit_urb(struct urb *urb, int mem_flags) +static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags) { etrax_hc_t *hc; int ret = -EINVAL; @@ -4277,7 +4278,8 @@ etrax_usb_bulk_eot_timer_func(unsigned long dummy) } static void* -etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, int mem_flags, dma_addr_t *dma) +etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, + unsigned mem_flags, dma_addr_t *dma) { return kmalloc(size, mem_flags); } diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index ff0a168e8eed..41bbae83fc71 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -17,7 +17,7 @@ * The driver basically works. A number of people have used it with a range * of devices. * - *The driver passes all usbtests 1-14. + * The driver passes all usbtests 1-14. * * Suspending/resuming of root hub via sysfs works. Remote wakeup works too. * And suspending/resuming of platform device works too. Suspend/resume @@ -83,7 +83,7 @@ #include "../core/hcd.h" #include "isp116x.h" -#define DRIVER_VERSION "08 Apr 2005" +#define DRIVER_VERSION "05 Aug 2005" #define DRIVER_DESC "ISP116x USB Host Controller Driver" MODULE_DESCRIPTION(DRIVER_DESC); @@ -229,9 +229,11 @@ static void preproc_atl_queue(struct isp116x *isp116x) struct isp116x_ep *ep; struct urb *urb; struct ptd *ptd; - u16 toggle, dir, len; + u16 len; for (ep = isp116x->atl_active; ep; ep = ep->active) { + u16 toggle = 0, dir = PTD_DIR_SETUP; + BUG_ON(list_empty(&ep->hep->urb_list)); urb = container_of(ep->hep->urb_list.next, struct urb, urb_list); @@ -251,8 +253,6 @@ static void preproc_atl_queue(struct isp116x *isp116x) dir = PTD_DIR_OUT; break; case USB_PID_SETUP: - toggle = 0; - dir = PTD_DIR_SETUP; len = sizeof(struct usb_ctrlrequest); ep->data = urb->setup_packet; break; @@ -264,11 +264,9 @@ static void preproc_atl_queue(struct isp116x *isp116x) ? PTD_DIR_OUT : PTD_DIR_IN; break; default: - /* To please gcc */ - toggle = dir = 0; ERR("%s %d: ep->nextpid %d\n", __func__, __LINE__, ep->nextpid); - BUG_ON(1); + BUG(); } ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle); @@ -631,14 +629,12 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs) ERR("Unrecoverable error\n"); /* What should we do here? Reset? */ } - if (intstat & HCINT_RHSC) { - isp116x->rhstatus = - isp116x_read_reg32(isp116x, HCRHSTATUS); - isp116x->rhport[0] = - isp116x_read_reg32(isp116x, HCRHPORT1); - isp116x->rhport[1] = - isp116x_read_reg32(isp116x, HCRHPORT2); - } + if (intstat & HCINT_RHSC) + /* When root hub or any of its ports is going + to come out of suspend, it may take more + than 10ms for status bits to stabilize. */ + mod_timer(&hcd->rh_timer, jiffies + + msecs_to_jiffies(20) + 1); if (intstat & HCINT_RD) { DBG("---- remote wakeup\n"); schedule_work(&isp116x->rh_resume); @@ -697,7 +693,7 @@ static int balance(struct isp116x *isp116x, u16 period, u16 load) static int isp116x_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep, struct urb *urb, - int mem_flags) + unsigned mem_flags) { struct isp116x *isp116x = hcd_to_isp116x(hcd); struct usb_device *udev = urb->dev; @@ -719,7 +715,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, } /* avoid all allocations within spinlocks: request or endpoint */ if (!hep->hcpriv) { - ep = kcalloc(1, sizeof *ep, (__force unsigned)mem_flags); + ep = kzalloc(sizeof *ep, mem_flags); if (!ep) return -ENOMEM; } @@ -927,20 +923,27 @@ static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf) { struct isp116x *isp116x = hcd_to_isp116x(hcd); int ports, i, changed = 0; + unsigned long flags; if (!HC_IS_RUNNING(hcd->state)) return -ESHUTDOWN; - ports = isp116x->rhdesca & RH_A_NDP; + /* Report no status change now, if we are scheduled to be + called later */ + if (timer_pending(&hcd->rh_timer)) + return 0; - /* init status */ + ports = isp116x->rhdesca & RH_A_NDP; + spin_lock_irqsave(&isp116x->lock, flags); + isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS); if (isp116x->rhstatus & (RH_HS_LPSC | RH_HS_OCIC)) buf[0] = changed = 1; else buf[0] = 0; for (i = 0; i < ports; i++) { - u32 status = isp116x->rhport[i]; + u32 status = isp116x->rhport[i] = + isp116x_read_reg32(isp116x, i ? HCRHPORT2 : HCRHPORT1); if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC)) { @@ -949,6 +952,7 @@ static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf) continue; } } + spin_unlock_irqrestore(&isp116x->lock, flags); return changed; } @@ -1054,7 +1058,7 @@ static int isp116x_hub_control(struct usb_hcd *hcd, break; case GetHubStatus: DBG("GetHubStatus\n"); - *(__le32 *) buf = cpu_to_le32(0); + *(__le32 *) buf = 0; break; case GetPortStatus: DBG("GetPortStatus\n"); @@ -1465,10 +1469,6 @@ static int isp116x_sw_reset(struct isp116x *isp116x) return ret; } -/* - Reset. Tries to perform platform-specific hardware - reset first; falls back to software reset. -*/ static int isp116x_reset(struct usb_hcd *hcd) { struct isp116x *isp116x = hcd_to_isp116x(hcd); @@ -1476,17 +1476,7 @@ static int isp116x_reset(struct usb_hcd *hcd) u16 clkrdy = 0; int ret = 0, timeout = 15 /* ms */ ; - if (isp116x->board && isp116x->board->reset) { - /* Hardware reset */ - isp116x->board->reset(hcd->self.controller, 1); - msleep(10); - if (isp116x->board->clock) - isp116x->board->clock(hcd->self.controller, 1); - msleep(1); - isp116x->board->reset(hcd->self.controller, 0); - } else - ret = isp116x_sw_reset(isp116x); - + ret = isp116x_sw_reset(isp116x); if (ret) return ret; @@ -1503,10 +1493,7 @@ static int isp116x_reset(struct usb_hcd *hcd) ERR("Clock not ready after 20ms\n"); /* After sw_reset the clock won't report to be ready, if H_WAKEUP pin is high. */ - if (!isp116x->board || !isp116x->board->reset) - ERR("The driver does not support hardware wakeup.\n"); - ERR("Please make sure that the H_WAKEUP pin " - "is pulled low!\n"); + ERR("Please make sure that the H_WAKEUP pin is pulled low!\n"); ret = -ENODEV; } return ret; @@ -1529,15 +1516,7 @@ static void isp116x_stop(struct usb_hcd *hcd) isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS); spin_unlock_irqrestore(&isp116x->lock, flags); - /* Put the chip into reset state */ - if (isp116x->board && isp116x->board->reset) - isp116x->board->reset(hcd->self.controller, 0); - else - isp116x_sw_reset(isp116x); - - /* Stop the clock */ - if (isp116x->board && isp116x->board->clock) - isp116x->board->clock(hcd->self.controller, 0); + isp116x_sw_reset(isp116x); } /* @@ -1563,6 +1542,9 @@ static int isp116x_start(struct usb_hcd *hcd) return -ENODEV; } + /* To be removed in future */ + hcd->uses_new_polling = 1; + isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE); isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE); @@ -1571,7 +1553,7 @@ static int isp116x_start(struct usb_hcd *hcd) if (board->sel15Kres) val |= HCHWCFG_15KRSEL; /* Remote wakeup won't work without working clock */ - if (board->clknotstop || board->remote_wakeup_enable) + if (board->remote_wakeup_enable) val |= HCHWCFG_CLKNOTSTOP; if (board->oc_enable) val |= HCHWCFG_ANALOG_OC; @@ -1582,16 +1564,13 @@ static int isp116x_start(struct usb_hcd *hcd) isp116x_write_reg16(isp116x, HCHWCFG, val); /* ----- Root hub conf */ - val = 0; - /* AN10003_1.pdf recommends NPS to be always 1 */ - if (board->no_power_switching) - val |= RH_A_NPS; - if (board->power_switching_mode) - val |= RH_A_PSM; - if (board->potpg) - val |= (board->potpg << 24) & RH_A_POTPGT; - else - val |= (25 << 24) & RH_A_POTPGT; + val = (25 << 24) & RH_A_POTPGT; + /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to + be always set. Yet, instead, we request individual port + power switching. */ + val |= RH_A_PSM; + /* Report overcurrent per port */ + val |= RH_A_OCPM; isp116x_write_reg32(isp116x, HCRHDESCA, val); isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA); @@ -1621,9 +1600,6 @@ static int isp116x_start(struct usb_hcd *hcd) /* Go operational */ val = HCCONTROL_USB_OPER; - /* Remote wakeup connected - NOT SUPPORTED */ - /* if (board->remote_wakeup_connected) - val |= HCCONTROL_RWC; */ if (board->remote_wakeup_enable) val |= HCCONTROL_RWE; isp116x_write_reg32(isp116x, HCCONTROL, val); @@ -1672,7 +1648,7 @@ static int __init_or_module isp116x_remove(struct device *dev) struct platform_device *pdev; struct resource *res; - if(!hcd) + if (!hcd) return 0; isp116x = hcd_to_isp116x(hcd); pdev = container_of(dev, struct platform_device, dev); @@ -1810,9 +1786,9 @@ static int isp116x_suspend(struct device *dev, pm_message_t state, u32 phase) ret = usb_suspend_device(hcd->self.root_hub, state); if (!ret) { dev->power.power_state = state; - INFO("%s suspended\n", (char *)hcd_name); + INFO("%s suspended\n", hcd_name); } else - ERR("%s suspend failed\n", (char *)hcd_name); + ERR("%s suspend failed\n", hcd_name); return ret; } diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index c58408c95c3d..7924c74f958e 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -228,23 +228,22 @@ ohci_dump_roothub ( char **next, unsigned *size) { - u32 temp, ndp, i; + u32 temp, i; temp = roothub_a (controller); if (temp == ~(u32)0) return; - ndp = (temp & RH_A_NDP); if (verbose) { ohci_dbg_sw (controller, next, size, - "roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp, + "roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d(%d)\n", temp, ((temp & RH_A_POTPGT) >> 24) & 0xff, (temp & RH_A_NOCP) ? " NOCP" : "", (temp & RH_A_OCPM) ? " OCPM" : "", (temp & RH_A_DT) ? " DT" : "", (temp & RH_A_NPS) ? " NPS" : "", (temp & RH_A_PSM) ? " PSM" : "", - ndp + (temp & RH_A_NDP), controller->num_ports ); temp = roothub_b (controller); ohci_dbg_sw (controller, next, size, @@ -266,7 +265,7 @@ ohci_dump_roothub ( ); } - for (i = 0; i < ndp; i++) { + for (i = 0; i < controller->num_ports; i++) { temp = roothub_portstatus (controller, i); dbg_port_sw (controller, i, temp, next, size); } @@ -631,7 +630,7 @@ show_registers (struct class_device *class_dev, char *buf) hcd->product_desc, hcd_name); - if (bus->controller->power.power_state) { + if (bus->controller->power.power_state.event) { size -= scnprintf (next, size, "SUSPENDED (no register access)\n"); goto done; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 13cd2177b557..67c1aa5eb1c1 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -180,7 +180,7 @@ static int ohci_urb_enqueue ( struct usb_hcd *hcd, struct usb_host_endpoint *ep, struct urb *urb, - int mem_flags + unsigned mem_flags ) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ed *ed; @@ -382,8 +382,7 @@ sanitize: goto sanitize; } spin_unlock_irqrestore (&ohci->lock, flags); - set_current_state (TASK_UNINTERRUPTIBLE); - schedule_timeout (1); + schedule_timeout_uninterruptible(1); goto rescan; case ED_IDLE: /* fully unlinked */ if (list_empty (&ed->td_list)) { @@ -485,6 +484,10 @@ static int ohci_init (struct ohci_hcd *ohci) // flush the writes (void) ohci_readl (ohci, &ohci->regs->control); + /* Read the number of ports unless overridden */ + if (ohci->num_ports == 0) + ohci->num_ports = roothub_a(ohci) & RH_A_NDP; + if (ohci->hcca) return 0; @@ -561,10 +564,8 @@ static int ohci_run (struct ohci_hcd *ohci) msleep(temp); temp = roothub_a (ohci); if (!(temp & RH_A_NPS)) { - unsigned ports = temp & RH_A_NDP; - /* power down each port */ - for (temp = 0; temp < ports; temp++) + for (temp = 0; temp < ohci->num_ports; temp++) ohci_writel (ohci, RH_PS_LSDA, &ohci->regs->roothub.portstatus [temp]); } @@ -673,8 +674,10 @@ retry: ohci_dump (ohci, 1); - if (ohci_to_hcd(ohci)->self.root_hub == NULL) + if (ohci_to_hcd(ohci)->self.root_hub == NULL) { + register_reboot_notifier (&ohci->reboot_notifier); create_debug_files (ohci); + } return 0; } @@ -718,6 +721,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) if (ints & OHCI_INTR_RD) { ohci_vdbg (ohci, "resume detect\n"); + ohci_writel (ohci, OHCI_INTR_RD, ®s->intrstatus); if (hcd->state != HC_STATE_QUIESCING) schedule_work(&ohci->rh_resume); } @@ -859,7 +863,7 @@ static int ohci_restart (struct ohci_hcd *ohci) * and that if we try to turn them back on the root hub * will respond to CSC processing. */ - i = roothub_a (ohci) & RH_A_NDP; + i = ohci->num_ports; while (i--) ohci_writel (ohci, RH_PS_PSS, &ohci->regs->roothub.portstatus [temp]); @@ -885,6 +889,10 @@ MODULE_LICENSE ("GPL"); #include "ohci-sa1111.c" #endif +#ifdef CONFIG_ARCH_S3C2410 +#include "ohci-s3c2410.c" +#endif + #ifdef CONFIG_ARCH_OMAP #include "ohci-omap.c" #endif @@ -907,6 +915,7 @@ MODULE_LICENSE ("GPL"); #if !(defined(CONFIG_PCI) \ || defined(CONFIG_SA1111) \ + || defined(CONFIG_ARCH_S3C2410) \ || defined(CONFIG_ARCH_OMAP) \ || defined (CONFIG_ARCH_LH7A404) \ || defined (CONFIG_PXA27x) \ diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index e2fc4129dfc6..ce7b28da7a15 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -184,7 +184,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) if (status != -EINPROGRESS) return status; - temp = roothub_a (ohci) & RH_A_NDP; + temp = ohci->num_ports; enables = 0; while (temp--) { u32 stat = ohci_readl (ohci, @@ -304,7 +304,7 @@ static int ohci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ports, i, changed = 0, length = 1; + int i, changed = 0, length = 1; int can_suspend = hcd->can_wakeup; unsigned long flags; @@ -319,9 +319,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) goto done; } - ports = roothub_a (ohci) & RH_A_NDP; - if (ports > MAX_ROOT_PORTS) { - ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports, + /* undocumented erratum seen on at least rev D */ + if ((ohci->flags & OHCI_QUIRK_AMD756) + && (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) { + ohci_warn (ohci, "bogus NDP, rereads as NDP=%d\n", ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP); /* retry later; "should not happen" */ goto done; @@ -332,13 +333,13 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) buf [0] = changed = 1; else buf [0] = 0; - if (ports > 7) { + if (ohci->num_ports > 7) { buf [1] = 0; length++; } /* look at each port */ - for (i = 0; i < ports; i++) { + for (i = 0; i < ohci->num_ports; i++) { u32 status = roothub_portstatus (ohci, i); if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC @@ -395,15 +396,14 @@ ohci_hub_descriptor ( struct usb_hub_descriptor *desc ) { u32 rh = roothub_a (ohci); - int ports = rh & RH_A_NDP; u16 temp; desc->bDescriptorType = 0x29; desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24; desc->bHubContrCurrent = 0; - desc->bNbrPorts = ports; - temp = 1 + (ports / 8); + desc->bNbrPorts = ohci->num_ports; + temp = 1 + (ohci->num_ports / 8); desc->bDescLength = 7 + 2 * temp; temp = 0; @@ -419,10 +419,11 @@ ohci_hub_descriptor ( /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ rh = roothub_b (ohci); + memset(desc->bitmap, 0xff, sizeof(desc->bitmap)); desc->bitmap [0] = rh & RH_B_DR; - if (ports > 7) { + if (ohci->num_ports > 7) { desc->bitmap [1] = (rh & RH_B_DR) >> 8; - desc->bitmap [2] = desc->bitmap [3] = 0xff; + desc->bitmap [2] = 0xff; } else desc->bitmap [1] = 0xff; } diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index 23735a36af00..fd3c4d3714bd 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -84,7 +84,7 @@ dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma) /* TDs ... */ static struct td * -td_alloc (struct ohci_hcd *hc, int mem_flags) +td_alloc (struct ohci_hcd *hc, unsigned mem_flags) { dma_addr_t dma; struct td *td; @@ -118,7 +118,7 @@ td_free (struct ohci_hcd *hc, struct td *td) /* EDs ... */ static struct ed * -ed_alloc (struct ohci_hcd *hc, int mem_flags) +ed_alloc (struct ohci_hcd *hc, unsigned mem_flags) { dma_addr_t dma; struct ed *ed; diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index b62d69937694..5cde76faab93 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -456,34 +456,22 @@ static int ohci_hcd_omap_drv_remove(struct device *dev) #ifdef CONFIG_PM -/* states match PCI usage, always suspending the root hub except that - * 4 ~= D3cold (ACPI D3) with clock off (resume sees reset). - * - * FIXME: above comment is not right, and code is wrong, too :-(. - */ - -static int ohci_omap_suspend(struct device *dev, pm_message_t state, u32 level) +static int ohci_omap_suspend(struct device *dev, pm_message_t message, u32 level) { struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); int status = -EINVAL; if (level != SUSPEND_POWER_DOWN) return 0; - if (state <= dev->power.power_state) - return 0; - dev_dbg(dev, "suspend to %d\n", state); down(&ohci_to_hcd(ohci)->self.root_hub->serialize); status = ohci_hub_suspend(ohci_to_hcd(ohci)); if (status == 0) { - if (state >= 4) { - omap_ohci_clock_power(0); - ohci_to_hcd(ohci)->self.root_hub->state = - USB_STATE_SUSPENDED; - state = 4; - } + omap_ohci_clock_power(0); + ohci_to_hcd(ohci)->self.root_hub->state = + USB_STATE_SUSPENDED; ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; - dev->power.power_state = state; + dev->power.power_state = PMSG_SUSPEND; } up(&ohci_to_hcd(ohci)->self.root_hub->serialize); return status; @@ -497,29 +485,20 @@ static int ohci_omap_resume(struct device *dev, u32 level) if (level != RESUME_POWER_ON) return 0; - switch (dev->power.power_state) { - case 0: - break; - case 4: - if (time_before(jiffies, ohci->next_statechange)) - msleep(5); - ohci->next_statechange = jiffies; - omap_ohci_clock_power(1); - /* FALLTHROUGH */ - default: - dev_dbg(dev, "resume from %d\n", dev->power.power_state); + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + omap_ohci_clock_power(1); #ifdef CONFIG_USB_SUSPEND - /* get extra cleanup even if remote wakeup isn't in use */ - status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub); + /* get extra cleanup even if remote wakeup isn't in use */ + status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub); #else - down(&ohci_to_hcd(ohci)->self.root_hub->serialize); - status = ohci_hub_resume(ohci_to_hcd(ohci)); - up(&ohci_to_hcd(ohci)->self.root_hub->serialize); + down(&ohci_to_hcd(ohci)->self.root_hub->serialize); + status = ohci_hub_resume(ohci_to_hcd(ohci)); + up(&ohci_to_hcd(ohci)->self.root_hub->serialize); #endif - if (status == 0) - dev->power.power_state = 0; - break; - } + if (status == 0) + dev->power.power_state = PMSG_ON; return status; } diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c index 17964c39d06a..251533363028 100644 --- a/drivers/usb/host/ohci-ppc-soc.c +++ b/drivers/usb/host/ohci-ppc-soc.c @@ -14,8 +14,6 @@ * This file is licenced under the GPL. */ -#include <asm/usb.h> - /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ @@ -23,9 +21,7 @@ * usb_hcd_ppc_soc_probe - initialize On-Chip HCDs * Context: !in_interrupt() * - * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. + * Allocates basic resources for this USB host controller. * * Store this function in the HCD's struct pci_driver as probe(). */ @@ -37,7 +33,6 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, struct ohci_hcd *ohci; struct resource *res; int irq; - struct usb_hcd_platform_data *pd = pdev->dev.platform_data; pr_debug("initializing PPC-SOC USB Controller\n"); @@ -73,9 +68,6 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, goto err2; } - if (pd->start && (retval = pd->start(pdev))) - goto err3; - ohci = hcd_to_ohci(hcd); ohci->flags |= OHCI_BIG_ENDIAN; ohci_hcd_init(ohci); @@ -85,9 +77,7 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, return retval; pr_debug("Removing PPC-SOC USB Controller\n"); - if (pd && pd->stop) - pd->stop(pdev); - err3: + iounmap(hcd->regs); err2: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); @@ -105,25 +95,21 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, * @pdev: USB Host Controller being removed * Context: !in_interrupt() * - * Reverses the effect of usb_hcd_ppc_soc_probe(), first invoking - * the HCD's stop() method. It is always called from a thread + * Reverses the effect of usb_hcd_ppc_soc_probe(). + * It is always called from a thread * context, normally "rmmod", "apmd", or something similar. * */ static void usb_hcd_ppc_soc_remove(struct usb_hcd *hcd, struct platform_device *pdev) { - struct usb_hcd_platform_data *pd = pdev->dev.platform_data; - usb_remove_hcd(hcd); pr_debug("stopping PPC-SOC USB Controller\n"); - if (pd && pd->stop) - pd->stop(pdev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_hcd_put(hcd); + usb_put_hcd(hcd); } static int __devinit diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index e5bc1789d18a..2fdb262d4726 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -75,33 +75,6 @@ static int pxa27x_ohci_select_pmm( int mode ) return 0; } -/* - If you select PMM_PERPORT_MODE, you should set the port power - */ -static int pxa27x_ohci_set_port_power( int port ) -{ - if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE) - && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) { - UHCRHPS(port) |= 0x100; - return 0; - } - return -1; -} - -/* - If you select PMM_PERPORT_MODE, you should set the port power - */ -static int pxa27x_ohci_clear_port_power( int port ) -{ - if ( (pxa27x_ohci_pmm_state==PMM_PERPORT_MODE) - && (port>0) && (port<PXA_UHC_MAX_PORTNUM) ) { - UHCRHPS(port) |= 0x200; - return 0; - } - - return -1; -} - extern int usb_disabled(void); /*-------------------------------------------------------------------------*/ @@ -130,11 +103,17 @@ static void pxa27x_start_hc(struct platform_device *dev) Polarity Low to active low. Supply power to USB ports. */ UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) & ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE); + + pxa27x_ohci_pmm_state = PMM_PERPORT_MODE; } UHCHR &= ~UHCHR_SSE; UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE); + + /* Clear any OTG Pin Hold */ + if (PSSR & PSSR_OTGPH) + PSSR |= PSSR_OTGPH; } static void pxa27x_stop_hc(struct platform_device *dev) @@ -198,17 +177,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, pxa27x_start_hc(dev); /* Select Power Management Mode */ - pxa27x_ohci_select_pmm( PMM_PERPORT_MODE ); - - /* If choosing PMM_PERPORT_MODE, we should set the port power before we use it. */ - if (pxa27x_ohci_set_port_power(1) < 0) - printk(KERN_ERR "Setting port 1 power failed.\n"); - - if (pxa27x_ohci_clear_port_power(2) < 0) - printk(KERN_ERR "Setting port 2 power failed.\n"); - - if (pxa27x_ohci_clear_port_power(3) < 0) - printk(KERN_ERR "Setting port 3 power failed.\n"); + pxa27x_ohci_select_pmm(pxa27x_ohci_pmm_state); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -258,6 +227,9 @@ ohci_pxa27x_start (struct usb_hcd *hcd) ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci); + /* The value of NDP in roothub_a is incorrect on this hardware */ + ohci->num_ports = 3; + if ((ret = ohci_init(ohci)) < 0) return ret; diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c new file mode 100644 index 000000000000..3d9bcf78a9a4 --- /dev/null +++ b/drivers/usb/host/ohci-s3c2410.c @@ -0,0 +1,496 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> + * (C) Copyright 2002 Hewlett-Packard Company + * + * USB Bus Glue for Samsung S3C2410 + * + * Written by Christopher Hoover <ch@hpl.hp.com> + * Based on fragments of previous driver by Rusell King et al. + * + * Modified for S3C2410 from ohci-sa1111.c, ohci-omap.c and ohci-lh7a40.c + * by Ben Dooks, <ben@simtec.co.uk> + * Copyright (C) 2004 Simtec Electronics + * + * Thanks to basprog@mail.ru for updates to newer kernels + * + * This file is licenced under the GPL. +*/ + +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/hardware/clock.h> +#include <asm/arch/usb-control.h> + +#define valid_port(idx) ((idx) == 1 || (idx) == 2) + +/* clock device associated with the hcd */ + +static struct clk *clk; + +/* forward definitions */ + +static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc); + +/* conversion functions */ + +struct s3c2410_hcd_info *to_s3c2410_info(struct usb_hcd *hcd) +{ + return hcd->self.controller->platform_data; +} + +static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd) +{ + struct s3c2410_hcd_info *info = dev->dev.platform_data; + + dev_dbg(&dev->dev, "s3c2410_start_hc:\n"); + clk_enable(clk); + + if (info != NULL) { + info->hcd = hcd; + info->report_oc = s3c2410_hcd_oc; + + if (info->enable_oc != NULL) { + (info->enable_oc)(info, 1); + } + } +} + +static void s3c2410_stop_hc(struct platform_device *dev) +{ + struct s3c2410_hcd_info *info = dev->dev.platform_data; + + dev_dbg(&dev->dev, "s3c2410_stop_hc:\n"); + + if (info != NULL) { + info->report_oc = NULL; + info->hcd = NULL; + + if (info->enable_oc != NULL) { + (info->enable_oc)(info, 0); + } + } + + clk_disable(clk); +} + +/* ohci_s3c2410_hub_status_data + * + * update the status data from the hub with anything that + * has been detected by our system +*/ + +static int +ohci_s3c2410_hub_status_data (struct usb_hcd *hcd, char *buf) +{ + struct s3c2410_hcd_info *info = to_s3c2410_info(hcd); + struct s3c2410_hcd_port *port; + int orig; + int portno; + + orig = ohci_hub_status_data (hcd, buf); + + if (info == NULL) + return orig; + + port = &info->port[0]; + + /* mark any changed port as changed */ + + for (portno = 0; portno < 2; port++, portno++) { + if (port->oc_changed == 1 && + port->flags & S3C_HCDFLG_USED) { + dev_dbg(hcd->self.controller, + "oc change on port %d\n", portno); + + if (orig < 1) + orig = 1; + + buf[0] |= 1<<(portno+1); + } + } + + return orig; +} + +/* s3c2410_usb_set_power + * + * configure the power on a port, by calling the platform device + * routine registered with the platform device +*/ + +static void s3c2410_usb_set_power(struct s3c2410_hcd_info *info, + int port, int to) +{ + if (info == NULL) + return; + + if (info->power_control != NULL) { + info->port[port-1].power = to; + (info->power_control)(port-1, to); + } +} + +/* ohci_s3c2410_hub_control + * + * look at control requests to the hub, and see if we need + * to take any action or over-ride the results from the + * request. +*/ + +static int ohci_s3c2410_hub_control ( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength) +{ + struct s3c2410_hcd_info *info = to_s3c2410_info(hcd); + struct usb_hub_descriptor *desc; + int ret = -EINVAL; + u32 *data = (u32 *)buf; + + dev_dbg(hcd->self.controller, + "s3c2410_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n", + hcd, typeReq, wValue, wIndex, buf, wLength); + + /* if we are only an humble host without any special capabilites + * process the request straight away and exit */ + + if (info == NULL) { + ret = ohci_hub_control(hcd, typeReq, wValue, + wIndex, buf, wLength); + goto out; + } + + /* check the request to see if it needs handling */ + + switch (typeReq) { + case SetPortFeature: + if (wValue == USB_PORT_FEAT_POWER) { + dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n"); + s3c2410_usb_set_power(info, wIndex, 1); + goto out; + } + break; + + case ClearPortFeature: + switch (wValue) { + case USB_PORT_FEAT_C_OVER_CURRENT: + dev_dbg(hcd->self.controller, + "ClearPortFeature: C_OVER_CURRENT\n"); + + if (valid_port(wIndex)) { + info->port[wIndex-1].oc_changed = 0; + info->port[wIndex-1].oc_status = 0; + } + + goto out; + + case USB_PORT_FEAT_OVER_CURRENT: + dev_dbg(hcd->self.controller, + "ClearPortFeature: OVER_CURRENT\n"); + + if (valid_port(wIndex)) { + info->port[wIndex-1].oc_status = 0; + } + + goto out; + + case USB_PORT_FEAT_POWER: + dev_dbg(hcd->self.controller, + "ClearPortFeature: POWER\n"); + + if (valid_port(wIndex)) { + s3c2410_usb_set_power(info, wIndex, 0); + return 0; + } + } + break; + } + + ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); + if (ret) + goto out; + + switch (typeReq) { + case GetHubDescriptor: + + /* update the hub's descriptor */ + + desc = (struct usb_hub_descriptor *)buf; + + if (info->power_control == NULL) + return ret; + + dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n", + desc->wHubCharacteristics); + + /* remove the old configurations for power-switching, and + * over-current protection, and insert our new configuration + */ + + desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM); + desc->wHubCharacteristics |= cpu_to_le16(0x0001); + + if (info->enable_oc) { + desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM); + desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001); + } + + dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", + desc->wHubCharacteristics); + + return ret; + + case GetPortStatus: + /* check port status */ + + dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex); + + if (valid_port(wIndex)) { + if (info->port[wIndex-1].oc_changed) { + *data |= cpu_to_le32(RH_PS_OCIC); + } + + if (info->port[wIndex-1].oc_status) { + *data |= cpu_to_le32(RH_PS_POCI); + } + } + } + + out: + return ret; +} + +/* s3c2410_hcd_oc + * + * handle an over-current report +*/ + +static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc) +{ + struct s3c2410_hcd_port *port; + struct usb_hcd *hcd; + unsigned long flags; + int portno; + + if (info == NULL) + return; + + port = &info->port[0]; + hcd = info->hcd; + + local_irq_save(flags); + + for (portno = 0; portno < 2; port++, portno++) { + if (port_oc & (1<<portno) && + port->flags & S3C_HCDFLG_USED) { + port->oc_status = 1; + port->oc_changed = 1; + + /* ok, once over-current is detected, + the port needs to be powered down */ + s3c2410_usb_set_power(info, portno+1, 0); + } + } + + local_irq_restore(flags); +} + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/* + * usb_hcd_s3c2410_remove - shutdown processing for HCD + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_3c2410_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * +*/ + +void usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev) +{ + usb_remove_hcd(hcd); + s3c2410_stop_hc(dev); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); +} + +/** + * usb_hcd_s3c2410_probe - initialize S3C2410-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + */ +int usb_hcd_s3c2410_probe (const struct hc_driver *driver, + struct platform_device *dev) +{ + struct usb_hcd *hcd = NULL; + int retval; + + s3c2410_usb_set_power(dev->dev.platform_data, 1, 1); + s3c2410_usb_set_power(dev->dev.platform_data, 2, 1); + + hcd = usb_create_hcd(driver, &dev->dev, "s3c24xx"); + if (hcd == NULL) + return -ENOMEM; + + hcd->rsrc_start = dev->resource[0].start; + hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_err(&dev->dev, "request_mem_region failed"); + retval = -EBUSY; + goto err0; + } + + clk = clk_get(NULL, "usb-host"); + if (IS_ERR(clk)) { + dev_err(&dev->dev, "cannot get usb-host clock\n"); + retval = -ENOENT; + goto err1; + } + + clk_use(clk); + s3c2410_start_hc(dev, hcd); + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(&dev->dev, "ioremap failed\n"); + retval = -ENOMEM; + goto err2; + } + + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT); + if (retval != 0) + goto err2; + + return 0; + + err2: + s3c2410_stop_hc(dev); + iounmap(hcd->regs); + clk_unuse(clk); + clk_put(clk); + + err1: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + err0: + usb_put_hcd(hcd); + return retval; +} + +/*-------------------------------------------------------------------------*/ + +static int +ohci_s3c2410_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + if ((ret = ohci_init(ohci)) < 0) + return ret; + + if ((ret = ohci_run (ohci)) < 0) { + err ("can't start %s", hcd->self.bus_name); + ohci_stop (hcd); + return ret; + } + + return 0; +} + + +static const struct hc_driver ohci_s3c2410_hc_driver = { + .description = hcd_name, + .product_desc = "S3C24XX OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .start = ohci_s3c2410_start, + .stop = ohci_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_s3c2410_hub_status_data, + .hub_control = ohci_s3c2410_hub_control, + +#if defined(CONFIG_USB_SUSPEND) && 0 + .hub_suspend = ohci_hub_suspend, + .hub_resume = ohci_hub_resume, +#endif +}; + +/* device driver */ + +static int ohci_hcd_s3c2410_drv_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + return usb_hcd_s3c2410_probe(&ohci_s3c2410_hc_driver, pdev); +} + +static int ohci_hcd_s3c2410_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + usb_hcd_s3c2410_remove(hcd, pdev); + return 0; +} + +static struct device_driver ohci_hcd_s3c2410_driver = { + .name = "s3c2410-ohci", + .bus = &platform_bus_type, + .probe = ohci_hcd_s3c2410_drv_probe, + .remove = ohci_hcd_s3c2410_drv_remove, + /*.suspend = ohci_hcd_s3c2410_drv_suspend, */ + /*.resume = ohci_hcd_s3c2410_drv_resume, */ +}; + +static int __init ohci_hcd_s3c2410_init (void) +{ + return driver_register(&ohci_hcd_s3c2410_driver); +} + +static void __exit ohci_hcd_s3c2410_cleanup (void) +{ + driver_unregister(&ohci_hcd_s3c2410_driver); +} + +module_init (ohci_hcd_s3c2410_init); +module_exit (ohci_hcd_s3c2410_cleanup); diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 71cdd2262860..8a9b9d9209e9 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -383,6 +383,7 @@ struct ohci_hcd { /* * driver state */ + int num_ports; int load [NUM_INTS]; u32 hc_control; /* copy of hc control reg */ unsigned long next_statechange; /* suspend/resume */ diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 6c3f910bc307..d2a1fd40dfcb 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -815,7 +815,7 @@ static int sl811h_urb_enqueue( struct usb_hcd *hcd, struct usb_host_endpoint *hep, struct urb *urb, - int mem_flags + unsigned mem_flags ) { struct sl811 *sl811 = hcd_to_sl811(hcd); struct usb_device *udev = urb->dev; @@ -835,7 +835,7 @@ static int sl811h_urb_enqueue( /* avoid all allocations within spinlocks */ if (!hep->hcpriv) - ep = kcalloc(1, sizeof *ep, mem_flags); + ep = kzalloc(sizeof *ep, mem_flags); spin_lock_irqsave(&sl811->lock, flags); @@ -1781,9 +1781,9 @@ sl811h_suspend(struct device *dev, pm_message_t state, u32 phase) if (phase != SUSPEND_POWER_DOWN) return retval; - if (state <= PM_SUSPEND_MEM) + if (state.event == PM_EVENT_FREEZE) retval = sl811h_hub_suspend(hcd); - else + else if (state.event == PM_EVENT_SUSPEND) port_power(sl811, 0); if (retval == 0) dev->power.power_state = state; @@ -1802,7 +1802,7 @@ sl811h_resume(struct device *dev, u32 phase) /* with no "check to see if VBUS is still powered" board hook, * let's assume it'd only be powered to enable remote wakeup. */ - if (dev->power.power_state > PM_SUSPEND_MEM + if (dev->power.power_state.event == PM_EVENT_SUSPEND || !hcd->can_wakeup) { sl811->port1 = 0; port_power(sl811, 1); diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 269d8ef01459..38aebe361ca1 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -20,7 +20,6 @@ #include <linux/timer.h> #include <linux/ioport.h> -#include <pcmcia/version.h> #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/cistpl.h> @@ -389,11 +388,6 @@ static dev_link_t *sl811_cs_attach(void) dev_list = link; client_reg.dev_info = (dev_info_t *) &driver_name; client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; - client_reg.EventMask = - CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | - CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | - CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; - client_reg.event_handler = &sl811_cs_event; client_reg.Version = 0x0210; client_reg.event_callback_args.client_data = link; ret = pcmcia_register_client(&link->handle, &client_reg); @@ -418,6 +412,7 @@ static struct pcmcia_driver sl811_cs_driver = { .name = (char *)driver_name, }, .attach = sl811_cs_attach, + .event = sl811_cs_event, .detach = sl811_cs_detach, .id_table = sl811_ids, }; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 0d5d2545bf07..0c024898cbea 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -97,14 +97,9 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci); /* to make sure it doesn't hog all of the bandwidth */ #define DEPTH_INTERVAL 5 -static inline void restart_timer(struct uhci_hcd *uhci) -{ - mod_timer(&uhci->stall_timer, jiffies + msecs_to_jiffies(100)); -} - -#include "uhci-hub.c" #include "uhci-debug.c" #include "uhci-q.c" +#include "uhci-hub.c" /* * Make sure the controller is completely inactive, unable to @@ -160,7 +155,6 @@ static void hc_died(struct uhci_hcd *uhci) { reset_hc(uhci); uhci->hc_inaccessible = 1; - del_timer(&uhci->stall_timer); } /* @@ -287,8 +281,11 @@ __acquires(uhci->lock) /* Enable resume-detect interrupts if they work. * Then enter Global Suspend mode, still configured. */ - int_enable = (resume_detect_interrupts_are_broken(uhci) ? - 0 : USBINTR_RESUME); + uhci->working_RD = 1; + int_enable = USBINTR_RESUME; + if (resume_detect_interrupts_are_broken(uhci)) { + uhci->working_RD = int_enable = 0; + } outw(int_enable, uhci->io_addr + USBINTR); outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD); mb(); @@ -315,7 +312,6 @@ __acquires(uhci->lock) uhci->rh_state = new_state; uhci->is_stopped = UHCI_IS_STOPPED; - del_timer(&uhci->stall_timer); uhci_to_hcd(uhci)->poll_rh = !int_enable; uhci_scan_schedule(uhci, NULL); @@ -335,7 +331,6 @@ static void start_rh(struct uhci_hcd *uhci) mb(); uhci->rh_state = UHCI_RH_RUNNING; uhci_to_hcd(uhci)->poll_rh = 1; - restart_timer(uhci); } static void wakeup_rh(struct uhci_hcd *uhci) @@ -374,20 +369,6 @@ __acquires(uhci->lock) mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); } -static void stall_callback(unsigned long _uhci) -{ - struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci; - unsigned long flags; - - spin_lock_irqsave(&uhci->lock, flags); - uhci_scan_schedule(uhci, NULL); - check_fsbr(uhci); - - if (!uhci->is_stopped) - restart_timer(uhci); - spin_unlock_irqrestore(&uhci->lock, flags); -} - static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); @@ -418,8 +399,10 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) "host controller halted, " "very bad!\n"); hc_died(uhci); - spin_unlock_irqrestore(&uhci->lock, flags); - return IRQ_HANDLED; + + /* Force a callback in case there are + * pending unlinks */ + mod_timer(&hcd->rh_timer, jiffies); } spin_unlock_irqrestore(&uhci->lock, flags); } @@ -427,10 +410,11 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) if (status & USBSTS_RD) usb_hcd_poll_rh_status(hcd); - - spin_lock_irqsave(&uhci->lock, flags); - uhci_scan_schedule(uhci, regs); - spin_unlock_irqrestore(&uhci->lock, flags); + else { + spin_lock_irqsave(&uhci->lock, flags); + uhci_scan_schedule(uhci, regs); + spin_unlock_irqrestore(&uhci->lock, flags); + } return IRQ_HANDLED; } @@ -595,10 +579,6 @@ static int uhci_start(struct usb_hcd *hcd) init_waitqueue_head(&uhci->waitqh); - init_timer(&uhci->stall_timer); - uhci->stall_timer.function = stall_callback; - uhci->stall_timer.data = (unsigned long) uhci; - uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl), &dma_handle, 0); if (!uhci->fl) { @@ -745,11 +725,11 @@ static void uhci_stop(struct usb_hcd *hcd) struct uhci_hcd *uhci = hcd_to_uhci(hcd); spin_lock_irq(&uhci->lock); - reset_hc(uhci); + if (!uhci->hc_inaccessible) + reset_hc(uhci); uhci_scan_schedule(uhci, NULL); spin_unlock_irq(&uhci->lock); - del_timer_sync(&uhci->stall_timer); release_uhci(uhci); } @@ -811,13 +791,12 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message) */ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0); uhci->hc_inaccessible = 1; + hcd->poll_rh = 0; /* FIXME: Enable non-PME# remote wakeup? */ done: spin_unlock_irq(&uhci->lock); - if (rc == 0) - del_timer_sync(&hcd->rh_timer); return rc; } @@ -850,8 +829,11 @@ static int uhci_resume(struct usb_hcd *hcd) spin_unlock_irq(&uhci->lock); - if (hcd->poll_rh) + if (!uhci->working_RD) { + /* Suspended root hub needs to be polled */ + hcd->poll_rh = 1; usb_hcd_poll_rh_status(hcd); + } return 0; } #endif diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index bf9c5f9b508b..282f40b75881 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -345,9 +345,6 @@ enum uhci_rh_state { /* * This describes the full uhci information. - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. */ struct uhci_hcd { @@ -360,8 +357,6 @@ struct uhci_hcd { struct dma_pool *qh_pool; struct dma_pool *td_pool; - struct usb_bus *bus; - struct uhci_td *term_td; /* Terminating TD, see UHCI bug */ struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ @@ -380,6 +375,8 @@ struct uhci_hcd { unsigned int scan_in_progress:1; /* Schedule scan is running */ unsigned int need_rescan:1; /* Redo the schedule scan */ unsigned int hc_inaccessible:1; /* HC is suspended or dead */ + unsigned int working_RD:1; /* Suspended root hub doesn't + need to be polled */ /* Support for port suspend/resume/reset */ unsigned long port_c_suspend; /* Bit-arrays of ports */ @@ -405,9 +402,7 @@ struct uhci_hcd { /* List of URB's awaiting completion callback */ struct list_head complete_list; /* P: uhci->lock */ - int rh_numports; - - struct timer_list stall_timer; + int rh_numports; /* Number of root-hub ports */ wait_queue_head_t waitqh; /* endpoint_disable waiters */ }; diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 4eace2b19ddb..a71e48a66805 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -145,15 +145,16 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long flags; - int status; + int status = 0; spin_lock_irqsave(&uhci->lock, flags); - if (uhci->hc_inaccessible) { - status = 0; - goto done; - } + uhci_scan_schedule(uhci, NULL); + if (uhci->hc_inaccessible) + goto done; + check_fsbr(uhci); uhci_check_ports(uhci); + status = get_hub_status_data(uhci, buf); switch (uhci->rh_state) { diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 5f18084a116d..ea0d168a8c67 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -33,7 +33,7 @@ static void uhci_free_pending_tds(struct uhci_hcd *uhci); static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci) { if (uhci->is_stopped) - mod_timer(&uhci->stall_timer, jiffies); + mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); } @@ -1164,7 +1164,7 @@ static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb) static int uhci_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep, - struct urb *urb, int mem_flags) + struct urb *urb, unsigned mem_flags) { int ret; struct uhci_hcd *uhci = hcd_to_uhci(hcd); |
