static kmem_cache_t *uhci_up_cachep; /* urb_priv */
+static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state);
+static void wakeup_rh(struct uhci_hcd *uhci);
static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
/* If a transfer is still active after this much time, turn off FSBR */
outw(0, uhci->io_addr + USBINTR);
outw(0, uhci->io_addr + USBCMD);
- uhci->resume_detect = 0;
uhci->port_c_suspend = uhci->suspended_ports =
uhci->resuming_ports = 0;
uhci->rh_state = UHCI_RH_RESET;
uhci->is_stopped = UHCI_IS_STOPPED;
uhci_to_hcd(uhci)->state = HC_STATE_HALT;
+ uhci_to_hcd(uhci)->poll_rh = 0;
}
/*
{
reset_hc(uhci);
uhci->hc_inaccessible = 1;
+ del_timer(&uhci->stall_timer);
}
/*
uhci->rh_state = new_state;
uhci->is_stopped = UHCI_IS_STOPPED;
- uhci->resume_detect = 0;
+ del_timer(&uhci->stall_timer);
+ uhci_to_hcd(uhci)->poll_rh = !int_enable;
uhci_scan_schedule(uhci, NULL);
}
static void start_rh(struct uhci_hcd *uhci)
{
- uhci->rh_state = UHCI_RH_RUNNING;
uhci->is_stopped = 0;
smp_wmb();
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
uhci->io_addr + USBINTR);
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)
}
start_rh(uhci);
-}
-
-static void rh_state_transitions(struct uhci_hcd *uhci)
-{
- switch (uhci->rh_state) {
- case UHCI_RH_RUNNING:
- /* are any devices attached? */
- if (!any_ports_active(uhci)) {
- uhci->rh_state = UHCI_RH_RUNNING_NODEVS;
- uhci->auto_stop_time = jiffies + HZ;
- }
- break;
- case UHCI_RH_RUNNING_NODEVS:
- /* auto-stop if nothing connected for 1 second */
- if (any_ports_active(uhci))
- uhci->rh_state = UHCI_RH_RUNNING;
- else if (time_after_eq(jiffies, uhci->auto_stop_time))
- suspend_rh(uhci, UHCI_RH_AUTO_STOPPED);
- break;
-
- case UHCI_RH_AUTO_STOPPED:
- /* wakeup if requested by a device */
- if (uhci->resume_detect)
- wakeup_rh(uhci);
- break;
-
- default:
- break;
- }
+ /* Restart root hub polling */
+ mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
}
static void stall_callback(unsigned long _uhci)
uhci_scan_schedule(uhci, NULL);
check_fsbr(uhci);
- /* Poll for and perform state transitions */
- if (!uhci->hc_inaccessible) {
- rh_state_transitions(uhci);
- if (uhci->suspended_ports)
- uhci_check_ports(uhci);
- }
-
- restart_timer(uhci);
+ if (!uhci->is_stopped)
+ restart_timer(uhci);
spin_unlock_irqrestore(&uhci->lock, flags);
}
}
if (status & USBSTS_RD)
- uhci->resume_detect = 1;
+ usb_hcd_poll_rh_status(hcd);
spin_lock_irqsave(&uhci->lock, flags);
uhci_scan_schedule(uhci, regs);
struct dentry *dentry;
io_size = (unsigned) hcd->rsrc_len;
+ hcd->uses_new_polling = 1;
if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM))
hcd->can_wakeup = 1; /* Assume it supports PME# */
configure_hc(uhci);
start_rh(uhci);
- restart_timer(uhci);
-
udev->speed = USB_SPEED_FULL;
if (usb_hcd_register_root_hub(udev, hcd) != 0) {
* error exits:
*/
err_start_root_hub:
- del_timer_sync(&uhci->stall_timer);
reset_hc(uhci);
+ del_timer_sync(&uhci->stall_timer);
err_alloc_skelqh:
for (i = 0; i < UHCI_NUM_SKELQH; i++)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
- del_timer_sync(&uhci->stall_timer);
-
spin_lock_irq(&uhci->lock);
reset_hc(uhci);
uhci_scan_schedule(uhci, NULL);
spin_unlock_irq(&uhci->lock);
-
+
+ del_timer_sync(&uhci->stall_timer);
release_uhci(uhci);
}
done:
spin_unlock_irq(&uhci->lock);
+ if (rc == 0)
+ del_timer_sync(&hcd->rh_timer);
return rc;
}
suspend_rh(uhci, UHCI_RH_SUSPENDED);
spin_unlock_irq(&uhci->lock);
+
+ if (hcd->poll_rh)
+ usb_hcd_poll_rh_status(hcd);
return 0;
}
#endif
* driver learns to autosuspend.)
*/
enum uhci_rh_state {
- /* In the next 4 states the HC must be halted */
- UHCI_RH_RESET, /* These two must come first */
+ /* In the following states the HC must be halted.
+ * These two must come first */
+ UHCI_RH_RESET,
UHCI_RH_SUSPENDED,
UHCI_RH_AUTO_STOPPED,
UHCI_RH_RESUMING,
- /* In the next state the HC changes from running to halted, so it
- * can legally appear either way */
+ /* In this state the HC changes from running to halted,
+ * so it can legally appear either way. */
UHCI_RH_SUSPENDING,
- /* In the next two states it's an error if the HC is halted.
+ /* In the following states it's an error if the HC is halted.
* These two must come last */
UHCI_RH_RUNNING, /* The normal state */
UHCI_RH_RUNNING_NODEVS, /* Running with no devices attached */
unsigned int scan_in_progress:1; /* Schedule scan is running */
unsigned int need_rescan:1; /* Redo the schedule scan */
- unsigned int resume_detect:1; /* Need a Global Resume */
unsigned int hc_inaccessible:1; /* HC is suspended or dead */
/* Support for port suspend/resume/reset */
return 0;
}
-static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
+static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf)
{
- struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int port;
- if (uhci->hc_inaccessible)
- return 0;
-
*buf = 0;
for (port = 0; port < uhci->rh_numports; ++port) {
if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) ||
test_bit(port, &uhci->port_c_suspend))
*buf |= (1 << (port + 1));
}
- if (*buf && uhci->is_stopped)
- uhci->resume_detect = 1;
return !!*buf;
}
set_bit(port, &uhci->resuming_ports);
uhci->ports_timeout = jiffies +
msecs_to_jiffies(20);
+
+ /* Make sure we see the port again
+ * after the resuming period is over. */
+ mod_timer(&uhci_to_hcd(uhci)->rh_timer,
+ uhci->ports_timeout);
} else if (time_after_eq(jiffies,
uhci->ports_timeout)) {
uhci_finish_suspend(uhci, port, port_addr);
}
}
+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;
+
+ spin_lock_irqsave(&uhci->lock, flags);
+ if (uhci->hc_inaccessible) {
+ status = 0;
+ goto done;
+ }
+
+ uhci_check_ports(uhci);
+ status = get_hub_status_data(uhci, buf);
+
+ switch (uhci->rh_state) {
+ case UHCI_RH_SUSPENDING:
+ case UHCI_RH_SUSPENDED:
+ /* if port change, ask to be resumed */
+ if (status)
+ usb_hcd_resume_root_hub(hcd);
+ break;
+
+ case UHCI_RH_AUTO_STOPPED:
+ /* if port change, auto start */
+ if (status)
+ wakeup_rh(uhci);
+ break;
+
+ case UHCI_RH_RUNNING:
+ /* are any devices attached? */
+ if (!any_ports_active(uhci)) {
+ uhci->rh_state = UHCI_RH_RUNNING_NODEVS;
+ uhci->auto_stop_time = jiffies + HZ;
+ }
+ break;
+
+ case UHCI_RH_RUNNING_NODEVS:
+ /* auto-stop if nothing connected for 1 second */
+ if (any_ports_active(uhci))
+ uhci->rh_state = UHCI_RH_RUNNING;
+ else if (time_after_eq(jiffies, uhci->auto_stop_time))
+ suspend_rh(uhci, UHCI_RH_AUTO_STOPPED);
+ break;
+
+ default:
+ break;
+ }
+
+done:
+ spin_unlock_irqrestore(&uhci->lock, flags);
+ return status;
+}
+
/* size of returned buffer is part of USB spec */
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)