3 * - By John Hodge (thePowersGang)
\r
6 * - PCI Enumeration and Arbitration
\r
10 #include <modules.h>
\r
12 #include <fs_devfs.h>
\r
13 #include <drv_pci.h>
\r
14 #include <drv_pci_int.h>
\r
15 #include <virtual_pci.h>
\r
17 #define USE_PORT_BITMAP 0
\r
18 #define LIST_DEVICES 1
\r
19 #define PCI_MAX_BUSSES 8
\r
21 // === STRUCTURES ===
\r
22 typedef struct sPCIDevice
\r
24 Uint16 bus, slot, fcn;
\r
25 Uint16 vendor, device;
\r
26 Uint32 class; // Class:Subclass:ProgIf
\r
28 Uint32 ConfigCache[256/4];
\r
33 // === CONSTANTS ===
\r
34 #define SPACE_STEP 5
\r
35 #define MAX_RESERVED_PORT 0xD00
\r
37 // === PROTOTYPES ===
\r
38 int PCI_Install(char **Arguments);
\r
39 int PCI_ScanBus(int ID, int bFill);
\r
41 int PCI_int_ReadDirRoot(tVFS_Node *node, int pos, char Dest[FILENAME_MAX]);
\r
42 tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename, Uint Flags);
\r
43 Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset);
\r
44 size_t PCI_int_ReadDevice(tVFS_Node *node, off_t Offset, size_t Length, void *buffer, Uint Flags);
\r
45 int PCI_int_EnumDevice(Uint16 bus, Uint16 dev, Uint16 fcn, tPCIDevice *info);
\r
48 MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL, NULL);
\r
49 int giPCI_BusCount = 1;
\r
50 Uint8 gaPCI_BusNumbers[PCI_MAX_BUSSES];
\r
51 int giPCI_InodeHandle = -1;
\r
52 int giPCI_DeviceCount = 0;
\r
53 tPCIDevice *gPCI_Devices = NULL;
\r
54 tVFS_NodeType gPCI_RootNodeType = {
\r
55 .TypeName = "PCI Root Node",
\r
56 .ReadDir = PCI_int_ReadDirRoot,
\r
57 .FindDir = PCI_int_FindDirRoot
\r
59 tVFS_NodeType gPCI_DevNodeType = {
\r
60 .TypeName = "PCI Dev Node",
\r
61 .Read = PCI_int_ReadDevice
\r
63 tDevFS_Driver gPCI_DriverStruct = {
\r
66 .Flags = VFS_FFLAG_DIRECTORY,
\r
69 .ACLs = &gVFS_ACL_EveryoneRX,
\r
70 .Type = &gPCI_RootNodeType
\r
74 Uint32 *gaPCI_PortBitmap = NULL;
\r
76 Uint32 gaPCI_BusBitmap[256/32];
\r
80 * \brief Scan the PCI Bus for devices
\r
81 * \param Arguments Boot-time parameters
\r
83 int PCI_Install(char **Arguments)
\r
90 gaPCI_PortBitmap = malloc( 1 << 13 );
\r
91 if( !gaPCI_PortBitmap ) {
\r
92 Log_Error("PCI", "Unable to allocate %i bytes for bitmap", 1 << 13);
\r
93 return MODULE_ERR_MALLOC;
\r
95 memset( gaPCI_PortBitmap, 0, 1 << 13 );
\r
97 for( i = 0; i < MAX_RESERVED_PORT / 32; i ++ )
\r
98 gaPCI_PortBitmap[i] = -1;
\r
99 for( i = 0; i < MAX_RESERVED_PORT % 32; i ++ )
\r
100 gaPCI_PortBitmap[MAX_RESERVED_PORT / 32] = 1 << i;
\r
103 // Scan Bus (Bus 0, Don't fill gPCI_Devices)
\r
104 giPCI_DeviceCount = 0;
\r
105 giPCI_BusCount = 1;
\r
106 gaPCI_BusNumbers[0] = 0;
\r
107 for( bus = 0; bus < giPCI_BusCount; bus ++ )
\r
109 ret = PCI_ScanBus(gaPCI_BusNumbers[bus], 0);
\r
110 if(ret != MODULE_ERR_OK) return ret;
\r
113 // - Add VPCI Devices
\r
114 giPCI_DeviceCount += giVPCI_DeviceCount;
\r
116 if(giPCI_DeviceCount == 0) {
\r
117 Log_Notice("PCI", "No devices were found");
\r
118 return MODULE_ERR_NOTNEEDED;
\r
121 // Allocate device buffer
\r
122 tmpPtr = malloc(giPCI_DeviceCount * sizeof(tPCIDevice));
\r
123 if(tmpPtr == NULL) {
\r
124 Log_Warning("PCI", "Malloc ERROR");
\r
125 return MODULE_ERR_MALLOC;
\r
127 gPCI_Devices = tmpPtr;
\r
129 Log_Log("PCI", "%i devices, filling structure", giPCI_DeviceCount);
\r
132 giPCI_DeviceCount = 0;
\r
133 giPCI_BusCount = 1;
\r
134 memset(gaPCI_BusBitmap, 0, sizeof(gaPCI_BusBitmap));
\r
135 // Rescan, filling the PCI device array
\r
136 for( bus = 0; bus < giPCI_BusCount; bus ++ )
\r
138 PCI_ScanBus(gaPCI_BusNumbers[bus], 1);
\r
140 // Insert VPCI Devices
\r
141 for( int i = 0; i < giVPCI_DeviceCount; i ++ )
\r
143 tPCIDevice *devinfo = &gPCI_Devices[giPCI_DeviceCount];
\r
148 devinfo->vendor = gaVPCI_Devices[i].Vendor;
\r
149 devinfo->device = gaVPCI_Devices[i].Device;
\r
150 devinfo->revision = gaVPCI_Devices[i].Class & 0xFF;
\r
151 devinfo->class = gaVPCI_Devices[i].Class >> 8;
\r
152 snprintf(devinfo->Name, sizeof(devinfo->Name), "%02x.%02x:%x", 0xFF, i, 0);
\r
155 Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x Rev %i",
\r
156 0xFF, i, 0, devinfo->class,
\r
157 devinfo->vendor, devinfo->device, devinfo->revision);
\r
160 for(int j = 0; j < 256/4; j ++ )
\r
161 devinfo->ConfigCache[j] = VPCI_Read(&gaVPCI_Devices[i], j*4, 4);
\r
163 memset(&devinfo->Node, 0, sizeof(devinfo->Node));
\r
164 devinfo->Node.Inode = giPCI_DeviceCount;
\r
165 devinfo->Node.Size = 256;
\r
166 devinfo->Node.NumACLs = 1;
\r
167 devinfo->Node.ACLs = &gVFS_ACL_EveryoneRO;
\r
168 devinfo->Node.Type = &gPCI_DevNodeType;
\r
170 giPCI_DeviceCount ++;
\r
173 // Complete Driver Structure
\r
174 gPCI_DriverStruct.RootNode.Size = giPCI_DeviceCount;
\r
176 // And add to DevFS
\r
177 DevFS_AddDevice(&gPCI_DriverStruct);
\r
179 return MODULE_ERR_OK;
\r
183 * \brief Scans a specific PCI Bus
\r
184 * \param BusID PCI Bus ID to scan
\r
185 * \param bFill Fill the \a gPCI_Devices array?
\r
187 int PCI_ScanBus(int BusID, int bFill)
\r
190 tPCIDevice devInfo;
\r
192 if( gaPCI_BusBitmap[BusID/32] & (1 << (BusID%32)) )
\r
193 return MODULE_ERR_OK;
\r
195 gaPCI_BusBitmap[BusID/32] |= (1 << (BusID%32));
\r
197 for( dev = 0; dev < 32; dev++ ) // 32 Devices per bus
\r
199 for( fcn = 0; fcn < 8; fcn++ ) // Max 8 functions per device
\r
201 // Check if the device/function exists
\r
202 if(!PCI_int_EnumDevice(BusID, dev, fcn, &devInfo))
\r
207 Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x Rev %i",
\r
208 BusID, dev, fcn, devInfo.class,
\r
209 devInfo.vendor, devInfo.device, devInfo.revision);
\r
213 devInfo.Node.Inode = giPCI_DeviceCount;
\r
214 memcpy(&gPCI_Devices[giPCI_DeviceCount], &devInfo, sizeof(tPCIDevice));
\r
216 giPCI_DeviceCount ++;
\r
218 switch( (devInfo.ConfigCache[3] >> 16) & 0x7F )
\r
220 case 0x00: // Normal device
\r
222 case 0x01: // PCI-PCI Bridge
\r
224 // TODO: Add to list of busses?
\r
225 Uint8 sec = (devInfo.ConfigCache[6] & 0x0000FF00) >> 8;
\r
228 Uint8 pri = (devInfo.ConfigCache[6] & 0x000000FF) >> 0;
\r
229 Log_Log("PCI", "- PCI-PCI Bridge %02x=>%02x", pri, sec);
\r
232 gaPCI_BusNumbers[giPCI_BusCount++] = sec;
\r
235 case 0x02: // PCI-CardBus Bridge
\r
239 // If bit 8 of the Header Type register is set, there are sub-functions
\r
240 if(fcn == 0 && !(devInfo.ConfigCache[3] & 0x00800000) )
\r
245 return MODULE_ERR_OK;
\r
249 * \brief Read from Root of PCI Driver
\r
251 int PCI_int_ReadDirRoot(tVFS_Node *Node, int Pos, char Dest[FILENAME_MAX])
\r
253 ENTER("pNode iPos", Node, Pos);
\r
254 if(Pos < 0 || Pos >= giPCI_DeviceCount) {
\r
255 LEAVE_RET('i', -EINVAL);
\r
258 LOG("Name = %s", gPCI_Devices[Pos].Name);
\r
259 strncpy(Dest, gPCI_Devices[Pos].Name, FILENAME_MAX);
\r
264 tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename, Uint Flags)
\r
267 for( int i = 0; i < giPCI_DeviceCount; i ++ )
\r
269 int cmp = strcmp(gPCI_Devices[i].Name, filename);
\r
270 if( cmp > 0 ) // Sorted list
\r
273 return &gPCI_Devices[i].Node;
\r
281 * \brief Read the PCI configuration space of a device
\r
283 size_t PCI_int_ReadDevice(tVFS_Node *node, off_t pos, size_t length, void *buffer, Uint Flags)
\r
285 if( pos + length > 256 ) return 0;
\r
289 (char*)gPCI_Devices[node->Inode].ConfigCache + pos,
\r
295 // --- Kernel Code Interface ---
\r
297 * \brief Counts the devices with the specified codes
\r
298 * \param vendor Vendor ID
\r
299 * \param device Device ID
\r
301 int PCI_CountDevices(Uint16 vendor, Uint16 device)
\r
304 for(i=0;i<giPCI_DeviceCount;i++)
\r
306 if(gPCI_Devices[i].vendor != vendor) continue;
\r
307 if(gPCI_Devices[i].device != device) continue;
\r
314 * \brief Gets the ID of the specified PCI device
\r
315 * \param vendor Vendor ID
\r
316 * \param device Device ID
\r
317 * \param idx Number of matching entry wanted
\r
319 tPCIDev PCI_GetDevice(Uint16 vendor, Uint16 device, int idx)
\r
322 for( i = 0; i < giPCI_DeviceCount; i ++ )
\r
324 if(gPCI_Devices[i].vendor != vendor) continue;
\r
325 if(gPCI_Devices[i].device != device) continue;
\r
326 if(j == idx) return i;
\r
333 * \brief Gets the ID of a device by it's class code
\r
334 * \param class Class Code
\r
335 * \param mask Mask for class comparison
\r
336 * \param prev ID of previous device (-1 for no previous)
\r
338 tPCIDev PCI_GetDeviceByClass(Uint32 class, Uint32 mask, tPCIDev prev)
\r
341 // Check if prev is negative (meaning get first)
\r
342 if(prev < 0) i = 0;
\r
345 for( ; i < giPCI_DeviceCount; i++ )
\r
347 if((gPCI_Devices[i].class & mask) == class)
\r
353 int PCI_GetDeviceInfo(tPCIDev ID, Uint16 *Vendor, Uint16 *Device, Uint32 *Class)
\r
355 tPCIDevice *dev = &gPCI_Devices[ID];
\r
356 if(ID < 0 || ID >= giPCI_DeviceCount) return 1;
\r
358 if(Vendor) *Vendor = dev->vendor;
\r
359 if(Device) *Device = dev->device;
\r
360 if(Class) *Class = dev->class;
\r
364 int PCI_GetDeviceVersion(tPCIDev ID, Uint8 *Revision)
\r
366 tPCIDevice *dev = &gPCI_Devices[ID];
\r
367 if(ID < 0 || ID >= giPCI_DeviceCount) return 1;
\r
369 if(Revision) *Revision = dev->revision;
\r
373 int PCI_GetDeviceSubsys(tPCIDev ID, Uint16 *SubsystemVendor, Uint16 *SubsystemID)
\r
375 tPCIDevice *dev = &gPCI_Devices[ID];
\r
376 if(ID < 0 || ID >= giPCI_DeviceCount) return 1;
\r
378 if(SubsystemVendor) *SubsystemVendor = dev->ConfigCache[0x2c/4] & 0xFFFF;
\r
379 if(SubsystemID) *SubsystemID = dev->ConfigCache[0x2c/4] >> 16;
\r
384 Uint32 PCI_int_GetBusAddr(Uint16 Bus, Uint16 Slot, Uint16 Fcn, Uint8 Offset)
\r
390 return ((Uint32)Bus << 16) | (Slot << 11) | (Fcn << 8) | (Offset & 0xFC);
\r
393 Uint32 PCI_ConfigRead(tPCIDev ID, int Offset, int Size)
\r
396 Uint32 dword, addr;
\r
398 if( ID < 0 || ID >= giPCI_DeviceCount ) return 0;
\r
399 if( Offset < 0 || Offset > 256 ) return 0;
\r
401 // TODO: Should I support non-aligned reads?
\r
402 if( Offset & (Size - 1) ) return 0;
\r
404 dev = &gPCI_Devices[ID];
\r
405 // Detect VPCI devices
\r
406 if( dev->bus == -1 ) {
\r
407 return VPCI_Read(&gaVPCI_Devices[dev->slot], Offset, Size);
\r
410 addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);
\r
412 dword = PCI_CfgReadDWord(addr);
\r
413 gPCI_Devices[ID].ConfigCache[Offset/4] = dword;
\r
416 case 1: return (dword >> (8 * (Offset&3))) & 0xFF;
\r
417 case 2: return (dword >> (8 * (Offset&2))) & 0xFFFF;
\r
418 case 4: return dword;
\r
424 void PCI_ConfigWrite(tPCIDev ID, int Offset, int Size, Uint32 Value)
\r
427 Uint32 dword, addr;
\r
430 if( ID < 0 || ID >= giPCI_DeviceCount ) return ;
\r
431 if( Offset < 0 || Offset > 256 ) return ;
\r
433 dev = &gPCI_Devices[ID];
\r
435 // Detect VPCI devices
\r
436 if( dev->bus == -1 ) {
\r
437 VPCI_Write(&gaVPCI_Devices[dev->slot], Offset, Size, Value);
\r
441 addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);
\r
444 dword = PCI_CfgReadDWord(addr);
\r
448 shift = (Offset&3)*8;
\r
449 dword &= ~(0xFF << shift);
\r
450 dword |= Value << shift;
\r
453 shift = (Offset&2)*8;
\r
454 dword &= ~(0xFFFF << shift);
\r
455 dword |= Value << shift;
\r
463 PCI_CfgWriteDWord(addr, dword);
\r
466 Uint16 PCI_SetCommand(tPCIDev id, Uint16 SBits, Uint16 CBits)
\r
468 tPCIDevice *dev = &gPCI_Devices[id];
\r
469 dev->ConfigCache[1] &= ~CBits;
\r
470 dev->ConfigCache[1] |= SBits;
\r
471 Uint32 addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, 1*4);
\r
472 PCI_CfgWriteDWord(addr, dev->ConfigCache[1]);
\r
473 dev->ConfigCache[1] = PCI_CfgReadDWord(addr);
\r
474 return dev->ConfigCache[1];
\r
478 * \brief Get the IRQ assigned to a device
\r
480 Uint8 PCI_GetIRQ(tPCIDev id)
\r
482 if(id < 0 || id >= giPCI_DeviceCount)
\r
484 return gPCI_Devices[id].ConfigCache[15] & 0xFF;
\r
485 //return PCI_CfgReadByte( gPCI_Devices[id].bus, gPCI_Devices[id].slot, gPCI_Devices[id].fcn, 0x3C);
\r
489 * \brief Read the a BAR (base address register) from the PCI config space
\r
491 Uint32 PCI_GetBAR(tPCIDev id, int BARNum)
\r
493 if(id < 0 || id >= giPCI_DeviceCount)
\r
495 if(BARNum < 0 || BARNum >= 6)
\r
497 return gPCI_Devices[id].ConfigCache[4+BARNum];
\r
502 Uint64 PCI_GetValidBAR(tPCIDev ID, int BARNum, tPCI_BARType Type)
\r
504 if( ID < 0 || ID >= giPCI_DeviceCount )
\r
506 if( BARNum < 0 || BARNum >= 6 )
\r
508 tPCIDevice *dev = &gPCI_Devices[ID];
\r
509 Uint32 bar_val = dev->ConfigCache[4+BARNum];
\r
513 case PCI_BARTYPE_IO:
\r
514 if( !(bar_val & 1) )
\r
516 ret = bar_val & ~3;
\r
519 case PCI_BARTYPE_MEMNP:
\r
520 if( bar_val & 8 ) return 0;
\r
522 case PCI_BARTYPE_MEMP:
\r
523 if( !(bar_val & 8) ) return 0;
\r
524 case PCI_BARTYPE_MEM:
\r
525 if( bar_val & 1 ) return 0;
\r
526 if( (bar_val & 6) == 4 ) {
\r
527 ASSERTCR(BARNum, <, 5, 0);
\r
528 ret = (bar_val & ~0xF) | ((Uint64)dev->ConfigCache[4+BARNum+1] << 32);
\r
531 ret = bar_val & ~0xF;
\r
534 case PCI_BARTYPE_MEM32:
\r
535 if( bar_val & 1 ) return 0;
\r
536 if( (bar_val & 6) != 0 ) return 0;
\r
537 ret = bar_val & ~0xF;
\r
539 case PCI_BARTYPE_MEM64:
\r
540 if( bar_val & 1 ) return 0;
\r
541 if( (bar_val & 6) != 4 ) return 0;
\r
542 ASSERTCR(BARNum, <, 5, 0);
\r
543 ret = (bar_val & ~0xF) | ((Uint64)dev->ConfigCache[4+BARNum+1] << 32);
\r
548 Log_Error("PCI", "PCI%i BAR%i correct type, but unallocated (0x%x)",
\r
549 ID, BARNum, bar_val);
\r
556 * \brief Get device information for a slot/function
\r
558 int PCI_int_EnumDevice(Uint16 bus, Uint16 slot, Uint16 fcn, tPCIDevice *info)
\r
560 Uint32 vendor_dev, tmp;
\r
563 addr = PCI_int_GetBusAddr(bus, slot, fcn, 0);
\r
565 vendor_dev = PCI_CfgReadDWord( addr );
\r
566 if((vendor_dev & 0xFFFF) == 0xFFFF) // Invalid Device
\r
573 // Read configuration
\r
574 info->ConfigCache[0] = vendor_dev;
\r
575 for( i = 1, addr += 4; i < 256/4; i ++, addr += 4 )
\r
577 info->ConfigCache[i] = PCI_CfgReadDWord(addr);
\r
580 info->vendor = vendor_dev & 0xFFFF;
\r
581 info->device = vendor_dev >> 16;
\r
582 tmp = info->ConfigCache[2];
\r
583 info->revision = tmp & 0xFF;
\r
584 info->class = tmp >> 8;
\r
586 // #if LIST_DEVICES
\r
587 // Log("BAR0 0x%08x BAR1 0x%08x BAR2 0x%08x", info->ConfigCache[4], info->ConfigCache[5], info->ConfigCache[6]);
\r
588 // Log("BAR3 0x%08x BAR4 0x%08x BAR5 0x%08x", info->ConfigCache[7], info->ConfigCache[8], info->ConfigCache[9]);
\r
589 // Log("Class: 0x%06x", info->class);
\r
593 snprintf(info->Name, 8, "%02x.%02x:%x", bus, slot, fcn);
\r
596 memset( &info->Node, 0, sizeof(tVFS_Node) );
\r
597 info->Node.Size = 256;
\r
599 info->Node.NumACLs = 1;
\r
600 info->Node.ACLs = &gVFS_ACL_EveryoneRO;
\r
602 info->Node.Type = &gPCI_DevNodeType;
\r
609 EXPORT(PCI_CountDevices);
\r
610 EXPORT(PCI_GetDevice);
\r
611 EXPORT(PCI_GetDeviceByClass);
\r
612 EXPORT(PCI_GetDeviceInfo);
\r
613 EXPORT(PCI_GetDeviceVersion);
\r
614 EXPORT(PCI_GetDeviceSubsys);
\r
615 //EXPORT(PCI_AssignPort);
\r
616 EXPORT(PCI_GetBAR);
\r
617 EXPORT(PCI_GetIRQ);
\r