Chapter 15. PCI Device Drivers

The Peripheral Component Interconnect (PCI) bus, initially designed at Intel Corp, is standardized by the PCI Bus Interest Group, a nonprofit consortium of vendors (see “Standards Documents” and “Internet Resources”).

The PCI bus is designed to be a high-performance local bus to connect peripherals to memory and a processor. In many personal computers based on Intel and Motorola processors, the primary system bus is a PCI bus. A wide range of vendors make devices that plug into the PCI bus.

The PCI bus is supported by the O2 workstation as well as other SGI systems. However, the O2 was the first SGI system to support PCI, and IRIX 6.3 was the first release of IRIX to contain PCI bus support. This chapter contains the following topics related to support for the PCI bus:

A PCI driver is a kernel-level device driver like other drivers. For information on the architecture of a kernel-level device driver and on how to build and debug one, see Part III, “Kernel-Level Drivers.”

PCI Bus in Silicon Graphics Workstations

This section contains an overview of the main features of PCI hardware attachment, for use as background material for software designers. Hardware designers can obtain a detailed technical paper on PCI hardware through the Silicon Graphics Developer Program. Design issues such as power supply capacities, card dimensions, signal latencies, and arbitration, are covered in that material.

PCI Bus and System Bus

In no Silicon Graphics system is the PCI bus the primary system bus. The primary system bus is always a proprietary bus that connects one or more CPUs with high-performance graphics adapters and main memory. The PCI bus adapter is connected (or “bridged,” in PCI terminology) to the system bus, as shown in Figure 15-1.

Figure 15-1. PCI Bus In Relation to System Bus


The PCI adapter is a custom circuit with these main functions:

  • To act as a PCI bus target when a PCI bus master requests a read or write to memory

  • To act as a PCI bus master when a CPU requests a PIO operation

  • To manage PCI bus arbitration, allocating bus use to devices as they request it

  • To interface PCI interrupt signals to the system bus and the CPU

Different SGI systems have different PCI adapter ASICs. Although all adapters conform to the PCI standard level 2.1, there are significant differences between them in capacities, in optional features such as support for the 64-bit extension, and in performance details such as memory-access latencies.

Buses, Slots, Cards, and Devices

A system may contain one or more PCI bus adapters. Each bus connects one or more physical packages. The PCI standard allows up to 32 physical packages on a bus. A “package” may consist of a card plugged into a slot on the bus. However, a “package” can also consist of an internal chipset mounted directly on the system board, using the PCI bus and occupying one or more virtual slots on the bus. For example, the SCSI adapter in the O2 workstation occupies the first two virtual slots of the PCI bus in that system.

Each physical package can implement from one to eight functions. A PCI function is an independent device with its own configuration registers in PCI configuration space, and its own address decoders.

In Silicon Graphics systems, each PCI function is integrated into IRIX as a device. A PCI device driver manages one or more devices in this sense. A driver does not manage a particular package, or card, or bus slot; it manages one or more logical devices.

PCI Implementation in O2 Workstations

In the O2 workstation, a proprietary system bus connects the CPU, multimedia devices (audio, video, and graphics) and main memory.

The PCI bus adapter interfaces one PCI bus to this system bus. The PCI bus adapter is a unit on the system bus, on a par with the other devices. The PCI bus adapter competes with the CPU and with multimedia I/O for the use of main memory.

The built-in SCSI adapter, which is located on the main system board, is logically connected to the PCI bus and takes the place of the first two “slots” on the PCI bus, so that the first actual slot is number 2.

Unsupported PCI Signals

In the O2, the PCI adapter implements a standard, 32-bit PCI bus operating at 33 MHZ. The following optional signal lines are not supported.

  • The LOCK# signal is ignored; atomic access to memory is not supported.

  • The cache-snoop signals SBO# and SDONE are ignored.

  • The JTAG signals are not supported.

64-bit Address and Data Support

The O2 PCI adapter supports 64-bit data transfers, but not 64-bit addressing. All bus addresses are 32 bits, that is, all PCI bus virtual addresses are in the 4 GB range. The Dual Address Cycle (DAC) command is not supported (or needed).

The 64-bit extension signals AD[63:32], C/BE#[7:4], REQ64# and ACK64# are pulled up as required by the PCI standard.

When the PCI bus adapter operates as a bus master (as it does when implementing a PIO load or store for the CPU), the PCI adapter generates 32-bit data cycles.

When the PCI bus adapter operates as a bus target (as it does when a PCI bus master transfers data using DMA), the PCI adapter does not respond to REQ64#, and hence 64-bit data transfers are accomplished in two, 32-bit, data phases as described in the PCI specification.

Configuration Register Initialization

When the IRIX kernel probes the PCI bus and finds an active device, it initializes the device configuration registers as follows:

Command Register

The enabling bits for I/O Access, Memory Access, and Master are set to 1. Other bits, such as Memory Write and Invalidate and Fast Back-to-Back are left at 0.

Cache Line Size

0x20 (32, 32-bit words, or 128 bytes).

Latency Timer

0x30 (48 clocks).

Base Address registers

Each register that requests memory or I/O address space is programmed with a starting address. In the O2 system, memory addresses are always greater than 0x8000 0000.

The device driver is free to set any other configuration parameters in the pfxattach() entry point (see “Attaching a Device”).

Address Spaces Supported

The relationship between the PCI bus address space and the system memory physical address space differs from one system type to another.

The O2 workstation and related systems support a 1 GB physical memory address space (30 bits of physical address used). Any part of physical address space can be mapped into PCI bus address space for purposes of DMA access from a PCI bus master device. The device driver ensures correct mapping through the use of a DMA map object (see “Allocating DMA Maps”).

Physical memory can be mapped to the PCI bus in normal order or byte-swapped order. Byte-swapping is done on the basis of 64-bit units. When a PCI bus master uses a byte-swapped DMA address as its target, and writes the 64-bit data item 0x0807 0605 0403 0201, the data 0x0102 0304 0506 0708 is delivered to memory.

PIO Address Mapping

For PIO purposes (the CPU loading and storing in device space), memory space defined by each PCI device in its configuration registers is allocated in the upper two gigabytes of the PCI address space, above 0x8000 0000. These addresses are allocated dynamically, based on the contents of the configuration registers of active devices. The I/O address space requested by each PCI device in its configuration registers is also allocated dynamically as the system comes up.

It is possible for a PCI device to request (in the initial state of its Base Address Registers) that its address space be allocated in the first 1 MB of the PCI bus. This request cannot be honored in the O2 workstation. Devices that cannot decode bus addresses above 0x8000 0000 are not supported.

Device drivers get a virtual address to use in PIO access by creating a PIO map (see “Managing PIO Maps for PCI”).

Slot Priority and Bus Arbitration

Two devices that are built in to the workstation take the positions of PCI bus slots 0 and 1. Actual bus slots begin with slot 2 and go up to the maximum for the system (just the one slot in O2).

The PCI adapter maintains two priority groups. The lower-priority group is arbitrated in round-robin style. The higher-priority group uses fixed priorities based on slot number, with the higher-numbered slot having the higher fixed priority.

The IRIX kernel assigns slots to priority groups dynamically by storing values in an adapter register. There is no kernel interface for changing this priority assignment. The audio and the available PCI slots are in the higher priority group.

Interrupt Signal Distribution

The PCI adapter can present eight unique interrupt signals to the system CPU. The IRIX kernel uses these interrupt signals to distinguish between the sources of PCI bus interrupts. The system interrupt numbers 0 through 7 are distributed across the PCI bus slots as shown in Table 15-1 (“n.c.” means no connection).

Table 15-1. PCI Interrupt Distribution to System Interrupt Numbers

PCI Interrupt

Slot 0 (built-in device)

Slot 1(built-in device)

Slot 2

Slot 3
(When Present)

Slot 4
(When Present)

INTA#

system 0

n.c.

system 2

system 3

system 4

INTB#

n.c.

system 1

system 5

system 7

system 6

INTC#

n.c.

n.c.

system 6

system 5

system 7

INTD#

n.c.

n.c.

system 7

system 6

system 5

Each physical PCI slot has a unique system interrupt number for its INTA# signal. The INTB#, INTC#, and INTD# signals are connected in a spiral pattern to three system interrupt numbers.

Driver/Kernel Interface for PCI Access

A PCI device driver manages the operation of one or more devices. In this section, “device” has two meanings.

  • A device can be a function associated with one set of configuration registers on a PCI card. A PCI card can contain up to eight such functions, but each configuration space is treated as a separate device by IRIX..

  • A logical device is a device special file in the /dev filesystem that refers to the original PCI device. For example, a PCI card that contains a serial port might be associated with /dev/ttyd12, /dev/ttyf12, and /dev/ttym12.

Besides the usual driver entry points for a block or character driver, a PCI driver has to supply the pfxattach() entry point. This entry point is called to initialize the PCI device and, optionally, to identify any additional logical devices for that PCI device. An entry point named pfxdetach() is optional.

Besides the usual DDI/DKI functions, the PCI driver calls on kernel functions unique to the PCI bus. These functions are introduced in the following topics. For a summary, see “PCI Function Summary”.

Overview of PCI Driver Structure

A PCI driver is a kernel-level device driver that has the general structure described in Chapter 8, “Structure of a Kernel-Level Driver.” It uses the driver/kernel interface described in Chapter 9, “Device Driver/Kernel Interface.” A PCI driver can be loadable or it can be linked with the kernel. In general it is configured into IRIX as described in Chapter 10, “Building and Installing a Driver.”

PCI hardware configuration is more dynamic than the configuration of the VME, EISA or SCSI buses. With other types of bus, the driver learns the hardware configuration when the driver is loaded, and the configuration remains static afterward. IRIX support for the PCI bus is designed to allow support for dynamic reconfiguration in future systems. In principle, a PCI driver has to be designed to allow devices to be attached and detached at any time (no detaching is done in the current release).

The general sequence of operations of a PCI driver is as follows:

  1. In the pfxinit() entry point, the driver calls a kernel function to register itself as a PCI driver, specifying the kind of device it supports.

  2. When the kernel discovers a device of this type, it calls the pfxattach() entry point of the driver. The driver creates PIO maps and (optionally) DMA maps to use in addressing the device; initializes the device; and if necessary, registers an interrupt handler.

  3. In the normal upper-half entry points such as pfxopen(), pfxread(), and pfxstrategy(), the driver operates the device and transfers data.

  4. When the device generates an interrupt, the driver's registered interrupt handler is called.

  5. If the kernel learns of a bus address error on the PCI bus, it can call an error-handling function registered by the driver to find out which device caused the error.

  6. Conceptually, if the kernel learns that the device is being detached, the kernel calls the driver's pfxdetach() entry point. The driver notes the device is unusable and stops servicing it through upper-half entry points. (This feature is not implemented in the current release.)

Driver Flag Bits

As described under “Driver Flag Constant”, the pfxdevflag public name is a byte containing flags for driver characteristics. Since a PCI driver is inevitably a new driver, with no heritage in older versions of IRIX or UNIX, Silicon Graphics, Inc. strongly recommends that you design it from the start to be compatible with multiprocessors. The implications of this are discussed under “Planning for Multiprocessor Use”.

Initializing and Registering the Driver

A PCI driver must register with the kernel in order to receive notification that devices exist. In the current release this is done in two stages. First the driver calls pciio_add_attach() to introduce itself to the kernel. See reference page pciio(d3) for the function prototype. The argments passed in this call are as follows:

attach

Address of the driver's pfx attach() entry point. This is required.

detach

NULL, or address of the driver's pfx detach() entry point if implemented.

error

NULL, or address of the driver's error-handling function if any.

driver_prefix

Pointer to the driver's prefix as a character string.

major

The major number supported by this driver. The number given in the third field of the descriptive line (see “Descriptive Line”).

This call associates the driver's pfxattach() entry point with the driver's prefix string. The pfxdetach() and error-handler addresses may be passed as NULL when these are not implemented. There is no need to implement pfxdetach() in IRIX 6.3.


Note: This call to pciio_add_attach() is unique to IRIX 6.3; it is not required or allowed in subsequent releases. You can compile the call conditionally based on the definition of the variable _EARLY_PCI, which is not defined in later releases—as demonstrated in Example 15-1.



Tip: You can create a static list of major numbers as a global variable in the driver descriptive file. See “Variables Section” for an example of such an array.

The next step—and the only step required in later releases—is to call the pciio_driver_register() function (see reference page pciio(d3) for the syntax). This call specifies the PCI vendor ID and device ID numbers as they appear in the PCI configuration space of any device that this driver supports. The third argument is a character string containing the driver's prefix string (as passed to pciio_add_attach()). The kernel uses this string to search the switch tables to find the addresses of the driver's pfxattach() and pfxdetach() entry points.

Example 15-1 shows a hypothetical example of driver registration.

Example 15-1. Driver Registration


__int32_t hypo_attach(dev_t); /* forward declaration */
int hypo_init()
{
   int allMyMajors[2];
   ...
   allMyMajors[0] = myMajNum; /* see Example 10-1 */
   allMyMajors[1] = 0;
#ifdef _EARLY_PCI /* need to call pciio_add_attach */
ret = pciio_add_attach(hypo_attach, /* attach entry point */
                        NULL,NULL, /* detach, error not done */
                        "hypo_",   /* prefix string */
                        allMyMajors); /* list of one major# */
   if (!ret) ...
#endif
   ret = pciio_driver_register(HYPO_VENDOR,HYPO_DEVID,"hypo_",0);
   if (!ret) ...
}

A device driver can register multiple times to handle multiple combinations of vendor ID and device ID.


Tip: You should defer the call to pciio_driver_register() to the end of the pfxinit() routine, when all global data has been initialized. The reason is that, if there is an available device of the specified type, pfxattach() might be called immediately, before the pci_driver_register() function returns. In a multiprocessor, pfxattach() can be called concurrently with the return of pci_driver_register() and following code.

A loadable driver, when called at its pfxunload() entry point, can unregister before unloading; but that is not required. (See “Unloading”).

Attaching a Device

The IRIX support for PCI is designed to allow for future support for hot-swapping and for multinode systems in which devices, slots, buses, and whole nodes come online and offline dynamically. In principle, a PCI device can be attached or detached at any time, meaning that the pfxattach() and pfxdetach() entry points could be called at any time.

In practice, the only systems supported by IRIX 6.3 for O2 do not permit hot-swapping. Also, the administrator commands that would force a device to detach are not defined as yet. In current systems, pfxattach() is only called at boot time and pfxdetach() is not called. Nevertheless the driver/kernel interface is designed for future flexibility. For future portability you can design your driver as if that flexibility existed now.

Matching A Device to A Driver

When the system boots up, the kernel probes the PCI bus configuration space and takes a census of active devices. For each device it notes

  • Vendor and device ID numbers

  • Requested size of memory space

  • Requested size of I/O space

The kernel assigns starting bus addresses for memory and I/O space and sets these addresses in the Base Address Registers (BARs) in the device. Then the kernel looks for a driver that has registered a matching set of vendor and device IDs using pciio_driver_register().

If no matching driver has registered, the device remains inactive. For example, the driver might be a loadable driver that has not been loaded as yet. When the driver is loaded and registers, the kernel will match it to any unattached devices.

When the kernel matches a device to its registered driver, the kernel calls the driver's pfxattach() entry point. It passes one argument, a vertex_hdl_t, which acts as an opaque handle to a kernel object that describes this device. This handle is used to:

  • Store and retrieve the driver's private information about the device

  • Request PIO and DMA maps on the device

  • Register an interrupt handler for the device

Allocating Storage for Device Information

A driver needs to save information about each device, usually in a structure. Fields in a typical structure might include:

  • Locks or semaphores used for mutual exclusion among upper-half entry points and between them and the interrupt handler.

  • Addresses of allocated PIO and DMA maps for this device (see “Allocating PIO Maps”).

  • Address of an interrupt connection object for the device (see “Registering an Interrupt Handler”).

  • In a block driver, anchors for a queue of buf_t objects being filled or emptied.

  • Device status information and flags.

A problem is that at initialization time a driver does not know how many devices it will be asked to manage. For a workstation such as O2 you can expect the number will be small, but you should allow for portability to server-class systems that support dozens of devices. In the past this problem has been handled by allocating an array of a fixed number of information structures, indexed by the device minor number.

This is not a good solution for a PCI driver because PCI configuration is so dynamic. In addition, a loadable driver loses the contents of its global variables when it unloads. The IRIX PCI support gives you a different way.

In a PCI driver, you dynamically allocate memory for an information structure to hold information about the one device being attached. (See “General-Purpose Allocation”.) You save the address of the structure in the kernel's hardware vertex, using the device_info_set() function, which associates an arbitrary pointer with a vertex_hdl_t.

extern void device_info_set(vertex_hdl_t, arbitrary_info_t);

The information structure can easily be recovered in any top-half routine; see “Locating Device Information”.

Allocating PIO Maps

For almost any device you need at least one PIO map. You use a PIO map to read the memory space or the I/O space of a device. These maps can be allocated when the device is attached, and the addresses of the maps can be stored in the device information structure.

A PIO map is created by pciio_piomap_alloc(). See reference page pciio_pio(d3) for the syntax. This call requires arguments are as follows:

vhdl

The vertex_hdl_t received by the pfx attach() routine. Every map must be associated to a specific device at its hardware graph vertex.

desc

Device descriptor structure with one field set (see text following).

space

Constant specifying the space to map: PCIIO_SPACE_CFG (configuration space), or PCIIO_SPACE_WIN(n).

addr

Offset within the selected space (typically 0).

size

Span of the total area over which this map might be applied.

max

Maximum size of the area that will be mapped at any one time. When the map is always used for the same area, size and max are the same. When the map can be used for smaller segments within a larger area, max is the limit of one segment and size the size of the total extent.

flags

Passed as 0.

A PIO map that you will use to access device configuration registers is based on a a space of PCIIO_SPACE_CFG. The space selection PCIIO_SPACE_WIN(n) means that this map is to be based on Base Address register n, from 0 through 5, in the PCI configuration space. The device configuration registers specify whether a given base address register defines memory or I/O space. When the space is defined by a 64-bit base address register, use the lower number, the index of the word that contains the configuration bits.


Tip: The header file sys/PCI/pciio.h declares constants of the form PCIIO_PIOMAP_CFG and PCIIO_PIOMAP_WIN(n). You can ignore these; they are not used in any calls. The target space for any kind of map is given with PCIIO_SPACE_*.

The device descriptor structure type dev_desc_t is declared in pciio.h. A descriptor structure is required in this call, but only one field is inspected, intr_swlevel. It must be set to one of the interrupt levels of type pl_t as declared in ddi.h, typically plhi, as shown in Example 15-2


Note: In subsequent releases the device descriptor is not required and the address can be passed as NULL; but for this release it is required.

Example 15-2 shows a function that simplifies the allocation of a PIO map. The space is passed as an argument, as is the size of the space to map. The function makes the simplifying assumption that the map should start at offset 0 in the selected space.

Example 15-2. Allocation of PCI PIO Map


pciio_piomap_t makeMap(vertex_hdl_t dev, int base, size_t size)
{
   struct device_desc_s ddesc;
   ddesc.intr_swlevel = plhi;
   return pciio_piomap_alloc(
         dev,         /* vertex handle */
         &ddesc,      /* dev descriptor w/ in level in it */
         base,        /* space, _CFG or _WIN(n) */
         0,           /* starting offset */
         size,size,   /* size to map */
         0);          /* default endian */
}

Allocating PIO Addresses Directly

In the O2 and some other platforms, PIO addressing is based on fixed hardware resources and a PIO address can be generated without use of a map. You request this using the function pciio_piotrans_addr(). In the general case of a PIO address for memory or I/O space, this function call can fail in some systems (as discussed in reference page pciio_pio(d3)). When used to obtain a PIO address for configuration space, it generally succeeds in all systems.

Allocating DMA Maps

For a bus-master device you will need at least one DMA map. A DMA map is created by pciio_dmamap_alloc(), which takes a vertex_hdl_t, a size, and flags regarding the treatment of the mapping. (See reference page pciio_dma(d3) for the syntax of this and related functions.)

More map functions are discussed under “Managing PIO Maps for PCI” and “Managing DMA Maps for PCI”.

Reading the Device Configuration

Typically a PCI driver needs to read the device configuration registers and possibly write to them. In principle, these are PIO operations. However, in the O2 (and possibly other platforms), the special PCI bus cycle called a Configuration cycle is not generated by a simple PIO load or store.

To access the configuration, create a PIO map for the configuration space and extract an address from it. Present this address to pciio_config_get() to fetch a word from configuration space, as shown in Example 15-3. (See reference page pciio_config(d3) for the syntax of this and related functions.)

The function in Example 15-3 fetches and returns a 32-bit word from configuration space. It obtains a PIO base address for configuration space using pciio_piotrans_addr() (see “Allocating PIO Addresses Directly”). When that call succeeds, as it does in the O2 and other current SGI systems, the address is passed to pciio_config_get().

Example 15-3. Reading PCI Configuration Space


__uint32_t get_cfg_word( vertex_hdl_t vh, int reg)
{
   device_desc_t dd = {0};
   volatile __uint32_t *pio_addr;
   dd.intr_swlevel = plhi;
   pio_addr = pciio_piotrans_addr(vh, dd,
               PCIIO_SPACE_CFG,0,256,0);
   if (pio_addr) /* trans_addr succeeded */
      return pciio_config_get(pio_addr,reg);
   else /* trans_addr failed, simulate hardware failure */
      return (__uint32_t)(-1);
}

For a PCI bus master device, the pfxattach() function should set the Cache Line Size register to 128 (the size of a cache line in all Silicon Graphics systems).

Registering an Interrupt Handler

For devices that can interrupt, a key step during pfxattach() is to register an interrupt handler for the device. This is done in a two-step process. First you create an interrupt connection object; then you use that object to specify the interrupt handling function.

The interrupt connection is created with pciio_intr_alloc(), which takes a vertex_hdl_t and a flag for the interrupt line that the device uses. (See reference page pciio_intr(d3) for the syntax.)

The interrupt object is used in establishing a handler, and it is needed later to stop taking interrupts (see “Inactivating an Interrupt Handler”). You probably want to save its address in the device information structure for later use.

After creating the interrupt object, you establish a handler using pciio_intr_connect(). Its principal arguments are the interrupt object, a handler address, and a value to be passed to the handler when it is called—typically the address of the device information structure you are preparing. If a device will interrupt on line C, interrupt setup could resemble Example 15-4.

Example 15-4. Setting Up a PCI Interrupt Handler


pciio_intr_t intobj;
extern void int_handler(eframe_t *, void *);
int retcode;
intobj = pciio_intr_alloc(
               vhdl, /* as received in attach() */
               0,    /* device descriptor is n.a. for pci */
               PCIIO_INTR_LINE_C, /* the line it uses */
               (vertex_hdl_t) 0);
retcode = pciio_intr_connect(
               intobj, /* the interrupt object */
               (intr_func_t) int_handler, /* the handler */
               (intr_arg_t) pDevInfo, /* dev info as input */
               (void*)0 ); /* threads are next release */
if (!retcode) cmn_err(CE_WARN,"oh fiddlesticks");


Note: The declaration of the interrupt handler function type, intr_func_t, requires two arguments, the first being an eframe_t. The availability of the eframe_t is unique to IRIX 6.3 for O2. In subsequent releases the interrupt handler receives only one argument, the value passed with the pciio_intr_connect() call.

The CPU is accepting interrupts when the pfxattach() entry point is called. If the PCI device is in a state that can produce an interrupt, the interrupt handling function can be called before pciio_intr_connect() returns. Make sure that all global data used by the interrupt handler has been initialized.

PCI devices can share the four PCI interrupt lines. As a result, in some cases the kernel cannot tell which device caused an interrupt. When there is any doubt, the kernel calls all the interrupt handlers that are registered to that interrupt line. For this reason, your interrupt handler must not assume that its device did cause the interrupt. It should always test to see if an interrupt is really pending, and exit immediately when one is not.

Return Value from Attach

The return code from pfxattach() is tested by the kernel. The driver can reject an attachment. When your driver cannot allocate memory, or fails due to another problem, it should:

  • Use cmn_err() to document the problem (see “Using cmn_err”)

  • Release any objects such as PIO and DMA maps that were created.

  • Release any space allocated to the device such as a device information structure.

  • Return an informative return code which might be meaningful in future releases.

More than one driver can register to support the same vendor ID and device ID. When the first driver fails to complete the attachment, the kernel continues on to test the next, until all have refused the attachment or one accepts it. The pfxdetach() entry point can only be called if the pfxattach() entry point returns success (0).

Establishing Logical Devices

Some kinds of physical devices are represented by multiple device special files in /dev. For example, each serial port appears as at least four devices /dev/tty*. A tape drive can appear under different names, and a disk device has two device special files for each disk partition, one in /dev/dsk and one in /dev/rdsk (raw, or character access). Each logical device represents a slightly different treatment of the same physical device.

The pfxattach() entry point initializes the real PCI device, but it must also create hardware vertexes to represent the logical devices that should be associated with the same real PCI device. This is done in three steps:

  • Create a new hardware vertex connected to the attached vertex, using hwgraph_device_add().

  • Associate the new vertex with the minor number of the logical device, using hwgraph_device_add_minor().

  • Associate the new vertex with the same device information structure, using device_info_set().

The hwgraph_device_add() function has the following prototype:

int hwgraph_device_add(vertex_hdl_t vhdl, /* parent vertex */
                       char *name, /* name of the device */
                       char *prefix, /* driver prefix */
                       vertex_hdl_t *new_vrtx) /* return result */

The name argument is not significant in the current release, but it will be significant and visible to users in a future release. It should be one word or numeric characters to label this vertex of the hardware graph, for example “0” (logical unit number) or “nonswap” (feature or access method).

For a simplified example, see Example 15-5. This hypothetical code, which would be part of the hypo_attach() entry point, creates two logical devices. The device minor number of the first is 0x01; the second is 0x02—a simplified version of the conventions for minor numbers of tape or serial devices, in which the minor number bits represent device options or features.

Example 15-5. Creating Logical Devices for a PCI Device


vertex_hdl_t subdev;
int ret;
my_dev_info_t *pDev; /* struct stored in PCI vertex */
...
ret = hwgraph_device_add(vhdl, /* attach() input */
                         "left", /* name of minor 01 */
                         "hypo_", /* driver prefix */
                         &subdev); /* output here */
if (ret)...
ret = hwgraph_device_add_minor(subdev,(minor_t)0x01);
if (ret)...
device_info_set(subdev,my_dev_info);
ret = hwgraph_device_add(vhdl, /* attach() input */
                         "right", /* name of minor 02 */
                         "hypo_", /* driver prefix */
                         &subdev); /* output here */
if (ret)...
ret = hwgraph_device_add_minor(subdev,(minor_t)0x02);
if (ret)...
device_info_set(subdev,my_dev_info);

Normal Operation

While handling normal operations on the device, the driver needs to locate device information from the top-half entry points, and needs to translate addresses using maps.

Locating Device Information

The driver upper-half entry points are called to implement requests from user processes or filesystems that need to open, read, write, map or control the device. These calls can occur at any time; and on a multiprocessor, they can occur multiple times concurrently, on parallel CPUs.

The user process refers to a device through a file descriptor opened to a device special file. The primary argument to any upper-half entry point is the dev_t, a value that distinguishes the device. Traditional drivers extract device numbers from the dev_t (see “The Device Number Types”).

The first thing any upper-half entry point needs to do is to locate the per-device information structure that was prepared in the pfxattach() entry point (see “Allocating Storage for Device Information”). You do this by calling device_info_get(). However, that function takes a vertex_hdl_t. You get that from dev_to_vhdl(). The code, which is repeated over and over in a PCI driver, resembles Example 15-6.

Example 15-6. Retrieving Device Information


vertex_hdl_t vhdl = dev_to_vhdl(dev);
my_dev_info_t *pDev = (my_dev_info_t)device_info_get(vhdl);
   if (!pDev) return(ENXIO);
   if (!(pDev->status & USABLE)) return(ENXIO);

In the pfxopen() entry point, the dev_t is received as a reference argument, not by value.

Verifying Device Usability

In the event that device_info_get() returns NULL, this device has not been attached, or the pfxattach() entry point did not allocate an information structure; or the pfxdetach() entry point has been called. In such cases, the upper-half routine should return ENXIO (no such device). This test is shown in Example 15-6.

Example 15-6 also shows another test. In a future release of IRIX, a driver will be able to implement a pfxdisable() entry point, called to make a device temporarily unusable. Even in the current release, your driver might find reasons, such as a persistent device error, to force a device offline. A single flag bit in the device information structure represents this state. Again, a return of ENXIO is appropriate.

Managing PIO Maps for PCI

The functions that are used to manage PIO maps are summarized in Table 15-2. See reference page pciio_pio(d3) for details.

Table 15-2. Functions for PIO Maps for PCI

Function

Purpose and Operation

pciio_piomap_addr()

Get a kernel virtual address from a PIO map for a specific offset and length.

pciio_piomap_alloc()

Create a PIO map object, specifying the bus address space, base offset, and length it needs to cover.

pciio_piomap_done()

Make a PIO map inactive until it is next needed (may release hardware resources asslociated to the map).

pciio_piomap_free()

Release a PIO map object.

pciio_piotrans_addr()

Request immediate translation of a bus address to a kernel virtual address without use of a PIO map. Returns NULL if this system does not support fixed PIO addressing for the requested space.

pciio_config_get()

Fetch a 32-bit value from configuration space using an address returned by pciio_piomap_addr().

pciio_config_set()

Store a 32-bit value into configuration space using an address returned by pciio_piomap_addr().

Maps are allocated with pciio_piomap_alloc(). Its use is covered under “Allocating PIO Maps”, because you typically will allocate the PIO maps you need while attaching the device.

You use a PIO map by calling pciio_piomap_addr(). This function takes a map, an offset within the PCI address space described by the map, and the number of bytes of space that the address will be used to retrieve.

The pciio_addr argument is added to the base offset specified to pciio_piomap_alloc(), and that in turn is added to the base address assigned by the kernel to this device, to arrive at the bus address needed. The byte_count argument specifies how many bytes beyond the bus address you may access. The returned value is a kernel virtual address that is mapped to the requested PCI bus address for at least that many bytes.


Tip: A program variable based on a PIO address should always be declared as “volatile.”

Once you have extracted an address using pciio_piomap_addr(), the map is active. It remains active until you call either pciio_piomap_done() or pciio_piomap_free(). In some systems, it costs nothing to keep a PIO map active. In other systems, an active PIO map may tie up global hardware resources. It is is a good idea to call pciio_piomap_done() when the address is no longer needed.

Some systems also support a one-step translation function, pciio_piotrans_addr(), as described under “Allocating PIO Addresses Directly”. However, this function can fail in systems that do not use hard-wired bus maps. The two-step process of allocating a map and then interrogating it is more general.

pciio_piotrans_addr() always succeeds when getting a PIO address in configuration space. Access to configuration space is done in two steps. First you get a PIO address; then you pass the address to pciio_config_get() or pciio_config_get(). An example is shown under “Reading the Device Configuration”..

Using Byte-Level PCI Addresses

When you use PIO to fetch or store 32-bit values on 32-bit-aligned PCI addresses, PIO works as you would expect, and a 32-bit value is fetched or returned.

However, when you use PIO to fetch or store less than 32 bits—either a 16-bit value or an 8-bit value—you must use an address that takes account of byte-swapping. The least significant address bits are summarized in Table 15-3.

Table 15-3. Least Significant Address Bytes for Short PIO

Binary Offset Within 32-bit Memory Word

LSB for 16-Bit Access

LSB for 8-bit Access

0x00

0x02

0x03

0x01

n.a.

0x02

0x02

0x00

0x01

0x03

n.a.

0x00

You can deal with this complication in either of three ways, as follows:

  • Always fetch and store 32-bit words. Unpack smaller units in memory.

  • Declare the device data as a structure and arrange the order of short fields in the structure so that the least significant address bits work out correctly. For example if the device offers the following logical structure in its PCI memory space:

    00--> dma base addr, 4 bytes
    04--> dma counter, 4 bytes
    08--> control reg, 2 bytes
    0A--> status reg, 1 byte
    0B--> byte fifo, 1 byte
    

    Declare this as a C structure as follows:

    struct {
       unsigned        dma_addr;
       unsigned        dma_count
       unsigned char   byte_fifo;
       unsigned char   status;
       unsigned short  control
    }
    

  • Write C macros for byte-address and halfword-address. The macros would use exclusive-OR to invert two or one (respectively) of the least-significant bits.

Managing DMA Maps for PCI

The functions that are used to manage simple DMA maps are summarized in Table 15-4. See reference page pciio_dma(d3) for syntax.

Table 15-4. Functions for Simple DMA Maps for PCI

Function

Purpose and Operation

pciio_dmamap_alloc()

Create a DMA map object, specifying the maximum extent of memory the map will have to cover.

pciio_dmamap_addr()

Get the bus virtual address corresponding to a memory address for a specified length.

pciio_dmamap_done()

Make a DMA map inactive (may release hardware resources asslociated to the map).

pciio_dmamap_free()

Release a DMA map object.

pciio_dmatrans_addr()

Request immediate translation of the address of a contiguous memory buffer to a bus address. Returns NULL unless this system supports fixed DMA addressing

Maps are allocated with pciio_dmamap_alloc(). Its use is covered under “Allocating PIO Maps”, because you typically will allocate the maps you need while attaching the device.

You obtain a map for a single, contiguous span of virtual memory by calling pciio_dmamap_addr(). It takes principle arguments of a map, a memory address, and a length. The value returned is a bus address that you can program into a bus master device. When the device accesses that address, it is accessing the specified memory location.

Once you have extracted an address using pciio_dmamap_addr(), the map is active. It remains active until you call either pciio_dmamap_done() or pciio_dmamap_free(). In the O2 workstation it costs nothing to keep a DMA map active. In other systems, an active map may tie up global hardware resources. It is is a good idea to call pciio_dmamap_done() when the I/O operation is complete.

In systems in which PCI space is hard-wired to specific memory addresses, pciio_dmamap_alloc() is a short function and pciio_dmamap_addr() is a trivial one. However, these systems also support a one-step translation function, pciio_dmatrans_addr(). This function takes a combination of the arguments of pciio_dmamap_alloc() and pciio_dmamap_addr(), and returns a translated address. In effect, it combines creating a map, using the map, and freeing the map, into a single step.

Managing Address-Length Lists

In some cases you are not sure whether a memory buffer is contiguous, or perhaps you are sure that it is not. In this case you need to create a list of memory addresses and lengths—one address and length for each segment of memory. Then you need to translate the segments into a list of bus addresses. The list of bus addresses can be programmed into the device one at a time or, if the device supports scatter/gather, you can program all of the list of addresses for transfer in sequence.

Support for these cases is proved by address-length lists, an abstract data type that is supported by a family of functions. The IRIX 6.4 contains a complete family of functions for address-length lists. IRIX 6.3 supports a subset necessary to use with DMA maps. The functions related to address-length lists are summarized in Table 15-5. See reference page alenlist(d4x) for an overview. For function syntax see alenlist_ops(d3x) and pciio_dma(d3).

Table 15-5. Functions for DMA Using Address-Length Lists

Function

Purpose and Operation

alenlist_destroy()

Release an address-length list.

alenlist_get()

Retrieve the next address and length pair from a list.

buf_to_alenlist()

Create an address-length list to describe the buffer represented by a buf_t object.

kvaddr_to_alenlist()

Create an address-length list to describe a buffer in kernel virtual memory.

pciio_dmamap_list()

Convert an address-length list of memory addresses into an address-length list of corresponding bus addresses.

pciio_dmatrans_list()

Request immediate conversion of an address-length list of memory addresses into an address-length list of corresponding bus addresses. Returns NULL unless this system supports fixed DMA mapping.

The function buf_to_alenlist() is called in a pfxstrategy() entry point. It takes a buf_t and returns an address-length list that describes each segment of memory in the buffer that the buf_t describes (see “Structure buf_t” and “Entry Point strategy()”. The function kvaddr_to_alenlist() takes the address and length of any buffer in kernel virtual memory and returns an address-length list to describe that extent of memory.

When you are ready to perform DMA to a buffer, you create an address-length list to describe the buffer, and pass that through pciio_dmamap_list(). This function returns a new address-length list in which the memory addresses have been replaced by PCI bus addresses.

You step through the contents of the converted address-length list using alenlist_get(), which returns successive pairs of values—an address and a length—from the list. You program each pair of values into the bus master device.

Detaching A Device

In future releases of IRIX, the pfxdetach() entry point is called when the kernel decides to detach a PCI device. This can be caused by a hardware failure or by administrator action. In practice, it does not happen at all in IRIX 6.3 for O2. You may provide the entry point, but it is not called.

Inactivating an Interrupt Handler

The functions for managing interrupt handlers are summarized in Table 15-6. See reference page pciio_intr(d3) for syntax.

Table 15-6. Functions for Managing PCI Interrupt Handlers

Function

Purpose and Operation

pciio_intr_alloc()

Create an interrupt object that enables interrupts to flow from a specified device.

pciio_intr_connect()

Associate an interrupt object with an interrupt handler function.

pciio_intr_disconnect()

Remove the association between an interrupt object and a handler function.

pciio_intr_free()

Release an interrupt object.

The allocation of an interrupt handler is covered under “Registering an Interrupt Handler”. When detaching a device, call pciio_intr_disconnect() to break the association between the interrupt and the handler function.

Inactivating Maps and Releasing Objects

There are typically various allocated objects—PIO and DMA maps, interrupt objects—that are addressed from the device information structure stored for the device. All such objects should be released at this time.

Unloading

When a loadable PCI driver is called at its pfxunload() entry point, indicating that the kernel would like to unload it, it must take great pains not to leave any dangling pointers (see “Entry Point unload()”). A driver should not unload when it has any registered interrupt handlers.

A driver does not have to unregister itself as a PCI driver before unloading. Nor does it have to detach any devices it has attached. However, if any devices are open or memory mapped, the driver should not unload.

If the kernel discovers a device and wants this driver to attach it, the kernel will reload the driver. If the driver has already attached one or more devices, the driver's information about the state of those devices is safely stored in each hardware vertex. When a process wants to open one of the devices, the driver will be reloaded automatically, and will be able to find the device information again.

PCI Function Summary

Table 15-7 contains a summary of the PCI-related kernel functions, in alphabetical order. Click on a function name to bring up its reference page (when man pages are written!).

Table 15-7. PCI-Related Kernel Functions

Function

Purpose and Operation

Discussed

alenlist_destroy()
(alenlist_ops(d3x))

Release an address-length list.

page 399

alenlist_get()
(alenlist_ops(d3x))

Retrieve the next address and length pair from a list.

page 399

buf_to_alenlist()
(alenlist_ops(d3x))

Create an address-length list to describe the buffer represented by a buf_t object.

page 399

hwgraph_device_add()

Add a device vertex for a logical device.

page 393

hwgraph_device_add_minor()

Associate a logical device vertex with a minor number.

page 393

kvaddr_to_alenlist()
(alenlist_ops(d3x))

Create an address-length list to describe a buffer in kernel virtual memory.

page 399

device_info_get()

Retrieve the address of device information from the hardware graph vertex.

page 395

device_info_set()

Store the address of device information in the hardware graph vertex.

page 395

pciio_config_get()
(pciio_config(d3))

Fetch a defined register from configuration space using a base address returned by pciio_piomap_ addr().

page 390

pciio_config_set()
(pciio_config(d3))

Store a value into one of the defined fields of configuration space using an address returned by pciio_piomap_addr().

page 390

pciio_dmamap_addr()
(pciio_dma(d3))

Get the bus virtual address corresponding to a memory address for a specified length.

page 399

pciio_dmamap_alloc()
(pciio_dma(d3))

Create a DMA map object, specifying the maximum extent of memory the map will have to cover.

page 388

pciio_dmamap_done()
(pciio_dma(d3))

Make a DMA map inactive (may release hardware resources asslociated to the map).

page 399

pciio_dmamap_free()
(pciio_dma(d3))

Release a DMA map object.

page 399

pciio_dmamap_list()
(pciio_dma(d3))

Convert an address-length list of memory addresses into an address-length list of corresponding bus addresses.

page 399

pciio_dmatrans_addr()
(pciio_dma(d3))

Request immediate translation of the address of a contiguous memory buffer to a bus address. Returns NULL unless this system supports fixed DMA addressing

page 399

pciio_dmatrans_list()
(pciio_dma(d3))

Request immediate conversion of an address-length list of memory addresses into an address-length list of corresponding bus addresses. Returns NULL unless this system supports fixed DMA mapping.

page 399

pciio_driver_register()
(pciio(d3))

Notify the kernel that this driver is ready, and tell the vendor and device numbers it supports.

page 384

pciio_driver_unregister()
(pciio(d3))

Notify the kernel this driver is not available (for example the driver is unloading).

 

pciio_intr_alloc()
(pciio_intr(d3))

Create an interrupt object that enables interrupts to flow from a specified device.

page 391

pciio_intr_connect()
(pciio_intr(d3))

Associate an interrupt object with an interrupt handler function.

page 391

pciio_intr_disconnect()
(pciio_intr(d3))

Remove the association between an interrupt object and a handler function.

 

pciio_intr_free()
(pciio_intr(d3))

Release an interrupt object.

 

pciio_piomap_addr()
(pciio_pio(d3))

Get a kernel virtual address from a PIO map for a specific offset and length.

page 396

pciio_piomap_alloc()
(pciio_pio(d3))

Create a PIO map object, specifying the bus address space, base offset, and length it needs to cover.

page 388

pciio_piomap_done()
(pciio_pio(d3))

Make a PIO map inactive until it is next needed (may release hardware resources asslociated to the map).

page 396

pciio_piomap_free()
(pciio_pio(d3))

Release a PIO map object.

page 396

pciio_piotrans_addr()
(pciio_pio(d3))

Request immediate translation of a bus address to a kernel virtual address without use of a PIO map. Returns NULL unless this system supports fixed PIO addressing.

page 396


Example Driver

The code in Example 15-7 implements a complete, working, PCI device driver for IRIX 6.3 for O2. This same source code is also available on the SGI Developer Toolbox CDROM, where you can also find the code for the user-level program that tests it.

  • Example 15-7 displays the descriptive file for /var/sysgen/master.d.

  • Example 15-8 displays the one-line VECTOR statement for /var/sysgen/system.

  • Example 15-9 displays the header file of device flags and information structure.

  • Example 15-10 displays the complete source code.

    Example 15-7. Example PCI Driver for IRIX 6.3—Descriptive File


    *
    *       Barco Chameleon card
    *
    * Loadable driver: FLAG = fdN
    * Non-loadable:    FLAG = c
    *FLAG   PREFIX  SOFT    #DEV    DEPENDENCIES
    cdN     coco_   73      -
    
    $$$
    
    
    
    

    Example 15-8. Example PCI Driver for IRIX 6.3—Configuration File


    VECTOR: bustype=PCI module=coco
    
    
    
    

    Example 15-9. Example PCI Driver for IRIX 6.3—Driver Header File


    /* =========================================
     *      Input/Output and Byte Swapping 
     * ========================================= */
    #define BYTE_SWAP16(u)  (ushort_t)(((u<<8)&0xff00)|((u>>8)&0x00ff))
    #define BYTE_SWAP32(u)  (uint_t)((u<<24)|((u<<8)&0xff0000)|((u>>8)&0xff00)|(u>>24))
    /*
    *  byte swap Input/Output
    */
    #if 0
    #define Out8(addr, b)   ( *(volatile uchar_t *)(addr) = (b) )
    #define Out16(addr, s)  ( *(volatile ushort_t *)(addr) = BYTE_SWAP16(s) )
    #define Out32(addr, w)  ( *(volatile uint_t *)(addr) = BYTE_SWAP32(w) )
    #define Inp8(addr)      ( *(volatile uchar_t *)(addr) )
    #define Inp16(addr)     BYTE_SWAP16( *(volatile ushort_t *)(addr) )
    #define Inp32(addr)     BYTE_SWAP32( *(volatile uint_t *)(addr) )
    #endif
    /*
    * Input/Output with no byte swap
    */
    #define Out8(addr, b)   ( *(volatile uchar_t *)(addr) = (b) )
    #define Out16(addr, w)  ( *(volatile ushort_t *)(addr) = (w) )
    #define Out32(addr, w)  ( *(volatile uint_t *)(addr) = (w) );flushbus()
    #define Inp8(addr)      ( *(volatile uchar_t *)(addr) )
    #define Inp16(addr)     ( *(volatile ushort_t *)(addr) )
    #define Inp32(addr)     ( *(volatile uint_t *)(addr) )
    /* ==========================
     *      Sleep/Wakeup/Lock
     * ========================== */
    #define COCO_LOCK       splhi
    #define COCO_UNLOCK(s)  splx(s)
    #define SleepEvent(x)   psema(x, (PRIBIO|PCATCH) )
    #define WakeEvent(x)    vsema(x)
    /* ==========================
     *      Misc. defaults
     * ========================== */
    #define DEVICE_ID       0x0001  
    #define VENDOR_ID       0x11a4
    #define DRIVER_PREFIX   "coco_"
    #define MAJOR_NUMBER    73
    #define AMCC_RAM_SIZE   64
    #define CONFIG_RAM_SIZE 16
    #define NORMAL_DMA_RAM_SIZE     16
    #define COCO_CONFIG_HDR 68
    #define END_OF_CHAIN    0x80000000
    #define RW_TIMER        500     /*  wait for read/write in clock ticks  */
    #define SIMRW_TIMER     500     /*  wait for sim R/W in  clock ticks     */
    #define CHAIN_FACTOR    10
    #define MAPPED_SIZE     17*NBPP
    #define COCO_CACHE_SIZE 32
    /* =================================
     *   Configuration Register bits 
     * ================================= */
    #define CONFIG_CCRES    0x00000001      /* reset when zero                 */
    #define CONFIG_FRES     0x00000002      /* Input/Output Fifo reset when 0  */
    #define CONFIG_FSDATI   0x00000004      /* Inp. Fifo serial config data    */
    #define CONFIG_FSDATO   0x00000008      /* Out. Fifo serial config data    */
    #define CONFIG_FSCLK    0x00000010      /* In/Out Fifo serial config clock */
    #define CONFIG_LUTSEL   0x00000020      /* External LUT bank selection (=0) */
    /*  bits 6-9 is RAM address  */
    #define CONFIG_DMA_READ_ADDR    0x00000000 /* DMA Read Address */
    #define CONFIG_DMA_WRITE_ADDR   0x00000040 /* DMA Write Address */
    #define CONFIG_SC_READ_ADDR     0x00000080 /* Scatter-Gather Read Address */
    #define CONFIG_SC_WRITE_ADDRR   0x00000000 /* Scatter-Gather Write Address */
    /* ============================
     *     Mode Register
     * ============================ */
    #define COCO_MODE       0x01
    #define COCO_SWAP       0x02
    #define COCO_SLICE      0x04
    #define COCO_DELAY      0x08
    #define COCO_FLAG       0x20
    #define COCO_WRENA      0x40
    /* ==========================================
     *      Configuration Register  
     * ========================================== */
    #define DMAREG_CCRES    0x00000001L /* Chameleon reset when zero           */
    #define DMAREG_HICRES   0x00000001L /* Chameleon reset when zero           */
    #define DMAREG_FRES     0x00000002L /* I/O Fifo reset when zero            */
    #define DMAREG_FSDATI   0x00000004L /* Input Fifo serial config. data      */
    #define DMAREG_FSDATO   0x00000008L /* Output Fifo serial config. data     */
    #define DMAREG_FSCLK    0x00000010L /* I/O serial config clock             */
    #define DMAREG_LED      0x00000020L /* external LUT bank selection (=0)    */
    /*  bit 6-9: Ram address   */
    #define DMAREG_RAMRADR  0x00000000L /* DMA read address                    */
    #define DMAREG_RAMWADR  0x00000040L /* DMA write address                   */
    #define DMAREG_RAMPRRD  0x00000080L /* scatter-gather read address         */
    #define DMAREG_RAMPRWR  0x000000C0L /* scatter-gather write address        */
    #define DMAREG_RAMRCNT  0x00000100L /* read count copy                     */
    #define DMAREG_RAMWCNT  0x00000140L /* write count copy                    */
    #define DMAREG_FILL     0x00000400L /* enable automatic ram fill for DMA   */
    #define DMAREG_PTEN     0x00008000L /* Fifo Interface Enable               */
    #define DMAREG_INTREN   0x00010000L /* DMA Read Interrupt Enable           */
    #define DMAREG_INTWEN   0x00020000L /* DMA Write Interrupt Enable          */
    #define DMAREG_INTPREN  0x00040000L /* data-chained DMA read int. enable   */
    #define DMAREG_INTPWEN  0x00080000L /* data-chained DMA write int. enable  */
    /*  bit 20-21: Device Selection    */ 
    #define DMAREG_RAM      0x00000000L /* RAM                                 */
    #define DMAREG_RCNT     0x00100000L /* DMA Read Count                      */
    #define DMAREG_WCNT     0x00200000L /* DMA Write Count                     */
    #define DMAREG_STAT     0x00300000L /* Status/Fifo control Register        */
    #define DMAREG_REN      0x10000000L /* DMA Read Enable                     */
    #define DMAREG_WEN      0x20000000L /* DMA Write Enable                    */
    #define DMAREG_PREN     0x40000000L /* data-chained DMA read enable        */
    #define DMAREG_PWEN     0x80000000L /* data-chained DMA write enable       */
    #define DMAREG_NVIFEN   0x00800000L /* Mailbox Interface Enable            */
    #define DMAREG_DMACVT   0x00400000L /* Unused                              */ 
    /*  Unknown !!     */
    #define DMAREG_HIDOIT   0x00000400L
    #define DMAREG_HISCLK   0x00000800L
    #define DMAREG_HISDI    0x00001000L
    #define DMAREG_HISDO    0x00000080L
    #define EOFPROG         0xF0000000L
    /* ==============================
     *     AMCC Register Offsets
     * ============================== */
    #define AMCC_OP_REG_OMB1        0x00
    #define AMCC_OP_REG_OMB2        0x04
    #define AMCC_OP_REG_OMB3        0x08
    #define AMCC_OP_REG_OMB4        0x0c
    #define AMCC_OP_REG_IMB1        0x11
    #define AMCC_OP_REG_IMB2        0x14
    #define AMCC_OP_REG_IMB3        0x18
    #define AMCC_OP_REG_IMB4        0x1c
    #define AMCC_OP_REG_FIFO        0x20
    #define AMCC_OP_REG_MWAR        0x24
    #define AMCC_OP_REG_MWTC        0x28
    #define AMCC_OP_REG_MRAR        0x2c
    #define AMCC_OP_REG_MRTC        0x30
    #define AMCC_OP_REG_MBEF        0x34
    #define AMCC_OP_REG_INTCSR      0x38
    #define AMCC_OP_REG_MCSR        0x3c
    #define AMCC_OP_REG_MCSR_NVDATA (AMCC_OP_REG_MCSR + 2) /* Data in byte 2 */
    #define AMCC_OP_REG_MCSR_NVCMD  (AMCC_OP_REG_MCSR + 3) /* Command in byte 3 */
    /*   Amcc INTCSR interrupt bits  */
    #define AMCC_INTCSR_WEN         0x00004000
    #define AMCC_INTCSR_REN         0x00008000
    #define AMCC_INTCSR_INTMB4      0x00001f00
    /*  enable Output Mbox4, byte 3 only  */
    #define AMCC_INTCSR_MASK        AMCC_INTCSR_INTMB4
    #if 0   /*  Write/Read Completion Interrupt enable only  */
    #define AMCC_INTCSR_MASK   AMCC_INTCSR_WEN|AMCC_INTCSR_REN
    /**   "Write/Read Completion Interrupt" with Output Maibox4 */ 
    #define AMCC_INTCSR_MASK AMCC_INTCSR_WEN|AMCC_INTCSR_REN|AMCC_INTCSR_INTMB4
    #endif
    #define AMCC_INTCSR_RCLR 0x00080000 /*  Read Transfer Complete Clear  */
    #define AMCC_INTCSR_WCLR 0x00040000 /*  Write Transfer Complete Clear */
    #define AMCC_INTCSR_RST  0x00330000 /*  Target/Master Abort and Out Mbox */
    /*   Amcc MCSR bits   */
    #define AMCC_REN        0x00004000  /* Read Enable  */
    #define AMCC_WEN        0x00000400  /* Write Enable */
    #define AMCC_RFMAN      0x00002000L
    #define AMCC_WFMAN      0x00000200L
    #define AMCC_RFPRI      0x00001000L  /* Read Priority over Write */
    #define AMCC_WFPRI      0x00000100L  /* Write Priority over Read */
    #define AMCC_RST_FIFOS  0x06000000L  /* Reset Fifos  */
    #define AMCC_RST_ADDON  0x01000000L  /* Reset Add-on */
    #define AMCC_MCSR_MASK  AMCC_RST_FIFOS | AMCC_RST_ADDON
    /* Outgoing Mailbox Register 4, byte 3   */
    #define AMCC_MB_EOFDMAR         0x01000000 /* 1 = end of read DMA        */
    #define AMCC_MB_EOFDMAW         0x02000000 /* 1 = end of write DMA       */
    #define AMCC_MB_EOFPRDMAR       0x04000000 /* 1 = end of prog. read DMA  */
    #define AMCC_MB_EOFPRDMAW       0x08000000 /* 1 = end of prog. write DMA */
    #define AMCC_MB_DIAGN           0x10000000 /* Diagnostic flag         */
    #define AMCC_MB_COCOMODE        0x20000000 /* Chameleon flag          */
    #define AMCC_MB_FIFORSTR        0x40000000 /* AMCC input Fifo Reset      */
    #define AMCC_MB_FIFORSTW        0x80000000 /* AMCC output Fifo Reset     */
    /* ==============================
     *      Device Information
     * ==============================
     *
     *      status:         Shows whether the driver is attached/opened, etc.
     *      dmacfg:         The boards Configuration default setting.
     *      dmabits:        DMA operation bit setting (in addition to dmacfg)
     *      dmatype:        Single or Chain DMA type
     *      dmastat:        curretnt status of DMA (Idle, Wait, etc.) 
     *      dmacmd:         Board's DMA command (Transp or Convert)
     *      dmawait:        Semaphore for wait/wakeup 
     *      wp_addr:        Residual phys. address for Write DMA.
     *      rp_addr:        Residual phys. address for Read  DMA.   
     *      rp_size:        Residual size in bytes for Read  DMA.
     *      wp_size:        Residual size in bytes for Write DMA.
     *      bp:             Current buf_t pointer for Read/Write DMA
     *      chain_list:     Address of current chain list buffer
     *      addrList:       Current DMA scatter/gather structure (single read/write)
     *      r_addrList:     Sim. Read/Write Read scatter/gather structure.
     *      w_addrList:     Sim. Read/Write Write scatter/gather structure.
     *      page_no:        Pages left to DMA (single read/write)
     *      r_page_no:      Pages left for Read to DMA (Sim. read/write).   
     *      w_page_no:      Pages left for Write to DMA (Sim. read/write).  
     *      cfg_adr:        Board's Configuration Area Address 
     *      amcc_adr:       Amcc PCI address
     *      conf_adr:       Board's Config Register address
     *      norm_adr:       Board's Normal DMA Registers address
     *      start_time:     Time DMA started
     *      intr_time:      Time board Interrupt completion of DMA
     *      call_time:      Time Call came into the driver
     *      ret_time:       Time driver returns to the user
     *      vhdl:           Vertex handle representing this board.
     *      dev_intr:       Our Interrupt Handler structure
     *      dev_desc:       Our Device Descriptor structure
     *      tid:            Timer ID ..timer waiting for board to interrupt. 
     */
    typedef struct {
            int           status;
            /*  dma stuff  */
            uint_t        dmacfg;
            uint_t        dmabits;  
            int           dmatype;
            int           dmastat;
            uint_t        dmacmd;
            sema_t        dmawait;
            int           iostat;
            int           dmasize;
            alenaddr_t    wp_addr;
            alenaddr_t    rp_addr;
            size_t        rp_size;
            size_t        wp_size;
            buf_t        *bp;
            caddr_t       chain_list;
            alenlist_t    addrList;
            alenlist_t    r_addrList;
            alenlist_t    w_addrList;
            int           page_no;
            int           r_page_no;
            int           w_page_no;
            /*  mapped memory   */
            vhandl_t      *vhandl;
            caddr_t       mappedkv;
            int           mappedkvlen;
            /*  addresses   */
            caddr_t       cfg_adr;
            caddr_t       amcc_adr;
            caddr_t       conf_adr;
            caddr_t       norm_adr;
            /*  for Dma time measurement  */
            struct timeval start_time;
            struct timeval intr_time;
            struct timeval call_time; 
            struct timeval ret_time; 
            /*  Irix interface structs  */
            vertex_hdl_t  vhdl;
            pciio_intr_t  dev_intr;
            device_desc_t dev_desc;
            toid_t        tid;
    } card_t;
    /*  bits for status  */
    #define  CARD_ATTACHED  0x01
    #define  CARD_OPEN      0x02
    /*  dmatype values    */
    #define  DMA_PROG     0  /*  chained - default value  */
    #define  DMA_SINGLE   1
    /*  dmastat values    */
    #define  DMA_IDLE       0
    #define  DMA_LUT_WAIT   1
    #define  DMA_READ_WAIT  2
    #define  DMA_WRITE_WAIT 3
    #define  DMA_RW_WAIT    4
    /*  iostat values  */
    #define IO_OK        0
    #define IO_ERROR     1
    #define IO_TIME      2 
    /*  chained DMA block    */
    typedef struct {
            paddr_t  nextaddr;
            paddr_t  addr;
            int      size;
    } coco_dmapage_t;
    
    
    
    

    Example 15-10. Example PCI Driver for IRIX 6.3—Driver Source Code


    /******************************************************************************
     *****          C h a m e l e o n   I r i x  P c i   D r i v e r          *****
     ******************************************************************************
    */
    #include <sys/types.h>
    #include "sys/cmn_err.h"
    #include "sys/sema.h"    
    #include <sys/param.h>
    #include <sys/errno.h>
    #include <sys/syslog.h>
    #include <sys/conf.h>
    #include <sys/pio.h>
    #include "sys/systm.h"
    #include <sys/time.h>
    #include <sys/kmem.h>
    #include <sys/ktime.h>
    #include <sys/mload.h>
    #include <sys/ddi.h>
    #include <sys/cred.h>
    #include <sys/user.h>
    #include <sys/mace.h>
    #include <sys/immu.h>
    #include <sys/region.h>
    #include <sys/alenlist.h>
    #include <sys/IP32.h>
    #include <sys/PCI/PCI_defs.h>
    #include <sys/PCI/pciio.h>
    #include "coco.h"
    #include "coco_user.h"
    char   *coco_mversion = M_VERSION;   /*  loadable driver requirement */
    int     coco_devflag = 0;            /*  ddi/dki requirement         */
    /* ======================================
     *    Device Driver/PCI entry routines 
     * ====================================== */
    int coco_unload(void);
    int coco_open(dev_t *, int, int, cred_t *);
    int coco_close(dev_t, int, int, cred_t *);
    int coco_read( dev_t, uio_t *, cred_t *);
    int coco_write( dev_t, uio_t *, cred_t *);
    int coco_ioctl(dev_t, int, void *, int, cred_t *, int *);
    int coco_map ( dev_t, vhandl_t *, off_t, int, int );
    int coco_unmap ( dev_t, vhandl_t * );
    int coco_init();
    int coco_attach(vertex_hdl_t);
    int coco_detach(vertex_hdl_t);
    int coco_error(vertex_hdl_t, int );
    void coco_intr( eframe_t *, intr_arg_t ); 
    /* =================================
     *   Supporting internal routines 
     * ================================= */
    static void cocoReset( card_t  * );
    static void cocoProgFlags(caddr_t,uint_t,ushort_t,ushort_t,ushort_t,ushort_t);
    static void cocoSetMode ( card_t *, int, int, int, int );
    static void cocoCommand ( card_t *, uint_t, uint_t );
    static void cocoBufOut ( card_t *, uint_t *, int );
    static void cocoBufIn  ( card_t *, uint_t *, int );
    static void cocoReadDmaRegs ( card_t *, uint_t * );
    static void cocoWriteDmaRegs ( card_t *, uint_t * );
    static void cocoSetAddr ( card_t *, uint_t );
    static void cocoReadIntRam ( card_t *, uint_t *, int, int );
    static void cocoWriteIntRam ( card_t *, uint_t *, int, int );
    static void cocoReadExtRam ( card_t *, uint_t *, int );
    static void cocoWriteExtRam ( card_t *, uint_t *, int );
    static void cocoConvert ( card_t *, uint_t );
    static void cocoConvertTest ( card_t *, coco_convert_t *);
    static void cocoPrepAmcc ( card_t * );
    static void cocoResetAmcc ( card_t *);
    static void cocoStartProgDma ( card_t *, iopaddr_t, int, int );
    static void cocoStartSingleDma ( card_t *, alenaddr_t, size_t, int );
    static void cocoReport ( card_t *, char * );
    static void cocoShowChain( char *, coco_dmapage_t * );
    static void cocoTimeOut( card_t *); 
    static void cocoTimeOut2( card_t *); 
    static void cocoDumpAmcc ( card_t *);
    static void cocoReportTime ( caddr_t, card_t *, int );
    static void cocoShowAlenlist ( caddr_t, alenlist_t );
    static void cocoUnlockUser ( caddr_t, int, int );
    static void cocoDiffTime( struct timeval *,struct timeval *,struct timeval *);
    static int  cocoStrategy ( buf_t * );
    static int  cocoReadAmccFifo ( card_t  * );
    static int  cocoFifoTest ( card_t *, int );
    static int  cocoPattern ( int, int );
    static int  cocoReadMode ( card_t * );
    static int  cocoReadAddr (card_t *);
    static int  cocoDmaRegsTest ( card_t * );
    static int  cocoIntRamTest ( card_t * );
    static int  cocoExtRamTest ( card_t * );
    static int  cocoDmaToLuts( card_t *, coco_buf_t *, int );
    static int  cocoReadWrite( card_t *, coco_rw_t * );
    static int  cocoStartRWDma ( card_t *, iopaddr_t, int, iopaddr_t, int );
    static int  cocoMakeChainRW ( card_t *, coco_dmapage_t **, coco_dmapage_t **);
    static int  cocoAlenlistSize ( alenlist_t );
    static int  cocoLockUser ( caddr_t, int, int );
    static coco_dmapage_t * cocoMakeChain ( card_t *, alenlist_t, int );
    static char *cocoIoctlStr(int);
    /*
    *  temporary declaration of buf_to_alenlist
    *  - missing from pciio.h & alenlist.h
    *  - also not compatible with IRIX 6.4!
    */
    extern alenlist_t buf_to_alenlist(buf_t *);
    #define COCO_TEST  0x999
    static void  cocoDebug ( coco_rw_t * );
    /*
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ~~~~~~~~~~~                                                      ~~~~~~~~~~~~~
    ~~~~~~~~~~~    D r i v e r ' s    E n t r y    R o u t i n e s   ~~~~~~~~~~~~~~
    ~~~~~~~~~~~                                                      ~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    /*************************************************************************
     ***                   c o c o _  i n i t                              ***
     *************************************************************************
     *
     *  Name:       coco_init
     *
     *  Purpose:    Called by kernel. Here we declare our PCI interface
     *              routines attach, detach and error and register our
     *              driver to take care of our card (card is identified by
     *              Vendor and Device IDs).
     *
     *  Returns:    None
     *
     *************************************************************************/
    int 
    coco_init()
    {
        register int ret;
        printf("coco_init()\n");    
    #ifdef _EARLY_PCI
        /*   Identify our attach, detach and error routines  */
        ret = pciio_add_attach(coco_attach, coco_detach, 
                               (pciio_error_handler_f *)coco_error,
                               DRIVER_PREFIX, MAJOR_NUMBER);
        if ( ret != 0 ) { 
                    printf ("coco_init: could not add_attach\n");
                    return(0);
        }
    #endif   
        /*    Register and identify the card   */
        ret = pciio_driver_register(VENDOR_ID, DEVICE_ID, DRIVER_PREFIX, 0);
        if ( ret != 0 ) {
                    printf ( "coco_init: could not register\n");
                    return(0);
        }
    }
    /*************************************************************************
     ***                   c o c o _ a t t a c h                           ***
     *************************************************************************
     *
     *  Name:       coco_attach
     *
     *  Purpose:    Called by the kernel. Our card is installed and hence
     *              we are called. Prepare everything necessary to handle 
     *              the card. Note that when the card is found, the following
     *              is set in the Command field of Config space:    
     *                      - Bus master enabled.
     *                      - Memory and IO space access enabled.
     *                      - Cache line is set to 0x20 (32) 
     *                      - Latency Timer is set to 0x30 (48)
     *
     *              For Chameleon, beside Configuration address space, we need 4 
     *              Memory address spaces to be mapped:
     *                 Base_Reg 0 = AMCC Registers and Fifos
     *                 Base_Reg 3 = Normal DMA registers
     *                 Base_Reg 4 = Configuration Register.
     *
     *  Returns:    0 for Success, or errno 
     *
     *************************************************************************/
    int
    coco_attach(vertex_hdl_t vhdl)
    {
        card_t      *cp;
        caddr_t     cfg_adr, mem_ptr, amcc_adr, conf_adr, norm_adr;
        int         ret;
        u_int32_t  vendor_id, device_id, base_reg, cmd_reg;
        device_desc_t dev_desc;
        #ifdef DEBUG
        printf ("coco_attach: ---- start --------\n");
        #endif
        /* =========================
         *  Configuration Space   
         * ========================= */
        /* Get a pointer to the card's Configuration address space  */
        cfg_adr = (caddr_t)pciio_piotrans_addr ( vhdl, NULL, PCIIO_SPACE_CFG,
                                                 (iopaddr_t)0, COCO_CONFIG_HDR, 0);
        if ( cfg_adr == (caddr_t)NULL ) {
            cmn_err ( CE_WARN, "coco_attach: Cannot get to Config space");
            return(EIO);
        }
        #ifdef DEBUG
        printf ("Config. address is 0x%x\n", cfg_adr );
        /*
         *  Get Vendor_Id, Device_Id and Base_Reg and print them
         *  Here we get only Base_Reg 0. Get others if your card uses more.
         *  Beside these general fields, get any vendor specific fields
         *  and check/print them for debugging purpose.
         */
        vendor_id    = pciio_config_get(cfg_adr, PCI_CFG_VENDOR_ID);
        device_id    = pciio_config_get(cfg_adr, PCI_CFG_DEVICE_ID); 
        cmd_reg      = pciio_config_get(cfg_adr, PCI_CFG_COMMAND );
        printf ("Coco Vendor_Id  = 0x%x, Device_Id = 0x%x, Cmd = 0x%x\n", 
                 vendor_id, device_id, cmd_reg );
        #endif
        /* ==========================
         *  Memory Address Space
         * ========================== */
        /*  Get Amcc Register addresses */
        amcc_adr = (caddr_t)pciio_piotrans_addr ( vhdl, NULL, PCIIO_SPACE_WIN(0),
                                                 (iopaddr_t)0, AMCC_RAM_SIZE, 0);
        #ifdef DEBUG
        printf ("Amcc address is 0x%x\n", amcc_adr ); 
        #endif
        if ( amcc_adr == (caddr_t)NULL ) {
            cmn_err(CE_WARN, "coco_attach: Cannot get to AMCC address space");
            return (EIO);
        }
        /*  Get DMA Config Register address  */
        conf_adr = (caddr_t)pciio_piotrans_addr ( vhdl, NULL, PCIIO_SPACE_WIN(4),
                                                 (iopaddr_t)0, CONFIG_RAM_SIZE, 0);
        #ifdef DEBUG
        printf ("Config Register Address is 0x%x\n", conf_adr );
        #endif
        if ( conf_adr == (caddr_t)NULL ) {
            cmn_err(CE_WARN, "coco_attach: Cannot get to Config Register");
            return (EIO);
        }
        /*  Get Normal DMA Register addresses */
        norm_adr = (caddr_t)pciio_piotrans_addr ( vhdl, NULL, PCIIO_SPACE_WIN(3),
                                                 (iopaddr_t)0, NORMAL_DMA_RAM_SIZE,
                                                   0);
        #ifdef DEBUG
        printf ("Normal DMA address is 0x%x\n", norm_adr);
        #endif
        if ( norm_adr == (caddr_t)NULL ) {
            cmn_err(CE_WARN, "coco_attach: Cannot get to Normal DMA address");
            return (EIO);
        }
        /*  allocate an internal structure for this card and save everything */
        cp = (card_t *)kmem_zalloc ( sizeof(card_t), KM_NOSLEEP ); 
        if ( cp == (card_t *)NULL ) {
            cmn_err(CE_WARN, "coco_attach: Cannot allocate memory");
            return(ENOMEM);
        }
        cp->vhdl     = vhdl;
        cp->cfg_adr  = cfg_adr;
        cp->amcc_adr = amcc_adr;
        cp->conf_adr = conf_adr;
        cp->norm_adr = norm_adr;
        /* =====================================
         *     Interrupt Handler Registration
         * ===================================== */
        dev_desc = kmem_alloc(sizeof (*dev_desc), KM_SLEEP);
        dev_desc->intr_swlevel = COCO_LOCK; 
        cp->dev_intr = pciio_intr_alloc ( vhdl, dev_desc, PCIIO_INTR_LINE_A, vhdl );
        if (cp->dev_intr == (pciio_intr_t)NULL){
            cmn_err(CE_WARN, "coco_attach: Can't pciio_intr_alloc");
            kmem_free (dev_desc, sizeof(*dev_desc) );
            kmem_free ( cp, sizeof(card_t) );
            return(EIO);
        }
        ret = pciio_intr_connect ( cp->dev_intr, (intr_func_t)coco_intr,  
                                   (intr_arg_t)cp, 0 );
        if ( ret == -1 ) {
              cmn_err(CE_WARN, "coco_attach: Cannot register interrupt handler");
              kmem_free (dev_desc, sizeof(*dev_desc) );
              kmem_free ( cp, sizeof(card_t) );
              return (EIO);
        }
        cp->dev_desc = dev_desc;
        cp->status   = CARD_ATTACHED; 
        /*   allocate memory for mapping   */
        cp->mappedkv = kmem_alloc (MAPPED_SIZE,
                                   KM_NOSLEEP | KM_PHYSCONTIG | KM_CACHEALIGN);
        if ( cp->mappedkv == (caddr_t)NULL ) {
                cmn_err (CE_NOTE, "coco_attach: Not enough memory for %d mapping",
                             MAPPED_SIZE);
                cmn_err (CE_WARN, "coco_attach: No mapping is allowed");
        }
        else {
            cp->mappedkvlen = MAPPED_SIZE; 
            cmn_err (CE_NOTE, "coco_attach: %d bytes available for mapping",
                     cp->mappedkvlen );
        }
        /*  save our structure  */
        device_info_set ( vhdl, (arbitrary_info_t)cp );
        cmn_err ( CE_NOTE, "coco_attach: driver is ready");
        return(0);
    }    
    /*************************************************************************
     ***                   c o c o _ d e t a c h                           ***
     *************************************************************************
     *
     *  Name:       coco_detach
     *
     *  Purpose:    Detaches a driver 
     *
     *  Returns:    0 Success or errno
     *
     *************************************************************************/
    int 
    coco_detach(vertex_hdl_t vhdl)
    {
        register card_t       *cp;
        cmn_err(CE_NOTE, 
               "coco_detach: Unregister Interrupt handler and free up mem");
        cp = (card_t *)device_info_get ( vhdl );
        /*  free up memory for mapping   */
        if ( cp->mappedkv )  
            kmem_free ( cp->mappedkv, cp->mappedkvlen );
        /*  Unregister the Interrupt Handler  */
        pciio_intr_disconnect(cp->dev_intr);
        pciio_intr_free(cp->dev_intr);
        kmem_free ( cp->dev_desc, sizeof(*cp->dev_desc) );
        kmem_free ( cp, sizeof(card_t) );
        return(0);
    }
    /*************************************************************************
     ***                   c o c o _ i n t r                               ***
     *************************************************************************
     *
     *  Name:       coco_intr  
     *
     *  Purpose:    Our interrupt handler. 
     *
     *  Returns:    None. 
     *
     *************************************************************************/
    void
    coco_intr( eframe_t *ef, intr_arg_t arg ) 
    {
            register card_t  *cp;
            register buf_t   *bp;
            register caddr_t adr_cfg, adr_amcc, adr_norm;
            register uint_t  intcsr, xil_stat, mbox;
            register uint_t  dmastat, dmatype, dmacfg, dmabits;
            register int     rw, err;
            size_t           p_size;
            alenaddr_t       p_addr;
            /*  is it stray interrupt ?  */
            cp = (card_t *)arg;
            microtime ( & cp->intr_time)
            adr_amcc = cp->amcc_adr;
            intcsr = Inp32(adr_amcc+AMCC_OP_REG_INTCSR);
            if ( (intcsr & 0x00800000) == 0 ) {
                    #ifdef DEBUG
                    printf ("cocoIntr: Stray interrupt\n");
                    #endif
                    return;
            }
            #ifdef DEBUGTIME
            cocoReportTime ( "cocoIntr", cp, 0 );
            #endif
            bp = cp->bp;
            adr_cfg  = cp->conf_adr;
            adr_norm = cp->norm_adr;
            dmacfg   = cp->dmacfg;
            dmabits  = cp->dmabits;
            dmastat  = cp->dmastat;
            dmatype  = cp->dmatype;
            #ifdef DEBUG
            printf ("cocoIntr: started, tid = %d\n", cp->tid );
            cocoDumpAmcc(cp);
            #endif
            /*  cancel any outstanding timer  */
            if ( cp->tid > 0 ) {
                    untimeout(cp->tid);
                    cp->tid = 0;
            }
            /* ======================================
             *      Reseting Interrupt and Status
             * ====================================== */
            /*  disable any Dma and read in Xilinx status */
            Out32(adr_cfg, dmacfg | DMAREG_STAT );
            xil_stat = Inp32(adr_norm);
            mbox     = Inp32(adr_amcc+AMCC_OP_REG_IMB4);
            #ifdef DEBUG
            printf ("coco_intr: amcc: 0x%x, xilinx: 0x%x, mbox4 = 0x%x\n",
                    intcsr, xil_stat, mbox );
            #endif
            /*  Reset Amcc Interrupts and enable interrupts again   */ 
            Out32(adr_amcc+AMCC_OP_REG_INTCSR, AMCC_INTCSR_RST | AMCC_INTCSR_RCLR |
                  AMCC_INTCSR_WCLR );
            Out32(adr_amcc+AMCC_OP_REG_INTCSR, AMCC_INTCSR_MASK);
            /* ===============================================
             *      End of Dma Read or Write 
             * =============================================== */
            if ( (mbox & AMCC_MB_EOFDMAR) || (mbox & AMCC_MB_EOFDMAW) ) {
                    dmabits &= ~(DMAREG_REN | DMAREG_INTREN);
                    dmabits &= ~(DMAREG_WEN | DMAREG_INTWEN );
                    #ifdef DEBUG
                    if ( mbox & AMCC_MB_EOFDMAR)
                            printf ("cocoIntr: End of DMA Read\n");
                    else    printf ("cocoIntr: End of DMA Write\n");
                    #endif
            }
            /* ===============================================
             *      End of Chain Dma Read or Write 
             * =============================================== */
            if ( (mbox & AMCC_MB_EOFPRDMAR) || (mbox & AMCC_MB_EOFPRDMAW) ) {
                    dmabits &= ~(DMAREG_PREN | DMAREG_INTPREN);
                    dmabits &= ~(DMAREG_PWEN | DMAREG_INTPWEN);
                    dmabits &= ~(DMAREG_WEN | DMAREG_REN);
                    #ifdef DEBUG
                    if ( mbox & AMCC_MB_EOFPRDMAR )
                            printf ("cocoIntr: End Prog DMA Read\n");
                    else    printf ("cocoIntr: End Prog DMA Write\n");
                    #endif
            }
            cp->dmabits = dmabits;
            cp->iostat  = IO_OK;
            switch ( cp->dmastat ) {
                /* ==============================
                 *     Dma to Lut
                 * ============================== */
               case DMA_LUT_WAIT:
                    #ifdef DEBUG
                    printf ("cocoIntr: Waking up Dma_Lut_Wait\n");
                    #endif
                    cp->dmastat = DMA_IDLE;
                    WakeEvent(&cp->dmawait);
                    goto get_out; 
                /* ==================================
                 *  Read/Write Dma
                 * ================================== */
                case DMA_READ_WAIT:
                case DMA_WRITE_WAIT:
                    /*  what we do depends on which type of Dma is done */
                    switch ( cp->dmatype ) {
                         /*  chained Dma is done. Simply wake the process up */
                         case DMA_PROG:
                            #ifdef DEBUG
                            printf ("cocoIntr: biodone() read/write\n");
                            #endif
                            kmem_free ( cp->chain_list,  
                                        cp->page_no * sizeof(coco_dmapage_t) );
                            alenlist_done(cp->addrList);
                            cp->addrList = 0;
                            cp->dmastat = DMA_IDLE;
                            bp->b_resid -= cp->dmasize;
                            biodone(cp->bp);
                            goto get_out;
                         /*  single page Dma done. Dma the next page if any  */ 
                         case DMA_SINGLE:
                               bp->b_resid -= cp->dmasize;
                               cp->page_no--;
                               if ( cp->page_no <= 0 ) {  /* no more pages */
                                    #ifdef DEBUG
                                    printf ("cocoIntr: No more pages to Dma\n");
                                    printf ("cocoIntr: biodone() read/write\n");
                                    #endif
                                    alenlist_done(cp->addrList);
                                    cp->addrList = 0;
                                    cp->dmastat = DMA_IDLE;
                                    biodone(cp->bp);
                                    goto get_out;
                               } 
                              /*  get next page to DMA  */
                              #ifdef DEBUG
                              printf ("cocoIntr: Dma next page ..left = %d\n",
                                      cp->page_no-1 );
                              #endif
                              if ( alenlist_get(cp->addrList, NULL, NBPP,
                                   &p_addr, &p_size) != ALENLIST_SUCCESS ) {
                                    cmn_err (CE_WARN, 
                                            "cocoIntr: Bad scatter-gather"); 
                                    cp->iostat = IO_ERROR;
                                    #ifdef DEBUG
                                    printf ("cocoIntr: biodone() read/write\n");
                                    #endif
                                    alenlist_done(cp->addrList);
                                    cp->addrList = 0;
                                    cp->dmastat = DMA_IDLE;
                                    bioerror (cp->bp, EIO);
                                    biodone(cp->bp);
                                    goto get_out;
                              }
                              if ( cp->dmastat == DMA_READ_WAIT )
                                    rw = B_READ;
                              else  rw = B_WRITE; 
                              cocoStartSingleDma ( cp, p_addr, p_size, rw );
                              goto get_out;
                    }  /*** switch ( cp->dmatype ) ***/
                /* ==========================
                 *   Simultaneus read/write 
                 * ========================== */
                case DMA_RW_WAIT:
                    /*  what we do depends on which type of Dma is done */
                    switch ( cp->dmatype ) {
                         /*  chained Dma is done. Both read/write is done */ 
                         case DMA_PROG:
                            #ifdef DEBUG
                            printf ("cocoIntr: Waking up Dma_RW_Wait\n");
                            #endif
                            cp->dmastat = DMA_IDLE;
                            WakeEvent(&cp->dmawait);
                            goto get_out;
                         /*  single page Dma done. Dma the next page if any  */
                         case DMA_SINGLE:
                               /*  Keep transfering as long as there is data */ 
                               if ( cp->w_page_no > 0 ||
                                    cp->r_page_no > 0 ||
                                    cp->wp_size > 0   ||
                                    cp->rp_size > 0 ) {
                                    #ifdef DEBUG
                    printf ("cocoIntr: Dma next page, w_left = %d, r_left = %d\n",
                                          cp->w_page_no, cp->r_page_no);
                                    #endif
                                    err = cocoStartRWDma(cp, 0, 0, 0, 0);
                                    if ( err == 0 ) 
                                        goto get_out;
                                    #ifdef DEBUG
                                    printf ("cocoIntr: error Dmaing next page\n");
                                    #endif
                                    cp->dmastat = DMA_IDLE;
                                    cp->iostat  = IO_ERROR;
                                    WakeEvent(&cp->dmawait);
                                    goto get_out;
                              }
                              #ifdef DEBUG
                              printf ("cocoIntr: Waking up Dma_RW_Wait\n"); 
                              #endif
                              cp->dmastat = DMA_IDLE;
                              WakeEvent(&cp->dmawait);
                              goto get_out;
                    }  /***  switch ( cp->dmatype ) ***/
             } /*** switch ( cp->dmastat ) ***/
    get_out:
            #ifdef DEBUG
            cocoReport ( cp, "cocoIntr exit" );
            #endif
            return;
    }
    /*************************************************************************
     ***                   c o c o _ u n l o a d                           ***
     *************************************************************************
     *
     *  Name:       coco_unload
     *
     *  Purpose:    Unloads the driver. 
     *
     *  Returns:    0 Success or errno
     *
     *************************************************************************/
    int
    coco_unload(void)
    {
            printf ("coco_unload: Unloading the driver\n");
            pciio_driver_unregister(DRIVER_PREFIX);
            return(0);
    }
    /*************************************************************************
     ***                   c o c o _ o p e n                               ***
     *************************************************************************
     *
     *  Name:       coco_open
     *
     *  Purpose:    Opens the card. This sample driver simply verifies that 
     *              the card's info structure can be retrieved and checks
     *              the card's Base_Register.
     *
     *  Returns:    0 = Success, or errno.
     *
     *************************************************************************/
    int
    coco_open(dev_t *devp, int flag, int otyp, cred_t *cred)
    {
            register card_t       *cp;
            register vertex_hdl_t vhdl;
            #ifdef DEBUG
            printf ("coco_open: Opening the card\n");
            #endif
            /*   Get the vertex handle and pointer to card's info  */
            vhdl = dev_to_vhdl ( *devp );
            if (vhdl == NULL){
                    cmn_err(CE_WARN, "coco_open: dev_to_vhdl returns NULL");
                    return(EIO);
            }
            cp = (card_t *)device_info_get ( vhdl );
            /*  some error checking first   */
            if ( !(cp->status & CARD_ATTACHED) ) {
                    cmn_err (CE_WARN, "coco_open: Driver is not attached");
                    return (ENODEV);
            }
            if ( cp->status & CARD_OPEN) {
                    cmn_err (CE_WARN, "coco_open: Device is busy");
                    return (EBUSY);
            }
            cocoReset(cp);  /*  reset the board  */ 
            cp->status |= CARD_OPEN;
            /*  default values */ 
            cp->dmatype = DMA_SINGLE;
            cp->dmacmd  = COCO_TRANSP;
            return(0);
    }
    /*************************************************************************
     ***                   c o c o _ c l o s e                             ***
     *************************************************************************
     *
     *  Name:       coco_close
     *
     *  Purpose:    Closes the card. This sample driver's close statement
     *              prints out the addresses and Base_Register's value for
     *              verification.
     *
     *  Returns:    0 = Success, or errno
     *
     *************************************************************************/
    int
    coco_close(dev_t dev, int flag, int otyp, cred_t *cred)
    {
            register card_t       *cp;
            register vertex_hdl_t vhdl;
            #ifdef DEBUG
            printf ("coco_close: Closing the card\n");
            #endif
             /*  get the vertex handle and pointer to card's info  */
            vhdl = dev_to_vhdl ( dev );
            cp = (card_t  *)device_info_get ( vhdl );
            cp->status &= ~CARD_OPEN;
            return(0);
    }
    /*************************************************************************
     ***                   c o c o _ m a p                                 ***
     *************************************************************************
     *
     *  Name:       coco_map  
     *
     *  Purpose:    Allocate a piece of continious memory and map it to user's
     *              address space.
     *
     *  Returns:    0 = Success, or errno
     *
     *************************************************************************/
    int
    coco_map ( dev_t dev, vhandl_t *vh, off_t offset, int len, int prot )
    {
            register int          ret;
            register caddr_t      kv;
            register vertex_hdl_t vhdl;
            register card_t       *cp;
            /*  get the vertex handle and pointer to card's info  */
            vhdl = dev_to_vhdl ( dev );
            cp = (card_t  *)device_info_get ( vhdl );
            if ( cp->mappedkv == (caddr_t)NULL ) {
                    cmn_err (CE_NOTE, "coco_map: Not memory for mapping");
                    return (ENOMEM);
            }
            if ( len > cp->mappedkvlen ) {
                    cmn_err (CE_NOTE,
               "coco_map: Only %d bytes available for map, requested %d bytes",
                                   cp->mappedkvlen, len );
                    return (ENOMEM);
            }
            ret = v_mapphys ( vh, cp->mappedkv, len ); 
            if ( ret > 0  ) {
                    cmn_err (CE_WARN, "coco_map: Could not map, ret = %d", ret );
                    return (ret);    
            }
            /*  save for later */
            cp->vhandl = vh;
            dki_dcache_inval ( cp->mappedkv, cp->mappedkvlen );
            printf ("coco_map: v_mapphys returned %d, mapped 0x%x for %d bytes\n", 
                    ret, cp->mappedkv, len );
            return (0);
    }
    /*************************************************************************
     ***                   c o c o _ u n m a p                             ***
     *************************************************************************
     *
     *  Name:       coco_unmap
     *
     *  Purpose:    Unmap the kernel buffer we allocated before.
     *
     *  Returns:    0 = Success, or errno
     *
     *************************************************************************/
    int
    coco_unmap ( dev_t dev, vhandl_t *vh )
    {
            return (0);
    }
    /*************************************************************************
     ***                   c o c o _ i o c t l                             ***
     *************************************************************************
     *
     *  Name:       coco_ioctl
     *
     *  Purpose:    Handles user Ioctl command. These commands can be 
     *              found in coco_user.h.
     *
     *  Returns:    0 = Success, or errno
     *
     *************************************************************************/
    int
    coco_ioctl(dev_t dev, int cmd, void *arg, int mode, cred_t *cred, 
                   int *rvalp )
    {
            register card_t       *cp;
            register vertex_hdl_t vhdl;
            register uint_t      *tmp_ibuf;
            register int          tot_bytes, err, i;
            uint_t         tmp_int;
            coco_buf_t     coco_buf; 
            coco_rw_t      coco_rw;  
            coco_mode_t    coco_mode;
            coco_cmd_t     coco_cmd;
            coco_convert_t coco_convert;
            uint_t         dmaRegs[DMA_REGS];
            #ifdef DEBUG
            printf ("\ncoco_ioctl: ---> command = %s [0x%x]\n", 
                     cocoIoctlStr(cmd), cmd );
            #endif
            /*  get the vertex handle and pointer to card's info  */
            vhdl = dev_to_vhdl ( dev );
            cp = (card_t  *)device_info_get ( vhdl );
            /*  is device opened ? */
            if (!( cp->status & CARD_OPEN) ) {
                    cmn_err (CE_NOTE, "coco_ioctl: Device is not opened"); 
                    return (EIO);
            }
            bzero ( &cp->start_time, sizeof(struct timeval) );
            bzero ( &cp->intr_time, sizeof(struct timeval) );
            bzero ( &cp->call_time, sizeof(struct timeval) );
            bzero ( &cp->ret_time, sizeof(struct timeval) );
            /* ===================
             *    Ioctl commands
             * =================== */
            switch ( cmd ) {
                    case COCO_TEST:
                         /*  read in coco_rw_t  struct..all we need is there */
                         if ( copyin(arg, (char *)&coco_rw, sizeof(coco_rw_t) ) )
                             return (EFAULT);
                         cocoDebug ( &coco_rw );
                         return(0);
                    /* ==============================
                     *      Board Identification
                     * ============================== */
                    case COCO_ISPCI:
                            *rvalp = 1;
                            break;
                    /* ==============================
                     *      Reset the board 
                     * ============================== */
                    case COCO_RESET:
                            cocoReset(cp); 
                            break;
                    /* ==============================
                     *      Set DMA Command
                     * ============================== */
                    case COCO_SETCMD_TRANSP:
                            cp->dmacmd = COCO_TRANSP;
                            break;
                    case COCO_SETCMD_CONVERT:
                            cp->dmacmd = COCO_CONVERT;
                            break;
                    /* ===============================
                     *     Issue a Command 
                     * =============================== */
                    case COCO_COMMAND:
                        if ( copyin ( (char *)arg, &coco_cmd, 
                             sizeof(coco_cmd_t) ) )
                             return (EFAULT);
                        cocoCommand ( cp, coco_cmd.cmd, coco_cmd.datav );
                        break;
                    /* ===================================
                     *      Set DMA type (Prog or Single)
                     * =================================== */
                    case COCO_SET_SINGLE_DMA:
                            #ifdef DEBUG
                            printf ("Chameleon DMA set to Single Mode\n");
                            #endif
                            cp->dmatype = DMA_SINGLE;
                            break;
                    case COCO_SET_PROG_DMA:
                            #ifdef DEBUG
                            printf ("Chameleon DMA set to Prog Mode\n");
                            #endif
                            cp->dmatype = DMA_PROG;
                            break;
                    /* ===============================
                     *      Set up Chameleon Mode
                     * =============================== */
                    case COCO_SET_MODE:   
                        if ( copyin ( (char *)arg, &coco_mode, 
                             sizeof(coco_mode_t) ) )
                             return (EFAULT);
                        cocoSetMode ( cp, coco_mode.mode, coco_mode.swap, 
                                        coco_mode.slice, coco_mode.flag ); 
                        break;
                    /* =================================
                     *      Read Chameleon Mode
                     * ================================= */
                    case COCO_READ_MODE:
                         tmp_int = cocoReadMode (cp);
                         if ( copyout((char *)&tmp_int, arg, sizeof(uint_t) ) )
                            return (EFAULT);
                         break;
                                    
                    /* ===============================
                     *   Raw write Buffer to Fifo    *
                     * =============================== */
                    case COCO_RAW_WRITEB_FIFO:
                         if ( copyin( (char *)arg, &coco_buf, sizeof(coco_buf_t) ) )
                              return (EFAULT);
                         if ( coco_buf.buf_size == 0 ) 
                            return (EINVAL);
                          /*
                          *   the data is in coco_buf.buf in user address
                          *   space. Move it to kernel address space and dump it
                          *   to the board.
                          */
                         tot_bytes = coco_buf.buf_size * sizeof(uint_t);
                         tmp_ibuf = (uint_t *)kmem_alloc (tot_bytes, KM_NOSLEEP);
                         if ( tmp_ibuf == (uint_t *)NULL )
                            return (ENOMEM);
                         if ( copyin((caddr_t)coco_buf.buf, (caddr_t)tmp_ibuf, 
                              tot_bytes) ) {
                              kmem_free (tmp_ibuf, tot_bytes);
                              return(EFAULT);
                         }
                         cocoBufOut (cp, tmp_ibuf, coco_buf.buf_size);
                         kmem_free (tmp_ibuf, tot_bytes ); 
                         break;    
                    /* ===============================
                     *  Raw Read Buffer from Fifo    *
                     * =============================== */
                    case COCO_RAW_READB_FIFO:
                         if ( copyin( (char *)arg, &coco_buf, sizeof(coco_buf_t) ) )
                              return (EFAULT);
                         if ( coco_buf.buf_size == 0 ) 
                            return (EINVAL);
                         /*
                          *  the buffer to be filled is coco_buf.buf and 
                          *  it can contain coco_buf.buf_size 32-bit values.
                          *  Read that many into our own temp buffer and move them
                          *  to user-address space.
                          */
                         tot_bytes = coco_buf.buf_size * sizeof(uint_t);
                         tmp_ibuf = (uint_t *)kmem_alloc (tot_bytes, KM_NOSLEEP);
                         if ( tmp_ibuf == (uint_t *)NULL )
                            return (ENOMEM);
                         cocoBufIn(cp, tmp_ibuf, coco_buf.buf_size);
                         if ( copyout ( (caddr_t)tmp_ibuf, (caddr_t)coco_buf.buf, 
                                         tot_bytes)) {
                              kmem_free ( tmp_ibuf, tot_bytes ); 
                              return (EFAULT);
                         }
                         kmem_free ( tmp_ibuf, tot_bytes );
                         break;
                    /* =============================
                     *   Read 32-bit from Fifo     
                     * ============================= */
                    case COCO_RAW_READ_FIFO:
                         tmp_int = cocoReadAmccFifo(cp);
                         if ( copyout((char *)&tmp_int, arg, sizeof(uint_t) ) )
                            return(EFAULT);
                         break;     
                    /* =============================
                     *   Write 32-bit value to Fifo
                     * ============================= */
                    case COCO_RAW_WRITE_FIFO:
                         if ( copyin(arg, (char *)&tmp_int, sizeof(uint_t) ) )
                            return(EFAULT);
                         cocoCommand(cp, COCO_TRANSP, tmp_int );
                         break;
                    /* =============================
                     *   Test Fifo data path 
                     * ============================= */
                    case COCO_FIFO_TEST:
                         return (cocoFifoTest(cp, 1) );
                    /* ===============================
                     *     Read DMA Registers
                     * =============================== */
                    case COCO_RAW_READ_DMA:
                            cocoReadDmaRegs(cp, &dmaRegs[0]);
                            if ( copyout((char *)&dmaRegs[0], arg, 
                                 sizeof(dmaRegs) ) )
                                    return (EFAULT);
                            break;
                    /* ===============================
                     *     Write DMA Registers
                     * =============================== */
                    case COCO_RAW_WRITE_DMA:
                            if ( copyin(arg, (char *)&dmaRegs[0], 
                                 sizeof(dmaRegs) ) )
                                    return (EFAULT);
                            cocoWriteDmaRegs(cp, &dmaRegs[0] );
                            break;
                    /* =============================
                     *    DMA Regsters Access Test 
                     * ============================= */
                    case COCO_DMAREGS_TEST:
                         return ( cocoDmaRegsTest(cp) );
                    /* ===============================
                     *      Read Address Register
                     * =============================== */
                    case COCO_READ_ADDR:
                            tmp_int = cocoReadAddr(cp);
                            if ( copyout((char *)&tmp_int, arg, sizeof(uint_t) ) )
                                    return (EFAULT);
                            break;
                    /* ===============================
                     *      Write Address Register 
                     * =============================== */
                    case COCO_SET_ADDR:
                            if ( copyin(arg, (char *)&tmp_int, sizeof(uint_t) ) )
                                    return(EFAULT);
                            cocoSetAddr(cp, tmp_int);
                            break;
                    /* ===============================
                     *      Internal RAM Test       
                     * =============================== */
                    case COCO_INTRAM_TEST:
                            return ( cocoIntRamTest(cp) );
                    /* ===============================
                     *      External RAM Test
                     * =============================== */
                    case COCO_EXTRAM_TEST:
                            return ( cocoExtRamTest(cp) );
                    
                    /* ==============================
                     *      Read Internal LUTs
                     * ============================== */
                    case COCO_READ_RAMIL:
                    case COCO_READ_RAMIH:
                    case COCO_READ_RAMO:
                         if ( copyin( (char *)arg, (char *)&coco_buf, 
                            sizeof(coco_buf_t) ) )
                               return (EFAULT);
                         if ( coco_buf.buf_size == 0)
                            return (EINVAL);
                         if ( coco_buf.buf_size > INT_RAM_SIZE )
                            return (EINVAL);
                         /*  allocate a buffer to hold Ram's contents  */
                         tot_bytes = coco_buf.buf_size * sizeof(uint_t);
                         tmp_ibuf = (uint_t *)kmem_alloc (tot_bytes, KM_NOSLEEP);
                         if ( tmp_ibuf == (uint_t *)NULL )
                            return(EFAULT);
                         /*  read in Ram's contents into the buffer  */
                         cocoReadIntRam(cp, tmp_ibuf, cmd, coco_buf.buf_size );
                         /*  copy the kernel buffer to user's buffer  */
                         if ( copyout((caddr_t)tmp_ibuf, (caddr_t)coco_buf.buf, 
                                       tot_bytes) ) {
                                    kmem_free ( (caddr_t)tmp_ibuf, tot_bytes );
                                    return (EFAULT);
                         }
                         kmem_free ( (caddr_t)tmp_ibuf, tot_bytes );
                         break;
                    /* ================================
                     *     Write Internal LUTs
                     * ================================ */
                    case COCO_FILL_RAMIL:
                    case COCO_FILL_RAMIH:
                    case COCO_FILL_RAMO:
                         if ( copyin( (char *)arg, &coco_buf, sizeof(coco_buf_t) ) )                          return (EFAULT);
                         if ( coco_buf.buf_size == 0)
                            return (EINVAL);
                         if ( coco_buf.buf_size > 2 * INT_RAM_SIZE )
                            return (EINVAL);
                         /*  allocate a buffer to hold user's data  */
                         tot_bytes = coco_buf.buf_size * sizeof(uint_t);
                         tmp_ibuf = (uint_t *)kmem_alloc (tot_bytes, KM_NOSLEEP);
                         if ( tmp_ibuf == (uint_t *)NULL )
                            return(EFAULT);
                         /*  copy user's buffer to our own  */
                         if ( copyin((caddr_t)coco_buf.buf,  (caddr_t)tmp_ibuf, 
                              tot_bytes ) ) {
                                    kmem_free ( tmp_ibuf, tot_bytes );
                                    return(EFAULT);
                         }
                         /*  fill the Ram with data just moved over  */
                         cocoWriteIntRam ( cp, tmp_ibuf, cmd,
                                           coco_buf.buf_size );
                         kmem_free ( tmp_ibuf, tot_bytes );
                         break;
                    /* ==============================
                     *      Read External LUT
                     * ============================== */
                    case COCO_READ_RAML:
                         if ( copyin( (char *)arg, &coco_buf, sizeof(coco_buf_t) ) )                          return (EFAULT);
                         if ( coco_buf.buf_size == 0)
                            return (EINVAL);
                         if ( coco_buf.buf_size > EXT_RAM_SIZE )
                            return (EINVAL);
                         /*  allocate a buffer to hold External Ram's contents */
                         tot_bytes = coco_buf.buf_size * sizeof(uint_t);
                         tmp_ibuf = (uint_t *)kmem_alloc (tot_bytes, KM_NOSLEEP);
                         if ( tmp_ibuf == (uint_t *)NULL )
                            return(EFAULT);
                         /*  fill the buffer with Ext. Ram's contents  */
                         cocoReadExtRam(cp, tmp_ibuf, coco_buf.buf_size);
                         /*  copy the data to user's buffer  */
                         if ( copyout((caddr_t)tmp_ibuf, (caddr_t)coco_buf.buf, 
                                       tot_bytes) ) {
                                    kmem_free ( tmp_ibuf, tot_bytes );
                                    return (EFAULT);
                         }
                         kmem_free ( tmp_ibuf, tot_bytes );
                         break;
                    /* ==============================
                     *      Write External LUT
                     * ============================== */
                    case COCO_FILL_RAML:
                         if ( copyin( (char *)arg, &coco_buf, sizeof(coco_buf_t) ) )                          return (EFAULT);
                         if ( coco_buf.buf_size == 0)
                            return (EINVAL);
                         if ( coco_buf.buf_size > 2 * EXT_RAM_SIZE )
                            return (EINVAL);
                         /*  allocate a buffer to hold user's data  */
                         tot_bytes = coco_buf.buf_size * sizeof(uint_t);
                         tmp_ibuf = (uint_t *)kmem_alloc (tot_bytes, KM_NOSLEEP);
                         if ( tmp_ibuf == (uint_t *)NULL )
                            return(EFAULT);
                         /*  copy user's buffer to our own  */
                         if ( copyin((caddr_t)coco_buf.buf, (caddr_t)tmp_ibuf, 
                                      tot_bytes ) ) {
                                    kmem_free ( tmp_ibuf, tot_bytes );
                                    return(EFAULT);
                         }
                         /*  fill the Ram with data just moved over  */
                         cocoWriteExtRam ( cp, tmp_ibuf, coco_buf.buf_size);
                         kmem_free ( tmp_ibuf, tot_bytes );
                         break;
                    /* ==========================================
                     *      Chameleon Single pixel Conversion 
                     * ========================================== */
                    case COCO_CONVERT_PIXLE:
                          /*  read in the coco_convert struct from user's space */ 
                          if ( copyin(arg, (char *)&tmp_int, sizeof(uint_t) ) )
                               return (EFAULT);
                          cocoConvert ( cp, tmp_int );
                          break;
                    /* ============================================
                     *      Chameleon Single pixel Conversion Test
                     * ============================================ */
                    case COCO_CONVERT_TEST:
                          /*  read in the coco_convert struct from user's space */
                          if ( copyin(arg, (char *)&coco_convert,
                               sizeof(coco_convert_t) ) )
                               return (EFAULT);
                          cocoConvertTest ( cp, &coco_convert );
                          /*  copy the structure back   */
                          if ( copyout((char *)&coco_convert, arg,
                               sizeof(coco_convert_t) ) )
                               return(EFAULT);
                          break;
                    /* ================================================
                     *      DMA fill of LUTs (Internals and External)
                     * ================================================ */
                    case COCO_BLOCK_FILL_RAMIL:
                    case COCO_BLOCK_FILL_RAMIH:
                    case COCO_BLOCK_FILL_RAMO:
                    case COCO_BLOCK_FILL_RAML:
                         /*  read in coco_buf_t struct..all we need is there */
                         if ( copyin(arg, (char *)&coco_buf, sizeof(coco_buf_t) ) )                          return (EFAULT);
                         /*  empty buffer ?  */
                         if ( coco_buf.buf_size == 0 )
                            return (EINVAL);
                         /*  DMA the data and report back the result */
                         microtime ( &cp->call_time );
                         err = cocoDmaToLuts(cp, &coco_buf, cmd );
                         microtime ( &cp->ret_time );
                         #ifdef DEBUGTIME
                         cocoReportTime ( "cocoDmaToLut", cp, 1 );
                         #endif
                         return (err);
                    /* ================================================
                     *      Simultaneous Read/Write DMA 
                     * ================================================ */
                    case COCO_RW_BUF:
                         /*  read in coco_rw_t  struct..all we need is there */
                         if ( copyin(arg, (char *)&coco_rw, sizeof(coco_rw_t) ) )
                             return (EFAULT);
                         if ( coco_rw.buf_size <= 0 ) {
                            cmn_err (CE_NOTE, 
                            "cocoIoctl: Bad Sim.read/write buff size of %d\n",
                            coco_rw.buf_size );
                            return(EINVAL);
                         }
                         microtime ( &cp->call_time );
                         err = cocoReadWrite(cp, &coco_rw );
                         microtime ( &cp->ret_time );
                         #ifdef DEBUGTIME
                         cocoReportTime ( "cocoReadWrite", cp, 1 );
                         #endif
                         if ( err != 0 ) 
                            cmn_err (CE_NOTE, 
                            "cocoIoctl: cocoReadWrite reported error %d\n", err );
                         return (err);
            }  /***  end switch   **/
            return(0);
    }
    /*************************************************************************
     ***                   c o c o _ r e a d                               ***
     *************************************************************************
     *
     *  Name:       coco_read 
     *
     *  Purpose:    Read entry routine. DMAs data from the board to the 
     *              user's address space. 
     *
     *  Returns:    0 = Success, or errno
     *
     *************************************************************************/
    int 
    coco_read( dev_t dev, uio_t *uiop, cred_t *crp)
    {
            register card_t  *cp;
            register vertex_hdl_t vhdl;
            register int          ret;
            /*   get to the board's info structure  */
            vhdl = dev_to_vhdl ( dev );
            cp = (card_t  *)device_info_get ( vhdl );
            cp->bp = (buf_t *)NULL;
            cp->addrList = 0;
            cp->dmastat = 0;
            cp->dmabits = 0;
            bzero ( &cp->start_time, sizeof(struct timeval) );
            bzero ( &cp->intr_time, sizeof(struct timeval) );
            bzero ( &cp->call_time, sizeof(struct timeval) );
            bzero ( &cp->ret_time, sizeof(struct timeval) );
            #ifdef DEBUG
            printf ("coco_read: Reading %d bytes\n", uiop->uio_resid );
            #endif
            /*  do the transfer    */
            microtime ( &cp->call_time );
            ret = uiophysio ( cocoStrategy, NULL, dev, B_READ, uiop );
            microtime ( &cp->ret_time );
            if ( cp->addrList ) {
                    alenlist_done(cp->addrList);
                    cp->addrList = 0;
            }
            #ifdef DEBUG
            printf ("coco_read: uiophysio retunred %d\n", ret );
            #endif
            #ifdef DEBUGTIME
            cocoReportTime ( "cocoRead", cp, 1);
            #endif
            return ( ret );
    }
    /*************************************************************************
     ***                   c o c o _ w r i t e                             ***
     *************************************************************************
     *
     *  Name:       coco_write 
     *
     *  Purpose:    Write entry routine. DMAs data from user's address space
     *              to the board. 
     *
     *  Returns:    0 = Success, or errno
     *
     *************************************************************************/
    int 
    coco_write( dev_t dev, uio_t *uiop, cred_t *crp)
    {
            register card_t       *cp;
            register vertex_hdl_t vhdl;
            register int          ret;
            /*   get to the board's info structure  */
            vhdl = dev_to_vhdl ( dev );
            cp = (card_t  *)device_info_get ( vhdl );
            cp->bp       = (buf_t *)NULL;
            cp->dmastat  = 0;
            cp->dmabits  = 0;
            cp->addrList = 0;
            bzero ( &cp->start_time, sizeof(struct timeval) );
            bzero ( &cp->intr_time, sizeof(struct timeval) );
            bzero ( &cp->call_time, sizeof(struct timeval) );
            bzero ( &cp->ret_time, sizeof(struct timeval) );
            #ifdef DEBUG
            printf ("coco_write: Writing %d bytes\n", uiop->uio_resid );
            #endif
            /*  do the transfer    */
            microtime ( &cp->call_time );
            ret = uiophysio ( cocoStrategy, NULL, dev, B_WRITE, uiop );
            microtime ( &cp->ret_time );
            if ( cp->addrList ) {
                    alenlist_done(cp->addrList);
                    cp->addrList = 0;
            }
            #ifdef DEBUG
            printf ("coco_write: uiophysio retunred %d\n", ret );
            #endif
            #ifdef DEBUGTIME
            cocoReportTime ( "cocoWrite",  cp, 1);
            #endif
            return ( ret );
    }
    /*************************************************************************
     ***                   c o c o _ e r r o r                             ***
     *************************************************************************
     *
     *  Name:       coco_error
     *
     *  Purpose:    Traps PCI bus error. 
     *
     *  Returns:    0 = Success, or errno
     *
     *************************************************************************/
    int 
    coco_error(vertex_hdl_t vhdl, int error)
    {
            printf("coco_error %d\n", error );
            return(0);
    }
    /*
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~                                                 ~~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~    S u p p o r t i n g    R o u t i n e s       ~~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~                                                 ~~~~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    /*************************************************************************
     ***                   c o c o S t r a t e g y                         ***
     *************************************************************************
     *
     *  Name:       cocoStrategy
     *
     *  Purpose:    Strategy routine. It actually handles read/write
     *              and starts the DMA. It is called by uiophysio() kernel
     *              routine. 
     *
     *  Returns:    0 = Success, or errno
     *
     *************************************************************************/
    static int 
    cocoStrategy ( buf_t  *bp )
    {
            register card_t    *cp;
            register vertex_hdl_t vhdl;
            register coco_dmapage_t  *dmaPg;
            register int       s, i, ret, rw, tot_bytes;
            register uint_t   fill_bits;
            alenlist_t        addrList2;
            size_t            p_size;
            alenaddr_t        p_addr;
            iopaddr_t         p_dmaPg; 
            if ( !BP_ISMAPPED(bp) ) {
                    cmn_err (CE_NOTE, "cocoStrategy: Unmapped buf_t used\n");
                    bioerror ( bp, EIO);
                    biodone (bp);
                    return(EIO);
            }
            /*   get to the board's info structure  */
            vhdl = dev_to_vhdl ( bp->b_edev );
            cp = (card_t  *)device_info_get ( vhdl );
            /*  clear error and save bp  */
            bioerror(bp, 0);
            bp->b_resid = bp->b_bcount;
            cp->bp = bp;
            if ( bp->b_flags & B_READ ) 
                    rw = B_READ;   /*  board -> mem   */
            else    rw = B_WRITE;  /*  mem   -> board */
            /*  create scatter-gather list   */
            addrList2  = buf_to_alenlist ( bp );
            cp->addrList = pciio_dmatrans_list ( cp->vhdl, cp->dev_desc,
                                                 addrList2, 
                                                 PCIIO_DMAMAP_BIGEND);
                                                 /* PCIIO_DMAMAP_LITTLEEND); */
            alenlist_done (addrList2);
            if ( cp->addrList == (alenaddr_t)NULL ) {
                    cmn_err (CE_NOTE, "cocoStrategy: Cannot create alenlist");
                                    bioerror ( bp, EIO);
                    biodone (bp);
                    return(EIO);
            }
    #if 0
            cp->page_no  = alenlist_size ( cp->addrList );
    #endif
            cp->page_no  = cocoAlenlistSize ( cp->addrList );
            #ifdef DEBUG
            printf ("cocoStrategy: %s %d bytes [%d pages] in %s Dma\n", 
                    rw == B_READ ? "Reading":"Writing",
                    bp->b_bcount, cp->page_no,
                    cp->dmatype == DMA_PROG ? "Chain":"Single" );
            #endif
            /*  set the command to be executed during Dma  */
            cp->dmabits = cp->dmacmd;
            cocoPrepAmcc ( cp );
            /* =======================
             *    Chained DMA
             * ======================= */
            if ( cp->dmatype == DMA_PROG ) {
                    dmaPg = cocoMakeChain ( cp, cp->addrList, cp->page_no );
                    if ( dmaPg == (coco_dmapage_t *)NULL ) {
                            printf ("cocoStrategy: Error creating chain list\n");
                            alenlist_done (cp->addrList);
                            bioerror (bp, EIO);
                            biodone (bp);
                            return(EIO);
                    }
                    tot_bytes = cp->page_no * sizeof(coco_dmapage_t);
                    p_dmaPg = pciio_dmatrans_addr ( cp->vhdl, cp->dev_desc,
                                              kvtophys(dmaPg), tot_bytes,
                                              PCIIO_DMAMAP_BIGEND );    
                    /*  write back cache for chain list  */
                    dki_dcache_wbinval ( dmaPg, tot_bytes );
                    s = COCO_LOCK();
                    /*  start the Chained Dma  */ 
                    cocoStartProgDma ( cp, p_dmaPg, bp->b_bcount, rw ); 
                    if ( rw == B_READ )
                         cp->dmastat = DMA_READ_WAIT;
                    else cp->dmastat = DMA_WRITE_WAIT;
                    cp->chain_list = (caddr_t)dmaPg;
                    #ifdef DEBUG
                    printf ("cocoStrategy: Waiting for chain interrupt\n");
                    #endif
                    /*  so we dont wait forever for an interrupt  */
                    cp->tid = itimeout(cocoTimeOut, cp, RW_TIMER,
                                       pltimeout,  0, 0, 0);
                    COCO_UNLOCK(s);
                    return(0);
            }
            /* ===========================
             *      Single page DMA
             * =========================== */
             /*  get a page to DMA  */
             if ( alenlist_get(cp->addrList, NULL, NBPP,
                 &p_addr, &p_size) != ALENLIST_SUCCESS ) {
                    cmn_err (CE_WARN, "cocoDma: Not enough Memory");
                    alenlist_done(cp->addrList);
                    bioerror ( bp, EIO);
                    biodone(bp);
                    return(EIO);
             }
            /*  single dma, memory -> board  */
            
            s = COCO_LOCK();
            cocoStartSingleDma ( cp, p_addr, p_size, rw );
            if ( rw == B_READ )
                 cp->dmastat = DMA_READ_WAIT;
            else cp->dmastat = DMA_WRITE_WAIT;
            cp->chain_list   = NULL;
            #ifdef DEBUG
            printf ("cocoStrategy: Waiting for Single interrupt\n");
            #endif
            /*  so we dont wait forever for an interrupt  */
            cp->tid = itimeout(cocoTimeOut, cp, RW_TIMER,
                               pltimeout,  0, 0, 0);
            #ifdef DEBUG
            printf ("cocoStrategy: Waiting for Single interrupt, tid = %d\n",
                    cp->tid );
            #endif
            COCO_UNLOCK(s);
            return(0);
    }
    /*************************************************************************
     ***                   c o c o R e a d W r i t e                       ***
     *************************************************************************
     *
     *  Name:       cocoReadWrite
     *
     *  Purpose:    Starts simultaneous Read and Write DMA to user's space. 
     *
     * Returns:     0 for success, or errno 
     *
     *************************************************************************/
    static int
    cocoReadWrite ( card_t *cp, coco_rw_t *rw )
    {
            register caddr_t  dum;
            register caddr_t  w_kvaddr, r_kvaddr;
            register uint_t  *cache_line;
            register int      r_page_no, w_page_no, s, err, i, cache_bytes;
            register int      tot_r_cache, tot_w_cache, tot_bytes;
            register int      coco_adjust_chain, tot_wrong;
            coco_dmapage_t    *w_dmaPg, *r_dmaPg;
            alenlist_t        addrList2;
            iopaddr_t         pr_dmaPg, pw_dmaPg;
            cocoResetAmcc(cp);  
            cocoPrepAmcc(cp);
            tot_bytes = rw->buf_size * sizeof(uint_t); 
            coco_adjust_chain = rw->adjust_chain; 
            /* =========================================================
             *      Scatter-Gather list for Write buffer (mem -> board)
             * ========================================================= */
            /*  lock user's pages in memory for DMA  */
            if ( cocoLockUser ( (caddr_t)rw->w_buf, tot_bytes, B_WRITE) != 0 ) {
                    cmn_err (CE_WARN, "cocoReadWrite: Cannot lock user's pages");
                    return (EFAULT);
            }
            /*
             *   write back and invalidate the data for mem->board
             *   adjust the address for current data cache size 
             */
    #ifdef R10000
            cache_line = (uint_t *)rw->w_buf;
            cache_line = (uint_t *)( ((uint_t)cache_line / COCO_CACHE_SIZE) * 
                                      COCO_CACHE_SIZE ); 
            cache_bytes =  tot_bytes + ( (uint_t)rw->w_buf - (uint_t)cache_line);
            dki_dcache_wbinval ( (caddr_t)cache_line, cache_bytes );
    #else
            dki_dcache_wbinval ( rw->w_buf, tot_bytes );
            if ( cp->mappedkv )
                    dki_dcache_wbinval (cp->mappedkv, cp->mappedkvlen );
    #endif
            /*   create scatter-gather list of user's buffer  */
            addrList2 = uvaddr_to_alenlist( (alenlist_t)NULL, (caddr_t)rw->w_buf, 
                                            (size_t)tot_bytes ); 
            /*  did we make it ? */
            if ( addrList2 == (alenlist_t)NULL ) {
                    cmn_err (CE_WARN, "cocoreadWrite: cannot create Wrt alenlist");
                    cocoUnlockUser ( (caddr_t)rw->w_buf, tot_bytes, B_WRITE );
                    return (EIO);
            }
            
            cp->w_addrList = pciio_dmatrans_list ( cp->vhdl, cp->dev_desc,
                                             addrList2, PCIIO_DMAMAP_BIGEND); 
    #if 0
            w_page_no = alenlist_size ( cp->w_addrList );
    #endif
            w_page_no = cocoAlenlistSize ( cp->w_addrList );
            cp->w_page_no = w_page_no; 
            alenlist_done ( addrList2 );
            #ifdef DEBUG4
            printf ("cocoReadWrite: Write %d bytes [%d pages]\n",
                    tot_bytes, w_page_no ); 
            #endif
            /* =========================================================
             *      Scatter-Gather list for Read buffer ( board -> mem )
             * ========================================================= */
            /*  lock user's pages in memory for DMA  */
            if ( cocoLockUser ( (caddr_t)rw->r_buf, tot_bytes, B_READ) != 0 ) {
                    cmn_err (CE_WARN, "cocoReadWrite: Cannot lock user's pages");
                    cocoUnlockUser ( (caddr_t)rw->w_buf, tot_bytes, B_WRITE );
                    return (EFAULT);
            }
            /*
             *   Invalidate the cache for board->mem
             *   adjust the address for current data cache line size
             */ 
    #ifdef R10000
            cache_line = (uint_t *)rw->r_buf;
            cache_line = (uint_t *)( ((uint_t)cache_line / COCO_CACHE_SIZE) *   
                                      COCO_CACHE_SIZE );
            cache_bytes =  tot_bytes + ( (uint_t)rw->r_buf - (uint_t)cache_line);
            dki_dcache_inval ( (caddr_t)cache_line, cache_bytes); 
    #else
            dki_dcache_inval ( rw->r_buf, ((tot_bytes/NBPP)+1)*NBPP );
    #endif
            /*   create scatter-gather list  */ 
            addrList2 = uvaddr_to_alenlist( (alenlist_t)NULL, (caddr_t)rw->r_buf, 
                                            (size_t)tot_bytes);
            /*  did we make it ? */
            if ( addrList2 == (alenlist_t)NULL ) {
                    cmn_err (CE_WARN, "cocoreadWrite: cannot create Rd alenlist");
                    cocoUnlockUser ( (caddr_t)rw->w_buf, tot_bytes, B_WRITE );
                    cocoUnlockUser ( (caddr_t)rw->r_buf, tot_bytes, B_READ);
                    return (EIO);
            }
            cp->r_addrList = pciio_dmatrans_list ( cp->vhdl, cp->dev_desc,
                                             addrList2, PCIIO_DMAMAP_BIGEND);
    #if 0
            r_page_no = alenlist_size ( cp->r_addrList );
    #endif
            r_page_no = cocoAlenlistSize ( cp->r_addrList );
            cp->r_page_no = r_page_no; 
            alenlist_done ( addrList2 );
            #ifdef DEBUG4
            printf ("cocoReadWrite: Read %d bytes [%d pages]\n", 
                    tot_bytes, r_page_no );
            #endif
            /*  assume Single Dma type   */
            w_dmaPg = (coco_dmapage_t *)NULL;
            r_dmaPg = (coco_dmapage_t *)NULL;
            /* ======================================================
             *    Chain DMA - Prepare chain list for read and write
             * ====================================================== */
            if ( cp->dmatype == DMA_PROG ) {
                    /*  need to adjust read/write byte counts  */
                    if ( (coco_adjust_chain == 1) &&
                         (rw->r_buf != rw->w_buf) ) {       
                            err = cocoMakeChainRW ( cp, &w_dmaPg, &r_dmaPg );
                            if ( err ) {
                               cmn_err (CE_WARN, 
                               "cocoReadWrite: Could not make Chain List, err = %d",
                               err );
                              cocoUnlockUser ( (caddr_t)rw->r_buf, tot_bytes,
                                                B_READ);
                              cocoUnlockUser ( (caddr_t)rw->w_buf, tot_bytes, 
                                                B_WRITE);
                               alenlist_done(cp->r_addrList);
                               alenlist_done(cp->w_addrList);
                               return (err);
                            }
                    }
                    /*  no need to adjust read/write byte counts  */
                    else {
                       /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                        *    Prepare Chain List for Write
                        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
                       #ifdef DEBUG
                       printf (
                        "cocoReadWrite: Preparing Chain for Write, %d pages\n",
                                          w_page_no );
                       #endif
                       w_dmaPg = cocoMakeChain ( cp, cp->w_addrList, w_page_no );
                       if ( w_dmaPg == (coco_dmapage_t *)NULL ) {
                            printf ("cocoReadWrite: Error creating chain list\n");
                            cocoUnlockUser ( (caddr_t)rw->r_buf, tot_bytes,
                                              B_READ);
                            cocoUnlockUser ( (caddr_t)rw->w_buf, tot_bytes, 
                                              B_WRITE);
                            alenlist_done(cp->r_addrList);
                            alenlist_done(cp->w_addrList);
                            return(EIO);
                       }
                       /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                        *    Prepare Chain List for Read
                        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
                        #ifdef DEBUG
                        printf (
                         "cocoReadWrite: Preparing Chain for Read, %d pages\n",
                                           r_page_no );
                        #endif
                        r_dmaPg = cocoMakeChain ( cp, cp->r_addrList, r_page_no );
                        if ( r_dmaPg == (coco_dmapage_t *)NULL ) {
                            printf ("cocoReadWrite: Error creating chain list\n");
                            cocoUnlockUser ( (caddr_t)rw->r_buf, tot_bytes,
                                              B_READ);
                            cocoUnlockUser ( (caddr_t)rw->w_buf, tot_bytes, 
                                              B_WRITE);
                            alenlist_done(cp->r_addrList);
                            alenlist_done(cp->w_addrList);
                            kmem_free ( w_dmaPg,
                                        w_page_no * sizeof(coco_dmapage_t));
                            return(EIO);
                        }
                    }
                    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                     *    Map to PCI and cache management
                     *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
                    /*  map Write chain list   */
                    if ( coco_adjust_chain == 1 )
                         tot_w_cache = w_page_no * CHAIN_FACTOR * 
                                       sizeof(coco_dmapage_t);
                    else tot_w_cache = w_page_no * sizeof(coco_dmapage_t);
                    pw_dmaPg = pciio_dmatrans_addr ( cp->vhdl, cp->dev_desc,
                                              kvtophys(w_dmaPg),
                                              tot_w_cache,
                                              PCIIO_DMAMAP_BIGEND );
                    #ifdef DEBUG
                    cocoShowChain( "Write Chain list", w_dmaPg );
                    #endif
                    /*  write back the cache for this chain list */
                    dki_dcache_wbinval ( w_dmaPg, tot_w_cache );
                    /*  map Read chain list  */ 
                    if ( coco_adjust_chain == 1 )
                         tot_r_cache = r_page_no * CHAIN_FACTOR * 
                                       sizeof(coco_dmapage_t);
                    else tot_r_cache = r_page_no * sizeof(coco_dmapage_t);
                    pr_dmaPg = pciio_dmatrans_addr ( cp->vhdl, cp->dev_desc,
                                              kvtophys(r_dmaPg),
                                              tot_r_cache,  
                                              PCIIO_DMAMAP_BIGEND );
                    #ifdef DEBUG
                    cocoShowChain( "Read Chain List", r_dmaPg );
                    #endif
                    /*  write back the cache for this chain list */
                    dki_dcache_wbinval ( r_dmaPg, tot_r_cache );
            }
            /*  initialize our DMA event semaphore  */
            initnsema ( &cp->dmawait, 0, "coco" );
            #ifdef DEBUG
            printf ("cocoReadWrite: Read/Write %d bytes\n", tot_bytes ); 
            #endif
            cp->iostat = IO_OK;
            cp->dmabits = cp->dmacmd;
            cp->wp_addr = (alenaddr_t)0;
            cp->rp_addr = (alenaddr_t)0;
            cp->wp_size = 0;
            cp->rp_addr = 0;
            s = COCO_LOCK();
            err = cocoStartRWDma( cp, pw_dmaPg, tot_bytes, pr_dmaPg, tot_bytes);
            if ( err == 0 ) {
               err = SleepEvent(&cp->dmawait); 
               if ( err ) {
                    err = EINTR;
                    cmn_err (CE_NOTE, "cocoReadWrite: Interrupted");
               }
               else {
                    if ( cp->iostat == IO_OK ) {
                            #ifdef DEBUG
                            printf ("cocoReadWrite: woken up\n");
                            #endif
                    }
                    
                    if ( cp->iostat == IO_TIME ) {
                            cmn_err (CE_NOTE, "cocoReadWrite: Timed out");  
                            err = ETIME;
                    }
                    if ( cp->iostat == IO_ERROR ) {
                            cmn_err (CE_NOTE, "cocoReadWrite: IO Error");
                            err = EIO;  
                    }
               }
            }
            else {
                    cmn_err (CE_WARN, 
                            "cocoReadWrite: Could not start Sim read/write");
            }
            /*  we are done   */
            cp->dmastat = DMA_IDLE;
            cp->dmabits = 0;
            /*
             *   Invalidate the cache for board->mem
             *   adjust the address for current data cache line size
             */
    #ifdef R10000
            cache_line = (uint_t *)rw->r_buf;
            cache_line = (uint_t *)( ((uint_t)cache_line / COCO_CACHE_SIZE) *
                                      COCO_CACHE_SIZE );
            cache_bytes =  tot_bytes + ( (uint_t)rw->r_buf - (uint_t)cache_line);
            dki_dcache_inval ( (caddr_t)cache_line, cache_bytes);
    #else
            dki_dcache_inval ( rw->r_buf, ((tot_bytes/NBPP)+1)*NBPP );
    #endif
            cocoUnlockUser ( (caddr_t)rw->w_buf, tot_bytes, B_WRITE );
            cocoUnlockUser ( (caddr_t)rw->r_buf, tot_bytes, B_READ);
            alenlist_done(cp->r_addrList);
            alenlist_done(cp->w_addrList);
            if ( r_dmaPg ) kmem_free ( r_dmaPg, tot_r_cache );
            if ( w_dmaPg ) kmem_free ( w_dmaPg, tot_w_cache); 
            COCO_UNLOCK(s);
            return(err);
    }
    /*************************************************************************
     ***                   c o c o S t a r t R W D m a                     ***
     *************************************************************************
     *
     *  Name:       cocoStartRWDma
     *
     *  Purpose:    Program the boards for Simultaneous Read/Write DMA. 
     *              The read and write channel's scatter-gather have been
     *              prepared before and are in cp->r_addrList and cp->w_addrList
     *              For Chained DMA, the chain list for read and write channels
     *              are prepared before and passed to us as parameters.
     *
     * Returns:     0 = Success, or errno 
     *
     *************************************************************************/
    static int
    cocoStartRWDma ( card_t *cp, iopaddr_t pw_dmaPg, int tot_write, 
                     iopaddr_t pr_dmaPg, int tot_read ) 
    {
            register caddr_t   adr_cfg, adr_norm, adr_amcc;
            register int       i, err, tot_words, tot_bytes;
            register uint_t    dmabits, dmacfg, dmacmd;
            size_t            rp_size, wp_size;
            alenaddr_t        rp_addr, wp_addr;
            adr_cfg  = cp->conf_adr;
            adr_norm = cp->norm_adr;
            adr_amcc = cp->amcc_adr;
            dmacfg   = cp->dmacfg;
            dmabits  = cp->dmabits;
            dmacmd   = cp->dmacmd;
            Out32(adr_cfg, dmacfg );   /*  clear eof markers */ 
            /* =======================
             *    Chained DMA
             * ======================= */
            if ( cp->dmatype == DMA_PROG ) {
                    /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                     *     Programming the board for Read/Write Chain Dma 
                     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
                    /*  prepare Amcc counters   */
                    Out32(adr_amcc+AMCC_OP_REG_MRTC, tot_write );
                    Out32(adr_amcc+AMCC_OP_REG_MWTC, tot_read ); 
                    /*  Program the Write channel Address (board -> mem ) */ 
                    dmabits |= DMAREG_PREN | DMAREG_PWEN;
                    dmacfg |= dmabits;
                    Out32(adr_cfg, dmacfg);
                    /*   Write channel address  (b->m) */
                    Out32(adr_cfg, dmacfg | DMAREG_RAM | DMAREG_RAMPRWR );
                    Out32(adr_norm, pr_dmaPg);
                    /*  Read channel address (m->b)    */ 
                    Out32(adr_cfg, dmacfg | DMAREG_RAM | DMAREG_RAMPRRD );
                    Out32(adr_norm, pw_dmaPg);
                    /*   Start Read/Write DMA - only Write(b->m) is enabled   */ 
                    dmabits |= DMAREG_INTPWEN | DMAREG_WEN | DMAREG_REN | dmacmd;
                    cp->dmasize = tot_write;
                    cp->dmabits = dmabits;
                    cp->iostat  = IO_OK;
                    cp->dmastat = DMA_RW_WAIT; 
                    #ifdef DEBUG
                    printf (
       "cocoStartRWDma: read chain = 0x%x for %d, write chain = 0x%x for %d\n",
                                  pr_dmaPg, tot_read, pw_dmaPg, tot_write );
                    cocoReport (cp, "cocoStartRWDma: Chain Dma started");
                    #endif
                    /*  start the DMA  */
                    pciio_flush_buffers ( cp->vhdl );
                    microtime ( &cp->start_time );
                    Out32(adr_cfg, dmacfg | dmabits );
                    /*  so we dont wait forever for an interrupt  */
                    cp->tid = itimeout(cocoTimeOut2, cp, SIMRW_TIMER,
                                       pltimeout,  0, 0, 0);
                    return(0);
            }
            /* ===========================
             *      Single page DMA
             * =========================== */
             /*     get next page for Read channel if nothing left from past */
             if ( cp->wp_addr == (alenaddr_t)0 ) { 
                 if ( cp->w_page_no <= 0 ) {
                    #ifdef DEBUG
                    printf (
    "cocoStartRWDma: Premature Write, w_page_no = %d, r_page_no = %d, wp_addr = 0x%x, rp_addr = 0x%x\n", 
                    cp->w_page_no, cp->r_page_no, cp->wp_addr, cp->rp_addr );
                    #endif
                    cmn_err (CE_WARN, "cocoStartRWDma: Premature end of Write");
                    return(EIO);
                 }
                 if ( alenlist_get(cp->w_addrList, NULL, NBPP,
                      &wp_addr, &wp_size) != ALENLIST_SUCCESS ) {
                      cmn_err (CE_WARN, "cocoStartRWDma: bad scater-gather");
                      return(EIO);
                 }
                 cp->w_page_no--;
             }
             /*  some bytes left from past  */
             else {
                    wp_addr = cp->wp_addr;
                    wp_size = cp->wp_size;
                    #ifdef DEBUG
                    printf (
                       "cocoStartRWDma: %d old bytes at 0x%x for read chan\n",
                                                  wp_size, wp_addr );
                    #endif
            }
             /*     get next page for Write channel if nothing left from past */ 
             if ( cp->rp_addr == (alenaddr_t)0 ) {
                 if ( cp->r_page_no <= 0 ) {
                    #ifdef DEBUG
                    printf (
    "cocoStartRWDma: Premature Read, r_page_no = %d, w_page_no = %d, wp_addr = 0x%x, rp_addr = 0x%x\n", 
                    cp->r_page_no, cp->w_page_no, cp->wp_addr, cp->rp_addr );
                    #endif
                    cmn_err (CE_WARN, "cocoStartRWDma: Premature end of Read");
                    return(EIO);
                 }
                 if ( alenlist_get(cp->r_addrList, NULL, NBPP,
                      &rp_addr, &rp_size) != ALENLIST_SUCCESS ) {
                      cmn_err (CE_WARN, "cocoReadWrite: bad scater-gather");
                      return(EIO);
                 }
                 cp->r_page_no--;
             }
             /*  some bytes left from past  */
             else {
                    rp_addr = cp->rp_addr;
                    rp_size = cp->rp_size;
                    #ifdef DEBUG
                    printf (
                            "cocoStartRWDma: %d old bytes at 0x%x for Write chan\n",
                                       rp_size, rp_addr );
                    #endif
            }
            /*  adjust - we shoud write as much as we can read   */
            cp->wp_addr = (alenaddr_t)0;
            cp->rp_addr = (alenaddr_t)0;
            cp->wp_size = 0;
            cp->rp_size = 0;
            #ifdef DEBUG
            printf (
    "cocoStartRWDma: Original sizes, rp_size = %d at 0x%x, wp_size = %d at 0x%x\n",
                          rp_size, rp_addr, wp_size, wp_addr );
            #endif
            /*  Write more than read ?   */
            if ( wp_size == rp_size )
                    tot_bytes = wp_size;
            if ( wp_size > rp_size ) {
                    tot_bytes   = rp_size;
                    cp->wp_addr = (alenaddr_t)((int)wp_addr + tot_bytes);
                    cp->wp_size = wp_size - tot_bytes;
            }
            /*  read more than write ?  */
            if ( rp_size > wp_size ) {
                    tot_bytes = wp_size;
                    cp->rp_addr = (alenaddr_t)((int)rp_addr + tot_bytes); 
                    cp->rp_size = rp_size - tot_bytes;
            }
            tot_words = (int)tot_bytes/sizeof(uint_t);
            #ifdef DEBUG
            printf (
    "cocoStartRWDma: Single Dma %d [%d words], rp_addr = 0x%x, wp_addr = 0x%x\n",
                              tot_bytes, tot_words, rp_addr, wp_addr );
            #endif
            tot_words--;   /*  counts down to 0xffff in hardware */
            /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
             *   Program the board for Read and Write
             * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
            /*  prepare Amcc counters   */
            /* Out32(adr_amcc+AMCC_OP_REG_MRTC, tot_bytes );  */
            Out32(adr_amcc+AMCC_OP_REG_MRTC, tot_bytes);  
            Out32(adr_amcc+AMCC_OP_REG_MWTC, tot_bytes ); 
            /*   program for Write channel ( board -> mem )  */
            Out32(adr_cfg, dmacfg | DMAREG_WCNT);
            Out32(adr_norm, (uint_t)tot_words);
            Out32(adr_cfg, dmacfg | DMAREG_RAM | DMAREG_RAMWADR); /* b->m */
            Out32(adr_norm, (uint_t)rp_addr);
            /*   program for Read channel ( mem -> board ) */
            Out32(adr_cfg, dmacfg | DMAREG_RCNT);
            Out32(adr_norm, (uint_t)tot_words); 
            Out32(adr_cfg, dmacfg | DMAREG_RAM | DMAREG_RAMRADR); /* b->m */
            Out32(adr_norm, (uint_t)wp_addr);
            /*  start read/write Dma - only write channel (b->m) is needed   */
            dmabits |= DMAREG_INTWEN | DMAREG_REN | DMAREG_WEN | dmacmd;
            cp->dmasize = wp_size;
            cp->dmabits = dmabits;
            cp->dmastat = DMA_RW_WAIT;
            cp->iostat  = IO_OK;
            /*  start the DMA   */
            pciio_flush_buffers ( cp->vhdl );
            microtime ( &cp->start_time );
            Out32(adr_cfg, dmacfg | dmabits | dmacmd );
            /*  so we dont wait forever for an interrupt  */
            cp->tid = itimeout(cocoTimeOut2, cp, SIMRW_TIMER,
                               pltimeout,  0, 0, 0);
            return(0);
    }
    /*************************************************************************
     ***                   c o c o R e s e t                               ***
     *************************************************************************
     *
     *  Name:       cocoReset
     *
     *  Purpose:    This routine initializes the board by resetting the add-on     
     *              logic, AMCC fifos, Chameleon chip and FIFOs. 
     *              After reset, FIFO flags are programmed, default DMA controller
     *              config. register is written and Chameleon chip is enabled     
     *
     * Returns:     None
     *
     *************************************************************************/
    static void
    cocoReset( card_t  *cp )
    {
            uint_t   stat;
            register caddr_t  adr_amcc; 
            register caddr_t  adr_cfg;
            register uint_t   tmp; 
            adr_amcc = cp->amcc_adr;
            adr_cfg  = cp->conf_adr;
            /*  disable interrupts   */
            Out32(adr_amcc+AMCC_OP_REG_INTCSR, AMCC_INTCSR_RST | AMCC_INTCSR_RCLR |
                                               AMCC_INTCSR_WCLR );
            /* reset AMCC fifos and Xilinx  */
            Out32(adr_amcc+AMCC_OP_REG_MCSR, AMCC_RST_ADDON );
            Out32(adr_amcc+AMCC_OP_REG_MCSR, AMCC_RST_FIFOS ); 
            /* reset Chameleon and FIFOs and bring FIFOs to programming flags */
            Out32(adr_cfg, DMAREG_CCRES | DMAREG_FRES | DMAREG_FSCLK );
            Out32(adr_cfg, DMAREG_FSCLK);
            Out32(adr_cfg, DMAREG_FSCLK);
            /* program FIFO flags */
            cocoProgFlags ( adr_cfg, DMAREG_FRES,100,450,100,450);
            /* bring FIFOs in functional mode and Chameleon out of reset */
            Out32(adr_cfg, DMAREG_FRES | DMAREG_FSCLK);
            Out32(adr_cfg, DMAREG_CCRES | DMAREG_FSCLK);
            Out32(adr_cfg, DMAREG_CCRES | DMAREG_FSCLK | DMAREG_PTEN);
            /* set default config. reg */
            cp->dmacfg = DMAREG_NVIFEN | DMAREG_PTEN | DMAREG_CCRES | DMAREG_FSCLK;
            /* initialize Chameleon */
            cocoSetMode(cp, 0,0,0,1);
            /*  enable Amcc Interrupt   */
            Out32(adr_amcc+AMCC_OP_REG_INTCSR, AMCC_INTCSR_MASK );
    }
    /*************************************************************************
     ***                   c o c o P r o g F l a g s                       ***
     *************************************************************************
     *
     *  Name:       cocoProgFlags  
     *
     *  Purpose:    Program offsets of ASIC fifo flags by writing 18 bit 
     *              serial registers of FIFO containg 9 bit AF and 9 bit AE 
     *              flag offset
     *
     * Returns:     None
     *
     *************************************************************************/
    static void
    cocoProgFlags ( caddr_t adr_cfg, uint_t d, ushort_t iae, ushort_t iaf, 
                    ushort_t oae, ushort_t oaf )
    {
            ushort_t andfl;
            ushort_t shft;
            ushort_t bit0;
            ushort_t bit1;
            uint_t   bit;
            andfl=0x01;
            shft=0;
            #ifdef DEBUG
            printf ("cocoProgFlags: iae = 0x%x, oae = 0x%x, oaf = 0x%x\n",
                    iae, oae, oaf ); 
            #endif
            do {
                    bit0 = (iaf & andfl);
                    bit1 = (oaf & andfl);
                    bit = d | 0x00000010;
                    if (bit0 == 0) 
                            bit |= 0x00000004;
                    if (bit1 == 0) 
                            bit |= 0x00000008;
                    Out32(adr_cfg, bit);
                    bit &= 0xFFFFFFEF;
                    Out32(adr_cfg, bit);
                    andfl = andfl << 1;
                    shft++;
           }
           while ( shft <= 8 );
            andfl = 0x01;
            shft = 0;
            do {
                    bit0 = (iae & andfl);
                    bit1 = (oae & andfl);
                    bit = d | 0x00000010;
                    if (bit0==0) 
                            bit |= 0x00000004;
                    if (bit1==0) 
                            bit |= 0x00000008;
                    Out32(adr_cfg, bit);
                    bit = bit & 0xFFFFFFEF;
                    Out32(adr_cfg, bit);
                    andfl = andfl << 1;
                    shft++;
            }
            while (shft<=8);
    }
    /*************************************************************************
     ***                   c o c o S e t  M o d e                          ***
     *************************************************************************
     *
     *  Name:       cocoSetMode
     *
     *  Purpose:    Set Chameleon Mode Register 
     *
     * Returns:     None
     *
     *************************************************************************/
    static void
    cocoSetMode ( card_t *cp, int mode, int swap, int slice, int flag )
    { 
            uint_t  cmd;
            cmd = COCO_WRENA | COCO_DELAY;
            if (mode) 
                    cmd |=  COCO_MODE;
            
            if (swap) 
                    cmd |= COCO_SWAP;
            if (slice) 
                    cmd |= COCO_SLICE;
            if (flag) 
                    cmd |= COCO_FLAG;
            
            #ifdef DEBUG
            printf ("cocoSetMode:  setting mode to 0x%x\n", cmd );
            #endif
            cocoCommand (cp, COCO_SETMODE, cmd);
            
    }
    /*************************************************************************
     ***                   c o c o C o m m a n d                           ***
     *************************************************************************
     *
     *  Name:       cocoCommand  
     *
     *  Purpose:    Sends a command to Chameleon. Writes the command to 
     *              the controller and writes the data to Fifo.
     *
     * Returns:     None
     *
     *************************************************************************/
    static void
    cocoCommand ( card_t *cp, uint_t cmd, uint_t data )
    {
            register uint_t   stat;
    #if 0
            stat = Inp32(cp->amcc_adr+AMCC_OP_REG_MCSR);
            while ( stat & 0x1 ) {
                  printf ("Fifo full..waiting\n");
                  delay(1);
                  stat = Inp32(cp->amcc_adr+AMCC_OP_REG_MCSR);
            }
    #endif
            /*  set Chameleon connad in Config. Register  */
            Out32(cp->conf_adr,  cmd | cp->dmacfg );
            /*  writes the data for the command to AMCC Fifo  */
            Out32(cp->amcc_adr + AMCC_OP_REG_FIFO, data);
    }
    /*************************************************************************
     ***                   c o c o B u f O u t                             ***
     *************************************************************************
     *
     *  Name:       cocoBufOut 
     *
     *  Purpose:    Dumps a buffer containig 32-bit values to Chameleon Fifo.
     *              Data is given to Chameleon in Transparent mode.
     *
     * Returns:     None
     *
     *************************************************************************/
    static void
    cocoBufOut ( card_t *cp, uint_t *buf, int size )
    {
            register int    i, j;
            register uint_t cmd, stat;
            #ifdef DEBUG
            printf ("cocoBufOut: outputing %d words to Fifo\n", size );
            #endif
            /*  set to Transparent Mode  */
            cmd = cp->dmacfg | COCO_TRANSP;
            Out32(cp->conf_adr, cmd );
            /*   Dump the data   */
            for ( i = 0; i < size; i++) {
                    j = 0;
                    stat = Inp32(cp->amcc_adr+AMCC_OP_REG_MCSR);
                    if ( stat & 0x03 ) {  /*  fifo has room  */
                        Out32(cp->amcc_adr+AMCC_OP_REG_FIFO, buf[i] );
                        continue;
                    }
                    printf (
                   "cocoBufOut: Fifo is full, stat = 0x%x, discarding %d bytes\n",
                                           stat, size - i );
                    break; 
            }       
    }
    /*************************************************************************
     ***                   c o c o B u f I n                               ***
     *************************************************************************
     *
     *  Name:       cocoBufIn 
     *
     *  Purpose:    Reads Fifo entries into a buf.  
     *
     * Returns:     None
     *
     *************************************************************************/
    static void
    cocoBufIn ( card_t *cp, uint_t *buf, int size )
    {
            register int    i;
            for ( i = 0; i < size; i++ ) {
                    buf[i] = cocoReadAmccFifo(cp);
            }
     
    }
    /*************************************************************************
     ***                   c o c o R e a d A m c c F i f o                 ***
     *************************************************************************
     *
     *  Name:       cocoReadAmccFifo
     *
     *  Purpose:    Reads a 32-bit value word from AMCC Internal Fifo
     *              Returns zero if Fifo is empty
     *
     * Returns:     Fifo value or zero if fifo is empty
     *
     *************************************************************************/
    static int 
    cocoReadAmccFifo ( card_t  *cp )
    {
            register uint_t  stat;
            register int     i;
            for ( i = 0; i < 3; i++ )
                    stat = Inp32(cp->amcc_adr+AMCC_OP_REG_MCSR);
            /* if ( stat != 0x000000e6 )  {  */
            if ( stat != 0x1026 )  {
                    stat = Inp32(cp->amcc_adr + AMCC_OP_REG_FIFO );
                    return(stat);
            }
            return(0);
    }
    /*************************************************************************
     ***                   c o c o F i f o T e s t                         ***
     *************************************************************************
     *
     *  Name:       cocoFifoTest
     *
     *  Purpose:    Fills Fifo with a pattern, read it back one by one
     *              and compare. Reports back the results.
     *
     * Returns:     0 = Success, 1 = Failed.
     *
     *************************************************************************/
    static int
    cocoFifoTest ( card_t *cp, int pat )
    {
            register int  i, j, err;
            uint_t   res, expect;
            #ifdef DEBUG
            printf ("cocoFifoTest:  testing Fifo, pattern: %d\n", pat );
            #endif
            err = 0;   /* assume success */
            /*  test for 50 times ..  */
            for( j = 0; j < 50; j++) {
     
                    /*  fill Fifo with pattern   */
                    for(i=0;i<750;i++) 
                         cocoCommand (cp, COCO_TRANSP, cocoPattern(pat,i) );
                    /* read it back and compare   */
                    for( i = 0;i < 750; i++) {
                         res = cocoReadAmccFifo( cp );   /* what we read   */
                         expect = cocoPattern( pat, i);  /* what we expect */
                         if (res != expect) {
                             printf(
          "cocoFifoTest: Fifo entry %d, expected 0x%x, read 0x%x (round %d)\n",
                                              i, expect, res, j);
                            err = 1;
                         }
                    }
            }
            return(err);
    }
    /*************************************************************************
     ***                   c o c o P a t t e r n                           ***
     *************************************************************************
     *
     *  Name:       cocoPattern      
     *
     *  Purpose:    Pattern generator for testing purpose.
     *
     * Returns:     The generated pattern 
     *
     *************************************************************************/
    static int
    cocoPattern ( int pat, int cnt )
    {
        register int res;
        /*  for now, we always return pattern 1  */
        pat=1;
            
        switch (pat) {
            case 1:   
                    res = cnt;   
                    break;
            case 2:   
                    res = ~cnt;  
                    break;
            case 3:
                    res = 0xFFFFFFFFL;
                    break;
            case 4:
                    res = 0x00000000L;
                    break;
            case 5:
                    if (cnt % 2 != 1) 
                         res = 0xaaaaaaaaL; 
                    else res = 0x55555555L;
                    break;
            case 6:
                    cnt = (cnt & 0x01FL);
                    res = (1 << cnt);
                    break;
            case 7:
                    cnt = (cnt & 0x01FL);
                    res = (1 << cnt);
                    res = ~res;
                    break;
            case 8:
                    if ( cnt % 2 == 1) 
                         res = 0x0f0f0f0fL; 
                    else res = 0xf0f0f0f0L;
                    break;
            case 9:
                    cnt = (cnt & 0xFF);
                    res = cnt + ( cnt << 8) + (cnt << 16) + (cnt << 24);
                    break;
            case 10:
                    cnt = (cnt & 0xFFFF);
                    res = cnt + ( cnt << 16);
                    break;
            }
            return(res);
    }
    /*************************************************************************
     ***                   c o c o R e a d M o d e                         ***
     *************************************************************************
     *
     *  Name:       cocoReadMode
     *
     *  Purpose:    Reads Chameleon Mode register
     *
     * Returns:     The generated pattern
     *
     *************************************************************************/
    static int
    cocoReadMode ( card_t *cp )
    {
            register uint_t   mode;
            /*  set Chameleon command in Config. Register  */
            cocoCommand ( cp, COCO_READMODE, 0x0 );
            mode = cocoReadAmccFifo(cp);
            #ifdef DEBUG
            printf ("cocoReadMode:  Mode = 0x%x\n", mode );
            #endif
            return (mode);
    }
    /*************************************************************************
     ***                   c o c o R e a d D m a R e g s                   ***
     *************************************************************************
     *
     *  Name:       cocoReadDmaRegs
     *
     *  Purpose:    Reads Xilinx DMA Registers 
     *
     * Returns:     None. 
     *
     *************************************************************************/
    static void
    cocoReadDmaRegs ( card_t *cp, uint_t *dmaRegs )
    {
            register int   i, k; 
            /*  read DMA registers into dmaRegs table  */
            i = 0;
            for ( k = 13; k <= 16; k++) {
                    Out32(cp->conf_adr, cp->dmacfg | (k << 6) );
                    dmaRegs[i++] = Inp32(cp->norm_adr);
            }
    }
    /*************************************************************************
     ***                   c o c o W r i t e D m a R e g s                 ***
     *************************************************************************
     *
     *  Name:       cocoWriteDmaRegs
     *
     *  Purpose:    Write values in dmaRegs[] to Xilinx DMA Registers
     *
     * Returns:     None.
     *
     *************************************************************************/
    static void
    cocoWriteDmaRegs ( card_t *cp, uint_t *dmaRegs )
    {
            register int   i, k;
            /*  read DMA registers into dmaRegs table  */
            i = 0;
            for ( k = 13; k <= 16; k++) {
                    Out32(cp->conf_adr, cp->dmacfg | (k << 6) );
                    Out32(cp->norm_adr, dmaRegs[i++] );
            }
    }
    /*************************************************************************
     ***                   c o c o R e a d A d d r                         ***
     *************************************************************************
     *
     *  Name:       cocoReadAddr     
     *
     *  Purpose:    Reads Address Register
     *
     * Returns:     Address Register value
     *
     *************************************************************************/
    static int
    cocoReadAddr (card_t *cp)
    {
            register uint_t  addr_reg;
            cocoCommand ( cp, COCO_READADDR, 0x0 );
            addr_reg = cocoReadAmccFifo(cp);
            
            #ifdef DEBUG
            printf ("cocoreadAddr:  Address Reg = 0x%x\n", addr_reg );
            #endif
            return(addr_reg);
    }
    /*************************************************************************
     ***                   c o c o S e t A d d r                           ***
     *************************************************************************
     *
     *  Name:       cocoSetAddr 
     *
     *  Purpose:    Set Address Register to given value 
     *
     * Returns:     None 
     *
     *************************************************************************/
    static void
    cocoSetAddr ( card_t *cp, uint_t val )
    {
            cocoCommand ( cp, COCO_SETADDR, val );
    }
    /*************************************************************************
     ***                   c o c o D m a R e g s T e s t                   ***
     *************************************************************************
     *
     *  Name:       cocoDmaRegsTest
     *
     *  Purpose:    Tests access to Xilinx DMA registers.
     *              Write and read to DMA registers, compare values and 
     *              report back the results.
     *
     * Returns:     Test result ( 0 = Success, 1 = Failed) 
     *
     *************************************************************************/
    static int
    cocoDmaRegsTest ( card_t *cp )
    {
            register int i, j, k, err, res, expct, dmacfg, pat;
            register caddr_t   adr_cfg, adr_norm;
            cmn_err (CE_NOTE, "testing Chameleon DMA registers access");
            adr_cfg  = cp->conf_adr;
            adr_norm = cp->norm_adr;
            dmacfg    = cp->dmacfg;
            err      = 0;
            pat      = 1;
            /*   try for 500 times   */
            for ( i = 0; i < 500; i++ ) {
                    
                    /*  write to DMA Regsiers  */
                    for ( k = 13; k <= 16; k++ ) {
                            Out32(adr_cfg, dmacfg | (k << 6) );
                            Out32(adr_norm, cocoPattern(pat, i+k) );
                    }
                    
                    /*  read back registers and compare   */
                    for ( k = 13; k <= 16; k++ ) {
                            Out32(adr_cfg, dmacfg | (k << 6) );
                            res   = Inp32(adr_norm);
                            expct = cocoPattern(pat, i+k);
                            /*  not equal ..report it !  */
                            if ( res != expct ) {
                               cmn_err ( CE_NOTE, 
                               "Dma reg %d, expected 0x%x, read 0x%x, round %x", 
                                k, expct, res, i ); 
                                err = 1;
                            }
                    }
            }
            cmn_err (CE_NOTE, "Chameleon DMA registers access test %s",
                    (err == 0 ? "Succeeded": "Failed") ); 
            return(err);
    }
    /*************************************************************************
     ***                   c o c o I n t R a m T e s t                     ***
     *************************************************************************
     *
     *  Name:       cocoIntRamTest
     *
     *  Purpose:    Test CHameleon Internal LUTs. 
     *              Fills Internal LUT with a pattern and reads them back
     *              for comparison and reports the results. 
     *
     * Returns:     Test result ( 0 = Success, 1 = Failed)
     *
     *************************************************************************/
    static int
    cocoIntRamTest ( card_t *cp )
    {
            register int  i, err, pat, num;
            register uint_t  res;
            cmn_err (CE_NOTE, "Testing Chameleon Internal Ram access\n");
            num = 1;
            err = 0;
            
            /* ====================
             *   Fill LUTs 
             * ==================== */
            for ( i = 0; i < INT_RAM_SIZE; i++ ) {
                    pat = cocoPattern(num, i);
                    /*  set address of internal LUT location  */
                    cocoCommand (cp, COCO_SETADDR, i); 
                    /*  fill Internal LUTs with pattern   */
                    cocoCommand (cp, COCO_FILLRAMIL, pat ); 
                    cocoCommand (cp, COCO_FILLRAMIH, pat ); 
                    cocoCommand (cp, COCO_FILLRAMO,  pat ); 
            }
            /* =============================
             *     Read LUTs and compare
             * ============================= */
            for ( i = 0; i < INT_RAM_SIZE; i++ ) {
                    
                    pat = cocoPattern(num, i) & 0x03ffff;
                    /* 
                     *  set address of internal RAMIL location 
                     *  read value from that location and compare 
                     */
                    cocoCommand (cp, COCO_SETADDR, i); 
                    cocoCommand ( cp, COCO_READRAMIL, 0x0 );
                    res = cocoReadAmccFifo (cp);
            
                    /*  is what we read ok ?   */
                    if ( (res & 0x03ffff) != pat ) {
                            cmn_err (CE_NOTE, 
                            "Chameleon RAMIL at 0x%x, expected 0x%x, read 0x%x\n",
                             i, pat, res );
                            err = 1;
                    }
                    /*  
                     *  set address of internal RAMIH location    
                     *  read value from that location and compare
                     */
                    cocoCommand (cp, COCO_SETADDR, i);
                    cocoCommand ( cp, COCO_READRAMIH, 0x0 );
                    res = cocoReadAmccFifo (cp);
                    /*  is what we read ok ?   */   
                    if ( (res & 0x03ffff) != pat ) {                
                            cmn_err (CE_NOTE,               
                            "Chameleon RAMIH at 0x%x, expected 0x%x, read 0x%x\n",
                             i, pat, res );
                            err = 1;
                    }
                    /*  
                     *  set address of internal RAMO location    
                     *  read value from that location and compare
                     */
                    cocoCommand (cp, COCO_SETADDR, i);
                    cocoCommand ( cp, COCO_READRAMO, 0x0 );
                    res = cocoReadAmccFifo (cp);
                    /*  is what we read ok ?   */   
                    if ( (res & 0x03ffff) != pat ) {                
                            cmn_err (CE_NOTE,               
                            "Chameleon RAMO at 0x%x, expected 0x%x, read 0x%x\n",
                             i, pat, res );
                            err = 1;
                    }
            }
            cmn_err (CE_NOTE, "Chameleon Internal RAM test %s\n",
                     ( err == 0 ? "Succeeded":"Failed") );
            return(err);
    }
    /*************************************************************************
     ***                   c o c o E x t R a m T e s t                     ***
     *************************************************************************
     *
     *  Name:       cocoExtRamTest
     *
     *  Purpose:    Test CHameleon External LUTs. 
     *              Fills External LUT with a pattern and reads them back
     *              for comparison and reports the results. 
     *
     * Returns:     Test result ( 0 = Success, 1 = Failed)
     *
     *************************************************************************/
    static int
    cocoExtRamTest ( card_t *cp )
    {
            register int  i, err, pat, num;
            register uint_t  res;
            cmn_err (CE_NOTE, "Testing Chameleon External Ram access\n");
            num = 1;
            err = 0;
            
            /* ====================
             *   Fill RAML 
             * ==================== */
            for ( i = 0; i < EXT_RAM_SIZE; i++ ) {
                    /*  set address of internal LUT location  */
                    cocoCommand (cp, COCO_SETADDR, i); 
                    /*  fill External LUTs with pattern   */
                    cocoCommand (cp, COCO_FILLRAML, cocoPattern(num, i) );
            }
            /* ===================================
             *     Read External LUT and compare
             * =================================== */
            for ( i = 0; i < EXT_RAM_SIZE; i++ ) {
                    
                    pat = cocoPattern(num, i); 
                    /* 
                     *  set address of internal RAMIL location 
                     *  read value from that location and compare 
                     */
                    cocoCommand (cp, COCO_SETADDR, i); 
                    cocoCommand ( cp, COCO_READRAML, 0x0 );
                    res = cocoReadAmccFifo (cp);
            
                    /*  is what we read ok ?   */
                    if ( res != pat ) {
                            cmn_err (CE_NOTE, 
                            "Chameleon RAML at 0x%x, expected 0x%x, read 0x%x\n",
                             i, pat, res );
                            err = 1;
                    }
            }
            cmn_err (CE_NOTE, "Chameleon External RAM test %s\n",
                     ( err == 0 ? "Succeeded":"Failed") );
            return(err);
    }
    /*************************************************************************
     ***                   c o c o R e a d I n t R a m                     *** 
     *************************************************************************
     *
     *  Name:       cocoReadIntRam
     *
     *  Purpose:    Read Chameleon's Internal LUTs into given buffer.
     *
     * Returns:     None. 
     *
     *************************************************************************/
    static void
    cocoReadIntRam ( card_t *cp, uint_t *buf, int lut, int size )
    {
            register uint_t  cmd;
            register int     i;
            /*  which LUT we should read ?  */
            switch ( lut ) {
                    case COCO_READ_RAMIL:  cmd = COCO_READRAMIL;  break;
                    case COCO_READ_RAMIH:  cmd = COCO_READRAMIH;  break;
                    case COCO_READ_RAMO:   cmd = COCO_READRAMO;   break;
            }
            /*   fill buffer with contents of Internal Ram  */
            for ( i = 0; i < size; i++ ) {
                    cocoCommand (cp, COCO_SETADDR, i);
                    cocoCommand (cp, cmd, 0x0 );
                    buf[i] = cocoReadAmccFifo (cp);
            }
    }
    /*************************************************************************
     ***                   c o c o W r i t e I n t R a m                   ***
     *************************************************************************
     *
     *  Name:       cocoWriteIntRam
     *
     *  Purpose:    Write Chameleon's Internal LUTs into given buffer.
     *
     * Returns:     None.
     *
     *************************************************************************/
    static void
    cocoWriteIntRam ( card_t *cp, uint_t *buf, int lut, int size )
    {
            register uint_t  cmd;
            register int     i;
            /*  which LUT we should read ?  */
            switch ( lut ) {
                    case COCO_FILL_RAMIL:  cmd = COCO_FILLRAMIL;  break;
                    case COCO_FILL_RAMIH:  cmd = COCO_FILLRAMIH;  break;
                    case COCO_FILL_RAMO:   cmd = COCO_FILLRAMO;   break;
            }
            /*   fill buffer with contents of Internal Ram  */
            for ( i = 0; i < size; i+=2 ) {
                    cocoCommand (cp, COCO_SETADDR, buf[i]);
                    cocoCommand (cp, cmd, buf[i+1] );
            }
    }
    /*************************************************************************
     ***                   c o c o R e a d E x t R a m                     ***
     *************************************************************************
     *
     *  Name:       cocoReadExtRam
     *
     *  Purpose:    Read Chameleon's External LUT into given buffer.
     *
     * Returns:     None.
     *
     *************************************************************************/
    static void
    cocoReadExtRam ( card_t *cp, uint_t *buf, int size )
    {
            register uint_t  cmd;
            register int     i;
            /*   fill External Ram with contents of the buffer  */
            for ( i = 0; i < size; i++ ) {
                    cocoCommand (cp, COCO_SETADDR, i);
                    cocoCommand (cp, COCO_READRAML, 0x0 );
                    buf[i] = cocoReadAmccFifo (cp);
            }
    }
    /*************************************************************************
     ***                   c o c o W r i t e E x t R a m                   ***
     *************************************************************************
     *
     *  Name:       cocoWriteExtRam
     *
     *  Purpose:    Write Chameleon's External LUT into given buffer.
     *
     * Returns:     None.
     *
     *************************************************************************/
    static void
    cocoWriteExtRam ( card_t *cp, uint_t *buf, int size )
    {
            register int     i;
            /*   fill buffer with contents of Internal Ram  */
            for ( i = 0; i < size; i+=2 ) {
                    cocoCommand (cp, COCO_SETADDR, buf[i]);
                    cocoCommand (cp, COCO_FILLRAML, buf[i+1] );
            }
    }
    /*************************************************************************
     ***                   c o c o C o n v e r t                           ***
     *************************************************************************
     *
     *  Name:       cocoConvert     
     *
     *  Purpose:    Converts a single value 
     *
     * Returns:     None
     *
     *************************************************************************/
    static void
    cocoConvert ( card_t *cp, uint_t val )
    {
            cocoCommand ( cp, COCO_CONVERT, val );
    }
    /*************************************************************************
     ***                   c o c o C o n v e r t T e s t                   ***
     *************************************************************************
     *
     *  Name:       cocoConvertTest
     *
     *  Purpose:    Converts a single value, read the converted value back
     *              and compare to known value.
     *
     * Returns:     None 
     *
     *************************************************************************/
    static void
    cocoConvertTest ( card_t *cp, coco_convert_t *cv )
    {
            register uint_t  res;
            
            /*  convert the pixle value   */
            cv->result = 0;         /*  assume success */
            cocoCommand ( cp, COCO_CONVERT, cv->in );
            /*  read the converted value and compare  */
            res = cocoReadAmccFifo (cp);
            if ( res != cv->out )
                    cv->result = 1;
    }
    /*************************************************************************
     ***                   c o c o D m a T o L u t s                       ***
     *************************************************************************
     *
     *  Name:       cocoDmaToLuts  
     *
     *  Purpose:    DMAs data in user's buffer to one of Internal LUTs or 
     *              the External LUTs (identified by cmd param). The DMA is
     *              done either in SINGLE or PROG (chained) mode depending
     *              on current set up (cp->dmatype).
     *
     * Returns:     0 = Success, or errno
     *
     *************************************************************************/
    static int
    cocoDmaToLuts( card_t *cp, coco_buf_t *cb, int cmd )
    {
            register caddr_t  kvaddr, amcc_adr;
            register coco_dmapage_t  *dmaPg;
            register int      len, page_no, s, i, dmatype, err, tot_bytes;
            register int      cache_bytes;
            register uint_t   dmabits, *ib, olddmacmd, dmacmd;
            register uint_t   *cache_line;
            alenlist_t        addrList2;
            alenlist_t        addrList;
            size_t            p_size;
            alenaddr_t        p_addr;
            iopaddr_t         p_dmaPg;
            amcc_adr = cp->amcc_adr;
            dmacmd   = cp->dmacmd;
            /* ========================================== 
             *      Scatter-Gather list preparation
             * ========================================== */
            /*  get the user's address and size   */
            len = cb->buf_size * sizeof(uint_t);
       
            /*  lock user pages into memory for DMA  */
            if ( cocoLockUser ( (caddr_t)cb->buf, len, B_WRITE) != 0 ) {
                    cmn_err (CE_WARN, "cocoDmaToLuts: Cannot lock user pages");
                    return (EFAULT);
            }
     
            /*
             *   write back and invalidate the data for mem->board
             *   adjust the address for current data cache size
             */
            cache_line = (uint_t *)cb->buf;
            cache_line = (uint_t *)( ((uint_t)cache_line / COCO_CACHE_SIZE) *
                                      COCO_CACHE_SIZE );
            cache_bytes =  len + ( (uint_t)cb->buf - (uint_t)cache_line);
            dki_dcache_wbinval ( (caddr_t)cache_line, cache_bytes );
            /*   create scatter-gather list of user's buffer  */
            addrList2 = uvaddr_to_alenlist( (alenlist_t)NULL, (caddr_t)cb->buf, 
                                            (size_t)len);
            if ( addrList2 == (alenlist_t)NULL ) {
                    cmn_err (CE_WARN, "cocoDmaToLuts: cannot create alenlist");
                    cocoUnlockUser ( (caddr_t)cb->buf, len, B_WRITE);
                    return (EIO);
            }
            addrList = pciio_dmatrans_list ( cp->vhdl, cp->dev_desc,
                                             /* addrList2, 0 ); */
                                              addrList2, PCIIO_DMAMAP_BIGEND); 
    #if 0
            page_no = alenlist_size ( addrList );   /*  total pages to DMA */
    #endif
            page_no = cocoAlenlistSize ( addrList );
            cp->page_no = page_no;
            alenlist_done (addrList2);
            /*  initialize our DMA event semaphore  */
            cp->dmabits = 0;
            olddmacmd = dmacmd; 
            dmatype = cp->dmatype;
            initnsema ( &cp->dmawait, 0, "coco" );
            cocoResetAmcc ( cp );
            cocoPrepAmcc ( cp );
            /* ===============================
             *    Set proper DMA flags
             * =============================== */
            dmacmd = DMAREG_FILL;
            /*  set proper LUT fill bit  */
            switch ( cmd ) {
                      case COCO_BLOCK_FILL_RAMIL: 
                            dmacmd |= COCO_FILLRAMIL; 
                            #ifdef DEBUG
                            printf (
                       "cocoDmaToLuts: Writing %d bytes to RAMIL, %d pages\n",
                                    len, page_no );
                            #endif
                             break;
                      case COCO_BLOCK_FILL_RAMIH:
                             dmacmd |= COCO_FILLRAMIH; 
                            #ifdef DEBUG
                            printf (
                       "cocoDmaToLuts: Writing %d bytes to RAMIH, %d pages\n",
                                    len, page_no );
                            #endif
                             break;
                      case COCO_BLOCK_FILL_RAMO:
                             dmacmd |= COCO_FILLRAMO; 
                            #ifdef DEBUG
                            printf (
                       "cocoDmaToLuts: Writing %d bytes to RAMIO, %d pages\n",
                                    len, page_no );
                            #endif
                             break;
                      case COCO_BLOCK_FILL_RAML:
                             dmacmd |= COCO_FILLRAML; 
                            #ifdef DEBUG
                            printf (
                        "cocoDmaToLuts: Writing %d bytes to RAML, %d pages\n",
                                    len, page_no );
                            #endif
                             break;
            }
            cp->dmacmd = dmacmd; 
            /* =======================
             *    Chained DMA
             * ======================= */
            if ( dmatype == DMA_PROG ) {
                    #ifdef DEBUG
                    printf ("cocoDmaToLuts: Chain Dma %d pages\n", page_no );
                    #endif
                    dmaPg = cocoMakeChain ( cp, addrList, page_no );
                    if ( dmaPg == (coco_dmapage_t *)NULL ) {
                            printf ("cocoDmaLut: Error creating chain list\n");
                            cocoUnlockUser ( (caddr_t)cb->buf, len, B_WRITE );
                            alenlist_done(addrList);
                            return(EIO);
                    }
                    tot_bytes = page_no * sizeof(coco_dmapage_t);
                    p_dmaPg = pciio_dmatrans_addr ( cp->vhdl, cp->dev_desc,
                                              kvtophys(dmaPg), tot_bytes,
                                              PCIIO_DMAMAP_BIGEND );
                    #ifdef DEBUG
                    cocoShowChain( "Lut Chain List", dmaPg );
                    #endif
                    /*   write back cache for the chain list */
                    dki_dcache_wbinval(dmaPg, tot_bytes );
                    s = COCO_LOCK();
                    err = 0;
                    /*  dma from memory -> board (selected lut buffer) */
                    cocoStartProgDma ( cp, p_dmaPg, len, B_WRITE );
                    cp->dmastat = DMA_LUT_WAIT;
                    #ifdef DEBUG
                    printf ("cocoDmaToLut: Waiting DMA Interrupt\n");
                    #endif
                    if ( SleepEvent(&cp->dmawait) != 0 ) {
                            #ifdef DEBUG
                            printf ("cocoDmaToLut: Interrupted\n");
                            #endif
                            err = EINTR;
                    }
                    else {
                            #ifdef DEBUG
                            printf ("cocoDmaToLuts: Woken up..all done\n");
                            #endif
                            
                            err = 0;
                    }
                    /*  we are done   */
                    cp->dmastat = DMA_IDLE;
                    cp->dmabits = 0;
                    cp->dmacmd  = olddmacmd;
                    COCO_UNLOCK(s);
                    cocoUnlockUser ( (caddr_t)cb->buf, len, B_WRITE );
                    alenlist_done(addrList);
                    kmem_free ( dmaPg, page_no * sizeof(coco_dmapage_t) );
                    return(err);
            }
            /* ===========================
             *      Single page DMA
             * =========================== */
             for ( i = 0; i < page_no; i++ ) {
                    /*  get a page to DMA  */
                    if ( alenlist_get(addrList, NULL, NBPP,
                        &p_addr, &p_size) != ALENLIST_SUCCESS ) {
                           cmn_err (CE_WARN, "cocoDma: Bad scatter-gather");
                           cocoUnlockUser ( (caddr_t)cb->buf, len, B_WRITE );
                           alenlist_done(addrList);
                           return(ENOMEM);
                    }
                    #ifdef DEBUG
                    printf (
          "cocoDmaToLuts: Loop ...DMA page %d of %d (%d bytes), p_addr = 0x%x\n",
                                           i+1, page_no, p_size, p_addr );
                    #endif
                    s = COCO_LOCK();
                    err = 0;
                    cp->dmastat = DMA_LUT_WAIT;
                    cocoStartSingleDma ( cp, p_addr, p_size, B_WRITE );
                    #ifdef DEBUG
                    printf ("cocoDmaToLut: Loop, waiting for Interrupt\n");  
                    #endif
                    if ( SleepEvent(&cp->dmawait) != 0 ) {
                            #ifdef DEBUG
                            printf ("cocoDmaToLut: Interrupted...\n");
                            #endif
                            err = EINTR;
                            COCO_UNLOCK(s);
                            break;
                    }
                    else {
                            COCO_UNLOCK(s);
                            #ifdef DEBUG
                            printf ("cocoDmaToLut: Loop ..Woken up\n");
                            #endif
                            
                    }
            }
            #ifdef DEBUG
            printf ("cocoDmaToLut: DMA is done\n");
            #endif
            /*  we are done   */
            cp->dmastat = DMA_IDLE;
            cp->dmabits = 0;
            cp->dmacmd  = olddmacmd;
            cocoUnlockUser ( (caddr_t)cb->buf, len, B_WRITE );
            alenlist_done(addrList);
            return(err);
                            
    }
    /*************************************************************************
     ***                   c o c o L o c k U s e r                         ***
     *************************************************************************
     *
     *  Name:       cocoLockUser
     *
     *  Purpose:    Given a user's address space pointer, it locks all user's
     *              pages in memory in preparation for DMA.    
     *
     * Returns:     0 = Success or errno 
     *
     *************************************************************************/
    static int 
    cocoLockUser ( caddr_t user_buf, int len,  int direction )
    {
            /*  lock pages in memory   */
            if ( userdma( (caddr_t)user_buf, len, direction) == 0 )
                    return (EFAULT);
            return (0);
    }
    /*************************************************************************
     ***                   c o c o U n l o c k U s e r                     ***
     *************************************************************************
     *
     *  Name:       cocoUnlockUser
     *
     *  Purpose:    Given a user's address, unlock all pages for that address 
     *
     * Returns:     None.
     *
     *************************************************************************/
    static void
    cocoUnlockUser ( caddr_t user_buf, int len, int direction )
    {
            undma ( user_buf, len, direction );
    }
    /*************************************************************************
     ***                   c o c o P r e p A m c c                         ***
     *************************************************************************
     *
     *  Name:       cocoPrepAmcc 
     *
     *  Purpose:    Prepares AMCC chip for DMA. 
     *
     * Returns:     None.
     *
     *************************************************************************/
    static void
    cocoPrepAmcc ( card_t *cp )
    {
            register caddr_t  adr_amcc;
            register uint_t   mcsr, intcsr; 
            register uint_t   tmp;
            adr_amcc = cp->amcc_adr;
            /* ===========================
             *      Prepare AMCC chip
             * =========================== */
            /*
             *  Prepare AMCC as follow:
             *      - Read Maibox registers to make sure they are empty
             *      - Set Amcc DMA count to 0 
             *      - Enable Amcc Read/Write interrupts
             */
            tmp = Inp32(adr_amcc+AMCC_OP_REG_IMB4);           /* Read In mbox  */
            tmp = Inp32(adr_amcc+AMCC_OP_REG_OMB4);           /* read Out mbox */
            Out32(adr_amcc+AMCC_OP_REG_MRTC, 0 );
            Out32(adr_amcc+AMCC_OP_REG_MWTC, 0 );
            Out32(adr_amcc+AMCC_OP_REG_INTCSR, AMCC_INTCSR_MASK );
    }
    /*************************************************************************
     ***                   c o c o R e s e t A m c c                       ***
     *************************************************************************
     *
     *  Name:       cocoResetAmcc
     *
     *  Purpose:    Resets Addon and Amcc Fifos
     *
     * Returns:     None.
     *
     *************************************************************************/
    static void
    cocoResetAmcc ( card_t *cp )
    {
            register caddr_t  adr_amcc;
            adr_amcc = cp->amcc_adr;
            /*  Reset Amcc Fifos and Addon interface     */ 
            Out32(adr_amcc+AMCC_OP_REG_MCSR, AMCC_RST_ADDON );
            Out32(adr_amcc+AMCC_OP_REG_MCSR, AMCC_RST_FIFOS ); 
    }
    /*************************************************************************
     ***                   c o c o S t a r t P r o g D m a                 ***
     *************************************************************************
     *
     *  Name:       cocoStartProgDma 
     *
     *  Purpose:    Programs the board for Chained DMA and starts the Dma 
     *
     * Returns:     None.
     *
     *************************************************************************/
    static void
    cocoStartProgDma ( card_t *cp, iopaddr_t p_dmaPg, int tot_bytes, int rw )
    {
            register caddr_t  adr_cfg, adr_norm, adr_amcc;
            register uint_t   adr_bits, enable_bits, dmacfg, dmacmd, dmabits;
            adr_cfg  = cp->conf_adr;
            adr_norm = cp->norm_adr;
            adr_amcc = cp->amcc_adr;
            dmacfg   = cp->dmacfg;
            dmacmd   = cp->dmacmd;
            dmabits  = cp->dmabits; 
            cp->dmasize = tot_bytes;
            #ifdef DEBUG
            cocoReport ( cp, "before cocoStartProgDma");
            #endif
            /*  clear eof marks  */
            Out32(adr_cfg, dmacfg ); 
            /* ======================
             *  board -> memory 
             * ====================== */ 
            if ( rw == B_READ ) {
                    /*  set Amcc counters  */
                    Out32(adr_amcc+AMCC_OP_REG_MWTC, tot_bytes ); 
                    Out32(adr_amcc+AMCC_OP_REG_MRTC, 0 ); 
                    adr_bits    = DMAREG_RAMPRWR;
                    enable_bits = DMAREG_INTPWEN | DMAREG_WEN;
                    dmabits |= DMAREG_PWEN;
            }
            /* ======================
             *  memory -> board  
             * ====================== */
            else {
                    /*  set Amcc counters  */
                    Out32(adr_amcc+AMCC_OP_REG_MRTC, tot_bytes ); 
                    Out32(adr_amcc+AMCC_OP_REG_MWTC, 0 ); 
                    adr_bits    = DMAREG_RAMPRRD;
                    enable_bits = DMAREG_INTPREN | DMAREG_REN;
                    dmabits |= DMAREG_PREN;
            }
            /*  enable chaining  */
            dmacfg |= dmabits;
            Out32(adr_cfg, dmacfg );  
            /*  set address of chained list  */
            Out32(adr_cfg, dmacfg | DMAREG_RAM | adr_bits );
            Out32(adr_norm, p_dmaPg);
            
            /*  start the DMA   */
            dmabits |= enable_bits | dmacmd;
            cp->dmabits = dmabits; 
            #ifdef DEBUG
            cocoReport ( cp, "after cocoStartProgDma");
            #endif
            pciio_flush_buffers ( cp->vhdl );
            microtime ( &cp->start_time );
            Out32(adr_cfg, dmacfg | dmabits ); 
    }
    /*************************************************************************
     ***                   c o c o S t a r t S i n g l e D m a             ***
     *************************************************************************
     *
     *  Name:       cocoStartSingleDma
     *
     *  Purpose:    Programs the board for Single page DMA (read or write) 
     *
     * Returns:     None.
     *
     *************************************************************************/
    static void
    cocoStartSingleDma ( card_t *cp, alenaddr_t p_addr, size_t p_size, int rw )
    {
            register caddr_t  adr_cfg, adr_norm, adr_amcc;
            register uint_t   adr_bits, enable_bits, dmacfg, dmacmd, dmabits, temp;
            register int      tot_words;
            adr_cfg  = cp->conf_adr;
            adr_norm = cp->norm_adr;
            adr_amcc = cp->amcc_adr;
            dmacfg   = cp->dmacfg;
            dmacmd   = cp->dmacmd;
            dmabits  = cp->dmabits; 
            enable_bits = 0;
            cp->dmasize = p_size;
            #ifdef DEBUG
            printf ("cocoStartSingleDma: Dma'ing %d bytes %s, p_addr = 0x%x\n",
                    p_size, rw==B_READ ? "from board":"to board", p_addr );
            #endif
            Out32(adr_cfg, dmacfg );  /*  clear eof marks */
            /*
             *  Select read/write count in DMA controller
             *  and set the DMA size in 32-bit values
             */
            tot_words = (int)p_size/sizeof(uint_t);
            if ( rw == B_READ ) {   /*  board -> memory  */
                 Out32(adr_amcc+AMCC_OP_REG_MWTC, (uint_t)p_size );
                 Out32(adr_amcc+AMCC_OP_REG_MRTC, 0xffffffff ); 
                 /* Out32(adr_amcc+AMCC_OP_REG_MRTC, (uint_t)p_size );  */
                 dmabits |= DMAREG_WEN;
                 Out32(adr_cfg, dmacfg | DMAREG_WCNT);
            }
            else {
                 Out32(adr_amcc+AMCC_OP_REG_MRTC, (uint_t)p_size ); 
                 Out32(adr_amcc+AMCC_OP_REG_MWTC, 0xffffffff ); 
                 /* Out32(adr_amcc+AMCC_OP_REG_MWTC, (uint_t)p_size ); */
                 dmabits |= DMAREG_REN;
                 Out32(adr_cfg, dmacfg | DMAREG_RCNT);
            }
            /*  set transfer counts in words - -1 since counts down to 0xffff */
            tot_words--; 
            Out32(adr_norm, (uint_t)tot_words); 
            /* select Read/Write Address and set the DMA address  */
            if ( rw == B_READ ) { 
                  Out32(adr_cfg, dmacfg | DMAREG_RAM | DMAREG_RAMWADR); /* b->m */
            }
            else {
                  Out32(adr_cfg, dmacfg | DMAREG_RAM | DMAREG_RAMRADR); /* m->b */
            }
            Out32(adr_norm, (uint_t)p_addr );
            /*   enable the right interrupt  */
            if ( rw == B_READ ) 
                    dmabits |= DMAREG_INTWEN;   /*  board  -> memory  */
            else    dmabits |= DMAREG_INTREN;   /*  memory -> board   */
            dmabits |= cp->dmacmd;
            cp->dmabits = dmabits;
            #ifdef DEBUG
            cocoReport ( cp, "cocoStartSingleDma exit");
            #endif
            pciio_flush_buffers ( cp->vhdl ); 
            microtime ( &cp->start_time);
            Out32(adr_cfg, dmacfg | dmabits ); 
    }
    /*************************************************************************
     ***                   c o c o M a k e C h a i n                       ***
     *************************************************************************
     *
     *  Name:       cocoMakeCHain
     *
     *  Purpose:    given an alenlist, creates chained list for Prog DMA. 
     *
     *  Returns:    NULL for error or address of chained list 
     *
     *************************************************************************/
    static coco_dmapage_t * 
    cocoMakeChain ( card_t *cp, alenlist_t addrList, int page_no )
    {
            register coco_dmapage_t * dmaPg;
            register int              i, tot_words;
            size_t            p_size;
            alenaddr_t        p_addr;
            iopaddr_t         pa;   
                    
            dmaPg = (coco_dmapage_t *)kmem_alloc (page_no * sizeof(coco_dmapage_t), 
                                      KM_NOSLEEP | KM_PHYSCONTIG | KM_CACHEALIGN);
            if ( dmaPg == (coco_dmapage_t *)NULL ) {
                        printf ("cocoMakeChain: Not enough mem for chained list\n");
                        return( (coco_dmapage_t *)NULL);
            }
            /*  fill the chained list with address-size values */
            for ( i = 0; i < page_no; i++ ) {
                    if ( alenlist_get(addrList, NULL, NBPP,
                         &p_addr, &p_size) != ALENLIST_SUCCESS ) {
                         cmn_err (CE_WARN, "cocoMakeChain: Bad alenlist\n"); 
                         return( (coco_dmapage_t *)NULL);
                    }
                    pa  = pciio_dmatrans_addr ( cp->vhdl, cp->dev_desc,
                                              kvtophys(&dmaPg[i+1]),
                                              sizeof(coco_dmapage_t),
                                              PCIIO_DMAMAP_BIGEND ); 
                    dmaPg[i].nextaddr = (paddr_t)pa; 
                    dmaPg[i].addr = (paddr_t)p_addr;
                    tot_words = (int)p_size/sizeof(uint_t);
                    dmaPg[i].size = tot_words - 1; 
            }
            dmaPg[i-1].size |= END_OF_CHAIN;
            return ( dmaPg ); 
    }
    /*************************************************************************
     ***                   c o c o M a k e C h a i n R W                   ***
     *************************************************************************
     *
     *  Name:       cocoMakeCHainRW
     *
     *  Purpose:    Creates chain list for simultaneous read and write
     *              but makes sure we read and write the same amount of bytes
     *              in each entry 
     *
     *  Returns:    0 = Success, or errno 
     *
     *************************************************************************/
    static int
    cocoMakeChainRW ( card_t *cp, 
                      coco_dmapage_t **w_dmaPg, 
                      coco_dmapage_t **r_dmaPg )
    {
            register coco_dmapage_t *wp, *rp; 
            register alenaddr_t wp_resadr, rp_resadr;
            register iopaddr_t  pa;
            register size_t     wp_left, rp_left;   
            register int        i, tot_rchain, tot_wchain, w_page, r_page;
            register int        tot_bytes, tot_words;
            size_t              wp_size, rp_size;
            alenaddr_t          wp_addr, rp_addr;
            w_page = cp->w_page_no;
            r_page = cp->r_page_no;
            tot_rchain = r_page * CHAIN_FACTOR;
            tot_wchain = w_page * CHAIN_FACTOR;
            wp_resadr = 0;
            rp_resadr = 0;
            wp_left   = 0;
            rp_left   = 0;
            *w_dmaPg = (coco_dmapage_t *)NULL; 
            *r_dmaPg = (coco_dmapage_t *)NULL;
            /*  allocate chain list for Write    */
            wp = (coco_dmapage_t *)kmem_alloc (tot_wchain * sizeof(coco_dmapage_t),
                                   KM_NOSLEEP | KM_PHYSCONTIG | KM_CACHEALIGN);
            if ( wp  == (coco_dmapage_t *)NULL ) {
                        cmn_err (CE_WARN, "cocoMakeChainRW: Not enough memory");
                        return (ENOMEM);
            }
            /*  allocate chain list for Read   */
            rp = (coco_dmapage_t *)kmem_alloc (tot_rchain * sizeof(coco_dmapage_t),
                                   KM_NOSLEEP | KM_PHYSCONTIG | KM_CACHEALIGN);
            if ( rp  == (coco_dmapage_t *)NULL ) {
                        kmem_free (wp, tot_wchain * sizeof(coco_dmapage_t) );
                        cmn_err (CE_WARN, "cocoMakeChainRW: Not enough memory");
                        return (ENOMEM);
            }
            /*  make sure we are at the top of the list  */
            alenlist_cursor_init ( cp->r_addrList, NULL, NULL );
            alenlist_cursor_init ( cp->w_addrList, NULL, NULL );
            
            #ifdef DEBUG
            printf ("cocoMakeChainRW: ----- Started -----\n");
            cocoShowAlenlist ( "Write Alenlist", cp->w_addrList );
            cocoShowAlenlist ( "Read  Alenlist", cp->r_addrList );
            #endif
            for ( i = 0;; i++ ) {   
                    /* ================================
                     *      Write address and count
                     * ================================ */
                    /* if no Write bytes left, get a new page  */
                    if ( wp_resadr == (alenaddr_t)NULL ) {
                        /*  we must have data for write */
                        if ( w_page <= 0 ) {
                            cmn_err (CE_WARN,
               "cocoMakeChainRW: Premature end of Write, w_page = %d, r_page = %d",
                                   w_page, r_page );
                             #ifdef DEBUG4
                             printf ("%d Write pages, %d Read pages\n",
                                    tot_wchain / CHAIN_FACTOR, 
                                    tot_rchain / CHAIN_FACTOR );
                             #ifdef DEBUG3
                             rp[i-1].size |= END_OF_CHAIN;
                             wp[i-1].size |= END_OF_CHAIN;
                             cocoShowChain ("Write Chain", wp );
                             cocoShowChain ("Read Chain", rp );
                             #endif
                             cocoShowAlenlist ( "Write Alenlist", cp->w_addrList );
                             cocoShowAlenlist ( "Read Alenlist", cp->r_addrList );
                             #endif
                             kmem_free (wp, tot_wchain * sizeof(coco_dmapage_t) );
                             kmem_free (rp, tot_rchain * sizeof(coco_dmapage_t) );
                             return(EIO);
                        }
                        if ( alenlist_get(cp->w_addrList, NULL, NBPP,
                             &wp_addr, &wp_size) != ALENLIST_SUCCESS ) {
                             cmn_err (CE_WARN, 
                             "cocoMakeChainRW: Bad Write alenlist\n");
                             kmem_free (wp, tot_wchain * sizeof(coco_dmapage_t) ); 
                             kmem_free (rp, tot_rchain * sizeof(coco_dmapage_t) ); 
                             return(EIO);
                        }
                        w_page--;
                    }
                    /*  some old Write bytes left - use those   */
                    else {
                            #ifdef DEBUG
                            printf ("using %d old Write bytes left at 0x%x\n",
                            wp_left, wp_resadr );
                            #endif
                            wp_addr = wp_resadr;
                            wp_size = wp_left;
                    }
                    /* ================================
                     *      Read address and count
                     * ================================ */
                    /* if no Read bytes left, get a new page  */
                    if ( rp_resadr == (alenaddr_t)NULL ) {
                        /*  we must have data for read */
                        if ( r_page <= 0 ) {
                            cmn_err (CE_WARN,  
                 "cocoMakeChainRW: Premature end of Read, r_page = %d, w_page = %d",
                                  r_page, w_page );
                             #ifdef DEBUG4
                             printf ("%d Write pages, %d Read pages\n",
                                    tot_wchain / CHAIN_FACTOR, 
                                    tot_rchain / CHAIN_FACTOR );
                             #ifdef DEBUG3
                             wp[i-1].size |= END_OF_CHAIN;
                             rp[i-1].size |= END_OF_CHAIN;
                             cocoShowChain ("Read Chain", rp );
                             cocoShowChain ("Write Chain", wp );
                             #endif
                             cocoShowAlenlist ( "Write Alenlist", cp->w_addrList );
                             cocoShowAlenlist ( "Read Alenlist", cp->r_addrList );
                             #endif
                             kmem_free (wp, tot_wchain * sizeof(coco_dmapage_t) );
                             kmem_free (rp, tot_rchain * sizeof(coco_dmapage_t) );
                             return(EIO);
                        }
                        if ( alenlist_get(cp->r_addrList, NULL, NBPP,
                             &rp_addr, &rp_size) != ALENLIST_SUCCESS ) {
                             cmn_err (CE_WARN, 
                             "cocoMakeChainRW: Bad Read alenlist\n");
                             kmem_free (wp, tot_wchain * sizeof(coco_dmapage_t) );
                             kmem_free (rp, tot_rchain * sizeof(coco_dmapage_t) );
                             return(EIO);
                        }
                        r_page--;
                    }
                    /*  some old Read bytes left - use those   */
                    else {
                            #ifdef DEBUG
                            printf ("using %d old Read bytes left at 0x%x\n",
                            rp_left, rp_resadr );
                            #endif
                            rp_addr = rp_resadr;
                            rp_size = rp_left;
                    }
                    /* ====================================
                     *      Adjust Read and Write counts
                     * ==================================== */
                    wp_resadr = 0;
                    rp_resadr = 0;
                    wp_left   = 0;
                    rp_left   = 0;
                    /*  Writing and Reading the same amount ? no adjustment */ 
                    if ( wp_size == rp_size ) {
                            #ifdef DEBUG
                            printf (
          "Same byte count (%d) at read 0x%x and write 0x%x ..no adjustment\n",
                                     wp_size, rp_addr, wp_addr ); 
                            #endif
                            tot_bytes = wp_size;
                    }
                    /*  Writing more than reading ? Adjust Write  */
                    if ( wp_size > rp_size ) {
                            tot_bytes = rp_size;
                            wp_resadr = (alenaddr_t)((int)wp_addr + tot_bytes);
                            wp_left   = wp_size - tot_bytes;
                            #ifdef DEBUG
                            printf ("Write adjusted ..%d bytes left at 0x%x\n",
                                    wp_left, wp_resadr );
                            #endif
                    }
            
                    /*  Reading more than write ? Adjust Read   */
                    if ( rp_size > wp_size ) {
                            tot_bytes = wp_size;
                            rp_resadr = (alenaddr_t)((int)rp_addr + tot_bytes);
                            rp_left   = rp_size - tot_bytes;        
                            #ifdef DEBUG
                            printf ("Read adjusted ..%d bytes left at 0x%x\n",
                                    rp_left, rp_resadr );
                            #endif
                    }       
                    /* =======================================
                     *      Make Read and Write Chain Entries 
                     * ======================================= */
                    tot_words = (int)tot_bytes/sizeof(uint_t);
                    tot_words--;
                    /*  Make Write Chain entry   */
                    pa  = pciio_dmatrans_addr ( cp->vhdl, cp->dev_desc,
                                              kvtophys(&wp[i+1]),
                                              sizeof(coco_dmapage_t),
                                              PCIIO_DMAMAP_BIGEND );
                    wp[i].nextaddr = (paddr_t)pa;
                    wp[i].addr = (paddr_t)wp_addr;
                    wp[i].size = tot_words;
                    /*  Make Write Chain entry   */
                    pa  = pciio_dmatrans_addr ( cp->vhdl, cp->dev_desc,
                                              kvtophys(&rp[i+1]),
                                              sizeof(coco_dmapage_t),
                                              PCIIO_DMAMAP_BIGEND );
                    rp[i].nextaddr = (paddr_t)pa;
                    rp[i].addr = (paddr_t)rp_addr;
                    rp[i].size = tot_words;
                    /*  end of Write chain list ?   */
                    if ( (w_page <=0) && (wp_resadr == (alenaddr_t)NULL) )
                            wp[i].size |= END_OF_CHAIN;
                    /*  end of Read chain list ?   */
                    if ( (r_page <=0) && (rp_resadr == (alenaddr_t)NULL) )
                            rp[i].size |= END_OF_CHAIN;
                    /*  end of loop ?  */
                    if ( (rp[i].size & END_OF_CHAIN) && 
                         (wp[i].size & END_OF_CHAIN) )
                            break;
                    /*  ran out of memory ?   */
                    if ( (i >= tot_rchain) || (i >= tot_wchain) ) {
                            cmn_err (CE_WARN,
                            "cocoMakeChainRW: Ran out of Chain entries");
                             kmem_free (wp, tot_wchain * sizeof(coco_dmapage_t) );
                             kmem_free (rp, tot_rchain * sizeof(coco_dmapage_t) );
                             return(EIO);
                     }
     
            }  /***  for ( i = 0;; i++ )  ***/
            *w_dmaPg = wp;
            *r_dmaPg = rp;
            return(0);
    }
            
    /*************************************************************************
     ***                   c o c o T i m e O u t                           ***
     *************************************************************************
     *
     *  Name:       cocoTimeOut   
     *
     *  Purpose:    We timedout waiting for a read/write interrupt. 
     *
     *  Returns:    None.
     *
     *************************************************************************/
    static void
    cocoTimeOut ( card_t *cp )
    {
            register uint_t  intcsr;
            cmn_err (CE_NOTE, "cocoTimeOut: Read/Write Timed out");
            alenlist_done ( cp->addrList );
            cp->addrList = 0;
            #ifdef DEBUG
            cocoDumpAmcc(cp);
            #endif
            bioerror ( cp->bp, ETIME);
            biodone (cp->bp);
    }
    /*************************************************************************
     ***                   c o c o T i m e O u t 2                         ***
     *************************************************************************
     *
     *  Name:       cocoTimeOut2
     *
     *  Purpose:    We timedout waiting for a simultaneous read/write intr.
     *
     *  Returns:    None.
     *
     *************************************************************************/
    static void
    cocoTimeOut2 ( card_t *cp )
    {
            cmn_err (CE_NOTE, "cocoTimeOut2: Sim. Read/Write Timed out");
            #ifdef DEBUG
            cocoDumpAmcc(cp);
            #endif
            cp->iostat = IO_TIME;
            WakeEvent(&cp->dmawait);
    }
    /*
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    ~~~~~~~~~~                                                      ~~~~~~~~~~~~~
    ~~~~~~~~~~         D e b u g i n g   R o u t i n e s            ~~~~~~~~~~~~~~
    ~~~~~~~~~~                                                      ~~~~~~~~~~~~~
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    /*************************************************************************
     ***                   c o c o I o c t l S t r                         ***
     *************************************************************************
     *
     *  Name:       cocoIoctlStr 
     *
     *  Purpose:    return string version of an Ioctl command 
     *
     *  Returns:    pointer to string  
     *
     *************************************************************************/
    static char *
    cocoIoctlStr ( int cmd )
    {
        switch ( cmd ) {
            case COCO_COMMAND:  return ("Coco_Command");
            case COCO_RAW_READ_FIFO:  return ("Raw_Read_Fifo");
            case COCO_RAW_WRITE_FIFO: return ("Raw_Write_Fifo");
            case COCO_RAW_READB_FIFO: return ("Raw_ReadB_Fifo");
            case COCO_RAW_WRITEB_FIFO: return ("Raw_WriteB_Fifo");
            case COCO_FIFO_TEST:       return ("Fifo_Test");
            case COCO_SET_MODE:        return ("Set_Mode");
            case COCO_READ_MODE:       return ("Read_Mode");
            case COCO_RAW_READ_DMA:    return ("Raw_Read_Dma");
            case COCO_RAW_WRITE_DMA:   return ("Raw_Write_Dma");
            case COCO_READ_ADDR:       return ("Read_Addr");
            case COCO_SET_ADDR:        return ("Set_Addr");
            case COCO_DMAREGS_TEST:    return ("DmaRegs_Test");
            case COCO_INTRAM_TEST:     return ("IntRam_Test");
            case COCO_EXTRAM_TEST:     return ("ExtRam_Test");
            case COCO_READ_RAMIL:      return ("Read_RamIL");
            case COCO_READ_RAMIH:      return ("Read_RamIH");
            case COCO_READ_RAMO:       return ("Read_RamO");
            case COCO_READ_RAML:       return ("Read_RamL");
            case COCO_FILL_RAMIL:      return ("Fill_RamIL"); 
            case COCO_FILL_RAMIH:      return ("Fill_RamIH");
            case COCO_FILL_RAMO:       return ("Fill_RamO");
            case COCO_FILL_RAML:       return ("Fill_RamL");
            case COCO_CONVERT_PIXLE:   return ("Convert_Pixle");
            case COCO_CONVERT_TEST:    return ("Convert_Test");
            case COCO_SET_SINGLE_DMA:  return ("Set_Single_Dma");
            case COCO_SET_PROG_DMA:    return ("Set_Prog_Dma");
            case COCO_BLOCK_FILL_RAMIL:  return ("Block_Fill_RamIL");
            case COCO_BLOCK_FILL_RAMIH:  return ("Block_Fill_RamIH");
            case COCO_BLOCK_FILL_RAML:   return ("Block_Fill_RamL");
            case COCO_BLOCK_FILL_RAMO:   return ("Block_Fill_RamO");
            case COCO_SETCMD_TRANSP:     return ("SetCmd_Transp");
            case COCO_SETCMD_CONVERT:    return ("SetCmd_Convert");
            case COCO_RESET:             return ("Reset");
            case COCO_RW_BUF:            return ("RW_Buff");
            case COCO_ISPCI:             return ("Is_PCI");
            defaults:                    return ("Unknown");
        }
    }
    /*************************************************************************
     ***                   c o c o R e p o r t                             ***
     *************************************************************************
     *
     *  Name:       cocoReport
     *
     *  Purpose:    Prints out the bit setting in dmacfg and other status 
     *
     *  Returns:     None.
     *
     *************************************************************************/
    static void
    cocoReport ( card_t *cp, char *s )
    {
            register uint_t   dmacfg, dmabits, dmacmd, ram;
            register int      dmastat, dmatype;
            dmacmd  = cp->dmacmd;
            dmabits = cp->dmabits;
            dmastat = cp->dmastat;
            dmatype = cp->dmatype;
            printf ("%s: ", s ); 
            printf ("dmabits: " );
            if ( dmabits & DMAREG_WEN ) printf ("|Wen");
            if ( dmabits & DMAREG_REN ) printf ("|Ren");
            if ( dmabits & DMAREG_PREN) printf ("|Pren");
            if ( dmabits & DMAREG_PWEN) printf ("|Pwen");
            if ( dmabits & DMAREG_FILL) printf ("|Reg_Fill");
            if ( dmabits & DMAREG_INTWEN) printf ("|IntWen");
            if ( dmabits & DMAREG_INTREN) printf ("|IntRen");
            if ( dmabits & DMAREG_INTPWEN ) printf ("|IntPWen");
            if ( dmabits & DMAREG_INTPREN ) printf ("|IntPRen");
            ram = dmabits & 0x0f000000; 
            if ( ram == COCO_FILLRAML)  printf ("|Fill_RamL");
            if ( ram == COCO_FILLRAMIL) printf ("|Fill_RamIL");
            if ( ram == COCO_FILLRAMIH) printf ("|Fill_RamIH");
            if ( ram == COCO_FILLRAMO)  printf ("|Fill_RamO");
            if ( ram == COCO_TRANSP)    printf ("|Transp");
            if ( ram == COCO_CONVERT)   printf ("|Convrt");
            printf ("  dmastat: ", dmastat);
            if ( dmastat == DMA_IDLE )     printf ("Idle");
            if ( dmastat == DMA_LUT_WAIT ) printf ("Dma_Lut_Wait");
            if ( dmastat == DMA_READ_WAIT ) printf ("Dma_Read_Wait");
            if ( dmastat == DMA_WRITE_WAIT ) printf ("Dma_Write_Wait");
            if ( dmastat == DMA_RW_WAIT )    printf ("Dma_RW_Wait");
            printf ("\n");
    }
    /*************************************************************************
     ***                   c o c o S h o w C h a i n                       ***
     *************************************************************************
     *
     *  Name:       cocoShowChain
     *
     *  Purpose:    Displays contents of a chain list 
     *
     *  Returns:    None. 
     *
     *************************************************************************/
    static void 
    cocoShowChain( char *title, coco_dmapage_t *dmaPg )
    {
            register int  i, tot_bytes, tot_words;
            register long all_bytes;
            printf ("--- %s ---\n", title );
            all_bytes = 0;
            for (i = 0;; i++ ) {
                    tot_words = dmaPg[i].size;
                    tot_bytes = ( (tot_words &~END_OF_CHAIN) +1) * sizeof(uint_t);
                    all_bytes += tot_bytes;
                    printf ("addr = 0x%x, size = %d  next addr = 0x%x %s\n",
                    dmaPg[i].addr, tot_bytes,  dmaPg[i].nextaddr,
                    tot_words & END_OF_CHAIN ? "[ End ]":" " );
                    if ( tot_words & END_OF_CHAIN ) 
                            break;
            }
            printf ("--- Total of %d entries, %d bytes ---\n\n", i, all_bytes );
    }
    /*************************************************************************
     ***                   c o c o D u m p A m c c                         ***
     *************************************************************************
     *
     *  Name:       cocoDumpAmcc
     *
     *  Purpose:    Dumps Amcc registers for debugging purpose. 
     *
     *  Returns:    None.
     *
     *************************************************************************/
    static void
    cocoDumpAmcc ( card_t *cp )
    {
            register caddr_t  amcc_adr, norm_adr, cfg_adr;
            register uint_t   dmacfg, xil_stat, war, wcnt, rar, rcnt, mbef;
            amcc_adr = cp->amcc_adr;
            norm_adr = cp->norm_adr;
            cfg_adr  = cp->conf_adr;
            dmacfg   = cp->dmacfg;
            /*  disable any Dma and read in Xilinx status */
            Out32(cfg_adr, dmacfg | DMAREG_STAT );
            xil_stat = Inp32(norm_adr);
            war     = Inp32(amcc_adr+AMCC_OP_REG_MWAR); 
            wcnt    = Inp32(amcc_adr+AMCC_OP_REG_MWTC); 
            rar     = Inp32(amcc_adr+AMCC_OP_REG_MRAR);
            rcnt    = Inp32(amcc_adr+AMCC_OP_REG_MRTC);
            mbef    = Inp32(amcc_adr+AMCC_OP_REG_MBEF);
            
            wcnt &= 0x01ffffff;
            rcnt &= 0x01ffffff;
            printf ("***  cocoDumpAmcc ***\n");
            printf ("WAR  = 0x%x     WTC = %d [ 0x%x ]\n", war, wcnt, wcnt );
            printf ("RAR  = 0x%x     RTC = %d [ 0x%x ]\n", rar, rcnt, rcnt );
            printf ("MBEF = 0x%x  Xilinx = 0x%x\n", mbef, xil_stat );
    }
    /*************************************************************************
     ***                   c o c o S h o w A l e n l i s t                 ***
     *************************************************************************
     *
     *  Name:       cocoShowAlenlist
     *
     *  Purpose:    Displayes the contents of a given Alenlist 
     *
     *  Returns:    None.
     *
     *************************************************************************/
    static void
    cocoShowAlenlist ( caddr_t title, alenlist_t  al )
    {
            size_t              size; 
            long                tot_bytes;
            alenaddr_t          addr;
            register int        count, i;
            /*  reset the cursor for the alenlist  */
            alenlist_cursor_init ( al, NULL, NULL );
            printf ("cocoShowAlenlist: --- %s ---\n", title );
            tot_bytes = 0;
            count     = 0;
            for ( ;; ) {
                    if ( alenlist_get(al, NULL, NBPP, &addr, &size) != 
                                      ALENLIST_SUCCESS ) {
                            break;
                    }
                    printf ("addr = 0x%x,  size = %d ...[%d]\n",addr, size, count );
                    tot_bytes += size;
                    count++;
            }
            printf ("--- Total of %d bytes [ %d entries ]---\n\n", 
                    tot_bytes, count );
            /*  reset the cursor now   */
            alenlist_cursor_init ( al, NULL, NULL );
    }
    /*************************************************************************
     ***                   c o c o A l e n l i s t S i z e                 ***
     *************************************************************************
     *
     *  Name:       cocoAlenlistSize
     *
     *  Purpose:    Returns number of pairs in a given alenlist. 
     *
     *  Returns:    Number of address/size entries 
     *
     *************************************************************************/
    static int 
    cocoAlenlistSize ( alenlist_t  al )
    {
            register int        count;
            size_t              size;
            alenaddr_t          addr;
            alenlist_cursor_init ( al, NULL, NULL );
            count = 0;
            for ( ;; ) {
                    if ( alenlist_get(al, NULL, NBPP, &addr, &size) !=
                                      ALENLIST_SUCCESS ) {
                            break;
                    }
                    count++;
            }
            alenlist_cursor_init ( al, NULL, NULL );
            return (count);
    }
    #ifdef DEBUGTIME
    /*************************************************************************
     ***                   c o c o R e p o r t T i m e                     ***
     *************************************************************************
     *
     *  Name:       cocoReportTime
     *
     *  Purpose:    Displays start, intr and end time of Dma 
     *
     *  Returns:    None.
     *
     *************************************************************************/
    static void
    cocoReportTime ( caddr_t title, card_t *cp, int final ) 
    {
            register long   s_sec, s_milsec, s_micsec;
            register long   i_sec, i_milsec, i_micsec;
            register long   e_sec, e_milsec, e_micsec;
            register struct timeval  *tv;
            struct timeval  dtv;
            if ( final == 0 ) {
                 tv = &cp->start_time;
                 s_sec    = tv->tv_sec & 0x000000ff;
                 s_milsec = tv->tv_usec / 1000;
                 s_micsec = tv->tv_usec % 1000;
                 tv = &cp->intr_time;
                 i_sec    = tv->tv_sec & 0x000000ff;
                 i_milsec = tv->tv_usec / 1000;
                 i_micsec = tv->tv_usec % 1000;
                 cocoDiffTime( &cp->start_time, &cp->intr_time, &dtv );
                 tv = &dtv;
                 e_sec    = tv->tv_sec & 0x000000ff;
                 e_milsec = tv->tv_usec / 1000;
                 e_micsec = tv->tv_usec % 1000;
                 printf (
    "%s: %s, %d bytes, ..Start: %d.%d.%d, ..Intr: %d.%d.%d, ..Diff: %d.%d.%d\n",
                    title, cp->dmatype == DMA_PROG ? "Chain":"Single", cp->dmasize,
                    s_sec, s_milsec, s_micsec,
                    i_sec, i_milsec, i_micsec,
                    e_sec, e_milsec, e_micsec );
                    
                    return;
            }
            tv = &cp->call_time;
            s_sec    = tv->tv_sec & 0x000000ff;
            s_milsec = tv->tv_usec / 1000;
            s_micsec = tv->tv_usec % 1000;
            tv = &cp->ret_time;
            i_sec    = tv->tv_sec & 0x000000ff;
            i_milsec = tv->tv_usec / 1000;
            i_micsec = tv->tv_usec % 1000;
            cocoDiffTime ( &cp->call_time, &cp->ret_time, &dtv );
            tv = &dtv;
            e_sec    = tv->tv_sec & 0x000000ff;
            e_milsec = tv->tv_usec / 1000;
            e_micsec = tv->tv_usec % 1000;
            printf ("%s: Call at %d.%d.%d, ...Ret at %d.%d.%d, ..Diff: %d.%d.%d\n",
                    title, 
                    s_sec, s_milsec, s_micsec,
                    i_sec, i_milsec, i_micsec,
                    e_sec, e_milsec, e_micsec );
    }
    /*************************************************************************
     ***                   c o c o D i f f T i m e                         ***
     *************************************************************************
     *
     *  Name:       cocoDiffTime   
     *
     *  Purpose:    Given two timeval struct, it calculates the difference 
     *
     *  Returns:    None.
     *
     *************************************************************************/
    static void
    cocoDiffTime ( struct timeval *st, struct timeval *et, struct timeval *dt )
    {
            register long   s_sec, s_milsec, s_micsec;
            register long   e_sec, e_milsec, e_micsec;
            register long   s_totmic, e_totmic, diff_mic;
            s_sec    = st->tv_sec & 0x000000ff;
            s_totmic = (s_sec * 1000000) + st->tv_usec; 
            e_sec    = et->tv_sec & 0x000000ff;
            e_totmic = (e_sec * 1000000) + et->tv_usec;
            diff_mic = e_totmic - s_totmic;
            dt->tv_sec  = diff_mic / 1000000;
            dt->tv_usec = diff_mic % 1000000;
    }
    #endif
    #define GOOD   (PG_M | PG_G | PG_SV | PG_VR)
    static void
    cocoDebug ( coco_rw_t *rw )
    {
            register int  tot_bytes, tot_wrong, i;
            register uint_t         *kvi;
            /* =========================================================
             *      Scatter-Gather list for Write buffer (mem -> board)
             * ========================================================= */
            tot_bytes = rw->buf_size * sizeof(uint_t);
            /*  lock user's pages in memory for DMA  */
            if ( cocoLockUser ( (caddr_t)rw->w_buf, tot_bytes, B_WRITE) != 0 ) {
                    cmn_err (CE_WARN, "cocoDebug: Cannot lock user's pages");
                    return;
            } 
            kvi  = (uint_t *)maputokv ( (caddr_t)rw->w_buf, tot_bytes,
                                        /* PG_UNCACHED | GOOD ); */
                                        pte_cachebits() | GOOD ); 
            /*  check memory before the dki_dcache   */
            if ( kvi ) {
                    tot_wrong = 0;
                    for ( i = 0; i < rw->buf_size; i++ ) {
                            if ( kvi[i] != 0x0 ) {
                                    if ( tot_wrong %100  == 0 ) {
                                            printf (
                        "cocoDebug: kvi[%d] = 0x%x at 0x%x kv and 0x%x user\n",
                                                    i, kvi[i], kvi+i, rw->w_buf+i );
                                    }
                                    tot_wrong++;
                            }
                    }
                    if ( tot_wrong )
                         printf (
              "cocoDebug BEFORE: --- %d pixles were wrong in w_buf (0x%x)\n\n",
                                  tot_wrong, rw->w_buf );
                    else printf ("cocoDebug BEFORE: Ok\n\n");
            }
            /*   write back and invalidate the data for mem->board */
            dki_dcache_wbinval ( (caddr_t)rw->w_buf , tot_bytes );
            /*  check after dki_dcache  */
            if ( kvi ) {
                    tot_wrong = 0;
                    for ( i = 0; i < rw->buf_size; i++ ) {
                            if ( kvi[i] != 0x0 ) {
                                    if ( tot_wrong %100  == 0 ) {
                                            printf (
                        "cocoDebug: kvi[%d] = 0x%x at 0x%x kv and 0x%x user\n",
                                                    i, kvi[i], kvi+i, rw->w_buf+i );
                                    }
                                    tot_wrong++;
                            }
                    }
                    if ( tot_wrong )
                         printf (
                "cocoDebug AFTER: --- %d pixles were wrong in w_buf (0x%x)\n\n", 
                                  tot_wrong, rw->w_buf );
                    else printf ("cocoDebug AFTER: Ok\n\n");
            }
            unmaputokv ( (caddr_t)kvi, tot_bytes );
            cocoUnlockUser ( (caddr_t)rw->w_buf, tot_bytes, B_WRITE );
    }