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

Archive Download this file

Revision: 1468