Chameleon

Chameleon Svn Source Tree

Root/trunk/i386/libsaio/usb.c

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 "config.h"
11#include "libsaio.h"
12#include "boot.h"
13#include "bootstruct.h"
14#include "pci.h"
15
16#if DEBUG_USB
17#define DBG(x...)printf(x)
18#else
19#define DBG(x...)
20#endif
21
22
23struct pciList
24{
25pci_dt_t* pciDev;
26struct pciList* next;
27};
28
29struct pciList* usbList = NULL;
30
31static int legacy_off (pci_dt_t *pci_dev);
32static int ehci_acquire (pci_dt_t *pci_dev);
33static int uhci_reset (pci_dt_t *pci_dev);
34static int xhci_legacy_off(pci_dt_t *pci_dev);
35
36// Add usb device to the list
37void notify_usb_dev(pci_dt_t *pci_dev)
38{
39struct pciList *current = usbList;
40if(!usbList)
41{
42usbList = (struct pciList*)malloc(sizeof(struct pciList));
43usbList->next = NULL;
44usbList->pciDev = pci_dev;
45
46}
47else
48{
49while(current != NULL && current->next != NULL)
50{
51current = current->next;
52}
53current->next = (struct pciList*)malloc(sizeof(struct pciList));
54current = current->next;
55
56current->pciDev = pci_dev;
57current->next = NULL;
58}
59}
60
61// Loop through the list and call the apropriate patch function
62int usb_loop()
63{
64int retVal = 1;
65bool fix_xhci, fix_ehci, fix_uhci, fix_usb, fix_legacy;
66fix_xhci = fix_ehci = fix_uhci = fix_usb = fix_legacy = false;
67
68if (getBoolForKey(kUSBBusFix, &fix_usb, &bootInfo->chameleonConfig))
69{
70fix_xhci = fix_ehci = fix_uhci = fix_legacy = fix_usb;// Disable all if none set
71}
72else
73{
74getBoolForKey(kXHCILegacyOff, &fix_xhci, &bootInfo->chameleonConfig);
75getBoolForKey(kEHCIacquire, &fix_ehci, &bootInfo->chameleonConfig);
76getBoolForKey(kUHCIreset, &fix_uhci, &bootInfo->chameleonConfig);
77getBoolForKey(kLegacyOff, &fix_legacy, &bootInfo->chameleonConfig);
78}
79
80struct pciList *current = usbList;
81
82while(current)
83{
84switch (pci_config_read8(current->pciDev->dev.addr, PCI_CLASS_PROG))
85{
86// XHCI
87case PCI_IF_XHCI:
88if(fix_xhci || fix_legacy) retVal &= xhci_legacy_off(current->pciDev);
89break;
90
91// EHCI
92case PCI_IF_EHCI:
93 if(fix_ehci) retVal &= ehci_acquire(current->pciDev);
94 if(fix_legacy) retVal &= legacy_off(current->pciDev);
95
96break;
97
98// UHCI
99case PCI_IF_UHCI:
100if (fix_uhci) retVal &= uhci_reset(current->pciDev);
101
102break;
103}
104
105current = current->next;
106}
107return retVal;
108}
109
110static int legacy_off (pci_dt_t *pci_dev)
111{
112// Set usb legacy off modification by Signal64
113// NOTE: This *must* be called after the last file is loaded from the drive in the event that we are booting form usb.
114// NOTE2: This should be called after any getc()/getchar() call. (aka, after the Wait=y keyworkd is used)
115// AKA: Make this run immediatly before the kernel is called
116uint32_tcapaddr, opaddr;
117uint8_teecp;
118uint32_tusbcmd, usbsts, usbintr;
119uint32_tusblegsup, usblegctlsts;
120
121int isOSowned;
122int isBIOSowned;
123
124verbose("\tSetting Legacy USB Off on controller [%04x:%04x] at %02x:%2x.%x\n",
125pci_dev->vendor_id, pci_dev->device_id,
126pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func);
127
128
129// capaddr = Capability Registers = dev.addr + offset stored in dev.addr + 0x10 (USBBASE)
130capaddr = pci_config_read32(pci_dev->dev.addr, 0x10);
131
132// opaddr = Operational Registers = capaddr + offset (8bit CAPLENGTH in Capability Registers + offset 0)
133opaddr = capaddr + *((unsigned char*)(capaddr));
134
135// eecp = EHCI Extended Capabilities offset = capaddr HCCPARAMS bits 15:8
136eecp=*((unsigned char*)(capaddr + 9));
137
138DBG("\tcapaddr=%x opaddr=%x eecp=%x\n", capaddr, opaddr, eecp);
139
140usbcmd = *((unsigned int*)(opaddr));// Command Register
141usbsts = *((unsigned int*)(opaddr + 4));// Status Register
142usbintr = *((unsigned int*)(opaddr + 8));// Interrupt Enable Register
143
144DBG("\tusbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
145
146// read PCI Config 32bit USBLEGSUP (eecp+0)
147usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);
148
149// informational only
150isBIOSowned = !!((usblegsup) & (1 << (16)));
151isOSowned = !!((usblegsup) & (1 << (24)));
152
153// read PCI Config 32bit USBLEGCTLSTS (eecp+4)
154usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);
155
156DBG("\tusblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
157
158// Reset registers to Legacy OFF
159DBG("\tClearing USBLEGCTLSTS\n");
160pci_config_write32(pci_dev->dev.addr, eecp + 4, 0);//usblegctlsts
161
162delay(100000);
163
164usbcmd = *((unsigned int*)(opaddr));
165usbsts = *((unsigned int*)(opaddr + 4));
166usbintr = *((unsigned int*)(opaddr + 8));
167
168DBG("\tusbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
169
170DBG("\tClearing Registers\n");
171
172// clear registers to default
173usbcmd = (usbcmd & 0xffffff00);
174*((unsigned int*)(opaddr)) = usbcmd;
175*((unsigned int*)(opaddr + 8)) = 0;//usbintr - clear interrupt registers
176*((unsigned int*)(opaddr + 4)) = 0x1000;//usbsts - clear status registers
177pci_config_write32(pci_dev->dev.addr, eecp, 1);//usblegsup
178
179// get the results
180usbcmd = *((unsigned int*)(opaddr));
181usbsts = *((unsigned int*)(opaddr + 4));
182usbintr = *((unsigned int*)(opaddr + 8));
183
184DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
185
186// read 32bit USBLEGSUP (eecp+0)
187usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);
188
189// informational only
190isBIOSowned = !!((usblegsup) & (1 << (16)));
191isOSowned = !!((usblegsup) & (1 << (24)));
192
193// read 32bit USBLEGCTLSTS (eecp+4)
194usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);
195
196DBG("\tusblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
197
198verbose("\tLegacy USB Off Done\n");
199return 1;
200}
201
202static int ehci_acquire (pci_dt_t *pci_dev)
203{
204intj, k;
205uint32_tbase;
206uint8_teecp;
207uint8_tlegacy[8];
208boolisOwnershipConflict;
209boolalwaysHardBIOSReset;
210
211alwaysHardBIOSReset = false;
212if (!getBoolForKey(kEHCIhard, &alwaysHardBIOSReset, &bootInfo->chameleonConfig)) {
213alwaysHardBIOSReset = true;
214}
215
216pci_config_write16(pci_dev->dev.addr, 0x04, 0x0002);
217base = pci_config_read32(pci_dev->dev.addr, 0x10);
218
219verbose("\tEHCI controller [%04x:%04x] at %02x:%2x.%x DMA @%x\n",
220pci_dev->vendor_id, pci_dev->device_id,
221pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func,
222base);
223
224if (*((unsigned char*)base) < 0xc)
225{
226DBG("Config space too small: no legacy implementation\n");
227return 1;
228}
229eecp = *((unsigned char*)(base + 9));
230if (!eecp) {
231DBG("No extended capabilities: no legacy implementation\n");
232return 1;
233}
234
235DBG("\teecp=%x\n",eecp);
236
237// bad way to do it
238// pci_conf_write(pci_dev->dev.addr, eecp, 4, 0x01000001);
239for (j = 0; j < 8; j++) {
240legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
241DBG("\t%02x ", legacy[j]);
242}
243DBG("\n");
244
245//Real Job: based on orByte's AppleUSBEHCI.cpp
246//We try soft reset first - some systems hang on reboot with hard reset
247// Definitely needed during reboot on 10.4.6
248
249isOwnershipConflict = (((legacy[3] & 1) != 0) && ((legacy[2] & 1) != 0));
250if (!alwaysHardBIOSReset && isOwnershipConflict) {
251DBG("\tEHCI - Ownership conflict - attempting soft reset ...\n");
252DBG("\tEHCI - toggle OS Ownership to 0\n");
253pci_config_write8(pci_dev->dev.addr, eecp + 3, 0);
254for (k = 0; k < 25; k++) {
255for (j = 0; j < 8; j++) {
256legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
257}
258if (legacy[3] == 0) {
259break;
260}
261delay(10);
262}
263}
264
265DBG("\tFound USBLEGSUP_ID - value %x:%x - writing OSOwned\n", legacy[3],legacy[2]);
266pci_config_write8(pci_dev->dev.addr, eecp + 3, 1);
267
268// wait for kEHCI_USBLEGSUP_BIOSOwned bit to clear
269for (k = 0; k < 25; k++) {
270for (j = 0;j < 8; j++) {
271legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
272}
273DBG ("\t%x:%x,",legacy[3],legacy[2]);
274if (legacy[2] == 0) {
275break;
276}
277delay(10);
278}
279
280for (j = 0;j < 8; j++) {
281legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
282}
283isOwnershipConflict = ((legacy[2]) != 0);
284if (isOwnershipConflict) {
285// Soft reset has failed. Assume SMI being ignored
286// Hard reset
287// Force Clear BIOS BIT
288DBG("\tEHCI - Ownership conflict - attempting hard reset ...\n");
289DBG ("\t%x:%x\n",legacy[3],legacy[2]);
290DBG("\tEHCI - Force BIOS Ownership to 0\n");
291
292pci_config_write8(pci_dev->dev.addr, eecp + 2, 0);
293for (k = 0; k < 25; k++) {
294for (j = 0; j < 8; j++) {
295legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
296}
297DBG ("\t%x:%x,",legacy[3],legacy[2]);
298
299if ((legacy[2]) == 0) {
300break;
301}
302delay(10);
303}
304// Disable further SMI events
305for (j = 4; j < 8; j++) {
306pci_config_write8(pci_dev->dev.addr, eecp + j, 0);
307}
308}
309
310for (j = 0; j < 8; j++) {
311legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
312}
313
314DBG ("\t%x:%x\n",legacy[3],legacy[2]);
315
316// Final Ownership Resolution Check...
317if (legacy[2] & 1) {
318DBG("\tEHCI controller unable to take control from BIOS\n");
319return 0;
320}
321
322DBG("\tEHCI Acquire OS Ownership done\n");
323return 1;
324}
325
326static int uhci_reset (pci_dt_t *pci_dev)
327{
328uint32_t base, port_base;
329
330base = pci_config_read32(pci_dev->dev.addr, 0x20);
331port_base = (base >> 5) & 0x07ff;
332
333verbose("\tUHCI controller [%04x:%04x] at %02x:%2x.%x base %x(%x)\n",
334pci_dev->vendor_id, pci_dev->device_id,
335pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func,
336port_base, base);
337
338pci_config_write16(pci_dev->dev.addr, 0xc0, 0x8f00);
339
340outw (port_base, 0x0002);
341delay(10);
342outw (port_base+4,0);
343delay(10);
344outw (port_base,0);
345return 1;
346}
347
348static int xhci_legacy_off(pci_dt_t *pci_dev)
349{
350uint32_t bar0, hccparams1, extendCap, value;
351int32_t timeOut;
352
353verbose("\tSetting Legacy USB Off on xHC [%04x:%04x] at %02x:%2x.%x\n",
354pci_dev->vendor_id, pci_dev->device_id,
355pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func);
356
357bar0 = pci_config_read32(pci_dev->dev.addr, 16);
358/*
359 * Check if memory bar
360 */
361if (bar0 & 1)
362{
363DBG("\t%s: BAR0 not a memory range\n", __FUNCTION__);
364return 0;
365}
366/*
367 * Check if outside 32-bit physical address space
368 */
369if (((bar0 & 6) == 4) &&
370pci_config_read32(pci_dev->dev.addr, 20))
371{
372DBG("\t%s: BAR0 outside 32-bit physical address space\n", __FUNCTION__);
373return 0;
374}
375bar0 &= ~15;
376
377hccparams1 = *(uint32_t const volatile*) (bar0 + 16);
378if (hccparams1 == ~0)
379{
380DBG("\t%s: hccparams1 invalid 0x%x\n", __FUNCTION__, hccparams1);
381return 0;
382}
383extendCap = (hccparams1 >> 14) & 0x3fffc;
384while (extendCap) {
385value = *(uint32_t const volatile*) (bar0 + extendCap);
386if (value == ~0)
387{
388break;
389}
390if ((value & 0xff) == 1) {
391#if DEBUG_USB
392verbose("\t%s: Found USBLEGSUP 0x%x, USBLEGCTLSTS 0x%x\n", __FUNCTION__,
393value, *(uint32_t const volatile*) (bar0 + extendCap + 4));
394#endif
395value |= (1 << 24);
396*(uint32_t volatile*) (bar0 + extendCap) = value;
397timeOut = 40;
398while (timeOut--)
399{
400delay(500);
401value = *(uint32_t const volatile*) (bar0 + extendCap);
402if (value == ~0)
403{
404timeOut = -1;
405break;
406}
407if ((value & 0x01010000) == 0x01000000)
408{
409timeOut = -1;/* Optional - always disable the SMI */
410break;
411}
412}
413#if DEBUG_USB
414verbose("\t%s: USBLEGSUP 0x%x, USBLEGCTLSTS 0x%x\n", __FUNCTION__,
415value, *(uint32_t const volatile*) (bar0 + extendCap + 4));
416#endif
417if (timeOut >= 0)
418{
419break;
420}
421/*
422 * Disable the SMI in USBLEGCTLSTS if BIOS doesn't respond
423 */
424value = *(uint32_t const volatile*) (bar0 + extendCap + 4);
425if (value == ~0)
426{
427break;
428}
429value &= 0x1f1fee;
430value |= 0xe0000000;
431*(uint32_t volatile*) (bar0 + extendCap + 4) = value;
432break;
433}
434if (!(value & 0xff00))
435break;
436extendCap += ((value >> 6) & 0x3fc);
437}
438verbose("\tXHCI Legacy Off Done\n");
439return 1;
440}
441

Archive Download this file

Revision: HEAD