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

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