aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2005-09-23 20:14:37 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-10-28 19:47:38 -0400
commit7586269c0b52970f60bb69fcb86e765fc1d72309 (patch)
treea0cb04f9527101e8b9331350e4dd9d57e6e019e7 /drivers/usb/host
parent38ffdd62b0f466ef9126ac9f060ade5f218b4887 (diff)
[PATCH] USB: move handoff code
This moves the PCI quirk handling for USB host controllers from the PCI directory to the USB directory. Follow-on patches will need to: (a) merge these copies with the originals in the HCD reset methods. they don't wholly agree, despite doing the very same thing; and (b) eventually change it so "usb-handoff" is the default, to help get more robust USB/BIOS/input/... interactions. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> drivers/Makefile | 2 drivers/pci/quirks.c | 253 --------------------------------------- drivers/usb/Makefile | 1 drivers/usb/host/Makefile | 5 drivers/usb/host/pci-quirks.c | 272 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 280 insertions(+), 253 deletions(-)
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/Makefile5
-rw-r--r--drivers/usb/host/pci-quirks.c272
2 files changed, 275 insertions, 2 deletions
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 350d14fc1cc9..58321d3f314c 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -1,8 +1,9 @@
1# 1#
2# Makefile for USB Host Controller Driver 2# Makefile for USB Host Controller Drivers
3# framework and drivers
4# 3#
5 4
5obj-$(CONFIG_PCI) += pci-quirks.o
6
6obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o 7obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
7obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o 8obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
8obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o 9obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
new file mode 100644
index 000000000000..8ee5c3ed4cd6
--- /dev/null
+++ b/drivers/usb/host/pci-quirks.c
@@ -0,0 +1,272 @@
1/*
2 * This file contains code to reset and initialize USB host controllers.
3 * Some of it includes work-arounds for PCI hardware and BIOS quirks.
4 * It may need to run early during booting -- before USB would normally
5 * initialize -- to ensure that Linux doesn't use any legacy modes.
6 *
7 * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
8 * (and others)
9 */
10
11#include <linux/config.h>
12#include <linux/types.h>
13#include <linux/kernel.h>
14#include <linux/pci.h>
15#include <linux/init.h>
16#include <linux/delay.h>
17#include <linux/acpi.h>
18
19
20/*
21 * PIIX3 USB: We have to disable USB interrupts that are
22 * hardwired to PIRQD# and may be shared with an
23 * external device.
24 *
25 * Legacy Support Register (LEGSUP):
26 * bit13: USB PIRQ Enable (USBPIRQDEN),
27 * bit4: Trap/SMI On IRQ Enable (USBSMIEN).
28 *
29 * We mask out all r/wc bits, too.
30 */
31static void __devinit quirk_piix3_usb(struct pci_dev *dev)
32{
33 u16 legsup;
34
35 pci_read_config_word(dev, 0xc0, &legsup);
36 legsup &= 0x50ef;
37 pci_write_config_word(dev, 0xc0, legsup);
38}
39DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_2, quirk_piix3_usb );
40DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_2, quirk_piix3_usb );
41
42
43/* FIXME these should be the guts of hcd->reset() methods; resolve all
44 * the differences between this version and the HCD's version.
45 */
46
47#define UHCI_USBLEGSUP 0xc0 /* legacy support */
48#define UHCI_USBCMD 0 /* command register */
49#define UHCI_USBSTS 2 /* status register */
50#define UHCI_USBINTR 4 /* interrupt register */
51#define UHCI_USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */
52#define UHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
53#define UHCI_USBCMD_GRESET (1 << 2) /* Global reset */
54#define UHCI_USBCMD_CONFIGURE (1 << 6) /* config semaphore */
55#define UHCI_USBSTS_HALTED (1 << 5) /* HCHalted bit */
56
57#define OHCI_CONTROL 0x04
58#define OHCI_CMDSTATUS 0x08
59#define OHCI_INTRSTATUS 0x0c
60#define OHCI_INTRENABLE 0x10
61#define OHCI_INTRDISABLE 0x14
62#define OHCI_OCR (1 << 3) /* ownership change request */
63#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
64#define OHCI_INTR_OC (1 << 30) /* ownership change */
65
66#define EHCI_HCC_PARAMS 0x08 /* extended capabilities */
67#define EHCI_USBCMD 0 /* command register */
68#define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */
69#define EHCI_USBSTS 4 /* status register */
70#define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */
71#define EHCI_USBINTR 8 /* interrupt register */
72#define EHCI_USBLEGSUP 0 /* legacy support register */
73#define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */
74#define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */
75#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
76#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */
77
78int usb_early_handoff __devinitdata = 0;
79static int __init usb_handoff_early(char *str)
80{
81 usb_early_handoff = 1;
82 return 0;
83}
84__setup("usb-handoff", usb_handoff_early);
85
86static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev)
87{
88 unsigned long base = 0;
89 int wait_time, delta;
90 u16 val, sts;
91 int i;
92
93 for (i = 0; i < PCI_ROM_RESOURCE; i++)
94 if ((pci_resource_flags(pdev, i) & IORESOURCE_IO)) {
95 base = pci_resource_start(pdev, i);
96 break;
97 }
98
99 if (!base)
100 return;
101
102 /*
103 * stop controller
104 */
105 sts = inw(base + UHCI_USBSTS);
106 val = inw(base + UHCI_USBCMD);
107 val &= ~(u16)(UHCI_USBCMD_RUN | UHCI_USBCMD_CONFIGURE);
108 outw(val, base + UHCI_USBCMD);
109
110 /*
111 * wait while it stops if it was running
112 */
113 if ((sts & UHCI_USBSTS_HALTED) == 0)
114 {
115 wait_time = 1000;
116 delta = 100;
117
118 do {
119 outw(0x1f, base + UHCI_USBSTS);
120 udelay(delta);
121 wait_time -= delta;
122 val = inw(base + UHCI_USBSTS);
123 if (val & UHCI_USBSTS_HALTED)
124 break;
125 } while (wait_time > 0);
126 }
127
128 /*
129 * disable interrupts & legacy support
130 */
131 outw(0, base + UHCI_USBINTR);
132 outw(0x1f, base + UHCI_USBSTS);
133 pci_read_config_word(pdev, UHCI_USBLEGSUP, &val);
134 if (val & 0xbf)
135 pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT);
136
137}
138
139static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
140{
141 void __iomem *base;
142 int wait_time;
143
144 base = ioremap_nocache(pci_resource_start(pdev, 0),
145 pci_resource_len(pdev, 0));
146 if (base == NULL) return;
147
148 if (readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
149 wait_time = 500; /* 0.5 seconds */
150 writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);
151 writel(OHCI_OCR, base + OHCI_CMDSTATUS);
152 while (wait_time > 0 &&
153 readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {
154 wait_time -= 10;
155 msleep(10);
156 }
157 }
158
159 /*
160 * disable interrupts
161 */
162 writel(~(u32)0, base + OHCI_INTRDISABLE);
163 writel(~(u32)0, base + OHCI_INTRSTATUS);
164
165 iounmap(base);
166}
167
168static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
169{
170 int wait_time, delta;
171 void __iomem *base, *op_reg_base;
172 u32 hcc_params, val, temp;
173 u8 cap_length;
174
175 base = ioremap_nocache(pci_resource_start(pdev, 0),
176 pci_resource_len(pdev, 0));
177 if (base == NULL) return;
178
179 cap_length = readb(base);
180 op_reg_base = base + cap_length;
181 hcc_params = readl(base + EHCI_HCC_PARAMS);
182 hcc_params = (hcc_params >> 8) & 0xff;
183 if (hcc_params) {
184 pci_read_config_dword(pdev,
185 hcc_params + EHCI_USBLEGSUP,
186 &val);
187 if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) {
188 /*
189 * Ok, BIOS is in smm mode, try to hand off...
190 */
191 pci_read_config_dword(pdev,
192 hcc_params + EHCI_USBLEGCTLSTS,
193 &temp);
194 pci_write_config_dword(pdev,
195 hcc_params + EHCI_USBLEGCTLSTS,
196 temp | EHCI_USBLEGCTLSTS_SOOE);
197 val |= EHCI_USBLEGSUP_OS;
198 pci_write_config_dword(pdev,
199 hcc_params + EHCI_USBLEGSUP,
200 val);
201
202 wait_time = 500;
203 do {
204 msleep(10);
205 wait_time -= 10;
206 pci_read_config_dword(pdev,
207 hcc_params + EHCI_USBLEGSUP,
208 &val);
209 } while (wait_time && (val & EHCI_USBLEGSUP_BIOS));
210 if (!wait_time) {
211 /*
212 * well, possibly buggy BIOS...
213 */
214 printk(KERN_WARNING "EHCI early BIOS handoff "
215 "failed (BIOS bug ?)\n");
216 pci_write_config_dword(pdev,
217 hcc_params + EHCI_USBLEGSUP,
218 EHCI_USBLEGSUP_OS);
219 pci_write_config_dword(pdev,
220 hcc_params + EHCI_USBLEGCTLSTS,
221 0);
222 }
223 }
224 }
225
226 /*
227 * halt EHCI & disable its interrupts in any case
228 */
229 val = readl(op_reg_base + EHCI_USBSTS);
230 if ((val & EHCI_USBSTS_HALTED) == 0) {
231 val = readl(op_reg_base + EHCI_USBCMD);
232 val &= ~EHCI_USBCMD_RUN;
233 writel(val, op_reg_base + EHCI_USBCMD);
234
235 wait_time = 2000;
236 delta = 100;
237 do {
238 writel(0x3f, op_reg_base + EHCI_USBSTS);
239 udelay(delta);
240 wait_time -= delta;
241 val = readl(op_reg_base + EHCI_USBSTS);
242 if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) {
243 break;
244 }
245 } while (wait_time > 0);
246 }
247 writel(0, op_reg_base + EHCI_USBINTR);
248 writel(0x3f, op_reg_base + EHCI_USBSTS);
249
250 iounmap(base);
251
252 return;
253}
254
255
256
257static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
258{
259 if (!usb_early_handoff)
260 return;
261
262 if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x00)) { /* UHCI */
263 quirk_usb_handoff_uhci(pdev);
264 } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10)) { /* OHCI */
265 quirk_usb_handoff_ohci(pdev);
266 } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x20)) { /* EHCI */
267 quirk_usb_disable_ehci(pdev);
268 }
269
270 return;
271}
272DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff);