PCI_CONFIGURE_BUS(9)

PCI_CONFIGURE_BUS(9) | Kernel Developer's Manual | PCI_CONFIGURE_BUS(9)

# NAME

`pci_configure_bus`, `pci_conf_hook`, `pci_conf_interrupt`, `pciconf_resource_init`, `pciconf_resource_add`, `pciconf_resource_fini` —
perform PCI bus configuration

# SYNOPSIS

`#include <dev/pci/pciconf.h>`

int
`pci_configure_bus`(pci_chipset_tag_t pc, struct pciconf_resources *res, int firstbus, int cacheline_size);

struct pciconf_resources *
`pciconf_resource_init`(void);

void
`pciconf_resource_add`(struct pciconf_resources *res, int type, bus_addr_t addr, bus_size_t size);

void
`pciconf_resource_fini`(struct pciconf_resources *res);

# DESCRIPTION

The `pci_configure_bus`() function configures a PCI bus for use. This involves:

- Defining bus numbers for all busses on the system,
- Setting the Base Address Registers for all devices,
- Setting up the interrupt line register for all devices,
- Configuring bus latency timers for all devices, and
- Configuring cacheline sizes for all devices.

In traditional PCs and Alpha systems, the BIOS or firmware takes care of this task, but that is not the case for all systems. `pci_configure_bus`() should be called prior to the autoconfiguration of the bus.

The pc argument is a machine-dependent tag used to specify the PCI chipset to the system. This should be the same value used with `pci_make_tag`(). The res argument is a container for PCI bus resources that will be used to configure the bus. The firstbus argument indicates the number of the first bus to be configured. The cacheline_size argument is used to configure the PCI Cache Line Size Register; it should be the size, in bytes, of the largest D-cache line on the system.

An implementation may choose to not have full configuration performed by `pci_configure_bus`() on certain PCI devices, such as PCI host bridges or PCI bus analyzers which are instantiated as devices on the bus. In order for this to take place, the header `<machine/pci_machdep.h>` must define the `__HAVE_PCI_CONF_HOOK` symbol (without a value), and a machine-dependent function `pci_conf_hook`() (declared in the same header) must be defined. The prototype for this function is:

int `pci_conf_hook`(pci_chipset_tag_t pc, int bus, int device, int function, pcireg_t id);

In this function, bus, device, and function uniquely identify the item being configured; in addition to this, the value of the device's PCI identification register is passed in id. For each device `pci_conf_hook`() can then decide upon the amount of configuration to be performed by returning a bitwise inclusive-or of the following flags:

`PCI_CONF_MAP_IO`
  Configure Base Address Registers that map I/O space
`PCI_CONF_MAP_MEM`
  Configure Base Address Registers that map memory space
`PCI_CONF_MAP_ROM`
  Configure Expansion ROM Base Address register
`PCI_CONF_ENABLE_IO`
  Enable I/O space accesses
`PCI_CONF_ENABLE_MEM`
  Enable memory space accesses
`PCI_CONF_ENABLE_BM`
  Enable bus mastering

In addition, `PCI_CONF_ALL` specifies all of the above.

One of the functions of `pci_configure_bus`() is to configure interrupt “line” information. This must be done on a machine-dependent basis, so a machine-dependent function `pci_conf_interrupt`() must be defined. The prototype for this function is

void `pci_conf_interrupt`(pci_chipset_tag_t pc, int bus, int device, int pin, int swiz, int *iline)

In this function, bus, device, and pin, uniquely identify the item being configured. The swiz argument is a “swizzle”, a sum of the device numbers of the primary interface of the bridges between the host bridge and the current device. The function is responsible for setting the value of iline. See chapter 9 of the “PCI-to-PCI Bridge Architecture Specification” for more information on swizzling (also known as interrupt routing).

The resources used to configure the PCI bus are encapsulated into a resource container. The `pciconf_resource_init`() function allocates and initializes one of these containers, and the `pciconf_resource_add`() function adds resources to the container, specifying the type, start address, and size of the resource being added. The following resource types are supported:

`PCICONF_RESOURCE_IO`
  An address region used for PCI I/O accesses.
`PCICONF_RESOURCE_MEM`
  An address region used for PCI memory accesses where reads may have side effects.
`PCICONF_RESOURCE_PREFETCHABLE_MEM`
  An address region used for PCI memory accesses where reads do not have side effects (e.g. ROMs, frame buffers, other memory-like regions that are marked as prefetchable in their BAR).

If an implementation does not distinguish between prefetchable and non-prefetchable memory, then adding a `PCICONF_RESOURCE_PREFETCHABLE_MEM` resource is not required; `PCICONF_RESOURCE_MEM` resources will be used for ROMs and BARs that are marked as prefetchable.

Once the bus has been successfully configured, the resource container should be disposed of by calling `pciconf_resource_fini`().

# RETURN VALUES

If successful `pci_configure_bus`() returns 0. A non-zero return value means that the bus was not completely configured for some reason. A description of the failure will be displayed on the console.

# ENVIRONMENT

The `pci_configure_bus`() function is only included in the kernel if the kernel is compiled with the `PCI_NETBSD_CONFIGURE` option enabled.

# EXAMPLES

The `pci_conf_hook`() function in evbppc's walnut implementation looks like:

    int
    pci_conf_hook(pci_chipset_tag_t pc, int bus, int dev, int func,
        pcireg_t id)
    {

    	if ((PCI_VENDOR(id) == PCI_VENDOR_IBM &&
    	     PCI_PRODUCT(id) == PCI_PRODUCT_IBM_405GP) ||
    	    (PCI_VENDOR(id) == PCI_VENDOR_INTEL &&
    	     PCI_PRODUCT(id) == PCI_PRODUCT_INTEL_80960_RP)) {
    		/* Don't configure the bridge and PCI probe. */
    		return 0;
    	}
    	return (PCI_CONF_ALL & ~PCI_CONF_MAP_ROM);
    }

The `pci_conf_interrupt`() function in the sandpoint implementation looks like:

    void
    pci_conf_interrupt(pci_chipset_tag_t pc, int bus, int dev, int pin,
        int swiz, int *iline)
    {
    	if (bus == 0) {
    		*iline = dev;
    	} else {
    		*iline = 13 + ((swiz + dev + 3) & 3);
    	}
    }

This configuration example is taken from the bebox port.

    #define PCI_IO_START    0x00008000
    #define PCI_IO_END      0x0000ffff
    #define PCI_IO_SIZE     ((PCI_IO_END - PCI_IO_START) + 1)

    #define PCI_MEM_START   0x00000000
    #define PCI_MEM_END     0x0fffffff
    #define PCI_MEM_SIZE    ((PCI_MEM_END - PCI_MEM_START) + 1)
    	...
    	struct pciconf_resources *pcires;
    	...
    	pcires = pciconf_resource_init();
    	pciconf_resource_add(pcires, PCICONF_RESOURCE_IO,
    	    PCI_IO_START, PCI_IO_SIZE);
    	pciconf_resource_add(pcires, PCICONF_RESOURCE_MEM,
    	    PCI_MEM_START, PCI_MEM_SIZE);
    	...
    	pci_configure_bus(pc, pcires, 0, CACHELINESIZE);
    	...
    	pciconf_resource_fini(pcires);
    	...

Note that this must be called before the PCI bus is attached during autoconfiguration.

# SEE ALSO

pci(4)

# HISTORY

`pci_configure_bus`() was added in NetBSD 1.6.

July 7, 2020 | NetBSD 10.0