Commit 13319e15 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: OHCI init cleanups

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.

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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent f6799f05
......@@ -2,7 +2,7 @@
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net>
*
* [ 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 @@ static int hc_reset (struct ohci_hcd *ohci)
/* 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 @@ static int hc_reset (struct ohci_hcd *ohci)
/* 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 @@ static int hc_reset (struct ohci_hcd *ohci)
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 @@ static int hc_reset (struct ohci_hcd *ohci)
}
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 @@ static int hc_reset (struct ohci_hcd *ohci)
* (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 @@ static int hc_start (struct ohci_hcd *ohci)
/* 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 @@ static int hc_start (struct ohci_hcd *ohci)
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 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
// 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 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
if (HCD_IS_RUNNING(ohci->hcd.state)) {
writel (ints, &regs->intrstatus);
writel (OHCI_INTR_MIE, &regs->intrenable);
// flush those pci writes
// flush those writes
(void) ohci_readl (&ohci->regs->control);
}
......
......@@ -61,6 +61,7 @@ ohci_pci_start (struct usb_hcd *hcd)
&& 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 @@ ohci_pci_start (struct usb_hcd *hcd)
&& 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_pci_start (struct usb_hcd *hcd)
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");
}
}
......@@ -101,6 +110,15 @@ ohci_pci_start (struct usb_hcd *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) {
ohci_err (ohci, "can't start\n");
ohci_stop (hcd);
......
......@@ -387,6 +387,7 @@ struct ohci_hcd {
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
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment