MODULE PCI;
IMPORT SYSTEM, Machine, KernelLog;
CONST
Trace = TRUE;
PciAdrReg=0CF8H;
PciDataReg = 0CFCH;
DevReg* = 0H; CmdReg* = 4H; RevIdReg* = 8H; CLSReg* = 0CH;
Adr0Reg* = 10H; Adr1Reg* = 14H; Adr2Reg* = 18H;
Adr3Reg* = 1CH; Adr4Reg* = 20H; Adr5Reg* = 24H;
CISReg* = 28H; SubvReg* = 2CH; ROMReg* = 30H; IntlReg* = 3CH;
IOSpace* = {0};
MemorySpace* = {1};
BusMaster* = {2};
debug = TRUE;
Done* = 0; NoPCI* = -1; NoBios32* = -1; Error* = -2;
FuncNotSupported* = 81H; BadVendorId* = 83H; DeviceNotFound* = 86H;
BadRegisterNumber* = 87H; SetFailed* = 88H; BufferTooSmall* = 89H;
PCIServiceId = 49435024H;
PCIString = 20494350H;
PCIFunctionId = 0B1H*256;
PCIBiosPresent = 1H; findPCIDevice = 2H; findPCIClassCode = 3H; generateSpecialCycle = 6H;
readConfigByte = 8H; readConfigWord = 9H; readConfigDword = 0AH;
writeConfigByte = 0BH; writeConfigWord = 0CH; writeConfigDword = 0DH;
getIrqRoutingOptions = 0EH; setPCIIrq = 0FH;
TYPE
RouteTable* = POINTER TO RouteTableDesc;
RouteTableDesc* = RECORD
busNr*, devNr*, slotNr*: LONGINT;
linkValIntA*, linkValIntB*, linkValIntC*, linkValIntD*: CHAR;
IrqBitmapA*, IrqBitmapB*, IrqBitmapC*, IrqBitmapD*: SET;
next*: RouteTable
END;
Pci = RECORD bus, device, function: LONGINT END;
VAR
pciEnabled: BOOLEAN;
PROCEDURE PCIPresent*(VAR version, lastPCIbus, hwMech: LONGINT): LONGINT;
VAR res: LONGINT; pci: Pci; r0: LONGINT;
BEGIN {EXCLUSIVE}
IF pciEnabled THEN
StartIterate(pci);
lastPCIbus := 0;
REPEAT
res := PCIReadConfig32(pci.bus, pci.device, pci.function, 0, r0);
IF r0 # LONGINT(0FFFFFFFFH) THEN
IF lastPCIbus < pci.bus THEN lastPCIbus := pci.bus END;
END;
UNTIL ~Iterate(pci);
res := Done;
IF debug THEN
KernelLog.String("PCIPresent, lastbus ="); KernelLog.Int(lastPCIbus,1); KernelLog.Ln;
END
ELSE
res := NoPCI
END;
RETURN res
END PCIPresent;
PROCEDURE FindPCIDevice*(devId, vendId, idx: LONGINT; VAR busNr, devNr, fktNr: LONGINT): LONGINT;
VAR pci: Pci; r0, vendorId, deviceId, index,res: LONGINT;
BEGIN {EXCLUSIVE}
IF pciEnabled THEN
StartIterate(pci); index := 0;
REPEAT
res := PCIReadConfig32(pci.bus, pci.device, pci.function, 0, r0);
IF r0 # LONGINT(0FFFFFFFFH) THEN
vendorId := r0 MOD 10000H;
deviceId := r0 DIV 10000H;
IF (devId = deviceId) & (vendId = vendorId) THEN
IF idx = index THEN
busNr := pci.bus; devNr := pci.device; fktNr := pci.function;
IF debug THEN
KernelLog.String("FindPCIDevice ");
KernelLog.Int(devId,1); KernelLog.String(", ");
KernelLog.Int(vendId,1); KernelLog.String(",");
KernelLog.Int(idx,1);
KernelLog.String(" found."); KernelLog.Ln;
END;
RETURN Done
ELSE INC(index)
END;
END;
END;
UNTIL ~Iterate(pci);
res := DeviceNotFound
ELSE
res := NoPCI
END;
RETURN res
END FindPCIDevice;
PROCEDURE FindPCIClassCode*(classCode, idx: LONGINT; VAR busNr, devNr, fktNr: LONGINT): LONGINT;
VAR pci: Pci; r0, r8, index,res,class: LONGINT;
BEGIN {EXCLUSIVE}
IF pciEnabled THEN
StartIterate(pci);
REPEAT
res := PCIReadConfig32(pci.bus, pci.device, pci.function, 0, r0);
IF r0 # LONGINT(0FFFFFFFFH) THEN
res := PCIReadConfig32(pci.bus, pci.device, pci.function, 8, r8);
class := r8 DIV 100H MOD 1000000H;
IF (classCode = class) THEN
IF idx = index THEN
busNr := pci.bus; devNr := pci.device; fktNr := pci.function;
IF debug THEN
KernelLog.String("FindPCIClassCode ");
KernelLog.Int(classCode,1); KernelLog.String(","); KernelLog.Int(idx,1);
KernelLog.String(" found."); KernelLog.Ln;
END;
RETURN Done
ELSE INC(index)
END;
END;
END;
UNTIL ~Iterate(pci);
res := DeviceNotFound;
ELSE
res := NoPCI
END;
RETURN res
END FindPCIClassCode;
PROCEDURE GenerateSpecialCycle*(busNr, specCycleData: LONGINT): LONGINT;
VAR res: LONGINT;
BEGIN {EXCLUSIVE}
IF pciEnabled THEN
ELSE
res := NoPCI
END;
RETURN res
END GenerateSpecialCycle;
PROCEDURE GetIrqRoutingOptions*(VAR rt: RouteTable; VAR IrqBitmap: SET): LONGINT;
CONST dbN = 16*8;
VAR
res: LONGINT;
BEGIN {EXCLUSIVE}
IF pciEnabled THEN
ELSE
res := NoPCI
END;
RETURN res
END GetIrqRoutingOptions;
PROCEDURE SetPCIIrq*(IntPin, IrqNum, busNr, devNr, fktNr: LONGINT): LONGINT;
VAR res:LONGINT;
BEGIN {EXCLUSIVE}
IF pciEnabled THEN
ELSE
res := NoPCI
END;
RETURN res
END SetPCIIrq;
PROCEDURE Enable*(mask : SET; busNr, devNr, fktNr : LONGINT) : LONGINT;
VAR cmdReg : LONGINT; res : LONGINT;
BEGIN
res := ReadConfigWord(busNr, devNr, fktNr, CmdReg, cmdReg);
IF (res = Done) THEN
IF mask - SYSTEM.VAL(SET, cmdReg) # {} THEN
cmdReg := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, cmdReg) + mask);
res := WriteConfigWord(busNr, devNr, fktNr, CmdReg, cmdReg);
IF (res = Done) THEN
res := ReadConfigWord(busNr, devNr, fktNr, CmdReg, cmdReg);
IF (res = Done) THEN
IF mask - SYSTEM.VAL(SET, cmdReg) # {} THEN
res := Error;
END;
END;
END;
END;
END;
RETURN res;
END Enable;
PROCEDURE ReadConfigByte*(busNr, devNr, fktNr, regNr: LONGINT; VAR regVal: LONGINT): LONGINT;
BEGIN
RETURN PCIReadConfig8(busNr, devNr, fktNr, regNr, regVal)
END ReadConfigByte;
PROCEDURE ReadConfigWord*(busNr, devNr, fktNr, regNr: LONGINT; VAR regVal: LONGINT): LONGINT;
BEGIN
ASSERT(regNr MOD 2 = 0);
RETURN PCIReadConfig16(busNr, devNr, fktNr, regNr, regVal)
END ReadConfigWord;
PROCEDURE ReadConfigDword*(busNr, devNr, fktNr, regNr: LONGINT; VAR regVal: LONGINT): LONGINT;
BEGIN
ASSERT(regNr MOD 4 = 0);
RETURN PCIReadConfig32(busNr, devNr, fktNr, regNr, regVal)
END ReadConfigDword;
PROCEDURE WriteConfigByte*(busNr, devNr, fktNr, regNr, regVal: LONGINT): LONGINT;
BEGIN
RETURN PCIWriteConfig8(busNr, devNr, fktNr, regNr, regVal)
END WriteConfigByte;
PROCEDURE WriteConfigWord*(busNr, devNr, fktNr, regNr, regVal: LONGINT): LONGINT;
BEGIN
ASSERT(regNr MOD 2 = 0);
RETURN PCIWriteConfig16(busNr, devNr, fktNr, regNr, regVal)
END WriteConfigWord;
PROCEDURE WriteConfigDword*(busNr, devNr, fktNr, regNr, regVal: LONGINT): LONGINT;
BEGIN
ASSERT(regNr MOD 4 = 0);
RETURN PCIWriteConfig32(busNr, devNr, fktNr, regNr, regVal)
END WriteConfigDword;
PROCEDURE Show*;
VAR version, lastPCIBus, hwMech, res : LONGINT;
BEGIN
IF ~PCIDisabled() THEN
res := PCIPresent(version, lastPCIBus, hwMech);
IF (res = Done) THEN
KernelLog.Enter;
KernelLog.String("PCI: "); KernelLog.Int(lastPCIBus + 1, 0); KernelLog.String(" bus(ses) found, PCI version: ");
KernelLog.Hex(version DIV 256, -2); KernelLog.Char("."); KernelLog.Hex(version MOD 256, -2);
KernelLog.Exit;
ELSE
KernelLog.Enter; KernelLog.String("PCI: No bus found."); KernelLog.Exit;
END;
ELSE
KernelLog.Enter; KernelLog.String("PCI: Not available (Disabled by user)."); KernelLog.Exit;
END;
END Show;
PROCEDURE GetAdr1(pciBus, pciDev, pciFn, reg, len: LONGINT; VAR adr, dataAdr: LONGINT);
BEGIN
adr := LONGINT(80000000H) + ASH(pciBus,16) + ASH(pciDev, 11) + ASH(pciFn, 8);
adr := adr + SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET,reg) * SYSTEM.VAL(SET,0FCH));
adr := adr + ASH(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET,reg) * SYSTEM.VAL(SET,0F00H)), 16);
CASE len OF
8: dataAdr := PciDataReg + SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET,reg) * SYSTEM.VAL(SET,3));
|16: dataAdr := PciDataReg + SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET,reg) * SYSTEM.VAL(SET,2));
|32: dataAdr := PciDataReg;
END;
END GetAdr1;
PROCEDURE PCIReadConfig32(pciBus, pciDev, pciFn, reg: LONGINT; VAR val: LONGINT): LONGINT;
VAR adr, dataAdr: LONGINT; state: SET; res: LONGINT;
BEGIN
IF pciEnabled THEN
state := Machine.DisableInterrupts();
GetAdr1(pciBus, pciDev, pciFn, reg, 32, adr, dataAdr);
Machine.Portout32(PciAdrReg, adr);
Machine.Portin32(dataAdr, val);
Machine.RestoreInterrupts(state);
res := Done;
ELSE
res := NoPCI
END;
RETURN res
END PCIReadConfig32;
PROCEDURE PCIReadConfig16(pciBus, pciDev, pciFn, reg: LONGINT; VAR val: LONGINT): LONGINT;
VAR adr, dataAdr: LONGINT; state: SET;res: LONGINT; int: INTEGER;
BEGIN
IF pciEnabled THEN
state := Machine.DisableInterrupts();
GetAdr1(pciBus, pciDev, pciFn, reg, 16, adr, dataAdr);
Machine.Portout32(PciAdrReg, adr);
Machine.Portin16(dataAdr, int);
val := int;
Machine.RestoreInterrupts(state);
res := Done;
ELSE
res := NoPCI
END;
RETURN res
END PCIReadConfig16;
PROCEDURE PCIReadConfig8(pciBus, pciDev, pciFn, reg: LONGINT; VAR val: LONGINT): LONGINT;
VAR adr, dataAdr: LONGINT; state: SET;res: LONGINT; chr: CHAR;
BEGIN
IF pciEnabled THEN
state := Machine.DisableInterrupts();
GetAdr1(pciBus, pciDev, pciFn, reg, 8, adr, dataAdr);
Machine.Portout32(PciAdrReg, adr);
Machine.Portin8(dataAdr, chr);
val := ORD(chr);
Machine.RestoreInterrupts(state);
res := Done;
ELSE
res := NoPCI
END;
RETURN res
END PCIReadConfig8;
PROCEDURE PCIWriteConfig32(pciBus, pciDev, pciFn: LONGINT; reg: LONGINT; val: LONGINT): LONGINT;
VAR adr, dataAdr: LONGINT; state: SET;res: LONGINT;
BEGIN
IF pciEnabled THEN
state := Machine.DisableInterrupts();
GetAdr1(pciBus, pciDev, pciFn, reg, 32, adr, dataAdr);
Machine.Portout32(PciAdrReg, adr);
Machine.Portout32(dataAdr, val);
Machine.RestoreInterrupts(state);
res := Done;
ELSE
res := NoPCI
END;
RETURN res
END PCIWriteConfig32;
PROCEDURE PCIWriteConfig16(pciBus, pciDev, pciFn: LONGINT; reg: LONGINT; val: LONGINT): LONGINT;
VAR adr, dataAdr: LONGINT; state: SET;res: LONGINT;
BEGIN
IF pciEnabled THEN
state := Machine.DisableInterrupts();
GetAdr1(pciBus, pciDev, pciFn, reg, 16, adr, dataAdr);
Machine.Portout32(PciAdrReg, adr);
Machine.Portout16(dataAdr, SHORT(val));
Machine.RestoreInterrupts(state);
res := Done;
ELSE
res := NoPCI
END;
RETURN res
END PCIWriteConfig16;
PROCEDURE PCIWriteConfig8(pciBus, pciDev, pciFn: LONGINT; reg: LONGINT; val: LONGINT): LONGINT;
VAR adr, dataAdr: LONGINT; state: SET;res: LONGINT;
BEGIN
IF pciEnabled THEN
state := Machine.DisableInterrupts();
GetAdr1(pciBus, pciDev, pciFn, reg, 8, adr, dataAdr);
Machine.Portout32(PciAdrReg, adr);
Machine.Portout8(dataAdr, CHR(val));
Machine.RestoreInterrupts(state);
res := Done;
ELSE
res := NoPCI
END;
RETURN res
END PCIWriteConfig8;
PROCEDURE PCICheckType1(): BOOLEAN;
VAR in,temp: LONGINT; works: BOOLEAN; state: SET;
BEGIN
state := Machine.DisableInterrupts();
Machine.Portout8(PciDataReg, 1X);
Machine.Portin32(PciAdrReg, temp);
Machine.Portout32(PciAdrReg, LONGINT(80000000H));
Machine.Portin32(PciAdrReg, in);
IF in = LONGINT(80000000H) THEN works := TRUE ELSE works := FALSE END;
Machine.Portout32(PciAdrReg, temp);
Machine.RestoreInterrupts(state);
RETURN works
END PCICheckType1;
PROCEDURE StartIterate(VAR pci: Pci);
BEGIN pci.bus := 0; pci.device := 0; pci.function := 0
END StartIterate;
PROCEDURE Iterate(VAR pci: Pci): BOOLEAN;
VAR hdrType,res: LONGINT;
BEGIN
IF pci.function = 0 THEN
res := PCIReadConfig8(pci.bus, pci.device, pci.function, 0EH, hdrType);
END;
INC(pci.function);
IF (pci.function >= 8) OR ~(7 IN SYSTEM.VAL(SET,hdrType)) THEN
pci.function := 0;
INC(pci.device);
IF pci.device >= 32 THEN
pci.device := 0;
INC(pci.bus);
IF pci.bus >= 8 THEN RETURN FALSE END;
END;
END;
RETURN TRUE
END Iterate;
PROCEDURE DisplayDeviceClass(class, subclass: LONGINT);
BEGIN
CASE class OF
1: KernelLog.String("disk controller:");
CASE subclass OF
0: KernelLog.String("SCSI")
|1: KernelLog.String("IDE")
|2: KernelLog.String("floppy");
|3: KernelLog.String("IPI");
|4: KernelLog.String("RAID");
|80H: KernelLog.String("Other ");
ELSE KernelLog.String("unkown")
END;
|2: KernelLog.String("network controller:");
CASE subclass OF
0: KernelLog.String("Ethernet");
|1: KernelLog.String("Token ring");
|2: KernelLog.String("FDDI");
|3: KernelLog.String("ATM");
|80H: KernelLog.String("other");
ELSE KernelLog.String("unknown");
END;
|3: KernelLog.String("display controller:");
CASE subclass OF
0: KernelLog.String("VGA");
|1: KernelLog.String("XGA");
|80H: KernelLog.String("other");
ELSE KernelLog.String("unknown");
END;
|4: KernelLog.String("multimedia controller:");
CASE subclass OF
0: KernelLog.String("Video");
|1: KernelLog.String("Audio");
|80H: KernelLog.String("other");
ELSE KernelLog.String("unknown");
END;
|5: KernelLog.String("memory:");
CASE subclass OF
0: KernelLog.String("RAM");
|1: KernelLog.String("Flash");
|80H: KernelLog.String("other");
ELSE KernelLog.String("unknown");
END;
|6: KernelLog.String("bridge:");
CASE subclass OF
0: KernelLog.String("Host/PCI")
|1: KernelLog.String("PCI/ISA")
|2: KernelLog.String("PCI/EISA");
|3: KernelLog.String("PCI/Microchannel");
|4: KernelLog.String("PCI/PCI");
|5: KernelLog.String("PCI/PCMCIA");
|6: KernelLog.String("PCI/NuBus");
|7: KernelLog.String("PCI/CardBus");
|80H: KernelLog.String("Other ");
ELSE KernelLog.String("unkown")
END;
|7: KernelLog.String("communications device:");
CASE subclass OF
0: KernelLog.String("Serial")
|1: KernelLog.String("Parallel")
|80H: KernelLog.String("Other ");
ELSE KernelLog.String("unkown")
END;
|8: KernelLog.String("system device:");
CASE subclass OF
0: KernelLog.String("PIC")
|1: KernelLog.String("DMA")
|2: KernelLog.String("Timer")
|3: KernelLog.String("RTC")
|80H: KernelLog.String("Other ");
ELSE KernelLog.String("unkown")
END;
|9: KernelLog.String("HID:");
CASE subclass OF
0: KernelLog.String("Keyboard")
|1: KernelLog.String("Digitizer")
|2: KernelLog.String("Mouse")
|80H: KernelLog.String("Other ");
ELSE KernelLog.String("unkown")
END;
|10: KernelLog.String("dock:");
CASE subclass OF
0: KernelLog.String("Generic")
|80H: KernelLog.String("Other ");
ELSE KernelLog.String("unkown")
END;
|11: KernelLog.String("CPU:");
CASE subclass OF
0: KernelLog.String("386")
|1: KernelLog.String("486")
|2: KernelLog.String("Pentium")
|10H: KernelLog.String("Alpha")
|20H: KernelLog.String("PowerPC")
|40H: KernelLog.String("Coprocessor")
|80H: KernelLog.String("Other ");
ELSE KernelLog.String("unkown")
END;
|12: KernelLog.String("serial bus controller:");
CASE subclass OF
0: KernelLog.String("Firewire")
|1: KernelLog.String("ACCESS")
|2: KernelLog.String("SSA")
|3: KernelLog.String("USB")
ELSE KernelLog.String("unkown")
END;
ELSE
KernelLog.String("unknown class");
END;
END DisplayDeviceClass;
PROCEDURE TracePCIDevices;
VAR r0,r8 : LONGINT; pci: Pci; res, class, subclass, api, vendorId, deviceId: LONGINT;
BEGIN
IF pciEnabled THEN
KernelLog.String("PCI Devices"); KernelLog.Ln;
StartIterate(pci);
REPEAT
res := PCIReadConfig32(pci.bus, pci.device, pci.function, 0, r0);
IF r0 # LONGINT(0FFFFFFFFH) THEN
res := PCIReadConfig32(pci.bus, pci.device, pci.function, 8, r8);
vendorId := r0 MOD 10000H;
deviceId := r0 DIV 10000H;
class := r8 DIV 1000000H MOD 100H;
subclass := r8 DIV 10000H MOD 100H;
api := r8 DIV 100H MOD 100H;
KernelLog.String("device bus="); KernelLog.Int(pci.bus,1);
KernelLog.String(" pciDev="); KernelLog.Int(pci.device,1);
KernelLog.String(" pciFn="); KernelLog.Int(pci.function,1);
KernelLog.String(" vendorId="); KernelLog.Int(vendorId,1);
KernelLog.String(" deviceId="); KernelLog.Int(deviceId,1);
KernelLog.String(" class="); KernelLog.Int(class,1);
KernelLog.String(" subclass="); KernelLog.Int(subclass,1);
KernelLog.String(" api="); KernelLog.Int(api,1);
KernelLog.String(" classCode= "); KernelLog.Address(r8 DIV 100H MOD 1000000H);
KernelLog.String(" : ");
DisplayDeviceClass(class, subclass);
KernelLog.Ln;
END;
UNTIL ~Iterate(pci);
ELSE
KernelLog.String("No PCI type 1 found"); KernelLog.Ln;
END;
END TracePCIDevices;
PROCEDURE PCIDisabled() : BOOLEAN;
VAR string : ARRAY 2 OF CHAR;
BEGIN
Machine.GetConfig("DisablePCI", string);
RETURN string = "1";
END PCIDisabled;
BEGIN
pciEnabled := FALSE;
IF ~PCIDisabled() THEN
pciEnabled := PCICheckType1();
IF Trace THEN TracePCIDevices END;
END;
Show;
END PCI.
useful sources:
http://tldp.org/LDP/tlk/dd/pci.html
http://my.execpc.com/~geezer/code/pci.c
(**
Notes
PCI devices are uniquely identified by their vendor ID and device ID. For example, a 3Com 905B Etherlink XL ethernet card has vendor ID 10B7H (3Com) and device ID 9055H. To get access to this card, use the FindPCIDevice call. The third parameter (idx) is used to find multiple instances of the card. If set to 0, the first card is returned; if set to 1, the second; etc. The last three parameters return the bus number, device number and function number of the card, respectively. This triple can be used with the other calls (e.g., ReadConfig..., WriteConfig...) to address a specific card.
Example:
VAR res, bus, dev, fkt: LONGINT;
(* look for a 3Com 905B ethernet card *)
res := PCI.FindPCIDevice(9055H, 10B7H, 0, bus, dev, fkt);
IF res = PCI.Done THEN (* found at (bus, dev, fkt) *) END
The PCI configuration area is a standardized set of registers provided by every PCI device. It can be accessed using the ReadConfig... and WriteConfig... calls. Typically, registers 10H, 14H, ..., 24H specify the base addresses of a card. Bit 0 is 1 if the address is in the I/O space, and 0 if it is in the physical memory space. For I/O addresses, the bottom two bits should be masked off, and for physical memory addresses, the bottom 4 bits should be masked off.
Example:
VAR res, adr: LONGINT;
(* find the I/O base address of the ethernet controller *)
res := PCI.ReadConfigDword(bus, dev, fkt, 10H, adr);
IF res = PCI.Done THEN
ASSERT(ODD(adr)); (* must be I/O mapped *)
DEC(adr, adr MOD 4); (* strip lower 2 bits *)
...
SYSTEM.PORTIN(adr+X, x) (* read some device register *)
END
To access a memory-mapped device, its address range has to be mapped into the virtual address space first.
Example:
CONST Size = 4096; (* the device has 4KB of registers *)
VAR res, physAdr, virtAdr: LONGINT;
(* find the base address of a memory-mapped device *)
res := PCI.ReadConfigDword(bus, dev, fkt, 10H, physAdr);
IF res = PCI.Done THEN
ASSERT(~ODD(physAdr)); (* must be memory mapped *)
DEC(physAdr, physAdr MOD 16); (* strip lower 4 bits *)
Machine.MapPhysical(physAdr, Size, virtAdr);
...
x := SYSTEM.GET32(virtAdr+X); (* read some device register *)
...
Machine.UnmapPhysical(virtAdr, Size)
END
*)