patch-2.4.8 linux/drivers/usb/usb-ohci.c
Next file: linux/drivers/usb/usb-ohci.h
Previous file: linux/drivers/usb/storage/usb.h
Back to the patch index
Back to the overall index
- Lines: 416
- Date:
Tue Jul 24 14:20:56 2001
- Orig file:
v2.4.7/linux/drivers/usb/usb-ohci.c
- Orig date:
Wed Jul 25 17:10:24 2001
diff -u --recursive --new-file v2.4.7/linux/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
@@ -2,7 +2,7 @@
* URB OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
- * (C) Copyright 2000 David Brownell <david-b@pacbell.net>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
@@ -12,7 +12,7 @@
*
* History:
*
- * 2001/04/08 Identify version on module load gb
+ * 2001/07/17 power management and pmac cleanup (Benjamin Herrenschmidt)
* 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
pci_map_single (db)
* 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
@@ -75,11 +75,8 @@
#ifdef CONFIG_PMAC_PBOOK
-/* All this PMAC_PBOOK stuff should disappear when those
- * platforms fully support the 2.4 kernel PCI APIs.
- */
-#include <linux/adb.h>
-#include <linux/pmu.h>
+#include <asm/feature.h>
+#include <asm/pci-bridge.h>
#ifndef CONFIG_PM
#define CONFIG_PM
#endif
@@ -89,7 +86,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v5.2"
+#define DRIVER_VERSION "v5.3"
#define DRIVER_AUTHOR "Roman Weissgaerber <weissg@vienna.at>, David Brownell"
#define DRIVER_DESC "USB OHCI Host Controller Driver"
@@ -136,28 +133,39 @@
{
int i;
int last = urb_priv->length - 1;
+ int len;
+ int dir;
struct td *td;
- for (i = 0; i <= last; i++) {
- td = urb_priv->td [i];
- if (td) {
- int len;
- int dir;
-
- if ((td->hwINFO & cpu_to_le32 (TD_DP)) == TD_DP_SETUP) {
- len = 8;
- dir = PCI_DMA_TODEVICE;
- } else if (i == last) {
- len = td->urb->transfer_buffer_length,
- dir = usb_pipeout (td->urb->pipe)
+ if (last >= 0) {
+
+ /* ISOC, BULK, INTR data buffer starts at td 0
+ * CTRL setup starts at td 0 */
+ td = urb_priv->td [0];
+
+ len = td->urb->transfer_buffer_length,
+ dir = usb_pipeout (td->urb->pipe)
? PCI_DMA_TODEVICE
: PCI_DMA_FROMDEVICE;
- } else
- len = dir = 0;
- if (len && td->data_dma)
- pci_unmap_single (hc->ohci_dev,
- td->data_dma, len, dir);
- td_free (hc, urb_priv->td [i]);
+
+ /* unmap CTRL URB setup */
+ if (usb_pipecontrol (td->urb->pipe)) {
+ pci_unmap_single (hc->ohci_dev,
+ td->data_dma, 8, PCI_DMA_TODEVICE);
+
+ /* CTRL data buffer starts at td 1 if len > 0 */
+ if (len && last > 0)
+ td = urb_priv->td [1];
+ }
+
+ /* unmap data buffer */
+ if (len && td->data_dma)
+ pci_unmap_single (hc->ohci_dev, td->data_dma, len, dir);
+
+ for (i = 0; i <= last; i++) {
+ td = urb_priv->td [i];
+ if (td)
+ td_free (hc, td);
}
}
@@ -981,7 +989,7 @@
}
ed->ed_prev = ohci->ed_controltail;
if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
- !ohci->ed_rm_list[1]) {
+ !ohci->ed_rm_list[1] && !ohci->sleeping) {
ohci->hc_control |= OHCI_CTRL_CLE;
writel (ohci->hc_control, &ohci->regs->control);
}
@@ -997,7 +1005,7 @@
}
ed->ed_prev = ohci->ed_bulktail;
if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
- !ohci->ed_rm_list[1]) {
+ !ohci->ed_rm_list[1] && !ohci->sleeping) {
ohci->hc_control |= OHCI_CTRL_BLE;
writel (ohci->hc_control, &ohci->regs->control);
}
@@ -1260,7 +1268,7 @@
ed->ed_rm_list = ohci->ed_rm_list[frame];
ohci->ed_rm_list[frame] = ed;
- if (!ohci->disabled) {
+ if (!ohci->disabled && !ohci->sleeping) {
/* enable SOF interrupt */
writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
writel (OHCI_INTR_SF, &ohci->regs->intrenable);
@@ -1366,7 +1374,8 @@
TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt);
cnt++;
- writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+ if (!ohci->sleeping)
+ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
break;
case PIPE_INTERRUPT:
@@ -1391,7 +1400,8 @@
info = usb_pipeout (urb->pipe)?
TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
td_fill (ohci, info, data, 0, urb, cnt++);
- writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+ if (!ohci->sleeping)
+ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
break;
case PIPE_ISOCHRONOUS:
@@ -1611,7 +1621,7 @@
writel (0, &ohci->regs->ed_controlcurrent);
if (bulk) /* reset bulk list */
writel (0, &ohci->regs->ed_bulkcurrent);
- if (!ohci->ed_rm_list[!frame]) {
+ if (!ohci->ed_rm_list[!frame] && !ohci->sleeping) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
@@ -2321,6 +2331,7 @@
memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
ohci->disabled = 1;
+ ohci->sleeping = 0;
ohci->irq = -1;
ohci->regs = mem_base;
@@ -2480,6 +2491,7 @@
pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
ohci->disabled = 1;
+ ohci->sleeping = 0;
if (ohci->bus->root_hub)
usb_disconnect (&ohci->bus->root_hub);
@@ -2588,7 +2600,9 @@
static int
ohci_pci_suspend (struct pci_dev *dev, u32 state)
{
- ohci_t *ohci = (ohci_t *) dev->driver_data;
+ ohci_t *ohci = (ohci_t *) dev->driver_data;
+ unsigned long flags;
+ u16 cmd;
if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
dbg ("can't suspend usb-%s (state is %s)", dev->slot_name,
@@ -2598,14 +2612,65 @@
/* act as if usb suspend can always be used */
info ("USB suspend: usb-%s", dev->slot_name);
+ ohci->sleeping = 1;
+
+ /* First stop processing */
+ spin_lock_irqsave (&usb_ed_lock, flags);
+ ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
+ writel (ohci->hc_control, &ohci->regs->control);
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ (void) readl (&ohci->regs->intrstatus);
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
+
+ /* Wait a frame or two */
+ mdelay(1);
+ if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
+ mdelay (1);
+
#ifdef CONFIG_PMAC_PBOOK
- disable_irq (ohci->irq);
+ if (_machine == _MACH_Pmac)
+ disable_irq (ohci->irq);
/* else, 2.4 assumes shared irqs -- don't disable */
#endif
+ /* Enable remote wakeup */
+ writel (readl(&ohci->regs->intrenable) | OHCI_INTR_RD, &ohci->regs->intrenable);
+
+ /* Suspend chip and let things settle down a bit */
ohci->hc_control = OHCI_USB_SUSPEND;
writel (ohci->hc_control, &ohci->regs->control);
- wait_ms (10);
+ (void) readl (&ohci->regs->control);
+ mdelay (500); /* No schedule here ! */
+ switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
+ case OHCI_USB_RESET:
+ dbg("Bus in reset phase ???");
+ break;
+ case OHCI_USB_RESUME:
+ dbg("Bus in resume phase ???");
+ break;
+ case OHCI_USB_OPER:
+ dbg("Bus in operational phase ???");
+ break;
+ case OHCI_USB_SUSPEND:
+ dbg("Bus suspended");
+ break;
+ }
+ /* In some rare situations, Apple's OHCI have happily trashed
+ * memory during sleep. We disable it's bus master bit during
+ * suspend
+ */
+ pci_read_config_word (dev, PCI_COMMAND, &cmd);
+ cmd &= ~PCI_COMMAND_MASTER;
+ pci_write_config_word (dev, PCI_COMMAND, cmd);
+#ifdef CONFIG_PMAC_PBOOK
+ {
+ struct device_node *of_node;
+ /* Disable USB PAD & cell clock */
+ of_node = pci_device_to_OF_node (ohci->ohci_dev);
+ if (of_node && _machine == _MACH_Pmac)
+ feature_set_usb_power (of_node, 0);
+ }
+#endif
return 0;
}
@@ -2616,15 +2681,27 @@
{
ohci_t *ohci = (ohci_t *) dev->driver_data;
int temp;
+ unsigned long flags;
/* guard against multiple resumes */
atomic_inc (&ohci->resume_count);
if (atomic_read (&ohci->resume_count) != 1) {
err ("concurrent PCI resumes for usb-%s", dev->slot_name);
atomic_dec (&ohci->resume_count);
- return -EBUSY;
+ return 0;
}
+#ifdef CONFIG_PMAC_PBOOK
+ {
+ struct device_node *of_node;
+
+ /* Re-enable USB PAD & cell clock */
+ of_node = pci_device_to_OF_node (ohci->ohci_dev);
+ if (of_node && _machine == _MACH_Pmac)
+ feature_set_usb_power (of_node, 1);
+ }
+#endif
+
/* did we suspend, or were we powered off? */
ohci->hc_control = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
@@ -2634,6 +2711,9 @@
ohci_dump_status (ohci);
#endif
+ /* Re-enable bus mastering */
+ pci_set_master(ohci->ohci_dev);
+
switch (temp) {
case OHCI_USB_RESET: // lost power
@@ -2648,8 +2728,10 @@
? "host" : "remote");
ohci->hc_control = OHCI_USB_RESUME;
writel (ohci->hc_control, &ohci->regs->control);
- wait_ms (20);
-
+ (void) readl (&ohci->regs->control);
+ mdelay (20); /* no schedule here ! */
+ /* Some controllers (lucent) need a longer delay here */
+ mdelay (15);
temp = readl (&ohci->regs->control);
temp = ohci->hc_control & OHCI_CTRL_HCFS;
if (temp != OHCI_USB_RESUME) {
@@ -2658,18 +2740,38 @@
return -EIO;
}
+ /* Some chips likes being resumed first */
+ writel (OHCI_USB_OPER, &ohci->regs->control);
+ (void) readl (&ohci->regs->control);
+ mdelay (3);
+
+ /* Then re-enable operations */
+ spin_lock_irqsave (&usb_ed_lock, flags);
ohci->disabled = 0;
+ ohci->sleeping = 0;
ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
- if (!ohci->ed_rm_list[0] & !ohci->ed_rm_list[1]) {
+ if (!ohci->ed_rm_list[0] && !ohci->ed_rm_list[1]) {
if (ohci->ed_controltail)
ohci->hc_control |= OHCI_CTRL_CLE;
if (ohci->ed_bulktail)
ohci->hc_control |= OHCI_CTRL_BLE;
}
writel (ohci->hc_control, &ohci->regs->control);
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+ /* Check for a pending done list */
+ writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);
+ (void) readl (&ohci->regs->intrdisable);
+ spin_unlock_irqrestore (&usb_ed_lock, flags);
#ifdef CONFIG_PMAC_PBOOK
- enable_irq (ohci->irq);
+ if (_machine == _MACH_Pmac)
+ enable_irq (ohci->irq);
#endif
+ if (ohci->hcca->done_head)
+ dl_done_list (ohci, dl_reverse_done_list (ohci));
+ writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
+ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
break;
default:
@@ -2724,71 +2826,24 @@
probe: ohci_pci_probe,
remove: ohci_pci_remove,
-#ifdef CONFIG_PMAC_PBOOK
- /* pbook PCI thinks different ... for now :-) */
-#else
#ifdef CONFIG_PM
suspend: ohci_pci_suspend,
resume: ohci_pci_resume,
#endif /* PM */
-#endif /* PBOOK */
};
-#ifdef CONFIG_PMAC_PBOOK
-
-/*-------------------------------------------------------------------------*/
-
-static int ohci_sleep_notify (struct pmu_sleep_notifier * self, int when)
-{
- struct list_head * ohci_l;
- ohci_t * ohci;
-
- for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) {
- ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list);
-
- switch (when) {
- case PBOOK_SLEEP_NOW:
- ohci_pci_suspend (ohci->ohci_dev, 3);
- break;
- case PBOOK_WAKE:
- ohci_pci_resume (ohci->ohci_dev);
- break;
- }
- }
- return PBOOK_SLEEP_OK;
-}
-
-static struct pmu_sleep_notifier ohci_sleep_notifier = {
- ohci_sleep_notify, SLEEP_LEVEL_MISC,
-};
-#endif /* CONFIG_PMAC_PBOOK */
-
-
/*-------------------------------------------------------------------------*/
static int __init ohci_hcd_init (void)
{
- int ret;
-
- if ((ret = pci_module_init (&ohci_pci_driver)) < 0) {
- return ret;
- }
-
-#ifdef CONFIG_PMAC_PBOOK
- pmu_register_sleep_notifier (&ohci_sleep_notifier);
-#endif
- info(DRIVER_VERSION ":" DRIVER_DESC);
- return ret;
+ return pci_module_init (&ohci_pci_driver);
}
/*-------------------------------------------------------------------------*/
static void __exit ohci_hcd_cleanup (void)
{
-#ifdef CONFIG_PMAC_PBOOK
- pmu_unregister_sleep_notifier (&ohci_sleep_notifier);
-#endif
pci_unregister_driver (&ohci_pci_driver);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)