Initial commit of kernel only
[tpg/acess2.git] / Kernel / drv / pci.c
1 /*\r
2 AcessOS/AcessBasic v0.1\r
3 PCI Bus Driver\r
4 */\r
5 #include <common.h>\r
6 #include <vfs.h>\r
7 #include <fs_devfs.h>\r
8 #include <drv_pci.h>\r
9 \r
10 #define DEBUG   0\r
11 #define LIST_DEVICES    1\r
12 \r
13 // === STRUCTURES ===\r
14 typedef struct s_pciDevice {\r
15         Uint16  bus, slot, fcn;\r
16         Uint16  vendor, device;\r
17         union {\r
18                 struct {Uint8 class, subclass;};\r
19                 Uint16  oc;\r
20         };\r
21         Uint16  revision;\r
22         Uint32  ConfigCache[256/4];\r
23         char    Name[8];\r
24         tVFS_Node       Node;\r
25 } t_pciDevice;\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 char    *PCI_ReadDirRoot(tVFS_Node *node, int pos);\r
34 tVFS_Node       *PCI_FindDirRoot(tVFS_Node *node, char *filename);\r
35 Uint64  PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer);\r
36  \r
37  int    PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn);\r
38  int    PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx);\r
39  int    PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev);\r
40 Uint8   PCI_GetIRQ(int id);\r
41 Uint32  PCI_GetBAR0(int id);\r
42 Uint32  PCI_GetBAR1(int id);\r
43 Uint32  PCI_GetBAR3(int id);\r
44 Uint32  PCI_GetBAR4(int id);\r
45 Uint32  PCI_GetBAR5(int id);\r
46 Uint16  PCI_AssignPort(int id, int bar, int count);\r
47 \r
48  int    PCI_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, t_pciDevice *info);\r
49 Uint32  PCI_CfgReadDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset);\r
50 void    PCI_CfgWriteDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset, Uint32 data);\r
51 Uint16  PCI_CfgReadWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset);\r
52 Uint8   PCI_CfgReadByte(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset);\r
53 \r
54 // === GLOBALS ===\r
55 //MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL);\r
56  int    giPCI_BusCount = 1;\r
57  int    giPCI_InodeHandle = -1;\r
58  int    giPCI_DeviceCount = 0;\r
59 t_pciDevice     *gPCI_Devices = NULL;\r
60 tDevFS_Driver   gPCI_DriverStruct = {\r
61         NULL, "pci",\r
62         {\r
63         .Flags = VFS_FFLAG_DIRECTORY,\r
64         .NumACLs = 1,\r
65         .ACLs = &gVFS_ACL_EveryoneRX,\r
66         .ReadDir = PCI_ReadDirRoot,\r
67         .FindDir = PCI_FindDirRoot\r
68         }\r
69 };\r
70  Uint32 *gaPCI_PortBitmap = NULL;\r
71  \r
72 // === CODE ===\r
73 /**\r
74  * \fn int PCI_Install()\r
75  * \brief Scan the PCI Bus for devices\r
76  */\r
77 int PCI_Install(char **Arguments)\r
78 {\r
79          int    bus, dev, fcn, i;\r
80          int    space = 0;\r
81         t_pciDevice     devInfo;\r
82         void    *tmpPtr = NULL;\r
83         \r
84         // Build Portmap\r
85         gaPCI_PortBitmap = malloc( 1 << 13 );\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         //LogF("Done.\n");\r
92         \r
93         // Scan Busses\r
94         for( bus = 0; bus < giPCI_BusCount; bus++ )\r
95         {\r
96                 for( dev = 0; dev < 10; dev++ ) // 10 Devices per bus\r
97                 {\r
98                         for( fcn = 0; fcn < 8; fcn++ )  // 8 functions per device\r
99                         {\r
100                                 // Check if the device/function exists\r
101                                 if(!PCI_EnumDevice(bus, dev, fcn, &devInfo))\r
102                                 {\r
103                                         continue;\r
104                                 }\r
105                                 \r
106                                 if(giPCI_DeviceCount == space)\r
107                                 {\r
108                                         space += SPACE_STEP;\r
109                                         tmpPtr = realloc(gPCI_Devices, space*sizeof(t_pciDevice));\r
110                                         if(tmpPtr == NULL)\r
111                                                 break;\r
112                                         gPCI_Devices = tmpPtr;\r
113                                 }\r
114                                 if(devInfo.oc == PCI_OC_PCIBRIDGE)\r
115                                 {\r
116                                         #if LIST_DEVICES\r
117                                         Log("[PCI ] Bridge @ %i,%i:%i (0x%x:0x%x)",\r
118                                                 bus, dev, fcn, devInfo.vendor, devInfo.device);\r
119                                         #endif\r
120                                         giPCI_BusCount++;\r
121                                 }\r
122                                 devInfo.Node.Inode = giPCI_DeviceCount;\r
123                                 memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(t_pciDevice));\r
124                                 giPCI_DeviceCount ++;\r
125                                 #if LIST_DEVICES\r
126                                 Log("[PCI ] Device %i,%i:%i => 0x%x:0x%x",\r
127                                         bus, dev, fcn, devInfo.vendor, devInfo.device);\r
128                                 #endif\r
129                                 \r
130                                 // WTF is this for?\r
131                                 if(fcn == 0) {\r
132                                         if( !(devInfo.ConfigCache[3] & 0x800000) )\r
133                                                 break;\r
134                                 }\r
135                         }\r
136                         if(tmpPtr != gPCI_Devices)\r
137                                 break;\r
138                 }\r
139                 if(tmpPtr != gPCI_Devices)\r
140                         break;\r
141         }\r
142         tmpPtr = realloc(gPCI_Devices, giPCI_DeviceCount*sizeof(t_pciDevice));\r
143         if(tmpPtr == NULL)\r
144                 return 0;\r
145         gPCI_Devices = tmpPtr;\r
146         //LogF("Done.\n");\r
147         \r
148         // Complete Driver Structure    \r
149         gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount;\r
150         \r
151         // And add to DevFS\r
152         DevFS_AddDevice(&gPCI_DriverStruct);\r
153         \r
154         return 1;\r
155 }\r
156 \r
157 /**\r
158  * \fn char *PCI_ReadDirRoot(tVFS_Node *node, int pos)\r
159  * \brief Read from Root of PCI Driver\r
160 */\r
161 char *PCI_ReadDirRoot(tVFS_Node *node, int pos)\r
162 {       \r
163         if(pos < 0 || pos >= giPCI_DeviceCount)\r
164                 return NULL;\r
165         \r
166         return gPCI_Devices[pos].Name;\r
167 }\r
168 /**\r
169  * \fn tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename)\r
170  */\r
171 tVFS_Node *PCI_FindDirRoot(tVFS_Node *node, char *filename)\r
172 {\r
173          int    bus,slot,fcn;\r
174          int    i;\r
175         // Validate Filename (Pointer and length)\r
176         if(!filename || strlen(filename) != 7)\r
177                 return NULL;\r
178         // Check for spacers\r
179         if(filename[2] != '.' || filename[5] != ':')\r
180                 return NULL;\r
181         \r
182         // Get Information\r
183         if(filename[0] < '0' || filename[0] > '9')      return NULL;\r
184         bus = (filename[0] - '0')*10;\r
185         if(filename[1] < '0' || filename[1] > '9')      return NULL;\r
186         bus += filename[1] - '0';\r
187         if(filename[3] < '0' || filename[3] > '9')      return NULL;\r
188         slot = (filename[3] - '0')*10;\r
189         if(filename[4] < '0' || filename[4] > '9')      return NULL;\r
190         slot += filename[4] - '0';\r
191         if(filename[6] < '0' || filename[6] > '9')      return NULL;\r
192         fcn = filename[6] - '0';\r
193         \r
194         // Find Match\r
195         for(i=0;i<giPCI_DeviceCount;i++)\r
196         {\r
197                 if(gPCI_Devices[i].bus != bus)          continue;\r
198                 if(gPCI_Devices[i].slot != slot)        continue;\r
199                 if(gPCI_Devices[i].fcn != fcn)  continue;\r
200                 \r
201                 return &gPCI_Devices[i].Node;\r
202         }\r
203         \r
204         // Error Return\r
205         return NULL;\r
206 }\r
207 \r
208 /**\r
209  * \fn Uint64 PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer)\r
210  */\r
211 Uint64 PCI_ReadDevice(tVFS_Node *node, Uint64 pos, Uint64 length, void *buffer)\r
212 {       \r
213         if( pos + length > 256 )        return 0;\r
214         \r
215         memcpy(\r
216                 buffer,\r
217                 (char*)gPCI_Devices[node->Inode].ConfigCache + pos,\r
218                 length);\r
219         \r
220         return length;\r
221 }\r
222 \r
223 // --- Kernel Code Interface ---\r
224 /**\r
225  \fn int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn)\r
226  \brief Counts the devices with the specified codes\r
227  \param vendor  Vendor ID\r
228  \param device  Device ID\r
229  \param fcn     Function ID\r
230 */\r
231 int PCI_CountDevices(Uint16 vendor, Uint16 device, Uint16 fcn)\r
232 {\r
233         int i, ret=0;\r
234         for(i=0;i<giPCI_DeviceCount;i++)\r
235         {\r
236                 if(gPCI_Devices[i].vendor != vendor)    continue;\r
237                 if(gPCI_Devices[i].device != device)    continue;\r
238                 if(gPCI_Devices[i].fcn != fcn)  continue;\r
239                 ret ++;\r
240         }\r
241         return ret;\r
242 }\r
243 \r
244 /**\r
245  \fn int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx)\r
246  \brief Gets the ID of the specified PCI device\r
247  \param vendor  Vendor ID\r
248  \param device  Device ID\r
249  \param fcn     Function IDs\r
250  \param idx     Number of matching entry wanted\r
251 */\r
252 int PCI_GetDevice(Uint16 vendor, Uint16 device, Uint16 fcn, int idx)\r
253 {\r
254         int i, j=0;\r
255         for(i=0;i<giPCI_DeviceCount;i++)\r
256         {\r
257                 if(gPCI_Devices[i].vendor != vendor)    continue;\r
258                 if(gPCI_Devices[i].device != device)    continue;\r
259                 if(gPCI_Devices[i].fcn != fcn)  continue;\r
260                 if(j == idx)    return i;\r
261                 j ++;\r
262         }\r
263         return -1;\r
264 }\r
265 \r
266 /**\r
267  * \fn int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev)\r
268  * \brief Gets the ID of a device by it's class code\r
269  * \param class Class Code\r
270  * \param mask  Mask for class comparison\r
271  * \param prev  ID of previous device (-1 for no previous)\r
272  */\r
273 int PCI_GetDeviceByClass(Uint16 class, Uint16 mask, int prev)\r
274 {\r
275          int    i;\r
276         // Check if prev is negative (meaning get first)\r
277         if(prev < 0)    i = 0;\r
278         else    i = prev+1;\r
279         \r
280         for( ; i < giPCI_DeviceCount; i++ )\r
281         {\r
282                 if((gPCI_Devices[i].oc & mask) != class)        continue;\r
283                 return i;\r
284         }\r
285         return -1;\r
286 }\r
287 \r
288 /**\r
289  \fn Uint8 PCI_GetIRQ(int id)\r
290 */\r
291 Uint8 PCI_GetIRQ(int id)\r
292 {\r
293         if(id < 0 || id >= giPCI_DeviceCount)\r
294                 return 0;\r
295         return gPCI_Devices[id].ConfigCache[15];\r
296         //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C);\r
297 }\r
298 \r
299 /**\r
300  \fn Uint32 PCI_GetBAR0(int id)\r
301 */\r
302 Uint32 PCI_GetBAR0(int id)\r
303 {\r
304         if(id < 0 || id >= giPCI_DeviceCount)\r
305                 return 0;\r
306         return gPCI_Devices[id].ConfigCache[4];\r
307 }\r
308 \r
309 /**\r
310  \fn Uint32 PCI_GetBAR1(int id)\r
311 */\r
312 Uint32 PCI_GetBAR1(int id)\r
313 {\r
314         if(id < 0 || id >= giPCI_DeviceCount)\r
315                 return 0;\r
316         return gPCI_Devices[id].ConfigCache[5];\r
317 }\r
318 \r
319 /**\r
320  \fn Uint32 PCI_GetBAR2(int id)\r
321 */\r
322 Uint32 PCI_GetBAR2(int id)\r
323 {\r
324         if(id < 0 || id >= giPCI_DeviceCount)\r
325                 return 0;\r
326         return gPCI_Devices[id].ConfigCache[6];\r
327 }\r
328 \r
329 /**\r
330  \fn Uint32 PCI_GetBAR3(int id)\r
331 */\r
332 Uint32 PCI_GetBAR3(int id)\r
333 {\r
334         if(id < 0 || id >= giPCI_DeviceCount)\r
335                 return 0;\r
336         return gPCI_Devices[id].ConfigCache[7];\r
337 }\r
338 \r
339 /**\r
340  \fn Uint32 PCI_GetBAR4(int id)\r
341 */\r
342 Uint32 PCI_GetBAR4(int id)\r
343 {\r
344         if(id < 0 || id >= giPCI_DeviceCount)\r
345                 return 0;\r
346         return gPCI_Devices[id].ConfigCache[8];\r
347 }\r
348 \r
349 /**\r
350  \fn Uint32 PCI_GetBAR5(int id)\r
351 */\r
352 Uint32 PCI_GetBAR5(int id)\r
353 {\r
354         if(id < 0 || id >= giPCI_DeviceCount)\r
355                 return 0;\r
356         return gPCI_Devices[id].ConfigCache[9];\r
357 }\r
358 \r
359 Uint16 PCI_AssignPort(int id, int bar, int count)\r
360 {\r
361         Uint16  portVals;\r
362          int    gran=0;\r
363          int    i, j;\r
364         t_pciDevice     *dev;\r
365         \r
366         //LogF("PCI_AssignPort: (id=%i,bar=%i,count=%i)\n", id, bar, count);\r
367         \r
368         if(id < 0 || id >= giPCI_DeviceCount)   return 0;\r
369         if(bar < 0 || bar > 5)  return 0;\r
370         \r
371         dev = &gPCI_Devices[id];\r
372         \r
373         PCI_CfgWriteDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4, -1 );\r
374         portVals = PCI_CfgReadDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4 );\r
375         dev->ConfigCache[4+bar] = portVals;\r
376         //LogF(" PCI_AssignPort: portVals = 0x%x\n", portVals);\r
377         \r
378         // Check for IO port\r
379         if( !(portVals & 1) )   return 0;\r
380         \r
381         // Mask out final bit\r
382         portVals &= ~1;\r
383         \r
384         // Get Granuality\r
385         __asm__ __volatile__ ("bsf %%eax, %%ecx" : "=c" (gran) : "a" (portVals) );\r
386         gran = 1 << gran;\r
387         //LogF(" PCI_AssignPort: gran = 0x%x\n", gran);\r
388         \r
389         // Find free space\r
390         portVals = 0;\r
391         for( i = 0; i < 1<<16; i += gran )\r
392         {\r
393                 for( j = 0; j < count; j ++ )\r
394                 {\r
395                         if( gaPCI_PortBitmap[ (i+j)>>5 ] & 1 << ((i+j)&0x1F) )\r
396                                 break;\r
397                 }\r
398                 if(j == count) {\r
399                         portVals = i;\r
400                         break;\r
401                 }\r
402         }\r
403         \r
404         if(portVals)\r
405         {\r
406                 for( j = 0; j < count; j ++ )\r
407                 {\r
408                         if( gaPCI_PortBitmap[ (portVals+j)>>5 ] |= 1 << ((portVals+j)&0x1F) )\r
409                                 break;\r
410                 }\r
411                 PCI_CfgWriteDWord( dev->bus, dev->slot, dev->fcn, 0x10+bar*4, portVals|1 );\r
412                 dev->ConfigCache[4+bar] = portVals|1;\r
413         }\r
414         \r
415         // Return\r
416         //LogF("PCI_AssignPort: RETURN 0x%x\n", portVals);\r
417         return portVals;\r
418 }\r
419 \r
420 /**\r
421  * \fn int      PCI_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, t_pciDevice *info)\r
422  */\r
423 int     PCI_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, t_pciDevice *info)\r
424 {\r
425         Uint16  vendor;\r
426          int    i;\r
427         Uint32  addr;\r
428         \r
429         vendor = PCI_CfgReadWord(bus, slot, fcn, 0x0|0);\r
430         if(vendor == 0xFFFF)    // Invalid Device\r
431                 return 0;\r
432                 \r
433         info->bus = bus;\r
434         info->slot = slot;\r
435         info->fcn = fcn;\r
436         info->vendor = vendor;\r
437         info->device = PCI_CfgReadWord(bus, slot, fcn, 0x0|2);\r
438         info->revision = PCI_CfgReadWord(bus, slot, fcn, 0x8|0);\r
439         info->oc = PCI_CfgReadWord(bus, slot, fcn, 0x8|2);\r
440         \r
441         // Load Config Bytes\r
442         addr = 0x80000000 | ((Uint)bus<<16) | ((Uint)slot<<11) | ((Uint)fcn<<8);\r
443         for(i=0;i<256/4;i++)\r
444         {\r
445                 #if 1\r
446                 outd(0xCF8, addr);\r
447                 info->ConfigCache[i] = ind(0xCFC);\r
448                 addr += 4;\r
449                 #else\r
450                 info->ConfigCache[i] = PCI_CfgReadDWord(bus, slot, fcn, i*4);\r
451                 #endif\r
452         }\r
453         \r
454         //#if LIST_DEVICES\r
455         //Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]);\r
456         //Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]);\r
457         //Log("Class: 0x%04x", info->oc);\r
458         //#endif\r
459         \r
460         // Make node name\r
461         info->Name[0] = '0' + bus/10;\r
462         info->Name[1] = '0' + bus%10;\r
463         info->Name[2] = '.';\r
464         info->Name[3] = '0' + slot/10;\r
465         info->Name[4] = '0' + slot%10;\r
466         info->Name[5] = '.';\r
467         info->Name[6] = '0' + fcn;\r
468         info->Name[7] = '\0';\r
469         \r
470         // Create VFS Node\r
471         memset( &info->Node, 0, sizeof(tVFS_Node) );\r
472         info->Node.Size = 256;\r
473         \r
474         info->Node.NumACLs = 1;\r
475         info->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
476         \r
477         info->Node.Read = PCI_ReadDevice;\r
478         \r
479         return 1;\r
480 }\r
481 \r
482 Uint32 PCI_CfgReadDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset)\r
483 {\r
484         Uint32  address;\r
485         Uint32  data;\r
486         \r
487         bus &= 0xFF;    // 8 Bits\r
488         dev &= 0x1F;    // 5 Bits\r
489         func &= 0x7;    // 3 Bits\r
490         offset &= 0xFF; // 8 Bits\r
491         \r
492         address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC);\r
493         outd(0xCF8, address);\r
494         \r
495         data = ind(0xCFC);\r
496         return (Uint32)data;\r
497 }\r
498 void PCI_CfgWriteDWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset, Uint32 data)\r
499 {\r
500         Uint32  address;\r
501         \r
502         bus &= 0xFF;    // 8 Bits\r
503         dev &= 0x1F;    // 5 Bits\r
504         func &= 0x7;    // 3 Bits\r
505         offset &= 0xFF; // 8 Bits\r
506         \r
507         address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC);\r
508         outd(0xCF8, address);\r
509         outd(0xCFC, data);\r
510 }\r
511 Uint16 PCI_CfgReadWord(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset)\r
512 {\r
513         Uint32  data;\r
514         \r
515         bus &= 0xFF;    // 8 Bits\r
516         dev &= 0x1F;    // 5 Bits\r
517         func &= 0x7;    // 3 Bits\r
518         offset &= 0xFF; // 8 Bits\r
519         \r
520         //LogF("PCI_CfgReadWord: (bus=0x%x,dev=0x%x,func=%x,offset=0x%x)\n", bus, dev, func, offset);\r
521         \r
522         outd(0xCF8,\r
523                 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC) );\r
524         \r
525         data = ind(0xCFC);\r
526         data >>= (offset&2)*8;  //Allow Access to Upper Word\r
527         //LogF("PCI_CfgReadWord: RETURN 0x%x\n", data&0xFFFF);\r
528         return (Uint16)data;\r
529 }\r
530 \r
531 Uint8 PCI_CfgReadByte(Uint16 bus, Uint16 dev, Uint16 func, Uint16 offset)\r
532 {\r
533         Uint32  address;\r
534         Uint32  data;\r
535         \r
536         bus &= 0xFF;    // 8 Bits\r
537         dev &= 0x1F;    // 4 Bits\r
538         func &= 0x7;    // 3 Bits\r
539         offset &= 0xFF; // 8 Bits\r
540         \r
541         address = 0x80000000 | ((Uint)bus<<16) | ((Uint)dev<<11) | ((Uint)func<<8) | (offset&0xFC);\r
542         outd(0xCF8, address);\r
543         \r
544         data = ind(0xCFC);\r
545         data >>= (offset&3)*8;  //Allow Access to Upper Word\r
546         return (Uint8)data;\r
547 }\r
548 \r
549 \r
550 // === EXPORTS ===\r
551 /*\r
552 EXPORT(PCI_CountDevices);\r
553 EXPORT(PCI_GetDevice);\r
554 EXPORT(PCI_AssignPort);\r
555 EXPORT(PCI_GetIRQ);\r
556 */\r

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