summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/uhci-hcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/uhci-hcd.c')
-rw-r--r--drivers/usb/host/uhci-hcd.c183
1 files changed, 56 insertions, 127 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 0c024898cbe..ed550132db0 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -101,37 +101,16 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
#include "uhci-q.c"
#include "uhci-hub.c"
+extern void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
+extern int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
+
/*
- * Make sure the controller is completely inactive, unable to
- * generate interrupts or do DMA.
+ * Finish up a host controller reset and update the recorded state.
*/
-static void reset_hc(struct uhci_hcd *uhci)
+static void finish_reset(struct uhci_hcd *uhci)
{
int port;
- /* Turn off PIRQ enable and SMI enable. (This also turns off the
- * BIOS's USB Legacy Support.) Turn off all the R/WC bits too.
- */
- pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
- USBLEGSUP_RWC);
-
- /* 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, uhci->io_addr + USBCMD);
- mb();
- udelay(5);
- if (inw(uhci->io_addr + USBCMD) & USBCMD_HCRESET)
- dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n");
-
- /* Just to be safe, disable interrupt requests and
- * make sure the controller is stopped.
- */
- outw(0, uhci->io_addr + USBINTR);
- outw(0, uhci->io_addr + USBCMD);
-
/* HCRESET doesn't affect the Suspend, Reset, and Resume Detect
* bits in the port status and control registers.
* We have to clear them by hand.
@@ -153,7 +132,8 @@ static void reset_hc(struct uhci_hcd *uhci)
*/
static void hc_died(struct uhci_hcd *uhci)
{
- reset_hc(uhci);
+ uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr);
+ finish_reset(uhci);
uhci->hc_inaccessible = 1;
}
@@ -163,44 +143,8 @@ static void hc_died(struct uhci_hcd *uhci)
*/
static void check_and_reset_hc(struct uhci_hcd *uhci)
{
- u16 legsup;
- unsigned int cmd, intr;
-
- /*
- * When restarting a suspended controller, we expect all the
- * settings to be the same as we left them:
- *
- * PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
- * Controller is stopped and configured with EGSM set;
- * No interrupts enabled except possibly Resume Detect.
- *
- * If any of these conditions are violated we do a complete reset.
- */
- pci_read_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, &legsup);
- if (legsup & ~(USBLEGSUP_RO | USBLEGSUP_RWC)) {
- dev_dbg(uhci_dev(uhci), "%s: legsup = 0x%04x\n",
- __FUNCTION__, legsup);
- goto reset_needed;
- }
-
- cmd = inw(uhci->io_addr + USBCMD);
- if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) {
- dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n",
- __FUNCTION__, cmd);
- goto reset_needed;
- }
-
- intr = inw(uhci->io_addr + USBINTR);
- if (intr & (~USBINTR_RESUME)) {
- dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n",
- __FUNCTION__, intr);
- goto reset_needed;
- }
- return;
-
-reset_needed:
- dev_dbg(uhci_dev(uhci), "Performing full reset\n");
- reset_hc(uhci);
+ if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr))
+ finish_reset(uhci);
}
/*
@@ -212,13 +156,13 @@ static void configure_hc(struct uhci_hcd *uhci)
outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF);
/* Store the frame list base address */
- outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);
+ outl(uhci->frame_dma_handle, uhci->io_addr + USBFLBASEADD);
/* Set the current frame number */
outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
- /* Mark controller as running before we enable interrupts */
- uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
+ /* Mark controller as not halted before we enable interrupts */
+ uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED;
mb();
/* Enable PIRQ */
@@ -319,6 +263,7 @@ __acquires(uhci->lock)
static void start_rh(struct uhci_hcd *uhci)
{
+ uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
uhci->is_stopped = 0;
smp_wmb();
@@ -437,36 +382,21 @@ static void release_uhci(struct uhci_hcd *uhci)
int i;
for (i = 0; i < UHCI_NUM_SKELQH; i++)
- if (uhci->skelqh[i]) {
- uhci_free_qh(uhci, uhci->skelqh[i]);
- uhci->skelqh[i] = NULL;
- }
+ uhci_free_qh(uhci, uhci->skelqh[i]);
- if (uhci->term_td) {
- uhci_free_td(uhci, uhci->term_td);
- uhci->term_td = NULL;
- }
+ uhci_free_td(uhci, uhci->term_td);
- if (uhci->qh_pool) {
- dma_pool_destroy(uhci->qh_pool);
- uhci->qh_pool = NULL;
- }
+ dma_pool_destroy(uhci->qh_pool);
- if (uhci->td_pool) {
- dma_pool_destroy(uhci->td_pool);
- uhci->td_pool = NULL;
- }
+ dma_pool_destroy(uhci->td_pool);
- if (uhci->fl) {
- dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
- uhci->fl, uhci->fl->dma_handle);
- uhci->fl = NULL;
- }
+ kfree(uhci->frame_cpu);
- if (uhci->dentry) {
- debugfs_remove(uhci->dentry);
- uhci->dentry = NULL;
- }
+ dma_free_coherent(uhci_dev(uhci),
+ UHCI_NUMFRAMES * sizeof(*uhci->frame),
+ uhci->frame, uhci->frame_dma_handle);
+
+ debugfs_remove(uhci->dentry);
}
static int uhci_reset(struct usb_hcd *hcd)
@@ -545,7 +475,6 @@ static int uhci_start(struct usb_hcd *hcd)
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
int retval = -EBUSY;
int i;
- dma_addr_t dma_handle;
struct dentry *dentry;
hcd->uses_new_polling = 1;
@@ -579,17 +508,23 @@ static int uhci_start(struct usb_hcd *hcd)
init_waitqueue_head(&uhci->waitqh);
- uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
- &dma_handle, 0);
- if (!uhci->fl) {
+ uhci->frame = dma_alloc_coherent(uhci_dev(uhci),
+ UHCI_NUMFRAMES * sizeof(*uhci->frame),
+ &uhci->frame_dma_handle, 0);
+ if (!uhci->frame) {
dev_err(uhci_dev(uhci), "unable to allocate "
"consistent memory for frame list\n");
- goto err_alloc_fl;
+ goto err_alloc_frame;
}
+ memset(uhci->frame, 0, UHCI_NUMFRAMES * sizeof(*uhci->frame));
- memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
-
- uhci->fl->dma_handle = dma_handle;
+ uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu),
+ GFP_KERNEL);
+ if (!uhci->frame_cpu) {
+ dev_err(uhci_dev(uhci), "unable to allocate "
+ "memory for frame pointers\n");
+ goto err_alloc_frame_cpu;
+ }
uhci->td_pool = dma_pool_create("uhci_td", uhci_dev(uhci),
sizeof(struct uhci_td), 16, 0);
@@ -672,7 +607,7 @@ static int uhci_start(struct usb_hcd *hcd)
irq = 7;
/* Only place we don't use the frame list routines */
- uhci->fl->frame[i] = UHCI_PTR_QH |
+ uhci->frame[i] = UHCI_PTR_QH |
cpu_to_le32(uhci->skelqh[irq]->dma_handle);
}
@@ -690,31 +625,29 @@ static int uhci_start(struct usb_hcd *hcd)
* error exits:
*/
err_alloc_skelqh:
- for (i = 0; i < UHCI_NUM_SKELQH; i++)
- if (uhci->skelqh[i]) {
+ for (i = 0; i < UHCI_NUM_SKELQH; i++) {
+ if (uhci->skelqh[i])
uhci_free_qh(uhci, uhci->skelqh[i]);
- uhci->skelqh[i] = NULL;
- }
+ }
uhci_free_td(uhci, uhci->term_td);
- uhci->term_td = NULL;
err_alloc_term_td:
dma_pool_destroy(uhci->qh_pool);
- uhci->qh_pool = NULL;
err_create_qh_pool:
dma_pool_destroy(uhci->td_pool);
- uhci->td_pool = NULL;
err_create_td_pool:
- dma_free_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
- uhci->fl, uhci->fl->dma_handle);
- uhci->fl = NULL;
+ kfree(uhci->frame_cpu);
-err_alloc_fl:
+err_alloc_frame_cpu:
+ dma_free_coherent(uhci_dev(uhci),
+ UHCI_NUMFRAMES * sizeof(*uhci->frame),
+ uhci->frame, uhci->frame_dma_handle);
+
+err_alloc_frame:
debugfs_remove(uhci->dentry);
- uhci->dentry = NULL;
err_create_debug_entry:
return retval;
@@ -726,7 +659,7 @@ static void uhci_stop(struct usb_hcd *hcd)
spin_lock_irq(&uhci->lock);
if (!uhci->hc_inaccessible)
- reset_hc(uhci);
+ hc_died(uhci);
uhci_scan_schedule(uhci, NULL);
spin_unlock_irq(&uhci->lock);
@@ -774,14 +707,8 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
if (uhci->hc_inaccessible) /* Dead or already suspended */
goto done;
-#ifndef CONFIG_USB_SUSPEND
- /* Otherwise this would never happen */
- suspend_rh(uhci, UHCI_RH_SUSPENDED);
-#endif
-
if (uhci->rh_state > UHCI_RH_SUSPENDED) {
dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n");
- hcd->state = HC_STATE_RUNNING;
rc = -EBUSY;
goto done;
};
@@ -790,6 +717,7 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
* at the source, so we must turn off PIRQ.
*/
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
+ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
uhci->hc_inaccessible = 1;
hcd->poll_rh = 0;
@@ -806,6 +734,11 @@ static int uhci_resume(struct usb_hcd *hcd)
dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__);
+ /* We aren't in D3 state anymore, we do that even if dead as I
+ * really don't want to keep a stale HCD_FLAG_HW_ACCESSIBLE=0
+ */
+ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
+
if (uhci->rh_state == UHCI_RH_RESET) /* Dead */
return 0;
spin_lock_irq(&uhci->lock);
@@ -820,10 +753,6 @@ static int uhci_resume(struct usb_hcd *hcd)
check_and_reset_hc(uhci);
configure_hc(uhci);
-#ifndef CONFIG_USB_SUSPEND
- /* Otherwise this would never happen */
- wakeup_rh(uhci);
-#endif
if (uhci->rh_state == UHCI_RH_RESET)
suspend_rh(uhci, UHCI_RH_SUSPENDED);
@@ -881,8 +810,8 @@ static const struct hc_driver uhci_driver = {
#ifdef CONFIG_PM
.suspend = uhci_suspend,
.resume = uhci_resume,
- .hub_suspend = uhci_rh_suspend,
- .hub_resume = uhci_rh_resume,
+ .bus_suspend = uhci_rh_suspend,
+ .bus_resume = uhci_rh_resume,
#endif
.stop = uhci_stop,