Kernel - Reenabled locking on debug output
[tpg/acess2.git] / KernelLand / Modules / USB / OHCI / ohci.c
1 /*
2  * Acess2 USB Stack - OHCI Driver
3  * - By John Hodge (thePowersGang)
4  *
5  * ohci.c
6  * - Open Host Controller Interface driver
7  */
8 #define DEBUG   1
9 #define VERSION VER2(0,1)
10 #include <usb_host.h>
11 #include "ohci.h"
12 #include <modules.h>
13 #include <drv_pci.h>
14 #include <timers.h>
15
16 // === CONSTANTS ===
17 #define MAX_CONTROLLERS 4
18 #define MAX_TD_PAGES    2
19 #define MAX_ENDPT_PAGES 2
20 #define MAX_PACKET_SIZE 1023    // TODO: Check what should be used
21
22 // === PROTOTYPES ===
23  int    OHCI_Initialise(char **Arguments);
24 void    OHCI_Cleanup(void);
25
26 void    OHCI_InitialiseController(tOHCI_Controller *Controller);
27
28 void    *OHCI_int_AddTD(tOHCI_Controller *Controller, int Dest, int DataTgl, int Type, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
29 void    *OHCI_DataIN(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
30 void    *OHCI_DataOUT(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
31 void    *OHCI_SendSETUP(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
32  int    OHCI_IsTransferComplete(void *Ptr, void *Handle);
33
34 void    *OHCI_StartPoll(void *Ptr, int Dest, int MaxPeriod, tUSBHostCb Cb, void *CbData, void *DataBuf, size_t Length);
35 void    OHCI_StopPoll(void *Ptr, void *Hdl);
36 void    OHCI_CheckPortUpdate(void *Ptr);
37 void    OHCI_InterruptHandler(int IRQ, void *Ptr);
38
39 tOHCI_Endpoint  *OHCI_int_AllocateEndpt(tOHCI_Controller *Controller, int Dest);
40 tOHCI_Endpoint  *OHCI_int_GetEndptFromPhys(tPAddr PhysAddr);
41 tOHCI_GeneralTD *OHCI_int_AllocateGTD(tOHCI_Controller *Controller);
42 tOHCI_GeneralTD *OHCI_int_GetGTDFromPhys(tPAddr PhysAddr);
43
44 // === GLOBALS ===
45 MODULE_DEFINE(0, VERSION, USB_OHCI, OHCI_Initialise, OHCI_Cleanup, "USB_Core", NULL);
46 tUSBHostDef     gOHCI_HostDef = {
47         .SendIN = OHCI_DataIN,
48         .SendOUT = OHCI_DataOUT,
49         .SendSETUP = OHCI_SendSETUP,
50         .IsOpComplete = OHCI_IsTransferComplete,
51
52 //      .StartPolling = OHCI_StartPoll,
53 //      .StopPolling = OHCI_StopPoll,
54
55         .CheckPorts = OHCI_CheckPortUpdate
56         };
57 tOHCI_Controller        gOHCI_Controllers[MAX_CONTROLLERS];
58 tOHCI_GeneralTD *gapOHCI_TDPool[MAX_TD_PAGES];  //!< Virtual pointers to TDs, each has an index in avail bits
59 const int       ciTDs_per_page = PAGE_SIZE/sizeof(tOHCI_GeneralTD);
60 tOHCI_Endpoint  *gapOHCI_EndpointPool[MAX_ENDPT_PAGES];
61 const int       ciEndpoints_per_page = PAGE_SIZE/sizeof(tOHCI_Endpoint);
62
63 // === CODE ===
64 int OHCI_Initialise(char **Arguments)
65 {
66          int    id = -1;
67          int    card_num = 0;
68         
69         while( (id = PCI_GetDeviceByClass(0x0C0310, 0xFFFFFF, id)) != -1 && card_num < MAX_CONTROLLERS )
70         {
71                 tOHCI_Controller        *ctrlr = &gOHCI_Controllers[card_num];
72
73                 ctrlr->ID = card_num;   
74                 ctrlr->PciId = id;      
75                 ctrlr->IRQNum = PCI_GetIRQ(id);
76                 ctrlr->ControlSpacePhys = PCI_GetBAR(id, 0);    // Offset 0x10
77                 PCI_ConfigWrite( id, 4, 2, 0x0006 );    // Enable memory and bus master
78
79                 if( ctrlr->ControlSpacePhys == 0 || ctrlr->ControlSpacePhys & 1 ) {
80                         ctrlr->ControlSpacePhys = 0;
81                         continue ;
82                 }
83
84                 OHCI_InitialiseController( ctrlr );
85         
86                 card_num ++;
87         }
88
89         if( id != -1 ) {
90                 Log_Warning("OHCI", "Too many controllers (> %i) were detected, ignoring the rest",
91                         MAX_CONTROLLERS);
92         }
93         
94         return 0;
95 }
96
97 void OHCI_Cleanup(void)
98 {
99          int    i;
100         // TODO: Cleanup for unload
101         
102         for( i = 0; i < MAX_CONTROLLERS && gOHCI_Controllers[i].ControlSpacePhys; i ++ )
103         {
104                 // TODO: Clean up resources used
105         }
106 }
107
108 void OHCI_InitialiseController(tOHCI_Controller *Controller)
109 {
110         tOHCI_Controller        *cnt = Controller;
111         volatile struct sRegisters      *iospace;
112         
113         Log_Debug("OHCI", "Card #%i at 0x%X, IRQ %i",
114                 cnt->ID, cnt->ControlSpacePhys, cnt->IRQNum);
115         
116         // - Prepare mappings
117         cnt->ControlSpace = (void*)MM_MapHWPages(cnt->ControlSpacePhys, 1);
118         IRQ_AddHandler( cnt->IRQNum, OHCI_InterruptHandler, cnt);
119         iospace = cnt->ControlSpace;
120         
121         Log_Debug("OHCI", "Card #%i version is 0x%x", cnt->ID, iospace->HcRevision);
122
123         // Check who had control
124         if( iospace->HcControl & (1 << 8) )     // InterruptRouting
125         {
126                 LOG("USB was in the hands of SMM, asking for it back");
127                 // SMM has control, ask for it back
128                 // - Write '1' to OwnershipChangeRequest
129                 // - Wait for InterruptRouting to clear
130                 // TODO: Timeout
131                 while( iospace->HcControl & (1 << 8) ) ;
132                 LOG("Obtained USB");
133         }
134         else if( (iospace->HcControl & 0xC0) != 0x00 )  // UsbReset
135         {
136                 LOG("USB was in the hands of the BIOS");
137                 // BIOS had control, check for Operational
138                 if( (iospace->HcControl & 0xC0) != 0x80 )       // UsbOperational
139                 {
140                         // - If not, set to resume
141                         iospace->HcControl &= ~0xC0;    // UsbResume
142                         iospace->HcControl |= 0x40;     // UsbResume
143                         // TODO: Wait
144                 }
145         }
146         else
147         {       
148                 // Cold boot, wait a bit
149                 // TODO: Wait for reset time
150         }
151
152         // Allocate HCCA area   
153         cnt->HCCA = (void*)MM_AllocDMA(1, 32, &cnt->HCCAPhys);
154         if( !cnt->HCCA ) {
155                 Log_Error("OHCI", "Unable to allocate HCCA (1 page, 32-bit)");
156                 return ;
157         }
158         // TODO: Check return value
159         // - HCCA is 256 bytes in length, but I like alignment
160         cnt->IntLists = (void*)( (tVAddr)cnt->HCCA + 512 );
161         LOG("HCCAPhys = %P, HCCA = %p", cnt->HCCAPhys, cnt->HCCA);
162         
163         // --- Restart the controller ---
164         Uint32  fm_interval = iospace->HcFmInterval;
165         iospace->HcCommandStatus |= (1 << 0);
166         // - Wait 10 micro-seconds
167         // TODO: 
168         iospace->HcFmInterval = fm_interval;
169         // (Now in UsbSuspend state)
170         // - Wait 2ms
171         // TODO:
172         // - Check that things are good?
173
174         // --- Initialise Virtual Queues ---
175         memset(cnt->IntLists, 0, sizeof(*cnt->IntLists));
176         // Set next pointers into a binary tree
177         {
178                 tPAddr  next_lvl = cnt->HCCAPhys + 512;
179                 next_lvl += 16 * sizeof(tOHCI_Endpoint);
180                 for( int i = 0; i < 16; i ++ )
181                         cnt->IntLists->Period16[i].NextED = next_lvl + i/2 * sizeof(tOHCI_Endpoint);
182                 next_lvl += 8 * sizeof(tOHCI_Endpoint);
183                 for( int i = 0; i < 8; i ++ )
184                         cnt->IntLists->Period8[i].NextED = next_lvl + i/2 * sizeof(tOHCI_Endpoint);
185                 next_lvl += 4 * sizeof(tOHCI_Endpoint);
186                 for( int i = 0; i < 4; i ++ )
187                         cnt->IntLists->Period4[i].NextED = next_lvl + i/2 * sizeof(tOHCI_Endpoint);
188                 next_lvl += 2 * sizeof(tOHCI_Endpoint);
189                 for( int i = 0; i < 2; i ++ )
190                         cnt->IntLists->Period2[i].NextED = next_lvl + sizeof(tOHCI_Endpoint);
191                 next_lvl += 1 * sizeof(tOHCI_Endpoint);
192                 cnt->IntLists->Period1[0].NextED = next_lvl;
193         }
194         // Set all endpoints to be skipped
195         for( int i = 0; i < 32; i ++ )
196         {
197                 tOHCI_Endpoint  *ep = &cnt->IntLists->Period16[i];
198                 ep->Flags |= (1 << 14); // Skip
199         }
200
201         // --- Initialise HCCA ---
202         // - Interrupt table is set up to point to each 
203         for( int i = 0; i < 32; i ++ )
204         {
205                 static const int _balance[] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
206                 cnt->HCCA->HccaInterruptTable[i] = cnt->HCCAPhys + 512 + _balance[i&15] * sizeof(tOHCI_Endpoint);
207         }
208         cnt->HCCA->HccaFrameNumber = 0;
209         cnt->HCCA->HccaDoneHead = 0;
210
211         // --- Initialise Registers
212         iospace->HcControlHeadED = MM_GetPhysAddr( (tVAddr)&cnt->IntLists->StopED );
213         iospace->HcBulkHeadED = MM_GetPhysAddr( (tVAddr)&cnt->IntLists->StopED );
214         iospace->HcHCCA = cnt->HCCAPhys;
215         iospace->HcInterruptEnable = 0x7B;      // 0111 1011 (RHSC, FNO, UE, RD, WDH, SO)
216         iospace->HcInterruptDisable = 0x4;      // Disable SOF interrupts
217         iospace->HcControl = 0x3C;      // All queues on
218         iospace->HcPeriodicStart = fm_interval / 10 * 9;        // 90% of fm_interval
219         
220         // --- Start
221         iospace->HcControl |= (2 << 6); // UsbOperational
222
223         LOG("HcRhDescriptorA = 0x%x", iospace->HcRhDescriptorA);
224         LOG("HcRhDescriptorB = 0x%x", iospace->HcRhDescriptorB);
225         
226         // --- Tell USB core that this controller is avaliable
227         cnt->nPorts = iospace->HcRhDescriptorA & 0x7F;
228         if( cnt->nPorts > 15 ) {
229                 // Oops?
230                 Log_Warning("OHCI", "Controller reports %i ports, but spec only allows 15, capping", cnt->nPorts);
231                 cnt->nPorts = 15;
232         }
233         cnt->RootHub = USB_RegisterHost(&gOHCI_HostDef, cnt, cnt->nPorts);
234 }
235
236 // --- USB IO Functions ---
237 void *OHCI_int_DoTD(
238         tOHCI_Controller *Controller, int Dest,
239         int DataTgl, int Type,
240         tUSBHostCb Cb, void *CbData,
241         void *Buf, size_t Length
242         )
243 {
244         tOHCI_GeneralTD *td;
245         tPAddr  td_phys;
246         tOHCI_Endpoint  *ep;
247
248         // --- Sanity check
249         if( Length > MAX_PACKET_SIZE )
250                 return NULL;
251         
252         ENTER("pController xDest iDataTgl iType pCb pCbData pBuf iLength",
253                 Controller, Dest, DataTgl, Type, Cb, CbData, Buf, Length);
254
255         // ---- Check that the packet resides within memory the controller can access
256         if( MM_GetPhysAddr((tVAddr)Buf) >= (1ULL << 32) || MM_GetPhysAddr((tVAddr)Buf + Length -1) >= (1ULL << 32) )
257         {
258                 // TODO: Handle destination outside of 32-bit address range
259                 Log_Warning("OHCI", "_int_DoTD - Buffer outside of 32-bit range, TODO: Handle this");
260                 LEAVE('n');
261                 return NULL;
262         }
263
264         // --- Find/Allocate Endpoint descriptor
265         ep = OHCI_int_AllocateEndpt(Controller, Dest);
266         if( !ep ) {
267                 Log_Warning("OHCI", "_int_DoTD - Unable to find/allocate Endpoint 0x%x... oops", Dest);
268                 LEAVE('n');
269                 return NULL;
270         }
271         
272         // --- Allocate a TD
273         td = OHCI_int_AllocateGTD(Controller);
274         if( !td ) {
275                 Log_Warning("OHCI", "_int_DoTD - Unable to allocate TD... oops");
276                 LEAVE('n');
277                 return NULL;
278         }
279         td_phys = MM_GetPhysAddr( (tVAddr)td );
280         
281         // --- Fill the TD
282         td->Flags |= (1 << 18); // Buffer rounding
283         td->Flags |= (Type & 3) << 19;  // Type/Direction
284         td->Flags |= (2 | DataTgl) << 24;       // Data Toggle
285         td->CBP = MM_GetPhysAddr( (tVAddr) Buf );
286         td->BE = MM_GetPhysAddr( (tVAddr)Buf + Length - 1 );
287         // - Save callback etc
288         td->CbPointer = (tVAddr)Cb;
289         td->CbArg = (tVAddr)CbData;
290
291         // --- Add to the end of the Endpoint's list
292         if( ep->TailP )
293                 OHCI_int_GetGTDFromPhys( ep->TailP )->NextTD = td_phys;
294         else
295                 ep->HeadP = td_phys;
296         ep->TailP = td_phys;
297
298         LEAVE('p', td);
299         return td;
300 }
301 void *OHCI_DataIN(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
302 {
303         return OHCI_int_DoTD(Ptr, Dest, DataTgl, 2, Cb, CbData, Buf, Length);
304 }
305 void *OHCI_DataOUT(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
306 {
307         return OHCI_int_DoTD(Ptr, Dest, DataTgl, 1, Cb, CbData, Buf, Length);
308 }
309 void *OHCI_SendSETUP(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
310 {
311         return OHCI_int_DoTD(Ptr, Dest, DataTgl, 0, Cb, CbData, Buf, Length);
312 }
313 int OHCI_IsTransferComplete(void *Ptr, void *Handle)
314 {
315         tOHCI_GeneralTD *td = Handle;
316         
317         ENTER("pPtr pHandle", Ptr, Handle);
318
319         if( td->CBP != 0 )
320         {
321                 LEAVE('i', 0);
322                 return 0;
323         }
324         else
325         {
326                 Log_Warning("OHCI", "TODO: Implement cleanup in OHCI_IsTransferComplete");
327                 LEAVE('i', 1);
328                 return 1;
329         }
330 }
331
332 // --- Interrupt polling ---
333 void *OHCI_StartPoll(void *Ptr, int Dest, int MaxPeriod, tUSBHostCb Cb, void *CbData, void *DataBuf, size_t Length)
334 {
335          int    slot;
336
337         if( MaxPeriod <= 0 )    return NULL;
338
339         // Allocate? (or obtain) an ED
340         
341         // Get update rate
342         for( slot = 32; slot > MaxPeriod && slot; slot >>= 1 );
343
344         // Allocate a TD
345         
346         // Place onto list
347         switch( slot )
348         {
349         case 1:
350                 // Add to period 1 list
351                 break;
352         case 2:
353                 // Determine best list by current load
354                 break;
355         case 4:
356                 break;
357         case 8:
358                 break;
359         case 16:
360                 break;
361         case 32:
362                 break;
363         default:
364                 Log_Error("OHCI", "OHCI_StartPoll - `slot` is invalid (%i)", slot);
365                 break;
366         }
367
368         Log_Warning("OHCI", "TODO: Implement OHCI_StartPoll");
369         return NULL;
370 }
371
372 void OHCI_StopPoll(void *Ptr, void *Hdl)
373 {
374         // Remove from list
375 }
376
377 // --- Root hub ---
378 void OHCI_CheckPortUpdate(void *Ptr)
379 {
380         tOHCI_Controller        *cnt = Ptr;
381         for( int i = 0; i < cnt->nPorts; i ++ )
382         {
383                 volatile Uint32 *status_ptr = &cnt->ControlSpace->HcRhPortStatus[i];
384                 Uint32  status = *status_ptr;
385                 // Connect change?
386                 if( status & (1 << 16) )
387                 {
388                         LOG("Connect status change port %i, *status = 0x%x", i, status);
389                         // Connect or disconnect?
390                         if( status & (1 << 0) )
391                         {
392                                 // Connect, set things up :)
393                         
394                                 // - TODO: Power on?            
395         
396                                 // - Reset port
397                                 *status_ptr = 1 << 4;   // PRS
398                                 Time_Delay(50);
399                                 // - Enable port
400                                 *status_ptr = 1 << 1;   // SetPortEnable
401                                 LOG("Device connected on port %i", i);  
402
403                                 // - Tell stack
404                                 USB_DeviceConnected(cnt->RootHub, i);
405                         }
406                         else
407                         {
408                                 // Disconnect
409                                 USB_DeviceDisconnected(cnt->RootHub, i);
410                         }
411                 }
412                 
413                 // TODO: Handle other bits?
414         }
415 }
416
417 // --- Interrupt handler
418 void OHCI_InterruptHandler(int IRQ, void *Ptr)
419 {
420         tOHCI_Controller        *cnt = Ptr;
421         // TODO: Interrupt handler
422         LOG("Interrupt on controller %i - Status = 0x%x",
423                 cnt->ID, cnt->ControlSpace->HcInterruptStatus);
424         
425         cnt->ControlSpace->HcInterruptStatus = cnt->ControlSpace->HcInterruptStatus;
426 }
427
428 // --- Internal Functions ---
429 tOHCI_Endpoint *OHCI_int_AllocateEndpt(tOHCI_Controller *Controller, int Dest)
430 {
431         tOHCI_Endpoint  *first_free_ep = NULL;
432         tOHCI_Endpoint  *first_freeable_ep = NULL;
433          int    pg, i;
434          int    _dest;
435         
436         // Convert 8.4 dev:endpt destination into 4.7 endpt:dev
437         _dest = (Dest >> 4) | ((Dest & 0xF) << 7);
438         
439         // TODO: Locking
440         for( pg = 0; pg < MAX_ENDPT_PAGES; pg ++ )
441         {
442                 if( !gapOHCI_EndpointPool[pg] ) {
443                         tPAddr  paddr;  // Not used
444                         gapOHCI_EndpointPool[pg] = (void*)MM_AllocDMA(1, 32, &paddr);
445                         memset(gapOHCI_EndpointPool[pg], 0, PAGE_SIZE);
446                 }
447                 
448                 for( i = 0; i < ciEndpoints_per_page; i ++ )
449                 {
450                         tOHCI_Endpoint  *ep = &gapOHCI_EndpointPool[pg][i];
451                         
452                         // Check if it's allocated
453                         if( ep->Flags & (1 << 31) )
454                         {
455                                 if( ep->HeadP == 0 )
456                                         first_freeable_ep = ep;
457                                 
458                                 if( ((ep->Flags >> 27) & 0xF) != Controller->ID )
459                                         continue ;
460                                 if( (ep->Flags & 0x7FF) != _dest )
461                                         continue ;
462                                 return ep;
463                         }
464                         else
465                         {
466                                 if( !first_free_ep )
467                                         first_free_ep = ep;
468                         }
469                 }
470         }
471         
472         if( first_free_ep ) {
473                 first_free_ep->Flags = (1 << 31);
474                 
475                 // Set controller ID
476                 first_free_ep->Flags |= Controller->ID << 27;
477                 first_free_ep->Flags |= _dest;
478                 first_free_ep->Flags |= 1023 << 16;     // Max Packet Size
479                 
480                 return first_free_ep;
481         }
482
483         if( first_freeable_ep ) {
484                 #if 0
485                 first_free_ep->Flags = (1 << 31);
486                 
487                 // Set controller ID
488                 first_free_ep->Flags |= Controller->ID << 27;
489                 first_free_ep->Flags |= _dest;
490                 
491                 return first_free_ep;
492                 #endif
493                 Log_Warning("OHCI", "TODO: Implement freeing EPs when no avalable slots");
494         }
495         
496         return NULL;
497 }
498
499 tOHCI_Endpoint *OHCI_int_GetEndptFromPhys(tPAddr PhysAddr)
500 {
501          int    i;
502         for( i = 0; i < MAX_ENDPT_PAGES; i ++ )
503         {
504                 tPAddr  addr;
505                 addr = MM_GetPhysAddr( (tVAddr)gapOHCI_EndpointPool[i] );
506                 if( PhysAddr >= addr && PhysAddr < addr + 0x1000 )
507                 {
508                         return gapOHCI_EndpointPool[i] + (PhysAddr - addr) / sizeof(tOHCI_Endpoint);
509                 }
510         }
511         return NULL;
512 }
513
514 tOHCI_GeneralTD *OHCI_int_AllocateGTD(tOHCI_Controller *Controller)
515 {
516          int    pg, i;
517         // TODO: Locking
518         for( pg = 0; pg < MAX_TD_PAGES; pg ++ )
519         {
520                 if( !gapOHCI_TDPool[pg] ) {
521                         tPAddr  paddr;  // Not used
522                         gapOHCI_TDPool[pg] = (void*)MM_AllocDMA(1, 32, &paddr);
523                         memset(gapOHCI_TDPool[pg], 0, PAGE_SIZE);
524                 }
525                 
526                 for( i = 0; i < ciTDs_per_page; i ++ )
527                 {
528                         // Check if allocated, and if not take it
529                         if( gapOHCI_TDPool[pg][i].Flags & (1 << 31) )
530                                 continue ;
531                         gapOHCI_TDPool[pg][i].Flags = (1 << 31);
532                         
533                         // Set controller ID
534                         gapOHCI_TDPool[pg][i].Flags |= Controller->ID << 27;
535                         
536                         return &gapOHCI_TDPool[pg][i];
537                 }
538         }
539         return NULL;
540 }
541
542 tOHCI_GeneralTD *OHCI_int_GetGTDFromPhys(tPAddr PhysAddr)
543 {
544          int    i;
545         for( i = 0; i < MAX_TD_PAGES; i ++ )
546         {
547                 tPAddr  addr;
548                 addr = MM_GetPhysAddr( (tVAddr)gapOHCI_TDPool[i] );
549                 if( PhysAddr >= addr && PhysAddr < addr + 0x1000 )
550                 {
551                         return gapOHCI_TDPool[i] + (PhysAddr - addr) / sizeof(tOHCI_GeneralTD);
552                 }
553         }
554         return NULL;
555 }
556

UCC git Repository :: git.ucc.asn.au