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

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