WSL2-Linux-Kernel/drivers/usb/host/ohci-hub.c

801 строка
21 KiB
C
Исходник Обычный вид История

// SPDX-License-Identifier: GPL-1.0+
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under GPL
*/
/*-------------------------------------------------------------------------*/
/*
* OHCI Root Hub ... the nonsharable stuff
*/
#define dbg_port(hc,label,num,value) \
ohci_dbg (hc, \
"%s roothub.portstatus [%d] " \
"= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
label, num, value, \
(value & RH_PS_PRSC) ? " PRSC" : "", \
(value & RH_PS_OCIC) ? " OCIC" : "", \
(value & RH_PS_PSSC) ? " PSSC" : "", \
(value & RH_PS_PESC) ? " PESC" : "", \
(value & RH_PS_CSC) ? " CSC" : "", \
\
(value & RH_PS_LSDA) ? " LSDA" : "", \
(value & RH_PS_PPS) ? " PPS" : "", \
(value & RH_PS_PRS) ? " PRS" : "", \
(value & RH_PS_POCI) ? " POCI" : "", \
(value & RH_PS_PSS) ? " PSS" : "", \
\
(value & RH_PS_PES) ? " PES" : "", \
(value & RH_PS_CCS) ? " CCS" : "" \
);
/*-------------------------------------------------------------------------*/
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
USB: OHCI: redesign the TD done list This patch changes the way ohci-hcd handles the TD done list. In addition to relying on the TD pointers stored by the controller hardware, we need to handle TDs that the hardware has forgotten about. This means the list has to exist even while the dl_done_list() routine isn't running. That function essentially gets split in two: update_done_list() reads the TD pointers stored by the hardware and adds the TDs to the done list, and process_done_list() scans through the list to handle URB completions. When we detect a TD that the hardware forgot about, we will be able to add it to the done list manually and then process it normally. Since the list is really a queue, and because there can be a lot of TDs, keep the existing singly linked implementation. To insure that URBs are given back in order of submission, whenever a TD is added to the done list, all the preceding TDs for the same endpoint must be added as well (going back to the first one that isn't already on the done list). The done list manipulations must all be protected by the private lock. The scope of the lock is expanded in preparation for the watchdog routine to be added in a later patch. We have to be more careful about giving back unlinked URBs. Since TDs may be added to the done list by the watchdog routine and not in response to a controller interrupt, we have to check explicitly to make sure all the URB's TDs that were added to the done list have been processed before giving back the URB. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-19 00:25:59 +04:00
static void update_done_list(struct ohci_hcd *);
static void ohci_work(struct ohci_hcd *);
#ifdef CONFIG_PM
static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
__releases(ohci->lock)
__acquires(ohci->lock)
{
int status = 0;
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_RESUME:
ohci_dbg (ohci, "resume/suspend?\n");
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci->hc_control |= OHCI_USB_RESET;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
fallthrough;
case OHCI_USB_RESET:
status = -EBUSY;
ohci_dbg (ohci, "needs reinit!\n");
goto done;
case OHCI_USB_SUSPEND:
if (!ohci->autostop) {
ohci_dbg (ohci, "already suspended\n");
goto done;
}
}
ohci_dbg (ohci, "%s root hub\n",
autostop ? "auto-stop" : "suspend");
/* First stop any processing */
if (!autostop && (ohci->hc_control & OHCI_SCHED_ENABLES)) {
ohci->hc_control &= ~OHCI_SCHED_ENABLES;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrstatus);
/* sched disables take effect on the next frame,
* then the last WDH could take 6+ msec
*/
ohci_dbg (ohci, "stopping schedules ...\n");
ohci->autostop = 0;
spin_unlock_irq (&ohci->lock);
msleep (8);
spin_lock_irq (&ohci->lock);
}
USB: OHCI: redesign the TD done list This patch changes the way ohci-hcd handles the TD done list. In addition to relying on the TD pointers stored by the controller hardware, we need to handle TDs that the hardware has forgotten about. This means the list has to exist even while the dl_done_list() routine isn't running. That function essentially gets split in two: update_done_list() reads the TD pointers stored by the hardware and adds the TDs to the done list, and process_done_list() scans through the list to handle URB completions. When we detect a TD that the hardware forgot about, we will be able to add it to the done list manually and then process it normally. Since the list is really a queue, and because there can be a lot of TDs, keep the existing singly linked implementation. To insure that URBs are given back in order of submission, whenever a TD is added to the done list, all the preceding TDs for the same endpoint must be added as well (going back to the first one that isn't already on the done list). The done list manipulations must all be protected by the private lock. The scope of the lock is expanded in preparation for the watchdog routine to be added in a later patch. We have to be more careful about giving back unlinked URBs. Since TDs may be added to the done list by the watchdog routine and not in response to a controller interrupt, we have to check explicitly to make sure all the URB's TDs that were added to the done list have been processed before giving back the URB. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-07-19 00:25:59 +04:00
update_done_list(ohci);
ohci_work(ohci);
/*
* Some controllers don't handle "global" suspend properly if
* there are unsuspended ports. For these controllers, put all
* the enabled ports into suspend before suspending the root hub.
*/
if (ohci->flags & OHCI_QUIRK_GLOBAL_SUSPEND) {
__hc32 __iomem *portstat = ohci->regs->roothub.portstatus;
int i;
unsigned temp;
for (i = 0; i < ohci->num_ports; (++i, ++portstat)) {
temp = ohci_readl(ohci, portstat);
if ((temp & (RH_PS_PES | RH_PS_PSS)) ==
RH_PS_PES)
ohci_writel(ohci, RH_PS_PSS, portstat);
}
}
/* maybe resume can wake root hub */
if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) {
ohci->hc_control |= OHCI_CTRL_RWE;
} else {
ohci_writel(ohci, OHCI_INTR_RHSC | OHCI_INTR_RD,
&ohci->regs->intrdisable);
ohci->hc_control &= ~OHCI_CTRL_RWE;
}
/* Suspend hub ... this is the "global (to this bus) suspend" mode,
* which doesn't imply ports will first be individually suspended.
*/
ohci->hc_control &= ~OHCI_CTRL_HCFS;
ohci->hc_control |= OHCI_USB_SUSPEND;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
/* no resumes until devices finish suspending */
if (!autostop) {
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
ohci->autostop = 0;
ohci->rh_state = OHCI_RH_SUSPENDED;
}
done:
return status;
}
static inline struct ed *find_head (struct ed *ed)
{
/* for bulk and control lists */
while (ed->ed_prev)
ed = ed->ed_prev;
return ed;
}
/* caller has locked the root hub */
static int ohci_rh_resume (struct ohci_hcd *ohci)
__releases(ohci->lock)
__acquires(ohci->lock)
{
struct usb_hcd *hcd = ohci_to_hcd (ohci);
u32 temp, enables;
int status = -EINPROGRESS;
int autostopped = ohci->autostop;
ohci->autostop = 0;
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
/* this can happen after resuming a swsusp snapshot */
if (ohci->rh_state != OHCI_RH_RUNNING) {
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
ohci->hc_control);
status = -EBUSY;
/* this happens when pmcore resumes HC then root */
} else {
ohci_dbg (ohci, "duplicate resume\n");
status = 0;
}
} else switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_SUSPEND:
ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
ohci->hc_control |= OHCI_USB_RESUME;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
ohci_dbg (ohci, "%s root hub\n",
autostopped ? "auto-start" : "resume");
break;
case OHCI_USB_RESUME:
/* HCFS changes sometime after INTR_RD */
ohci_dbg(ohci, "%swakeup root hub\n",
autostopped ? "auto-" : "");
break;
case OHCI_USB_OPER:
/* this can happen after resuming a swsusp snapshot */
ohci_dbg (ohci, "snapshot resume? reinit\n");
status = -EBUSY;
break;
default: /* RESET, we lost power */
ohci_dbg (ohci, "lost power\n");
status = -EBUSY;
}
if (status == -EBUSY) {
if (!autostopped) {
spin_unlock_irq (&ohci->lock);
status = ohci_restart (ohci);
usb_root_hub_lost_power(hcd->self.root_hub);
spin_lock_irq (&ohci->lock);
}
return status;
}
if (status != -EINPROGRESS)
return status;
if (autostopped)
goto skip_resume;
spin_unlock_irq (&ohci->lock);
/* Some controllers (lucent erratum) need extra-long delays */
msleep (20 /* usb 11.5.1.10 */ + 12 /* 32 msec counter */ + 1);
temp = ohci_readl (ohci, &ohci->regs->control);
temp &= OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
ohci_err (ohci, "controller won't resume\n");
spin_lock_irq(&ohci->lock);
return -EBUSY;
}
/* disable old schedule state, reinit from scratch */
ohci_writel (ohci, 0, &ohci->regs->ed_controlhead);
ohci_writel (ohci, 0, &ohci->regs->ed_controlcurrent);
ohci_writel (ohci, 0, &ohci->regs->ed_bulkhead);
ohci_writel (ohci, 0, &ohci->regs->ed_bulkcurrent);
ohci_writel (ohci, 0, &ohci->regs->ed_periodcurrent);
ohci_writel (ohci, (u32) ohci->hcca_dma, &ohci->regs->hcca);
/* Sometimes PCI D3 suspend trashes frame timings ... */
periodic_reinit (ohci);
/*
* The following code is executed with ohci->lock held and
* irqs disabled if and only if autostopped is true. This
* will cause sparse to warn about a "context imbalance".
*/
skip_resume:
/* interrupts might have been disabled */
ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
if (ohci->ed_rm_list)
ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
/* Then re-enable operations */
ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
if (!autostopped)
msleep (3);
temp = ohci->hc_control;
temp &= OHCI_CTRL_RWC;
temp |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
ohci->hc_control = temp;
ohci_writel (ohci, temp, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
/* TRSMRCY */
if (!autostopped) {
msleep (10);
spin_lock_irq (&ohci->lock);
}
/* now ohci->lock is always held and irqs are always disabled */
/* keep it alive for more than ~5x suspend + resume costs */
ohci->next_statechange = jiffies + STATECHANGE_DELAY;
/* maybe turn schedules back on */
enables = 0;
temp = 0;
if (!ohci->ed_rm_list) {
if (ohci->ed_controltail) {
ohci_writel (ohci,
find_head (ohci->ed_controltail)->dma,
&ohci->regs->ed_controlhead);
enables |= OHCI_CTRL_CLE;
temp |= OHCI_CLF;
}
if (ohci->ed_bulktail) {
ohci_writel (ohci, find_head (ohci->ed_bulktail)->dma,
&ohci->regs->ed_bulkhead);
enables |= OHCI_CTRL_BLE;
temp |= OHCI_BLF;
}
}
if (hcd->self.bandwidth_isoc_reqs || hcd->self.bandwidth_int_reqs)
enables |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
if (enables) {
ohci_dbg (ohci, "restarting schedules ... %08x\n", enables);
ohci->hc_control |= enables;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
if (temp)
ohci_writel (ohci, temp, &ohci->regs->cmdstatus);
(void) ohci_readl (ohci, &ohci->regs->control);
}
ohci->rh_state = OHCI_RH_RUNNING;
return 0;
}
static int ohci_bus_suspend (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int rc;
spin_lock_irq (&ohci->lock);
if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
rc = -ESHUTDOWN;
else
rc = ohci_rh_suspend (ohci, 0);
spin_unlock_irq (&ohci->lock);
ohci-hcd: Fix race condition caused by ohci_urb_enqueue() and io_watchdog_func() Running io_watchdog_func() while ohci_urb_enqueue() is running can cause a race condition where ohci->prev_frame_no is corrupted and the watchdog can mis-detect following error: ohci-platform 664a0800.usb: frame counter not updating; disabled ohci-platform 664a0800.usb: HC died; cleaning up Specifically, following scenario causes a race condition: 1. ohci_urb_enqueue() calls spin_lock_irqsave(&ohci->lock, flags) and enters the critical section 2. ohci_urb_enqueue() calls timer_pending(&ohci->io_watchdog) and it returns false 3. ohci_urb_enqueue() sets ohci->prev_frame_no to a frame number read by ohci_frame_no(ohci) 4. ohci_urb_enqueue() schedules io_watchdog_func() with mod_timer() 5. ohci_urb_enqueue() calls spin_unlock_irqrestore(&ohci->lock, flags) and exits the critical section 6. Later, ohci_urb_enqueue() is called 7. ohci_urb_enqueue() calls spin_lock_irqsave(&ohci->lock, flags) and enters the critical section 8. The timer scheduled on step 4 expires and io_watchdog_func() runs 9. io_watchdog_func() calls spin_lock_irqsave(&ohci->lock, flags) and waits on it because ohci_urb_enqueue() is already in the critical section on step 7 10. ohci_urb_enqueue() calls timer_pending(&ohci->io_watchdog) and it returns false 11. ohci_urb_enqueue() sets ohci->prev_frame_no to new frame number read by ohci_frame_no(ohci) because the frame number proceeded between step 3 and 6 12. ohci_urb_enqueue() schedules io_watchdog_func() with mod_timer() 13. ohci_urb_enqueue() calls spin_unlock_irqrestore(&ohci->lock, flags) and exits the critical section, then wake up io_watchdog_func() which is waiting on step 9 14. io_watchdog_func() enters the critical section 15. io_watchdog_func() calls ohci_frame_no(ohci) and set frame_no variable to the frame number 16. io_watchdog_func() compares frame_no and ohci->prev_frame_no On step 16, because this calling of io_watchdog_func() is scheduled on step 4, the frame number set in ohci->prev_frame_no is expected to the number set on step 3. However, ohci->prev_frame_no is overwritten on step 11. Because step 16 is executed soon after step 11, the frame number might not proceed, so ohci->prev_frame_no must equals to frame_no. To address above scenario, this patch introduces a special sentinel value IO_WATCHDOG_OFF and set this value to ohci->prev_frame_no when the watchdog is not pending or running. When ohci_urb_enqueue() schedules the watchdog (step 4 and 12 above), it compares ohci->prev_frame_no to IO_WATCHDOG_OFF so that ohci->prev_frame_no is not overwritten while io_watchdog_func() is running. Signed-off-by: Shigeru Yoshida <Shigeru.Yoshida@windriver.com> Signed-off-by: Haiqing Bai <Haiqing.Bai@windriver.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-02-02 08:51:39 +03:00
if (rc == 0) {
del_timer_sync(&ohci->io_watchdog);
ohci-hcd: Fix race condition caused by ohci_urb_enqueue() and io_watchdog_func() Running io_watchdog_func() while ohci_urb_enqueue() is running can cause a race condition where ohci->prev_frame_no is corrupted and the watchdog can mis-detect following error: ohci-platform 664a0800.usb: frame counter not updating; disabled ohci-platform 664a0800.usb: HC died; cleaning up Specifically, following scenario causes a race condition: 1. ohci_urb_enqueue() calls spin_lock_irqsave(&ohci->lock, flags) and enters the critical section 2. ohci_urb_enqueue() calls timer_pending(&ohci->io_watchdog) and it returns false 3. ohci_urb_enqueue() sets ohci->prev_frame_no to a frame number read by ohci_frame_no(ohci) 4. ohci_urb_enqueue() schedules io_watchdog_func() with mod_timer() 5. ohci_urb_enqueue() calls spin_unlock_irqrestore(&ohci->lock, flags) and exits the critical section 6. Later, ohci_urb_enqueue() is called 7. ohci_urb_enqueue() calls spin_lock_irqsave(&ohci->lock, flags) and enters the critical section 8. The timer scheduled on step 4 expires and io_watchdog_func() runs 9. io_watchdog_func() calls spin_lock_irqsave(&ohci->lock, flags) and waits on it because ohci_urb_enqueue() is already in the critical section on step 7 10. ohci_urb_enqueue() calls timer_pending(&ohci->io_watchdog) and it returns false 11. ohci_urb_enqueue() sets ohci->prev_frame_no to new frame number read by ohci_frame_no(ohci) because the frame number proceeded between step 3 and 6 12. ohci_urb_enqueue() schedules io_watchdog_func() with mod_timer() 13. ohci_urb_enqueue() calls spin_unlock_irqrestore(&ohci->lock, flags) and exits the critical section, then wake up io_watchdog_func() which is waiting on step 9 14. io_watchdog_func() enters the critical section 15. io_watchdog_func() calls ohci_frame_no(ohci) and set frame_no variable to the frame number 16. io_watchdog_func() compares frame_no and ohci->prev_frame_no On step 16, because this calling of io_watchdog_func() is scheduled on step 4, the frame number set in ohci->prev_frame_no is expected to the number set on step 3. However, ohci->prev_frame_no is overwritten on step 11. Because step 16 is executed soon after step 11, the frame number might not proceed, so ohci->prev_frame_no must equals to frame_no. To address above scenario, this patch introduces a special sentinel value IO_WATCHDOG_OFF and set this value to ohci->prev_frame_no when the watchdog is not pending or running. When ohci_urb_enqueue() schedules the watchdog (step 4 and 12 above), it compares ohci->prev_frame_no to IO_WATCHDOG_OFF so that ohci->prev_frame_no is not overwritten while io_watchdog_func() is running. Signed-off-by: Shigeru Yoshida <Shigeru.Yoshida@windriver.com> Signed-off-by: Haiqing Bai <Haiqing.Bai@windriver.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-02-02 08:51:39 +03:00
ohci->prev_frame_no = IO_WATCHDOG_OFF;
}
return rc;
}
static int ohci_bus_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int rc;
if (time_before (jiffies, ohci->next_statechange))
msleep(5);
spin_lock_irq (&ohci->lock);
if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
rc = -ESHUTDOWN;
else
rc = ohci_rh_resume (ohci);
spin_unlock_irq (&ohci->lock);
/* poll until we know a device is connected or we autostop */
if (rc == 0)
usb_hcd_poll_rh_status(hcd);
return rc;
}
/* Carry out polling-, autostop-, and autoresume-related state changes */
static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
USB: OHCI: fix endless polling behavior This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-10-09 23:40:23 +04:00
int any_connected, int rhsc_status)
{
int poll_rh = 1;
USB: OHCI: fix endless polling behavior This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-10-09 23:40:23 +04:00
int rhsc_enable;
/* Some broken controllers never turn off RHSC in the interrupt
* status register. For their sake we won't re-enable RHSC
* interrupts if the interrupt bit is already active.
*/
rhsc_enable = ohci_readl(ohci, &ohci->regs->intrenable) &
OHCI_INTR_RHSC;
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_OPER:
/* If no status changes are pending, enable RHSC interrupts. */
if (!rhsc_enable && !rhsc_status && !changed) {
rhsc_enable = OHCI_INTR_RHSC;
ohci_writel(ohci, rhsc_enable, &ohci->regs->intrenable);
}
/* Keep on polling until we know a device is connected
* and RHSC is enabled, or until we autostop.
*/
if (!ohci->autostop) {
if (any_connected ||
!device_may_wakeup(&ohci_to_hcd(ohci)
->self.root_hub->dev)) {
if (rhsc_enable)
poll_rh = 0;
} else {
ohci->autostop = 1;
ohci->next_statechange = jiffies + HZ;
}
/* if no devices have been attached for one second, autostop */
} else {
if (changed || any_connected) {
ohci->autostop = 0;
ohci->next_statechange = jiffies +
STATECHANGE_DELAY;
} else if (time_after_eq(jiffies,
ohci->next_statechange)
&& !ohci->ed_rm_list
&& !(ohci->hc_control &
OHCI_SCHED_ENABLES)) {
ohci_rh_suspend(ohci, 1);
if (rhsc_enable)
poll_rh = 0;
}
}
break;
case OHCI_USB_SUSPEND:
case OHCI_USB_RESUME:
/* if there is a port change, autostart or ask to be resumed */
if (changed) {
if (ohci->autostop)
ohci_rh_resume(ohci);
else
usb_hcd_resume_root_hub(ohci_to_hcd(ohci));
USB: OHCI: fix endless polling behavior This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-10-09 23:40:23 +04:00
/* If remote wakeup is disabled, stop polling */
} else if (!ohci->autostop &&
!ohci_to_hcd(ohci)->self.root_hub->
do_remote_wakeup) {
poll_rh = 0;
} else {
USB: OHCI: fix endless polling behavior This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-10-09 23:40:23 +04:00
/* If no status changes are pending,
* enable RHSC interrupts
*/
if (!rhsc_enable && !rhsc_status) {
rhsc_enable = OHCI_INTR_RHSC;
ohci_writel(ohci, rhsc_enable,
&ohci->regs->intrenable);
}
USB: OHCI: fix endless polling behavior This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-10-09 23:40:23 +04:00
/* Keep polling until RHSC is enabled */
if (rhsc_enable)
poll_rh = 0;
}
break;
}
return poll_rh;
}
#else /* CONFIG_PM */
static inline int ohci_rh_resume(struct ohci_hcd *ohci)
{
return 0;
}
/* Carry out polling-related state changes.
* autostop isn't used when CONFIG_PM is turned off.
*/
static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
USB: OHCI: fix endless polling behavior This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-10-09 23:40:23 +04:00
int any_connected, int rhsc_status)
{
/* If RHSC is enabled, don't poll */
if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)
return 0;
USB: OHCI: fix endless polling behavior This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-10-09 23:40:23 +04:00
/* If status changes are pending, continue polling.
* Conversely, if no status changes are pending but the RHSC
* status bit was set, then RHSC may be broken so continue polling.
*/
if (changed || rhsc_status)
return 1;
/* It's safe to re-enable RHSC interrupts */
ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
return 0;
}
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
/* build "status change" packet (one or two bytes) from HC registers */
int ohci_hub_status_data(struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int i, changed = 0, length = 1;
int any_connected = 0;
USB: OHCI: fix endless polling behavior This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-10-09 23:40:23 +04:00
int rhsc_status;
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
if (!HCD_HW_ACCESSIBLE(hcd))
goto done;
/* 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;
}
/* init status */
if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
buf [0] = changed = 1;
else
buf [0] = 0;
if (ohci->num_ports > 7) {
buf [1] = 0;
length++;
}
USB: OHCI: fix endless polling behavior This patch (as1149) fixes an obscure problem in OHCI polling. In the current code, if the RHSC interrupt status flag turns on at a time when RHSC interrupts are disabled, it will remain on forever: The interrupt handler is the only place where RHSC status gets turned back off; The interrupt handler won't turn RHSC status off because it doesn't turn off status flags if the corresponding interrupt isn't enabled; RHSC interrupts will never get enabled because ohci_root_hub_state_changes() doesn't reenable RHSC if RHSC status is on! As a result we will continue polling indefinitely instead of reverting to interrupt-driven operation, and the root hub will not autosuspend. This particular sequence of events is not at all unusual; in fact plugging a USB device into an OHCI controller will usually cause it to occur. Of course, this is a bug. The proper thing to do is to turn off RHSC status just before reading the actual port status values. That way either a port status change will be detected (if it occurs before the status read) or it will turn RHSC back on. Possibly both, but that won't hurt anything. We can still check for systems in which RHSC is totally broken, by re-reading RHSC after clearing it and before reading the port statuses. (This re-read has to be done anyway, to post the earlier write.) If RHSC is on but no port-change statuses are set, then we know that RHSC is broken and we can avoid re-enabling it. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-10-09 23:40:23 +04:00
/* Clear the RHSC status flag before reading the port statuses */
ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus);
rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) &
OHCI_INTR_RHSC;
/* look at each port */
for (i = 0; i < ohci->num_ports; i++) {
u32 status = roothub_portstatus (ohci, i);
/* can't autostop if ports are connected */
any_connected |= (status & RH_PS_CCS);
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
| RH_PS_OCIC | RH_PS_PRSC)) {
changed = 1;
if (i < 7)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
}
}
if (ohci_root_hub_state_changes(ohci, changed,
any_connected, rhsc_status))
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
else
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
done:
spin_unlock_irqrestore (&ohci->lock, flags);
return changed ? length : 0;
}
EXPORT_SYMBOL_GPL(ohci_hub_status_data);
/*-------------------------------------------------------------------------*/
static void
ohci_hub_descriptor (
struct ohci_hcd *ohci,
struct usb_hub_descriptor *desc
) {
u32 rh = roothub_a (ohci);
u16 temp;
desc->bDescriptorType = USB_DT_HUB;
desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
desc->bHubContrCurrent = 0;
desc->bNbrPorts = ohci->num_ports;
temp = 1 + (ohci->num_ports / 8);
desc->bDescLength = 7 + 2 * temp;
temp = HUB_CHAR_COMMON_LPSM | HUB_CHAR_COMMON_OCPM;
if (rh & RH_A_NPS) /* no power switching? */
temp |= HUB_CHAR_NO_LPSM;
if (rh & RH_A_PSM) /* per-port power switching? */
temp |= HUB_CHAR_INDV_PORT_LPSM;
if (rh & RH_A_NOCP) /* no overcurrent reporting? */
temp |= HUB_CHAR_NO_OCPM;
else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
temp |= HUB_CHAR_INDV_PORT_OCPM;
desc->wHubCharacteristics = cpu_to_le16(temp);
/* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
rh = roothub_b (ohci);
USB 3.0 Hub Changes Update the USB core to deal with USB 3.0 hubs. These hubs have a slightly different hub descriptor than USB 2.0 hubs, with a fixed (rather than variable length) size. Change the USB core's hub descriptor to have a union for the last fields that differ. Change the host controller drivers that access those last fields (DeviceRemovable and PortPowerCtrlMask) to use the union. Translate the new version of the hub port status field into the old version that khubd understands. (Note: we need to fix it to translate the roothub's port status once we stop converting it to USB 2.0 hub status internally.) Add new code to handle link state change status. Send out new control messages that are needed for USB 3.0 hubs, like Set Hub Depth. This patch is a modified version of the original patch submitted by John Youn. It's updated to reflect the removal of the "bitmap" #define, and change the hub descriptor accesses of a couple new host controller drivers. Signed-off-by: John Youn <johnyoun@synopsys.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> Cc: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> Cc: Tony Olech <tony.olech@elandigitalsystems.com> Cc: "Robert P. J. Day" <rpjday@crashcourse.ca> Cc: Max Vozeler <mvz@vozeler.com> Cc: Tejun Heo <tj@kernel.org> Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Cc: Rodolfo Giometti <giometti@linux.it> Cc: Mike Frysinger <vapier@gentoo.org> Cc: Anton Vorontsov <avorontsov@mvista.com> Cc: Sebastian Siewior <bigeasy@linutronix.de> Cc: Lothar Wassmann <LW@KARO-electronics.de> Cc: Olav Kongas <ok@artecdesign.ee> Cc: Martin Fuzzey <mfuzzey@gmail.com> Cc: Alan Stern <stern@rowland.harvard.edu> Cc: David Brownell <dbrownell@users.sourceforge.net>
2001-09-17 11:00:00 +04:00
memset(desc->u.hs.DeviceRemovable, 0xff,
sizeof(desc->u.hs.DeviceRemovable));
desc->u.hs.DeviceRemovable[0] = rh & RH_B_DR;
if (ohci->num_ports > 7) {
USB 3.0 Hub Changes Update the USB core to deal with USB 3.0 hubs. These hubs have a slightly different hub descriptor than USB 2.0 hubs, with a fixed (rather than variable length) size. Change the USB core's hub descriptor to have a union for the last fields that differ. Change the host controller drivers that access those last fields (DeviceRemovable and PortPowerCtrlMask) to use the union. Translate the new version of the hub port status field into the old version that khubd understands. (Note: we need to fix it to translate the roothub's port status once we stop converting it to USB 2.0 hub status internally.) Add new code to handle link state change status. Send out new control messages that are needed for USB 3.0 hubs, like Set Hub Depth. This patch is a modified version of the original patch submitted by John Youn. It's updated to reflect the removal of the "bitmap" #define, and change the hub descriptor accesses of a couple new host controller drivers. Signed-off-by: John Youn <johnyoun@synopsys.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> Cc: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> Cc: Tony Olech <tony.olech@elandigitalsystems.com> Cc: "Robert P. J. Day" <rpjday@crashcourse.ca> Cc: Max Vozeler <mvz@vozeler.com> Cc: Tejun Heo <tj@kernel.org> Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Cc: Rodolfo Giometti <giometti@linux.it> Cc: Mike Frysinger <vapier@gentoo.org> Cc: Anton Vorontsov <avorontsov@mvista.com> Cc: Sebastian Siewior <bigeasy@linutronix.de> Cc: Lothar Wassmann <LW@KARO-electronics.de> Cc: Olav Kongas <ok@artecdesign.ee> Cc: Martin Fuzzey <mfuzzey@gmail.com> Cc: Alan Stern <stern@rowland.harvard.edu> Cc: David Brownell <dbrownell@users.sourceforge.net>
2001-09-17 11:00:00 +04:00
desc->u.hs.DeviceRemovable[1] = (rh & RH_B_DR) >> 8;
desc->u.hs.DeviceRemovable[2] = 0xff;
} else
USB 3.0 Hub Changes Update the USB core to deal with USB 3.0 hubs. These hubs have a slightly different hub descriptor than USB 2.0 hubs, with a fixed (rather than variable length) size. Change the USB core's hub descriptor to have a union for the last fields that differ. Change the host controller drivers that access those last fields (DeviceRemovable and PortPowerCtrlMask) to use the union. Translate the new version of the hub port status field into the old version that khubd understands. (Note: we need to fix it to translate the roothub's port status once we stop converting it to USB 2.0 hub status internally.) Add new code to handle link state change status. Send out new control messages that are needed for USB 3.0 hubs, like Set Hub Depth. This patch is a modified version of the original patch submitted by John Youn. It's updated to reflect the removal of the "bitmap" #define, and change the hub descriptor accesses of a couple new host controller drivers. Signed-off-by: John Youn <johnyoun@synopsys.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Cc: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> Cc: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> Cc: Tony Olech <tony.olech@elandigitalsystems.com> Cc: "Robert P. J. Day" <rpjday@crashcourse.ca> Cc: Max Vozeler <mvz@vozeler.com> Cc: Tejun Heo <tj@kernel.org> Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> Cc: Rodolfo Giometti <giometti@linux.it> Cc: Mike Frysinger <vapier@gentoo.org> Cc: Anton Vorontsov <avorontsov@mvista.com> Cc: Sebastian Siewior <bigeasy@linutronix.de> Cc: Lothar Wassmann <LW@KARO-electronics.de> Cc: Olav Kongas <ok@artecdesign.ee> Cc: Martin Fuzzey <mfuzzey@gmail.com> Cc: Alan Stern <stern@rowland.harvard.edu> Cc: David Brownell <dbrownell@users.sourceforge.net>
2001-09-17 11:00:00 +04:00
desc->u.hs.DeviceRemovable[1] = 0xff;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_OTG
static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
u32 status;
if (!port)
return -EINVAL;
port--;
/* start port reset before HNP protocol times out */
status = ohci_readl(ohci, &ohci->regs->roothub.portstatus [port]);
if (!(status & RH_PS_CCS))
return -ENODEV;
/* hub_wq will finish the reset later */
ohci_writel(ohci, RH_PS_PRS, &ohci->regs->roothub.portstatus [port]);
return 0;
}
#else
#define ohci_start_port_reset NULL
#endif
/*-------------------------------------------------------------------------*/
/* See usb 7.1.7.5: root hubs must issue at least 50 msec reset signaling,
* not necessarily continuous ... to guard against resume signaling.
*/
#define PORT_RESET_MSEC 50
/* this timer value might be vendor-specific ... */
#define PORT_RESET_HW_MSEC 10
/* wrap-aware logic morphed from <linux/jiffies.h> */
#define tick_before(t1,t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
/* called from some task, normally hub_wq */
static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
{
__hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port];
u32 temp = 0;
u16 now = ohci_readl(ohci, &ohci->regs->fmnumber);
u16 reset_done = now + PORT_RESET_MSEC;
int limit_1 = DIV_ROUND_UP(PORT_RESET_MSEC, PORT_RESET_HW_MSEC);
/* build a "continuous enough" reset signal, with up to
* 3msec gap between pulses. scheduler HZ==100 must work;
* this might need to be deadline-scheduled.
*/
do {
int limit_2;
/* spin until any current reset finishes */
limit_2 = PORT_RESET_HW_MSEC * 2;
while (--limit_2 >= 0) {
temp = ohci_readl (ohci, portstat);
/* handle e.g. CardBus eject */
if (temp == ~(u32)0)
return -ESHUTDOWN;
if (!(temp & RH_PS_PRS))
break;
udelay (500);
}
/* timeout (a hardware error) has been observed when
* EHCI sets CF while this driver is resetting a port;
* presumably other disconnect paths might do it too.
*/
if (limit_2 < 0) {
ohci_dbg(ohci,
"port[%d] reset timeout, stat %08x\n",
port, temp);
break;
}
if (!(temp & RH_PS_CCS))
break;
if (temp & RH_PS_PRSC)
ohci_writel (ohci, RH_PS_PRSC, portstat);
/* start the next reset, sleep till it's probably done */
ohci_writel (ohci, RH_PS_PRS, portstat);
msleep(PORT_RESET_HW_MSEC);
now = ohci_readl(ohci, &ohci->regs->fmnumber);
} while (tick_before(now, reset_done) && --limit_1 >= 0);
/* caller synchronizes using PRSC ... and handles PRS
* still being set when this returns.
*/
return 0;
}
int ohci_hub_control(
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength
) {
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ports = ohci->num_ports;
u32 temp;
int retval = 0;
if (unlikely(!HCD_HW_ACCESSIBLE(hcd)))
return -ESHUTDOWN;
switch (typeReq) {
case ClearHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
ohci_writel (ohci, RH_HS_OCIC,
&ohci->regs->roothub.status);
break;
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case ClearPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
switch (wValue) {
case USB_PORT_FEAT_ENABLE:
temp = RH_PS_CCS;
break;
case USB_PORT_FEAT_C_ENABLE:
temp = RH_PS_PESC;
break;
case USB_PORT_FEAT_SUSPEND:
temp = RH_PS_POCI;
break;
case USB_PORT_FEAT_C_SUSPEND:
temp = RH_PS_PSSC;
break;
case USB_PORT_FEAT_POWER:
temp = RH_PS_LSDA;
break;
case USB_PORT_FEAT_C_CONNECTION:
temp = RH_PS_CSC;
break;
case USB_PORT_FEAT_C_OVER_CURRENT:
temp = RH_PS_OCIC;
break;
case USB_PORT_FEAT_C_RESET:
temp = RH_PS_PRSC;
break;
default:
goto error;
}
ohci_writel (ohci, temp,
&ohci->regs->roothub.portstatus [wIndex]);
// ohci_readl (ohci, &ohci->regs->roothub.portstatus [wIndex]);
break;
case GetHubDescriptor:
ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
break;
case GetHubStatus:
temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
put_unaligned_le32(temp, buf);
break;
case GetPortStatus:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
temp = roothub_portstatus (ohci, wIndex);
put_unaligned_le32(temp, buf);
if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
dbg_port(ohci, "GetStatus", wIndex, temp);
break;
case SetHubFeature:
switch (wValue) {
case C_HUB_OVER_CURRENT:
// FIXME: this can be cleared, yes?
case C_HUB_LOCAL_POWER:
break;
default:
goto error;
}
break;
case SetPortFeature:
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
#ifdef CONFIG_USB_OTG
if (hcd->self.otg_port == (wIndex + 1)
&& hcd->self.b_hnp_enable)
ohci->start_hnp(ohci);
else
#endif
ohci_writel (ohci, RH_PS_PSS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
case USB_PORT_FEAT_POWER:
ohci_writel (ohci, RH_PS_PPS,
&ohci->regs->roothub.portstatus [wIndex]);
break;
case USB_PORT_FEAT_RESET:
retval = root_port_reset (ohci, wIndex);
break;
default:
goto error;
}
break;
default:
error:
/* "protocol stall" on error */
retval = -EPIPE;
}
return retval;
}
EXPORT_SYMBOL_GPL(ohci_hub_control);