This appendix contains the text of new and updated reference pages that have been created since IRIX 6.3 initially shipped.
“Address/Length List Reference Pages” displays two pages about alenlist concepts and functions.
“PCI Infrastructure Reference Pages” displays the pages about the PCI bus support functions.
Example B-1 displays the text of an overview of address/length lists.
Example B-2 displays the reference page listing operations on alenlists.
NAME
alenlist - overview of Address/Length Lists
DESCRIPTION
An Address/Length List or alenlist is an abstract object containing a
sequential list of address/length pairs.
All addresses in the list belong to the same address space, for example
kernel virtual address space, physical memory address space, PCI DMA
space, VME DMA space, and so on. All lengths are byte counts. Each
address/length pair added to a list describes one contiguous segment in
the relevant address space. Together, all the pairs in the list describe
a region of memory, typically an I/O buffer, that is logically, but not
necessarily physically, contiguous.
When a driver receives a request for I/O, the request may be specified in
one of several forms, for example a kernel virtual address, a user
virtual address, or a vector of addresses from a device register array.
The driver can convert any of these forms into a single alenlist for easy
management.
Other kernel layers (notably, the PCI infrastructure, see pciio(D3))
accept alenlists. Translation from one address space to another, a
constant challenge for device drivers, can be performed on an entire
alenlist in a single operation, avoiding repeated bouncing up and down
through layers of software for each segment.
An alenlist is created by the driver when needed. It expands
automatically as address/length pairs are added to the list. Its memory
can easily be released for recycling, or a list can be cleared and re-
used. There are operations to append a new pair to a list, and to load a
list based on a user or kernel virtual buffer address and size.
Address/length pairs can be read out from a list in sequence, and in
length units different from the units that were initially loaded (for
example, load the list based on a user-space buffer, then read out pairs
in 512-byte segments).
A cursor marks a position within an alenlist, and is used to scan through
the pairs in the list. A cursor can point to the beginning of its list,
any number of bytes into the list, or at the end of the list. Every
alenlist contains an implicit cursor for simple management, but there are
operations to create, destroy, and initialize additional cursors, so
multiple positions can be maintained at once.
Usage
In order to use any of the Address/Length types or interfaces, a driver
should include these header files in this order:
#include <sys/types.h>
#include <sys/alenlist.h>
#include <sys/ddi.h>
Types
The following abstract types are used and returned by alenlist functions.
alenaddr_t An abstract address in some address space. This is the type
of any address added to a list. It is guaranteed to be big
enough to hold an address in any supported address space
(currently 64 bits).
size_t The standard byte-count type. Every address/length pair in a
list comprises one alenaddr_t and one size_t.
alenlist_t A handle to an alenlist.
alenlist_cursor_t
A handle to a cursor, an object that designates a position
within an alenlist. An alenlist_cursor_t value of NULL
always designates the implicit cursor of a list. Non-null
values designate cursor objects allocated separate from their
lists.
Operations
The functions that operate on alenlists are detailed in
alenlist_ops(D3X). Operations to allocate and destroy alenlists are as
follows:
o alenlist_create() creates a new, empty list.
o alenlist_destroy() recycles the storage used by an alenlist.
o alenlist_cursor_create() creates a new cursor object.
o alenlist_cursor_destroy() recycles the storage used by a cursor.
Operations to clear and load pairs into an alenlist are as follows:
o alenlist_clear() empties a list.
o alenlist_append() appends one address/length pair to a list.
o kvaddr_to_alenlist() converts a kernel virtual address and length
into physical memory address/length pairs and appends the pairs to a
list.
o uvaddr_to_alenlist() converts a user process virtual address into
physical memory address/length pairs and appends the pairs to a list.
o buf_to_alenlist() examines a buf structure (see buf(D4)) and appends
physical memory address/length pairs to describe the buffer to a
list.
In addition, pciio_dma(D3) documents pciio_dmamap_list(), a function that
translates an alenlist of physical memory addresses into an alenlist of
PCI bus addresses for DMA use.
Operations to read out address/length pairs from an alenlist are as
follows:
o alenlist_get() returns an address/length pair from a list, based on
the implicit cursor or on a specified cursor, and optionally advances
the cursor.
o alenlist_cursor_init() initializes a specific cursor or an implicit
cursor to a specified offset.
o alenlist_cursor_offset() returns the current offset of a cursor.
Flags
The following flag values are used with alenlist functions:
AL_NOSLEEP Indicates that the caller does not wish to be put to sleep
while waiting for resources such as memory. If an operation
would entail sleeping, the function returns failure.
AL_NOCOMPACT
Indicates that adjacent address/length pairs that happen to
be contiguous in their address space should not be compacted
and treated as a single unit (which is the default behavior).
Rather, they should be treated as if there is a
discontiguity. This flag is not needed by most drivers.
AL_LEAVE_CURSOR
Indicates that the operation should not affect the position
of the internal cursor.
Error Codes
The following error codes are used by all alenlist functions:
ALENLIST_SUCCESS
Indicates a successful operation.
ALENLIST_FAILURE
Indicates a failed operation.
SEE ALSO
alenlist_ops(D3X), pciio_dma(D3), IRIX Device Driver Programmer's Guide
|
NAME
alenlist_ops: alenlist_append, alenlist_clear, alenlist_create,
alenlist_cursor_create, alenlist_cursor_destroy, alenlist_cursor_init,
alenlist_cursor_offset, alenlist_destroy, alenlist_get,
kvaddr_to_alenlist, uvaddr_to_alenlist, buf_to_alenlist - operations on
address/length lists
SYNOPSIS
#include <sys/types.h>
#include <sys/alenlist.h>
#include <sys/ddi.h>
alenlist_t
alenlist_create(unsigned flags);
void
alenlist_clear(alenlist_t plist);
void
alenlist_destroy(alenlist_t plist);
alenlist_cursor_t
alenlist_cursor_create(alenlist_t plist, unsigned flags);
void
alenlist_cursor_destroy(alenlist_cursor_t ocursor);
int
alenlist_get(alenlist_t plist,
alenlist_cursor_t icursor,
size_t maxlength,
alenaddr_t *paddr,
size_t *plength);
int
alenlist_cursor_init(alenlist_t plist,
size_t offset,
alenlist_cursor_t icursor);
size_t
alenlist_cursor_offset(alenlist_t plist, alenlist_cursor_t icursor);
int
alenlist_append(alenlist_t plist,
alenaddr_t address,
size_t length);
alenlist_t
kvaddr_to_alenlist(caddr_t kvaddr, size_t length);
alenlist_t
uvaddr_to_alenlist( alenlist_t plist,
uvaddr_t uvaddr,
size_t length);
alenlist_t
buf_to_alenlist(buf_t *buf);
Arguments
address An address in some address space.
buf Address of a buf struct.
flags A Boolean combination of the flags declared in
sys/alenlist.h.
icursor A handle to an existing cursor, or NULL to indicate the
implicit cursor in plist.
kvaddr A valid address in kernel virtual memory.
length The length related to an address.
maxlength The maximum length allowed in the returned address/length
pair, or 0 to indicate no maximum applies.
ocursor A handle to an existing cursor, the target of the operation.
offset An initial byte offset for icursor, usually 0.
paddr A pointer to a variable to receive the address from an
address/length pair.
plength A pointer to a variable to receive the length from an
address/length pair.
plist A handle to an existing alenlist.
uvaddr A valid address in user virtual memory for the in-context
process.
DESCRIPTION
For an overview of address/length lists (alenlists) see alenlist(D4X).
Allocation and Release
Create an empty list using alenlist_create(). The AL_NOSLEEP flag
ensures that the caller will not sleep waiting for memory. If memory
cannot be allocated, NULL is returned. The returned list has no pairs in
it, and its implicit cursor is initialized to point to the start of the
(empty) list.
The functions kvaddr_to_alenlist(), uvaddr_to_alenlist(), and
buf_to_alenlist(), when the plist argument is NULL, allocate a new
alenlist and return it. However, these functions do not honor
AL_NOSLEEP, either when creating a list or when getting memory to extend
an existing list. If it is important not to sleep, preallocate the list.
Empty a list by applying alenlist_clear(). The implicit cursor is reset
to the start of the (empty) list.
Release a list using alenlist_destroy(). This lets the system know that
the specified List is no longer in use.
Create a cursor by calling alenlist_cursor_create(). Pass AL_NOSLEEP to
avoid sleeping on memory allocation. When a cursor cannot be created,
NULL is returned. The new cursor is associated with plist and is
initialized to point to the head of that list. More than one cursor can
point to a given list.
Use alenlist_cursor_destroy() to tell the system the cursor is no longer
needed. The cursor must not be used after this call is made.
Reading a List
Use the alenlist_get() function to retrieve pairs from the list. An
address/length pair from plist is stored based on paddr and plength. The
pair to retrieve is established by a cursor, either icursor or the
implicit cursor in plist when icursor is NULL. The cursor used is
updated by the length returned, provided the AL_LEAVE_CURSOR flag is not
used.
It is not necessary to read out exactly the pairs that were added to the
list. When maxlength is nonzero, it establishes the maximum length
retrieved. When maxlength is also an integral power of 2, the returned
length is further constrained so that the returned address and length do
not cross a maxlength boundary. For example, when maxlength is 512, the
address/length values returned are such that the next pair returned will
begin on a 512-boundary.
Returns ALENLIST_SUCCESS or ALENLIST_FAILURE. The normal cause for
failure is that the list is exhausted.
Call alenlist_cursor_init() to initialize a cursor to a specified offset
(usually 0). If cursorp is NULL, the implicit internal cursor of plist
is initialized.
To retrieve the current byte offset of a cursor, call
alenlist_cursor_offset(). When icursor is NULL, the offset of the
implicit cursor in plist is returned.
Loading a List
The basic operation to add an address/length pair to a list is
alenlist_append(). The list is expanded if necessary. Use the
AL_NOSLEEP flag to prevent sleeping on memory allocation that might be
necessary to resize the list. Use AL_NOCOMPACT to prevent the added pair
from being merged into a logically-adjacent preceding pair. Returns
ALENLIST_SUCCESS or ALENLIST_FAILURE.
To build an alenlist that describes a buffer in kernel virtual memory,
call kvaddr_to_alenlist(). The specified kernel virtual address is
converted into a list of physical address/length pairs and the pairs are
appended to an alenlist. A handle to the alenlist is returned. When
plist is NULL, a new list is created, and this list is returned.
To build an alenlist that describes a buffer in user virtual memory, call
uvaddr_to_alenlist(). The specified user virtual address for the
specified length is converted into a list of physical address/length
pairs and the pairs are appended to an alenlist. A handle to the
alenlist is returned. When plist is NULL, a new list is created, and
this list is returned.
To build an alenlist that describes a buffer mapped by a buf structure,
call buf_to_alenlist(). The memory described by the buf is converted
into a list of physical address/length pairs and the pairs are appended
to an alenlist. A handle to the alenlist is returned. When plist is
NULL, a new list is created, and this list is returned.
SEE ALSO
alenlist(D4X), buf(D4), IRIX Device Driver Programmer's Guide
NOTES
In IRIX 6.3, the declaration of buf_to_alenlist() was omitted from the
header file. Declare it manually as follows:
/* temporary */
extern alenlist_t buf_to_alenlist(buf_t *);
This prototype for buf_to_alenlist(), as well as the prototypes shown
above for uvaddr_to_alenlist(), kvaddr_to_alenlist(), alenlist_append(), and
alenlist_get() are all different in future releases of IRIX. There is no simple
workaround. These functions are extremely useful, but you should place a
comment near each one indicating that when porting to IRIX 6.4 or later,
additional arguments are required.
|
Example B-3 displays the reference page for registering and unregistering a PCI driver.
Example B-4 displays the reference page about configuration space access.
Example B-5 displays the reference page about setting up and using DMA maps.
Example B-6 displays the reference page documenting the PCI error handler.
Example B-7 displays the reference page for PCI query functions.
Example B-8 displays the reference page documenting the PCI interrupt hander.
Example B-9 displays the reference page about setting up and using PIO maps.
NAME pciio: pciio_add_attach, pciio_driver_register, pciio_driver_unregister - control PCI driver infrastructure SYNOPSIS #include <sys/PCI/pciio.h> int pciio_add_attach(__int32_t (*attach)(vertex_hdl_t), __int32_t (*detach)(vertex_hdl_t), pciio_error_handler_f *error, char *driver_prefix, int major) int pciio_driver_register( pciio_vendor_id_t vendor_id, pciio_device_id_t device_id, char *driver_prefix, unsigned flags); void pciio_driver_unregister(char *driver_prefix); void pciio_reset (vertex_hdl_t pconn); Arguments attach Address of the driver's attach() entry point. detach Address of the driver's detach() entry point, or NULL. error Address of the driver's error() entry point, or NULL. driver_prefix The prefix string for the driver's standard entry points as configured in /var/sysgen/system. major Major device number configured for this driver. vendor_id device_id Values that the PCI device will present in its configuration space as its vendor and device ID codes. flags Normally passed as zero. pconn is an appropriate PCI connection point. DESCRIPTION The PCI infrastructure is a package of kernel services used by drivers for PCI devices to set up services for their devices. These services include: o Manipulating the PCI configuration space for the device (see pciio_config(D3)). o Constructing physical addresses to use for PIO access to the device (see pciio_pio(D3)). o Constructing PCI addresses for the device to use for DMA access to memory (see pciio_dma(D3)). o Arranging for a function to be called when the device requests interrupt service (see pciio_intr(D3)). o Arranging for a function to be called when an error occurs during PIO to, or DMA from the device (see pciio_error(D3)). o Accessing useful fields in some otherwise opaque data structures (see pciio_get(D3)). Driver Registration The first function a PCI driver must call, usually in the init() entry point, is pciio_add_attach(), to introduce the driver to the PCI infrastructure. NOTE: This function is only called in the IRIX 6.3 implementation on the O2 workstation. The call to this function can be enclosed in a conditional test of the variable _EARLY_PCI, as follows: xxxx_init() { #ifdef _EARLY_PCI /* 6.3, must do add_attach */ pciio_add_attach(xxxx_attach,NULL,NULL,"xxxx",XXXX_MAJNO); #endif pciio_driver_register() is used by a PCI driver in IRIX 6.3 or any later release to inform the infrastructure that it handles all PCI devices designated by specified device_id and vendor_id values. The infrastructure associates the specified ID numbers with the specified device driver prefix. When a device with these IDs is discovered, the infrastructure calls the attach entry point for the driver with that driver prefix, passing the hardware graph connection point vertex as the only parameter. This connection point is then used in most calls to the infrastructure to identify the PCI device of interest. A loadable device driver calls pciio_driver_register() from its reg() entry point. A driver prelinked into the kernel should also make the call from reg() for consistency, but may call from the init() entry point if necessary. Device drivers may make multiple calls with different vendor and device ID numbers, representing several compatible PCI devices. Wildcard values PCIIO_VENDOR_ID_NONE and PCIIO_DEVICE_ID_NONE may be used if cards from any vendor or cards with any device code are of supported. When both vendor and device are wildcarded, the attach() routine is called for every PCI device connected to the system. When a loadable device driver calls pciio_driver_register(), one or more calls to the driver's attach() function can occur before the infrastructure returns control to the caller. On some large systems, the attach() calls can be executed by other threads and possibly on other processors, concurrently with continued execution of the reg() entry point. pciio_driver_unregister() should be called by any unloadable device driver from within the driver's unreg() entry point. This triggers calls to the driver's detach() entry point, and removes the association between the driver and any vendor and device IDs. Resetting a PCI card pciio_reset() is used to attempt to activate the PCI Reset line connected to a specific card without affecting other devices on the PCI bus. When reset is possible, the device is reset and basic configuration information is reloaded. EXAMPLES Here is how a typical driver might make use of these functions: static char pcifoo_prefix[] = "pcifoo_"; static char pcifoo_edge[] = "foo"; #ifdef _EARLY_PCI /* 6.3, must do add_attach, need init() */ pcifoo_init(void) { pciio_add_attach(pcifoo_attach,NULL,NULL,"pcifoo",42); } #endif pcifoo_reg(void) { pciio_driver_register( PCIFOO_VENDOR_ID, PCIFOO_DEVICE_ID, "pcifoo", 0); } pcifoo_unreg(void) { pciio_driver_unregister("pcifoo"); } SEE ALSO pciio_config(D3), pciio_dma(D3), pciio_error(D3), pciio_get(D3), pciio_intr(D3), pciio_pio(D3). |
NAME pciio_config: pciio_config_get, pciio_config_set - access PCI Configuration register SYNOPSIS #include <sys/PCI/pciio.h> u_int32_t pciio_config_get( volatile unsigned char *bus_addr, int cfg_reg) int pciio_config_set( volatile unsigned char *bus_addr, int cfg_reg, __int32_t value) Arguments bus_addr PIO target address of PCI configuration space for a device, as returned by pciio_piomap_addr() or pciio_piotrans_addr(). cfg_reg Byte offset of the register of interest in the PCI address space. value Value to be written to the specified register. DESCRIPTION Various SGI platforms introduce complexities and restrictions in how Configuration Space cycles are generated on the PCI bus. Some platforms may require all PCI Configuration accesses to be done using 32-bit wide accesses. Others require more than a simple load or store to trigger the actual cycle, so that configuration access cannot be performed using normal PIO loads and stores. (Both of these restrictions are true of the O2 workstation.) The functions described here were introduced to allow the kernel to trigger a PCI bus Configuration Cycle based on a PIO address. The cfg_reg value specifies the offset of the target value in configuration space. Registers defined by the PCI standard are 1, 2, 3, 4, or 8 bytes, but these functions support only 1-4 bytes. (Eight-byte registers can be fetched in two calls.) Because configuration space is accessed in 32-bit units on 32-bit boundaries, when reg specifies a standard PCI configuration register, pciio_config_get() shifts and masks appropriately to return just the value of the register. Similarly, pciio_config_set() executes a read- merge-write operation to place the value data in the correct portion of the word. Standard PCI Configuration Registers To access vendor-specific registers, specify the base address in PCI configuration space, bearing in mind that PCI places the least significant data in the lowest offset. The following constants are declared in the header file sys/PCI/PCI_defs.h for use as the cfg_reg value to specify a standard register in the Type 00 PCI configuration space: PCI_CFG_VENDOR_ID PCI_CFG_DEVICE_ID PCI_CFG_COMMAND PCI_CFG_STATUS PCI_CFG_REV_ID PCI_CFG_BASE_CLASS PCI_CFG_SUB_CLASS PCI_CFG_PROG_IF PCI_CFG_CACHE_LINE PCI_CFG_LATENCY_TIMER PCI_CFG_HEADER_TYPE PCI_CFG_BISTPCI_CFG_BIST PCI_CFG_BASE_ADDR_0 PCI_CFG_BASE_ADDR_1 PCI_CFG_BASE_ADDR_2 PCI_CFG_BASE_ADDR_3 PCI_CFG_BASE_ADDR_4 PCI_CFG_BASE_ADDR_5 PCI_CFG_BASE_ADDR(n) PCI_CFG_CARDBUS_CIS PCI_CFG_SUBSYS_VEND_ID PCI_CFG_SUBSYS_ID PCI_EXPANSION_ROM PCI_INTR_LINE PCI_INTR_PIN PCI_MIN_GNT PCI_MAX_LAT Use PCI_CFG_VEND_SPECIFIC to specify the first vendor-specific register word. The configuration functions deduce the length of the register from its offset. For a nonstandard or vendor-specific register the functions assume a 32-bit word. NOTES Logical byte order is preserved; that is, the most significant byte of a (big-endian) program word is sent as the most significant byte of a (little-endian) PCI bus word. Writes to registers of less than 32 bits are synthesized by reading the word containing the register, modifying the proper bits in the word, then rewriting the entire bus word. The read-modify-write code knows about the special handling of the STATUS register. However, if other registers in your card's configuration space are sensitive to being rewritten, you should access them using full four-byte-wide accesses, manipulating the word data appropriately. Subsequent Releases In all platforms supported IRIX 6.4, configuration access is possible with normal PIO. Accordingly, these functions were not defined in IRIX 6.4 and will result in an unresolved extern if porting is attempted. In releases after IRIX 6.4, these configuration access functions are returned, but with different arguments and with more capabilities: o They take the register size as an argument, from 1-8 bytes. o They do not require use of a mapped PIO address, but take only the vertex handle of the device. It is possible to code configuration access macros so that they compile properly in all releases from 6.3 onward. The macro code would be similar to the following: /* PCI Config Space Access Macros ** for source compatibility in drivers ** that need to use the same source ** for IRIX 6.3, IRIX 6.4, and IRIX 6.5 ** ** PCI_CFG_BASE(conn) ** PCI_CFG_GET(conn,base,offset,type) ** PCI_CFG_SET(conn,base,offset,type,value) ** ** Use PCI_CFG_BASE once during attach to get the ** base value to be used for the specific device. ** Later, use PCI_CFG_GET to read and PCI_CFG_SET ** to write config registers. ** ** NOTE: Irix 6.3 determines the size of the register ** directly on its own, based on the layout of a Type 00 ** PCI Configuration Space Header. If you specify a ** nonstandard size, you will get different results ** depending on the system revision number. */ #if IRIX6_3 #define PCI_CFG_BASE(c) pciio_piotrans_addr(c,0,PCIIO_SPACE_CFG,0,256,0) #define PCI_CFG_GET(c,b,o,t) pciio_config_get(b,o) #define PCI_CFG_SET(c,b,o,t,v) pciio_config_set(b,o,v) #elif IRIX6_4 #define PCI_CFG_BASE(c) pciio_piotrans_addr(c,0,PCIIO_SPACE_CFG,0,256,0) #define PCI_CFG_GET(c,b,o,t) ((*(t *)((char *)(b)+(o)))) #define PCI_CFG_SET(c,b,o,t,v) ((*(t *)((char *)(b)+(o))) = v) #else /* starting in IRIX 6.5 */ #define PCI_CFG_BASE(c) NULL #define PCI_CFG_GET(c,b,o,t) pciio_config_get(c,o,sizeof(t)) #define PCI_CFG_SET(c,b,o,t,v) pciio_config_set(c,o,sizeof(t),v) #endif The macros would be used approximately as follows: pcifoo_attach(vertex_hdl_t conn) { void * config_base = PCI_CFG_BASE(conn); ... /* retrieve current device revision */ foo_soft->fs_revision = PCI_CFG_GET(conn, config_base, PCI_CFG_REV_ID, uchar); ... /* write 0x5555AAAA test pattern to first ** vendor specific register */ PCI_CFG_SET(conn, config_base, PCI_CFG_VEND_SPECIFIC, uint32_t, 0x5555AAAA); } SEE ALSO pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_error(D3), pciio_get(D3), pciio_intr(D3). pciio_pio(D3). |
NAME
pciio_dma: pciio_dmatrans_addr, pciio_dmatrans_list, pciio_dmamap_alloc,
pciio_dmamap_addr, pciio_dmamap_list, pciio_dmamap_done,
pciio_dmamap_free, - manage DMA on PCI bus
SYNOPSIS
#include <sys/PCI/pciio.h>
iopaddr_t
pciio_dmatrans_addr(
vertex_hdl_t vhdl,
device_desc_t desc,
iopaddr_t addr,
size_t size,
unsigned flags)
alenlist_t
pciio_dmatrans_list(
vertex_hdl_t vhdl,
device_desc_t desc,
alenlist_t list,
unsigned flags)
pciio_dmamap_t
pciio_dmamap_alloc(
vertex_hdl_t vhdl,
device_desc_t desc,
size_t max,
unsigned flags)
iopaddr_t
pciio_dmamap_addr(
pciio_dmamap_t map,
iopaddr_t addr,
size_t size);
alenlist_t
pciio_dmamap_list(
pciio_dmamap_t map,
alenlist_t list);
void
pciio_dmamap_done(pciio_dmamap_t map)
void
pciio_dmamap_free(pciio_dmamap_t map)
Arguments
addr The DMA buffer address in system physical address space.
desc A device descriptor, usually zero.
flags
Attributes of the mapping.
list An address/length list as prepared by one of the alenlist
construction functions (see alenlist(D4)).
map A dma map as returned by pciio_dmamap_alloc().
max The maximum range of addresses this map will cover at any one time.
size The size of the mapped buffer in bytes.
vhdl The device connection point as passed to the attach() entry
point.
DESCRIPTION
When a device driver wishes to use Direct Memory Access (DMA) to
communicate with a device, the system needs to have a chance to set up
any appropriate mapping registers. The work to be done varies with the
available hardware and with the version of IRIX.
The functions described here provide an abstract interface to the
creation of DMA mapping objects that is consistent across most hardware.
These functions always do the least possible work given the available
hardware. (In IRIX 6.3 and on the O2 hardware, the amount of work is
minimal. In later releases and on multiprocessor platforms, the work can
be considerable.)
There are two different models for setting up a DMA map, one simple but
fallible and the other more general. In both models, the final goal is
to retrieve an address in PCI bus address space that can be used by a PCI
device to write into, or read from, system physical memory.
Simple Model
The simple model provides permanent mappings through fixed mapping
resources that may or may not exist in a given system at a given time.
pciio_dmatrans_addr() is the one-stop shopping place for using system
fixed shareable mapping resources to construct a DMA address. Such
resources are not always available, in which case, the function returns
NULL.
pciio_dmatrans_list() is similar, but operates on an address/length list
of blocks of memory and returns a list of blocks in PCI address space.
When they work, these functions allow the driver to set up DMA with the
fewest complications. Typically the functions always succeed in some
platforms (those having simple hardware mappings of PCI to memory), and
always fail in other platforms (where multiple layers of hardware
mappings must be configured dynamically). However, drivers that hope to
be portable must be coded as if the functions could succeed or fail
alternately in the same system.
General Model
It is not always possible to establish DMA mappings using common shared
system resources, so the concept of a DMA channel that preallocates
scarce mapping resources is provided.
Such a channel is allocated using pciio_dmamap_alloc(), which is given
the maximum size to be mapped. pciio_dmamap_addr() or
pciio_dmamap_list() is then applied to the map to actually establish the
proper mappings for a DMA target. Given the base address and block size
of the buffer for DMA (or a list of buffers), the functions hand back the
base PCI address to use for accessing that buffer (or a list of PCI
addresses).
When all DMA to a given buffer (or list) is complete, pciio_dmamap_done()
should be called to idle any mapping hardware (and possibly flush out any
pipes or buffers along the path that might do unexpected things when
mapping registers are modified). Later, pciio_dmamap_addr() or
pciio_dmamap_list() can again be called, specifying the same or a
different buffer area.
When a driver is completely finished with a DMA channel -- because the
channel is used only for initialization of the device, because the
driver's close() entry point is called so it is known that the device
will be idle for some time, or because the device or the driver is being
shut down -- the DMA channel resources should be released using
pciio_dmamap_free().
DMA Attribute Flags
The following attributes can be specified in the flags argument:
PCIIO_DMAMAP_CMD This map is primarily for transfer of device-to-driver
command and status data. (Flag ignored in this
release.)
PCIIO_DMAMAP_DATA This map is primarily for transfer of user data. (Flag
ignored in this release.)
PCIIO_DMAMAP_A64 The device is capable of using PCI-64 addressing.
(Flag ignored in this release.)
PCIIO_DMAMAP_LITTLEEND
demands that any byte-swapping hardware along this DMA
path be organized so that an ordered stream of bytes
from the device are deposited in order in system
memory. This is the typical setting for data streams.
If this endianness cannot be supplied, then the service
call fails.
PCIIO_DMAMAP_BIGEND
demands that any byte-swapping hardware along this DMA
path be initialized so that 32-bit quantities on PCI-
bus 32-bit boundaries maintain their binary values.
This is the typical setting for command-type
transactions because command words exchanged with a
little-endian PCI device retain their binary values.
If this endianness cannot be supplied, then the service
call fails.
When PCIIO_DMAMAP_LITTLEEND is used, the bytes of multibyte values
embedded in input data are found at their original offsets. Multibyte
values from little-endian devices may require programmed swapping before
use.
When PCIIO_DMAMAP_BIGEND is used,
o Single bytes in input data are found at the offset the device places
them, exclusive-or with 3.
o 16-bit quantities in input data are found at the offset used by the
device, exclusive-or with 2, and do not need to be byteswapped.
o 32-bit values are found at the expected offset, and do not need to be
byteswapped.
o 64-bit values are found at the expected offset, and their 32-bit
halves need to be swapped before use.
Source Compatibility With IRIX 6.4
In IRIX 6.4 and subsequent releases, the flag names given above are
renamed, and additional useful flags are defined and supported. For easy
source compatibility with later releases, insert the following code:
#ifdef _EARLY_PCI
# define PCIIO_DMA_CMD PCIIO_DMAMAP_CMD
# define PCIIO_DMA_DATA PCIIO_DMAMAP_DATA
# define PCIIO_DMA_A64 PCIIO_DMAMAP_A64
# define PCIIO_BYTE_STREAM PCIIO_DMAMAP_LITTLEEND
# define PCIIO_WORD_VALUES PCIIO_DMAMAP_BIGEND
#endif
Then use the defined names such as PCIIO_BYTE_STREAM when creating DMA
maps, instead of the flag values mentioned above.
When porting to a later release of IRIX, be sure to read this reference
page for that release to see what flags are supported.
In IRIX 6.4 and later releases, pciio_dmamap_list() takes a third
argument, flags, with the same meaning as the other flags arguments. In
order to simplify source compatibility with later releases, you could use
the _EARLY_PCI identifier to code a macro in this form:
#ifdef _EARLY_PCI
# define PCIIO_DMAMAP_LIST(a,b) pciio_dmamap_list(a,b)
#else
# define PCIIO_DMAMAP_LIST(a,b) pciio_dmamap_list(a,b,0)
#endif
However, the flags supported in later releases are sufficiently useful
you should probably recode the calls to use nonzero flags.
EXAMPLES
Here is one way that a driver might make use of dmamap and dmatrans
calls.
#ifdef _EARLY_PCI
# define PCIIO_DMA_CMD PCIIO_DMAMAP_CMD
# define PCIIO_DMA_DATA PCIIO_DMAMAP_DATA
# define PCIIO_DMA_A64 PCIIO_DMAMAP_A64
# define PCIIO_BYTE_STREAM PCIIO_DMAMAP_LITTLEEND
# define PCIIO_WORD_VALUES PCIIO_DMAMAP_BIGEND
#endif
pcifoo_attach(vertex_hdl_t vhdl)
{
pciio_dmamap_t command_map;
iopaddr_t command_dma;
struct pcifoo_regs *reg_pio;
struct pcifoo_ring *command_ring;
...
/*
* This driver has decided to use a dmamap
* to get to its command rings, which contain
* things like DMA addresses and counts; we
* set PCIIO_WORD_VALUES so we don't have to
* byteswap the 32-bit values.
*
* We still have to swap the upper and lower
* halves of the 64-bit values.
*/
/* allocate the channel
*/
command_map = pciio_dmamap_alloc(
vhdl, 0,
RINGBYTES,
PCIIO_DMA_CMD | PCIIO_WORD_VALUES);
command_dma = pciio_dmamap_addr(
command_map,
kvtophys(command_ring),
RINGBYTES);
/* tell the device where it can find
* it's command rings.
*/
reg_pio->command_dma = command_dma;
...
}
{
caddr_t data_buffer;
size_t data_size;
...
data_dma = pciio_dmatrans_addr(
vhdl, 0,
kvtophys(data_buffer), data_size,
PCIIO_DMA_DATA | PCIIO_DMA_A64 | PCIIO_BYTE_STREAM);
command_ring->data_dma_lo = data_dma & 0xFFFFFFFF;
command_ring->data_dma_hi = data_dma >> 32;
command_ring->data_dma_size = data_size;
command_ring->ready = 1;
}
SEE ALSO
alenlist(D3), pciio(D3), pciio_config(D3), pciio_error(D3),
pciio_get(D3), pciio_intr(D3), pciio_pio(D3).
DIAGNOSTICS
pciio_dmatrans_addr() returns zero if shared (fixed) resources can not be
used to construct a valid PCI address that maps to the desired range of
physical addresses. (Fixed resources are always available in IRIX 6.3
for O2, but may not be in other systems.)
pciio_dmatrans_list() returns a null pointer if any of the requested
physical address blocks can not be reached using shared fixed resources,
or if unable to allocate a return list.
pciio_dmamap_alloc() returns a null pointer if resources can not be
allocated to establish DMA mappings of the requested size, or if the
parameters are inconsistant.
pciio_dmamap_addr() returns zero if the specified target address can not
be mapped using the specified DMA channel. This would usually be due to
specifying a target block that is outside the previously specified target
area or is larger than the previously specified maximum mapping size. It
may also return a null pointer if the DMA channel is currently in use and
has not been marked idle by a call to pciio_dmamap_done().
pciio_dmamap_list() can return a null pointer for all the reasons
mentioned above, or if it is unable to allocate the return list.
|
NAME
pciio_error - IRIX 6.3 PCI error interface
SYNOPSIS
#include <sys/PCI/pciio.h>
int
typedef int
(pciio_error_handler_f)( vertex_hdl_t vhdl,
int error_code,
ioerror_mode_t mode,
ioerror_t *ioerror);
Arguments
vhdl The connection point of the PCI device with the error.
error_code
Bit-set describing the error type.
mode Code for the type of device access when the error was detected.
ioerror Error mode structure with more information.
DESCRIPTION
A PCI device driver can pass the address of an error-handling function to
the pciio_add_attach() function documented in pciio(d3). The error-
handling function must has the prototype shown; that is, it must agree
with type pciio_error_handler_f. When a NULL is passed to
pciio_add_attach(), the PCI infrastructure handles all errors.
When an error occurs, the handler is called. The error_code value
contains a set of the bits defined in sys/mace.h, as follows:
#define PERR_MASTER_ABORT 0x80000000
#define PERR_TARGET_ABORT 0x40000000
#define PERR_DATA_PARITY_ERR 0x20000000
#define PERR_RETRY_ERR 0x10000000
#define PERR_ILLEGAL_CMD 0x08000000
#define PERR_SYSTEM_ERR 0x04000000
#define PERR_INTERRUPT_TEST 0x02000000
#define PERR_PARITY_ERR 0x01000000
#define PERR_OVERRUN 0x00800000
#define PERR_RSVD 0x00400000
#define PERR_MEMORY_ADDR 0x00200000
#define PERR_CONFIG_ADDR 0x00100000
#define PERR_MASTER_ABORT_ADDR_VALID 0x00080000
#define PERR_TARGET_ABORT_ADDR_VALID 0x00040000
#define PERR_DATA_PARITY_ADDR_VALID 0x00020000
#define PERR_RETRY_ADDR_VALID 0x00010000
The mode value is one of the following, defined in sys/PCI/pci_compat.h
(which in turn is included by sys/PCI/pciio.h):
typedef enum {
MODE_DEVPROBE, /* Probing mode. Errors not fatal */
MODE_DEVERROR, /* Error while system is running */
MODE_DEVREENABLE /* Reenable pass */
}ioerror_mode_t;
The structure addressed by ioerror is also declared in
sys/PCI/pci_compat.h and contains numerous fields that may give the
handler additional information.
The handler returns nonzero when it has handled the error adequately.
The handler returns zero when it wants the PCI infrastructure to handle
the error.
NOTES
This error-handling interface is unique to IRIX 6.3 for O2. In IRIX 6.4
and onward, a different interface is used: the driver calls a function
pciio_error_register(), to register an error handler that takes fewer,
and different, arguments.
The error-handling function in IRIX 6.3 receives information that is
unique to the hardware of the O2 workstation. For example, all the
declarations in sys/mace.h are unique to the MACE chip that manages bus
and memory access in the O2. None of this information is relevant in
other platforms such as the OCTANE workstation. None of this information
is available in later releases of IRIX.
There is no simple way to make PCI error-handling in IRIX 6.3 source-
compatible with later releases.
SEE ALSO
pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_get(D3),
pciio_intr(D3), pciio_pio(D3).
|
NAME
pciio_get: pciio_info_get, pciio_info_bus_get, pciio_intr_cpu_get,
pciio_dma_dev_get, pciio_info_dev_get, pciio_intr_dev_get,
pciio_pio_dev_get, pciio_dma_slot_get, pciio_info_slot_get,
pciio_pio_slot_get, pciio_pio_mapsz_get, pciio_pio_pciaddr_get,
pciio_pio_space_get, pciio_info_vendor_id_get, pciio_info_device_id_get,
pciio_info_function_get - interrogate PCI infrastructure
SYNOPSIS
#include <sys/PCI/pciio.h>
vertex_hdl_t
pciio_intr_cpu_get(pciio_intr_t intr)
vertex_hdl_t
pciio_intr_dev_get(pciio_intr_t intr)
vertex_hdl_t
pciio_pio_dev_get(pciio_piomap_t piomap)
pciio_slot_t
pciio_pio_slot_get(pciio_piomap_t piomap)
pciio_space_t
pciio_pio_space_get(pciio_piomap_t piomap)
iopaddr_t
pciio_pio_pciaddr_get(pciio_piomap_t piomap)
ulong
pciio_pio_mapsz_get(pciio_piomap_t piomap)
vertex_hdl_t
pciio_dma_dev_get(pciio_dmamap_t dmamap)
pciio_slot_t
pciio_dma_slot_get(pciio_dmamap_t dmamap)
pciio_info_t
pciio_info_get(vertex_hdl_t vhdl)
vertex_hdl_t
pciio_info_dev_get(pciio_info_t info)
pciio_slot_t
pciio_info_bus_get(pciio_info_t info)
pciio_function_t
pciio_info_function_get(pciio_info_t info)
pciio_slot_t
pciio_info_slot_get(pciio_info_t info)
pciio_vendor_id_t
pciio_info_vendor_id_get(pciio_info_t info)
pciio_device_id_t
pciio_info_device_id_get(pciio_info_t info)
Arguments
intr A PCI interrupt object handle returned by pciio_intr_alloc().
piomap A PCI PIO map returned by pciio_piomap_alloc().
dmamap is a pciio_dmamap_t that was created by pciio_dmamap_alloc()
vhdl A pci connection point in the hardware graph, obtained as the
parameter to the attach call.
info A PCI info object returned by pciio_info_get().
DESCRIPTION
These routines are used to pull specific useful bits of information out
of the various opaque data structures used by the PCI infrastructure.
Few drivers will need to make use of these routines, but having them
available might save the driver from doing extra bookkeeping.
Interrupt Queries
Two functions fetch parameters from an interrupt object:
o pciio_intr_dev_get() returns the connection point of the interrupt
device.
o pciio_intr_cpu_get() returns the CPU that is the target of interrupts
for that PCI bus.
PIO Map Queries
Several functions return items based on a PIO map (see pciio_pio(D3)):
o pciio_pio_dev_get() returns the connection point of the mapped
device.
o pciio_pio_mapsz_get() returns the map maximum size.
o pciio_pio_pciaddr_get() returns the base address specified for the
map.
o pciio_pio_space_get() returns the target address space that was
specified.
o pciio_pio_slot_get() returns the slot number on the PCI bus for a
device.
DMA Map Queries
Two functions return items based on a DMA as map (see pciio_dma(D3)):
o pciio_dma_dev_get() returns the connection point of the mapped
device.
o pciio_dma_slot_get() returns the slot number on the PCI bus for a
device.
Info Structure Queries
The PCI infrastructure stores a version-dependent information structure
in the connection point for a PCI device. Several functions are provided
to retrieve and interrogate this structure. Those most likely to be
useful to a device driver are:
o pciio_info_get() returns a handle to the information structure. The
driver can save this handle at attach time to avoid the small
overhead of looking it up each time it is needed.
o pciio_info_dev_get() returns the vertex handle of the connection
point (from which the information structure was originally
retrieved).
o pciio_info_bus_get() returns the bus number, always 0 unless the
system has more than one PCI bus. Bus numbers are arbitrary, not
necessarily sequential.
o pciio_info_slot_get() returns the PCI card slot number of the device.
o pciio_info_function_get() returns the PCI function number, 0 unless
the device is part of a multifunction card.
o pciio_info_vendor_id_get() returns the vendor ID configuration value
of the device.
o pciio_info_device_id_get() returns the device ID configuration value
of the device.
SEE ALSO
pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_error(D3),
pciio_intr(D3), pciio_pio(D3).
DIAGNOSTICS
pciio_info_get() returns NULL if there is no pciio info structure
attached to that vertex.
Do not pass info as NULL to any of these functions, that would cause a
kernel panic.
|
NAME
pciio_intr: pciio_intr_alloc, pciio_intr_connect, pciio_intr_disconnect,
pciio_intr_free - manage PCI Interrupts
SYNOPSIS
#include <sys/PCI/pciio.h>
pciio_intr_t
pciio_intr_alloc(
vertex_hdl_t vhdl,
device_desc_t desc,
pciio_intr_line_t lines,
vertex_hdl_t owner)
int
pciio_intr_connect(
pciio_intr_t intr,
intr_func_t func,
intr_arg_t arg,
void *thread)
void
pciio_intr_disconnect(pciio_intr_t intr)
void
pciio_intr_free(pciio_intr_t intr)
Arguments
arg A parameter to pass to func() when this particular interrupt
occurs, commonly a pointer to a driver-private data structure.
desc A device descriptor containing an interrupt priority level.
func The function to perform interrupt service.
intr The interrupt channel handle returned by pciio_intr_alloc().
lines Specifies one or more of the PCI Interrupt pins used by the
device.
owner An appropriate vertex handle to use when printing messages about
this particular interrupt, and is usually a vertex created by the
device driver.
vhdl The PCI device connection point as passed to the driver attach()
entry point.
thread Reserved, should be NULL.
DESCRIPTION
When a device driver wishes to accept interrupt events from a device, the
system needs to make sure that there is a path from the PCI interrupt pin
to the appropriate CPU interrupt hardware. This is split into two phases
-- establishing the channel and connecting a service function -- so that
the service function can be changed or disconnected without losing the
allocated hardware resources.
The driver is responsible for connecting an interrupt handler when the
device needs one, and for disconnecting the handler when it does not.
The interrupt delivery mechanism depends on the address of the interrupt
function. It is important to disconnect interrupts before a driver
unloads, otherwise the PCI infrastructure might call a nonexistent
function. (A driver cannot be auto-loaded when an interrupt occurs.)
The necessary sequence of calls is based on the use of the driver entry
points, as follows.
At the reg() or init() entry point the driver registers to handle a class
of PCI devices, triggering attach() calls.
At the attach() entry, the driver calls pciio_intr_alloc() to establish
interrupt connectivity between the device and the processor. The
designated interrupts are disabled at this point. If interrupts can
occur and are needed at this time, a call to pciio_intr_connect() enables
interrupts and directs them to the designated handler.
At the unload() entry, the driver text is going to be removed, so it is
important for all interrupts to be disconnected by calling
pciio_intr_disconnect() as appropriate. It is not necessary to call
pciio_intr_free() at this time.
Some devices do not require interrupt service when they are not open.
Leaving an interrupt allocated but not connected keeps the interrupt
disabled, possibly reducing impact on the system from handling interrupts
from devices that do not actually need service.
If this is the situation, then the scenario above may be somewhat
simplified:
attach() Allocate the interrupt to establish a connection and disable
the interrupt. Only connect the interrupt if interrupts are
required as part of device initialization; then disconnect
it.
open() If the interrupt is not yet connected, connect it.
close() No processes have the device open; disconnect the interrupt
when all pending I/O is complete or purged.
unload() The driver is not called to unload when one of its devices is
open, so no interrupts should be connected.
Specifying PCI Interrupt Lines
The lines parameter is formed by or-ing together appropriate flags:
PCIIO_INTR_LINE_A
PCIIO_INTR_LINE_B
PCIIO_INTR_LINE_C
PCIIO_INTR_LINE_D
Specifying the Device Descriptor
The desc value must be the address of a device_desc_t structure in which
the intr_swlevel field has been assigned a value. The data type of this
field, pl_t, is declared in sys/types.h. The header sys/ddi.h includes
sys/types.h, and also declares several external objects of type pl_t.
In IRIX 6.4 and later, it is not required to supply a device descriptor
to this function; NULL may be passed instead. Also in IRIX 6.4 and
later, a default device descriptor is readily available by a function
call. The following example shows how to code the call to
pciio_intr_alloc() in a source-compatible way:
foo_attach(vertex_hdl_t connpt)
{
#ifdef _EARLY_PCI
device_desc_t work_desc = {0};
#endif
device_desc_t *pdesc;
vertex_hdl_t foo_chardev; /* our char device */
pciio_intr_t intr;
...
/* allocate an interrupt object. This device
* uses both INTA and INTB, and routes both
* interrupts to the same function.
*/
#ifdef _EARLY_PCI
pdesc = &work_desc;
pdesc -> intr_swlevel = plhi; /* in ddi.h */
#else
pdesc = NULL; /* or: pdesc = device_desc_default_get(connpt) */
#endif
intr = pciio_intr_alloc(connpt, pdesc,
PCIIO_INTR_LINE_A | PCIIO_INTR_LINE_B,
foo_chardev);
pciio_intr_connect(intr,
foo_int_hdlr,
(intr_arg_t)foo_dev_info,
(void *)0);
...
SEE ALSO
pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_error(D3),
pciio_get(D3), pciio_pio(D3).
DIAGNOSTICS
pciio_intr_alloc() returns a null value if it can not allocate memory.
pciio_intr_connect() returns a zero for success or a negative value on
failure. Since the channel is preallocated, the only interesting failure
for this function is the attempt to use a null interrupt handle value.
|
NAME
pciio_pio: pciio_piotrans_addr, pciio_piomap_alloc, pciio_piomap_addr,
pciio_piomap_done, pciio_piomap_free, pciio_piospace_alloc,
pciio_piospace_free - programmed I/O to PCI bus
SYNOPSIS
#include <sys/PCI/pciio.h>
caddr_t
pciio_piotrans_addr(
vertex_hdl_t vhdl,
device_desc_t desc,
pciio_space_t space,
iopaddr_t addr,
size_t size,
unsigned flags)
pciio_piomap_t
pciio_piomap_alloc(
vertex_hdl_t vhdl,
device_desc_t desc,
pciio_space_t space,
iopaddr_t addr,
size_t size,
size_t max,
unsigned flags)
caddr_t
pciio_piomap_addr(
pciio_piomap_t map,
iopaddr_t addr,
size_t size);
void
pciio_piomap_done(pciio_piomap_t map)
void
pciio_piomap_free(pciio_piomap_t map)
iopaddr_t
pciio_piospace_alloc(
vertex_hdl_t vhdl,
device_desc_t desc,
pciio_space_t space,
size_t size,
size_t align)
void
pciio_piospace_free(
vertex_hdl_t vhdl,
pciio_space_t space,
iopaddr_t addr,
size_t size)
Arguments
addr The offset within the given space.
align A desired alignment in PCI address space.
desc A device descriptor with the one field intr_swlevel set to plhi.
flags Flags describing the use of the PIO map.
max The maximum size within space to be mapped at any one time.
map The map address returned by pciio_piomap_alloc().
mapp A pointer variable to receive the address of an allocated map.
size The size of the region to be mapped.
space Specifies the target PCI address space.
vhdl The PCI connection point as given to the attach() entry point.
DESCRIPTION
When a device driver wishes to use Programmed I/O (PIO) to communicate
with a device, the system needs to have a chance to set up any
appropriate mapping registers. The work to be done varies with the
available hardware and with the version of IRIX. The functions described
here provide an abstract interface that is consistent across most
hardware. These functions always do the least possible work given the
available hardware.
There are two models for setting up a PIO map, one simple but fallible,
and one more general. In both models, the final goal is to retrieve a
physical address that, when used as the operand of a store or fetch, will
access a word in PCI bus address space rather than in CPU memory address
space.
Simple Model
The simple model provides permanent mappings through fixed mapping
resources that may or may not exist in a given system at a given time.
pciio_piotrans_addr() attempts to use shared hardware resources to
construct a physical address that, whenever used, routes the transaction
to the proper target on the PCI bus. This is not always possible. When
it is not, the function returns NULL.
When it works, pciio_piotrans_addr() allows the driver to do PIO with the
fewest complications. Typically pciio_piotrans_addr() always succeeds in
some platforms (those having a simple mapping of PCI bus to memory), and
always fails in others (where multiple layers of hardware mappings must
be configured dynamically). However, a driver that uses it should be
coded as if it could succeed or fail alternately in the same system
(which it could).
General Model
It is not always possible to establish a PIO mapping using common shared
system resources, so the concept of a PIO channel that preallocates
scarce mapping resources is provided.
Such a channel is allocated using pciio_piomap_alloc(), which is given
the limits of the region that will be mapped and the maximum size to be
mapped at any time within that region. The model assumes that many
channels may be created, but that not all channels will be actively in
use at any time.
pciio_piomap_addr() is used to actually establish the proper mappings for
a PIO target. Given the offset within the target address space and the
size of the region for PIO, it returns the base address to be used for
accessing that region.
After all PIO transactions to that region are executed,
pciio_piomap_done() should be called to idle any mapping hardware and
possibly to flush out any pipes or buffers along the path that might do
unexpected things when mapping registers are modified.
Later, pciio_piomap_addr() can again be called, specifying the same or a
new target area.
When a driver is completely finished with a PIO channel -- either because
the channel is used only for initialization of the device, or because the
device or the driver is being shut down -- the PIO channel resources
should be released using pciio_piomap_free().
Utility Functions
pciio_piospace_alloc() can be used to find a block of PCI address space
that nobody else is using, which can then be used for whatever the device
and driver wish to use it for. The PCI infrastructure preallocates PCI
address space regions based on the device configuration BASE registers at
the time the bus is discovered. As a result this function is needed only
to manage a device that does not completely declare its address space
usage in its hardware configuration registers.
pciio_piospace_free() is used to release an allocation made previously by
pciio_piospace_alloc().
Specifying PCI Address Spaces
The space parameter takes on of the following values:
PCIIO_SPACE_WIN(n)
specifies one of the regions on the PCI bus decoded by the
PCI card's BASE registers. The address specified is the
offset within the decoded area, and the entire PIO region
must fit within the decoded area.
PCIIO_SPACE_CFG
requests a pointer handle that can be used to access the
configuration space for the card, via the pciio_config_get()
and pciio_config_set() functions documented in
pciio_config(D3).
Other space types are rarely needed but can be used:
PCIIO_SPACE_IO
requests a mapping into somewhere in the PCI bus I/O address
space.
PCIIO_SPACE_MEM
requests a mapping into somewhere in the PCI bus Memory
space. Since PCI bus address space is preallocated by the
kernel, this is a dangerous function to use.
PIO Attribute Flags
There are no useful values for the flags argument in this release.
Specify the argument as 0. In IRIX 6.4 and onward, some usable flags are
available.
EXAMPLES
Here is a contrived example of how one might initialize a very strange
PCI card. It is not clear that this would be the best way to do it, but
it does give an example of the relationship between the various
functions.
pcifoo_attach(vertex_hdl_t vhdl)
{
unsigned *cfgspace;
struct pcifoo_devregs *devregs;
pciio_piomap_t pmap;
pciio_piomap_t cmap;
struct pcifoo_chan_config *tune;
...
/* Get the configuration space base
* pointer.
*/
cfgspace = pciio_piotrans_addr
(vhdl, 0, PCIIO_SPACE_CFG, 0, 256, 0);
if (cfgspace == NULL) {
cmn_err(CE_ALERT,
"pcifoo_attach: pciio_piotrans_addr failed");
return -1;
}
/* Get a pointer we can use for PIO to our
* device's control registers. This call
* attempts to use fixed shared resources,
* but will allocate unshared mapping resources
* if required.
*/
devregs = pciio_pio_addr
(vhdl, 0,
PCIIO_SPACE_WIN(0), 0,
sizeof (struct pcifoo_devregs),
&pmap, 0);
if (devregs == NULL) {
cmn_err(CE_ALERT,
"pcifoo_attach: pciio_pio_addr failed");
return -1;
}
/* save cfgspace and devregs for use;
* save pmap for pciio_dmamap_free
* call if/when we are unregistered.
*/
...
/* pretend our "channel" space is too big
* to successfully map with piotrans, so
* we have to use piomap, and that it is
* too big for us to get it in one call
* to piomap_addr.
*/
cmap = pciio_piomap_alloc(vhdl, 0,
PCIIO_SPACE_WIN(2), 0, CHAN_SEP * CHANS,
sizeof (struct pcifoo_chan_config), 0);
for (chan = 0; chan < chans; ++chan) {
tune = (struct pcifoo_chan_config *)
pciio_piomap_addr(cmap, CHAN_SEP * chan,
sizeof (struct pcifoo_chan_config));
/* now fiddle with this particular channel */
tune->chan = chan + 2;
tune->volume = 5;
tune->balance = 0;
pciio_piomap_done(cmap);
}
pciio_piomap_free(cmap);
...
}
NOTES
It is not necessary to separately establish mappings for each individual
PIO target register. It is customary and more efficient to use a single
mapping to cover the entire register set of a device.
SEE ALSO
pciio(D3), pciio_config(D3), pciio_dma(D3), pciio_error(D3),
pciio_get(D3), pciio_intr(D3).
DIAGNOSTICS
pciio_piotrans_addr() returns a null pointer when shared (fixed)
resources can not be used to construct a valid physical address that maps
to the desired range of PCI addresses.
pciio_pio_addr() returns a null pointer when the target PCI address can
not be mapped either with shared (fixed) resources, or with unshared
mapping resources. If this happens, and the object being mapped is
large, it might be possible to set up mappings to smaller regions of the
target space.
pciio_piomap_alloc() returns a null pointer when resources can not be
allocated to establish PIO mappings to the described region, or if the
function parameters are inconsistant.
pciio_piomap_addr() returns a null pointer when the specified target
address can not be mapped using the specified PIO channel. This would
usually be due to specifying a target block that is outside the
previously specified target area or is larger than the previously
specified maximum mapping size. It may also return a null pointer if the
PIO channel is currently in use and has not been marked idle by a
pciio_piomap_done() call.
|