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 | int ehci_acquire (pci_dt_t *pci_dev)␊ |
26 | {␊ |
27 | ␉int␉␉j, k;␊ |
28 | ␉uint32_t␉base;␊ |
29 | ␉uint8_t␉␉eecp;␊ |
30 | ␉uint8_t␉␉legacy[8];␊ |
31 | ␉bool␉␉isOwnershipConflict;␉␊ |
32 | ␉bool␉␉alwaysHardBIOSReset;␊ |
33 | ␊ |
34 | ␉alwaysHardBIOSReset = false;␉␊ |
35 | ␉if (!getBoolForKey(kEHCIhard, &alwaysHardBIOSReset, &bootInfo->bootConfig)) {␊ |
36 | ␉␉alwaysHardBIOSReset = true;␊ |
37 | ␉}␊ |
38 | ␊ |
39 | ␉pci_config_write16(pci_dev->dev.addr, 0x04, 0x0002);␊ |
40 | ␉base = pci_config_read32(pci_dev->dev.addr, 0x10);␊ |
41 | ␊ |
42 | ␉verbose("EHCI controller [%04x:%04x] at %02x:%2x.%x DMA @%x\n", ␊ |
43 | ␉␉pci_dev->vendor_id, pci_dev->device_id,␊ |
44 | ␉␉pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func, ␊ |
45 | ␉␉base);␊ |
46 | ␊ |
47 | ␉if (*((unsigned char*)base) < 0xc)␊ |
48 | ␉{␊ |
49 | ␉␉DBG("Config space too small: no legacy implementation\n");␊ |
50 | ␉␉return 1;␊ |
51 | ␉}␊ |
52 | ␉eecp = *((unsigned char*)(base + 9));␊ |
53 | ␉if (!eecp) {␊ |
54 | ␉␉DBG("No extended capabilities: no legacy implementation\n");␊ |
55 | ␉␉return 1;␊ |
56 | ␉}␊ |
57 | ␊ |
58 | ␉DBG("eecp=%x\n",eecp);␊ |
59 | ␊ |
60 | ␉// bad way to do it␊ |
61 | ␉// pci_conf_write(pci_dev->dev.addr, eecp, 4, 0x01000001);␊ |
62 | ␉for (j = 0; j < 8; j++) {␊ |
63 | ␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
64 | ␉␉DBG("%02x ", legacy[j]);␊ |
65 | ␉}␊ |
66 | ␉DBG("\n");␊ |
67 | ␊ |
68 | ␉//Real Job: based on orByte's AppleUSBEHCI.cpp␊ |
69 | ␉//We try soft reset first - some systems hang on reboot with hard reset␊ |
70 | ␉// Definitely needed during reboot on 10.4.6␊ |
71 | ␊ |
72 | ␉isOwnershipConflict = ((legacy[3] & 1 != 0) && (legacy[2] & 1 != 0));␊ |
73 | ␉if (!alwaysHardBIOSReset && isOwnershipConflict) {␊ |
74 | ␉␉DBG("EHCI - Ownership conflict - attempting soft reset ...\n");␊ |
75 | ␉␉DBG("EHCI - toggle OS Ownership to 0\n");␊ |
76 | ␉␉pci_config_write8(pci_dev->dev.addr, eecp + 3, 0);␊ |
77 | ␉␉for (k = 0; k < 25; k++) {␊ |
78 | ␉␉␉for (j = 0; j < 8; j++) {␊ |
79 | ␉␉␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
80 | ␉␉␉}␊ |
81 | ␉␉␉if (legacy[3] == 0) {␊ |
82 | ␉␉␉␉break;␊ |
83 | ␉␉␉}␊ |
84 | ␉␉␉delay(10);␊ |
85 | ␉␉}␊ |
86 | ␉}␉␊ |
87 | ␊ |
88 | ␉DBG("Found USBLEGSUP_ID - value %x:%x - writing OSOwned\n", legacy[3],legacy[2]);␊ |
89 | ␉pci_config_write8(pci_dev->dev.addr, eecp + 3, 1);␊ |
90 | ␊ |
91 | ␉// wait for kEHCI_USBLEGSUP_BIOSOwned bit to clear␊ |
92 | ␉for (k = 0; k < 25; k++) {␊ |
93 | ␉␉for (j = 0;j < 8; j++) {␊ |
94 | ␉␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
95 | ␉␉}␊ |
96 | ␉␉DBG ("%x:%x,",legacy[3],legacy[2]);␊ |
97 | ␉␉if (legacy[2] == 0) {␊ |
98 | ␉␉␉break;␊ |
99 | ␉␉}␊ |
100 | ␉␉delay(10);␊ |
101 | ␉}␊ |
102 | ␊ |
103 | ␉for (j = 0;j < 8; j++) {␊ |
104 | ␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
105 | ␉}␊ |
106 | ␉isOwnershipConflict = ((legacy[2]) != 0);␊ |
107 | ␉if (isOwnershipConflict) {␊ |
108 | ␉␉// Soft reset has failed. Assume SMI being ignored␊ |
109 | ␉␉// Hard reset␊ |
110 | ␉␉// Force Clear BIOS BIT␊ |
111 | ␉␉DBG("EHCI - Ownership conflict - attempting hard reset ...\n");␉␉␉␊ |
112 | ␉␉DBG ("%x:%x\n",legacy[3],legacy[2]);␊ |
113 | ␉␉DBG("EHCI - Force BIOS Ownership to 0\n");␊ |
114 | ␊ |
115 | ␉␉pci_config_write8(pci_dev->dev.addr, eecp + 2, 0);␊ |
116 | ␉␉for (k = 0; k < 25; k++) {␊ |
117 | ␉␉␉for (j = 0; j < 8; j++) {␊ |
118 | ␉␉␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
119 | ␉␉␉}␊ |
120 | ␉␉␉DBG ("%x:%x,",legacy[3],legacy[2]);␊ |
121 | ␊ |
122 | ␉␉␉if ((legacy[2]) == 0) {␊ |
123 | ␉␉␉␉break;␊ |
124 | ␉␉␉}␊ |
125 | ␉␉␉delay(10);␉␊ |
126 | ␉␉}␉␉␊ |
127 | ␉␉// Disable further SMI events␊ |
128 | ␉␉for (j = 4; j < 8; j++) {␊ |
129 | ␉␉␉pci_config_write8(pci_dev->dev.addr, eecp + j, 0);␊ |
130 | ␉␉}␊ |
131 | ␉}␊ |
132 | ␊ |
133 | ␉for (j = 0; j < 8; j++) {␊ |
134 | ␉␉legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);␊ |
135 | ␉}␊ |
136 | ␊ |
137 | ␉DBG ("%x:%x\n",legacy[3],legacy[2]);␊ |
138 | ␊ |
139 | ␉// Final Ownership Resolution Check...␊ |
140 | ␉if (legacy[2] & 1) {␉␉␉␉␉␊ |
141 | ␉␉DBG("EHCI controller unable to take control from BIOS\n");␊ |
142 | ␉␉return 0;␊ |
143 | ␉}␊ |
144 | ␊ |
145 | ␉DBG("EHCI Acquire OS Ownership done\n");␉␊ |
146 | ␉return 1;␊ |
147 | }␊ |
148 | ␊ |
149 | int legacy_off (pci_dt_t *pci_dev)␊ |
150 | {␊ |
151 | ␉// Set usb legacy off modification by Signal64␊ |
152 | ␉uint32_t␉capaddr, opaddr; ␉␉␊ |
153 | ␉uint8_t␉␉eecp;␉␉␉␊ |
154 | ␉uint32_t␉usbcmd, usbsts, usbintr;␉␉␉␊ |
155 | ␉uint32_t␉usblegsup, usblegctlsts;␉␉␊ |
156 | ␉␊ |
157 | ␉int isOSowned;␊ |
158 | ␉int isBIOSowned;␊ |
159 | ␉␊ |
160 | ␉verbose("Setting Legacy USB Off on controller [%04x:%04x] at %02x:%2x.%x\n", ␊ |
161 | ␉␉␉pci_dev->vendor_id, pci_dev->device_id,␊ |
162 | ␉␉␉pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func);␊ |
163 | ␉␊ |
164 | ␉// capaddr = Capability Registers = dev.addr + offset stored in dev.addr + 0x10 (USBBASE)␊ |
165 | ␉capaddr = pci_config_read32(pci_dev->dev.addr, 0x10);␉␊ |
166 | ␉␊ |
167 | ␉// opaddr = Operational Registers = capaddr + offset (8bit CAPLENGTH in Capability Registers + offset 0)␊ |
168 | ␉opaddr = capaddr + *((unsigned char*)(capaddr)); ␉␉␊ |
169 | ␉␊ |
170 | ␉// eecp = EHCI Extended Capabilities offset = capaddr HCCPARAMS bits 15:8␊ |
171 | ␉eecp=*((unsigned char*)(capaddr + 9));␊ |
172 | ␉␊ |
173 | ␉DBG("capaddr=%x opaddr=%x eecp=%x\n", capaddr, opaddr, eecp);␊ |
174 | ␉␊ |
175 | ␉usbcmd = *((unsigned int*)(opaddr));␉␉␉// Command Register␊ |
176 | ␉usbsts = *((unsigned int*)(opaddr + 4));␉␉// Status Register␊ |
177 | ␉usbintr = *((unsigned int*)(opaddr + 8));␉␉// Interrupt Enable Register␊ |
178 | ␉␊ |
179 | ␉DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);␊ |
180 | ␉␊ |
181 | ␉// read PCI Config 32bit USBLEGSUP (eecp+0) ␊ |
182 | ␉usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);␊ |
183 | ␉␊ |
184 | ␉// informational only␊ |
185 | ␉isBIOSowned = !!((usblegsup) & (1 << (16)));␊ |
186 | ␉isOSowned = !!((usblegsup) & (1 << (24)));␊ |
187 | ␉␊ |
188 | ␉// read PCI Config 32bit USBLEGCTLSTS (eecp+4) ␊ |
189 | ␉usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);␊ |
190 | ␉␊ |
191 | ␉DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);␊ |
192 | ␉␊ |
193 | ␉// Reset registers to Legacy OFF␊ |
194 | ␉DBG("Clearing USBLEGCTLSTS\n");␊ |
195 | ␉pci_config_write32(pci_dev->dev.addr, eecp + 4, 0);␉//usblegctlsts␊ |
196 | ␉␊ |
197 | ␉// if delay value is in milliseconds it doesn't appear to work. ␊ |
198 | ␉// setting value to anything up to 65535 does not add the expected delay here.␊ |
199 | ␉delay(100);␊ |
200 | ␉␊ |
201 | ␉usbcmd = *((unsigned int*)(opaddr));␊ |
202 | ␉usbsts = *((unsigned int*)(opaddr + 4));␊ |
203 | ␉usbintr = *((unsigned int*)(opaddr + 8));␊ |
204 | ␉␊ |
205 | ␉DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);␊ |
206 | ␉␊ |
207 | ␉DBG("Clearing Registers\n");␊ |
208 | ␉␊ |
209 | ␉// clear registers to default␊ |
210 | ␉usbcmd = (usbcmd & 0xffffff00);␊ |
211 | ␉*((unsigned int*)(opaddr)) = usbcmd;␊ |
212 | ␉*((unsigned int*)(opaddr + 8)) = 0;␉␉␉␉␉//usbintr - clear interrupt registers␊ |
213 | ␉*((unsigned int*)(opaddr + 4)) = 0x1000;␉␉␉//usbsts - clear status registers ␉␊ |
214 | ␉pci_config_write32(pci_dev->dev.addr, eecp, 1);␉␉//usblegsup␊ |
215 | ␉␊ |
216 | ␉// get the results␊ |
217 | ␉usbcmd = *((unsigned int*)(opaddr));␊ |
218 | ␉usbsts = *((unsigned int*)(opaddr + 4));␊ |
219 | ␉usbintr = *((unsigned int*)(opaddr + 8));␊ |
220 | ␉␊ |
221 | ␉DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);␊ |
222 | ␉␊ |
223 | ␉// read 32bit USBLEGSUP (eecp+0) ␊ |
224 | ␉usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);␊ |
225 | ␉␊ |
226 | ␉// informational only␊ |
227 | ␉isBIOSowned = !!((usblegsup) & (1 << (16)));␊ |
228 | ␉isOSowned = !!((usblegsup) & (1 << (24)));␊ |
229 | ␉␊ |
230 | ␉// read 32bit USBLEGCTLSTS (eecp+4) ␊ |
231 | ␉usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);␊ |
232 | ␉␊ |
233 | ␉DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);␊ |
234 | ␉␊ |
235 | ␉verbose("Legacy USB Off Done\n");␉␊ |
236 | ␉return 1;␊ |
237 | }␊ |
238 | ␊ |
239 | int uhci_reset (pci_dt_t *pci_dev)␊ |
240 | {␊ |
241 | ␉uint32_t base, port_base;␊ |
242 | ␉␊ |
243 | ␉base = pci_config_read32(pci_dev->dev.addr, 0x20);␊ |
244 | ␉port_base = (base >> 5) & 0x07ff;␊ |
245 | ␊ |
246 | ␉verbose("UHCI controller [%04x:%04x] at %02x:%2x.%x base %x(%x)\n", ␊ |
247 | ␉␉pci_dev->vendor_id, pci_dev->device_id,␊ |
248 | ␉␉pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func, ␊ |
249 | ␉␉port_base, base);␊ |
250 | ␉␊ |
251 | ␉pci_config_write16(pci_dev->dev.addr, 0xc0, 0x8f00);␊ |
252 | ␊ |
253 | ␉outw (port_base, 0x0002);␊ |
254 | ␉delay(10);␊ |
255 | ␉outw (port_base+4,0);␊ |
256 | ␉delay(10);␊ |
257 | ␉outw (port_base,0);␊ |
258 | ␉return 1;␊ |
259 | }␊ |
260 | |