Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2537