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> | |
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 | */ | |
31 | static 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 | } | |
39 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_2, quirk_piix3_usb ); | |
40 | DECLARE_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 | ||
78 | int usb_early_handoff __devinitdata = 0; | |
79 | static 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 | ||
86 | static 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 | ||
139 | static 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 | ||
168 | static 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 | ||
257 | static 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 | } | |
272 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff); |