Commit | Line | Data |
---|---|---|
7586269c DB |
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> | |
bb200f6e AS |
12 | #ifdef CONFIG_USB_DEBUG |
13 | #define DEBUG | |
14 | #else | |
15 | #undef DEBUG | |
16 | #endif | |
17 | ||
7586269c DB |
18 | #include <linux/types.h> |
19 | #include <linux/kernel.h> | |
20 | #include <linux/pci.h> | |
21 | #include <linux/init.h> | |
22 | #include <linux/delay.h> | |
23 | #include <linux/acpi.h> | |
24 | ||
25 | ||
26 | /* | |
27 | * PIIX3 USB: We have to disable USB interrupts that are | |
28 | * hardwired to PIRQD# and may be shared with an | |
29 | * external device. | |
30 | * | |
31 | * Legacy Support Register (LEGSUP): | |
32 | * bit13: USB PIRQ Enable (USBPIRQDEN), | |
33 | * bit4: Trap/SMI On IRQ Enable (USBSMIEN). | |
34 | * | |
35 | * We mask out all r/wc bits, too. | |
36 | */ | |
37 | static void __devinit quirk_piix3_usb(struct pci_dev *dev) | |
38 | { | |
39 | u16 legsup; | |
40 | ||
41 | pci_read_config_word(dev, 0xc0, &legsup); | |
42 | legsup &= 0x50ef; | |
43 | pci_write_config_word(dev, 0xc0, legsup); | |
44 | } | |
45 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_2, quirk_piix3_usb ); | |
46 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_2, quirk_piix3_usb ); | |
47 | ||
48 | ||
49 | /* FIXME these should be the guts of hcd->reset() methods; resolve all | |
50 | * the differences between this version and the HCD's version. | |
51 | */ | |
52 | ||
53 | #define UHCI_USBLEGSUP 0xc0 /* legacy support */ | |
54 | #define UHCI_USBCMD 0 /* command register */ | |
7586269c | 55 | #define UHCI_USBINTR 4 /* interrupt register */ |
bb200f6e AS |
56 | #define UHCI_USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ |
57 | #define UHCI_USBLEGSUP_RO 0x5040 /* R/O and reserved bits */ | |
58 | #define UHCI_USBCMD_RUN 0x0001 /* RUN/STOP bit */ | |
59 | #define UHCI_USBCMD_HCRESET 0x0002 /* Host Controller reset */ | |
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 */ | |
7586269c DB |
63 | |
64 | #define OHCI_CONTROL 0x04 | |
65 | #define OHCI_CMDSTATUS 0x08 | |
66 | #define OHCI_INTRSTATUS 0x0c | |
67 | #define OHCI_INTRENABLE 0x10 | |
68 | #define OHCI_INTRDISABLE 0x14 | |
69 | #define OHCI_OCR (1 << 3) /* ownership change request */ | |
f2cb36c1 | 70 | #define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ |
7586269c DB |
71 | #define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ |
72 | #define OHCI_INTR_OC (1 << 30) /* ownership change */ | |
73 | ||
74 | #define EHCI_HCC_PARAMS 0x08 /* extended capabilities */ | |
75 | #define EHCI_USBCMD 0 /* command register */ | |
76 | #define EHCI_USBCMD_RUN (1 << 0) /* RUN/STOP bit */ | |
77 | #define EHCI_USBSTS 4 /* status register */ | |
78 | #define EHCI_USBSTS_HALTED (1 << 12) /* HCHalted bit */ | |
79 | #define EHCI_USBINTR 8 /* interrupt register */ | |
80 | #define EHCI_USBLEGSUP 0 /* legacy support register */ | |
81 | #define EHCI_USBLEGSUP_BIOS (1 << 16) /* BIOS semaphore */ | |
82 | #define EHCI_USBLEGSUP_OS (1 << 24) /* OS semaphore */ | |
83 | #define EHCI_USBLEGCTLSTS 4 /* legacy control/status */ | |
84 | #define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */ | |
85 | ||
86 | int usb_early_handoff __devinitdata = 0; | |
87 | static int __init usb_handoff_early(char *str) | |
88 | { | |
89 | usb_early_handoff = 1; | |
90 | return 0; | |
91 | } | |
92 | __setup("usb-handoff", usb_handoff_early); | |
93 | ||
bb200f6e AS |
94 | /* |
95 | * Make sure the controller is completely inactive, unable to | |
96 | * generate interrupts or do DMA. | |
97 | */ | |
98 | void uhci_reset_hc(struct pci_dev *pdev, unsigned long base) | |
99 | { | |
100 | /* Turn off PIRQ enable and SMI enable. (This also turns off the | |
101 | * BIOS's USB Legacy Support.) Turn off all the R/WC bits too. | |
102 | */ | |
103 | pci_write_config_word(pdev, UHCI_USBLEGSUP, UHCI_USBLEGSUP_RWC); | |
104 | ||
105 | /* Reset the HC - this will force us to get a | |
106 | * new notification of any already connected | |
107 | * ports due to the virtual disconnect that it | |
108 | * implies. | |
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 | } | |
122 | EXPORT_SYMBOL_GPL(uhci_reset_hc); | |
123 | ||
124 | /* | |
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 | */ | |
130 | int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base) | |
131 | { | |
132 | u16 legsup; | |
133 | unsigned int cmd, intr; | |
134 | ||
135 | /* | |
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. | |
144 | */ | |
145 | pci_read_config_word(pdev, UHCI_USBLEGSUP, &legsup); | |
146 | if (legsup & ~(UHCI_USBLEGSUP_RO | UHCI_USBLEGSUP_RWC)) { | |
147 | dev_dbg(&pdev->dev, "%s: legsup = 0x%04x\n", | |
148 | __FUNCTION__, legsup); | |
149 | goto reset_needed; | |
150 | } | |
151 | ||
152 | cmd = inw(base + UHCI_USBCMD); | |
153 | if ((cmd & UHCI_USBCMD_RUN) || !(cmd & UHCI_USBCMD_CONFIGURE) || | |
154 | !(cmd & UHCI_USBCMD_EGSM)) { | |
155 | dev_dbg(&pdev->dev, "%s: cmd = 0x%04x\n", | |
156 | __FUNCTION__, cmd); | |
157 | goto reset_needed; | |
158 | } | |
159 | ||
160 | intr = inw(base + UHCI_USBINTR); | |
161 | if (intr & (~UHCI_USBINTR_RESUME)) { | |
162 | dev_dbg(&pdev->dev, "%s: intr = 0x%04x\n", | |
163 | __FUNCTION__, intr); | |
164 | goto reset_needed; | |
165 | } | |
166 | return 0; | |
167 | ||
168 | reset_needed: | |
169 | dev_dbg(&pdev->dev, "Performing full reset\n"); | |
170 | uhci_reset_hc(pdev, base); | |
171 | return 1; | |
172 | } | |
173 | EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); | |
174 | ||
7586269c DB |
175 | static void __devinit quirk_usb_handoff_uhci(struct pci_dev *pdev) |
176 | { | |
177 | unsigned long base = 0; | |
7586269c DB |
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 | } | |
185 | ||
bb200f6e AS |
186 | if (base) |
187 | uhci_check_and_reset_hc(pdev, base); | |
7586269c DB |
188 | } |
189 | ||
190 | static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) | |
191 | { | |
192 | void __iomem *base; | |
193 | int wait_time; | |
f2cb36c1 | 194 | u32 control; |
7586269c DB |
195 | |
196 | base = ioremap_nocache(pci_resource_start(pdev, 0), | |
197 | pci_resource_len(pdev, 0)); | |
198 | if (base == NULL) return; | |
199 | ||
f2cb36c1 DB |
200 | /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ |
201 | #ifndef __hppa__ | |
202 | control = readl(base + OHCI_CONTROL); | |
203 | if (control & OHCI_CTRL_IR) { | |
204 | wait_time = 500; /* arbitrary; 5 seconds */ | |
7586269c DB |
205 | writel(OHCI_INTR_OC, base + OHCI_INTRENABLE); |
206 | writel(OHCI_OCR, base + OHCI_CMDSTATUS); | |
207 | while (wait_time > 0 && | |
208 | readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) { | |
209 | wait_time -= 10; | |
210 | msleep(10); | |
211 | } | |
f2cb36c1 DB |
212 | if (wait_time <= 0) |
213 | printk(KERN_WARNING "%s %s: early BIOS handoff " | |
214 | "failed (BIOS bug ?)\n", | |
215 | pdev->dev.bus_id, "OHCI"); | |
216 | ||
217 | /* reset controller, preserving RWC */ | |
218 | writel(control & OHCI_CTRL_RWC, base + OHCI_CONTROL); | |
7586269c | 219 | } |
f2cb36c1 | 220 | #endif |
7586269c DB |
221 | |
222 | /* | |
223 | * disable interrupts | |
224 | */ | |
225 | writel(~(u32)0, base + OHCI_INTRDISABLE); | |
226 | writel(~(u32)0, base + OHCI_INTRSTATUS); | |
227 | ||
228 | iounmap(base); | |
229 | } | |
230 | ||
231 | static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) | |
232 | { | |
233 | int wait_time, delta; | |
234 | void __iomem *base, *op_reg_base; | |
235 | u32 hcc_params, val, temp; | |
236 | u8 cap_length; | |
237 | ||
238 | base = ioremap_nocache(pci_resource_start(pdev, 0), | |
239 | pci_resource_len(pdev, 0)); | |
240 | if (base == NULL) return; | |
241 | ||
242 | cap_length = readb(base); | |
243 | op_reg_base = base + cap_length; | |
244 | hcc_params = readl(base + EHCI_HCC_PARAMS); | |
245 | hcc_params = (hcc_params >> 8) & 0xff; | |
246 | if (hcc_params) { | |
247 | pci_read_config_dword(pdev, | |
248 | hcc_params + EHCI_USBLEGSUP, | |
249 | &val); | |
250 | if (((val & 0xff) == 1) && (val & EHCI_USBLEGSUP_BIOS)) { | |
251 | /* | |
252 | * Ok, BIOS is in smm mode, try to hand off... | |
253 | */ | |
254 | pci_read_config_dword(pdev, | |
255 | hcc_params + EHCI_USBLEGCTLSTS, | |
256 | &temp); | |
257 | pci_write_config_dword(pdev, | |
258 | hcc_params + EHCI_USBLEGCTLSTS, | |
259 | temp | EHCI_USBLEGCTLSTS_SOOE); | |
260 | val |= EHCI_USBLEGSUP_OS; | |
261 | pci_write_config_dword(pdev, | |
262 | hcc_params + EHCI_USBLEGSUP, | |
263 | val); | |
264 | ||
265 | wait_time = 500; | |
266 | do { | |
267 | msleep(10); | |
268 | wait_time -= 10; | |
269 | pci_read_config_dword(pdev, | |
270 | hcc_params + EHCI_USBLEGSUP, | |
271 | &val); | |
272 | } while (wait_time && (val & EHCI_USBLEGSUP_BIOS)); | |
273 | if (!wait_time) { | |
274 | /* | |
275 | * well, possibly buggy BIOS... | |
276 | */ | |
f2cb36c1 DB |
277 | printk(KERN_WARNING "%s %s: early BIOS handoff " |
278 | "failed (BIOS bug ?)\n", | |
279 | pdev->dev.bus_id, "EHCI"); | |
7586269c DB |
280 | pci_write_config_dword(pdev, |
281 | hcc_params + EHCI_USBLEGSUP, | |
282 | EHCI_USBLEGSUP_OS); | |
283 | pci_write_config_dword(pdev, | |
284 | hcc_params + EHCI_USBLEGCTLSTS, | |
285 | 0); | |
286 | } | |
287 | } | |
288 | } | |
289 | ||
290 | /* | |
291 | * halt EHCI & disable its interrupts in any case | |
292 | */ | |
293 | val = readl(op_reg_base + EHCI_USBSTS); | |
294 | if ((val & EHCI_USBSTS_HALTED) == 0) { | |
295 | val = readl(op_reg_base + EHCI_USBCMD); | |
296 | val &= ~EHCI_USBCMD_RUN; | |
297 | writel(val, op_reg_base + EHCI_USBCMD); | |
298 | ||
299 | wait_time = 2000; | |
300 | delta = 100; | |
301 | do { | |
302 | writel(0x3f, op_reg_base + EHCI_USBSTS); | |
303 | udelay(delta); | |
304 | wait_time -= delta; | |
305 | val = readl(op_reg_base + EHCI_USBSTS); | |
306 | if ((val == ~(u32)0) || (val & EHCI_USBSTS_HALTED)) { | |
307 | break; | |
308 | } | |
309 | } while (wait_time > 0); | |
310 | } | |
311 | writel(0, op_reg_base + EHCI_USBINTR); | |
312 | writel(0x3f, op_reg_base + EHCI_USBSTS); | |
313 | ||
314 | iounmap(base); | |
315 | ||
316 | return; | |
317 | } | |
318 | ||
319 | ||
320 | ||
321 | static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev) | |
322 | { | |
323 | if (!usb_early_handoff) | |
324 | return; | |
325 | ||
326 | if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x00)) { /* UHCI */ | |
327 | quirk_usb_handoff_uhci(pdev); | |
328 | } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x10)) { /* OHCI */ | |
329 | quirk_usb_handoff_ohci(pdev); | |
330 | } else if (pdev->class == ((PCI_CLASS_SERIAL_USB << 8) | 0x20)) { /* EHCI */ | |
331 | quirk_usb_disable_ehci(pdev); | |
332 | } | |
333 | ||
334 | return; | |
335 | } | |
336 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); |