# PCI

Interaction with devices through the PCI bus is mainly done through the
configuration registers of each device. The method through which we access those
registers is machine-specific, but the adressing scheme is not. A "PCI address"
has this structure:

    b31     Enable bit
    b30:24  Reserved
    b23:16  Bus number
    b15:11  Device (Slot) number
    b10:8   Function number
    b7:0    Register offset, b1:0 is always 0

With that address, you can read and write to PCI registers, usually to set up
some memory mapping through which the real interaction will occur.

PCI addresses only ever refer to aligned 32-bit values. All PCI values are
little-endian.

## Driver

The PCI driver lives at /drv/pci.fs and is machine-independent, but rests on a
small machine-dependent layer (for example /drv/pc/pci.fs) to provide those
words:

pci@ ( addr -- n )
  Fetch a 32-bit number from PCI address "addr".

pci! ( n addr -- )
  Store a 32-bit number to PCI address "addr".

Before loading /drv/pci.fs, you need to have this layer loaded.

The upper layer provides a few conveniences:

* PCI address constructors
* PCI field accessors
* Slot/Function enumeration capabilities
* Prints out informations about a Slot/Function

## Construct a PCI address

To construct a PCI address in a way that conforms to the bit structure described
above, you can "or" bits in, but you can also use the constructor words:

pcibus) ( busno -- addr )
pcislot) ( addr slotno -- addr )
pcifunc) ( addr funcno -- addr )
pcireg) ( addr offset -- addr )

For example, printing the bus 42 is done this way:

    42 pcibus) .pcibus

Printing the details of the function 1 on slot 2 on bus 3, it's

    3 pcibus) 2 pcislot) 1 pcifunc) .pci

## PCI field accessors

The PCI field accessor partially obey "to" semantics (only a straight "to" can
be used with them) and allow convenient access to common PCI fields. When 16-bit
or 8-bit mangling is necessary, it does it. For example:

  0 pcibus) pci.vendor .x

will print the 16-bit "Vendor ID" field of address 00:00.00 (bus 0, slot 0,
function 0). The available fields are as follow:

$00 pcifieldw0 pci.vendor      "Vendor ID"
$00 pcifieldw1 pci.device      "Device ID"
$04 pcifieldw0 pci.command     "Command"
$04 pcifieldw1 pci.status      "Status"
$08 pcifieldb0 pci.revision    "Revision ID"
$08 pcifieldb1 pci.progIF      "progIF"
$08 pcifieldb2 pci.subclass    "Subclass"
$08 pcifieldb3 pci.class       "Class"
$0c pcifieldb0 pci.cachelinesz "Cache Line Size"
$0c pcifieldb1 pci.lattimer    "Latency Timer"
$0c pcifieldb2 pci.hdrtype     "Type"
$0c pcifieldb3 pci.bist        "BIST"

$10 pcifield   pci0.bar0            "BAR0"
$14 pcifield   pci0.bar1            "BAR1"
$18 pcifield   pci0.bar2            "BAR2"
$1c pcifield   pci0.bar3            "BAR3"
$20 pcifield   pci0.bar4            "BAR4"
$24 pcifield   pci0.bar5            "BAR5"
$28 pcifield   pci0.cisptr          "Cardbus CIS ptr"
$2c pcifieldw0 pci0.subsystemvendor "Subsystem Vendor"
$2c pcifieldw1 pci0.subsystem       "Subsystem ID"
$30 pcifield   pci0.expansionaddr   "Expansion Addr"
$34 pcifieldb0 pci0.capabiliesptr   "Capabilities ptr"
$3c pcifieldb0 pci0.interruptline   "Interrupt Line"
$3c pcifieldb1 pci0.interruptpin    "Interrupt PIN"
$3c pcifieldb2 pci0.mingrant        "Min grant"
$3c pcifieldb3 pci0.maxlatency      "Max latency"

$10 pcifield   pci1.bar0            "BAR0"
$14 pcifield   pci1.bar1            "BAR1"
$18 pcifieldb0 pci1.pribus          "Primary Bus"
$18 pcifieldb1 pci1.secbus          "Secondary Bus"
$18 pcifieldb2 pci1.subbus          "Subordinate Bus"
$18 pcifieldb3 pci1.seclattimer     "Sec Latency timer"
$1c pcifieldb0 pci1.iobase          "I/O base"
$1c pcifieldb1 pci1.iolimit         "I/O limit"
$1c pcifieldw1 pci1.secstatus       "Secondary status"
$20 pcifieldw0 pci1.membase         "Memory base"
$20 pcifieldw1 pci1.memlimit        "Memory limit"
$24 pcifieldw0 pci1.prefmembase     "Prefetch mbase"
$24 pcifieldw1 pci1.prefmemlimit    "Prefetch mlimit"
$28 pcifield   pci1.prefhmem        "Prefetch high mem"
$2c pcifield   pci1.preflmem        "Prefetch low mem"
$30 pcifieldw0 pci1.iobaseh         "I/O base (high)"
$30 pcifieldw1 pci1.iolimith        "I/O limit (high)"
$34 pcifieldb0 pci1.capabiliesptr   "Capabilities ptr"
$38 pcifield   pci1.expansionbase   "Exp ROM base"
$3c pcifieldb0 pci1.interruptline   "Interrupt Line"
$3c pcifieldb1 pci1.interruptpin    "Interrupt PIN"
$3c pcifieldw1 pci1.bridgectl       "Bridge Control"

## PCI enumeration

This driver provides these iterators [doc/usage/iter]:

forpcislot ( addr -- ) i=addr
  If slot if of type $80, iterate over itself and its valid children. Otherwise,
  only iterate once over self.

forpcibus ( addr -- ) i=addr
  Iterate over all valid slots of the bus.

By "valid", we mean an address that doesn't return $ffffffff in its register 0.

Example usage:

  : foo 0 pcibus) forpcibus i forpcislot i pci.vendor .x spc> next next ;

Calling "foo" will print the value of the "Vendor ID" field for every valid
function of every valid slot of bus 0.

## Printing information

The driver provides the following convenience printers:

.pciaddr ( addr -- )
  Prints addr in its 00:00.00 format.

.pciln ( addr -- )
  Prints summary information of Function at addr on one line.

.pcislot ( addr -- )
  Iterate over all valid functions of the slot and call .pciln

.pcibus ( addr -- )
  Iterate over all valid slots of the bus and call .pcislot

.pci ( addr -- )
  For the function at addr, print values of all fields of the "common" PCI
  block, one line per field, with descriptions.

.pci0 ( addr -- )
  Like ".pci", but for the "Type 0" block.
