Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2006