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

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