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).