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