+
+ for( i = 0; i < MAX_CONTROLLERS && gOHCI_Controllers[i].ControlSpacePhys; i ++ )
+ {
+ // TODO: Clean up resources used
+ }
+}
+
+void OHCI_InitialiseController(tOHCI_Controller *Controller)
+{
+ tOHCI_Controller *cnt = Controller;
+ volatile struct sRegisters *iospace;
+
+ Log_Debug("OHCI", "Card #%i at 0x%X, IRQ %i",
+ cnt->ID, cnt->ControlSpacePhys, cnt->IRQNum);
+
+ // - Prepare mappings
+ cnt->ControlSpace = (void*)MM_MapHWPages(cnt->ControlSpacePhys, 1);
+ IRQ_AddHandler( cnt->IRQNum, OHCI_InterruptHandler, cnt);
+ iospace = cnt->ControlSpace;
+
+ Log_Debug("OHCI", "Card #%i version is 0x%x", cnt->ID, iospace->HcRevision);
+
+ // Check who had control
+ if( iospace->HcControl & (1 << 8) ) // InterruptRouting
+ {
+ LOG("USB was in the hands of SMM, asking for it back");
+ // SMM has control, ask for it back
+ // - Write '1' to OwnershipChangeRequest
+ // - Wait for InterruptRouting to clear
+ // TODO: Timeout
+ while( iospace->HcControl & (1 << 8) ) ;
+ LOG("Obtained USB");
+ }
+ else if( (iospace->HcControl & 0xC0) != 0x00 ) // UsbReset
+ {
+ LOG("USB was in the hands of the BIOS");
+ // BIOS had control, check for Operational
+ if( (iospace->HcControl & 0xC0) != 0x80 ) // UsbOperational
+ {
+ // - If not, set to resume
+ iospace->HcControl &= ~0xC0; // UsbResume
+ iospace->HcControl |= 0x40; // UsbResume
+ // TODO: Wait
+ }
+ }
+ else
+ {
+ // Cold boot, wait a bit
+ // TODO: Wait for reset time
+ }
+
+ // Allocate HCCA area
+ cnt->HCCA = (void*)MM_AllocDMA(1, 32, &cnt->HCCAPhys);
+ if( !cnt->HCCA ) {
+ Log_Error("OHCI", "Unable to allocate HCCA (1 page, 32-bit)");
+ return ;
+ }
+ // TODO: Check return value
+ // - HCCA is 256 bytes in length, but I like alignment
+ cnt->IntLists = (void*)( (tVAddr)cnt->HCCA + 512 );
+ LOG("HCCAPhys = %P, HCCA = %p", cnt->HCCAPhys, cnt->HCCA);
+
+ // --- Restart the controller ---
+ Uint32 fm_interval = iospace->HcFmInterval;
+ iospace->HcCommandStatus |= (1 << 0);
+ // - Wait 10 micro-seconds
+ // TODO:
+ iospace->HcFmInterval = fm_interval;
+ // (Now in UsbSuspend state)
+ // - Wait 2ms
+ // TODO:
+ // - Check that things are good?
+
+ // --- Initialise Virtual Queues ---
+ memset(cnt->IntLists, 0, sizeof(*cnt->IntLists));
+ // Set next pointers into a binary tree
+ {
+ tPAddr next_lvl = cnt->HCCAPhys + 512;
+ next_lvl += 16 * sizeof(tOHCI_Endpoint);
+ for( int i = 0; i < 16; i ++ )
+ cnt->IntLists->Period16[i].NextED = next_lvl + i/2 * sizeof(tOHCI_Endpoint);
+ next_lvl += 8 * sizeof(tOHCI_Endpoint);
+ for( int i = 0; i < 8; i ++ )
+ cnt->IntLists->Period8[i].NextED = next_lvl + i/2 * sizeof(tOHCI_Endpoint);
+ next_lvl += 4 * sizeof(tOHCI_Endpoint);
+ for( int i = 0; i < 4; i ++ )
+ cnt->IntLists->Period4[i].NextED = next_lvl + i/2 * sizeof(tOHCI_Endpoint);
+ next_lvl += 2 * sizeof(tOHCI_Endpoint);
+ for( int i = 0; i < 2; i ++ )
+ cnt->IntLists->Period2[i].NextED = next_lvl + sizeof(tOHCI_Endpoint);
+ next_lvl += 1 * sizeof(tOHCI_Endpoint);
+ cnt->IntLists->Period1[0].NextED = next_lvl;
+ }
+ // Set all endpoints to be skipped
+ for( int i = 0; i < 32; i ++ )
+ {
+ tOHCI_Endpoint *ep = &cnt->IntLists->Period16[i];
+ ep->Flags |= (1 << 14); // Skip
+ }
+
+ // --- Initialise HCCA ---
+ // - Interrupt table is set up to point to each
+ for( int i = 0; i < 32; i ++ )
+ {
+ static const int _balance[] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
+ cnt->HCCA->HccaInterruptTable[i] = cnt->HCCAPhys + 512 + _balance[i&15] * sizeof(tOHCI_Endpoint);
+ }
+ cnt->HCCA->HccaFrameNumber = 0;
+ cnt->HCCA->HccaDoneHead = 0;
+
+ // --- Initialise Registers
+ iospace->HcControlHeadED = MM_GetPhysAddr( (tVAddr)&cnt->IntLists->StopED );
+ iospace->HcBulkHeadED = MM_GetPhysAddr( (tVAddr)&cnt->IntLists->StopED );
+ iospace->HcHCCA = cnt->HCCAPhys;
+ iospace->HcInterruptEnable = 0x7B; // 0111 1011 (RHSC, FNO, UE, RD, WDH, SO)
+ iospace->HcInterruptDisable = 0x4; // Disable SOF interrupts
+ iospace->HcControl = 0x3C; // All queues on
+ iospace->HcPeriodicStart = fm_interval / 10 * 9; // 90% of fm_interval
+
+ // --- Start
+ iospace->HcControl |= (2 << 6); // UsbOperational
+
+ LOG("HcRhDescriptorA = 0x%x", iospace->HcRhDescriptorA);
+ LOG("HcRhDescriptorB = 0x%x", iospace->HcRhDescriptorB);
+
+ // --- Tell USB core that this controller is avaliable
+ cnt->nPorts = iospace->HcRhDescriptorA & 0x7F;
+ if( cnt->nPorts > 15 ) {
+ // Oops?
+ Log_Warning("OHCI", "Controller reports %i ports, but spec only allows 15, capping", cnt->nPorts);
+ cnt->nPorts = 15;
+ }
+ cnt->RootHub = USB_RegisterHost(&gOHCI_HostDef, cnt, cnt->nPorts);