This ought to fix the NS9750 init issue, and make the AMD756 case
at least somewhat better. It makes the init go "by the book" in more
ways, and formalizes one quirk.
- Dave
Various OHCI init/reset cleanups for different silicion environments:
- Reset a bit more "by the book".
* Define a new quirk flag for the SiS and OPTi problem seen earlier.
Since 2.4 we've always worked around that quirk, even though we've
not seen that on other chips; but it's "wrong" and doesn't work on
some chips (notably NetSilicon NS9750).
The quirk still seems to be needed for SiS, but either this test
machine is too fast for the OPTi problem to show up, or the frame
timing setup problem there came from a now-fixed bug.
* Look at the HC state before resetting it; depending on whether
it was previously owned by BIOS, SMM, an OS, or nobody, different
USB signaling (and timings) might be needed.
* Re-init the frame timings right after soft reset, rather than
later (potentially too much later).
* Restore a reset in the PCI startup code, so this logic more closely
resembles the non-PCI paths (future code sharing). It also makes it
easier to guarantee a 1-millisecond ceiling between reset and "go".
An earlier reset is being done to help workaround BIOS-related
problems on some boards, but we may need an even earlier one
(as a PCI quirk, before IRQs get reconfigured).
- Add an explicit #define to disable the BIOS/SMM handoff; it's
not just HPPA, many embedded chips don't expect BIOS either.
- There are reports of AMD 756 machines disliking the OHCI suspend
patch of a few months back. Erratum #10 partly explains that, so
now root hubs won't autosuspend on those Slot-A era chips.
- Other minor fixes
* We've got lots of non-PCI OHCI now too, so comments shouldn't
be assuming all-is-pci!
* Hey, it's unsafe to call hc_reset() in IRQ (after unrecoverable
error); so just force a soft reset, don't do the whole thing.
Tested on half a dozen different OHCI versions, but maybe some other
versions of OHCI will be sensitive to one of these changes.
Signed-off-by: David Brownell <[EMAIL PROTECTED]>
--- a/drivers/usb/host/ohci-hcd.c Thu Sep 2 16:39:09 2004
+++ b/drivers/usb/host/ohci-hcd.c Thu Sep 2 16:39:09 2004
@@ -2,7 +2,7 @@
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <[EMAIL PROTECTED]>
- * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
+ * (C) Copyright 2000-2004 David Brownell <[EMAIL PROTECTED]>
*
* [ Initialisation is based on Linus' ]
* [ uhci code and gregs ohci fragments ]
@@ -122,6 +122,16 @@
#define OHCI_INTR_INIT \
(OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+#ifdef __hppa__
+/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
+#define IR_DISABLE
+#endif
+
+#ifdef CONFIG_ARCH_OMAP
+/* OMAP doesn't support IR (no SMM; not needed) */
+#define IR_DISABLE
+#endif
+
/*-------------------------------------------------------------------------*/
static const char hcd_name [] = "ohci_hcd";
@@ -407,10 +417,8 @@
/* also: power/overcurrent flags in roothub.a */
}
- /* SMM owns the HC? not for long!
- * On PA-RISC, PDC can leave IR set incorrectly; ignore it there.
- */
-#ifndef __hppa__
+#ifndef IR_DISABLE
+ /* SMM owns the HC? not for long! */
if (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) {
ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n");
@@ -435,18 +443,40 @@
/* Disable HC interrupts */
writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
- ohci_dbg (ohci, "reset, control = 0x%x\n",
- ohci_readl (&ohci->regs->control));
-
- /* Reset USB (needed by some controllers); RemoteWakeupConnected
+ /* Reset USB nearly "by the book". RemoteWakeupConnected
* saved if boot firmware (BIOS/SMM/...) told us it's connected
* (for OHCI integrated on mainboard, it normally is)
*/
ohci->hc_control = ohci_readl (&ohci->regs->control);
- ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */
- if (ohci->hc_control)
+ ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n",
+ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+ ohci->hc_control);
+
+ if (ohci->hc_control & OHCI_CTRL_RWC
+ && !(ohci->flags & OHCI_QUIRK_AMD756))
ohci->hcd.can_wakeup = 1;
+
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ temp = 0;
+ break;
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESUME;
+ temp = 10 /* msec wait */;
+ break;
+ // case OHCI_USB_RESET:
+ default:
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_USB_RESET;
+ temp = 50 /* msec wait */;
+ break;
+ }
writel (ohci->hc_control, &ohci->regs->control);
+ // flush the writes
+ (void) ohci_readl (&ohci->regs->control);
+ msleep(temp);
if (power_switching) {
unsigned ports = roothub_a (ohci) & RH_A_NDP;
@@ -455,9 +485,8 @@
writel (RH_PS_LSDA,
&ohci->regs->roothub.portstatus [temp]);
}
- // flush those pci writes
+ // flush those writes
(void) ohci_readl (&ohci->regs->control);
- msleep (50);
/* HC Reset requires max 10 us delay */
writel (OHCI_HCR, &ohci->regs->cmdstatus);
@@ -469,6 +498,7 @@
}
udelay (1);
}
+ periodic_reinit (ohci);
/* now we're in the SUSPEND state ... must go OPERATIONAL
* within 2msec else HC enters RESUME
@@ -477,10 +507,11 @@
* (SiS, OPTi ...), so reset again instead. SiS doesn't need
* this if we write fmInterval after we're OPERATIONAL.
*/
- writel (ohci->hc_control, &ohci->regs->control);
- // flush those pci writes
- (void) ohci_readl (&ohci->regs->control);
-
+ if (ohci->flags & OHCI_QUIRK_INITRESET) {
+ writel (ohci->hc_control, &ohci->regs->control);
+ // flush those writes
+ (void) ohci_readl (&ohci->regs->control);
+ }
return 0;
}
@@ -506,8 +537,6 @@
/* a reset clears this */
writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);
- periodic_reinit (ohci);
-
/* some OHCI implementations are finicky about how they init.
* bogus values here mean not even enumeration could work.
*/
@@ -553,7 +582,7 @@
writel (tmp, &ohci->regs->roothub.a);
writel (RH_HS_LPSC, &ohci->regs->roothub.status);
writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b);
- // flush those pci writes
+ // flush those writes
(void) ohci_readl (&ohci->regs->control);
// POTPGT delay is bits 24-31, in 2 ms units.
@@ -620,7 +649,8 @@
// e.g. due to PCI Master/Target Abort
ohci_dump (ohci, 1);
- hc_reset (ohci);
+ ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */
+ writel (ohci->hc_control, &ohci->regs->control);
}
if (ints & OHCI_INTR_RD) {
@@ -655,7 +685,7 @@
if (HCD_IS_RUNNING(ohci->hcd.state)) {
writel (ints, ®s->intrstatus);
writel (OHCI_INTR_MIE, ®s->intrenable);
- // flush those pci writes
+ // flush those writes
(void) ohci_readl (&ohci->regs->control);
}
--- a/drivers/usb/host/ohci-pci.c Thu Sep 2 16:39:10 2004
+++ b/drivers/usb/host/ohci-pci.c Thu Sep 2 16:39:10 2004
@@ -61,6 +61,7 @@
&& pdev->device == 0x740c) {
ohci->flags = OHCI_QUIRK_AMD756;
ohci_info (ohci, "AMD756 erratum 4 workaround\n");
+ // also somewhat erratum 10 (suspend/resume issues)
}
/* FIXME for some of the early AMD 760 southbridges, OHCI
@@ -75,6 +76,8 @@
&& pdev->device == 0xc861) {
ohci_info (ohci,
"WARNING: OPTi workarounds unavailable\n");
+ /* OPTi sometimes acts wierd during init */
+ ohci->flags = OHCI_QUIRK_INITRESET;
}
/* Check for NSC87560. We have to look at the bridge (fn1) to
@@ -92,6 +95,12 @@
ohci_info (ohci, "Using NSC SuperIO setup\n");
}
}
+
+ /* SiS sometimes acts wierd during init */
+ else if (pdev->vendor == PCI_VENDOR_ID_SI) {
+ ohci->flags = OHCI_QUIRK_INITRESET;
+ ohci_info(ohci, "SiS init quirk\n");
+ }
}
@@ -99,6 +108,15 @@
if ((ret = ohci_mem_init (ohci)) < 0) {
ohci_stop (hcd);
return ret;
+ }
+
+ /* NOTE: this is a second reset. the first one helps
+ * keep bios/smm irqs from making trouble, but it was
+ * probably more than 1msec ago...
+ */
+ if (hc_reset (ohci) < 0) {
+ ohci_stop (hcd);
+ return -ENODEV;
}
if (hc_start (ohci) < 0) {
--- a/drivers/usb/host/ohci.h Thu Sep 2 16:39:10 2004
+++ b/drivers/usb/host/ohci.h Thu Sep 2 16:39:10 2004
@@ -387,6 +387,7 @@
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */
+#define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */
// there are also chip quirks/bugs in init logic
/*