|
@@ -90,7 +90,6 @@ static char *errbuf;
|
|
static kmem_cache_t *uhci_up_cachep; /* urb_priv */
|
|
static kmem_cache_t *uhci_up_cachep; /* urb_priv */
|
|
|
|
|
|
static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
|
|
static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
|
|
-static void hc_state_transitions(struct uhci_hcd *uhci);
|
|
|
|
|
|
|
|
/* If a transfer is still active after this much time, turn off FSBR */
|
|
/* If a transfer is still active after this much time, turn off FSBR */
|
|
#define IDLE_TIMEOUT msecs_to_jiffies(50)
|
|
#define IDLE_TIMEOUT msecs_to_jiffies(50)
|
|
@@ -105,96 +104,43 @@ static void hc_state_transitions(struct uhci_hcd *uhci);
|
|
#include "uhci-debug.c"
|
|
#include "uhci-debug.c"
|
|
#include "uhci-q.c"
|
|
#include "uhci-q.c"
|
|
|
|
|
|
-static int init_stall_timer(struct usb_hcd *hcd);
|
|
|
|
-
|
|
|
|
-static void stall_callback(unsigned long ptr)
|
|
|
|
-{
|
|
|
|
- struct usb_hcd *hcd = (struct usb_hcd *)ptr;
|
|
|
|
- struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
|
|
|
- struct urb_priv *up;
|
|
|
|
- unsigned long flags;
|
|
|
|
-
|
|
|
|
- spin_lock_irqsave(&uhci->lock, flags);
|
|
|
|
- uhci_scan_schedule(uhci, NULL);
|
|
|
|
-
|
|
|
|
- list_for_each_entry(up, &uhci->urb_list, urb_list) {
|
|
|
|
- struct urb *u = up->urb;
|
|
|
|
-
|
|
|
|
- spin_lock(&u->lock);
|
|
|
|
-
|
|
|
|
- /* Check if the FSBR timed out */
|
|
|
|
- if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
|
|
|
|
- uhci_fsbr_timeout(uhci, u);
|
|
|
|
-
|
|
|
|
- spin_unlock(&u->lock);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Really disable FSBR */
|
|
|
|
- if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
|
|
|
|
- uhci->fsbrtimeout = 0;
|
|
|
|
- uhci->skel_term_qh->link = UHCI_PTR_TERM;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Poll for and perform state transitions */
|
|
|
|
- hc_state_transitions(uhci);
|
|
|
|
- if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))
|
|
|
|
- uhci_check_ports(uhci);
|
|
|
|
-
|
|
|
|
- init_stall_timer(hcd);
|
|
|
|
- spin_unlock_irqrestore(&uhci->lock, flags);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int init_stall_timer(struct usb_hcd *hcd)
|
|
|
|
|
|
+static int ports_active(struct uhci_hcd *uhci)
|
|
{
|
|
{
|
|
- struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
|
|
|
|
|
+ unsigned long io_addr = uhci->io_addr;
|
|
|
|
+ int connection = 0;
|
|
|
|
+ int i;
|
|
|
|
|
|
- init_timer(&uhci->stall_timer);
|
|
|
|
- uhci->stall_timer.function = stall_callback;
|
|
|
|
- uhci->stall_timer.data = (unsigned long)hcd;
|
|
|
|
- uhci->stall_timer.expires = jiffies + msecs_to_jiffies(100);
|
|
|
|
- add_timer(&uhci->stall_timer);
|
|
|
|
|
|
+ for (i = 0; i < uhci->rh_numports; i++)
|
|
|
|
+ connection |= (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_CCS);
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return connection;
|
|
}
|
|
}
|
|
|
|
|
|
-static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
|
|
|
|
|
|
+static int suspend_allowed(struct uhci_hcd *uhci)
|
|
{
|
|
{
|
|
- struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
|
|
|
unsigned long io_addr = uhci->io_addr;
|
|
unsigned long io_addr = uhci->io_addr;
|
|
- unsigned short status;
|
|
|
|
|
|
+ int i;
|
|
|
|
|
|
- /*
|
|
|
|
- * Read the interrupt status, and write it back to clear the
|
|
|
|
- * interrupt cause. Contrary to the UHCI specification, the
|
|
|
|
- * "HC Halted" status bit is persistent: it is RO, not R/WC.
|
|
|
|
|
|
+ if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ /* Some of Intel's USB controllers have a bug that causes false
|
|
|
|
+ * resume indications if any port has an over current condition.
|
|
|
|
+ * To prevent problems, we will not allow a global suspend if
|
|
|
|
+ * any ports are OC.
|
|
|
|
+ *
|
|
|
|
+ * Some motherboards using Intel's chipsets (but not using all
|
|
|
|
+ * the USB ports) appear to hardwire the over current inputs active
|
|
|
|
+ * to disable the USB ports.
|
|
*/
|
|
*/
|
|
- status = inw(io_addr + USBSTS);
|
|
|
|
- if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */
|
|
|
|
- return IRQ_NONE;
|
|
|
|
- outw(status, io_addr + USBSTS); /* Clear it */
|
|
|
|
|
|
|
|
- if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
|
|
|
|
- if (status & USBSTS_HSE)
|
|
|
|
- dev_err(uhci_dev(uhci), "host system error, "
|
|
|
|
- "PCI problems?\n");
|
|
|
|
- if (status & USBSTS_HCPE)
|
|
|
|
- dev_err(uhci_dev(uhci), "host controller process "
|
|
|
|
- "error, something bad happened!\n");
|
|
|
|
- if ((status & USBSTS_HCH) && uhci->state > 0) {
|
|
|
|
- dev_err(uhci_dev(uhci), "host controller halted, "
|
|
|
|
- "very bad!\n");
|
|
|
|
- /* FIXME: Reset the controller, fix the offending TD */
|
|
|
|
- }
|
|
|
|
|
|
+ /* check for over current condition on any port */
|
|
|
|
+ for (i = 0; i < uhci->rh_numports; i++) {
|
|
|
|
+ if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
- if (status & USBSTS_RD)
|
|
|
|
- uhci->resume_detect = 1;
|
|
|
|
-
|
|
|
|
- spin_lock(&uhci->lock);
|
|
|
|
- uhci_scan_schedule(uhci, regs);
|
|
|
|
- spin_unlock(&uhci->lock);
|
|
|
|
-
|
|
|
|
- return IRQ_HANDLED;
|
|
|
|
|
|
+ return 1;
|
|
}
|
|
}
|
|
|
|
|
|
static void reset_hc(struct uhci_hcd *uhci)
|
|
static void reset_hc(struct uhci_hcd *uhci)
|
|
@@ -276,43 +222,46 @@ static void wakeup_hc(struct uhci_hcd *uhci)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static int ports_active(struct uhci_hcd *uhci)
|
|
|
|
|
|
+static int start_hc(struct uhci_hcd *uhci)
|
|
{
|
|
{
|
|
unsigned long io_addr = uhci->io_addr;
|
|
unsigned long io_addr = uhci->io_addr;
|
|
- int connection = 0;
|
|
|
|
- int i;
|
|
|
|
-
|
|
|
|
- for (i = 0; i < uhci->rh_numports; i++)
|
|
|
|
- connection |= (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_CCS);
|
|
|
|
|
|
+ int timeout = 10;
|
|
|
|
|
|
- return connection;
|
|
|
|
-}
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Reset the HC - this will force us to get a
|
|
|
|
+ * new notification of any already connected
|
|
|
|
+ * ports due to the virtual disconnect that it
|
|
|
|
+ * implies.
|
|
|
|
+ */
|
|
|
|
+ outw(USBCMD_HCRESET, io_addr + USBCMD);
|
|
|
|
+ while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
|
|
|
|
+ if (--timeout < 0) {
|
|
|
|
+ dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n");
|
|
|
|
+ return -ETIMEDOUT;
|
|
|
|
+ }
|
|
|
|
+ msleep(1);
|
|
|
|
+ }
|
|
|
|
|
|
-static int suspend_allowed(struct uhci_hcd *uhci)
|
|
|
|
-{
|
|
|
|
- unsigned long io_addr = uhci->io_addr;
|
|
|
|
- int i;
|
|
|
|
|
|
+ /* Mark controller as running before we enable interrupts */
|
|
|
|
+ uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
|
|
|
|
|
|
- if (to_pci_dev(uhci_dev(uhci))->vendor != PCI_VENDOR_ID_INTEL)
|
|
|
|
- return 1;
|
|
|
|
|
|
+ /* Turn on PIRQ and all interrupts */
|
|
|
|
+ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
|
|
|
|
+ USBLEGSUP_DEFAULT);
|
|
|
|
+ outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
|
|
|
|
+ io_addr + USBINTR);
|
|
|
|
|
|
- /* Some of Intel's USB controllers have a bug that causes false
|
|
|
|
- * resume indications if any port has an over current condition.
|
|
|
|
- * To prevent problems, we will not allow a global suspend if
|
|
|
|
- * any ports are OC.
|
|
|
|
- *
|
|
|
|
- * Some motherboards using Intel's chipsets (but not using all
|
|
|
|
- * the USB ports) appear to hardwire the over current inputs active
|
|
|
|
- * to disable the USB ports.
|
|
|
|
- */
|
|
|
|
|
|
+ /* Start at frame 0 */
|
|
|
|
+ outw(0, io_addr + USBFRNUM);
|
|
|
|
+ outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
|
|
|
|
|
|
- /* check for over current condition on any port */
|
|
|
|
- for (i = 0; i < uhci->rh_numports; i++) {
|
|
|
|
- if (inw(io_addr + USBPORTSC1 + i * 2) & USBPORTSC_OC)
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
|
|
+ /* Run and mark it configured with a 64-byte max packet */
|
|
|
|
+ uhci->state = UHCI_RUNNING_GRACE;
|
|
|
|
+ uhci->state_end = jiffies + HZ;
|
|
|
|
+ outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
|
|
|
|
+ uhci->is_stopped = 0;
|
|
|
|
|
|
- return 1;
|
|
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void hc_state_transitions(struct uhci_hcd *uhci)
|
|
static void hc_state_transitions(struct uhci_hcd *uhci)
|
|
@@ -353,56 +302,106 @@ static void hc_state_transitions(struct uhci_hcd *uhci)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-/*
|
|
|
|
- * Store the current frame number in uhci->frame_number if the controller
|
|
|
|
- * is runnning
|
|
|
|
- */
|
|
|
|
-static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
|
|
|
|
|
|
+static int init_stall_timer(struct usb_hcd *hcd);
|
|
|
|
+
|
|
|
|
+static void stall_callback(unsigned long ptr)
|
|
{
|
|
{
|
|
- if (!uhci->is_stopped)
|
|
|
|
- uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
|
|
|
|
|
|
+ struct usb_hcd *hcd = (struct usb_hcd *)ptr;
|
|
|
|
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
|
|
|
+ struct urb_priv *up;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&uhci->lock, flags);
|
|
|
|
+ uhci_scan_schedule(uhci, NULL);
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(up, &uhci->urb_list, urb_list) {
|
|
|
|
+ struct urb *u = up->urb;
|
|
|
|
+
|
|
|
|
+ spin_lock(&u->lock);
|
|
|
|
+
|
|
|
|
+ /* Check if the FSBR timed out */
|
|
|
|
+ if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT))
|
|
|
|
+ uhci_fsbr_timeout(uhci, u);
|
|
|
|
+
|
|
|
|
+ spin_unlock(&u->lock);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Really disable FSBR */
|
|
|
|
+ if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, uhci->fsbrtimeout)) {
|
|
|
|
+ uhci->fsbrtimeout = 0;
|
|
|
|
+ uhci->skel_term_qh->link = UHCI_PTR_TERM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Poll for and perform state transitions */
|
|
|
|
+ hc_state_transitions(uhci);
|
|
|
|
+ if (unlikely(uhci->suspended_ports && uhci->state != UHCI_SUSPENDED))
|
|
|
|
+ uhci_check_ports(uhci);
|
|
|
|
+
|
|
|
|
+ init_stall_timer(hcd);
|
|
|
|
+ spin_unlock_irqrestore(&uhci->lock, flags);
|
|
}
|
|
}
|
|
|
|
|
|
-static int start_hc(struct uhci_hcd *uhci)
|
|
|
|
|
|
+static int init_stall_timer(struct usb_hcd *hcd)
|
|
{
|
|
{
|
|
|
|
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
|
|
|
+
|
|
|
|
+ init_timer(&uhci->stall_timer);
|
|
|
|
+ uhci->stall_timer.function = stall_callback;
|
|
|
|
+ uhci->stall_timer.data = (unsigned long)hcd;
|
|
|
|
+ uhci->stall_timer.expires = jiffies + msecs_to_jiffies(100);
|
|
|
|
+ add_timer(&uhci->stall_timer);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
|
|
|
|
+{
|
|
|
|
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
|
|
unsigned long io_addr = uhci->io_addr;
|
|
unsigned long io_addr = uhci->io_addr;
|
|
- int timeout = 10;
|
|
|
|
|
|
+ unsigned short status;
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Reset the HC - this will force us to get a
|
|
|
|
- * new notification of any already connected
|
|
|
|
- * ports due to the virtual disconnect that it
|
|
|
|
- * implies.
|
|
|
|
|
|
+ * Read the interrupt status, and write it back to clear the
|
|
|
|
+ * interrupt cause. Contrary to the UHCI specification, the
|
|
|
|
+ * "HC Halted" status bit is persistent: it is RO, not R/WC.
|
|
*/
|
|
*/
|
|
- outw(USBCMD_HCRESET, io_addr + USBCMD);
|
|
|
|
- while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
|
|
|
|
- if (--timeout < 0) {
|
|
|
|
- dev_err(uhci_dev(uhci), "USBCMD_HCRESET timed out!\n");
|
|
|
|
- return -ETIMEDOUT;
|
|
|
|
|
|
+ status = inw(io_addr + USBSTS);
|
|
|
|
+ if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */
|
|
|
|
+ return IRQ_NONE;
|
|
|
|
+ outw(status, io_addr + USBSTS); /* Clear it */
|
|
|
|
+
|
|
|
|
+ if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
|
|
|
|
+ if (status & USBSTS_HSE)
|
|
|
|
+ dev_err(uhci_dev(uhci), "host system error, "
|
|
|
|
+ "PCI problems?\n");
|
|
|
|
+ if (status & USBSTS_HCPE)
|
|
|
|
+ dev_err(uhci_dev(uhci), "host controller process "
|
|
|
|
+ "error, something bad happened!\n");
|
|
|
|
+ if ((status & USBSTS_HCH) && uhci->state > 0) {
|
|
|
|
+ dev_err(uhci_dev(uhci), "host controller halted, "
|
|
|
|
+ "very bad!\n");
|
|
|
|
+ /* FIXME: Reset the controller, fix the offending TD */
|
|
}
|
|
}
|
|
- msleep(1);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- /* Mark controller as running before we enable interrupts */
|
|
|
|
- uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
|
|
|
|
-
|
|
|
|
- /* Turn on PIRQ and all interrupts */
|
|
|
|
- pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
|
|
|
|
- USBLEGSUP_DEFAULT);
|
|
|
|
- outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
|
|
|
|
- io_addr + USBINTR);
|
|
|
|
|
|
+ if (status & USBSTS_RD)
|
|
|
|
+ uhci->resume_detect = 1;
|
|
|
|
|
|
- /* Start at frame 0 */
|
|
|
|
- outw(0, io_addr + USBFRNUM);
|
|
|
|
- outl(uhci->fl->dma_handle, io_addr + USBFLBASEADD);
|
|
|
|
|
|
+ spin_lock(&uhci->lock);
|
|
|
|
+ uhci_scan_schedule(uhci, regs);
|
|
|
|
+ spin_unlock(&uhci->lock);
|
|
|
|
|
|
- /* Run and mark it configured with a 64-byte max packet */
|
|
|
|
- uhci->state = UHCI_RUNNING_GRACE;
|
|
|
|
- uhci->state_end = jiffies + HZ;
|
|
|
|
- outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
|
|
|
|
- uhci->is_stopped = 0;
|
|
|
|
|
|
+ return IRQ_HANDLED;
|
|
|
|
+}
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+/*
|
|
|
|
+ * Store the current frame number in uhci->frame_number if the controller
|
|
|
|
+ * is runnning
|
|
|
|
+ */
|
|
|
|
+static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
|
|
|
|
+{
|
|
|
|
+ if (!uhci->is_stopped)
|
|
|
|
+ uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|