Merge branch 'master' of github.com:thepowersgang/acess2
[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                 for(int j = 0; j < 256/4; j ++ )\r
155                         devinfo->ConfigCache[i] = VPCI_Read(&gaVPCI_Devices[i], j*4, 4);\r
156 \r
157                 memset(&devinfo->Node, 0, sizeof(devinfo->Node));\r
158                 devinfo->Node.Inode = giPCI_DeviceCount;\r
159                 devinfo->Node.Size = 256;\r
160                 devinfo->Node.NumACLs = 1;\r
161                 devinfo->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
162                 devinfo->Node.Type = &gPCI_DevNodeType;\r
163 \r
164                 giPCI_DeviceCount ++;\r
165         }\r
166         \r
167         // Complete Driver Structure\r
168         gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount;\r
169         \r
170         // And add to DevFS\r
171         DevFS_AddDevice(&gPCI_DriverStruct);\r
172         \r
173         return MODULE_ERR_OK;\r
174 }\r
175 \r
176 /**\r
177  * \brief Scans a specific PCI Bus\r
178  * \param BusID PCI Bus ID to scan\r
179  * \param bFill Fill the \a gPCI_Devices array?\r
180  */\r
181 int PCI_ScanBus(int BusID, int bFill)\r
182 {\r
183          int    dev, fcn;\r
184         tPCIDevice      devInfo;\r
185         \r
186         if( gaPCI_BusBitmap[BusID/32] & (1 << (BusID%32)) )\r
187                 return MODULE_ERR_OK;\r
188         \r
189         gaPCI_BusBitmap[BusID/32] |= (1 << (BusID%32));\r
190         \r
191         for( dev = 0; dev < 32; dev++ ) // 32 Devices per bus\r
192         {\r
193                 for( fcn = 0; fcn < 8; fcn++ )  // Max 8 functions per device\r
194                 {\r
195                         // Check if the device/function exists\r
196                         if(!PCI_int_EnumDevice(BusID, dev, fcn, &devInfo))\r
197                                 continue;\r
198                         \r
199                         #if LIST_DEVICES\r
200                         if( !bFill )\r
201                                 Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x Rev %i",\r
202                                         BusID, dev, fcn, devInfo.class,\r
203                                         devInfo.vendor, devInfo.device, devInfo.revision);\r
204                         #endif\r
205                         \r
206                         if( bFill ) {\r
207                                 devInfo.Node.Inode = giPCI_DeviceCount;\r
208                                 memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(tPCIDevice));\r
209                         }\r
210                         giPCI_DeviceCount ++;\r
211                         \r
212                         switch(devInfo.ConfigCache[3] & 0x007F0000)\r
213                         {\r
214                         case 0x00:      // Normal device\r
215                                 break;\r
216                         case 0x01:      // PCI-PCI Bridge\r
217                                 {\r
218                                 // TODO: Add to list of busses?\r
219                                 Uint8   sec = (devInfo.ConfigCache[6] & 0x0000FF00) >> 8;\r
220                                 #if LIST_DEVICES\r
221                                 if( !bFill ) {\r
222                                         Uint8   pri = (devInfo.ConfigCache[6] & 0x000000FF) >> 0;\r
223                                         Log_Log("PCI", "- PCI-PCI Bridge %02x=>%02x", pri, sec);\r
224                                 }\r
225                                 #endif\r
226                                 gaPCI_BusNumbers[giPCI_BusCount++] = sec;\r
227                                 }\r
228                                 break;\r
229                         case 0x02:      // PCI-CardBus Bridge\r
230                                 break;\r
231                         }\r
232 \r
233                         // If bit 8 of the Header Type register is set, there are sub-functions\r
234                         if(fcn == 0 && !(devInfo.ConfigCache[3] & 0x00800000) )\r
235                                 break;\r
236                 }\r
237         }\r
238         \r
239         return MODULE_ERR_OK;\r
240 }\r
241 \r
242 /**\r
243  * \brief Read from Root of PCI Driver\r
244 */\r
245 int PCI_int_ReadDirRoot(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])\r
246 {\r
247         ENTER("pNode iPos", Node, Pos);\r
248         if(Pos < 0 || Pos >= giPCI_DeviceCount) {\r
249                 LEAVE_RET('i', -EINVAL);\r
250         }\r
251         \r
252         LOG("Name = %s", gPCI_Devices[Pos].Name);\r
253         strncpy(Dest, gPCI_Devices[Pos].Name, FILENAME_MAX);\r
254         LEAVE_RET('i', 0);\r
255 }\r
256 /**\r
257  */\r
258 tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename)\r
259 {\r
260          int    i;\r
261         \r
262         // Find Match\r
263         for(i=0;i<giPCI_DeviceCount;i++)\r
264         {\r
265                 int cmp = strcmp(gPCI_Devices[i].Name, filename);\r
266                 if( cmp > 0 )   // Sorted list\r
267                         break;\r
268                 if( cmp == 0 )\r
269                         return &gPCI_Devices[i].Node;\r
270         }\r
271         \r
272         // Error Return\r
273         return NULL;\r
274 }\r
275 \r
276 /**\r
277  * \brief Read the PCI configuration space of a device\r
278  */\r
279 size_t PCI_int_ReadDevice(tVFS_Node *node, off_t pos, size_t length, void *buffer)\r
280 {       \r
281         if( pos + length > 256 )        return 0;\r
282         \r
283         memcpy(\r
284                 buffer,\r
285                 (char*)gPCI_Devices[node->Inode].ConfigCache + pos,\r
286                 length);\r
287         \r
288         return length;\r
289 }\r
290 \r
291 // --- Kernel Code Interface ---\r
292 /**\r
293  * \brief Counts the devices with the specified codes\r
294  * \param vendor        Vendor ID\r
295  * \param device        Device ID\r
296  */\r
297 int PCI_CountDevices(Uint16 vendor, Uint16 device)\r
298 {\r
299         int i, ret=0;\r
300         for(i=0;i<giPCI_DeviceCount;i++)\r
301         {\r
302                 if(gPCI_Devices[i].vendor != vendor)    continue;\r
303                 if(gPCI_Devices[i].device != device)    continue;\r
304                 ret ++;\r
305         }\r
306         return ret;\r
307 }\r
308 \r
309 /**\r
310  * \brief Gets the ID of the specified PCI device\r
311  * \param vendor        Vendor ID\r
312  * \param device        Device ID\r
313  * \param idx   Number of matching entry wanted\r
314  */\r
315 tPCIDev PCI_GetDevice(Uint16 vendor, Uint16 device, int idx)\r
316 {\r
317          int    i, j=0;\r
318         for( i = 0; i < giPCI_DeviceCount; i ++ )\r
319         {\r
320                 if(gPCI_Devices[i].vendor != vendor)    continue;\r
321                 if(gPCI_Devices[i].device != device)    continue;\r
322                 if(j == idx)    return i;\r
323                 j ++;\r
324         }\r
325         return -1;\r
326 }\r
327 \r
328 /**\r
329  * \brief Gets the ID of a device by it's class code\r
330  * \param class Class Code\r
331  * \param mask  Mask for class comparison\r
332  * \param prev  ID of previous device (-1 for no previous)\r
333  */\r
334 tPCIDev PCI_GetDeviceByClass(Uint32 class, Uint32 mask, tPCIDev prev)\r
335 {\r
336          int    i;\r
337         // Check if prev is negative (meaning get first)\r
338         if(prev < 0)    i = 0;\r
339         else    i = prev+1;\r
340         \r
341         for( ; i < giPCI_DeviceCount; i++ )\r
342         {\r
343                 if((gPCI_Devices[i].class & mask) == class)\r
344                         return i;\r
345         }\r
346         return -1;\r
347 }\r
348 \r
349 int PCI_GetDeviceInfo(tPCIDev ID, Uint16 *Vendor, Uint16 *Device, Uint32 *Class)\r
350 {\r
351         tPCIDevice      *dev = &gPCI_Devices[ID];\r
352         if(ID < 0 || ID >= giPCI_DeviceCount)   return 1;\r
353         \r
354         if(Vendor)      *Vendor = dev->vendor;\r
355         if(Device)      *Device = dev->device;\r
356         if(Class)       *Class = dev->class;\r
357         return 0;\r
358 }\r
359 \r
360 int PCI_GetDeviceVersion(tPCIDev ID, Uint8 *Revision)\r
361 {\r
362         tPCIDevice      *dev = &gPCI_Devices[ID];\r
363         if(ID < 0 || ID >= giPCI_DeviceCount)   return 1;\r
364         \r
365         if(Revision)    *Revision = dev->revision;\r
366         return 0;\r
367 }\r
368 \r
369 int PCI_GetDeviceSubsys(tPCIDev ID, Uint16 *SubsystemVendor, Uint16 *SubsystemID)\r
370 {\r
371         tPCIDevice      *dev = &gPCI_Devices[ID];\r
372         if(ID < 0 || ID >= giPCI_DeviceCount)   return 1;\r
373         \r
374         if(SubsystemVendor)     *SubsystemVendor = dev->ConfigCache[0x2c/4] & 0xFFFF;\r
375         if(SubsystemID) *SubsystemID = dev->ConfigCache[0x2c/4] >> 16;\r
376 \r
377         return 0;\r
378 }\r
379 \r
380 Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset)\r
381 {\r
382         Bus &= 0xFF;\r
383         Slot &= 0x1F;\r
384         Fcn &= 7;\r
385         Offset &= 0xFC;\r
386         return ((Uint32)Bus << 16) | (Slot << 11) | (Fcn << 8) | (Offset & 0xFC);\r
387 }\r
388 \r
389 Uint32 PCI_ConfigRead(tPCIDev ID, int Offset, int Size)\r
390 {\r
391         tPCIDevice      *dev;\r
392         Uint32  dword, addr;\r
393         \r
394         if( ID < 0 || ID >= giPCI_DeviceCount ) return 0;\r
395         if( Offset < 0 || Offset > 256 )        return 0;\r
396 \r
397         // TODO: Should I support non-aligned reads?\r
398         if( Offset & (Size - 1) )       return 0;\r
399 \r
400         dev = &gPCI_Devices[ID];\r
401         // Detect VPCI devices\r
402         if( dev->bus == -1 ) {\r
403                 return VPCI_Read(&gaVPCI_Devices[dev->slot], Offset, Size);\r
404         }\r
405 \r
406         addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
407 \r
408         dword = PCI_CfgReadDWord(addr);\r
409         gPCI_Devices[ID].ConfigCache[Offset/4] = dword;\r
410         switch( Size )\r
411         {\r
412         case 1: return (dword >> (8 * (Offset&3))) & 0xFF;\r
413         case 2: return (dword >> (8 * (Offset&2))) & 0xFFFF;\r
414         case 4: return dword;\r
415         default:\r
416                 return 0;\r
417         }\r
418 }\r
419 \r
420 void PCI_ConfigWrite(tPCIDev ID, int Offset, int Size, Uint32 Value)\r
421 {\r
422         tPCIDevice      *dev;\r
423         Uint32  dword, addr;\r
424          int    shift;\r
425 \r
426         if( ID < 0 || ID >= giPCI_DeviceCount ) return ;\r
427         if( Offset < 0 || Offset > 256 )        return ;\r
428 \r
429         dev = &gPCI_Devices[ID];\r
430 \r
431         // Detect VPCI devices\r
432         if( dev->bus == -1 ) {\r
433                 VPCI_Write(&gaVPCI_Devices[dev->slot], Offset, Size, Value);\r
434                 return ;\r
435         }\r
436 \r
437         addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
438 \r
439         if(Size != 4)\r
440                 dword = PCI_CfgReadDWord(addr);\r
441         switch(Size)\r
442         {\r
443         case 1:\r
444                 shift = (Offset&3)*8;\r
445                 dword &= ~(0xFF << shift);\r
446                 dword |= Value << shift;\r
447                 break;\r
448         case 2:\r
449                 shift = (Offset&2)*8;\r
450                 dword &= ~(0xFFFF << shift);\r
451                 dword |= Value << shift;\r
452                 break;\r
453         case 4:\r
454                 dword = Value;\r
455                 break;\r
456         default:\r
457                 return;\r
458         }\r
459         PCI_CfgWriteDWord(addr, dword);\r
460 }\r
461 \r
462 /**\r
463  * \brief Get the IRQ assigned to a device\r
464  */\r
465 Uint8 PCI_GetIRQ(tPCIDev id)\r
466 {\r
467         if(id < 0 || id >= giPCI_DeviceCount)\r
468                 return 0;\r
469         return gPCI_Devices[id].ConfigCache[15] & 0xFF;\r
470         //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C);\r
471 }\r
472 \r
473 /**\r
474  * \brief Read the a BAR (base address register) from the PCI config space\r
475  */\r
476 Uint32 PCI_GetBAR(tPCIDev id, int BARNum)\r
477 {\r
478         if(id < 0 || id >= giPCI_DeviceCount)\r
479                 return 0;\r
480         if(BARNum < 0 || BARNum >= 6)\r
481                 return 0;\r
482         return gPCI_Devices[id].ConfigCache[4+BARNum];\r
483 }\r
484 \r
485 /**\r
486  * \brief Get device information for a slot/function\r
487  */\r
488 int PCI_int_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, tPCIDevice *info)\r
489 {\r
490         Uint32  vendor_dev, tmp;\r
491          int    i;\r
492         Uint32  addr;\r
493         addr = PCI_int_GetBusAddr(bus, slot, fcn, 0);   \r
494 \r
495         vendor_dev = PCI_CfgReadDWord( addr );\r
496         if((vendor_dev & 0xFFFF) == 0xFFFF)     // Invalid Device\r
497                 return 0;\r
498         \r
499         info->bus = bus;\r
500         info->slot = slot;\r
501         info->fcn = fcn;\r
502 \r
503         // Read configuration\r
504         info->ConfigCache[0] = vendor_dev;\r
505         for( i = 1, addr += 4; i < 256/4; i ++, addr += 4 )\r
506         {\r
507                 info->ConfigCache[i] = PCI_CfgReadDWord(addr);\r
508         }       \r
509 \r
510         info->vendor = vendor_dev & 0xFFFF;\r
511         info->device = vendor_dev >> 16;\r
512         tmp = info->ConfigCache[2];\r
513         info->revision = tmp & 0xFF;\r
514         info->class = tmp >> 8;\r
515         \r
516 //      #if LIST_DEVICES\r
517 //      Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]);\r
518 //      Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]);\r
519 //      Log("Class: 0x%06x", info->class);\r
520 //      #endif\r
521         \r
522         // Make node name\r
523         snprintf(info->Name, 8, "%02x.%02x:%x", bus, slot, fcn);\r
524         \r
525         // Create VFS Node\r
526         memset( &info->Node, 0, sizeof(tVFS_Node) );\r
527         info->Node.Size = 256;\r
528         \r
529         info->Node.NumACLs = 1;\r
530         info->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
531         \r
532         info->Node.Type = &gPCI_DevNodeType;\r
533         \r
534         return 1;\r
535 }\r
536 \r
537 // === EXPORTS ===\r
538 //*\r
539 EXPORT(PCI_CountDevices);\r
540 EXPORT(PCI_GetDevice);\r
541 EXPORT(PCI_GetDeviceByClass);\r
542 EXPORT(PCI_GetDeviceInfo);\r
543 EXPORT(PCI_GetDeviceVersion);\r
544 EXPORT(PCI_GetDeviceSubsys);\r
545 //EXPORT(PCI_AssignPort);\r
546 EXPORT(PCI_GetBAR);\r
547 EXPORT(PCI_GetIRQ);\r
548 //*/\r

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