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

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