Subject: PCI device driver interface changes
To: None <current-users@NetBSD.ORG>
From: Chris G Demetriou <Chris_G_Demetriou@UX2.SP.CS.CMU.EDU>
List: current-users
Date: 03/26/1996 23:22:00
People using the -current PCI code (mostly: me, as NetBSD/Alpha
developer, and the i386 users):

I've just substantially changed the interfaces used by PCI drivers to
access PCI bus resources.  A start on a 'reasonable' document to
explain the new interfaces is below; in time, i I (or somebody else
who's more motivated 8-) will turn it into a set of section 9 manual
pages.

What this means to you:

If you're a user of PCI bus devices (i.e. NetBSD/i386 user who uses
one of the PCI devices that's actually treated as a PCI device by
NetBSD 8-), and note any new problems with your drivers, please get in
touch with me ASAP.  Please try to update your kernel to -current, and
tell me about any problems you have.

If you're a developer of PCI bus device drivers:
	(1) you should use the interfaces described below in new
	    drivers, and 
	(2) where possible, you should update your drivers to use
	    the new interfaces.
	(3) the new interfaces will require some small changes to
	    existing drivers if you use the pci functions
	    pci_conf_read() and pci_conf_write() directly (you'll need
	    to add an argument).
If you need help with (1) or (2), feel free to get in touch.  I'd like
to see all new drivers for 'real' PCI devices in NetBSD be easily
portable to the Alpha, which is only really possible if you follow
these and the <machine/bus.h> interfaces carefully.  Unfortunately,
neither set of interfaces are very well documented, at this point in
time.  8-)


It's worth noting that with this set of changes, and a nasty hack to
do system PA -> PCI bus PA translation, the 'ncr' and 'de' drivers
should work without change between NetBSD/Alpha and NetBSD/i386.
At a future point, i plan to address DMA mapping issues (at the same
time i actually get around to implementing bounce buffers for the
i386; it's all the same problem), but that's "not quite yet."



chris
========================================================================
The new PCI interface that consists of the functions and definitions
described below.  It goes hand in hand with the <machine/bus.h>
definitions, and people who write PCI code to go into /sys/dev/pci
should use those interfaces.  (Yes, i know they're currently
incomplete.  If you're developing a driver, and need services that
they should provide but don't, get in touch with me.)

I'm not going to bother describing the definitions in
<dev/pci/pcireg.h>, since most drivers will not have to use them.
Those that do need to use them should use the ones provided, rather
than rolling their own.  The same goes for PCI vendor and product ID
constants, as defined in <dev/pci/pcidevs.h>.

This document is intended as a brief description of the interfaces,
for both device driver developers and developers of machine-dependent
PCI support.


These machine-independent PCI definitions are contained in
<dev/pci/pcivar.h>.  PCI device register definitions are
in <dev/pci/pcireg.h>.

Machine-dependent PCI definitions are contained in
<MACHINE/pci/pci_machdep.h>, but that header should never be included
directly by machine-independent drivers.


The following types are defined, to be used by PCI device drivers:

pcireg_t	A 32-bit quantity, that is a PCI configuration space
		register.  (Yes, configuration space register can
		be read and written in other sizes, but this is
		the only size used by the PCI code, for now.)

pci_chipset_tag_t
		A fully copyable type of machine-dependent size
		and content, used to describe a PCI bus to the extent
		needed by these functions.  This type should be
		considered opaque by machine-independent code, and
		should be efficiently passable to functions.

pcitag_t	A fully copyable type of machine-dependent size
		and content, used to hold a PCI configuration space
		tag.  This type should be considered opaque to
		machine-independent code, and should be efficiently
		passable to functions.

pci_intr_handle_t
		A fully copyable type of machine-dependent size
		and content, used to describe the machine-specific
		mapping of a device's interrupt data to its actual
		interrupt request mechanism.  This type should be
		considered opaque by machine-independent code, and
		should be efficiently passable to functions.


Additionally, the following types, defined in <machine/bus.h>, are
used by the PCI interface described in this document:

	bus_chipset_tag_t
	bus_io_addr_t
	bus_io_size_t
	bus_mem_addr_t
	bus_mem_size_t

Finally, though the bus_io_handle_t and bus_mem_handle_t types and
the functions which manipulate them, defined in <machine/bus.h>, are
not used directly by this interface, drivers which are written to this
interface are expected to use them to access device I/O and memory
space.


The following structure is used to describe PCI device attachment:

	struct pci_attach_args {
	        bus_chipset_tag_t pa_bc;
	        pci_chipset_tag_t pa_pc;

	        u_int           pa_device;	/* XXX may go away */
	        u_int           pa_function;	/* XXX may go away */
	        pcitag_t        pa_tag;
	        pcireg_t        pa_id, pa_class;

	        u_int           pa_intrswiz;
	        pcitag_t        pa_intrtag;
	        pci_intr_pin_t  pa_intrpin;
	        pci_intr_line_t pa_intrline;
	};

The pa_bc and pc_pc members are used to access bus resources, via the
<machine/bus.h> interfaces and the PCI bus interfaces.

The pa_device and pa_function members are the device's PCI device and
function numbers.  The PCI bus number is not provided, because it
should not be needed for any reason by PCI devices.  Do not rely on
these members; at least the device member may go away in the future.

The pa_tag member is the PCI congfiguration space tag used to access
the device's configuration space resources.

The pa_intr* members are for use when mapping the devices's interrupt,
if any.  If the device uses an interrupt line, pa_intrpin with not
have a value of PCI_INTERRUPT_PIN_NONE.  The pa_intrtag, pa_intrpin,
and pa_intrline members should be passed to the pci_intr_map().
The pa_intrswiz member is reserved for use by PCI-PCI bridges.


This interface defines 9 functions which may be used by PCI device
drivers.

The following 6 functions are implemented in machine-dependent code:

pcireg_t        pci_conf_read __P((pci_chipset_tag_t pc, pcitag_t tag,
		    int off));

	Read and return four bytes from offset 'off' in the
	configuration space of the bus/device/function specified by
	'pc' and 'tag.'  'off' must be 4-byte aligned.

	This function is always successful.  If the arguments are
	invalid, behaviour is undefined.

	This function should not implemented in a way that causes
	it to touch any PCI device's memory or address space.
	(In other words, DO NOT invoke printf from this function
	if your console is a PCI device.)

void            pci_conf_write __P((pci_chipset_tag_t, pcitag_t, int,
                    pcireg_t));

	Write four bytes to offset 'off' in the configuration space of
	the bus/device/function specified by 'pc' and 'tag.'  'off'
	must be 4-byte aligned.

	This function is always successful.  If the arguments are
	invalid, behaviour is undefined.

	This function should not implemented in a way that causes
	it to touch any PCI device's memory or address space.
	(In other words, DO NOT invoke printf from this function
	if your console is a PCI device.)

int             pci_intr_map __P((pci_chipset_tag_t pc, pcitag_t intrtag,
		    int intrpin, int intrline, pci_intr_handle_t *ihp));

	Map the PCI interrupt specified by the combination of 'pc,'
	'intrtag,' 'intrpin,' and 'intrline' into a machine-specific
	interrupt identifier, and return that identifier in the memory
	pointed to by 'ihp.'

	This function returns 0 if successful, and non-zero if there
	was an error.  Possible errors include but are not limited to
	the device not using an interrupt line, and that interrupt
	line not being properly mapped by the system firmware.  If
	the arguments are invalid, behaviour is undefined

const char      *pci_intr_string __P((pci_chipset_tag_t pc,
		    pci_intr_handle_t ih));

	Transform the given PCI chipset identifier 'pc' and interrupt
	identifier 'ih' into a meaningful character string.  If there
	is no meaningful representation of the interrupt identifier,
	NULL is returned.  (A return of NULL is _NOT_ an error.)

	This function should, if possible, use as the string the name
	for the interrupt identifier that the 'vmstat -i' uses when
	printing information about that interrupt.

	Multiple calls to this function may overwrite the values
	returned by previous calls.

	This function is always successful.  If the arguments are
	invalid, behaviour is undefined.

void            *pci_intr_establish __P((pci_chipset_tag_t pc,
		    pci_intr_handle_t ih, int level, int (*func)(void *),
		    void *arg)); 

	Establish the function 'func' as an interrupt handler for the
	interrupt named by 'pc' and 'ih.'  That function will be
	called with the system IPL set to 'level' (one of the IPL_*
	constants), and with one argument, 'arg.'  The interrupt
	handler function should return 0 if it was not expecting an
	interrupt, 1 if it was expecting an interrupt, and -1 if
	whether or not an an interrupt was expected is unknown.

	This function return NULL if the interrupt handler could not
	be established for any reason, and non-NULL if the interrupt
	handler was successfully established.  If the interrupt
	handler was successfully established, the return value may
	later be passed to a call to pci_intr_disestablish, along
	with the PCI chipset tag for the device, to remove the
	interrupt handler.  If this functions arguments are invalid,
	behaviour is undefined.

void		pci_intr_disestablish __P((pci_chipset_tag_t pc,
		    void *cookie));

	Remove the interrupt handler specified by 'cookie' from
	the PCI bus described by 'pc.'  'cookie' must be a value
	returned from a previous successful invocation of
	pci_intr_establish(), and 'pc' must be the pci_chipset_tag_t
	that accompanied that invocation.

	This function is always successful.  If the arguments
	are invalid, behaviour is undefined.


The following three functions are implemented in machine-independent
code:

int     pci_io_find __P((pci_chipset_tag_t pc, pcitag_t tag, int off,
	    bus_io_addr_t *iobase, bus_io_size_t *iosize));

	Examine the PCI configuration space Base Address register at
	offset 'off' in the configuration space of the device named by
	'pc' and 'tag,' and fill in the variables pointed to by
	'iobase' and 'iosize' with the information found there.
	'off' must be 4-byte aligned.

	If 'off' not 4-byte aligned, or if 'off' is outside
	of the valid range of Base Address registers or does
	not indicate a Base Address register that maps an I/O
	range, behaviour is undefined.  This function returns 0 if
	successful, and 1 if unsuccessful. 

int     pci_mem_find __P((pci_chipset_tag_t pc, pcitag_t tag, int off,
	    bus_mem_addr_t *membase, bus_mem_size_t *memsize,
	    int *cacheable));

	Examine the PCI configuration space Base Address register at
	offset 'off' in the configuration space of the device named by
	'pc' and 'tag,' and fill in the variables pointed to by
	'membase' and 'memsize' with the information found there.
	'off' must be 4-byte aligned

	If 'off' not 4-byte aligned, or if 'off' is outside
	of the valid range of Base Address registers or does
	not indicate a Base Address register that maps a memory
	range, behaviour is undefined.  This function returns
	0 if successful, and 1 if unsuccessful.  This function
	may fail for reasons including, but not limited to,
	inability to support the desired address register type
	(e.g. the case of trying to use use a base address
	register that maps 64-bit PCI memory, when bus_mem_addr_t
	is only 32 bits wide).


void    pci_devinfo __P((pcireg_t id_reg, pcireg_t class_reg,
	    int showclass, char *cp));

	Given the device id and class information given in
	'id_reg' and 'class_reg,' respectively, try to
	come up with a meaningful description of the device, and
	place that description into the memory pointed to
	by 'cp.'  'cp' is assumed to point to a character
	array at least 256 bytes.

	If the kernel option PCIVERBOSE is set, this function
	will search a table of known vendors and devices
	whose identifiers match those given in 'id_reg.'
	It will then store the vendor and device name, or if they
	are unknown, printable versions of the ID values and an
	indication that they were unknown, in the memory pointed
	to by 'cp,'  

	If the kernel option PCIVERBOSE is not set, this
	function will store printable versions of the vendor and
	product ID values in the memory pointed to by 'cp.'

	If 'showclass' is set, the device's class and subclass
	types and revision are stored after the devices identification
	in the memory pointed to by 'cp.'

	In most cases, device names to be printed at autoconfiguration
	time should be coded into their drivers.  Because of the relative
	lack of information provided when PCIVERBOSE is not set, this
	interface should only be used to get and print device information
	when no more information about the device is avaiable, such as
	when the device does not have a driver (i.e. by the PCI bus
	autoconfiguration code), or when the driver is sufficiently
	generic as to apply to a broad range of devices (e.g. PCI-PCI
	bridges or PCI VGA adapters).