aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/pci-quirks.c
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2005-10-03 16:36:29 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-10-28 19:47:44 -0400
commitbb200f6eac6372839921be1fe87c1f5c292a7bd6 (patch)
treed55cd6ac5d8b52b96ad766cbd8f31ca85e0e4f8b /drivers/usb/host/pci-quirks.c
parenta922c68732725866c88457026cf06a7620846506 (diff)
[PATCH] UHCI: unify BIOS handoff and driver reset code
This patch (as574) updates the PCI BIOS usb-handoff code for UHCI controllers, making it work like the reset routines in uhci-hcd. This allows uhci-hcd to drop its own routines in favor of the new ones (code-sharing). Once the patch is merged we can turn the usb-handoff option on permanently, as far as UHCI is concerned. OHCI and EHCI may still have some issues. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/pci-quirks.c')
-rw-r--r--drivers/usb/host/pci-quirks.c142
1 files changed, 96 insertions, 46 deletions
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 49f7381a6fd3..f7411ca48835 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -9,6 +9,12 @@
9 */ 9 */
10 10
11#include <linux/config.h> 11#include <linux/config.h>
12#ifdef CONFIG_USB_DEBUG
13#define DEBUG
14#else
15#undef DEBUG
16#endif
17
12#include <linux/types.h> 18#include <linux/types.h>
13#include <linux/kernel.h> 19#include <linux/kernel.h>
14#include <linux/pci.h> 20#include <linux/pci.h>
@@ -46,13 +52,14 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_2, qui
46 52
47#define UHCI_USBLEGSUP 0xc0 /* legacy support */ 53#define UHCI_USBLEGSUP 0xc0 /* legacy support */
48#define UHCI_USBCMD 0 /* command register */ 54#define UHCI_USBCMD 0 /* command register */
49#define UHCI_USBSTS 2 /* status register */
50#define UHCI_USBINTR 4 /* interrupt register */ 55#define UHCI_USBINTR 4 /* interrupt register */
51#define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ 56#define UHCI_USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
52#define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ 57#define UHCI_USBLEGSUP_RO 0x5040 /* R/O and reserved bits */
53#define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */ 58#define UHCI_USBCMD_RUN 0x0001 /* RUN/STOP bit */
54#define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */ 59#define UHCI_USBCMD_HCRESET 0x0002 /* Host Controller reset */
55#define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */ 60#define UHCI_USBCMD_EGSM 0x0008 /* Global Suspend Mode */
61#define UHCI_USBCMD_CONFIGURE 0x0040 /* Config Flag */
62#define UHCI_USBINTR_RESUME 0x0002 /* Resume interrupt enable */
56 63
57#define OHCI_CONTROL 0x04 64#define OHCI_CONTROL 0x04
58#define OHCI_CMDSTATUS 0x08 65#define OHCI_CMDSTATUS 0x08
@@ -84,57 +91,100 @@ static int __init usb_handoff_early(char *str)
84} 91}
85__setup("usb-handoff", usb_handoff_early); 92__setup("usb-handoff", usb_handoff_early);
86 93
87static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) 94/*
95 * Make sure the controller is completely inactive, unable to
96 * generate interrupts or do DMA.
97 */
98void uhci_reset_hc(struct pci_dev *pdev, unsigned long base)
88{ 99{
89 unsigned long base = 0; 100 /* Turn off PIRQ enable and SMI enable. (This also turns off the
90 int wait_time, delta; 101 * BIOS's USB Legacy Support.) Turn off all the R/WC bits too.
91 u16 val, sts; 102 */
92 int i; 103 pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_RWC);
93 104
94 for (i = 0; i < PCI_ROM_RESOURCE; i++) 105 /* Reset the HC - this will force us to get a
95 if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) { 106 * new notification of any already connected
96 base = pci_resource_start(pdev, i); 107 * ports due to the virtual disconnect that it
97 break; 108 * implies.
98 } 109 */
110 outw(UHCI_USBCMD_HCRESET, base + UHCI_USBCMD);
111 mb();
112 udelay(5);
113 if (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET)
114 dev_warn(&pdev->dev, "HCRESET not completed yet!\n");
115
116 /* Just to be safe, disable interrupt requests and
117 * make sure the controller is stopped.
118 */
119 outw(0, base + UHCI_USBINTR);
120 outw(0, base + UHCI_USBCMD);
121}
122EXPORT_SYMBOL_GPL(uhci_reset_hc);
99 123
100 if (!base) 124/*
101 return; 125 * Initialize a controller that was newly discovered or has just been
126 * resumed. In either case we can't be sure of its previous state.
127 *
128 * Returns: 1 if the controller was reset, 0 otherwise.
129 */
130int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
131{
132 u16 legsup;
133 unsigned int cmd, intr;
102 134
103 /* 135 /*
104 * stop controller 136 * When restarting a suspended controller, we expect all the
137 * settings to be the same as we left them:
138 *
139 * PIRQ and SMI disabled, no R/W bits set in USBLEGSUP;
140 * Controller is stopped and configured with EGSM set;
141 * No interrupts enabled except possibly Resume Detect.
142 *
143 * If any of these conditions are violated we do a complete reset.
105 */ 144 */
106 sts = inw(base + UHCI_USBSTS); 145 pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup);
107 val = inw(base + UHCI_USBCMD); 146 if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) {
108 val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE); 147 dev_dbg(&pdev->dev, "%s: legsup = 0x%04x\n",
109 outw(val, base + UHCI_USBCMD); 148 __FUNCTION__, legsup);
149 goto reset_needed;
150 }
110 151
111 /* 152 cmd = inw(base + UHCI_USBCMD);
112 * wait while it stops if it was running 153 if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) ||
113 */ 154 !(cmd & UHCI_USBCMD_EGSM)) {
114 if ((sts & UHCI_USBSTS_HALTED) == 0) 155 dev_dbg(&pdev->dev, "%s: cmd = 0x%04x\n",
115 { 156 __FUNCTION__, cmd);
116 wait_time = 1000; 157 goto reset_needed;
117 delta = 100; 158 }
118 159
119 do { 160 intr = inw(base + UHCI_USBINTR);
120 outw(0x1f, base + UHCI_USBSTS); 161 if (intr & (~UHCI_USBINTR_RESUME)) {
121 udelay(delta); 162 dev_dbg(&pdev->dev, "%s: intr = 0x%04x\n",
122 wait_time -= delta; 163 __FUNCTION__, intr);
123 val = inw(base + UHCI_USBSTS); 164 goto reset_needed;
124 if (val & UHCI_USBSTS_HALTED)
125 break;
126 } while (wait_time > 0);
127 } 165 }
166 return 0;
128 167
129 /* 168reset_needed:
130 * disable interrupts & legacy support 169 dev_dbg(&pdev->dev, "Performing full reset\n");
131 */ 170 uhci_reset_hc(pdev, base);
132 outw(0, base + UHCI_USBINTR); 171 return 1;
133 outw(0x1f, base + UHCI_USBSTS); 172}
134 pci_read_config_word(pdev, UHCI_USBLEGSUP, &val); 173EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
135 if (val & 0xbf) 174
136 pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT); 175static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
176{
177 unsigned long base = 0;
178 int i;
179
180 for (i = 0; i < PCI_ROM_RESOURCE; i++)
181 if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
182 base = pci_resource_start(pdev, i);
183 break;
184 }
137 185
186 if (base)
187 uhci_check_and_reset_hc(pdev, base);
138} 188}
139 189
140static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) 190static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)