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