aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/uhci-hcd.c70
-rw-r--r--drivers/usb/host/uhci-hcd.h12
-rw-r--r--drivers/usb/host/uhci-hub.c67
-rw-r--r--drivers/usb/host/uhci-q.c3
4 files changed, 92 insertions, 60 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 82e608a4bbd..25a718eb1d0 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -84,6 +84,8 @@ static char *errbuf;
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 */
@@ -133,12 +135,12 @@ static void reset_hc(struct uhci_hcd *uhci)
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;
}
/*
@@ -148,6 +150,7 @@ static void hc_died(struct uhci_hcd *uhci)
{
reset_hc(uhci);
uhci->hc_inaccessible = 1;
+ del_timer(&uhci->stall_timer);
}
/*
@@ -302,14 +305,14 @@ __acquires(uhci->lock)
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();
@@ -320,6 +323,9 @@ static void start_rh(struct uhci_hcd *uhci)
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)
@@ -353,36 +359,9 @@ __acquires(uhci->lock)
}
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)
@@ -394,14 +373,8 @@ 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);
}
@@ -443,7 +416,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
}
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);
@@ -542,6 +515,7 @@ static int uhci_start(struct usb_hcd *hcd)
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# */
@@ -714,8 +688,6 @@ static int uhci_start(struct usb_hcd *hcd)
configure_hc(uhci);
start_rh(uhci);
- restart_timer(uhci);
-
udev->speed = USB_SPEED_FULL;
if (usb_hcd_register_root_hub(udev, hcd) != 0) {
@@ -730,8 +702,8 @@ static int uhci_start(struct usb_hcd *hcd)
* 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++)
@@ -771,13 +743,12 @@ static void uhci_stop(struct usb_hcd *hcd)
{
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);
}
@@ -844,6 +815,8 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
done:
spin_unlock_irq(&uhci->lock);
+ if (rc == 0)
+ del_timer_sync(&hcd->rh_timer);
return rc;
}
@@ -875,6 +848,9 @@ static int uhci_resume(struct usb_hcd *hcd)
suspend_rh(uhci, UHCI_RH_SUSPENDED);
spin_unlock_irq(&uhci->lock);
+
+ if (hcd->poll_rh)
+ 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 827df5e0680..d7c67b73eb7 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -327,18 +327,19 @@ static inline int __interval_to_skel(int interval)
* 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 */
@@ -380,7 +381,6 @@ struct uhci_hcd {
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 */
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index 13652de5220..4eace2b19dd 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -49,22 +49,16 @@ static int any_ports_active(struct uhci_hcd *uhci)
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;
}
@@ -134,6 +128,11 @@ static void uhci_check_ports(struct uhci_hcd *uhci)
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);
@@ -142,6 +141,60 @@ static void uhci_check_ports(struct uhci_hcd *uhci)
}
}
+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)
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index f5c75885f7b..77f264851e9 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -32,6 +32,8 @@ 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);
uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC);
}
@@ -1497,6 +1499,7 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
rescan:
uhci->need_rescan = 0;
+ uhci_clear_next_interrupt(uhci);
uhci_get_current_frame_number(uhci);
if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age)