static kmem_cache_t *uhci_up_cachep; /* urb_priv */
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 */
#define IDLE_TIMEOUT msecs_to_jiffies(50)
#include "uhci-debug.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 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 int ports_active(struct uhci_hcd *uhci)
+static int start_hc(struct uhci_hcd *uhci)
{
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)
}
}
-/*
- * 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;
- 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);
}
/*