Subject: PCI configuration
To: None <>
From: Allen Briggs <>
List: tech-kern
Date: 02/01/2001 14:03:40
Some PCI-based systems do not have firmware that configures the PCI bus
(setting up the BARs, the interrupt lines, etc.).  I think that the
bebox is in this category (or only partially?).  As I mentioned a few
days ago, I've been working on a system like this, and I've put together
some code to do this configuration when the bus is attached.

Some parts of the code (especially code to deal with setting GNT/LAT
values) are adapted from PMON/2000 (

I have defined a new defopt PCI_NETBSD_CONFIGURE in opt_pci.h that
brings in a new pciconf.c and the following block in pci.c:pci_probe_bus():

@@ -142,6 +142,13 @@
	bus = sc->sc_bus;
	maxndevs = sc->sc_maxndevs;
+	if (pci_configure_bus(pc, bus, maxndevs) < 0) {
+		printf("Failed to configure %s\n", sc->sc_dev.dv_xname);
+		return;
+	}
	for (device = 0; device < maxndevs; device++) {
		pcitag_t tag;
		pcireg_t id, class, intr, bhlcr, csr;

pci_configure_bus() requires one m.d. function:
int	pci_get_memory_ranges __P((pci_chipset_tag_t pc, int bus,
	    struct pciconf_addr_range_head **io_range,
	    struct pciconf_addr_range_head **mem_range));

struct pciconf_addr_range_head is part of a LIST defined as:
struct pciconf_addr_range {
	paddr_t		par_base;	/* Aligned base address */
	vm_size_t	par_len;	/* Length of address range */
	int		par_align;	/* Power-of-two alignment for this */
					/* memory--subsets will only be taken */
					/* as multiples of this.  I.e., if */
					/* par_align is 4 and 5 bytes are */
					/* requested, 8 bytes will actually */
					/* be allocated. */
	LIST_ENTRY(pciconf_addr_range)	par_list;

LIST_HEAD(pciconf_addr_range_head, pciconf_addr_range);

[ I'm not wild about the names...  Suggestions welcome if you feel
  strongly about it ]

The machdep code for pci_get_memory_ranges() initializes and fills in
the two LISTs with available I/O and memory ranges in decreasing size
order.  The m.i. code will use these lists and modify them as it
configures things.  So these lists should be persistent across calls
to the PCI bus attachment calls.

An example of a ridiculously simple pci_get_memory_ranges():
struct pciconf_addr_range_head sandpoint_io_head;
struct pciconf_addr_range_head sandpoint_mem_head;
struct pciconf_addr_range sandpoint_io_range =
	{      0x600,  16 * 1024 * 1024, 4 };
struct pciconf_addr_range sandpoint_mem_range =
	{ 0x80000000, 256 * 1024 * 1024, 4 };

pci_get_memory_ranges(pc, bus, pioh, pmemh)
	pci_chipset_tag_t pc;
	int bus;
	struct pciconf_addr_range_head **pioh;
	struct pciconf_addr_range_head **pmemh;
	static struct pciconf_addr_range_head *pioh_base;
	static struct pciconf_addr_range_head *pmemh_base;
	struct pciconf_addr_range *n, *n2;

	if (pioh_base || pmemh_base) {
		*pioh = pioh_base;
		*pmemh = pmemh_base;
		return 0;

	pioh_base = &sandpoint_io_head;
	pmemh_base = &sandpoint_mem_head;


	*pioh = pioh_base;
	*pmemh = pmemh_base;

	LIST_INSERT_HEAD(*pioh, &sandpoint_io_range, par_list);
	LIST_INSERT_HEAD(*pmemh, &sandpoint_mem_range, par_list);

	return 0;

I'm not entirely satisfied by this, though it does work for me.
It would be nice if this resource of available I/O and memory
regions could be shared for those ports that have PnP ISA or such
and want to configure the allocations there as well.

Right now, the interrupt line is just assigned to be the device number.
Should this also be allocated by an m.d. callback?  Should it be
something more like "function * 32 + device" or "device * 8 + function"?

So, any thoughts or suggestions?  Should I commit what I have and let
us work from there?  It will not affect existing systems in the slightest.


 Allen Briggs	  Quality NetBSD CDs, Sales, Support, Service