1df421ec2c2cc0a0a700cc489af4cfe06bb14ad8
[tpg/acess2.git] / KernelLand / Kernel / drv / pci.c
1 /*\r
2  * Acess2 Kernel\r
3  * - By John Hodge (thePowersGang)\r
4  * \r
5  * drv/pci.c\r
6  * - PCI Enumeration and Arbitration\r
7  */\r
8 #define DEBUG   0\r
9 #include <acess.h>\r
10 #include <modules.h>\r
11 #include <vfs.h>\r
12 #include <fs_devfs.h>\r
13 #include <drv_pci.h>\r
14 #include <drv_pci_int.h>\r
15 #include <virtual_pci.h>\r
16 \r
17 #define USE_PORT_BITMAP 0\r
18 #define LIST_DEVICES    1\r
19 #define PCI_MAX_BUSSES  8\r
20 \r
21 // === STRUCTURES ===\r
22 typedef struct sPCIDevice\r
23 {\r
24         Uint16  bus, slot, fcn;\r
25         Uint16  vendor, device;\r
26         Uint32  class;  // Class:Subclass:ProgIf\r
27         Uint8   revision;\r
28         Uint32  ConfigCache[256/4];\r
29         char    Name[8];\r
30         tVFS_Node       Node;\r
31 } tPCIDevice;\r
32 \r
33 // === CONSTANTS ===\r
34 #define SPACE_STEP      5\r
35 #define MAX_RESERVED_PORT       0xD00\r
36 \r
37 // === PROTOTYPES ===\r
38  int    PCI_Install(char **Arguments);\r
39  int    PCI_ScanBus(int ID, int bFill);\r
40  \r
41  int    PCI_int_ReadDirRoot(tVFS_Node *node, int pos, char Dest[FILENAME_MAX]);\r
42 tVFS_Node       *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename);\r
43 Uint32  PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset);\r
44 size_t  PCI_int_ReadDevice(tVFS_Node *node, off_t Offset, size_t Length, void *buffer);\r
45  int    PCI_int_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, tPCIDevice *info);\r
46 \r
47 // === GLOBALS ===\r
48 MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL, NULL);\r
49  int    giPCI_BusCount = 1;\r
50 Uint8   gaPCI_BusNumbers[PCI_MAX_BUSSES];\r
51  int    giPCI_InodeHandle = -1;\r
52  int    giPCI_DeviceCount = 0;\r
53 tPCIDevice      *gPCI_Devices = NULL;\r
54 tVFS_NodeType   gPCI_RootNodeType = {\r
55         .TypeName = "PCI Root Node",\r
56         .ReadDir = PCI_int_ReadDirRoot,\r
57         .FindDir = PCI_int_FindDirRoot\r
58 };\r
59 tVFS_NodeType   gPCI_DevNodeType = {\r
60         .TypeName = "PCI Dev Node",\r
61         .Read = PCI_int_ReadDevice\r
62 };\r
63 tDevFS_Driver   gPCI_DriverStruct = {\r
64         NULL, "pci",\r
65         {\r
66         .Flags = VFS_FFLAG_DIRECTORY,\r
67         .Size = -1,\r
68         .NumACLs = 1,\r
69         .ACLs = &gVFS_ACL_EveryoneRX,\r
70         .Type = &gPCI_RootNodeType\r
71         }\r
72 };\r
73 #if USE_PORT_BITMAP\r
74 Uint32  *gaPCI_PortBitmap = NULL;\r
75 #endif\r
76 Uint32  gaPCI_BusBitmap[256/32];\r
77  \r
78 // === CODE ===\r
79 /**\r
80  * \brief Scan the PCI Bus for devices\r
81  * \param Arguments     Boot-time parameters\r
82  */\r
83 int PCI_Install(char **Arguments)\r
84 {\r
85          int    ret, bus;\r
86         void    *tmpPtr;\r
87         \r
88         #if USE_PORT_BITMAP\r
89         // Build Portmap\r
90         gaPCI_PortBitmap = malloc( 1 << 13 );\r
91         if( !gaPCI_PortBitmap ) {\r
92                 Log_Error("PCI", "Unable to allocate %i bytes for bitmap", 1 << 13);\r
93                 return MODULE_ERR_MALLOC;\r
94         }\r
95         memset( gaPCI_PortBitmap, 0, 1 << 13 );\r
96          int    i;\r
97         for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ )\r
98                 gaPCI_PortBitmap[i] = -1;\r
99         for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ )\r
100                 gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i;\r
101         #endif  \r
102 \r
103         // Scan Bus (Bus 0, Don't fill gPCI_Devices)\r
104         giPCI_DeviceCount = 0;\r
105         giPCI_BusCount = 1;\r
106         gaPCI_BusNumbers[0] = 0;\r
107         for( bus = 0; bus < giPCI_BusCount; bus ++ )\r
108         {\r
109                 ret = PCI_ScanBus(gaPCI_BusNumbers[bus], 0);\r
110                 if(ret != MODULE_ERR_OK)        return ret;\r
111         }\r
112         // TODO: PCIe\r
113         // - Add VPCI Devices\r
114         giPCI_DeviceCount += giVPCI_DeviceCount;\r
115         \r
116         if(giPCI_DeviceCount == 0) {\r
117                 Log_Notice("PCI", "No devices were found");\r
118                 return MODULE_ERR_NOTNEEDED;\r
119         }\r
120         \r
121         // Allocate device buffer\r
122         tmpPtr = malloc(giPCI_DeviceCount * sizeof(tPCIDevice));\r
123         if(tmpPtr == NULL) {\r
124                 Log_Warning("PCI", "Malloc ERROR");\r
125                 return MODULE_ERR_MALLOC;\r
126         }\r
127         gPCI_Devices = tmpPtr;\r
128         \r
129         Log_Log("PCI", "%i devices, filling structure", giPCI_DeviceCount);\r
130         \r
131         // Reset counts\r
132         giPCI_DeviceCount = 0;\r
133         giPCI_BusCount = 1;\r
134         memset(gaPCI_BusBitmap, 0, sizeof(gaPCI_BusBitmap));\r
135         // Rescan, filling the PCI device array\r
136         for( bus = 0; bus < giPCI_BusCount; bus ++ )\r
137         {\r
138                 PCI_ScanBus(gaPCI_BusNumbers[bus], 1);\r
139         }\r
140         // Insert VPCI Devices\r
141         for( int i = 0; i < giVPCI_DeviceCount; i ++ )\r
142         {\r
143                 tPCIDevice      *devinfo = &gPCI_Devices[giPCI_DeviceCount];\r
144                 \r
145                 devinfo->bus = -1;\r
146                 devinfo->slot = i;\r
147                 devinfo->fcn = 0;\r
148                 devinfo->vendor = gaVPCI_Devices[i].Vendor;\r
149                 devinfo->device = gaVPCI_Devices[i].Device;\r
150                 devinfo->revision = gaVPCI_Devices[i].Class & 0xFF;\r
151                 devinfo->class = gaVPCI_Devices[i].Class >> 8;\r
152                 snprintf(devinfo->Name, sizeof(devinfo->Name), "%02x.%02x:%x", 0xFF, i, 0);\r
153                 \r
154                 #if LIST_DEVICES\r
155                 Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x Rev %i",\r
156                         0xFF, i, 0, devinfo->class,\r
157                         devinfo->vendor, devinfo->device, devinfo->revision);\r
158                 #endif\r
159 \r
160                 for(int j = 0; j < 256/4; j ++ )\r
161                         devinfo->ConfigCache[j] = VPCI_Read(&gaVPCI_Devices[i], j*4, 4);\r
162 \r
163                 memset(&devinfo->Node, 0, sizeof(devinfo->Node));\r
164                 devinfo->Node.Inode = giPCI_DeviceCount;\r
165                 devinfo->Node.Size = 256;\r
166                 devinfo->Node.NumACLs = 1;\r
167                 devinfo->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
168                 devinfo->Node.Type = &gPCI_DevNodeType;\r
169 \r
170                 giPCI_DeviceCount ++;\r
171         }\r
172         \r
173         // Complete Driver Structure\r
174         gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount;\r
175         \r
176         // And add to DevFS\r
177         DevFS_AddDevice(&gPCI_DriverStruct);\r
178         \r
179         return MODULE_ERR_OK;\r
180 }\r
181 \r
182 /**\r
183  * \brief Scans a specific PCI Bus\r
184  * \param BusID PCI Bus ID to scan\r
185  * \param bFill Fill the \a gPCI_Devices array?\r
186  */\r
187 int PCI_ScanBus(int BusID, int bFill)\r
188 {\r
189          int    dev, fcn;\r
190         tPCIDevice      devInfo;\r
191         \r
192         if( gaPCI_BusBitmap[BusID/32] & (1 << (BusID%32)) )\r
193                 return MODULE_ERR_OK;\r
194         \r
195         gaPCI_BusBitmap[BusID/32] |= (1 << (BusID%32));\r
196         \r
197         for( dev = 0; dev < 32; dev++ ) // 32 Devices per bus\r
198         {\r
199                 for( fcn = 0; fcn < 8; fcn++ )  // Max 8 functions per device\r
200                 {\r
201                         // Check if the device/function exists\r
202                         if(!PCI_int_EnumDevice(BusID, dev, fcn, &devInfo))\r
203                                 continue;\r
204                         \r
205                         #if LIST_DEVICES\r
206                         if( !bFill )\r
207                                 Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x Rev %i",\r
208                                         BusID, dev, fcn, devInfo.class,\r
209                                         devInfo.vendor, devInfo.device, devInfo.revision);\r
210                         #endif\r
211                         \r
212                         if( bFill ) {\r
213                                 devInfo.Node.Inode = giPCI_DeviceCount;\r
214                                 memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(tPCIDevice));\r
215                         }\r
216                         giPCI_DeviceCount ++;\r
217                         \r
218                         switch(devInfo.ConfigCache[3] & 0x007F0000)\r
219                         {\r
220                         case 0x00:      // Normal device\r
221                                 break;\r
222                         case 0x01:      // PCI-PCI Bridge\r
223                                 {\r
224                                 // TODO: Add to list of busses?\r
225                                 Uint8   sec = (devInfo.ConfigCache[6] & 0x0000FF00) >> 8;\r
226                                 #if LIST_DEVICES\r
227                                 if( !bFill ) {\r
228                                         Uint8   pri = (devInfo.ConfigCache[6] & 0x000000FF) >> 0;\r
229                                         Log_Log("PCI", "- PCI-PCI Bridge %02x=>%02x", pri, sec);\r
230                                 }\r
231                                 #endif\r
232                                 gaPCI_BusNumbers[giPCI_BusCount++] = sec;\r
233                                 }\r
234                                 break;\r
235                         case 0x02:      // PCI-CardBus Bridge\r
236                                 break;\r
237                         }\r
238 \r
239                         // If bit 8 of the Header Type register is set, there are sub-functions\r
240                         if(fcn == 0 && !(devInfo.ConfigCache[3] & 0x00800000) )\r
241                                 break;\r
242                 }\r
243         }\r
244         \r
245         return MODULE_ERR_OK;\r
246 }\r
247 \r
248 /**\r
249  * \brief Read from Root of PCI Driver\r
250 */\r
251 int PCI_int_ReadDirRoot(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])\r
252 {\r
253         ENTER("pNode iPos", Node, Pos);\r
254         if(Pos < 0 || Pos >= giPCI_DeviceCount) {\r
255                 LEAVE_RET('i', -EINVAL);\r
256         }\r
257         \r
258         LOG("Name = %s", gPCI_Devices[Pos].Name);\r
259         strncpy(Dest, gPCI_Devices[Pos].Name, FILENAME_MAX);\r
260         LEAVE_RET('i', 0);\r
261 }\r
262 /**\r
263  */\r
264 tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename)\r
265 {\r
266          int    i;\r
267         \r
268         // Find Match\r
269         for(i=0;i<giPCI_DeviceCount;i++)\r
270         {\r
271                 int cmp = strcmp(gPCI_Devices[i].Name, filename);\r
272                 if( cmp > 0 )   // Sorted list\r
273                         break;\r
274                 if( cmp == 0 )\r
275                         return &gPCI_Devices[i].Node;\r
276         }\r
277         \r
278         // Error Return\r
279         return NULL;\r
280 }\r
281 \r
282 /**\r
283  * \brief Read the PCI configuration space of a device\r
284  */\r
285 size_t PCI_int_ReadDevice(tVFS_Node *node, off_t pos, size_t length, void *buffer)\r
286 {       \r
287         if( pos + length > 256 )        return 0;\r
288         \r
289         memcpy(\r
290                 buffer,\r
291                 (char*)gPCI_Devices[node->Inode].ConfigCache + pos,\r
292                 length);\r
293         \r
294         return length;\r
295 }\r
296 \r
297 // --- Kernel Code Interface ---\r
298 /**\r
299  * \brief Counts the devices with the specified codes\r
300  * \param vendor        Vendor ID\r
301  * \param device        Device ID\r
302  */\r
303 int PCI_CountDevices(Uint16 vendor, Uint16 device)\r
304 {\r
305         int i, ret=0;\r
306         for(i=0;i<giPCI_DeviceCount;i++)\r
307         {\r
308                 if(gPCI_Devices[i].vendor != vendor)    continue;\r
309                 if(gPCI_Devices[i].device != device)    continue;\r
310                 ret ++;\r
311         }\r
312         return ret;\r
313 }\r
314 \r
315 /**\r
316  * \brief Gets the ID of the specified PCI device\r
317  * \param vendor        Vendor ID\r
318  * \param device        Device ID\r
319  * \param idx   Number of matching entry wanted\r
320  */\r
321 tPCIDev PCI_GetDevice(Uint16 vendor, Uint16 device, int idx)\r
322 {\r
323          int    i, j=0;\r
324         for( i = 0; i < giPCI_DeviceCount; i ++ )\r
325         {\r
326                 if(gPCI_Devices[i].vendor != vendor)    continue;\r
327                 if(gPCI_Devices[i].device != device)    continue;\r
328                 if(j == idx)    return i;\r
329                 j ++;\r
330         }\r
331         return -1;\r
332 }\r
333 \r
334 /**\r
335  * \brief Gets the ID of a device by it's class code\r
336  * \param class Class Code\r
337  * \param mask  Mask for class comparison\r
338  * \param prev  ID of previous device (-1 for no previous)\r
339  */\r
340 tPCIDev PCI_GetDeviceByClass(Uint32 class, Uint32 mask, tPCIDev prev)\r
341 {\r
342          int    i;\r
343         // Check if prev is negative (meaning get first)\r
344         if(prev < 0)    i = 0;\r
345         else    i = prev+1;\r
346         \r
347         for( ; i < giPCI_DeviceCount; i++ )\r
348         {\r
349                 if((gPCI_Devices[i].class & mask) == class)\r
350                         return i;\r
351         }\r
352         return -1;\r
353 }\r
354 \r
355 int PCI_GetDeviceInfo(tPCIDev ID, Uint16 *Vendor, Uint16 *Device, Uint32 *Class)\r
356 {\r
357         tPCIDevice      *dev = &gPCI_Devices[ID];\r
358         if(ID < 0 || ID >= giPCI_DeviceCount)   return 1;\r
359         \r
360         if(Vendor)      *Vendor = dev->vendor;\r
361         if(Device)      *Device = dev->device;\r
362         if(Class)       *Class = dev->class;\r
363         return 0;\r
364 }\r
365 \r
366 int PCI_GetDeviceVersion(tPCIDev ID, Uint8 *Revision)\r
367 {\r
368         tPCIDevice      *dev = &gPCI_Devices[ID];\r
369         if(ID < 0 || ID >= giPCI_DeviceCount)   return 1;\r
370         \r
371         if(Revision)    *Revision = dev->revision;\r
372         return 0;\r
373 }\r
374 \r
375 int PCI_GetDeviceSubsys(tPCIDev ID, Uint16 *SubsystemVendor, Uint16 *SubsystemID)\r
376 {\r
377         tPCIDevice      *dev = &gPCI_Devices[ID];\r
378         if(ID < 0 || ID >= giPCI_DeviceCount)   return 1;\r
379         \r
380         if(SubsystemVendor)     *SubsystemVendor = dev->ConfigCache[0x2c/4] & 0xFFFF;\r
381         if(SubsystemID) *SubsystemID = dev->ConfigCache[0x2c/4] >> 16;\r
382 \r
383         return 0;\r
384 }\r
385 \r
386 Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset)\r
387 {\r
388         Bus &= 0xFF;\r
389         Slot &= 0x1F;\r
390         Fcn &= 7;\r
391         Offset &= 0xFC;\r
392         return ((Uint32)Bus << 16) | (Slot << 11) | (Fcn << 8) | (Offset & 0xFC);\r
393 }\r
394 \r
395 Uint32 PCI_ConfigRead(tPCIDev ID, int Offset, int Size)\r
396 {\r
397         tPCIDevice      *dev;\r
398         Uint32  dword, addr;\r
399         \r
400         if( ID < 0 || ID >= giPCI_DeviceCount ) return 0;\r
401         if( Offset < 0 || Offset > 256 )        return 0;\r
402 \r
403         // TODO: Should I support non-aligned reads?\r
404         if( Offset & (Size - 1) )       return 0;\r
405 \r
406         dev = &gPCI_Devices[ID];\r
407         // Detect VPCI devices\r
408         if( dev->bus == -1 ) {\r
409                 return VPCI_Read(&gaVPCI_Devices[dev->slot], Offset, Size);\r
410         }\r
411 \r
412         addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
413 \r
414         dword = PCI_CfgReadDWord(addr);\r
415         gPCI_Devices[ID].ConfigCache[Offset/4] = dword;\r
416         switch( Size )\r
417         {\r
418         case 1: return (dword >> (8 * (Offset&3))) & 0xFF;\r
419         case 2: return (dword >> (8 * (Offset&2))) & 0xFFFF;\r
420         case 4: return dword;\r
421         default:\r
422                 return 0;\r
423         }\r
424 }\r
425 \r
426 void PCI_ConfigWrite(tPCIDev ID, int Offset, int Size, Uint32 Value)\r
427 {\r
428         tPCIDevice      *dev;\r
429         Uint32  dword, addr;\r
430          int    shift;\r
431 \r
432         if( ID < 0 || ID >= giPCI_DeviceCount ) return ;\r
433         if( Offset < 0 || Offset > 256 )        return ;\r
434 \r
435         dev = &gPCI_Devices[ID];\r
436 \r
437         // Detect VPCI devices\r
438         if( dev->bus == -1 ) {\r
439                 VPCI_Write(&gaVPCI_Devices[dev->slot], Offset, Size, Value);\r
440                 return ;\r
441         }\r
442 \r
443         addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
444 \r
445         if(Size != 4)\r
446                 dword = PCI_CfgReadDWord(addr);\r
447         switch(Size)\r
448         {\r
449         case 1:\r
450                 shift = (Offset&3)*8;\r
451                 dword &= ~(0xFF << shift);\r
452                 dword |= Value << shift;\r
453                 break;\r
454         case 2:\r
455                 shift = (Offset&2)*8;\r
456                 dword &= ~(0xFFFF << shift);\r
457                 dword |= Value << shift;\r
458                 break;\r
459         case 4:\r
460                 dword = Value;\r
461                 break;\r
462         default:\r
463                 return;\r
464         }\r
465         PCI_CfgWriteDWord(addr, dword);\r
466 }\r
467 \r
468 /**\r
469  * \brief Get the IRQ assigned to a device\r
470  */\r
471 Uint8 PCI_GetIRQ(tPCIDev id)\r
472 {\r
473         if(id < 0 || id >= giPCI_DeviceCount)\r
474                 return 0;\r
475         return gPCI_Devices[id].ConfigCache[15] & 0xFF;\r
476         //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C);\r
477 }\r
478 \r
479 /**\r
480  * \brief Read the a BAR (base address register) from the PCI config space\r
481  */\r
482 Uint32 PCI_GetBAR(tPCIDev id, int BARNum)\r
483 {\r
484         if(id < 0 || id >= giPCI_DeviceCount)\r
485                 return 0;\r
486         if(BARNum < 0 || BARNum >= 6)\r
487                 return 0;\r
488         return gPCI_Devices[id].ConfigCache[4+BARNum];\r
489 }\r
490 \r
491 /**\r
492  * \brief Get device information for a slot/function\r
493  */\r
494 int PCI_int_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, tPCIDevice *info)\r
495 {\r
496         Uint32  vendor_dev, tmp;\r
497          int    i;\r
498         Uint32  addr;\r
499         addr = PCI_int_GetBusAddr(bus, slot, fcn, 0);   \r
500 \r
501         vendor_dev = PCI_CfgReadDWord( addr );\r
502         if((vendor_dev & 0xFFFF) == 0xFFFF)     // Invalid Device\r
503                 return 0;\r
504         \r
505         info->bus = bus;\r
506         info->slot = slot;\r
507         info->fcn = fcn;\r
508 \r
509         // Read configuration\r
510         info->ConfigCache[0] = vendor_dev;\r
511         for( i = 1, addr += 4; i < 256/4; i ++, addr += 4 )\r
512         {\r
513                 info->ConfigCache[i] = PCI_CfgReadDWord(addr);\r
514         }       \r
515 \r
516         info->vendor = vendor_dev & 0xFFFF;\r
517         info->device = vendor_dev >> 16;\r
518         tmp = info->ConfigCache[2];\r
519         info->revision = tmp & 0xFF;\r
520         info->class = tmp >> 8;\r
521         \r
522 //      #if LIST_DEVICES\r
523 //      Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]);\r
524 //      Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]);\r
525 //      Log("Class: 0x%06x", info->class);\r
526 //      #endif\r
527         \r
528         // Make node name\r
529         snprintf(info->Name, 8, "%02x.%02x:%x", bus, slot, fcn);\r
530         \r
531         // Create VFS Node\r
532         memset( &info->Node, 0, sizeof(tVFS_Node) );\r
533         info->Node.Size = 256;\r
534         \r
535         info->Node.NumACLs = 1;\r
536         info->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
537         \r
538         info->Node.Type = &gPCI_DevNodeType;\r
539         \r
540         return 1;\r
541 }\r
542 \r
543 // === EXPORTS ===\r
544 //*\r
545 EXPORT(PCI_CountDevices);\r
546 EXPORT(PCI_GetDevice);\r
547 EXPORT(PCI_GetDeviceByClass);\r
548 EXPORT(PCI_GetDeviceInfo);\r
549 EXPORT(PCI_GetDeviceVersion);\r
550 EXPORT(PCI_GetDeviceSubsys);\r
551 //EXPORT(PCI_AssignPort);\r
552 EXPORT(PCI_GetBAR);\r
553 EXPORT(PCI_GetIRQ);\r
554 //*/\r

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