Web resources
The official PCI web site is located at www.pcisig.com
This PCI Special Interest Group site contains a little information on PCI as Microsoft Word 6 document files which are the official specifications, it also contains a few test/diagnostic programs in C for working with PCI that you can download and also they keep a list of Vendor ID numbers but not product ID numbers.
- Craig "Merlin" Hart's PCI page
http://members.datafast.net.au/dft0802/ - Jim Boemler's PCI page http://www.halcyon.com/scripts/jboemler/pci/pcicode looks broken and finally points to
http://www.pcidatabase.com/ - RalfBrown's page
http://www.cs.cmu.edu/afs/cs.cmu.edu/user/ralf/pub/WWW/files.html - Christopher Griese's page
http://my.execpc.com/~geezer/osd/pnp/index.htm, which includes code snippets for both direct, BIOS16 and BIOS32 access to PCI configuration. - The Linux Documentation Project has a very nice guide which give you a quick overview of how PCI devices can be enumerated and identified.
http://www.tldp.org/LDP/tlk/dd/pci.html, mirrored
here.
Recommended books on PCI Programming
The two books below are based on the PCI spec and supply much information about device types, interrupt rerouting, Hot-plug /Power management specs etc.
PCI System Architecture - from Mindshare Inc
by Tom Shanley and Don Anderson
ISBN 0-201-30974-2 ($US 40 in 2004)
publisher Addison Wesley
PCI-X System Architecture - from Mindshare Inc
by Tom Shanley
ISBN 0-201-72682-3 ($US 45 in 2004)
publisher Addison Wesley
What can i expect from the PCI docs ?
The PCI documentation tells us how one can list the PCI hardware (both extension card and built-in components such as hard drive controllers, PCI-to-PCMCIA bridges, etc.) and what resources that hardware use for communication with the CPU. However, how you should use those resources to program the hardware depends on the very hardware you try to program (i.e. PCI has no info about how AC97 pci soundcard works: it can just tell you that it is a soundcard and that it will use memory-mapped region [ 0x1234..0x5678 ] and interrupt 10 to communicate with you.
Do I have to pay $xxx for an open-standard specification
Official PCI documents (PCI_Local_Bus_Spec_v2.x.pdf) unfortunately come at a high fee. Free-pirate hosting links are not welcome here (of course). Before ordering the documents, keep in mind that they're describing the whole standard, not just the programming information you need. That means you'd end up with a 300+ pages hard-cover book full of electric & mechanics specifications, bus timing, burst frame structure etc. which are completely meaningless for you.
What should a 'PCI core' component offer ?
Primitives for accessing configuration space
- Quoting 'PCICFG.TXT' from RalfBrown PCI utility
- PCI devices have an address which is broken down into a PCI-bus number (usually 0), a device number within that bus (0-31), and a function number within the device (0-7). (...) There are two incompatible methods for accessing the ports; on most chipsets, -b1 is the proper method ("PCI Configuration Space Access Mechanism #1"). On some chipsets (mostly the earliest ones supporting PCI), you will need to use -b2 ("PCI Configuration Space Access Mechanism #2"). USING THE INCORRECT METHOD CAN HANG OR RESET YOUR COMPUTER!, which explains why using BIOS service for accessing PCI makes sense ...
Configuration mode 2 is deprecated, afaik, so it won't be covered here. The following code snippet (under RalfBrown tutorial license ;) reads the value of register reg on bus:device:func pci space. Accessing a datum in PCI mechanism 1 consist of sending its 'address' on port 0xCF8 and then either reading or writing the value through port 0xcfc.
DWORD addr = 0x80000000L | (((DWORD)(bus & 0xFF)) << 16) |
((((unsigned)device) & 0x1F) << 11) |
((((unsigned)func) & 0x07) << 8) | (reg & 0xFC) ;
DWORD orig = inp(0xCF8) ; // get current state
outpd(0xCF8,addr) ; // set up addressing to config data
value = inpd(0xCFC) ; // get requested DWORD of config data
outpd(0xCF8,orig) ; // restore configuration control
note: PCI bios interface do have them
Bus enumeration
There are two possible approach for device/driver handling:
- driver push
- the user tells the system to use the driver and the system check there's a device here. This check requires that you scan devices, looking for a specific vendor/subclass.
- driver pull
- the system scans for devices and loads drivers for devices that exists.
So in both approach, enumeration is required. The basics of devices enumeration consists of probing each bus:device:function in the configuration space and see if we have something consistent.
- if the device does not exists, reading the PCI_VENDORID field (word at 0x00) will return either 0x0000 or =0xffff, which are both invalid vendors.
- if the device exists and has only one function, the PCI_HEADER_TYPE (byte at 0x0E) will have it's highest bit cleared.
- if the device exists and has multiple functions, the PCI_HEADER_TYPE has its highest bit set. In that case, the scan is repeated for all the 8 functions
- There is a maximum of 8 PCI busses. Initially, the scan starts on bus #0. Some devices may act as bridges. This can be detected because PCI_HEADER_TYPE has the value 1. Bridges have a byte at 0x1a that tells the highest bus number behind the bridge.
Note that devices does not necessarily have contiguous numbers. E.g. a bus may have device 4 present even if there are no devices 1, 2, or 3. This is especially common on the bus #0 due to on-chip devices.
// disclaimer: this is untested code summarizing
// http://my.execpc.com/~geezer/osd/pnp/pci32.c
// it doesn't take pci-to-pci bridges into account
// and blindly scans all the possible busses.
int nb_bus=1;
for (bus=0;bus<nb_bus;bus++) {
for (dev=0;dev<64;dev++) {
byte htype=pciGetCfgByte(bus,dev,fn,PCI_HEADER_TYPE);
word vendor_id=pciGetCfgWord(bus,dev,0,PCI_VENDORID);
int nbfuncs=1;
if (vendor_id==0 || vendor_id==0xffff) continue;
if (htype & 0x80) nbfuncs=8;
for (fn=0;fn<nbfuncs;fn++) {
/** we have a device. Get vendor (0x00:2), product (0x02:2), class (0x09:3)
* information and create a DeviceNfo with it for later retrieval
* Don't forget to insert bus:dev:fn in the DeviceNfo so that we don't need
* to bother with enumeration again
*/
}
}
}
Primitives for decoding BARs
Address registers (telling what memory range or I/O range is used by the device) may not be immediate to use. According to
sigops's chapter, the lowest bit you read will tell you if you're facing memory (e.g. bit0==0) or IO resource (e.g. bit0==1). You can tell the size of the region by writing 0xffffffff in the BAR and reading back. Once you're done, don't forget to write back the old value.
void pciDecodeBar(pci_device* pci, pci_bar_register bar)
{
unsigned type = pciReadDword(pci, bar) & 0x0f;
unsigned addr = pciReadDword(pci, bar) & ~0x0f;
unsigned size;
pciWriteDword(pci, bar, 0xffffffff);
size = pciReadDword(pci, bar) & 0xfffffff0;
pciWriteDword(pci, bar, addr|type);
size = 1 + ~size;
kprint(((type&1)?"memory %8x..%8x, %s":"i/o %4x..%4x"),
addr,addr+size,(type&8)?"prefetchable":"no prefetch");
}
``BARs'' ? You mean i should go for a drink ?
For auto-configuration, PCI has has a special space called "PCI Configuration Space". This space is accessed using I/O ports. There's 2 different configuration space access mechanisms, but for the most common one you set the address in one I/O port (the bus, device, function and offset) and read or write the data in another I/O port.
Anyway, in each PCI device's configuration space there's normally one or more BARs (or "Base Address Registers"), which can be used to set or find the address (in physical memory or in I/O space) for each resource the card uses.
The BARs could be used for memory mapped I/O, I/O port ranges and/or a boot ROM.
They aren't used for IRQs though - there's different fields in PCI configuration space for that, one for the interrupt line the card uses (which is a fixed/static flag) and another for which IRQ it is routed to (which is set by the BIOS during boot, and only exists as a means to pass this information to operating systems - the hardware itself ignores it).
Categories: CollectedKnowledge, HardWareBus
related threads
PCI related
,
calling BIOS32 service,
finding PCI device,
Clicker 0.8.20 -- pci
most of the links posted on these threads looks broken, though (unfortunately)
additionnal material
http://www.cs.cmu.edu/ralf/pub-files/rbpci119.zip
http://www.acm.uiuc.edu/sigops/roll_your_own/7.c.0.html
whyme's pci driver for Clicker32
there was an article called "pentium_vme_article.pdf" formerly on the Web, but it disappeared. PciSectionOfPentiumVme is revived here ...
