Chameleon

Chameleon Svn Source Tree

Root/branches/slice/trunkM/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 "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
26struct pciList
27{
28pci_dt_t* pciDev;
29struct pciList* next;
30};
31
32struct pciList* usbList = NULL;
33
34int legacy_off (pci_dt_t *pci_dev);
35int ehci_acquire (pci_dt_t *pci_dev);
36int uhci_reset (pci_dt_t *pci_dev);
37int ohci_handsoff (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 = (struct pciList*)malloc(sizeof(struct pciList));
43current->next = usbList;
44current->pciDev = pci_dev;
45
46usbList = current;
47}
48
49// Loop through the list and call the apropriate patch function
50int usb_loop()
51{
52int retVal = 1;
53bool fix_ehci, fix_uhci, fix_usb, fix_legacy;
54fix_ehci = fix_uhci = fix_usb = fix_legacy = true;
55
56if (getBoolForKey(kUSBBusFix, &fix_usb, &bootInfo->bootConfig))
57{
58fix_ehci = fix_uhci = fix_legacy = fix_usb;// Disable all if none set
59}
60else
61{
62getBoolForKey(kEHCIacquire, &fix_ehci, &bootInfo->bootConfig);
63getBoolForKey(kUHCIreset, &fix_uhci, &bootInfo->bootConfig);
64getBoolForKey(kLegacyOff, &fix_legacy, &bootInfo->bootConfig);
65}
66
67struct pciList* current = usbList;
68
69while(current)
70{
71switch (pci_config_read8(current->pciDev->dev.addr, PCI_CLASS_PROG))
72{
73// EHCI
74case 0x20:
75 if(fix_legacy) retVal &= legacy_off(current->pciDev);
76 if(fix_ehci) retVal &= ehci_acquire(current->pciDev);
77
78break;
79// OHCI
80case 0x10: //
81if (fix_uhci) retVal &= ohci_handsoff(current->pciDev);
82
83break;
84
85
86// UHCI
87case 0x00:
88if (fix_uhci) retVal &= uhci_reset(current->pciDev);
89
90break;
91}
92
93current = current->next;
94}
95return retVal;
96}
97
98int 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
104uint32_tcapaddr, opaddr;
105uint8_teecp;
106uint32_tusbcmd, usbsts, usbintr;
107uint32_tusblegsup, usblegctlsts;
108
109int isOSowned;
110int isBIOSowned;
111
112verbose("Setting Legacy USB Off on controller [%04x:%04x] at %02x:%2x.%x\n",
113pci_dev->vendor_id, pci_dev->device_id,
114pci_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)
118capaddr = pci_config_read32(pci_dev->dev.addr, 0x10);
119
120// opaddr = Operational Registers = capaddr + offset (8bit CAPLENGTH in Capability Registers + offset 0)
121opaddr = capaddr + *((unsigned char*)(capaddr));
122
123// eecp = EHCI Extended Capabilities offset = capaddr HCCPARAMS bits 15:8
124eecp=*((unsigned char*)(capaddr + 9));
125
126DBG("capaddr=%x opaddr=%x eecp=%x\n", capaddr, opaddr, eecp);
127
128usbcmd = *((unsigned int*)(opaddr));// Command Register
129usbsts = *((unsigned int*)(opaddr + 4));// Status Register
130usbintr = *((unsigned int*)(opaddr + 8));// Interrupt Enable Register
131
132DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
133
134// read PCI Config 32bit USBLEGSUP (eecp+0)
135usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);
136
137// informational only
138isBIOSowned = !!((usblegsup) & (1 << (16)));
139isOSowned = !!((usblegsup) & (1 << (24)));
140
141// read PCI Config 32bit USBLEGCTLSTS (eecp+4)
142usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);
143
144DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
145
146// Reset registers to Legacy OFF
147DBG("Clearing USBLEGCTLSTS\n");
148pci_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.
152delay(100);
153
154usbcmd = *((unsigned int*)(opaddr));
155usbsts = *((unsigned int*)(opaddr + 4));
156usbintr = *((unsigned int*)(opaddr + 8));
157
158DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
159
160DBG("Clearing Registers\n");
161
162// clear registers to default
163usbcmd = (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
167pci_config_write32(pci_dev->dev.addr, eecp, 1);//usblegsup
168
169// get the results
170usbcmd = *((unsigned int*)(opaddr));
171usbsts = *((unsigned int*)(opaddr + 4));
172usbintr = *((unsigned int*)(opaddr + 8));
173
174DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
175
176// read 32bit USBLEGSUP (eecp+0)
177usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);
178
179// informational only
180isBIOSowned = !!((usblegsup) & (1 << (16)));
181isOSowned = !!((usblegsup) & (1 << (24)));
182
183// read 32bit USBLEGCTLSTS (eecp+4)
184usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);
185
186DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
187
188msglog("Legacy USB Off Done\n");
189return 1;
190}
191
192int ehci_acquire (pci_dt_t *pci_dev)
193{
194intj, k;
195uint32_tbase;
196uint8_teecp;
197uint8_tlegacy[8];
198boolisOwnershipConflict;
199boolalwaysHardBIOSReset;
200
201alwaysHardBIOSReset = false;
202if (!getBoolForKey(kEHCIhard, &alwaysHardBIOSReset, &bootInfo->bootConfig)) {
203alwaysHardBIOSReset = true;
204}
205
206pci_config_write16(pci_dev->dev.addr, 0x04, 0x0002);
207base = pci_config_read32(pci_dev->dev.addr, 0x10);
208
209msglog("EHCI controller [%04x:%04x] at %02x:%2x.%x DMA @%x\n",
210pci_dev->vendor_id, pci_dev->device_id,
211pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func,
212base);
213
214if (*((unsigned char*)base) < 0xc)
215{
216DBG("Config space too small: no legacy implementation\n");
217return 1;
218}
219eecp = *((unsigned char*)(base + 9));
220if (!eecp) {
221DBG("No extended capabilities: no legacy implementation\n");
222return 1;
223}
224
225DBG("eecp=%x\n",eecp);
226
227// bad way to do it
228// pci_conf_write(pci_dev->dev.addr, eecp, 4, 0x01000001);
229for (j = 0; j < 8; j++) {
230legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
231DBG("%02x ", legacy[j]);
232}
233DBG("\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
239isOwnershipConflict = ((legacy[3] & 1 != 0) && (legacy[2] & 1 != 0));
240if (!alwaysHardBIOSReset && isOwnershipConflict) {
241DBG("EHCI - Ownership conflict - attempting soft reset ...\n");
242DBG("EHCI - toggle OS Ownership to 0\n");
243pci_config_write8(pci_dev->dev.addr, eecp + 3, 0);
244for (k = 0; k < 25; k++) {
245for (j = 0; j < 8; j++) {
246legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
247}
248if (legacy[3] == 0) {
249break;
250}
251delay(10);
252}
253}
254
255DBG("Found USBLEGSUP_ID - value %x:%x - writing OSOwned\n", legacy[3],legacy[2]);
256pci_config_write8(pci_dev->dev.addr, eecp + 3, 1);
257
258// wait for kEHCI_USBLEGSUP_BIOSOwned bit to clear
259for (k = 0; k < 25; k++) {
260for (j = 0;j < 8; j++) {
261legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
262}
263DBG ("%x:%x,",legacy[3],legacy[2]);
264if (legacy[2] == 0) {
265break;
266}
267delay(10);
268}
269
270for (j = 0;j < 8; j++) {
271legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
272}
273isOwnershipConflict = ((legacy[2]) != 0);
274if (isOwnershipConflict) {
275// Soft reset has failed. Assume SMI being ignored
276// Hard reset
277// Force Clear BIOS BIT
278DBG("EHCI - Ownership conflict - attempting hard reset ...\n");
279DBG ("%x:%x\n",legacy[3],legacy[2]);
280DBG("EHCI - Force BIOS Ownership to 0\n");
281
282pci_config_write8(pci_dev->dev.addr, eecp + 2, 0);
283for (k = 0; k < 25; k++) {
284for (j = 0; j < 8; j++) {
285legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
286}
287DBG ("%x:%x,",legacy[3],legacy[2]);
288
289if ((legacy[2]) == 0) {
290break;
291}
292delay(10);
293}
294// Disable further SMI events
295for (j = 4; j < 8; j++) {
296pci_config_write8(pci_dev->dev.addr, eecp + j, 0);
297}
298}
299
300for (j = 0; j < 8; j++) {
301legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
302}
303
304DBG ("%x:%x\n",legacy[3],legacy[2]);
305
306// Final Ownership Resolution Check...
307if (legacy[2] & 1) {
308DBG("EHCI controller unable to take control from BIOS\n");
309return 0;
310}
311
312msglog("EHCI Acquire OS Ownership done\n");
313return 1;
314}
315
316int uhci_reset (pci_dt_t *pci_dev)
317{
318uint32_t base, port_base;
319
320base = pci_config_read32(pci_dev->dev.addr, 0x20);
321port_base = (base >> 5) & 0x07ff;
322
323msglog("UHCI controller [%04x:%04x] at %02x:%2x.%x base %x(%x) reset\n",
324pci_dev->vendor_id, pci_dev->device_id,
325pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func,
326port_base, base);
327
328pci_config_write16(pci_dev->dev.addr, 0xc0, 0x8f00);
329
330outw (port_base, 0x0002);
331delay(10);
332outw (port_base+4,0);
333delay(10);
334outw (port_base,0);
335return 1;
336}
337
338int ohci_handsoff (pci_dt_t *pci_dev)
339{
340uint32_t base, control;
341#define OHCI_CTRL_MASK(1 << 9)
342#define OHCI_CONTROL0x04
343#define OHCI_INTRDISABLE0x14
344#define OHCI_INTRSTATUS0x0c
345
346base = pci_config_read32(pci_dev->dev.addr, 0x10);
347msglog("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 */
367control = *(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 */
376return 1;
377}
378

Archive Download this file

Revision: 1171