1 | /*␊ |
2 | * usb.c␊ |
3 | * ␊ |
4 | *␊ |
5 | * Created by mackerintel on 12/20/08.␊ |
6 | * Copyright 2008 mackerintel. All rights reserved.␊ |
7 | *␊ |
8 | */␊ |
9 | ␊ |
10 | #include "libsaio.h"␊ |
11 | #include "boot.h"␊ |
12 | #include "bootstruct.h"␊ |
13 | #include "pci.h"␊ |
14 | ␊ |
15 | #ifndef DEBUG_USB␊ |
16 | #define DEBUG_USB 0␊ |
17 | #endif␊ |
18 | ␊ |
19 | #if DEBUG_USB␊ |
20 | #define DBG(x...)␉verbose(x)␊ |
21 | #else␊ |
22 | #define DBG(x...)␊ |
23 | #endif␊ |
24 | ␊ |
25 | ␊ |
26 | struct pciList␊ |
27 | {␊ |
28 | ␉pci_dt_t* pciDev;␊ |
29 | ␉struct pciList* next;␊ |
30 | };␊ |
31 | ␊ |
32 | struct pciList* usbList = NULL;␊ |
33 | ␊ |
34 | int legacy_off (pci_dt_t *pci_dev);␊ |
35 | int ehci_acquire (pci_dt_t *pci_dev);␊ |
36 | int uhci_reset (pci_dt_t *pci_dev);␊ |
37 | int ohci_handsoff (pci_dt_t *pci_dev);␊ |
38 | ␊ |
39 | // Add usb device to the list␊ |
40 | void notify_usb_dev(pci_dt_t *pci_dev)␊ |
41 | {␊ |
42 | ␉struct pciList* current = (struct pciList*)malloc(sizeof(struct pciList));␊ |
43 | ␉current->next = usbList;␊ |
44 | ␉current->pciDev = pci_dev;␊ |
45 | ␉␉␊ |
46 | ␉usbList = current;␊ |
47 | }␊ |
48 | ␊ |
49 | // Loop through the list and call the apropriate patch function␊ |
50 | int usb_loop()␊ |
51 | {␊ |
52 | ␉int retVal = 1;␊ |
53 | ␉bool fix_ehci, fix_uhci, fix_usb, fix_legacy;␊ |
54 | ␉fix_ehci = fix_uhci = fix_usb = fix_legacy = true;␊ |
55 | ␉␊ |
56 | ␉if (getBoolForKey(kUSBBusFix, &fix_usb, &bootInfo->bootConfig))␊ |
57 | ␉{␊ |
58 | ␉␉fix_ehci = fix_uhci = fix_legacy = fix_usb;␉// Disable all if none set␊ |
59 | ␉}␊ |
60 | ␉else ␊ |
61 | ␉{␊ |
62 | ␉␉getBoolForKey(kEHCIacquire, &fix_ehci, &bootInfo->bootConfig);␊ |
63 | ␉␉getBoolForKey(kUHCIreset, &fix_uhci, &bootInfo->bootConfig);␊ |
64 | ␉␉getBoolForKey(kLegacyOff, &fix_legacy, &bootInfo->bootConfig);␊ |
65 | ␉}␊ |
66 | ␉␊ |
67 | ␉struct pciList* current = usbList;␊ |
68 | ␉␊ |
69 | ␉while(current)␊ |
70 | ␉{␊ |
71 | ␉␉switch (pci_config_read8(current->pciDev->dev.addr, PCI_CLASS_PROG))␊ |
72 | ␉␉{␊ |
73 | ␉␉␉// EHCI␊ |
74 | ␉␉␉case 0x20:␊ |
75 | ␉␉ ␉if(fix_legacy) retVal &= legacy_off(current->pciDev);␊ |
76 | ␉␉ ␉if(fix_ehci) retVal &= ehci_acquire(current->pciDev);␊ |
77 | ␉␉␉␉␊ |
78 | ␉␉␉␉break;␊ |
79 | ␉␉␉// OHCI␊ |
80 | ␉␉␉case 0x10: //␊ |
81 | ␉␉␉␉if (fix_uhci) retVal &= ohci_handsoff(current->pciDev);␊ |
82 | ␉␉␉␉␊ |
83 | ␉␉␉␉break;␊ |
84 | ␉␉␉␉␊ |
85 | ␉␉␉␉␊ |
86 | ␉␉␉// UHCI␊ |
87 | ␉␉␉case 0x00:␊ |
88 | ␉␉␉␉if (fix_uhci) retVal &= uhci_reset(current->pciDev);␊ |
89 | ␊ |
90 | ␉␉␉␉break;␊ |
91 | ␉␉}␊ |
92 | ␉␉␊ |
93 | ␉␉current = current->next;␊ |
94 | ␉}␊ |
95 | ␉return retVal;␊ |
96 | }␊ |
97 | ␊ |
98 | int legacy_off (pci_dt_t *pci_dev)␊ |
99 | {␊ |
100 | ␉// Set usb legacy off modification by Signal64␊ |
101 | ␉// NOTE: This *must* be called after the last file is loaded from the drive in the event that we are booting form usb.␊ |
102 | ␉// NOTE2: This should be called after any getc() call. (aka, after the Wait=y keyworkd is used)␊ |
103 | ␉// AKA: Make this run immediatly before the kernel is called␊ |
104 | ␉uint32_t␉capaddr, opaddr; ␉␉␊ |
105 | ␉uint8_t␉␉eecp;␉␉␉␊ |
106 | ␉uint32_t␉usbcmd, usbsts, usbintr;␉␉␉␊ |
107 | ␉uint32_t␉usblegsup, usblegctlsts;␉␉␊ |
108 | ␉␊ |
109 | ␉int isOSowned;␊ |
110 | ␉int isBIOSowned;␊ |
111 | ␉␊ |
112 | ␉verbose("Setting Legacy USB Off on controller [%04x:%04x] at %02x:%2x.%x\n", ␊ |
113 | ␉␉␉pci_dev->vendor_id, pci_dev->device_id,␊ |
114 | ␉␉␉pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func);␊ |
115 | ␉␊ |
116 | ␉␊ |
117 | ␉// capaddr = Capability Registers = dev.addr + offset stored in dev.addr + 0x10 (USBBASE)␊ |
118 | ␉capaddr = pci_config_read32(pci_dev->dev.addr, 0x10);␉␊ |
119 | ␉␊ |
120 | ␉// opaddr = Operational Registers = capaddr + offset (8bit CAPLENGTH in Capability Registers + offset 0)␊ |
121 | ␉opaddr = capaddr + *((unsigned char*)(capaddr)); ␉␉␊ |
122 | ␉␊ |
123 | ␉// eecp = EHCI Extended Capabilities offset = capaddr HCCPARAMS bits 15:8␊ |
124 | ␉eecp=*((unsigned char*)(capaddr + 9));␊ |
125 | ␉␊ |
126 | ␉DBG("capaddr=%x opaddr=%x eecp=%x\n", capaddr, opaddr, eecp);␊ |
127 | ␉␊ |
128 | ␉usbcmd = *((unsigned int*)(opaddr));␉␉␉// Command Register␊ |
129 | ␉usbsts = *((unsigned int*)(opaddr + 4));␉␉// Status Register␊ |
130 | ␉usbintr = *((unsigned int*)(opaddr + 8));␉␉// Interrupt Enable Register␊ |
131 | ␉␊ |
132 | ␉DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);␊ |
133 | ␉␊ |
134 | ␉// read PCI Config 32bit USBLEGSUP (eecp+0) ␊ |
135 | ␉usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);␊ |
136 | ␉␊ |
137 | ␉// informational only␊ |
138 | ␉isBIOSowned = !!((usblegsup) & (1 << (16)));␊ |
139 | ␉isOSowned = !!((usblegsup) & (1 << (24)));␊ |
140 | ␉␊ |
141 | ␉// read PCI Config 32bit USBLEGCTLSTS (eecp+4) ␊ |
142 | ␉usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);␊ |
143 | ␉␊ |
144 | ␉DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);␊ |
145 | ␉␊ |
146 | ␉// Reset registers to Legacy OFF␊ |
147 | ␉DBG("Clearing USBLEGCTLSTS\n");␊ |
148 | ␉pci_config_write32(pci_dev->dev.addr, eecp + 4, 0);␉//usblegctlsts␊ |
149 | ␉␊ |
150 | ␉// if delay value is in milliseconds it doesn't appear to work. ␊ |
151 | ␉// setting value to anything up to 65535 does not add the expected delay here.␊ |
152 | ␉delay(100);␊ |
153 | ␉␊ |
154 | ␉usbcmd = *((unsigned int*)(opaddr));␊ |
155 | ␉usbsts = *((unsigned int*)(opaddr + 4));␊ |
156 | ␉usbintr = *((unsigned int*)(opaddr + 8));␊ |
157 | ␉␊ |
158 | ␉DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);␊ |
159 | ␉␊ |
160 | ␉DBG("Clearing Registers\n");␊ |
161 | ␉␊ |
162 | ␉// clear registers to default␊ |
163 | ␉usbcmd = (usbcmd & 0xffffff00);␊ |
164 | ␉*((unsigned int*)(opaddr)) = usbcmd;␊ |
165 | ␉*((unsigned int*)(opaddr + 8)) = 0;␉␉␉␉␉//usbintr - clear interrupt registers␊ |
166 | ␉*((unsigned int*)(opaddr + 4)) = 0x1000;␉␉␉//usbsts - clear status registers ␉␊ |
167 | ␉pci_config_write32(pci_dev->dev.addr, eecp, 1);␉␉//usblegsup␊ |
168 | ␉␊ |
169 | ␉// get the results␊ |
170 | ␉usbcmd = *((unsigned int*)(opaddr));␊ |
171 | ␉usbsts = *((unsigned int*)(opaddr + 4));␊ |
172 | ␉usbintr = *((unsigned int*)(opaddr + 8));␊ |
173 | ␉␊ |
174 | ␉DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);␊ |
175 | ␉␊ |
176 | ␉// read 32bit USBLEGSUP (eecp+0) ␊ |
177 | ␉usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);␊ |
178 | ␉␊ |
179 | ␉// informational only␊ |
180 | ␉isBIOSowned = !!((usblegsup) & (1 << (16)));␊ |
181 | ␉isOSowned = !!((usblegsup) & (1 << (24)));␊ |
182 | ␉␊ |
183 | ␉// read 32bit USBLEGCTLSTS (eecp+4) ␊ |
184 | ␉usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);␊ |
185 | ␉␊ |
186 | ␉DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);␊ |
187 | ␉␊ |
188 | ␉msglog("Legacy USB Off Done\n");␉␊ |
189 | ␉return 1;␊ |
190 | }␊ |
191 | ␊ |
192 | int ehci_acquire (pci_dt_t *pci_dev)␊ |
193 | {␊ |
194 | ␉int␉␉j, k;␊ |
195 | ␉uint32_t␉base;␊ |
196 | ␉uint8_t␉␉eecp;␊ |
197 | ␉uint8_t␉␉legacy[8];␊ |
198 | ␉bool␉␉isOwnershipConflict;␉␊ |
199 | ␉bool␉␉alwaysHardBIOSReset;␊ |
200 | ␊ |
201 | ␉alwaysHardBIOSReset = false;␉␊ |
202 | ␉if (!getBoolForKey(kEHCIhard, &alwaysHardBIOSReset, &bootInfo->bootConfig)) {␊ |
203 | ␉␉alwaysHardBIOSReset = true;␊ |
204 | ␉}␊ |
205 | ␊ |
206 | ␉pci_config_write16(pci_dev->dev.addr, 0x04, 0x0002);␊ |
207 | ␉base = pci_config_read32(pci_dev->dev.addr, 0x10);␊ |
208 | ␊ |
209 | ␉msglog("EHCI controller [%04x:%04x] at %02x:%2x.%x DMA @%x\n", ␊ |
210 | ␉␉pci_dev->vendor_id, pci_dev->device_id,␊ |
211 | ␉␉pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func, ␊ |
212 | ␉␉base);␊ |
213 | ␊ |
214 | ␉if (*((unsigned char*)base) < 0xc)␊ |
215 | ␉{␊ |
216 | ␉␉DBG("Config space too small: no legacy implementation\n");␊ |
217 | ␉␉return 1;␊ |
218 | ␉}␊ |
219 | ␉eecp = *((unsigned char*)(base + 9));␊ |
220 | ␉if (!eecp) {␊ |
221 | ␉␉DBG("No extended capabilities: no legacy implementation\n");␊ |
222 | ␉␉return 1;␊ |
223 | ␉}␊ |
224 | ␊ |
225 | ␉DBG("eecp=%x\n",eecp);␊ |
226 | ␊ |
227 | ␉// bad way to do it␊ |
228 | ␉// pci_conf_write(pci_dev->dev.addr, eecp, 4, 0x01000001);␊ |
229 | ␉for (j = 0; j < 8; j++) {␊ |
230 | ␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
231 | ␉␉DBG("%02x ", legacy[j]);␊ |
232 | ␉}␊ |
233 | ␉DBG("\n");␊ |
234 | ␊ |
235 | ␉//Real Job: based on orByte's AppleUSBEHCI.cpp␊ |
236 | ␉//We try soft reset first - some systems hang on reboot with hard reset␊ |
237 | ␉// Definitely needed during reboot on 10.4.6␊ |
238 | ␊ |
239 | ␉isOwnershipConflict = ((legacy[3] & 1 != 0) && (legacy[2] & 1 != 0));␊ |
240 | ␉if (!alwaysHardBIOSReset && isOwnershipConflict) {␊ |
241 | ␉␉DBG("EHCI - Ownership conflict - attempting soft reset ...\n");␊ |
242 | ␉␉DBG("EHCI - toggle OS Ownership to 0\n");␊ |
243 | ␉␉pci_config_write8(pci_dev->dev.addr, eecp + 3, 0);␊ |
244 | ␉␉for (k = 0; k < 25; k++) {␊ |
245 | ␉␉␉for (j = 0; j < 8; j++) {␊ |
246 | ␉␉␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
247 | ␉␉␉}␊ |
248 | ␉␉␉if (legacy[3] == 0) {␊ |
249 | ␉␉␉␉break;␊ |
250 | ␉␉␉}␊ |
251 | ␉␉␉delay(10);␊ |
252 | ␉␉}␊ |
253 | ␉}␉␊ |
254 | ␊ |
255 | ␉DBG("Found USBLEGSUP_ID - value %x:%x - writing OSOwned\n", legacy[3],legacy[2]);␊ |
256 | ␉pci_config_write8(pci_dev->dev.addr, eecp + 3, 1);␊ |
257 | ␊ |
258 | ␉// wait for kEHCI_USBLEGSUP_BIOSOwned bit to clear␊ |
259 | ␉for (k = 0; k < 25; k++) {␊ |
260 | ␉␉for (j = 0;j < 8; j++) {␊ |
261 | ␉␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
262 | ␉␉}␊ |
263 | ␉␉DBG ("%x:%x,",legacy[3],legacy[2]);␊ |
264 | ␉␉if (legacy[2] == 0) {␊ |
265 | ␉␉␉break;␊ |
266 | ␉␉}␊ |
267 | ␉␉delay(10);␊ |
268 | ␉}␊ |
269 | ␊ |
270 | ␉for (j = 0;j < 8; j++) {␊ |
271 | ␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
272 | ␉}␊ |
273 | ␉isOwnershipConflict = ((legacy[2]) != 0);␊ |
274 | ␉if (isOwnershipConflict) {␊ |
275 | ␉␉// Soft reset has failed. Assume SMI being ignored␊ |
276 | ␉␉// Hard reset␊ |
277 | ␉␉// Force Clear BIOS BIT␊ |
278 | ␉␉DBG("EHCI - Ownership conflict - attempting hard reset ...\n");␉␉␉␊ |
279 | ␉␉DBG ("%x:%x\n",legacy[3],legacy[2]);␊ |
280 | ␉␉DBG("EHCI - Force BIOS Ownership to 0\n");␊ |
281 | ␊ |
282 | ␉␉pci_config_write8(pci_dev->dev.addr, eecp + 2, 0);␊ |
283 | ␉␉for (k = 0; k < 25; k++) {␊ |
284 | ␉␉␉for (j = 0; j < 8; j++) {␊ |
285 | ␉␉␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
286 | ␉␉␉}␊ |
287 | ␉␉␉DBG ("%x:%x,",legacy[3],legacy[2]);␊ |
288 | ␊ |
289 | ␉␉␉if ((legacy[2]) == 0) {␊ |
290 | ␉␉␉␉break;␊ |
291 | ␉␉␉}␊ |
292 | ␉␉␉delay(10);␉␊ |
293 | ␉␉}␉␉␊ |
294 | ␉␉// Disable further SMI events␊ |
295 | ␉␉for (j = 4; j < 8; j++) {␊ |
296 | ␉␉␉pci_config_write8(pci_dev->dev.addr, eecp + j, 0);␊ |
297 | ␉␉}␊ |
298 | ␉}␊ |
299 | ␊ |
300 | ␉for (j = 0; j < 8; j++) {␊ |
301 | ␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
302 | ␉}␊ |
303 | ␊ |
304 | ␉DBG ("%x:%x\n",legacy[3],legacy[2]);␊ |
305 | ␊ |
306 | ␉// Final Ownership Resolution Check...␊ |
307 | ␉if (legacy[2] & 1) {␉␉␉␉␉␊ |
308 | ␉␉DBG("EHCI controller unable to take control from BIOS\n");␊ |
309 | ␉␉return 0;␊ |
310 | ␉}␊ |
311 | ␊ |
312 | ␉msglog("EHCI Acquire OS Ownership done\n");␉␊ |
313 | ␉return 1;␊ |
314 | }␊ |
315 | ␊ |
316 | int uhci_reset (pci_dt_t *pci_dev)␊ |
317 | {␊ |
318 | ␉uint32_t base, port_base;␊ |
319 | ␉␊ |
320 | ␉base = pci_config_read32(pci_dev->dev.addr, 0x20);␊ |
321 | ␉port_base = (base >> 5) & 0x07ff;␊ |
322 | ␊ |
323 | ␉msglog("UHCI controller [%04x:%04x] at %02x:%2x.%x base %x(%x) reset\n", ␊ |
324 | ␉␉pci_dev->vendor_id, pci_dev->device_id,␊ |
325 | ␉␉pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func, ␊ |
326 | ␉␉port_base, base);␊ |
327 | ␉␊ |
328 | ␉pci_config_write16(pci_dev->dev.addr, 0xc0, 0x8f00);␊ |
329 | ␊ |
330 | ␉outw (port_base, 0x0002);␊ |
331 | ␉delay(10);␊ |
332 | ␉outw (port_base+4,0);␊ |
333 | ␉delay(10);␊ |
334 | ␉outw (port_base,0);␊ |
335 | ␉return 1;␊ |
336 | }␊ |
337 | ␊ |
338 | int ohci_handsoff (pci_dt_t *pci_dev)␊ |
339 | {␊ |
340 | ␉uint32_t base, control;␊ |
341 | #define OHCI_CTRL_MASK␉(1 << 9)␊ |
342 | #define OHCI_CONTROL␉␉0x04␊ |
343 | #define OHCI_INTRDISABLE␉0x14␊ |
344 | #define OHCI_INTRSTATUS␉␉0x0c␊ |
345 | ␉␊ |
346 | ␉base = pci_config_read32(pci_dev->dev.addr, 0x10);␊ |
347 | ␉msglog("OHCI controller [%04x:%04x] at %02x:%2x.%x base %x reset\n", ␊ |
348 | ␉␉ pci_dev->vendor_id, pci_dev->device_id,␊ |
349 | ␉␉ pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func, ␊ |
350 | ␉␉ base);␊ |
351 | //Linux␊ |
352 | ␉/*␊ |
353 | ␉ if (control & OHCI_CTRL_IR) {␊ |
354 | int wait_time = 500; // arbitrary; 5 seconds ␊ |
355 | writel(OHCI_INTR_OC, base + OHCI_INTRENABLE);␊ |
356 | writel(OHCI_OCR, base + OHCI_CMDSTATUS);␊ |
357 | while (wait_time > 0 &&␊ |
358 | ␉␉␉␉ 194 readl(base + OHCI_CONTROL) & OHCI_CTRL_IR) {␊ |
359 | wait_time -= 10;␊ |
360 | msleep(10);␊ |
361 | }␊ |
362 | if (wait_time <= 0)␊ |
363 | dev_warn(&pdev->dev, "OHCI: BIOS handoff failed"␊ |
364 | ␉␉␉␉␉␉␉␉" (BIOS bug?) %08x\n", readl(base + OHCI_CONTROL));␊ |
365 | }␊ |
366 | ␉ */␊ |
367 | ␉control = *(uint32_t *)(base + OHCI_CONTROL);␊ |
368 | ␉*(uint32_t *)(base + OHCI_CONTROL) = control & OHCI_CTRL_MASK;␊ |
369 | //Linux␊ |
370 | ␉/*␊ |
371 | ␉ * disable interrupts ␊ |
372 | writel(~(u32)0, base + OHCI_INTRDISABLE);␊ |
373 | writel(~(u32)0, base + OHCI_INTRSTATUS);␊ |
374 | ␊ |
375 | ␉ */␊ |
376 | ␉return 1;␊ |
377 | }␊ |
378 | |