./118notes-2.60000644000076400007640000005172610223360163012466 0ustar wdwwdw00000000000000Linux 2.6.x driver notes for Tahoma Technology's PCI DR11-W interface Model 10118. Tahoma Technology (formerly IKON Corporation) 107 2nd Avenue, Seattle, WA, USA 98109 206.728.6465 http://www.tahomatech.com tahoma@tahomatech.com 4 February, 2004 This document contains a brief set of notes on the calling sequences used with the driver for the 10118 DR11 emulator from Tahoma Technology (formerly Ikon Corporation). It will describe the general purpose of each ioctl() call, and attempt to give suggestions as to how they might be used. It will also describe the compile-time configuration defaults and choices available. Since the DR11 is a very general-purpose board, it is impossible to give examples of all possible uses of the board and driver. The user is referred to the hardware manual provided with the 10118. It contains register descriptions, and some discussion of how DMA transfers and interrupts are used. NOTE: Many compile-time defaults can be overridden by the insmod loader at driver install time. This is most easily accomplished by editing the idr_install-2.6 script before installing the driver. The driver's header files: idr_io.h, contains the #define statements for the arguments to be used in ioctl() calls to the 10118 driver. It must be included in the code of the program calling the driver. (#include "idr_io.h") This will also cause the idr_reg.h file to be included. idr_reg contains the register bit definitions for the pci DR11 which may be used as arguments in some of the ioctls. idr_io.h also includes register bit definitions that match the Sbus DR11, in order to preserve compatibility with existing drivers. The idr_var.h and idr_util headers are used internally by the driver. The c programs, link.master.c, and link.slave.c, which are included on the driver diskette, are very simple examples of how the 10118 might be used. Also included is link.master.manual, which is an example of operation in manual mode. Manual mode is useful for continuous data collection, since it allows the DR11's fifos to continue to run between unix read or write calls, and allows multiple read/write calls to be used to satisfy the DR11 block range count. NOTE: The headers included with the Linux, Solaris 1.x (SunOS), and Solaris 2.x drivers may not be intermixed. They will produce the same ioctl command codes when used with the appropriate compiler and source. They will not compile correclty if used with the wrong source or compiler. (That is, you can't compile idrio.h with a Solaris 2.x driver or application, you must use idr_io.h supplied with the Solaris 2.x driver, and likewise for idr_io.h supplied with the Linux driver). A call to the 10118 driver looks like: ioctl(filedes,cmd,arg) ; Filedes is a filedescriptor returned by the open() call. Cmd is one of the pre-defined commands from idr_io.h. Arg (when used) is a pointer to one, or a combination, of the arguments defined in idr_io.h. Arg is typically defined as an array of unsigned integers, or a pointer to an unsigned integer (32 bit). An code fragment that opens the 10118 named idr0, and then sets the dma timeout might look like (without any error checking): #define DMATIMEOUT 120 /* 120 seconds */ ... int filedes ; u_long arg; ... filedes = open("/dev/idr0",O_RDWR) ; ... arg = DMATIMEOUT ; ioctl(filedes, IDRIO_SET_DMA_TIME, &arg) ; All of the ioctl() calls conform to this general example. What follows is a description of each command, and the possible arguments used with that command. In the argument descriptions for each command, including a particular argument in arg is equivalent to setting it to one. Omitting it is equivalent to setting it to zero. IDRIO_SET_MODE Sets the operating mode of the board and driver. This is generally used once - if at all - at the beginning of a program to configure the board and driver's "static" parameters, which will not change during this use of the board. This ioctl corresponds to the mode register on the 10103 Sbus DR11 emulator. The pci board and driver will set bits appropriately to duplicate these functions. IDR_RDISX Disables the DR11 range counter. May be useful for continuous data collection applications. IDR_RDYT If 0, READY goes true during BUSY. If 1, READY goes true at BUSY trailing edge. READY true during BUSY is the normal mode for DR11-W applications. IDR_FMOD Not used on pci board. IDR_BDIS A one here disables the CYCLE RQ B input. Used if for some reason the device connected to the 10118 floats this input. IDR_SWAP A one causes DMA bytes to and from the attached device to be swapped within a 16 bit DR11 word. Does not affect the 10118 registers, only device data. THIS FUNCTION IS NOT IMPLEMENTED IN EARLY VERSIONS OF THE 10116 BOARD, WHICH ALWAYS SWAPS DMA DATA. (This is due to the way the sparc processor and memory are connected to the pci bus). Later versions of the '116 board - eeprom revision x.2 and later, and all 10118 boards implement byte swapping for dma data. Their default is no swap. IDR_CRQP 0 causes the CYCLE RQ A & B inputs to be rising edge active. 1 selects falling edge active. Rising edge active is normal for DR11-W applications. IDR_BSYP 0 selects the BUSY output to be asserted low, 1 selects asserted high. Asserted low is normal for interprocessor links to another DR11. An attached device may require either high or low assertions, depending on how it is designed. IDR_SPEED_0, IDR_SPEED_1, IDR_SPEED_2, IDR_SPEED_3 One of these arguments may be used to select the speed of the DR11 handshake with the attached device. This choice affects the input data de-skew, the output data settling time, and the length of BUSY. Speed 0 is the fastest selection. See the 10116/10118 manual. A call to set the mode to byte swapping, and the slowest speed would be: arg[0] = IDR_SWAP | IDR_SPEED_3 ; ioctl(filedes,IDRIO_SET_MODE,arg) ; IDRIO_IMM_FCN This call sets the outgoing function bits to the requested value immediately. The desired function bit combinations are very application dependent, although they usually have a fixed meaning in interprocessor links. In links, FCN1 on means that the local DR11 is to be the receiver of the transfer, FCN3 is always set to 1 during the DMA block transfer, and FCN2 can be used to pulse the other DR11's ATTENTION input (the ACF2 pulse can be used to do this automatically, which avoids having to use this comand once to set FCN2 and again to reset it). IDR_FCN3, IDR_FCN2, IDR_FCN1 These arguments may be or'd together. IDRIO_READ_FCN, IDRIO_WRITE_FCN These calls pre-define the function bit patterns to be set at the start of a read or write operation. They have no effect until the read or write is actually issued. It is only necessary to make one or both of these calls once if at all. After being initially set, they will apply to all later reads and/or writes. The arguments used are the same as for IDRIO_IMM_FCN. There are pre-defined defaults for the function bits that may be changed by editing the idr_io.h file. See a later section of these notes for further information. IDRIO_IMM_PULSE This call issues certain pulses to the attached device and to the 10118 logic. Some of the available pulses affect the internal operation of the 10118, and should be used with caution! The internal pulses are IDR_RATN, _TERM, and _MCLR. Most of these pulses will be set using the read and write deferred pulse calls. The IDR_ACF2 pulse is the only one likely to be used as an immediate pulse, unless operating in MANUAL mode. The arguments to this command may be or'd together. IDR_REOR Not supported by the pci driver IDR_RATN Clears the internal ATTENTION flag. IDR_TERM Sets READY true. Can be used to terminate a DR11 block transfer that was started in MANUAL mode. NOT RECOMMENDED!!! Not necessary when operating in AUTO mode. (See later for descriptions of these modes). IDR_MCLR Clears all 10118 idr (not PLX) logic. Use with extreme caution!!!! Better yet, don't use it at all!!!!!!! IDR_INIT Sends an INITAILIZE pulse to the attached device. Has no effect on the 10116/10118. IDR_ACF2 Pulses the ACLO_FNCT2 output. In interprocessor links, this will cause an ATTENTION pulse at the other end of the link. Typically used for preliminary handshaking and synchronization prior to transferring a DMA block. IDR_CYCL Forces an immediate cycle request. This simulates a cycle request from the attached device. It is normally used to start a link-mode output transfer. It is generally NOT used to start input in any application. There are some non-link applications that will use it to start output transfers. These are situations where the attached device does not issue the first cycle request at the beginnig of a transfer, but does issue all subsequent requests. IDR_GO Issues a GO pulse to the attached device. The GO indicates that DMA block transfers may begin. IDRIO_READ_PULSE, IDRIO_WRITE_PULSE These calls set the pulse commands to be issued at the start of a read or write. They need to be set only once. There are pre-defined defaults for the read and write pulses. Typically, (IDR_GO | IDR_CYCLE) would be used at the beginning of a link mode write. (IDR_GO | IDR_ACF2) would be used at the start of a link mode read. GO enables the block transfer, CYCLE forces the first word of the transfer for output transfers, and ACF2 indicates to the other end of the link that this board is ready to receive data for input transfers. IDRIO_SET_DMA_TIME, IDRIO_SET_ATTN_TIME, IDRIO_SET_RDY_TIME These calls set the timeout values (in seconds) used during a wait for the end of a DMA block transfer, wait for ATTENTION, and wait for READY to go true. Currently, the pre-defined defaults are 30 seconds for each of these parameters. In addition, the ioctl() code will not let the calling program set the timeout(s) to less than 10 seconds, or more than 10 minutes. This is left over from an earlier version of the driver that couldn't receive signals while waiting. The defaults, and/or the limits, can be changed by editing the idr_io.h file. IDRIO_ATTN_WAIT, IDRIO_RDY_WAIT These calls can be used to wait (by sleeping if necessary) for ATTENTION, or READY, respectively. The sleeps are subject to the above timeout values. ATTN_WAIT is typically used when waiting for an attention pulse from the attached device, or other DR11. In an interprocessor link, ATTENTION is often used to synchronize the start of a DMA block transfer. See interprocessor links in the 10116/10118 manual. RDY_WAIT is usually used in MANUAL mode. If multiple unix reads or writes are used to satisfy a larger DR11 range count, this wait can be used to test for DR11 end of range - or termination of a block transfer by ATTENTION (which sets READY). IDRIO_GET_STATUS This call can be used to determine board and driver status. It may be useful when polling for certain device or board conditions. Any or all of these conditions/flags may be true at the same time. These bits match those of the 10103 Sbus DR11 emulator, they are gathered from various registers in the 10118 and presented here for compatibility. IDR_EORF This flag sets when the DR11 range counter underflows. IDR_ATTF Sets at the rising edge of an incoming ATTENTION pulse. IDR_ATTN This flag follows the state of the ATTENTION input. Note that GO has no effect on the 10118 if ATTENTION is true when GO is issued. This bit can be used to make sure that ATTENTION is off prior to issuing GO in MANUAL mode. In AUTO mode, this is taken care of by the driver code. IDR_MCER This is an error flag that sets if the attached device requests a transfer while the 10118 is busy processing the previous transfer request. (CYCLE RQ while BUSY is true). In normal transfers that use a full handshake - such as interprocessor link transfers - this bit will never set. It is useful for applications that must use a continuous input clock to drive CYCLE RQ. In this case, the 10118 must always be fast enough to process each request in the time between clocks, and more important, must always have room in its fifo. The 10118 is extremely fast. However, in MANUAL mode, the fifo will continue to fill in the gap between reads. In this case, it is possible to overflow the fifo if the system takes too long to start the next read. The MCER flag will alert the user to this error if tested after each unix read call. This flag does not cause an interrupt or affect the transfer in any way. It is a status flag only. IDR_STTA, IDR_STTB, IDR_STTC These follow the status A, B, and C inputs from the attached device. They have no effect on the 10116/10118 logic. IDR_REDY This is the master READY bit in the 10118. It goes to 0 when GO is issued, and set to 1 when the DR11 range count is exhausted, ATTENTION is received, or TERM or MCLR is pulsed. IDRIO_GET_RANGE This call returns the DR11 range counter residual value. It is returned as the 24 least significant bits of an unsigned integer. It is equal to the remaining transfer count MINUS ONE. At the end of a complete block, the value returned will be 0x00FFFFFF. This call may be useful when doing an input of a word count less than the requested block size. Some applications may involve a variable block size. The range count may be set to the maximum allowed - or desired - and read after the transfer to determine the number of words actaully received. It may be necessary to use this count to determine how many unix read calls are necessary to input to memory the data actually received. IDRIO_GET_REGS (requires array as argument) This ioctl command returns 16 of the board's registers, and the device IDs and revision level in an arg array of unsigned integers. The PLX registers are 32 bits wide. The DR11 registers are 8 bits wide except the data in register which is 16 bits wide. See the 10116/10118 manual for complete register descriptions. The organization of the returned array is: arg[0] device ID (high 16 bits) & vendor ID (low 16 bits) arg[1] revision ID arg[2] PLX interrupt control/status register arg[3] PLX EEPROM control and user bits register arg[4] PLX DMA mode register arg[5] PLX DMA PCI address arg[6] PLX DMA local address register arg[7] PLX DMA transfer count register arg[8] PLX DMA descriptor pointer register arg[9] PLX DMA command/status register arg[10] latched functions register arg[11] flags register arg[12] status register arg[13] mode register arg[14] DR11 range register - low byte arg[15] DR11 range register - mid byte arg[16] DR11 range register - high byte arg[17] fifo status register arg[18] data in register IDRIO_GET_FLAGS Returns various driver software flags and error bits. This is primarily diagnostic. IDR_DVMA_WAIT waiting for DVMA terminal count IDR_EOR_WAIT waiting for DR11 end of range IDR_ATTN_WAIT waiting for ATTENTION IDR_RDY_WAIT waiting for READY IDR_DVMA_TIMEOUT DVMA wait timeout IDR_EOR_TIMEOUT EOR wait timeout IDR_ATTN_TIMEOUT ATTENTION wait timeout IDR_RDY_TIMEOUT READY wait timeout IDR_MANUAL manual mode IDR_INPUT input mode IDR_MCYL_ERR multicycle error IDR_SIG_RECEIVED signal received IDR_PAR_ERR DR11 parity error - new for pci (only works with DR11 parity compatible boards) IDRIO_DATA_OUT Sends a 16 bit value to the attached device. This is a programmed i/o output, and does not use any of the DVMA handshake signals (GO, BUSY, etc). ACF2 and ATTENTION pulses are often used for software synchronization of p-i/o data transfer. The data sent can be anything useful to the user. For instance, it could be the requested transfer count in an interprocessor link, or command bits to a display or data collection device. IDRIO_DATA_IN Reads the 16 bit value from the DR11 input data lines. IDRIO_SET_RANGE Sets the DR11 range counter to an initial value. This value is must be the desired WORD count MINUS 1. This call is only used in MANUAL mode. The maximum value allowed is 0xFFFFFF, which gives a maximum DR11 block size of 32Mbytes. IDRIO_AUTO, IDRIO_MANUAL These calls determine the overall operating mode of the 10118 driver. In AUTO mode, each call to the idr_strategy routine (made via unix read or write calls), is treated as a complete transfer. The DR11 and DMA range couters are initialized to equivalent counts, and the appropriate read or write pulses and function bits are set. The driver sleeps until the counts are exhausted, or an ATTENTION is received. If read or write is called with a block size to big for the DMA copy buffer, multiple DMA and copy operations will be made invisibly to the user. AUTO mode operation pulses GO and sets the read/write function bits and sets the DR11 range counter only once per read or write call, not per each DMA block. AUTO mode is the default. NOTE: With Tahoma's Solaris driver, a user buffer too large to be handled by a single DMA transfer will also be broken into multiple transfers. However, GO, read/write function bits, and other pulses will be issued with each DMA transfer, not just once per read or write call. The DR11 range counter is also set with each DMA transfer in the Solaris driver. The difference is dictated by the maximum size of the DMA transfer allowed by Linux (limited by user DMA mapping/copy boffer capacity) and Solaris (limited by mapping resources - since DMA is done direcly to/from the users buffer). With current hardware (PC and SPARC), mapping resources are probably not an issue, and the allowed max DMA size can be quite large. MANUAL mode may be selected for cases when multiple read or write calls will be used explicitly to satisfy a larger DR11 block size - or for the case of continuous data transfers where data must be transferred to or from disk without terminating the overall DR11 block. In MANUAL mode, unix read or write calls only communicate with the 10118's DVMA logic. The user must set the DR11 range counter, issue all necessary function bit changes and pulses, and wait for DR11 block completion - if desired. The calling program must also use the START_READ and START_WRITE calls prior to issuing GO to start the overall block, and prior to issuing the first unix read or write call within the block. The BLOCK_END call must be used after all DR11 transfers are complete, typically after a wait for READY (for output) or after the last read (for input). IDRIO_START_READ, IDRIO_START_WRTITE, IDRIO_BLOCK_END See the description of MANUAL mode, above. IDRIO_DEV_AND_VEND_ID Returns the pci ids for the 10118 board(s). The vendor id is the low 16 bits, the device id is the high 16 bits. IDRIO_REVISION_ID Returns the revision of the board's eeprom - which indicates the board's features. This is not the same as the artwork revision of the board, which is indicated in the board's silk screen legend. IDRIO_SET_NEW_MODE This ioctl allows direct access to the board's mode register (similar to the Sbus board's mode register). See the IDRIO_SET_MODE ioctl above for descriptions of these functions. Use the NEW_MODE definitions with this ioctl, and the OLD_MODE definitions with IDRIO_SET_MODE. NEW_MODE OLD_MODE LONG_READY same as IDR_RDYT DISABLE_RANGE same as IDR_RDISX DISABLE_CRQ_B same as IDR_BDIS CYCLE_POLARITY same as IDR_CRQP BUSY_POLARITY same as IDR_BSYP SPEED_0,1,2,3 same as IDR_SPEED_0,1,2,3 IDRIO_GET_NEW_STATUS Returns the pci DR11 emulator's status register. This register is similar to the Sbus DR11 emulator's status register. See IDRIO_GET_STATUS ioctl above for descriptions of these functions. Use the NEW_STATUS definitions with this ioctl, and the OLD_STATUS definitions with IDRIO_GET_STATUS. NEW_STATUS OLD_STATUS READY same as IDR_READY ATTENTION same as IDR_ATTN STATUS_A,B,C same as IDR_STTA,B,C A00_INPUT reports the state of the A00 input (driven by the other end's READY output in DR11-DR11 interprocessor link) IDRIO_GET_NEW_FLAGS Returns the state of the hardware flags register (not the unit_flags driver state word). END_OF_RANGE_FLAG set when DR11 counter underflows ATTENTION_FLAG set by false to true transition on ATTENTION input PARITY_ERROR_FLAG set by DR11 input data parity error - only valid if connected to another device that supports DR11 cable parity MULTI_CYCLE_ERROR_FLAG set if attached device issues a cycle request while the board is still busy processing the previous cycle request - indicates data overrun/underrun in applications where the device does not use a full cycle request/busy handshake IDRIO_MASTER_CLEAR This ioctl does a full reset of the board, including a reset of the PLX pci interface chip and an eeprom reload. This should be used for extreme emergencies only (or perhaps when initially debugging an application)!! DRIVER DEFAULT PARAMETERS There are several default driver parameters that may be overridden by insmod when the driver is loaded. They may be set by manually loading the driver and specifying each parameter to be overridden, or by editing the idr_install-2.6 script and using that script to load the driver. They may also be modified at run time by using the above ioctl() calls. See idr_install-2.6 and README.idr-2.6 for additinal information. ./idr_driver.c0000644000076400007640000042441210223360163013147 0ustar wdwwdw00000000000000/* * idr_driver.c * code module for Linux 2.6.x driver for PCI DR11 boards * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * * 22 February, 1999 start of development - initial target Linux 2.0.35 (R.H. 5.1) * * 14 Sept, 1999 re-code for linux 2.2 * this version no longer compatible with earlier linux revs * * 11 November, 1999 change the way we do config load of PLX chip to avoid * possible future panic if we run into a bridge chip * with a short pci timer * * 13 March, 2000 started working w/2.3.x * new pci_dev definition required changes in the way we get * base addresses * changed file_ops struct initialization to use named fields * changed wait_queue type and added initialization * * 24 March, 2000 incorporated kiobuf mechanism for dma to/from user buffers * to replace dma copy buffer used in older versions * separated int level read from board and one read from * pci_dev (so we can use the hardware level to restore * board after en eeprom reload * incorporated modified version of Kaz Kylheku's mutex stuff * for SMP * convert all IPLS accesses to PLX accesses - doing accesses via the * idr space may have been allowing pre-fetch from plx registers * disable address space 0 pre-fetch * make int/cstat read in intr volatile * add a dummy read when input fifo or output fifo directly accessed * needed a few 100ns to allow fifo op to complete, especially in box * w/write posting buffers * start DMA in two steps to avoid PLX bug * pause DMA after sleep in strategy - looping a while if not done * * 30 August, 2000 use readl and writel rather than direct dereference (for non x86 * architectures) this change in idr_reg.h * SA_SHIRQ rather than SA_INTERRUPT to allow sharing and int code * interruption * use time_before() when delaying to avoid jiffies wrap * add pci_enable_device() * * 18 June, 2001 move pci_enable_device() prior to device resource accesses to try to * keep non-x86 architectures happy * * 1 August, 2002 corrected use of kiobuf maplist to generate scatterlist to allow for * highmem enabled machines (up to 4G) * * added kernel copy buffer for case where machine is and kernel * are highmem, but kernel does not have new scatterlist capability * in this case, we will allocate a copy buffer at init time, and * stuff the kiobuf with its pages * * changed max_phys to orders for allocation efficiency in case of * copy buf * * include slab.h instead of malloc.h * * 4 February, 2004 major modifications for 2.6 kernel * * kiobuf functionality brought inside driver to allow mapping * and pinning user buffer pages - since no longer supported * directly in kernel * * reorganized some code into helper functions * * 4 September, 2004 corrected a few non-code typos/thinkos * * 8 October, 2004 added owner to file_operations struct * added pci_disable_device to unload code * * 1 April, 2005 (no fooling!) modified some types for x86_64 compatibility */ /* * This code released under the GPL, and in the public domain * References to IKON left in place for compatibility and historical reasons * * Please send us code modifications and BUG reports */ /* * linux includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * tahoma ("ikon") includes */ #include "idr_reg.h" #include "idr_io.h" #include "idr_var.h" /* * prototypes */ static int idr_init_module(void); static void idr_cleanup_module(void); static void idr_free_units(void); static int idr_open(struct inode *, struct file *); static int idr_close(struct inode *, struct file *); static ssize_t idr_read(struct file *, char *, size_t, loff_t *); static ssize_t idr_write(struct file *, const char *, size_t, loff_t *); static int idr_ioctl(struct inode *, struct file *, u_int, u_long); static irqreturn_t idr_intr(int, void *, struct pt_regs *); static int idr_strategy(struct idr_unit_t *, char *, int, int, int); static void plx_soft_reset(struct idr_unit_t *); static void idr_set_global_defaults(void); static void idr_set_board_defaults(struct idr_unit_t *, int); static int idr_dma_alloc(struct idr_unit_t *, int); static void idr_dma_free(struct idr_unit_t *, int); static int idr_map_regs(struct idr_unit_t *, int); static void idr_unmap_regs(struct idr_unit_t *, int); static int idr_map_or_copy_buf(struct idr_unit_t *, int, char *, int, int); static int idr_unmap_or_copy_buf(struct idr_unit_t *, int); static void idr_unmap_on_error(struct idr_unit_t *, int); static void idr_unmap_user_buf(struct idr_unit_t *, int); static int idr_map_user_buf(struct idr_unit_t *, int); static int idr_maplist_to_sg_list(struct idr_unit_t *, int); static int idr_sg_list_to_iopb_list(struct idr_unit_t *, int); static int idr_maplist_check(struct idr_unit_t *, int); /* * mutex and conditional variable stuff - was in ikon_mutex.h (Kaz Kylheku & wdw) */ static void mutex_init(mutex_t *); static void mutex_lock(mutex_t *); static void mutex_unlock(mutex_t *); static void cond_init(cond_t *); static int cond_timed_wait_rel(cond_t *, mutex_t *, long); static void cond_broadcast(cond_t *); /* * configuration variables that may be specified at module install time * see idr_io.h for possible values * * each internal variable is paired with one that can be modified by insmod * * all of the function bits are kept separate, rather than combined into * three variables to preserve some kind of compatibility with the * solaris conf file */ static int speed_def = -1; static int dma_time_def = -1; static int attn_time_def = -1; static int rdy_time_def = -1; static int byte_swap_def = -1; static int cycle_pol_def = -1; static int busy_pol_def = -1; static int write_cycle_def = -1; static int read_cycle_def = -1; static int read_acf2_def = -1; static int open_f3_def = -1; static int open_f2_def = -1; static int open_f1_def = -1; static int write_f3_def = -1; static int write_f2_def = -1; static int write_f1_def = -1; static int read_f3_def = -1; static int read_f2_def = -1; static int read_f1_def = -1; static int max_boards = -1; static int max_phys_order = -1; /* 2**max_phys_order * PAGE_SIZE = max xfer */ module_param(speed_def, int, 0); module_param(dma_time_def, int, 0); module_param(attn_time_def, int, 0); module_param(rdy_time_def, int, 0); module_param(byte_swap_def, int, 0); module_param(cycle_pol_def, int, 0); module_param(busy_pol_def, int, 0); module_param(write_cycle_def, int, 0); module_param(read_cycle_def, int, 0); module_param(read_acf2_def, int, 0); module_param(open_f3_def, int, 0); module_param(open_f2_def, int, 0); module_param(open_f1_def, int, 0); module_param(write_f3_def, int, 0); module_param(write_f2_def, int, 0); module_param(write_f1_def, int, 0); module_param(read_f3_def, int, 0); module_param(read_f2_def, int, 0); module_param(read_f1_def, int, 0); module_param(max_phys_order, int, 0); module_param(max_boards, int, 0); /* * internal variables will use defaults if paired modifiable variables are still -1 after insmod * most of these variables have working versions kept in the unit structures on a per-board basis * this multi-layered approach is to preserve some similarity with the solaris driver */ static int idr_speed_def; static int idr_dma_time_def; static int idr_attn_time_def; static int idr_rdy_time_def; static int idr_byte_swap_def; static int idr_cycle_pol_def; static int idr_busy_pol_def; static int idr_write_cycle_def; static int idr_read_cycle_def; static int idr_read_acf2_def; static int idr_open_f3_def; static int idr_open_f2_def; static int idr_open_f1_def; static int idr_write_f3_def; static int idr_write_f2_def; static int idr_write_f1_def; static int idr_read_f3_def; static int idr_read_f2_def; static int idr_read_f1_def; static int idr_max_phys_order; static int idr_max_boards; /* * some global variables */ static int idr_boards_found; /* number of boards actually detected */ static int idr_boards_attached; /* number of boards successfully attached */ static int idr_module_initialized; /* non zero if any board attaches and module */ /* initialized successfully */ static int idr_device_major; /* major number dynamically assigned to this driver */ static int idr_max_buf_pages; /* # of pages in copy buf = 2**idr_max_phys_order */ /* if copy buff used, copies will be to 0 offset in buf */ /* so extra page not necessary */ static int idr_max_buf_bytes; /* in bytes = idr_max_buf_pages * PAGE+SIZE */ static int idr_max_sg_length; /* idr_max_buf_pages + 1 (allows for user buff offset) */ /* MUST TRACK KIOBUF MAP SIZE AND COPY BUF SIZE */ static int idr_iopb_mem_size; /* size of mem allocated for iopb list */ /* * pointer at base of unit structure array * array - rather than linked list, to try to be a little more efficient with kernel memory */ static struct idr_unit_t *idr_unit_base_p; /* * declare dummy variables used to the left of = when we read a board * register to force the write cache to flush a register write to the bus * * may not be necessary in PC architecture */ static volatile u_long read_dump_0; static volatile u_long read_dump_1; /* * our file-ops structure */ struct file_operations idr_fops = { owner:THIS_MODULE, read:idr_read, write:idr_write, ioctl:idr_ioctl, open:idr_open, release:idr_close, }; /* * get the global parameters, kmalloc and zero the unit structure array * * register interrupts, map the working registers, get the dma iopb memory & * scatterlist, and copy buffer if required, register the driver and initialize * things on per-instance basis * * unless debug printks are enabled, init_module and cleanup_module are silent, * with the exception of a dma/copy mode anouncement and a single success or * failure message for each board instance */ static int idr_init_module(void) { register struct idr_unit_t *unit_p; /* points to current unit structure */ int instance; /* current board # */ int unit_array_size; /* size of array of unit structures */ u_long temp; /* generic temp */ u_char c_temp; /* etc */ struct pci_dev *temp_pci_dev_p; /* temp pci dev struct pointer */ DPRINT((KERN_DEBUG "idr: init_module: entering init_module\n")); temp = LINUX_VERSION_CODE; DPRINT((KERN_DEBUG "idr: init_module: compiled under linux version %ld.%ld.%ld uts release: %s\n", (temp >> 16) & 0xffff, (temp >> 8) & 0xff, temp & 0xff, UTS_RELEASE)); /* * identify our memory mode/copy buffer status in msg log */ printk(KERN_INFO "idr: init_module: %s", IDR_DMA_MODE_MSG); idr_module_initialized = 0; /* * set board and driver global defaults from parameters * includes things like iopb mem size */ idr_set_global_defaults(); /* * allocate space for unit structure array * get enough for idr_max_boards unit structures * zero the allocated memory, and save a pointer * to the array */ unit_array_size = idr_max_boards * sizeof(struct idr_unit_t); idr_unit_base_p = (struct idr_unit_t *) kmalloc(unit_array_size, GFP_KERNEL); if (idr_unit_base_p == NULL) { printk(KERN_ERR "idr: init_module: can't allocate unit struct array (lmalloc error)!\n"); return -ENOMEM; } memset(idr_unit_base_p, 0, unit_array_size); DPRINT((KERN_DEBUG "idr: init_module: allocated unit structure array, idr_unit_base_p = 0x%p, size = 0x%x\n", idr_unit_base_p, unit_array_size)); /* * loop, looking for our boards - there better be at least one */ DPRINT((KERN_DEBUG "idr: init_module: starting search for boards\n")); idr_boards_found = 0; idr_boards_attached = 0; /* * initialize pci dev struct pointer used to walk through device structures */ temp_pci_dev_p = NULL; for (instance = 0; instance < idr_max_boards; instance++) { /* * make a pointer to the correct element of the unit structure array, and save * the instance number which may be useful later - for debug and error reporting */ unit_p = idr_unit_base_p + instance; unit_p->instance = instance; /* * look for board */ DPRINT((KERN_DEBUG "idr: init_module: probing for board at instance %d, unit_p = 0x%p\n", instance, unit_p)); temp_pci_dev_p = pci_find_device(IKON_VENDOR_ID, IDR_DEVICE_ID, temp_pci_dev_p); /* * if successful, we have a board * if not found, it means that we have already found all of our boards - IF ANY * if anything else, we print an error message * * there doesn't seem to be any way to detect an error, just a NULL result */ if (temp_pci_dev_p == NULL) { DPRINT((KERN_DEBUG "idr: init_module: instance %d: no (more) boards found, ending search\n", instance)); break; } /* * we have actually found a board - we have work to do! * * we won't increment idr_boards_attached until we get through this without errors!!! */ /* * save pointer to this board's pci dev structure */ unit_p->pci_dev_p = temp_pci_dev_p; DPRINT((KERN_DEBUG "idr: init_module: instance %d: board found, initializing unit structure\n", instance)); idr_boards_found++; /* * memset undoubtedly zero'ed our unit structure, but we will init some things in * a more formal manner it seems like a good idea to refer to these struct elements * explicitly - as a reminder, if nothing else * * initialize mutex and conditional variable */ mutex_init(&unit_p->mutex); cond_init(&unit_p->cond); unit_p->sleeping = 0; unit_p->unit_attached = 0; unit_p->unit_open = 0; unit_p->unit_flags = 0; /* * allocate dma resources - iopb list memory, sg list memory, user buffer map list, * and copy buffer if direct user dma not to be used - and set dma mask */ if(idr_dma_alloc(unit_p, instance)) { printk(KERN_ERR "idr: init_module: instance %d: idr_dma_alloc error! board not attached!\n", instance); continue; } /* * grind through the config registers to get register base addresses and other values * use the new pci interface, but keep the rest of the old code * * the base address registers contain the bus addresses of our two register sets * - plx and idr unfortunately, the plx register set is smaller than a page, so bios * may align it on an address that isn't a page boundary * * ioremap doesn't tolerate that, so we have to figure out the appropriate (maybe lower) * page boundary and the appropriate size to ask for we will save the mapped base and * the actual virtual pointer to the register set * * I hope linux allows multiple processes to map the same bus page, or we may clobber * someone else - or that the config process doesn't share pages */ /* * make sure that io and mem accesses are enabled * make sure that the pci master enable bit is set * * pci_enable_device does some things on non-x86 arch's * that must be done before accessing device resource registers */ if(pci_enable_device(unit_p->pci_dev_p)) { printk(KERN_ERR "idr: init_module: instance %d: pci_enable_device error! board not attached\n", instance); idr_dma_free(unit_p, instance); continue; } pci_set_master(unit_p->pci_dev_p); DPRINT((KERN_DEBUG "idr: init_module: instance %d: master enable bit set\n", instance)); /* * map the board's registers for slave access */ if(idr_map_regs(unit_p, instance)) { printk(KERN_ERR "idr: init_module: instance %d: idr_map_regs error! board not attached\n", instance); idr_dma_free(unit_p, instance); continue; } /* * get (possibly) mapped int level from pci_dev for use in kernel calls, and * the hardware level from the board for use when restoring the level after * an eeprom reload */ unit_p->pci_dev_int_level = unit_p->pci_dev_p->irq; if (pci_read_config_byte (unit_p->pci_dev_p, PCI_INTERRUPT_LINE, (u_char *) &c_temp)) { printk(KERN_ERR "idr: init_module: instance %d: error reading pci interrupt line!\n", instance); idr_unmap_regs(unit_p, instance); idr_dma_free(unit_p, instance); continue; } unit_p->int_level = c_temp; DPRINT((KERN_DEBUG "idr: init_module: instance %d: board int line = 0x%x, pci_dev int line = 0x%x\n", instance, unit_p->int_level, unit_p->pci_dev_int_level)); /* * for some reason we save device and vendor ids and rev level - * we already know the id's or we wouldn't be here! */ if (pci_read_config_dword (unit_p->pci_dev_p, PCI_VENDOR_ID, (int *) &temp)) { printk(KERN_ERR "idr: init_module: instance %d: error reading pci vendor id! board not attached! \n", instance); idr_unmap_regs(unit_p, instance); idr_dma_free(unit_p, instance); continue; } unit_p->dev_and_vendor_id = temp; DPRINT((KERN_DEBUG "idr: init_modlue: instance %d: device and vendor id = 0x%08lx\n", instance, temp)); if (pci_read_config_byte (unit_p->pci_dev_p, PCI_REVISION_ID, (char *) &temp)) { printk(KERN_ERR "idr: init_module: instance %d: error reading revision id! board not attached!\n", instance); idr_unmap_regs(unit_p, instance); idr_dma_free(unit_p, instance); continue; } unit_p->revision_id = temp & 0xff; DPRINT((KERN_DEBUG "idr: init_modlue: instance %d: revision id = 0x%lx\n", instance, temp & 0xff)); /* * set defaults on a per-board basis */ idr_set_board_defaults(unit_p, instance); /* * do a soft reset of the plx chip - which also clears the idr logic * this will also cause an init to be sent to the device */ plx_soft_reset(unit_p); DPRINT((KERN_DEBUG "idr: init_module: instance %d: plx_soft_reset() complete\n", instance)); /* * get a plx and an idr register and dprint for a sanity check */ DPRINT((KERN_DEBUG "idr: init_module: instance %d: sanity check: plx eeprom reg = 0x%x (s.b. xxxx767e)\n", instance, PLX_GETL(PLX_EEPROM_USER))); DPRINT( (KERN_DEBUG "idr: init_module: instance %d: sanity check: fifo stat reg = 0x%x (s.b. 0x22)\n", instance, IDR_GETL(IDR_FIFO_STATUS) & 0xff)); /* * clear unit flags */ unit_p->unit_flags = 0; /* * register the interrupt line for this board & link to our interrupt handler * pass our unit pointer to the interrupt code * * we will use our node name as a label for the interrupt - that way /proc/interrupts * will have a unique name per interrupt (per board) rather than all of them being * named idr (we could use this when mknod is invoked) * * we have to keep the name in the unit structure, since request_irq just saves the pointer, * and doesn't copy the string - so by the time /proc/interrupts gets read, the stack has * changed, and the name would be garbage if not kept in "permanent" memory * * (thank you Mr. Rubini!) * * i hope that unit_p->minor_node_name actually generates a pointer! (mnm is an array) * */ (void) sprintf((char *) unit_p->minor_node_name, "idr%d", instance); if (request_irq (unit_p->pci_dev_int_level, idr_intr, SA_SHIRQ, (const char *) unit_p->minor_node_name, (void *) unit_p)) { printk(KERN_ERR "idr: init_module: instance %d: error requesting interrupt! board not attached! \n", instance); idr_unmap_regs(unit_p, instance); idr_dma_free(unit_p, instance); continue; } printk(KERN_NOTICE "idr: init_module: instance %d: board attached, irq = 0x%x\n", instance, unit_p->pci_dev_int_level); /* * set per-board attach complete flag for use in open * and bump the count of successfully attached boards */ unit_p->unit_attached = 1; idr_boards_attached++; } /* end of for (instance) loop */ /* * if no boards have attached, give back the unit structure array and return an error * if no boards are found, do the same, but print a different message */ if (!idr_boards_attached) { if (!idr_boards_found) printk(KERN_NOTICE "idr: init_module: no boards found! module not initialized!\n"); else printk(KERN_WARNING "idr: init_module: %d board(s) found but no boards attached! module not initialized!\n", idr_boards_found); kfree(idr_unit_base_p); return -ENODEV; } /* * register the module, and get dynamic major number assigned */ idr_device_major = register_chrdev(0, "idr", &idr_fops); if (idr_device_major < 0) { printk(KERN_ERR "idr: init_module: can't register character driver module! module not initialized!\n"); /* * call routine to walk through unit structure array and free all resources * owned by attached boards and give back the unit array when done */ idr_free_units(); kfree(idr_unit_base_p); return -ENODEV; } DPRINT((KERN_DEBUG "idr: init_module: driver registered, major # = %d\n", idr_device_major)); /* * flag module as initialized and return good status to insmod */ idr_module_initialized = 1; printk(KERN_NOTICE "idr: init_module: %d board(s) found, %d board(s) attached, module initialized\n", idr_boards_found, idr_boards_attached); return 0; } /* end of init_module */ /* * idr_free_units walks through the allocated unit structure array and frees all the * resources of any board that was successfully attached, including iopb memory, * scatterlist, user buf mapping space, copy buffer (if allocated), register mappings, * and interrupt line, and disables device * * it looks at all the slots, up to max_boards, not up to boards_attached, because it is * theoretically possible for pcibios errors to cause some boards to be found, but not * attach (VERY UNLIKELY) * * THIS ROUTINE DOES NOT RETURN THE UNIT STRUCTURE ARRAY - THAT IS LEFT TO THE CALLING ROUTINE */ static void idr_free_units(void) { register struct idr_unit_t *unit_p; /* points at current unit structure */ int instance; /* current instance (minor number) */ /* * look at each unit structure element of array */ DPRINT((KERN_DEBUG "idr_free_units: max boards = %d, boards found = %d, boards attached = %d\n", idr_max_boards, idr_boards_found, idr_boards_attached)); for (instance = 0; instance < idr_max_boards; instance++) { unit_p = idr_unit_base_p + instance; if (unit_p->unit_attached) { DPRINT((KERN_DEBUG "idr_free_units: instance %d: freeing interrupt, mappings, copy buffer (if any), scatterlist, iopb memory, and disabling device\n", instance)); free_irq(unit_p->pci_dev_int_level, (void *) unit_p); idr_unmap_regs(unit_p, instance); idr_dma_free(unit_p, instance); pci_disable_device(unit_p->pci_dev_p); unit_p->unit_attached = 0; } else DPRINT((KERN_DEBUG "idr_free_units: instance %d: this instance not attached - no action taken\n", instance)); } /* end of for (instance) */ } /* end of idr_free_units */ /* * When our driver is unloaded, cleanup_module cleans up and frees the resources * we allocated in init_module * * unregister the driver * * return resources on an per instance basis (interrupts, kiovec, iopb stuff, etc) * * free the unit structure array */ static void idr_cleanup_module(void) { register struct idr_unit_t *unit_p; /* will point to a particular unit structure */ int instance; /* board's minor number */ int boards_detached; /* count of boards detached */ DPRINT((KERN_DEBUG "idr: cleanup_module: entering cleanup_module\n")); /* * make sure module was previously initialized * there doesn't seem to be any way to return an error code */ if (!idr_module_initialized) { printk(KERN_ERR "idr: cleanup_module: cleanup called when module not previously initialized!"); return; } /* * unregister the device (driver) */ if (unregister_chrdev(idr_device_major, "idr")) { printk(KERN_ERR "idr: cleanup_module: unregister_chrdev error! exiting without cleaning up module!\n"); return; } DPRINT((KERN_DEBUG "idr: cleanup_module: device (driver) successfully unregistered\n")); /* * walk through the unit structure, and force any attached boards to be quiet (should already be) * * we could have put this code in free_units, but the tasks are slightly different */ boards_detached = 0; for (instance = 0; instance < idr_max_boards; instance++) { unit_p = idr_unit_base_p + instance; if (unit_p->unit_attached) { /* * issue soft reset to clear plx and idr logic (and disable dma and ints) */ plx_soft_reset(unit_p); DPRINT((KERN_DEBUG "idr: cleanup_module: instance %d: soft_reset issued\n", instance)); /* * soft reset leaves some int enb bits on -- we will just hammer off all the dma * and int enable bits we can think of (most are already cleared) */ PLX_PUTL(PLX_INT_CSTAT, 0); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); IDR_PUTL(IDR_LATCHED_FUNCTIONS, 0); boards_detached++; } else DPRINT((KERN_DEBUG "idr: cleanup_modlue: instance %d: this instance not attached - no action taken\n", instance)); } /* end of for (instance) */ /* * call idr_free_units to release resources * and turn off attached flag(s) * * return unit structure memory */ idr_free_units(); kfree(idr_unit_base_p); /* * clear module initialized flag */ idr_module_initialized = 0; printk(KERN_NOTICE "idr: cleanup_module: %d board(s) detached\n", boards_detached); /* * this is as good a place as any to do phony references to the read dump varaibles * to try to absolutely guarantee that the optimizer won't remove our cache flush reads * * and to try to shut lint up */ read_dump_0 += read_dump_1; return; } /* * do various error checking, move the defaults into the boards logic, * then make the board available for read, write, and ioctl calls * * bump the usage count so driver can't be unlaoded until close called */ static int idr_open(struct inode *inode_p, struct file *file_p) { int instance; register struct idr_unit_t *unit_p; instance = MINOR(inode_p->i_rdev); DPRINT((KERN_DEBUG "idr_open: instance %d: entering idr_open\n", instance)); /* * make sure minor number is within range */ if ((instance < 0) || (instance >= idr_max_boards)) { printk(KERN_ERR "idr_open: instance %d: instance out of range!\n", instance); return -ENODEV; } /* * get unit structure pointer and * make sure driver is (completely) attached */ unit_p = idr_unit_base_p + instance; DPRINT((KERN_DEBUG "idr_open: instance %d: unit pointer = 0x%p\n", instance, unit_p)); if (unit_p->unit_attached == 0) { printk(KERN_WARNING "idr open: instance %d: open attempted when board not attached!\n", instance); return -ENODEV; } /* * Only allow a single open */ if (unit_p->unit_open != 0) { DPRINT((KERN_DEBUG "idr_open: instance %d: open called when already open!\n", instance)); return -EBUSY; } /* * mark device as open */ unit_p->unit_open = 1; /* * initialize DVMA and DR11 logic * SOFT RESET WILL ALSO CAUSE AN INIT TO BE SENT TO THE DEVICE! * if this causes problems, the plx_soft_reset() should be commented out */ plx_soft_reset(unit_p); /* * initialize various defaults using values in soft state * soft state values were derived from insmod properties in init_module() */ /* * initialize time out values to defaults */ unit_p->dma_time = (u_long) (HZ * unit_p->dma_time_def); unit_p->attn_time = (u_long) (HZ * unit_p->attn_time_def); unit_p->rdy_time = (u_long) (HZ * unit_p->rdy_time_def); /* * set read and write pulse and function values to defaults */ unit_p->read_fcn = unit_p->read_fcn_def; unit_p->write_fcn = unit_p->write_fcn_def; unit_p->read_pulse = unit_p->read_pulse_def; unit_p->write_pulse = unit_p->write_pulse_def; /* * set the mode and latch registers to their default values */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, unit_p->latch_reg_def); IDR_PUTL(IDR_MODE, unit_p->mode_reg_def); /* * set plx chip (9060sd and later) to default endian mode * * note that normal default is big_endian to un-swap the byte * swapping that the memory path does to 16 bit words in SPARC * and little_endian in x86 systems */ PLX_PUTL(PLX_ENDIAN_REG, unit_p->endian_def); /* * clear the various flags in unit_flags */ unit_p->unit_flags = 0; DPRINT((KERN_DEBUG "idr_open: instance %d: open complete\n", instance)); return 0; } /* * make the board unavailable for write and ioctl calls * decrement use count, so driver can be unloaded if desired */ static int idr_close(struct inode *inode_p, struct file *file_p) { int instance; register struct idr_unit_t *unit_p; instance = MINOR(inode_p->i_rdev); DPRINT((KERN_DEBUG "idr_close: instance %d: entering idr_close\n", instance)); /* * make sure minor number is within range */ if ((instance < 0) || (instance >= idr_max_boards)) { printk(KERN_ERR "idr_close: instance %d: instance out of range!\n", instance); return -ENODEV; } /* * get unit structure pointer * make sure we are trying to close board that has already been opened */ unit_p = idr_unit_base_p + instance; DPRINT((KERN_DEBUG "idr_close: instance %d: unit pointer = 0x%p\n", instance, unit_p)); if (unit_p->unit_open == 0) { printk(KERN_WARNING "idr close: instance %d: close attempted when board not open!\n", instance); return -ENODEV; } /* * let the next user open the device * and decrement use count */ unit_p->unit_open = 0; /* * reset dma logic, DR11 logic, and flush fifo * THIS CAUSES AN INIT TO BE SENT TO THE ATTACHED DEVICE! * if the init causes problems the soft reset should be commented out */ plx_soft_reset(unit_p); DPRINT((KERN_DEBUG "idr_close: instance %d: soft_reset issued\n", instance)); DPRINT((KERN_DEBUG "idr_close: instance %d: close complete\n", instance)); return 0; } /* * set up dr11 range counter (if automatic mode) * set transfer direction * * loop on data transfer from device to user memory unitl full * user count exhausted * * NOTE THAT THERE ARE SOME ERROR RETURNS IN THIS CODE -- IF WE EVER NEED * A MUTEX HERE, OR ITS EQUIVALENT, THESE WILL ALL NEED TO SET AN ERROR CODE AND * GOTO .. TO AVOID LEAKING OUT THE SIDE OF THE MUTEX */ static ssize_t idr_read(struct file *file_p, char *user_buf_p, size_t user_count, loff_t * loff_p) { struct inode *inode_p; /* will point to inode */ register struct idr_unit_t *unit_p; /* points at our unit structure */ int retval; /* value returned from read */ int instance; /* our instance (minor) number */ int dma_count; /* even byte dma count */ int range; /* temp range counter value */ int strategy_retval; /* value returned from strategy */ int first_dma_flag; /* flags first strat call in auto mode */ int last_dma_flag; /* flags last strat call in auto mode */ /* * we always say we xferred all the bytes, unless error * the plx chip can't read back the dma count anyway * * some idea of the residual after a failure can be read from the dr11 range counter */ retval = user_count; /* * extract inode pointer from file struct - we used to get this passed directly with * write call - pre 2.2 */ inode_p = file_p->f_dentry->d_inode; /* * get instance number and make unit struct pointer */ instance = MINOR(inode_p->i_rdev); DPRINT((KERN_DEBUG "idr_read: instance %d: entering read routine, user_buf_p = 0x%p, count = 0x%x\n", instance, user_buf_p, (unsigned int)user_count)); if ((instance < 0) || (instance >= idr_max_boards)) { printk(KERN_ERR "idr_read: instance %d: instance out of range!\n", instance); return -ENODEV; } unit_p = idr_unit_base_p + instance; /* * check to make sure we have been opened */ if (!unit_p->unit_open) { printk(KERN_ERR "idr_read: instance %d: read called when unit not open!\n", instance); return -EIO; } /* * clear the timeout, multicycle error, parity, sig received, and "waiting for" flags * strategy will set the waiting for dvma or eor flag. */ unit_p->unit_flags &= IDR_CLEAR_FLAGS; /* * set the input flag */ unit_p->unit_flags |= IDR_INPUT; /* * buffer size and start address must be even (16 bit aligned) * and buffer size must be non-zero */ if ((unsigned long) user_buf_p & 1) { printk(KERN_WARNING "idr_read: instance %d: odd buffer address!\n", instance); return -EINVAL; } if ((user_count == 0) || (user_count & 1)) { printk(KERN_WARNING "idr_read: instance %d: zero or odd buffer size!\n", instance); return -EINVAL; } /* * if automatic mode, set up dr11 range count and set first_dma_flag * to indicate first call to strategy in auto mode * * first dma flag will be set back to 0 after first strategy call */ if (!(unit_p->unit_flags & IDR_MANUAL)) { DPRINT((KERN_DEBUG "idr_read: instance %d: auto mode, setting range counter to 0x%x (words - 1)\n", instance, (unsigned int)(user_count >> 1) - 1)); /* * dr11 counter wants WORD count minus 1 */ range = (user_count >> 1) - 1; if (range > IDR_DR11_MAXBLOCK) { printk(KERN_WARNING "idr_read: instance %d DR11 range count too large!\n", instance); return -EINVAL; } IDR_PUTL(IDR_RANGE_HIGH, range >> 16); IDR_PUTL(IDR_RANGE_MID, range >> 8); IDR_PUTL(IDR_RANGE_LOW, range); first_dma_flag = 1; /* * we have some bookkeeping to do that would normally be in strategy * but since we may have to call strategy multiple times, and we * only want to do this stuff once, we will have to do it here */ /* * in auto mode, pulse off any error flags (leave on for later code if manual) */ IDR_PUTL(IDR_116_PULSES, RESET_EOR_FLAG | RESET_ATTENTION_FLAG | RESET_ERROR_FLAGS); /* * force off idr dma enb and direction bit * set input mode, * then re-enable dma * * we don't fiddle interrupts here - that will be done in strategy */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~(IDR_DMA_ENABLE | DMA_INPUT_MODE)); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | DMA_INPUT_MODE); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | IDR_DMA_ENABLE); } else { /* * in manual mode, just clear first dma flag * pulses will be issued by user via ioctl */ first_dma_flag = 0; } /* * loop on strategy while user_count >0 * strategy expects the virtual address of the next chunk of the user buffer */ while (user_count > 0) { /* * use the min of remaining user count and max dma buffer size * as the dma count */ dma_count = (user_count < idr_max_buf_bytes) ? user_count : idr_max_buf_bytes; DPRINT((KERN_DEBUG "idr_read: instance %d: inside while loop, buf_pointer = 0x%p, dma_count = 0x%x, first_dma_flag = 0x%x\n", instance, user_buf_p, dma_count, first_dma_flag)); /* * if auto mode and this is the last call to strategy required to satisfy user count, * set last_dma_flag - it is probably not going to be used by strategy for input * operations, but output will require it */ if ((!(unit_p->unit_flags & IDR_MANUAL)) && (dma_count >= user_count)) { DPRINT( (KERN_DEBUG "idr_read: instance %d: setting last_dma_flag to 1\n", instance)); last_dma_flag = 1; } else { DPRINT((KERN_DEBUG "idr_read: instance %d: setting last_dma_flag to 0\n", instance)); last_dma_flag = 0; } DPRINT((KERN_DEBUG "idr_read: instance %d: calling idr_strategy\n", instance)); strategy_retval = idr_strategy(unit_p, user_buf_p, dma_count, first_dma_flag, last_dma_flag); DPRINT((KERN_DEBUG "idr_read: instance %d: idr_strategy returns 0x%x\n", instance, strategy_retval)); if (strategy_retval) { printk(KERN_WARNING "idr_read: instance %d: idr_strategy returns error = %d\n", instance, strategy_retval); return strategy_retval; } /* * bump user buffer pointer, decrement counter * force first_dma_flag off (if on) */ user_buf_p += dma_count; user_count -= dma_count; first_dma_flag = 0; } /* end of while(user_count) */ /* * if automatic mode - turn off dma enable, then * check the board for multicycle error or parity error * and set flags accordingly * * this was done in strategy in the solaris driver * * regardless of mode, * we will then look at those flags and the sig received * and timeout flags passed from strategy, and return * errors accordingly */ if (!(unit_p->unit_flags & IDR_MANUAL)) { IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE); if (IDR_GETL(IDR_FLAGS) & PARITY_ERROR_FLAG) { DPRINT((KERN_DEBUG "idr_read: instance %d: parity error!\n", instance)); unit_p->unit_flags |= IDR_PAR_ERR; } if (IDR_GETL(IDR_FLAGS) & MULTI_CYCLE_ERROR_FLAG) { printk(KERN_WARNING "idr_read: instance %d: multi-cycle error!\n", instance); unit_p->unit_flags |= IDR_MCYL_ERR; } } /* * check flags for timeout or multicycle error or sig received * * most dr11 devices do not check parity on the dr11 cable * most tahoma dr11s check dr11 cable parity and flag an error * * we will not report parity errors as read or write errors here, since * most applications (that don't support parity) will cause the flag to * set often * * parity errors can be tested for by examining the driver flag word, or * the board's flag register using the IDRIO_GET_NEW_FLAGS ioctl * * if parity checking is desired within the read or write routine, add * the following code and re make the driver * * if(unit_p->unit_flags & IDR_PAR_ERR) { * printk(KERN_WARNING "idr_read: istance %d: parity error!\n", instance); * return -EIO; * } */ if (unit_p->unit_flags & (IDR_DVMA_TIMEOUT | IDR_EOR_TIMEOUT)) { printk(KERN_WARNING "idr_read: instance %d: DMA timeout! \n", instance); return -EIO; } if (unit_p->unit_flags & IDR_MCYL_ERR) { printk(KERN_WARNING "idr_read: instance %d: Multicycle error! \n", instance); return -EIO; } if (unit_p->unit_flags & IDR_SIG_RECEIVED) { printk(KERN_WARNING "idr_read: instance %d: signal received! \n", instance); return -EINTR; } DPRINT((KERN_DEBUG "idr_read: instance %d: leaving read routine\n", instance)); return (retval); } /* * set up dr11 range counter (if automatic mode) * * set direction of transfer * * loop on dma transfer to device until user count exhausted * * NOTE THAT THERE ARE LOTS OF ERROR RETURNS IN THIS CODE -- IF WE EVER NEED A MUTEX HERE, * OR ITS EQUIVALENT, THESE WILL ALL NEED TO SET AN ERROR CODE AND GOTO ... TO AVOID * LEAKING OUT THE SIDE OF THE MUTEX */ static ssize_t idr_write(struct file *file_p, const char *user_buf_p, size_t user_count, loff_t * loff_p) { struct inode *inode_p; /* will point to inode */ register struct idr_unit_t *unit_p; /* points at our unit structure */ int retval; /* value returned from write */ int instance; /* our instance (minor) number */ int dma_count; /* even byte dma count */ int range; /* temp range counter value */ int strategy_retval; /* value returned from strategy */ int first_dma_flag; /* flags first strat call in auto mode */ int last_dma_flag; /* flags last dma call in auto mode */ /* * we always say we xferred all the bytes, unless error * the plx chip can't read back the dma count anyway * * some idea of the residual after a failure can be read from the dr11 range counter */ retval = user_count; /* * extract inode pointer from file struct - we used to get this passed directly with * write call - pre 2.2 */ inode_p = file_p->f_dentry->d_inode; /* * get instance number and make unit struct pointer */ instance = MINOR(inode_p->i_rdev); DPRINT((KERN_DEBUG "idr_write: instance %d: entering write routine, user_buf_p = 0x%p, count = 0x%x\n", instance, user_buf_p, (unsigned int)user_count)); if ((instance < 0) || (instance >= idr_max_boards)) { printk(KERN_ERR "idr_write: instance %d: instance out of range!\n", instance); return -ENODEV; } unit_p = idr_unit_base_p + instance; /* * check to make sure we have been opened */ if (!unit_p->unit_open) { printk(KERN_ERR "idr_write: instance %d: write called when unit not open!\n", instance); return -EIO; } /* * clear the timeout, multicycle error, parity, sig received, and "waiting for" flags * strategy will set the waiting for dvma or eor flag. */ unit_p->unit_flags &= IDR_CLEAR_FLAGS; /* * clear the input flag (=output) */ unit_p->unit_flags &= ~IDR_INPUT; /* * buffer size and start address must be even (16 bit aligned) * and buffer size must be non-zero */ if ((unsigned long) user_buf_p & 1) { printk(KERN_WARNING "idr_write: instance %d: odd buffer address!\n", instance); return -EINVAL; } if ((user_count == 0) || (user_count & 1)) { printk(KERN_WARNING "idr_write: instance %d: zero or odd buffer size!\n", instance); return -EINVAL; } /* * if automatic mode, set up dr11 range count and set first_dma_flag * to indicate first call to strategy in auto mode * * first_dma_flag will be set back to 0 after first strategy call */ if (!(unit_p->unit_flags & IDR_MANUAL)) { DPRINT((KERN_DEBUG "idr_write: instance %d: auto mode, setting range counter to 0x%x (words - 1)\n", instance, (unsigned int)(user_count >> 1) - 1)); /* * dr11 counter wants WORD count minus 1 */ range = (user_count >> 1) - 1; if (range > IDR_DR11_MAXBLOCK) { printk(KERN_WARNING "idr_write: instance %d DR11 range count too large!\n", instance); return -EINVAL; } IDR_PUTL(IDR_RANGE_HIGH, range >> 16); IDR_PUTL(IDR_RANGE_MID, range >> 8); IDR_PUTL(IDR_RANGE_LOW, range); first_dma_flag = 1; /* * we have some bookkeeping to do that would normally be in strategy * but since we may have to call strategy multiple times, and we * only want to do this stuff once, we will have to do it here */ /* * in auto mode, pulse off any error flags (leave on for later code if manual) */ IDR_PUTL(IDR_116_PULSES, RESET_EOR_FLAG | RESET_ATTENTION_FLAG | RESET_ERROR_FLAGS); /* * force off idr dma enb and direction bit * then re-enable dma in output mode * * we don't fiddle interrupts here - that will be done in strategy */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~(IDR_DMA_ENABLE | DMA_INPUT_MODE)); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | IDR_DMA_ENABLE); } else { /* * in manual mode, just clear first_dma_flag * pulses will be issued by user via ioctl */ first_dma_flag = 0; } /* * loop on strategy while user_count > 0 * strategy expects the virtual address of the next chunk of the user buffer */ while (user_count > 0) { /* * use the min of remaining user count and max dma buffer buffer size * as the dma count */ dma_count = (user_count < idr_max_buf_bytes) ? user_count : idr_max_buf_bytes; DPRINT((KERN_DEBUG "idr_write: instance %d: inside while loop, buf_pointer = 0x%p, dma_count = 0x%x, first_dma_flag = 0x%x\n", instance, user_buf_p, dma_count, first_dma_flag)); /* * if auto mode and this is the last call to strategy required to satisfy user count, * set last_dma_flag - it is probably not going to be used by strategy for input * operations, but output will require it */ if ((!(unit_p->unit_flags & IDR_MANUAL)) && (dma_count >= user_count)) { DPRINT( (KERN_DEBUG "idr_write: instance %d: setting last_dma_flag to 1\n", instance)); last_dma_flag = 1; } else { DPRINT((KERN_DEBUG "idr_write: instance %d: setting last_dma_flag to 0\n", instance)); last_dma_flag = 0; } DPRINT((KERN_DEBUG "idr_write: instance %d: calling idr_strategy\n", instance)); strategy_retval = idr_strategy(unit_p, (char *) user_buf_p, dma_count, first_dma_flag, last_dma_flag); DPRINT((KERN_DEBUG "idr_write: instance %d: idr_strategy returns 0x%x\n", instance, strategy_retval)); if (strategy_retval) { printk(KERN_WARNING "idr_write: instance %d: idr_strategy returns error = %d\n", instance, strategy_retval); return strategy_retval; } /* * bump user buffer pointer, and decrement counter * force first_dma_flag off (if on) */ user_buf_p += dma_count; user_count -= dma_count; first_dma_flag = 0; } /* end of while(user_count) */ /* * if auto mode, make sure idr dma enb is off * * check for multicycle error and flag in unit structure * in manual mode, this stuff is done in block_end * * we will then look at the flags we just set, and return * errors, if appropriate * * (this looks weird, but it happened when some of the hardware * error checking moved here from strategy for the linux driver) * */ if (!(unit_p->unit_flags & IDR_MANUAL)) { IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE); if (IDR_GETL(IDR_FLAGS) & MULTI_CYCLE_ERROR_FLAG) { printk(KERN_WARNING "idr_write: instance %d: multi-cycle error!\n", instance); unit_p->unit_flags |= IDR_MCYL_ERR; } } /* * check flags for timeout or multicycle error or sig received * doesn't make any sense to test for parity error on output */ if (unit_p->unit_flags & (IDR_DVMA_TIMEOUT | IDR_EOR_TIMEOUT)) { printk(KERN_WARNING "idr_write: instance %d: DMA timeout! \n", instance); return -EIO; } if (unit_p->unit_flags & IDR_MCYL_ERR) { printk(KERN_WARNING "idr_write: instance %d: Multicycle error! \n", instance); return -EIO; } if (unit_p->unit_flags & IDR_SIG_RECEIVED) { printk(KERN_WARNING "idr_write: instance %d: signal received! \n", instance); return -EINTR; } DPRINT((KERN_DEBUG "idr_write: instance %d: leaving write routine, return value = 0x%x\n", instance, retval)); /* * retval should be original byte count if we get to this point */ return retval; } /* * provide mechanism for controlling device and driver * * THIS CODE USES A FEW ERROR RETURNS - IF WE SOMEDAY NEED A MUTEX OR EQUIVALENT, GO BACK TO * SOLARIS STYLE .. SET RETVAL CODES AND JUMP TO IOCTLEXIT, SO WE DON'T * RISK LEAKING OUT THE SIDE OF A MUTEX!! * * note that the ioctl routine treats arg as a pointer */ static int idr_ioctl(struct inode *inode_p, struct file *file_p, u_int cmd, u_long arg) { register struct idr_unit_t *unit_p; u_long bits; /* will contain incoming or outgoing arg value */ u_long lt; /* temp long */ u_int command; /* extracted ioctl command */ int count; /* size of argument */ int instance; /* our instance - from softstate */ int i; /* generic counter */ int wait_return; /* saved return value from conditional wait */ u_long return_array[IDR_RETURN_ARRAY_SIZE]; /* will contain copy of registers */ /* to return to caller */ /* * get instance and make unit struct pointer */ instance = MINOR(inode_p->i_rdev); DPRINT((KERN_DEBUG "idr_ioctl: instance %d: entering ioctl routine, command = 0x%x, arg = 0x%lx\n", instance, cmd, arg)); /* * make sure minor number is within valid range */ if ((instance < 0) || (instance >= idr_max_boards)) { printk(KERN_ERR "idr_ioctl: instance %d: instance out of range!\n", instance); return -ENODEV; } unit_p = idr_unit_base_p + instance; /* * check - probably needlessly - to make sure we have been opened */ if (!unit_p->unit_open) { printk(KERN_ERR "idr_ioctl: instance %d: ioctl called when unit not open!\n", instance); return -EIO; } /* * get command and argument size */ command = cmd & IDRIO_CMD_MASK; count = (cmd & IDRIO_COUNT_MASK) >> _IOC_SIZESHIFT; DPRINT((KERN_DEBUG "idr_ioctl: instance %d: masked command = 0x%x, argument size = 0x%x\n", instance, command, count)); /* * get first word of argument (even if we are eventually doing a * copyout operation) - this saves some typeing */ if (copy_from_user(&bits, (caddr_t) arg, sizeof(bits))) { printk(KERN_ERR "idr_ioctl: instance %d: copy_from_user error!\n", instance); return -EINVAL; } DPRINT((KERN_DEBUG "idr_ioctl: instance %d: arg = 0x%lx, *arg = 0x%lx\n", instance, arg, bits)); /* * use MASK to strip the count from cases */ #define MASK IDRIO_CMD_MASK switch (command) { case MASK & IDRIO_SET_MODE: /* set operating mode using old sbus bit */ /* definitions to preserve compatibility with */ /* existing applications */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at set mode\n", instance)); lt = bits & IDR_SPEED_MASK; /* speed bits are in the same place */ if (bits & IDR_RDYT) lt |= LONG_READY; if (bits & IDR_BDIS) lt |= DISABLE_CRQ_B; if (bits & IDR_CRQP) lt |= CYCLE_POLARITY; if (bits & IDR_BSYP) lt |= BUSY_POLARITY; if (bits & IDR_RDISX) lt |= DISABLE_RANGE; IDR_PUTL(IDR_MODE, lt); /* * check dma byte swap mode - request for swapping will turn off big endian * mode (SPARC) SPARC swaps bytes between dma and memory, so big_endian = swap * again, to give un-swapped data * x86 doesn't swap - so SWAP really means swap in the plx hardware */ if (bits & IDR_SWAP) PLX_PUTL(PLX_ENDIAN_REG, PLX_GETL(PLX_ENDIAN_REG) | DMA_1_BIG_ENDIAN); else PLX_PUTL(PLX_ENDIAN_REG, PLX_GETL(PLX_ENDIAN_REG) & ~DMA_1_BIG_ENDIAN); return 0; break; case MASK & IDRIO_IMM_FCN: /* set function bits immediately */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at immediate funtion\n", instance)); /* * just mask the function bits and stuff them directly into the latched * function register - the other register bits should all be either * zero or don't care at this point! * * the pci and sbus boards have the funtions bits in the same places * in their respective registers - so no bit fiddling necessary */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, bits & FUNCTION_MASK); return 0; break; case MASK & IDRIO_READ_FCN: /* save fcn bits for use at read start */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at read function\n", instance)); unit_p->read_fcn = (bits & FUNCTION_MASK); return 0; break; case MASK & IDRIO_WRITE_FCN: /* save fcn bits for use at write start */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at write function\n", instance)); unit_p->write_fcn = (bits & FUNCTION_MASK); return 0; break; case MASK & IDRIO_IMM_PULSE: /* issue pulses asap */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at immediate pulse\n", instance)); /* * wait for attention off if issuing go pulse * wait 100 us max - ?????? */ if (bits & IDR_GO) { i = 0; while ((IDR_GETL(IDR_STATUS) & ATTENTION) && (i < 100)) { udelay(1); i++; } if (IDR_GETL(IDR_STATUS) & ATTENTION) { printk(KERN_WARNING "idr_ioctl: instance %d at IMM_PULSE: attempt to issue GO while ATTENTION true!\n", instance); return -EIO; } } /* * fiddle bits to maintain sbus driver compatibility */ lt = 0; if (bits & IDR_TERM) lt |= SET_READY; if (bits & IDR_INIT) lt |= DEVICE_INIT; if (bits & IDR_ACF2) lt |= ACLO_FCN_2; if (bits & IDR_CYCL) lt |= SOFT_CYCLE; if (bits & IDR_GO) lt |= GO; IDR_PUTL(IDR_DEVICE_PULSES, lt); /* * we will allow a master clear pulse here - since the sbus driver did * but it should not be used lightly * * it clears the idr logic only, not the plx logic * * we will also permit a reset attention flag pulse - for compatibility */ lt = 0; if (bits & IDR_RATN) lt |= RESET_ATTENTION_FLAG; if (bits & IDR_MCLR) lt |= MASTER_CLEAR; IDR_PUTL(IDR_116_PULSES, lt); return 0; break; case MASK & IDRIO_READ_PULSE: /* save pulses for read start (in pci format) */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at read pulse\n", instance)); /* * fiddle bits to maintain sbus driver compatibility */ lt = 0; if (bits & IDR_TERM) lt |= SET_READY; if (bits & IDR_INIT) lt |= DEVICE_INIT; if (bits & IDR_ACF2) lt |= ACLO_FCN_2; if (bits & IDR_CYCL) lt |= SOFT_CYCLE; if (bits & IDR_GO) lt |= GO; unit_p->read_pulse = lt; return 0; break; case MASK & IDRIO_WRITE_PULSE: /* save pulses for write start */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at write pulse\n", instance)); /* * fiddle bits to maintain sbus driver compatibility */ lt = 0; if (bits & IDR_TERM) lt |= SET_READY; if (bits & IDR_INIT) lt |= DEVICE_INIT; if (bits & IDR_ACF2) lt |= ACLO_FCN_2; if (bits & IDR_CYCL) lt |= SOFT_CYCLE; if (bits & IDR_GO) lt |= GO; unit_p->write_pulse = lt; return 0; break; case MASK & IDRIO_SET_DMA_TIME: /* set dma timeout parameter - in ticks */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at set dma time\n", instance)); if (bits < DMA_TIME_MIN) bits = DMA_TIME_MIN; /* not less than min */ if (bits > DMA_TIME_MAX) bits = DMA_TIME_MAX; /* or more than max */ unit_p->dma_time = (u_long) (bits * HZ); return 0; break; case MASK & IDRIO_SET_ATTN_TIME: /* set wait for attention timeout value */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at set attn time\n", instance)); if (bits < ATTN_TIME_MIN) bits = ATTN_TIME_MIN; if (bits > ATTN_TIME_MAX) bits = ATTN_TIME_MAX; unit_p->attn_time = (u_long) (bits * HZ); return 0; break; case MASK & IDRIO_SET_RDY_TIME: /* set wait for ready timeout value */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at set rdy time\n", instance)); if (bits < RDY_TIME_MIN) bits = RDY_TIME_MIN; if (bits > RDY_TIME_MAX) bits = RDY_TIME_MAX; unit_p->rdy_time = (u_long) (bits * HZ); return 0; break; case MASK & IDRIO_ATTN_WAIT: /* wait for attention to set attf */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at attn wait\n", instance)); /* * clear the "waiting-for" flags in the soft state * these flags are maintained for compatibility with the sbus driver * they are probably not necessary */ unit_p->unit_flags &= IDR_CLEAR_FLAGS; /* * if the attention flag is already set, clear it and return */ if (IDR_GETL(IDR_FLAGS) & ATTENTION_FLAG) { DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at attn wait, attf already set, returning w/out waiting\n", instance)); IDR_PUTL(IDR_116_PULSES, RESET_ATTENTION_FLAG); return 0; } /* * save processor flags, disable interrupts, and take spinlock w/mutex */ mutex_lock(&unit_p->mutex); /* * set "waiting-for" flag */ unit_p->unit_flags |= IDR_ATTN_WAIT; /* * set sleeping flag, so we can detect an abnormal return from sleep */ unit_p->sleeping = 1; /* * enable attention flag interrupts in the idr logic, and local ints in the plx logic * we have to or in the idr bits, since the speed bits are * also in that register */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | ATTENTION_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE); /* * snooze until attention interrupt, timeout, or signal */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at attn wait, time_now = %ld, unit_p->attn_time = %ld\n", instance, jiffies, unit_p->attn_time)); /* * it looks like (from kernel source) that this returns 0 ir timeout, >0 if not */ wait_return = cond_timed_wait_rel(&unit_p->cond, &unit_p->mutex, unit_p->attn_time); /* * make sure interrupt enables are off - we may have gotten here * via signal or timeout */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~ATTENTION_INT_ENB); PLX_PUTL(PLX_INT_CSTAT, 0); /* * re-enable interrupts (if previously on) * and release spin lock via mutex */ mutex_unlock(&unit_p->mutex); /* * find out why we woke up */ if (unit_p->sleeping) { unit_p->sleeping = 0; DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at wait for attention, abnormal return from cond_timed_wait_rel!\n", instance)); /* * check for timeout */ if (wait_return == COND_WAIT_TIMEOUT) { /* * timeout may not be an error - so no error msg * let caller test flags for timeout */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: timeout waiting for attention!\n", instance)); unit_p->unit_flags |= IDR_ATTN_TIMEOUT; return -EIO; } /* * check for signal */ if (wait_return == COND_WAIT_SIGNAL) { /* * signal is not exactly an error - but send to console log * return EINTR to caller can decide what to do */ printk(KERN_WARNING "idr_ioctl: instance %d: signal while waiting for attention!\n", instance); unit_p->unit_flags |= IDR_SIG_RECEIVED; return -EINTR; } } /* end of if sleeping */ /* * clear attention flag */ IDR_PUTL(IDR_116_PULSES, RESET_ATTENTION_FLAG); return 0; break; case MASK & IDRIO_RDY_WAIT: /* wait for REDY to go true from attn or eor */ /* if already set - just return */ /* look at flags to figure out why redy was */ /* set. if we were waiting for eor and got */ /* an attention also - the attn flag is left */ /* on since it might represent an incoming */ /* attention that followed the block xfer */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at ready wait\n", instance)); unit_p->unit_flags &= IDR_CLEAR_FLAGS; if (!(IDR_GETL(IDR_STATUS) & READY)) { DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at ready wait, board not already ready\n", instance)); /* * save processor flags, disable interrupts, and take spinlock w/mutex */ mutex_lock(&unit_p->mutex); /* * set "waiting-for" flag */ unit_p->unit_flags |= IDR_RDY_WAIT; /* * set sleeping flag, so we can detect an abnormal return from sleep */ unit_p->sleeping = 1; /* * enable attention and eor flag interrupts in the idr logic, and * local ints in the plx logic * we have to or in the idr bits, since the speed bits are * also in that register */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, (IDR_GETL(IDR_LATCHED_FUNCTIONS) | ATTENTION_INT_ENB | END_OF_RANGE_INT_ENB)); PLX_PUTL(PLX_INT_CSTAT, (PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE)); /* * snooze until attention interrupt, timeout, or signal */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at ready wait, time_now = %ld, unit_p->rdy_time = %ld\n", instance, jiffies, unit_p->rdy_time)); wait_return = cond_timed_wait_rel(&unit_p->cond, &unit_p->mutex, unit_p->rdy_time); /* * make sure interrupt enables are off - we may have gotten here * via signal or timeout */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~(ATTENTION_INT_ENB | END_OF_RANGE_INT_ENB)); PLX_PUTL(PLX_INT_CSTAT, 0); /* * re-enable interrupts (if previously on) * and release spin lock via mutex */ mutex_unlock(&unit_p->mutex); /* * find out why we woke up */ if (unit_p->sleeping) { DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at rdy wait, abnormal return from cond_timed_wait_rel!\n", instance)); /* * check for timeout */ if (wait_return == COND_WAIT_TIMEOUT) { /* * timeout may not be an error - so no error msg * let caller test flags for timeout */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: timeout waiting for ready!\n", instance)); unit_p->unit_flags |= IDR_RDY_TIMEOUT; return -EIO; } /* * check for signal */ if (COND_WAIT_SIGNAL) { /* * signal is not exactly an error - but send to console log * return EINTR to caller can decide what to do */ printk(KERN_WARNING "idr_ioctl: instance %d: signal while waiting for ready!\n", instance); unit_p->unit_flags |= IDR_SIG_RECEIVED; return -EINTR; } } /* end of if sleeping */ } /* end of if not ready */ else { DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at ready wait, board already ready\n", instance)); } /* * either board was ready when case was entered, or it is ready now * after sleeping, so ... * * examine dr11 flag bits to figure out why ready was set * if attf is set and eor is not - clear attf */ if ((IDR_GETL(IDR_FLAGS) & ATTENTION_FLAG) && !(IDR_GETL(IDR_FLAGS) & END_OF_RANGE_FLAG)) { DPRINT( (KERN_DEBUG "idr_ioctl: instance %d: at ready wait - attf w/out eorf\n", instance)); IDR_PUTL(IDR_116_PULSES, RESET_ATTENTION_FLAG); } /* * always clear eorf */ IDR_PUTL(IDR_116_PULSES, RESET_EOR_FLAG); return 0; break; case MASK & IDRIO_GET_STATUS: /* return dr11 status register - in sbus format */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at get status\n", instance)); /* * get flags and status registers and fiddle bits * to look like sbus status register */ bits = 0; lt = IDR_GETL(IDR_FLAGS); if (lt & END_OF_RANGE_FLAG) bits |= IDR_EORF; if (lt & ATTENTION_FLAG) bits |= IDR_ATTF; if (lt & MULTI_CYCLE_ERROR_FLAG) bits |= IDR_MCER; lt = IDR_GETL(IDR_STATUS); if (lt & READY) bits |= IDR_REDY; if (lt & ATTENTION) bits |= IDR_ATTN; /* * dr11 status bits are in the same place in sbus and pci */ bits |= (lt & STATUS_MASK); if (copy_to_user((caddr_t) arg, &bits, count)) { printk(KERN_ERR "idr_ioctl: instance %d: copy_to_user error!\n", instance); return -EINVAL; } return 0; break; case MASK & IDRIO_GET_RANGE: /* gets 24 bit range counter value */ /* in raw form: = word count minus 1 */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at get range\n", instance)); bits = ((IDR_GETL(IDR_RANGE_HIGH) & 0xFF) << 16) | ((IDR_GETL(IDR_RANGE_MID) & 0xFF) << 8) | (IDR_GETL(IDR_RANGE_LOW) & 0xFF); if (copy_to_user((caddr_t) arg, &bits, count)) { printk(KERN_ERR "idr_ioctl: instance %d: copy_to_user error!\n", instance); return -EINVAL; } return 0; break; case MASK & IDRIO_GET_REGS: /* get most of the board's registers */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at get regs\n", instance)); return_array[0] = unit_p->dev_and_vendor_id; /* device and vendor id */ return_array[1] = unit_p->revision_id; /* revision id */ return_array[2] = PLX_GETL(PLX_INT_CSTAT); /* interrupt control-status */ return_array[3] = PLX_GETL(PLX_EEPROM_USER); /* eeprom control and user bits */ return_array[4] = PLX_GETL(PLX_DMA_MODE_1); /* dma mode reg 1 */ return_array[5] = PLX_GETL(PLX_DMA_PCI_ADD_1); /* dma pci add 1 */ return_array[6] = PLX_GETL(PLX_DMA_LOC_ADD_1); /* dma local add 1 */ return_array[7] = PLX_GETL(PLX_DMA_COUNT_1); /* dma transfer count 1 */ return_array[8] = PLX_GETL(PLX_DMA_DESC_PTR_1); /* dma descriptor pointer 1 */ return_array[9] = PLX_GETL(PLX_DMA_CMD_STAT_BOTH); /* dma cmd/stat - both channels */ return_array[11] = IDR_GETL(IDR_FLAGS) & 0xFF; /* idr flags register */ return_array[12] = IDR_GETL(IDR_STATUS) & 0xFF; /* idr status register */ return_array[13] = IDR_GETL(IDR_MODE) & 0xFF; /* idr mode register */ return_array[14] = IDR_GETL(IDR_RANGE_LOW) & 0xFF; /* dr11 range register low byte */ return_array[15] = IDR_GETL(IDR_RANGE_MID) & 0xFF; /* dr11 range register mid byte */ return_array[16] = IDR_GETL(IDR_RANGE_HIGH) & 0xFF; /* dr11 range register hi byte */ return_array[17] = IDR_GETL(IDR_FIFO_STATUS) & 0xFF; /* idr fifo status bits */ /* * reading the data in lines requires clearing the input fifo, * forcing a dr11 input word into the input fifo, * and then reading in * * use the latched function read to guarantee time between the * write_input_fifo write and the data in read */ IDR_PUTL(IDR_116_PULSES, CLEAR_INPUT_FIFO); IDR_PUTL(IDR_116_PULSES, WRITE_INPUT_FIFO); return_array[10] = IDR_GETL(IDR_LATCHED_FUNCTIONS) & 0xFF; /* idr latched fcn reg */ return_array[18] = IDR_GETL(IDR_DATA_IN) & 0xFFFF; /* 16 bit data in reg */ if (copy_to_user((caddr_t) arg, return_array, count)) { printk(KERN_ERR "idr_ioctl: instance %d: copy_to_user error!\n", instance); return -EINVAL; } return 0; break; case MASK & IDRIO_GET_FLAGS: /* return unit flags longword to caller */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at get flags, flags = 0x%lx\n", instance, unit_p->unit_flags)); bits = unit_p->unit_flags; if (copy_to_user((caddr_t) arg, &bits, count)) { printk(KERN_ERR "idr_ioctl: instance %d: copy_to_user error!\n", instance); return -EINVAL; } return 0; break; case MASK & IDRIO_DATA_OUT: /* output 16 bit word to data out reg */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at data out, data = 0x%lx\n", instance, bits)); /* * first we clear the output fifo, then write to it, then force a read from it * into the output data latches * * we use a throwaway read to add some time before read output fifo cmd - * w/heavy pci traffic (DMA??) the writes and the "read" may stack * (somewhere) and the read may happed before the fifo is quite * ready */ IDR_PUTL(IDR_116_PULSES, CLEAR_OUTPUT_FIFO); IDR_PUTL(IDR_DATA_OUT, bits); read_dump_0 = IDR_GETL(IDR_LATCHED_FUNCTIONS); IDR_PUTL(IDR_116_PULSES, READ_OUTPUT_FIFO); return 0; break; case MASK & IDRIO_DATA_IN: /* read dr11 input data register */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at data in\n", instance)); /* * clear the input fifo, force a write into it from the data in lines * then read the fifo * * we use a throwaway read to add some time before the real read - * w/heavy pci traffic (DMA??) the writes and the read may stack * (somewhere) and the read may happed before the fifo is quite * ready */ IDR_PUTL(IDR_116_PULSES, CLEAR_INPUT_FIFO); IDR_PUTL(IDR_116_PULSES, WRITE_INPUT_FIFO); read_dump_0 = IDR_GETL(IDR_LATCHED_FUNCTIONS); bits = IDR_GETL(IDR_DATA_IN) & 0xFFFF; if (copy_to_user((caddr_t) arg, &bits, count)) { printk(KERN_ERR "idr_ioctl: instance %d: copy_to_user error!\n", instance); return -EINVAL; } return 0; break; case MASK & IDRIO_SET_RANGE: /* set dr11 range counter to desired value */ /* count is not adjusted - calling argument */ /* will be plugged directly into range */ /* arg should be word count minus one!!! */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at set range, range = 0x%lx\n", instance, bits)); /* * make sure that requested count will fit in our range counter */ if (bits > IDR_DR11_MAXBLOCK) { printk(KERN_WARNING "idr_ioctl: instance %d at SET_RANGE - range count too big! \n", instance); return -EINVAL; } IDR_PUTL(IDR_RANGE_HIGH, (bits >> 16)); IDR_PUTL(IDR_RANGE_MID, (bits >> 8)); IDR_PUTL(IDR_RANGE_LOW, bits); return 0; break; case MASK & IDRIO_AUTO: /* set automatic mode (default) */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at idr auto\n", instance)); unit_p->unit_flags &= ~IDR_MANUAL; return 0; break; case MASK & IDRIO_MANUAL: /* set manual mode */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at idr manual\n", instance)); unit_p->unit_flags |= IDR_MANUAL; return 0; break; case MASK & IDRIO_START_READ: /* enables block read in manual mode */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at start read\n", instance)); /* * make sure that the "end" flags are clear * and reset error flags * also clear the input fifo */ IDR_PUTL(IDR_116_PULSES, (RESET_ATTENTION_FLAG | RESET_EOR_FLAG | RESET_ERROR_FLAGS | CLEAR_INPUT_FIFO)); /* * make sure dma rqs are off, switch to input mode, and enable dma * * the plx dma logic will be set up and enabled by strategy */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | DMA_INPUT_MODE); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | IDR_DMA_ENABLE); return 0; break; case MASK & IDRIO_START_WRITE: /* enables block write in man mode */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at start write\n", instance)); /* * make sure "end" flags are clean * and reset error flags and output fifo */ IDR_PUTL(IDR_116_PULSES, (RESET_ATTENTION_FLAG | RESET_EOR_FLAG | RESET_ERROR_FLAGS | CLEAR_OUTPUT_FIFO)); /* * make sure dma rqs are off, switch to output mode, and enable dma * * the plx dma logic will be set up and enabled by strategy */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~DMA_INPUT_MODE); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | IDR_DMA_ENABLE); return 0; break; case MASK & IDRIO_BLOCK_END: /* terminate overall block transfer */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at block end\n", instance)); /* * turn off dma enable bit and interrupt masks * interrupt masks should already be off */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~(END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB | IDR_DMA_ENABLE)); /* * make sure ready is true */ IDR_PUTL(IDR_DEVICE_PULSES, SET_READY); /* * check for multicycle error or parity error and flag in unit structure * * parity error will occur frequently when connected to device that don't * support parity (most!) so don't do a cmn_err print */ if (IDR_GETL(IDR_FLAGS) & PARITY_ERROR_FLAG) { DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at BLOCK_END, parity error!\n", instance)); unit_p->unit_flags |= IDR_PAR_ERR; } if (IDR_GETL(IDR_FLAGS) & MULTI_CYCLE_ERROR_FLAG) { printk(KERN_WARNING "idr_ioctl: instance %d: at BLOCK_END, multi-cycle error!\n", instance); unit_p->unit_flags |= IDR_MCYL_ERR; } /* * just to be sure, force off plx dma and interrupt enable bits */ PLX_PUTL(PLX_INT_CSTAT, 0); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); return 0; break; case MASK & IDRIO_DEV_AND_VEND_ID: /* return vendor id in low 16 bits, */ /* device id in high 16 bits */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at dev and vend id\n", instance)); bits = unit_p->dev_and_vendor_id; if (copy_to_user((caddr_t) arg, &bits, count)) { printk(KERN_ERR "idr_ioctl: instance %d: copy_to_user error!\n", instance); return -EINVAL; } return 0; break; case MASK & IDRIO_REVISION_ID: /* return board revision id */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at revision id\n", instance)); bits = unit_p->revision_id; if (copy_to_user((caddr_t) arg, &bits, count)) { printk(KERN_ERR "idr_ioctl: instance %d: copy_to_user error!\n", instance); return -EINVAL; } return 0; break; case MASK & IDRIO_SET_NEW_MODE: /* direct write to pci dr11 mode register */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at set new mode\n", instance)); IDR_PUTL(IDR_MODE, bits); return 0; break; case MASK & IDRIO_GET_NEW_STATUS: /* direct read of pci dr11 status register */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at get new status\n", instance)); bits = IDR_GETL(IDR_STATUS) & 0xFF; if (copy_to_user((caddr_t) arg, &bits, count)) { printk(KERN_ERR "idr_ioctl: instance %d: copy_to_user error!\n", instance); return -EINVAL; } return 0; break; case MASK & IDRIO_GET_NEW_FLAGS: /* direct read of pci dr11 flags register */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at get new flags\n", instance)); bits = IDR_GETL(IDR_FLAGS) & 0xFF; if (copy_to_user((caddr_t) arg, &bits, count)) { printk(KERN_ERR "idr_ioctl: instance %d: copy_to_user error!\n", instance); return -EINVAL; } return 0; break; case MASK & IDRIO_MASTER_CLEAR: /* do a total reset of the board - don't try this at home! */ DPRINT((KERN_DEBUG "idr_ioctl: instance %d: at master clear\n", instance)); /* * call soft reset to completely reset the board - including the dr11 mode register * this will also cause an init pulse to be sent to the attached device */ plx_soft_reset(unit_p); return 0; break; default: /* * an unrecognized ioctl command may not be an error - we may * get probed by pipe logic w/ a generic TCGETA ioctl. * return an error so we don't look like a terminal, but * don`t print an error. */ return -EINVAL; break; } } /* * our interrupt handler - registered with the system in init_module * each board's instance will register the same handler, but with a different unit structure pointer * when called, dev_id is a pointer at the soft state structure for this instance * * as of the 2.3.x kiobuf version, we use mutex and conditional variable for SMP compatibility * 2.6.x adds an interrupt claimed return value */ static irqreturn_t idr_intr(int irq, void *dev_id, struct pt_regs *regs) { struct idr_unit_t *unit_p; /* will point at our soft state */ volatile u_long temp; /* just what it says */ int instance; /* this board's minor number */ int wake_up_needed; /* 1 if wake up needed */ /* * get the unit struct pointer this way to avoid parametere type complaints by the compiler * get instance for debugging * clear wake up needed flag */ unit_p = (struct idr_unit_t *) dev_id; instance = unit_p->instance; wake_up_needed = 0; /* * grab mutex - in case strategy and int thread are running on different cup's ??? */ mutex_lock(&unit_p->mutex); /* * get plx interrupt control/status register and check for interrupt * look for master interrupt enable bit and either local int rq or dma int rq */ temp = PLX_GETL(PLX_INT_CSTAT); if ((temp & PCI_INTERRUPT_ENABLE) && ((temp & LOCAL_INTERRUPT) || (temp & DMA_1_INTERRUPT))) { DPRINT((KERN_DEBUG "idr_intr: instance %d: interrupt claimed, unit_p = 0x%p\n", instance, unit_p)); /* * clear sleeping flag so sleeping process will know we got a normal interrupt * set wake up needed flag */ unit_p->sleeping = 0; wake_up_needed = 1; /* * turn off interrupts now - but leave dma enabled if it is * not already off * * in input mode, it is possible to terminate a block before the * dr11 or dma range count is exhausted, by pulsing attention * * the idr portion of the logic will hold off the attention * interrupt until the big fifo is empty, but there may still be * a few long words in the plx fifo * * we will leave dma on (if it is still on) in the plx logic, * and turn it off later in strategy * */ PLX_PUTL(PLX_INT_CSTAT, 0); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~(END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB)); /* * figure out why we were waiting for an interrupt * unfortunately, we can't treat all interrupts the same way */ switch (unit_p-> unit_flags & (IDR_DVMA_WAIT | IDR_EOR_WAIT | IDR_ATTN_WAIT | IDR_RDY_WAIT)) { case IDR_DVMA_WAIT: DPRINT((KERN_DEBUG "idr_intr: instance %d: at case dvma wait\n", instance)); /* * we are waiting for dma t/c interrupt - attention interrupts will also * be enabled, so we will have to figure out what caused the interrupt * and whether the transfer is complete */ case IDR_EOR_WAIT: DPRINT((KERN_DEBUG "idr_intr: instance %d: at case eor wait\n", instance)); /* * or we are waiting for a dr11 end of range * in auto output mode, eor is the desired interrupt * in input mode, or manual mode (either in or out), dma tc * is the desired interrupt */ /* * in the solaris driver we would turn off idr dma enb if * we are in automatic mode, but since the linux driver * may use several dma blocks to satisfy a user count, * we leave it on here, and turn it off (if automatic) * in read or write * * solaris code was: * * if(!(unit_p->unit_flags & IDR_MANUAL)) * IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) * & ~IDR_DMA_ENABLE); */ /* * if we are waiting for an output interrupt, force the plx dma logic * off, since either we are done with dma, or an attention has terminated * the dr11 transfer, and any further output data is destined for the * bit bucket * * if we are doing input, the attention or eor interrupt will be held * off until the big fifos are empty, but there may still be a few * long words in the plx fifos, so leave dma on for now - strategy * will clean things up */ if (!(unit_p->unit_flags & IDR_INPUT)) PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); if (! ((PLX_GETL (PLX_DMA_CMD_STAT_BOTH) & DMA_1_DONE) || (IDR_GETL(IDR_FLAGS) & END_OF_RANGE_FLAG))) { /* * if not eor or end of range, we must have been terminated by * an attention pulse - in this case, do not save the attn flag */ IDR_PUTL(IDR_116_PULSES, RESET_ATTENTION_FLAG); } if (unit_p->unit_flags & IDR_EOR_WAIT) { /* * if waiting for dr11 eor, turn it off - if not, it * may be useful later, so leave it on for now */ IDR_PUTL(IDR_116_PULSES, RESET_EOR_FLAG); } /* * turn off "waiting for" flags for dma t/c and eor */ unit_p->unit_flags &= ~(IDR_DVMA_WAIT | IDR_EOR_WAIT); break; case IDR_ATTN_WAIT: DPRINT((KERN_DEBUG "idr_intr: instance %d: at case attn wait\n", instance)); /* * we are waiting for an attention interrupt * i would like to make sure attf is set, but there * is no way to flag an error * so we will * just reset the "waiting for" flag * * the attention flag gets reset in the ioctl code * we could do it here, but we will stick with the * way it was done in the Sbus driver */ unit_p->unit_flags &= ~IDR_ATTN_WAIT; break; case IDR_RDY_WAIT: DPRINT((KERN_DEBUG "idr_intr: instance %d: at case rdy wait\n", instance)); /* * waiting for ready - either attention or eor * like attn wait, we could check for either flag set * and report an error if neither was set (serious * hardware error) but there is no good way to do so * * we will just turn off the "waiting for" flag * to try to keep some similarity with the Sbus * driver */ unit_p->unit_flags &= ~IDR_RDY_WAIT; break; default: /* * if we got here, we claimed the interrupt flag, but no "waiting" flag was set * this is a major error -- all we can do is pump out a message, and we * probably better issue a wakeup anyway */ printk(KERN_ERR "idr_intr: instance %d: got interrupt while not waiting!\n", instance); break; } } /* * release mutex * if we claimed this int, wake up waiting code and tell kernel */ mutex_unlock(&unit_p->mutex); if (wake_up_needed) { DPRINT((KERN_DEBUG "idr_intr: instance %d: claiming int and waking up sleeping process\n", instance)); cond_broadcast(&unit_p->cond); return IRQ_HANDLED; } else { DPRINT((KERN_DEBUG "idr_intr: instance %d: interrupt not claimed\n", instance)); return IRQ_NONE; } } /* * call strategy with the virtual address of the user buffer * it must be 16 bit aligned and the byte count must be even * * if we are in user buffer DMA mode, strategy will map and pin the buffer, construct * an sg list of buffer pages, map the pages to the bus (using the dynamic mapping stuff), * build the PLX scatter-gather list in iopb memory, synch the iopb memory, and start the * dma block transfer * * if we are in copy user buffer mode ( machine has >4G of ram) strategy will copy data * to/from a kernel buffer, and do DMA from that buffer * * if the first_dma_flag argument is non-zero, strategy will set the function bits and issue * pulses immediately after starting dma if input, and before starting dma if output * to avoid long gaps between go and dma on in input mode - which might allow a very fast * device to overflow the input buffer before dma starts (if linux interrupts for something * else between go and dma enb, and causes a long delay) * * this must only be done for the FIRST call to strategy for a given read or write buffer, * and will only be done if in automatic mode * * it is up to the read/write code to set this flag for the first call to strategy ONLY * * if the last_dma_flag argument is non-zero, strategy will wait for eor in output mode * if zero, strategy will wait for dma done in output mode, since multiple calls to strategy * will be made before eor is reached * * THERE ARE SOME ERROR AND OTHER RETURNS IN THIS CODE - IF WE EVER NEED A MUTEX, GATHER THEM WITH GOTOs, SO * WE WON'T ESCAPE OUT THE SIDE OF THE MUTEX */ static int idr_strategy(struct idr_unit_t *unit_p, char *dma_buf_p, int dma_count, int first_dma_flag, int last_dma_flag) { int instance; /* our instance (minor) number */ int idr_input; /* direction flag, 1=input */ u_long plx_direction; /* plx dma transfer direction */ u_long plx_dma_done_int; /* plx dma done int enable bit */ u_long plx_local_dma_int; /* plx local dma int enable bit */ int wait_return; /* saved value returned from cond_wait */ u_long temp; /* utility temp */ u_long temp_mode; /* temp idr mode */ u_long temp_latches; /* temp idr latches */ int i; /* wait loop counter */ /* * get instance number from unit structure */ instance = unit_p->instance; DPRINT((KERN_DEBUG "idr_strategy: instance %d: entering strategy, unit_p = 0x%p, buf_p = 0x%p, count = 0x%x, first_dma_flag = 0x%x, last_dma_flag = 0x%x\n", instance, unit_p, dma_buf_p, dma_count, first_dma_flag, last_dma_flag)); /* * make sure dma byte count size is non-zero and count and buf address are even (word aligned) * this is not strictly necessary, but makes for a more generic strategy routine * and check for max allowable size */ if ((u_long) dma_buf_p & 0x1) { printk(KERN_WARNING "idr_strategy: instance %d: buffer not 16 bit aligned!\n", instance); return -EINVAL; } if (dma_count == 0) { printk(KERN_WARNING "idr_strategy: instance %d: dma count zero!\n", instance); return -EINVAL; } if (dma_count & 0x1) { printk(KERN_WARNING "idr_strategy: instance %d: dma count not even!\n", instance); return -EINVAL; } if (dma_count > idr_max_buf_bytes) { printk(KERN_WARNING "idr_strategy: instance %d: dma count too large!\n", instance); return -EINVAL; } /* * set local input flag, plx direction, and save in unit struct */ if (unit_p->unit_flags & IDR_INPUT) { idr_input = 1; plx_direction = DMA_INPUT; } else { idr_input = 0; plx_direction = 0; } /* * interrupts enabled and other housekeeping differ in auto and manual modes */ if (!(unit_p->unit_flags & IDR_MANUAL)) { DPRINT((KERN_DEBUG "idr_strategy: instance %d: in auto mode\n", instance)); if (idr_input) { DPRINT((KERN_DEBUG "idr_strategy: instance %d: auto read mode, setting dvma_wait\n", instance)); /* * if first_dma_flag is on, set read function bits to pre-selected pattern * and clear the input fifo (in manual mode, that is done in start read) */ if (first_dma_flag) { DPRINT((KERN_DEBUG "idr_strategy: instance %d: setting read function bits to 0x%lx\n", instance, unit_p->read_fcn)); IDR_PUTL(IDR_LATCHED_FUNCTIONS, (IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~FUNCTION_MASK) | unit_p-> read_fcn); IDR_PUTL(IDR_116_PULSES, CLEAR_INPUT_FIFO); } /* * in auto read mode, set direction to input * and enable attention interrupt, but not eor interrupt */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | DMA_INPUT_MODE | ATTENTION_INT_ENB); /* * set to dma t/c wait - the one to use for input mode */ unit_p->unit_flags |= IDR_DVMA_WAIT; } else { DPRINT((KERN_DEBUG "idr_strategy: instance %d: auto write mode\n", instance)); /* * if first_dma_flag is on, set write function bits to pre-selected patern * and clear the output fifo (in manual mode, that is done in start write) */ if (first_dma_flag) { DPRINT((KERN_DEBUG "idr_strategy: instance %d: setting write function bits to 0x%lx\n", instance, unit_p->write_fcn)); IDR_PUTL(IDR_LATCHED_FUNCTIONS, (IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~FUNCTION_MASK) | unit_p-> write_fcn); IDR_PUTL(IDR_116_PULSES, CLEAR_OUTPUT_FIFO); } /* * if last_dma_flag is on, enb eor and attn ints, if not, use dma done and attn ints */ if (last_dma_flag) { /* * automatic write mode - leave input mode bit off * enable eor or attention interrupt since last dma */ DPRINT((KERN_DEBUG "idr_strategy: instance %d: auto write mode & last dma, using eor and attn ints\n", instance)); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB); /* * since automatic output and last dma, wait for end-of-range */ unit_p->unit_flags |= IDR_EOR_WAIT; } else { /* * auto write mode - leave input mode bit off * enable attn ints only (here - dma will be enabled in later code) since not last dma */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | ATTENTION_INT_ENB); /* * set to dma t/c wait - the one to use for output mode if not last dma */ unit_p->unit_flags |= IDR_DVMA_WAIT; } } /* end of if not input */ } /* end of if not manual mode */ else { DPRINT((KERN_DEBUG "idr_strategy: instance %d: manual mode, setteng dvma_wait\n", instance)); /* * manual mode - always wait for dma terminal count * and always enable attention interrupt * * direction and idr dma enable bit will be set in start read or start write */ unit_p->unit_flags |= IDR_DVMA_WAIT; IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) | ATTENTION_INT_ENB); } /* * the error flags are not cleared here so they will persist for multiple * calls to strategy by physio */ /* * we assume that we will always be issuing a go here if in automatic mode * and the first_dma_flag is set (1st call from read or write) * in manual mode, issuing go is up to the calling program * * we will issue the dr11 pulses before starting dma if we are doing output, * and after starting dma if doing input * * the reasoning is as follows: if we do the go before dma is started for input, * a very fast device might fill our fifos before we could get dma started -- very * unlikely with 16K fifos, but there is a small chance that our code could be * pre-empted for a long time * * if we start dma first for output, the bus will get very busy, and it is not clear * that the p-i/o accesses necessary to issue the function and go bits will get through * (this was a serious problem on the sbus - probably not an issue with plx on pci) */ if ((first_dma_flag) && !(unit_p->unit_flags & IDR_MANUAL)) { /* * test for attention stuck on - it will prevent the block * from starting if it is on - allow some time for a software- * generated pulse from the other end to finish * * do it here since we need to check for both input and output * modes - in automatic mode */ i = 0; while ((IDR_GETL(IDR_STATUS) & ATTENTION) && (i < 100)) { udelay(1); i++; } if (IDR_GETL(IDR_STATUS) & ATTENTION) { printk(KERN_WARNING "idr_strategy: instance %d attempt to issue GO while ATTENTION true!\n", instance); /* * DMON may be on at this point - shut down everything!! */ IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~(IDR_DMA_ENABLE | END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB)); return -EIO; } if (!idr_input) { /* * auto write issues pulses before dma starts * if first_dma_flag on!! */ DPRINT((KERN_DEBUG "idr_strategy: instance %d: auto write & first_dma_flag on - issuing GO before starting dma\n", instance)); IDR_PUTL(IDR_DEVICE_PULSES, unit_p->write_pulse | GO); } } /* * disable dma done interrupt if auto out mode, and last dma flag is set * otherwise, use dma done ints (for intermediate dma blocks in manual or auto, or input mode) */ if ((unit_p->unit_flags & IDR_MANUAL) || idr_input || (!last_dma_flag)) { DPRINT( (KERN_DEBUG "idr_strategy: instance %d: manual read/write or auto read or not last dma, enabling dma done interrupt\n", instance)); plx_dma_done_int = DMA_DONE_INTERRUPT_ENB; plx_local_dma_int = LOCAL_DMA_1_INT_ENABLE; } else { DPRINT((KERN_DEBUG "idr_strategy: instance %d: auto write & last_dma_flag, disabling dma done interrupt\n", instance)); plx_dma_done_int = 0; plx_local_dma_int = 0; } /* * map and pin - or copy - user buffer */ if(idr_map_or_copy_buf(unit_p, instance, dma_buf_p, dma_count, idr_input ? READ : WRITE)) { printk(KERN_WARNING "idr_strategy: instance %d: idr_map_or_copy_failure!\n", instance); return -EIO; } if(idr_maplist_to_sg_list(unit_p, instance)) { printk(KERN_WARNING "idr_strategy: instance %d: idr_maplist_to_sg_list failure!\n", instance); idr_unmap_on_error(unit_p, instance); return -EIO; } /* * map the sg list (which may combine contiguous entries * there is no error return, as I am told that the mapping resources are huge */ unit_p->nr_iopbs = pci_map_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, idr_input ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); DPRINT((KERN_DEBUG "idr_strategy: instance %d: scatter list mapped, nr_iopbs = 0x%x\n", instance, unit_p->nr_iopbs)); /* * convert sg list to iopb list for plx dma engine */ if(idr_sg_list_to_iopb_list(unit_p, instance)) { printk(KERN_ERR "idr_strategy: instance %d: idr_sg_list_to_iopb_list failure!\n", instance); pci_unmap_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, idr_input ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); idr_unmap_on_error(unit_p, instance); return -EIO; } /* * synch iopb memory for dma - NOP on a pc - necessary on other * architectures to ensure coherence betweene list and device * synch only as much iopb space as was touched this time */ pci_dma_sync_single(unit_p->pci_dev_p, unit_p->dma_handle, unit_p->nr_iopbs * IOPB_SIZE, PCI_DMA_TODEVICE); DPRINT((KERN_DEBUG "idr_strategy: instance %d: iopb memory synched for DMA\n", instance)); /* * program plx dma logic on board - in chaining mode * buffer is already aligned, and count masked * * make sure that any left over dma done interrupt * is cleared */ DPRINT((KERN_DEBUG "idr_strategy: instance %d: programming PLX chip for DMA\n", instance)); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_1_CLEAR_INTERRUPT); PLX_PUTL(PLX_DMA_MODE_1, DMA_BUS_16_BIT | DMA_WAIT_1 | DMA_BURST_ENABLE | DMA_CHAIN_ENABLE | plx_dma_done_int | DMA_LOCAL_ADD_HOLD | DMA_DEMAND_MODE); /* mode register */ PLX_PUTL(PLX_DMA_DESC_PTR_1, plx_direction | DMA_CHAIN_IN_PCI_MEM | unit_p->dma_handle); /* output, list in pci mem, list pointer */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_1_ENABLE); /* start DMA in two steps - PLX bug */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_1_ENABLE | DMA_1_START); /* actually fire up xfer */ /* * the 9060 drives the local interrupt out true when a dma * interrupt happens * * we have to loop that output back to the local interrupt input * external to the chip to get a dma interrupt to the pci bus * * save processor flags and disable interrupts prior to enabling plx interrupts * and sleeping - and pickup spinlock via mutex */ DPRINT((KERN_DEBUG "idr_strategy: instance %d: disabling cpu ints and enabling board ints\n", instance)); mutex_lock(&unit_p->mutex); PLX_PUTL(PLX_INT_CSTAT, PCI_INTERRUPT_ENABLE | PCI_LOCAL_INT_ENABLE | LOCAL_INT_OUT_ENABLE | plx_local_dma_int); /* * if automatic and read mode and first_dma_flag on - issue go AFTER starting dma */ if ((!(unit_p->unit_flags & IDR_MANUAL)) && idr_input && (first_dma_flag)) { DPRINT((KERN_DEBUG "idr_strategy: instance %d: automatic & input & first_dma_flag on - issuing GO after starting dma\n", instance)); IDR_PUTL(IDR_DEVICE_PULSES, unit_p->read_pulse | GO); } /* * set sleeping flag - so we can detect abnormal sleep termination later * (int code will clear sleeping flag) * * board interrupts are already enabled, and processor interrupts disabled, * to avoid race wait will re-enable * * snooze until done or timeout or signal */ unit_p->sleeping = 1; /* * sleep until done, timeout, or signal */ DPRINT((KERN_DEBUG "idr_strategy: instance %d: unit_flags = 0x%lx, dma mode = 0x%x, int cstat = 0x%x\n", instance, unit_p->unit_flags, PLX_GETL(PLX_DMA_MODE_1), PLX_GETL(PLX_INT_CSTAT))); DPRINT((KERN_DEBUG "idr_strategy: instance %d: calling cond_timed_wait_rel, jiffies = %ld, unit_p->dma_time = %ld\n", instance, jiffies, unit_p->dma_time)); wait_return = cond_timed_wait_rel(&unit_p->cond, &unit_p->mutex, unit_p->dma_time); /* * leave idr dma enb on for read/write or block end if manual * idr dma must stay on to preserve fifo data between dma blocks * * make sure plx interrupt enables are off and pause dma * * in input mode, plx dma will still be enabled at this point to allow any * data in the plx fifo that was left after a block terminated early by attention * to make it to memory * * if the application uses attention to do early block termination, it may be necessary * to add some delay here to make sure that the (small) plx fifo has drained * * there doesn't seem to be any way to determine whether there is data in the plx fifos * so for now, we will trust to luck! */ /* * spin a while (if necessary) to give DMA a chance to finish * then pause DMA - if still on */ for(i=0; i<100; i++) if(PLX_GETL(PLX_DMA_CMD_STAT_BOTH) & DMA_1_DONE) break; PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, 0); IDR_PUTL(IDR_LATCHED_FUNCTIONS, IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~(END_OF_RANGE_INT_ENB | ATTENTION_INT_ENB)); PLX_PUTL(PLX_INT_CSTAT, 0); /* * re-enable processor ints (if previously on) * and release spin lock via mutex */ mutex_unlock(&unit_p->mutex); /* * if dma is not done (after sig or timeout or short input block), * force a plx dma abort * and issue a master clear to flush idr buffers and turn off the dma enable bit in the idr logic * * abort may take some time to complete, so use delay and * register read to give it some time */ if (!(PLX_GETL(PLX_DMA_CMD_STAT_BOTH) & DMA_1_DONE)) { DPRINT((KERN_DEBUG "idr_strategy: instance %d: dma not done after cond_timed_wait_rel\n", instance)); PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_1_ABORT); /* * get latched functions and mode - so we can restore them after a mclr * * it is not clear whether the 9060 needs dma rq in order to * finish an abort * * if it does, it may transfer a word or two after the mclr, * so we will repeat the mclr later */ udelay(5); temp_latches = IDR_GETL(IDR_LATCHED_FUNCTIONS) & ~IDR_DMA_ENABLE; temp_mode = IDR_GETL(IDR_MODE); IDR_PUTL(IDR_116_PULSES, MASTER_CLEAR); /* * force write cache to flush - so delay appears on the bus */ read_dump_0 = IDR_GETL(IDR_STATUS); udelay(5); IDR_PUTL(IDR_116_PULSES, MASTER_CLEAR); IDR_PUTL(IDR_LATCHED_FUNCTIONS, temp_latches); IDR_PUTL(IDR_MODE, temp_mode); /* * make sure dma actually says done */ if (!(PLX_GETL(PLX_DMA_CMD_STAT_BOTH) & DMA_1_DONE)) { printk(KERN_ERR "idr_strategy: instance %d: dma not done after abort!\n", instance); pci_unmap_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, idr_input ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); idr_unmap_on_error(unit_p, instance); return -EIO; } } /* * now we can safely make sure that the done interrupt is clear, * * (the above is probably not necessary after normal dma done) */ PLX_PUTL(PLX_DMA_CMD_STAT_BOTH, DMA_1_CLEAR_INTERRUPT); /* * unmap the sg list (NOP on pc, release iommu mappings on other machines) * unmap sg wants the original sg list length */ DPRINT((KERN_DEBUG "idr_strategy: instance %d: unmapping sg list after dma\n", instance)); pci_unmap_sg(unit_p->pci_dev_p, unit_p->sg_list_p, unit_p->maplist_nr_pages, idr_input ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); /* * unmap user buffer, or copy dma buffer to user buffer */ if(idr_unmap_or_copy_buf(unit_p, instance)) { printk(KERN_ERR "idr_strategy: instance %d: idr_unmap_or_copy_buf failure!\n", instance); return -EIO; } /* * find out why we woke up * * if sleeping is true, we got here via timeout or signal -- do a soft reset of * the plx and idr logic - saving and restoring the latched functions and mode * registers */ if (unit_p->sleeping) { /* * flag some kind of error - in unlikely case that both the following * tests fail!! */ temp = -EIO; unit_p->sleeping = 0; DPRINT((KERN_DEBUG "idr_strategy: instance %d: abnormal return from cond_timed_wait_rel()!\n", instance)); /* * check for timeout */ if (wait_return == COND_WAIT_TIMEOUT) { /* * timeout is always an error so print message and return error code to write * and flag timeout in unit flags */ printk(KERN_ERR "idr_strategy: instance %d: timeout while waiting for interrupt!\n", instance); unit_p->unit_flags |= IDR_DVMA_TIMEOUT; temp = -EIO; } /* * check for signal */ if (wait_return == COND_WAIT_SIGNAL) { /* * flag signal received and print error message */ printk(KERN_WARNING "idr_strategy: instance %d: signal received while waiting for interrupt\n", instance); unit_p->unit_flags |= IDR_SIG_RECEIVED; temp = -EINTR; } /* * save latched functions and mode and do a soft reset, then restore saved registers */ temp_latches = IDR_GETL(IDR_LATCHED_FUNCTIONS); temp_mode = IDR_GETL(IDR_MODE); plx_soft_reset(unit_p); IDR_PUTL(IDR_LATCHED_FUNCTIONS, temp_latches); IDR_PUTL(IDR_MODE, temp_mode); return temp; } /* end of if sleeping */ /* * if we get here - just return good status * * there is no way to figure a residual count with the hardware at hand * so we don't even try */ return 0; } /* * do a soft reset of the plx chip - also clears the idr logic * * the pci_dev handle, and int_level elements of the unit structure * MUST be set before calling this routine! * * ALSO - THIS ROUTINE MUST NOT BE CALLED FROM THE INTERRUPT SIDE OF THINGS!! * (it calls schedule to do a delay) */ static void plx_soft_reset(struct idr_unit_t *unit_p) { u_long wait_j; /* wait jiffies */ /* * soft reset clears the local config registers, which turns off add space 0 accesses * * so we will force an eeprom reload, and wait a while (0.2s) to avoid too many retry * cycles while the eeprom is reloading - we will use delay, not drv_usec_wait, so we * don't tie up the cpu * * do a read dump before the reload to force any write buffer to flush, so our wait * timing is reflected on the bus (hopefully). if we try to access the plx chip * during reload, retry cycles happen, and a short bus timer could cause a panic * * an earlier rev cleared the int line byte in the configuration registers during * soft reset, but that is not supposed to be true anymore * * IT SEEMS THAT IT IS STILL TRUE - SO RESTORE THE INT LINE AFTER SOFT_RESET * * we probably don't have to clear the config reload bit after writing it, * but we will to be sure */ /* * there is really no good way to return an error from here - so just put it on the console * and return without touching anything */ PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | PLX_SOFT_RESET)); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~PLX_SOFT_RESET)); read_dump_0 = PLX_GETL(PLX_INT_CSTAT); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) | CONFIGURATION_RELOAD)); /* * wait ... */ wait_j = jiffies + HZ / 5; while (time_before(jiffies, wait_j)) schedule(); PLX_PUTL(PLX_EEPROM_USER, (PLX_GETL(PLX_EEPROM_USER) & ~CONFIGURATION_RELOAD)); pci_write_config_byte(unit_p->pci_dev_p, PCI_INTERRUPT_LINE, unit_p->int_level); DPRINT((KERN_DEBUG "idr_??? (plx_soft_reset): instance %d: soft reset and configuration reload complete\n", unit_p->instance)); /* * soft reset leaves pci master int enable on, so turn it off for safety * don't bother with oring bits - just hammer them all off! */ PLX_PUTL(PLX_INT_CSTAT, 0); /* * restore endian mode default */ PLX_PUTL(PLX_ENDIAN_REG, unit_p->endian_def); /* * disable address space 0 pre-fetch */ PLX_PUTL(PLX_ADD_0_ROM_DESC, PLX_GETL(PLX_ADD_0_ROM_DESC) | ADD_0_PREFETCH_DISABLE); } /* * establish driver and board defaults - some of these will be used to establish initial * configuration on a per-board basis * * there are a ton of these things - to try to stay consistent with solaris driver and conf file * we could probably eliminate one level of defaults (in unit structure) but this way will avoid * some code modification, and let the insmod parameters match the solaris .conf parameters * * the config variables that end in _def may be later modified by ioctl calls on a per board * basis * * examine the variables that may be set during insmod - if not -1, and within permitted range, * use the insmod supplied value - if -1, use the default * * some of the range checks look redundant ( .. >= 0 ..), but I prefer to keep the insmod set * detection separate from the range check, and not assume anything */ static void idr_set_global_defaults(void) { DPRINT((KERN_DEBUG "idr: entering idr_set_global_defaults\n")); idr_speed_def = SPEED_DEF; if (speed_def >= 0) { if ((speed_def >= 0) && (speed_def <= 3)) idr_speed_def = speed_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: speed_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_speed_def = 0x%x\n", idr_speed_def)); idr_dma_time_def = DMA_TIME_DEF; if (dma_time_def >= 0) { if ((dma_time_def >= DMA_TIME_MIN) && (dma_time_def <= DMA_TIME_MAX)) idr_dma_time_def = dma_time_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: dma_time_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_dma_time_def = 0x%x\n", idr_dma_time_def)); idr_attn_time_def = ATTN_TIME_DEF; if (attn_time_def >= 0) { if ((attn_time_def >= ATTN_TIME_MIN) && (attn_time_def <= ATTN_TIME_MAX)) idr_attn_time_def = attn_time_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: attn_time_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_attn_time_def = 0x%x\n", idr_attn_time_def)); idr_rdy_time_def = RDY_TIME_DEF; if (rdy_time_def >= 0) { if ((rdy_time_def >= RDY_TIME_MIN) && (rdy_time_def <= RDY_TIME_MAX)) idr_rdy_time_def = rdy_time_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: rdy_time_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_rdy_time_def = 0x%x\n", idr_rdy_time_def)); /* * the following are a pile of defaults that have legit values of 0 or 1 * we could test by masking with 0xfffffffe, but ==0 || ==1 reads better, and * we don't do this very often */ idr_byte_swap_def = BYTE_SWAP_DEF; if (byte_swap_def >= 0) { if ((byte_swap_def == 0) || (byte_swap_def == 1)) idr_byte_swap_def = byte_swap_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: byte_swap_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_byte_swap_def = 0x%x\n", idr_byte_swap_def)); idr_cycle_pol_def = CYCLE_POL_DEF; if (cycle_pol_def >= 0) { if ((cycle_pol_def == 0) || (cycle_pol_def == 1)) idr_cycle_pol_def = cycle_pol_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: cycle_pol_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_cycle_pol_def = 0x%x\n", idr_cycle_pol_def)); idr_busy_pol_def = BUSY_POL_DEF; if (busy_pol_def >= 0) { if ((busy_pol_def == 0) || (busy_pol_def == 1)) idr_busy_pol_def = busy_pol_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: busy_pol_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_busy_pol_def = 0x%x\n", idr_busy_pol_def)); idr_write_cycle_def = WRITE_CYCLE_DEF; if (write_cycle_def >= 0) { if ((write_cycle_def == 0) || (write_cycle_def == 1)) idr_write_cycle_def = write_cycle_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: write_cycle_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_write_cycle_def = 0x%x\n", idr_write_cycle_def)); idr_read_cycle_def = READ_CYCLE_DEF; if (read_cycle_def >= 0) { if ((read_cycle_def == 0) || (read_cycle_def == 1)) idr_read_cycle_def = read_cycle_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: read_cycle_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_read_cycle_def = 0x%x\n", idr_read_cycle_def)); idr_read_acf2_def = READ_ACF2_DEF; if (read_acf2_def >= 0) { if ((read_acf2_def == 0) || (read_acf2_def == 1)) idr_read_acf2_def = read_acf2_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: read_acf2_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_read_acf2_def = 0x%x\n", idr_read_acf2_def)); idr_open_f3_def = OPEN_F3_DEF; if (open_f3_def >= 0) { if ((open_f3_def == 0) || (open_f3_def == 1)) idr_open_f3_def = open_f3_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: open_f3_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_open_f3_def = 0x%x\n", idr_open_f3_def)); idr_open_f2_def = OPEN_F2_DEF; if (open_f2_def >= 0) { if ((open_f2_def == 0) || (open_f2_def == 1)) idr_open_f2_def = open_f2_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: open_f2_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_open_f2_def = 0x%x\n", idr_open_f2_def)); idr_open_f1_def = OPEN_F1_DEF; if (open_f1_def >= 0) { if ((open_f1_def == 0) || (open_f1_def == 1)) idr_open_f1_def = open_f1_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: open_f1_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_open_f1_def = 0x%x\n", idr_open_f1_def)); idr_write_f3_def = WRITE_F3_DEF; if (write_f3_def >= 0) { if ((write_f3_def == 0) || (write_f3_def == 1)) idr_write_f3_def = write_f3_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: write_f3_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_write_f3_def = 0x%x\n", idr_write_f3_def)); idr_write_f2_def = WRITE_F2_DEF; if (write_f2_def >= 0) { if ((write_f2_def == 0) || (write_f2_def == 1)) idr_write_f2_def = write_f2_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: write_f2_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_write_f2_def = 0x%x\n", idr_write_f2_def)); idr_write_f1_def = WRITE_F1_DEF; if (write_f1_def >= 0) { if ((write_f1_def == 0) || (write_f1_def == 1)) idr_write_f1_def = write_f1_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: write_f1_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_write_f1_def = 0x%x\n", idr_write_f1_def)); idr_read_f3_def = READ_F3_DEF; if (read_f3_def >= 0) { if ((read_f3_def == 0) || (read_f3_def == 1)) idr_read_f3_def = read_f3_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: read_f3_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_read_f3_def = 0x%x\n", idr_read_f3_def)); idr_read_f2_def = READ_F2_DEF; if (read_f2_def >= 0) { if ((read_f2_def == 0) || (read_f2_def == 1)) idr_read_f2_def = read_f2_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: read_f2_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_read_f2_def = 0x%x\n", idr_read_f2_def)); idr_read_f1_def = READ_F1_DEF; if (read_f1_def >= 0) { if ((read_f1_def == 0) || (read_f1_def == 1)) idr_read_f1_def = read_f1_def; else printk(KERN_WARNING "idr: idr_set_global_defaults: read_f1_def out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_read_f1_def = 0x%x\n", idr_read_f1_def)); /* * set max board number - this will determine how much we kmalloc for the unit structures * we will do a sanity check, and require this number to be a single digit */ idr_max_boards = MAX_BOARDS_DEF; if (max_boards >= 0) { if ((max_boards >= 0) && (max_boards <= 9)) idr_max_boards = max_boards; else printk(KERN_WARNING "idr: idr_set_global_defaults: max_boards out of range! using default\n"); } DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: idr_max_boards = %d\n", idr_max_boards)); /* * set max_phys_order and several values * that derive from it * * max buffer pages = 2**max phys order * max bytes per dma xfer = max buffer pages * PAGE_SIZE * max size of scatter/gather list = max buffer pages + 1 * * these calculations must track w/copy buf and user buf map size as well * * size of iopb memory allocated = max sg size * IOPB_SIZE * * we will check for a request less than zero, but not limit the upper end - USER BEWARE! */ idr_max_phys_order = MAX_PHYS_ORDER_DEF; if (max_phys_order >= 0) idr_max_phys_order = max_phys_order; idr_max_buf_pages = (1 << idr_max_phys_order); idr_max_buf_bytes = idr_max_buf_pages * PAGE_SIZE; idr_max_sg_length = idr_max_buf_pages + 1; idr_iopb_mem_size = idr_max_sg_length * IOPB_SIZE; DPRINT((KERN_DEBUG "idr: idr_set_global_defaults: max_phys_order = 0x%x, max_bytes = 0x%x, max_sg_length = 0x%x, iopb_mem_size = 0x%x\n", idr_max_phys_order, idr_max_buf_bytes, idr_max_sg_length, idr_iopb_mem_size)); } /* * sort out and save the defaults on a per-board basis * some of this is probably redundant, but we are trying * to stay with the solaris code as much as possible * * the ranges of any insmod-supplied parameters have already been checked */ static void idr_set_board_defaults(struct idr_unit_t *unit_p, int instance) { DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d\n", instance)); /* * get and save timer default values */ unit_p->dma_time_def = idr_dma_time_def; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: dma_time_def = %d\n", instance, unit_p->dma_time_def)); unit_p->attn_time_def = idr_attn_time_def; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: attn_time_def = %d\n", instance, unit_p->attn_time_def)); unit_p->rdy_time_def = idr_rdy_time_def; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: rdy_time_def = %d\n", instance, unit_p->rdy_time_def)); /* * get the default byte swap property * * in a sparc system, we must tell the plx chip to swap dma bytes, to reverse * the byte swapping that the host always does for 16 bit values * if the byte swap property says to swap bytes, we turn byte swapping OFF in the plx part * * in an x86 system, the default is to NOT swap in hardware, so if the byte * swap proprety says to swap, we swap * */ if (idr_byte_swap_def != 0) { unit_p->endian_def = DMA_1_BIG_ENDIAN; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: setting DMA byte order to BIG ENDIAN\n", instance)); } else { unit_p->endian_def = 0; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: setting DMA byte order to LITTLE ENDIAN\n", instance)); } /* * start assembling the mode register default */ unit_p->mode_reg_def = 0; switch (idr_speed_def) { case 0: unit_p->mode_reg_def = SPEED_0; break; case 1: unit_p->mode_reg_def = SPEED_1; break; case 2: unit_p->mode_reg_def = SPEED_2; break; case 3: unit_p->mode_reg_def = SPEED_3; break; } if (idr_cycle_pol_def == 1) unit_p->mode_reg_def |= CYCLE_POLARITY; if (idr_busy_pol_def == 1) unit_p->mode_reg_def |= BUSY_POLARITY; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: mode_reg_def = 0x%lx\n", instance, unit_p->mode_reg_def)); /* * assemble read pulse default - ALWAYS INCLUDE GO! */ unit_p->read_pulse_def = GO; if (idr_read_cycle_def == 1) unit_p->read_pulse_def |= SOFT_CYCLE; if (idr_read_acf2_def == 1) unit_p->read_pulse_def |= ACLO_FCN_2; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: read_pulse_def = 0x%lx\n", instance, unit_p->read_pulse_def)); /* * assemble write pulse default - ALWAYS INCLUDE GO! */ unit_p->write_pulse_def = GO; if (idr_write_cycle_def == 1) unit_p->write_pulse_def |= SOFT_CYCLE; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: write_pulse_def = 0x%lx\n", instance, unit_p->write_pulse_def)); /* * start compiling the open, read, and write function bit defaults * * there is a separate property for each function bit for each of the three * conditions -- it would be simlpler to combine all 3 bits into one mask, * but it wouldn't read as well, and wouldn't allow using the idr_reg.h * bit definitions */ /* * start with read function bits */ unit_p->read_fcn_def = 0; if (idr_read_f3_def == 1) unit_p->read_fcn_def |= FUNCTION_3; if (idr_read_f2_def == 1) unit_p->read_fcn_def |= FUNCTION_2; if (idr_read_f1_def == 1) unit_p->read_fcn_def |= FUNCTION_1; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: read_fcn_def = 0x%lx\n", instance, unit_p->read_fcn_def)); /* * now write function bits */ unit_p->write_fcn_def = 0; if (idr_write_f3_def == 1) unit_p->write_fcn_def |= FUNCTION_3; if (idr_write_f2_def == 1) unit_p->write_fcn_def |= FUNCTION_2; if (idr_write_f1_def == 1) unit_p->write_fcn_def |= FUNCTION_1; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: write_fcn_def = 0x%lx\n", instance, unit_p->write_fcn_def)); /* * now the open function bits, which will be plugged into the latched function * register at open time */ unit_p->latch_reg_def = 0; if (idr_open_f3_def == 1) unit_p->latch_reg_def |= FUNCTION_3; if (idr_open_f2_def == 1) unit_p->latch_reg_def |= FUNCTION_2; if (idr_open_f1_def == 1) unit_p->latch_reg_def |= FUNCTION_1; DPRINT((KERN_DEBUG "idr: idr_set_board_defaults: instance %d: latch_reg_def = 0x%lx\n", instance, unit_p->latch_reg_def)); } /* * allocate dma resources - iopb list memory, sg list memory, user buffer map list, * and copy buffer if direct user dma not to be used - and set dma mask */ static int idr_dma_alloc(struct idr_unit_t *unit_p, int instance) { int i; DPRINT((KERN_DEBUG "idr: idr_dma_alloc: instance %d\n", instance)); /* * get contiguous pages of kernel memory to contain to iopbs (dma chaining list) * GFP_KERNEL should get us contiguous memory, don't set dma flag so it won't be * forced into low memory */ unit_p->iopb_base_p = kmalloc(idr_iopb_mem_size, GFP_KERNEL); if (!unit_p->iopb_base_p) { printk(KERN_ERR "idr: idr_dma_alloc: instance %d: kmalloc failure (iopb memory)!\n", instance); return -1; } DPRINT((KERN_DEBUG "idr: idr_dma_alloc: instance %d: iopb memory allocated, virtual address = 0x%p\n", instance, unit_p->iopb_base_p)); /* * check for alignment - we could have asked for more and forced it, * but I don't think the mem caches have any * chunks w/smaller alignments than we need */ if ((unsigned long)(unit_p->iopb_base_p) & (IOPB_SIZE - 1)) { printk(KERN_ERR "idr: idr_dma_alloc: instance %d: iopb memory not IOPB_SIZE aligned!\n", instance); kfree(unit_p->iopb_base_p); return -1; } /* * inform the kernel what our dma engine can address * (32 bits at this time/with this hardware) */ if (pci_set_dma_mask(unit_p->pci_dev_p, IDR_DMA_MASK)) { printk(KERN_ERR "idr: idr_dma_alloc: instance %d: DMA addressing not compatible!\n", instance); kfree(unit_p->iopb_base_p); return -1; } /* * get the bus address of iopb memory using the dynamic DMA mapping stuff, * (doesn't amount to much on a PC, but will help w/compatibility on SPARC, * and other arch's w/iommu hardware - like x86_64 * * do a sanity check on our DMA_MASK and the map code - make sure value * returned fits in DMA_MASK (32 bits for this hardware) */ unit_p->dma_handle = pci_map_single(unit_p->pci_dev_p, unit_p->iopb_base_p, idr_iopb_mem_size, PCI_DMA_TODEVICE); if (unit_p->dma_handle > IDR_DMA_MASK) { printk(KERN_ERR "idr: idr_dma_alloc: instance %d: dma_handle out of range!\n", instance); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, idr_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } DPRINT((KERN_DEBUG "idr: idr_dma_alloc: instance %d: iopb memory mapped, dma_handle = 0x%lx\n", instance, (unsigned long)unit_p->dma_handle)); /* * get space for the scatterlist ... this probably isn't necessary on a pc, since * we should be able to convert the user buff page mappings direcly into bus adds, * and build the iopb list, but to try for compatibility with other architectures, * we will go through a scatterlist on the way to building an iopb list * * we now have to zero the allocated memory, since the kernel gods have added new * .page and .offset to the scatterlist struct, and if .page AND .address are either * both zero or both non-zero, they BUG()! (maybe not in 2.6...) */ unit_p->sg_list_p = kmalloc(idr_max_sg_length * sizeof(struct scatterlist), GFP_KERNEL); if (unit_p->sg_list_p == NULL) { printk(KERN_ERR "idr: idr_dma_alloc: instance %d: can't allocate scatter list (kmalloc error)!\n", instance); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, idr_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } memset(unit_p->sg_list_p, 0, idr_max_sg_length * sizeof(struct scatterlist)); DPRINT((KERN_DEBUG "idr: idr_dma_alloc: instance %d: scatter list allocated, sg_list_p = 0x%p\n", instance, unit_p->sg_list_p)); /* * allocate an array of page pointers to hold ptrs to mapped * user pages * same size as max sg list since user bfr alignment may require * an extra page */ if((unit_p->maplist_p = kmalloc(idr_max_sg_length * sizeof(*unit_p->maplist_p), GFP_KERNEL)) == NULL) { printk(KERN_ERR "idr: idr_dma_alloc: instance %d: can't allocate maplist!\n", instance); kfree(unit_p->sg_list_p); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, idr_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } DPRINT((KERN_DEBUG "idr: idr_dma_alloc: instance %d: maplist allocated, address = 0x%p\n", instance, unit_p->maplist_p)); /* * if a copy buffer is required, allocate it here and stuff the maplist w/its * pages * * we only allocate just enough pages to satisfy the max buffer size, since we will always * copy to the buffer starting at offset = 0, so we don't need an extra page to handle * alignment (like the maplist and sg list do) */ if (IDR_COPY_BUF) { DPRINT((KERN_DEBUG "idr: init_module: instance %d: allocating copy buffer\n", instance)); unit_p->dma_copy_buf_p = (char *)__get_free_pages(GFP_KERNEL, idr_max_phys_order); if(unit_p->dma_copy_buf_p == NULL) { printk(KERN_ERR "idr: idr_dma_alloc: instance %d: can't allocate copy buffer!\n", instance); kfree(unit_p->maplist_p); kfree(unit_p->sg_list_p); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, idr_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); return -1; } DPRINT((KERN_DEBUG "idr: idr_dma_alloc: instance %d: copy buffer allocated, address = 0x%p\n", instance, unit_p->dma_copy_buf_p)); /* * fill maplist array with pointers to buffer page structures * since we are in copy mode, these pointers will never change */ for (i=0; imaplist_p[i] = virt_to_page(unit_p->dma_copy_buf_p + ( i * PAGE_SIZE)); DPRINT((KERN_DEBUG "idr: idr_dma_alloc: instance %d: maplist loaded w/copy buff pages\n", instance)); } else { DPRINT((KERN_DEBUG "idr: idr_dma_alloc: instance %d: user DMA mode, no copy buffer allocated\n", instance)); unit_p->dma_copy_buf_p = NULL; } return 0; } /* * free the above */ static void idr_dma_free(struct idr_unit_t *unit_p, int instance) { DPRINT((KERN_DEBUG "idr: idr_dma_free: instance %d\n", instance)); if(unit_p->dma_copy_buf_p) free_pages((unsigned long)unit_p->dma_copy_buf_p, idr_max_phys_order); kfree(unit_p->maplist_p); kfree(unit_p->sg_list_p); pci_unmap_single(unit_p->pci_dev_p, unit_p->dma_handle, idr_iopb_mem_size, PCI_DMA_TODEVICE); kfree(unit_p->iopb_base_p); } /* * map the board registers for slave access */ static int idr_map_regs(struct idr_unit_t *unit_p, int instance) { u_long bus_address; /* temp base address */ u_long bus_base; /* temp address base */ u_long bus_offset; /* temp address offset */ DPRINT((KERN_DEBUG "idr: idr_map_regs: entering, instance %d\n", instance)); bus_address = unit_p->pci_dev_p->resource[0].start; DPRINT((KERN_DEBUG "idr: idr_map_regs: instance %d: pci_base_address_0 (plx regs) = 0x%lx\n", instance, bus_address)); bus_address &= PCI_BASE_ADDRESS_MEM_MASK; bus_base = bus_address & ~(PAGE_SIZE - 1); bus_offset = bus_address & (PAGE_SIZE - 1); DPRINT((KERN_DEBUG "idr: idr_map_regs: instance %d: plx bus add = 0x%lx, base = 0x%lx, offset = 0x%lx\n", instance, bus_address, bus_base, bus_offset)); unit_p->plx_page_base_p = ioremap(bus_base, PLX_REG_SIZE + bus_offset); unit_p->plx_base_p = unit_p->plx_page_base_p + bus_offset; if (unit_p->plx_page_base_p == NULL) { printk(KERN_ERR "idr: idr_map_regs: instance %d: can't ioremap plx registers!\n", instance); return -1; } DPRINT((KERN_DEBUG "idr: idr_map_regs: instance %d: plx mapped base = 0x%p, virt addr = 0x%p\n", instance, unit_p->plx_page_base_p, unit_p->plx_base_p)); bus_address = unit_p->pci_dev_p->resource[2].start; DPRINT((KERN_DEBUG "idr: idr_map_regs: instance %d: pci_base_address_2 (idr regs) = 0x%lx\n", instance, bus_address)); bus_address &= PCI_BASE_ADDRESS_MEM_MASK; bus_base = bus_address & ~(PAGE_SIZE - 1); bus_offset = bus_address & (PAGE_SIZE - 1); DPRINT((KERN_DEBUG "idr: idr_map_regs: instance %d: idr bus add = 0x%lx, base = 0x%lx, offset = 0x%lx\n", instance, bus_address, bus_base, bus_offset)); unit_p->idr_page_base_p = ioremap(bus_base, IDR_REG_SIZE + bus_offset); unit_p->idr_base_p = unit_p->idr_page_base_p + bus_offset; if (unit_p->idr_page_base_p == NULL) { printk(KERN_ERR "idr: idr_map_regs: instance %d: can't ioremap idr registers!\n", instance); iounmap(unit_p->plx_page_base_p); return -1; } DPRINT((KERN_DEBUG "idr: idr_map_regs: instance %d: idr mapped base = 0x%p, virt addr = 0x%p\n", instance, unit_p->idr_page_base_p, unit_p->idr_base_p)); return 0; } /* * unmap the board's registers */ static void idr_unmap_regs(struct idr_unit_t *unit_p, int instance) { DPRINT((KERN_DEBUG "idr: idr_unmap_regs: instance %d\n", instance)); iounmap(unit_p->idr_page_base_p); iounmap(unit_p->plx_page_base_p); } /* * check maplist values for max/min permissible */ static int idr_maplist_check(struct idr_unit_t *unit_p, int instance) { DPRINT((KERN_DEBUG "idr: idr_maplist_check: instance %d\n", instance)); if((unit_p->maplist_user_buf_p + unit_p->maplist_length) < unit_p->maplist_user_buf_p) { printk(KERN_ERR "idr_maplist_check: instance %d: buffer add + length overflow!\n", instance); return -EINVAL; } if(unit_p->maplist_nr_pages == 0) { printk(KERN_ERR "idr_maplist_check: instance %d: maplist_nr_pages = 0!\n", instance); return -EINVAL; } if(unit_p->maplist_nr_pages > idr_max_sg_length) { printk(KERN_ERR "idr_maplist_check: instance %d: maplist_nr_pages too large!\n", instance); return -EINVAL; } if(unit_p->maplist_length == 0) { printk(KERN_ERR "idr_maplist_check: instance %d: maplist_length = 0!\n", instance); return -EINVAL; } if(unit_p->maplist_length > idr_max_buf_bytes) { printk(KERN_ERR "idr_maplist_check: instance %d: maplist_length too large!\n", instance); return -EINVAL; } return 0; } /* * map and pin user buffer or copy user buffer to kernel buffer * maplist_rw_mode must be set before calling this * (might be cleaner to make that a parameter...) * * set the ->maplist_xx items to appropriate values for * either case * * CHECK FOR SANE VALUES in either case */ static int idr_map_or_copy_buf(struct idr_unit_t *unit_p, int instance, char *user_buf_p, int buf_size, int rw_mode) { DPRINT((KERN_DEBUG "idr: idr_map_or_copy_buf: instance %d, user_buf_p = 0x%p, size = 0x%x, rw_mode = 0x%x\n", instance, user_buf_p, buf_size, rw_mode)); unit_p->maplist_rw_mode = rw_mode; unit_p->maplist_user_buf_p = user_buf_p; /* not used in copy mode? */ if(unit_p->dma_copy_buf_p) { /* * we are in copy mode - if OUTPUT copy user data to buffer * set up maplist - we don't need to map since buffer already in kernel * offset always zero when using copy buffer */ unit_p->maplist_offset = 0; unit_p->maplist_length = buf_size; unit_p->maplist_nr_pages = (buf_size + PAGE_SIZE -1)/PAGE_SIZE; if(rw_mode != READ) { DPRINT((KERN_DEBUG "idr_map_or_copy_buf: instance %d: copying user buffer to dma buffer\n", instance)); if(idr_maplist_check(unit_p, instance)) { printk(KERN_ERR "idr_map_or_copy_buf (copy): instance %d: maplist value(s) out of range!\n", instance); return -EINVAL; } if(copy_from_user(unit_p->dma_copy_buf_p, user_buf_p, buf_size)) { printk(KERN_ERR "idr_map_or_copy_buf: instance %d: copy_from_user error!\n", instance); return -EIO; } } } else { /* * we will be mapping and pinning the user buffer for direct DMA */ DPRINT((KERN_DEBUG "idr_map_or_copy_buf: instance %d: mapping and pinning user buffer\n", instance)); unit_p->maplist_offset = (unsigned long)user_buf_p & ~PAGE_MASK; unit_p->maplist_length = buf_size; unit_p->maplist_nr_pages = (unit_p->maplist_offset + buf_size -1 + ~PAGE_MASK) >> PAGE_SHIFT; if(idr_maplist_check(unit_p, instance)) { printk(KERN_ERR "idr_map_or_copy_buf (map): instance %d: maplist value(s) out of range!\n", instance); return -EINVAL; } if(idr_map_user_buf(unit_p, instance)) { printk(KERN_ERR "idr_map_or_copy_buf: instance %d: idr_map_user_buf failure!\n", instance); return -EIO; } } DPRINT((KERN_DEBUG "idr_map_or_copy_buf: instance %d: buf_p = 0x%p, nr_pages = 0x%x, offset = 0x%x, length = 0x%x\n", instance, unit_p->maplist_user_buf_p, unit_p->maplist_nr_pages, unit_p->maplist_offset, unit_p->maplist_length)); return 0; } /* * unmap user buffer or copy kernel buffer to user buffer */ static int idr_unmap_or_copy_buf(struct idr_unit_t *unit_p, int instance) { DPRINT((KERN_DEBUG "idr: idr_unmap_or_copy_buf: instance %d\n", instance)); if(unit_p->dma_copy_buf_p) { /* * copy buffer mode, copy if input operation */ if(unit_p->maplist_rw_mode == READ) { DPRINT((KERN_DEBUG "idr_unmap_or_copy_buf: instance %d: copying dma data to user buffer\n", instance)); if(idr_maplist_check(unit_p, instance)) { printk(KERN_ERR "idr_unmap_or_copy_buf (copy): instance %d: maplist value(s) out of range!\n", instance); return -EINVAL; } if(copy_to_user(unit_p->maplist_user_buf_p, unit_p->dma_copy_buf_p, unit_p->maplist_length)) { printk(KERN_ERR "idr_unmap_or_copy_buf: instance %d: copy_to_user error!\n", instance); return -EIO; } } } else { /* * direct user buffer dma mode, unmap user buffer * maplist values already checked */ DPRINT((KERN_DEBUG "idr_unmap_or_copy_buf: instance %d: unmapping user buffer\n", instance)); idr_unmap_user_buf(unit_p, instance); } return 0; } /* * unmap if error requires undoing mapping - if copy mode, do nothing */ static void idr_unmap_on_error(struct idr_unit_t *unit_p, int instance) { DPRINT((KERN_DEBUG "idr: idr_unmap_on_error: instance %d\n", instance)); if(!unit_p->dma_copy_buf_p) { DPRINT((KERN_DEBUG "idr: idr_unmap_on_error, instance %d: calling idr_unmap_user_buf\n", instance)); idr_unmap_user_buf(unit_p, instance); } else DPRINT((KERN_DEBUG "idr: idr_unmap_on_error, instance %d: copy buf mode, doing nothing\n", instance)); } /* * map and pin user buffer for direct DMA * * code adapted from st.c */ static int idr_map_user_buf(struct idr_unit_t *unit_p, int instance) { int nr_pages_mapped; int i; DPRINT((KERN_DEBUG "idr: idr_map_user_buf: instance %d\n", instance)); /* * Try to fault in all of the necessary pages * * rw==READ means read from drive, write into memory area * 0 arg = don't force */ down_read(¤t->mm->mmap_sem); nr_pages_mapped = get_user_pages(current, current->mm, (unsigned long)unit_p->maplist_user_buf_p, unit_p->maplist_nr_pages, unit_p->maplist_rw_mode == READ, 0, unit_p->maplist_p, NULL); up_read(¤t->mm->mmap_sem); /* * result == nr pages requested if all pages mapped * or - if error */ if(nr_pages_mapped < unit_p->maplist_nr_pages) { printk(KERN_DEBUG "idr_map_user_buf: instance %d: couldn't map all (any?) pages!\n", instance); if(nr_pages_mapped > 0) { for(i=0; i < nr_pages_mapped; i++) put_page(unit_p->maplist_p[i]); } return -EIO; } /* * st.c includes this call here, but I believe * it is included in get_user_pages w/2.6 * * for (i=0; i < nr_pages; i++) * flush_dcache_page(unit_p->maplist_p[i]); */ return 0; } /* * unmap user buffer * * (comment from st.c) * FIXME: cache flush missing for rw==READ * FIXME: call the correct reference counting function */ static void idr_unmap_user_buf(struct idr_unit_t *unit_p, int instance) { int i; DPRINT((KERN_DEBUG "idr: idr_unmap_user_buf: instance %d\n", instance)); for (i=0; i < unit_p->maplist_nr_pages; i++) { if (unit_p->maplist_rw_mode == READ) SetPageDirty(unit_p->maplist_p[i]); put_page(unit_p->maplist_p[i]); } } /* * convert maplist int sg list */ static int idr_maplist_to_sg_list(struct idr_unit_t *unit_p, int instance) { struct page **maplist; /* points to array of ptrs to struct page */ int map_nr_pages; /* numer of pages in mapped buffer */ int map_length; /* # of bytes in mapped buffer */ int map_offset; /* offset of data in mapped buffer */ int temp_length; int i; DPRINT((KERN_DEBUG "idr: idr_maplist_to_sg_list: instance %d\n", instance)); /* * do a sanity check on the number of pages - it better not exceed the sg list max size */ if (unit_p->maplist_nr_pages > idr_max_sg_length) { printk(KERN_ERR "idr_maplist_to_sg_list: instance %d: too many maplist pages!\n", instance); return -EIO; } /* * grind through the map list, extracting addresses and lengths for the sg list * don't forget the initial offset, and total length */ maplist = unit_p->maplist_p; map_length = unit_p->maplist_length; map_offset = unit_p->maplist_offset; map_nr_pages = unit_p->maplist_nr_pages; for (i = 0; i < map_nr_pages; i++) { temp_length = (PAGE_SIZE - map_offset) < map_length ? PAGE_SIZE - map_offset : map_length; unit_p->sg_list_p[i].page = maplist[i]; unit_p->sg_list_p[i].offset = map_offset; unit_p->sg_list_p[i].length = temp_length; DPRINT((KERN_DEBUG "idr_maplist_to_sg_list: instance %d: page_ptr = 0x%p, page_offset = 0x%x, page_len = 0x%x\n", instance, maplist[i], map_offset, temp_length)); map_length -= temp_length; map_offset = 0; } return 0; } /* * convert sg list to iopb list for plx dma logic */ static int idr_sg_list_to_iopb_list(struct idr_unit_t *unit_p, int instance) { int num_iopbs; /* count of iopbs used & final sg size */ int iopb_number; /* iopb index - for readability */ uint32_t temp_bus_addr; /* */ int temp_length; /* */ uint32_t next_iopb_add; /* bus add of next iopb */ uint32_t plx_direction; /* xfer direction in plx format */ DPRINT((KERN_DEBUG "idr: idr_sg_list_to_iopb_list: instance %d\n", instance)); /* * make sure num_iopbs <= kio_pages (we should probably just trust the mapping code) */ num_iopbs = unit_p->nr_iopbs; if (num_iopbs > unit_p->maplist_nr_pages) { printk(KERN_ERR "idr_sg_list_to_iopb_list: instance %d: num_iopbs > maplist_nr_pages!\n", instance); return -EIO; } plx_direction = unit_p->maplist_rw_mode == READ ? DMA_INPUT : 0; /* * now assemble the scatter list into a form that the PLX chip wants to see, and build iopb list * use IOPB macros to ease possible later mods to swizzle byte lanes */ for (iopb_number = 0; iopb_number < num_iopbs; iopb_number++) { temp_bus_addr = (uint32_t) sg_dma_address(&(unit_p->sg_list_p[iopb_number])); temp_length = sg_dma_len(&(unit_p->sg_list_p[iopb_number])); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_PCI_ADDRESS, temp_bus_addr); /* buf segment bus address */ IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_TRANSFER_SIZE, temp_length); /* buf segment size */ IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_LOCAL_ADDRESS, IDR_DATA_OUT); /* data destination */ DPRINT((KERN_DEBUG "idr_sg_list_to_iopb_list: instance %d: iopb number 0x%x, buf seg addr = 0x%x, buf seg length = 0x%x\n", instance, iopb_number, temp_bus_addr, temp_length)); /* * the next descriptor pointer element of the iopb includes the location bit * (pci or local memory), the end of chain bit, the interrupt after terminal * countbit (not used), the direction of transfer bit (0 = output to device), * and the bus add of the next iopb * * if the iopb_number = num_iopbs -1, this is the last one so flag it, and * don't set a next iopb address */ if (iopb_number < (num_iopbs - 1)) { next_iopb_add = DMA_NEXT_ADDRESS_MASK & ((uint32_t)unit_p->dma_handle + ((iopb_number + 1) * IOPB_SIZE)); DPRINT((KERN_DEBUG "idr_sg_list_to_iopb_list: instance %d: not last iopb, next iopb add = 0x%x\n", instance, next_iopb_add)); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_NEXT_IOPB, DMA_CHAIN_IN_PCI_MEM | next_iopb_add | plx_direction); } else { DPRINT((KERN_DEBUG "idr_sg_list_to_iopb_list: instance %d: last iopb, setting end of chain bit\n", instance)); IOPB_PUTL((iopb_number * IOPB_SIZE) + IOPB_NEXT_IOPB, DMA_CHAIN_IN_PCI_MEM | DMA_END_OF_CHAIN | plx_direction); } } return 0; } /* * mutex and conditional variable routines - were in idr_mutex.c * Kaz Kylheku & wdw */ static void mutex_init(mutex_t * mx) { spin_lock_init(&mx->spin); } static void mutex_lock(mutex_t * mx) { unsigned long flags; spin_lock_irqsave(&mx->spin, flags); mx->flags = flags; } static void mutex_unlock(mutex_t * mx) { unsigned long flags = mx->flags; spin_unlock_irqrestore(&mx->spin, flags); } static void cond_init(cond_t * cv) { init_waitqueue_head(&cv->queue); } static int cond_timed_wait_rel(cond_t * cv, mutex_t * mx, long jiff_delta) { wait_queue_t wait; int ret; long remaining; init_waitqueue_entry(&wait, current); add_wait_queue(&cv->queue, &wait); current->state = TASK_INTERRUPTIBLE; mutex_unlock(mx); remaining = schedule_timeout(jiff_delta); ret = remaining ? signal_pending(current) ? COND_WAIT_SIGNAL : COND_WAIT_SUCCESS : COND_WAIT_TIMEOUT; current->state = TASK_RUNNING; remove_wait_queue(&cv->queue, &wait); mutex_lock(mx); return ret; } static void cond_broadcast(cond_t * cv) { wake_up_interruptible(&cv->queue); } /* * new-style init and cleanup routine invocations */ module_init(idr_init_module); module_exit(idr_cleanup_module); ./idr_install-2.60000755000076400007640000002751410223361246013414 0ustar wdwwdw00000000000000#!/bin/bash # # idr_install-2.6 # Script to compile and load Tahoma Technology # DR11-W driver for Linux version 2.6. # Can also configure rc.local and /etc/idr for driver autoload on boot. # # 14 November, 2000 # # 20 November, 2000 added -x test to load script line in rc.local # # 1 August, 2002 added/changed many things - try to autodetect # some options # # 4 February, 2004 modified for 2.6 kernel # # 4 September, 2004 corrected a few non-code typos/thinkos # # 1 April 2005 just updated date to match driver date # # Tahoma Technology # (formerly Ikon Corporation) # 107 2nd Avenue North # Seattle, WA, USA 98109 # # 206.728.6465 # http://www.tahomatech.com # tahoma@tahomatech.com # ***************************************** # SEE BELOW FOR USER CONFIGURABLE VARIABLES # ***************************************** # general script variables IDR_SCRIPT_NAME="idr_install-2.6" IDR_SCRIPT_DATE="1 April, 2005 15:43" IDR_SCRIPT_DESC="Tahoma Technology (formerly Ikon Corporation) DR11 driver installer" IDR_LINUX_VERS="for Linux version 2.6.x including x86_64" IDR_THISDIR=`pwd` # make and copy variables IDR_MAKE=make IDR_GREP=grep IDR_SOURCE=idr_driver IDR_CFLAGS_EXTRA= IDR_DFLAGS= IDR_MODULE=idr IDR_KERNEL_VERSION=`uname -r` IDR_KERNEL_SOURCE_DIR=/lib/modules/$IDR_KERNEL_VERSION/build IDR_MODULE_DIR=/lib/modules/$IDR_KERNEL_VERSION/misc IDR_TEST_DIR= # load (insmod), autoload, and dev node variables IDR_INSMOD=/sbin/insmod IDR_RMMOD=/sbin/rmmod IDR_FFLAG= IDR_TEMP=idr_temp IDR_DRIVER=$IDR_MODULE IDR_NODENAME=$IDR_MODULE IDR_MINOR=0 IDR_GROUP=bin IDR_OWNER=bin IDR_PERM=666 IDR_INIT_SCRIPT=/etc/rc.d/rc.local IDR_INSTALL_SCRIPT_DIR=/etc/idr IDR_INSTALL_SCRIPT_PERM=744 IDR_INSTALL_SCRIPT_DIR_PERM=755 # *****USER CONFIGURABLE SCRIPT BEHAVIOR VARIABLES***** # these control variables have two or three options # those with YES|NO options force the associated # behavior -- those with an AUTO option allow the # script and/or the driver to autoconfigure the # associated behavior IDR_USER_DMA=AUTO # NO|AUTO - dma direct to/from user buffer # AUTO = YES unless kernel configured for > 4G RAM # NO or > 4G RAM = dma to/from kernel copy buffer IDR_ENB_DEBUG=NO # YES|NO - YES or script cmd line option = debug mode IDR_FORCE_LOAD=NO # YES|NO - force module load # *****USER CONFIGURABLE LOAD-TIME VARIABLES***** # the following will be passed to insmod and may # be edited to control driver and device configuration # IDR_BOARDS determines the number of boards probed # for and device nodes created IDR_BOARDS=1 # number of boards and dev nodes IDR_MAX_PHYS_ORDER=4 # max dma xfer in "orders" of pages # also controls size of copy buffer (if not user dma) # larger user buffers will be done in max_phys chunks # max = PAGE_SIZE*(2**IDR_MAX_PHYS_ORDER) # w/current 4K page size, 0=4K, 1=8K, 2=16K, 3=32K, 4=64K ... IDR_SPEED=0 # handshake speed 0=fastest, 3=slowest IDR_DMA_TIME=30 # dma timeout in seconds IDR_RDY_TIME=30 # ready wait timeout in seconds IDR_ATTN_TIME=30 # attention wait timeout in seconds IDR_BYTE_SWAP=0 # 0=no swap, 1=swap IDR_CYCLE_POL=0 # 0=rising edge active, 1=falling edge active IDR_BUSY_POL=0 # 0=low assertion, 1=high assertion IDR_WRITE_CYCLE=1 # 0=do not pulse cycle on write, 1=pulse cycle on write IDR_READ_CYCLE=0 # 0=do not pulse cycle on read, 1=pulse cycle on read IDR_READ_ACF2=0 # 0=do not pulse acf2 on read, 1=pulse acf2 on read # the following function bit variables cause assertion if =1, no assertion if =0 IDR_OPEN_F3=0 IDR_OPEN_F2=0 IDR_OPEN_F1=0 IDR_WRITE_F3=1 IDR_WRITE_F2=0 IDR_WRITE_F1=0 IDR_READ_F3=1 IDR_READ_F2=0 IDR_READ_F1=1 echo # if no argument, print usage if [ -z $1 ] then echo "$IDR_SCRIPT_NAME: $IDR_SCRIPT_DATE" echo "$IDR_SCRIPT_DESC" echo "$IDR_LINUX_VERS" echo echo "usage: $IDR_SCRIPT_NAME [compile|load|autoload|remove|all]" echo echo " compile: compiles driver and copies to module directory" echo " load: loads driver into running kernel and creates device node(s)" echo " autoload: copies this script to $IDR_INSTALL_SCRIPT_DIR/" echo " and adds entry to rc.local to invoke this script (load) at boot time" echo " remove: unloads driver, deletes module, removes autoload entry," echo " script copy and directory, and device nodes" echo " all: remove, compile, load, and autoload" echo echo " compile and all may take an optional second argument: debug, which" echo " causes the driver to be compiled in debug mode (many printfs to log)" echo echo " some compile/install options are autodetected by this script and/or the" echo " driver code -- autodetected and hard coded options may be changed by" echo " editing this script" echo exit 0 fi # if arg #2 is debug, or debug enabled in script - set debug compile mode if [ $IDR_ENB_DEBUG = YES ] then IDR_DFLAGS=-DIDR_DEBUG fi if ! [ -z $2 ] then if [ $2 = debug ] then IDR_DFLAGS=-DIDR_DEBUG else echo "***** unrecognized 2nd argument!" ./$IDR_SCRIPT_NAME exit 1 fi fi # compile driver module and copy to module directory # first sort out the compile options if [ $1 = compile ] then echo "$IDR_SCRIPT_NAME: command is $1 $2" echo "removing old $IDR_MODULE.o .ko .mod.c .mod.o files (if any)" rm -f $IDR_MODULE.o rm -f $IDR_MODULE.ko rm -f $IDR_MODULE.mod.o rm -f $IDR_MODULE.mod.c # pass user dma/copy buffer mode to compiler if [ $IDR_USER_DMA = AUTO ] then echo "auto dma/copy buffer mode" IDR_CFLAGS_EXTRA="$IDR_CFLAGS_EXTRA -DIDR_DMA_MODE_AUTO" else echo "forcing copy buffer mode" IDR_CFLAGS_EXTRA="$IDR_CFLAGS_EXTRA -DIDR_DMA_MODE_COPY" fi if [ -n "$IDR_DFLAGS" ] then echo "debug compile enabled" fi echo "compiling $IDR_SOURCE.c using kbuild make" if ! $IDR_MAKE -C $IDR_KERNEL_SOURCE_DIR SUBDIRS=$PWD EXTRA_CFLAGS="$IDR_DFLAGS $IDR_CFLAGS_EXTRA" modules then echo "***** make error!" exit 1 fi if ! [ -d $IDR_MODULE_DIR ] then echo "creating $IDR_MODULE_DIR" mkdir $IDR_MODULE_DIR fi echo "copying $IDR_MODULE.ko to $IDR_MODULE_DIR" if ! cp $IDR_MODULE.ko $IDR_MODULE_DIR/ then echo "***** error copying $IDR_MODULE.ko to $IDR_MODULE_DIR!" exit 1 fi echo "compile and copy complete" exit 0 fi # load module into kernel and create device nodes if [ $1 = load ] then echo "$IDR_SCRIPT_NAME: command is $1" if [ $IDR_FORCE_LOAD = YES ] then IDR_FFLAG='-f' echo "forced load enabled" fi echo "loading module into kernel" if ! $IDR_INSMOD $IDR_FFLAG $IDR_MODULE_DIR/$IDR_MODULE.ko\ speed_def=$IDR_SPEED dma_time_def=$IDR_DMA_TIME\ attn_time_def=$IDR_ATTN_TIME rdy_time_def=$IDR_RDY_TIME\ byte_swap_def=$IDR_BYTE_SWAP cycle_pol_def=$IDR_CYCLE_POL\ busy_pol_def=$IDR_BUSY_POL write_cycle_def=$IDR_WRITE_CYCLE\ read_cycle_def=$IDR_READ_CYCLE read_acf2_def=$IDR_READ_ACF2\ open_f3_def=$IDR_OPEN_F3 open_f2_def=$IDR_OPEN_F2\ open_f1_def=$IDR_OPEN_F1 write_f3_def=$IDR_WRITE_F3\ write_f2_def=$IDR_WRITE_F2 write_f1_def=$IDR_WRITE_F1\ read_f3_def=$IDR_READ_F3 read_f2_def=$IDR_READ_F2\ read_f1_def=$IDR_READ_F1 max_phys_order=$IDR_MAX_PHYS_ORDER\ max_boards=$IDR_BOARDS then echo "***** insmod error!" exit 1 fi echo "removing stale device nodes (if any)" rm -f /dev/$IDR_NODENAME* echo "getting device major number from /proc/devices" IDR_MAJOR=`cat /proc/devices | awk "\\$2==\"$IDR_DRIVER\" {print \\$1}"` echo "creating device node(s)" while [ $IDR_BOARDS -gt 0 ] ; do if ! mknod /dev/$IDR_NODENAME$IDR_MINOR c $IDR_MAJOR $IDR_MINOR then echo "***** mknod error!" exit 1 fi IDR_BOARDS=$( expr $IDR_BOARDS - 1 ) IDR_MINOR=$( expr $IDR_MINOR + 1 ) done echo "setting device node ownership" chown $IDR_OWNER /dev/$IDR_NODENAME* chgrp $IDR_GROUP /dev/$IDR_NODENAME* chmod $IDR_PERM /dev/$IDR_NODENAME* echo "driver loaded and device nodes created" echo "load complete" exit 0 fi # copy this script to script dir (/etc/idr, probably) and # invoke it (load) from /etc/rc.d/rc.local if [ $1 = autoload ] then echo "$IDR_SCRIPT_NAME: command is $1" echo "removing old $IDR_SCRIPT_NAME (if present) from $IDR_INSTALL_SCRIPT_DIR" if [ -d $IDR_INSTALL_SCRIPT_DIR ] then rm -f $IDR_INSTALL_SCRIPT_DIR/$IDR_SCRIPT_NAME* fi echo "creating $IDR_INSTALL_SCRIPT_DIR (if necessary) and setting permissions" if [ -d $IDR_INSTALL_SCRIPT_DIR ] then echo "$IDR_INSTALL_SCRIPT_DIR exists, leaving permissions unchanged" else if ! mkdir $IDR_INSTALL_SCRIPT_DIR then echo "***** error creating $IDR_INSTALL_SCRIPT_DIR!" exit 1 fi if ! chmod $IDR_INSTALL_SCRIPT_DIR_PERM $IDR_INSTALL_SCRIPT_DIR then echo "***** error setting permissions on $IDR_INSTALL_SCRIPT_DIR!" exit 1 fi fi echo "copying $IDR_SCRIPT_NAME to $IDR_INSTALL_SCRIPT_DIR and setting permissions" if ! cp $IDR_SCRIPT_NAME $IDR_INSTALL_SCRIPT_DIR/ then echo "***** error copying $IDR_SCRIPT_NAME to $IDR_INSTALL_SCRIPT_DIR!" rmdir --ignore-fail-on-non-empty $IDR_INSTALL_SCRIPT_DIR exit 1 fi if ! chmod $IDR_INSTALL_SCRIPT_PERM $IDR_INSTALL_SCRIPT_DIR/$IDR_SCRIPT_NAME then echo "***** error setting permissions on $IDR_SCRIPT_NAME!" exit 1 fi echo "removing old $IDR_SCRIPT_NAME entry (if any) from $IDR_INIT_SCRIPT" if ! sed /$IDR_SCRIPT_NAME/d $IDR_INIT_SCRIPT > ./$IDR_TEMP then echo "***** sed error removing entry from $IDR_INIT_SCRIPT!" exit 1 fi if ! cp ./$IDR_TEMP $IDR_INIT_SCRIPT then echo "***** error writing back $IDR_INIT_SCRIPT!" exit 1 fi echo "adding $IDR_SCRIPT_NAME to $IDR_INIT_SCRIPT" if ! echo "if [ -x $IDR_INSTALL_SCRIPT_DIR/$IDR_SCRIPT_NAME ] ; then { date ; echo ; echo \"\$0: calling $IDR_INSTALL_SCRIPT_DIR/$IDR_SCRIPT_NAME load\" ; $IDR_INSTALL_SCRIPT_DIR/$IDR_SCRIPT_NAME load ; } > $IDR_INSTALL_SCRIPT_DIR/$IDR_SCRIPT_NAME.bootlog 2>&1 ; fi" >> $IDR_INIT_SCRIPT then echo "***** error adding entry to $IDR_INIT_SCRIPT!" exit 1 fi rm -f ./$IDR_TEMP echo "$IDR_SCRIPT_NAME added to $IDR_INIT_SCRIPT" echo "autoload complete" exit 0 fi # unload driver and clean up if [ $1 = remove ] then echo "$IDR_SCRIPT_NAME: command is $1" echo "unloading module from kernel (if present)" $IDR_RMMOD $IDR_MODULE echo "removing device nodes" rm -f /dev/$IDR_NODENAME* echo "deleting $IDR_MODULE.ko module from $IDR_MODULE_DIR" rm -f $IDR_MODULE_DIR/$IDR_MODULE.ko echo "removing $IDR_MODULE_DIR (if present and empty)" rmdir --ignore-fail-on-non-empty $IDR_MODULE_DIR echo "removing $IDR_SCRIPT_NAME entry (if any) from $IDR_INIT_SCRIPT" if ! sed /$IDR_SCRIPT_NAME/d $IDR_INIT_SCRIPT > ./$IDR_TEMP then echo "***** sed error removing entry from $IDR_INIT_SCRIPT!" exit 1 fi if ! cp ./$IDR_TEMP $IDR_INIT_SCRIPT then echo "***** error writing back $IDR_INIT_SCRIPT!" exit 1 fi rm -f ./$IDR_TEMP echo "deleting $IDR_SCRIPT_NAME (if present) from $IDR_INSTALL_SCRIPT_DIR" if ! rm -f $IDR_INSTALL_SCRIPT_DIR/$IDR_SCRIPT_NAME* then echo "***** error deleting $IDR_INIT_SCRIPT from $IDR_INSTALL_SCRIPT_DIR!" exit 1 fi echo "removing $IDR_INSTALL_SCRIPT_DIR (if present and empty)" rmdir --ignore-fail-on-non-empty $IDR_INSTALL_SCRIPT_DIR echo "remove complete" exit 0 fi # compile, load, build nodes, and set autoload if [ $1 = all ] then echo "$IDR_SCRIPT_NAME: command is $1 $2" if ! ./$IDR_SCRIPT_NAME remove then echo "***** remove error!" exit 1 fi if ! ./$IDR_SCRIPT_NAME compile $2 then echo "***** compile error!" exit 1 fi if ! ./$IDR_SCRIPT_NAME load then echo "***** load error!" exit 1 fi if ! ./$IDR_SCRIPT_NAME autoload then echo "***** autoload error!" exit 1 fi echo "" echo "remove, compile, load, and autoload complete" exit 0 fi # if unrecognized arg, bitch, and print usage echo "***** unrecongnized 1st argument!" ./$IDR_SCRIPT_NAME exit 1 ./idr_io.h0000644000076400007640000004762610223360163012300 0ustar wdwwdw00000000000000/* * idr_io.h * public ioctl and default definition file for Linux 2.0.35 driver for PCI DR11 boards * * (this file unchanged through 2.6.x versions of driver) * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * * 22 February, 1999 start of development - initial target Linux 2.0.35 (R.H. 5.1) * * 1 August, 2002 changed max buffer size to "orders" for allocation efficiency */ /* * this file is to be #include(d) in the program that calls the 10118 driver. * it is also used internally by the driver - don't modify this unless * you know what you are doing!!!! * * the PCI driver attempts to maintain ioctl command and argument compatibility * with the Sbus SunOS and Solaris drivers, and the Solaris Pci driver. * Some Sbus ioctls used the Sbus register include file * for the bit definitions of arguments. this driver will move those old compatibility * definitions into this _io.h file as sub-defines to command defines. * * including this file in the calling program will also cause idr_reg.h to be included. * this makes register bit defines available - to be used in the ioctls that access * and report the state of the pci board's registers. see idr_reg.h for bit definitions. * * some new ioctls are added to allow direct access to new registers, and to PCI * configuration data that wasn't available in the Sbus version */ #include /* * pick up register definitions for use by calling program - if not already included */ #ifndef _IDR_REG_H #include "./idr_reg.h" #endif /* the ioctl() function call looks like: * * ioctl(filedescriptor,command,argument) * * argument is used in some of the idr ioctl calls to provide values to * the driver ioctl routine, or return values to the calling program. * the argument is restricted to a maximum of 255 * bytes - by unix, and by the driver. * * the following ioctl commands * are available to programs using the idr driver: * * IDRIO_SET_MODE sets the mode register bits and the RDIS bit in * the latched functions register - Sbus compatibility * IDRIO_IMM_FCN sets the function bits immediately * IRDIO_READ_FCN sets the function bits before each block read * IDRIO_WRITE_FCN sets the function bits before each block write * IDRIO_IMM_PULSE issues pulse commands immediately * IDRIO_READ_PULSE issues pulse commands at each block read * IDRIO_WRITE_PULSE issues pulse commands at each block write * IDRIO_SET_DMA_TIME sets dma timeout value * IDRIO_SET_ATTN_TIME sets wait for attention timeout value * IDRIO_SET_RDY_TIME sets wait for ready timeout value * IDRIO_ATTN_WAIT waits for attention 0-to-1 transition * IDRIO_RDY_WAIT waits for READY * IDRIO_GET_STATUS gets flags/status register - Sbus compatibility * IDRIO_GET_RANGE gets range counter value * IDRIO_GET_REGS gets all registers including PLX chip's * note that latch reg bit DMIN is forced to 1 by * this ioctl(to allow reading data in reg) * IDRIO_GET_FLAGS returns various driver flag and error bits - Sbus compatibility * IDRIO_DATA_OUT writes 16 bits to DR11 output latches * IDRIO_DATA_IN reads 16 bits from DR11 input latches * IDRIO_SET_RANGE sets DR11 range counter in manual mode * IDRIO_AUTO selects auto mode (default). auto mode causes DR11 * range & controls to be set up for each read/write * call at same time as DMA logic is set up. DMA and * DR11 blocks are set to the same size, and a single * DMA block is used per DR11 block. in auto mode, all * that is necessary to do dma transfers is read or write * calls. * IDRIO_MANUAL selects manual mode. manual mode causes read/write * calls to set up DMA controller only. DR11 range * and controls must be set up explicitly by calling pgm. * manual mode allows multiple read/write calls per DR11 * block, or disabling the DR11 range counter and doing * continuous transfers. * IDRIO_START_READ * IDRIO_START_WRITE in manual mode, enables overall block-issued before * DR11 logic is enabled with GO command. and before the * first unix read or write call. * IDRIO_BLOCK_END in manual mode, disables overall block transfer-issued * after all DR11 transfers are complete, typically after * a wait for ready has returned successfully. * * *** new ioctl commands for PCI DR11-W emulators *** * * IDRIO_DEV_AND_VEND_ID return board device and vendor ids in arg * IDRIO_REVISION_ID return board revision level in arg * IDRIO_SET_NEW_MODE set PCI DR11 mode * IDRIO_GET_NEW_STATUS get PCI device status * IDRIO_GET_NEW_FLAGS get PCI flag bits * IDRIO_MASTER_CLEAR reset board - use only in dire emergencies! * */ /* the ioctl command codes conform to the unix pattern: * * the top 3 bits of the 32 bit value indicate whether arguments * are to be copied in, copied out, both, or neither. * * 0x80000000 = copy in * 0x40000000 = copy out * 0x20000000 = no argument transfer * 0xC0000000 = copy in and out * * the number of bytes in the argument is encoded in the lower * 8 bits of the upper half of the u_int, and the actual command * is encoded in the lower half. a rather arbitrary character, * which is intended to identify the driver, is also encoded in * the lower half of the command. it becomes part of the command * value. * * for all commands the arg length is part of * the command value. * * commands which require arguments - in or out - will pass those * arguments as unsigned integers. * * the magic character that identifies this driver is hereby * (arbitrarily) chosen to be 'D'. * * the following ioctl commands are defined using pre-existing * ioctl command macros. the CMD_MASK and COUNT_MASK values * defined MUST match the usage in ioccom.h. refer to that include * file for further information. */ /* * other versions of this driver used the entire lower 16 bits of the command to decode the specific * function requested. this includes both the ioctl type (magic letter) and number as defined * in linux/ioctl.h. we reconstruct the full function mask the hard way to try to protect against * the shifting sands of linux defines */ #define IDRIO_CMD_MASK ((_IOC_TYPEMASK << _IOC_TYPESHIFT) | (_IOC_NRMASK << _IOC_NRSHIFT)) #define IDRIO_COUNT_MASK IOCSIZE_MASK /* arg byte count here - IOCSIZE_MASK is already shifted left */ /* * THE SOLARIS 2.0 MACROS IN ioccom.h WANT THE 'MAGIC LETTER' QUOTED. * THE SOLARIS 1.X MACROS WANTED IT WITHOUT QUOTES. * LINUX WANTS QUOTES * * SOME VERSIONS OF ioccom.h DIDN'T INCLUDE _IORN AND _IOWN. SOL2 SEEMS TO HAVE THEM * linux/ioctl.h DOESN'T SEEM TO HAVE THEM * * we will use the lower-level _IOC macro (asm/ioctl.h) to define these since we need to * provide the size as an actual byte count, not as a type input to sizeof(), which is used * by the higher level (_IOR, _IOW) macros */ #define _IORN(type, nr, size) _IOC(_IOC_READ, (type), (nr), (size)) #define _IOWN(type, nr, size) _IOC(_IOC_WRITE, (type), (nr), (size)) #define IDRIO_SET_MODE _IOW('D',1,int) /* set mode reg bits & RDIS in latched reg */ /* Sbus compatibility ioctl - define old bits */ #define IDR_RDYT 0x80 /* long ready timing */ #define IDR_FMOD 0x40 /* not used in pci version */ #define IDR_BDIS 0x20 /* disable cycle req b */ #define IDR_SWAP 0x10 /* byte swap - not currently supported in pci */ #define IDR_CRQP 0x08 /* selects crq falling edge active */ #define IDR_BSYP 0x04 /* selects busy active high */ #define IDR_SPEED_MASK 0x03 /* two bits select handshake speed */ #define IDR_SPD_0 0x00 /* fastest timing */ #define IDR_SPD_1 0x01 #define IDR_SPD_2 0x02 #define IDR_SPD_3 0x03 #define IDR_RDISX 0x2000 /* disable range counter */ #define IDRIO_IMM_FCN _IOW('D',2,int) /* set function bits NOW - immediate write to fcn bits */ /* define old bits for Sbus driver compatibility */ #define IDR_FCN3 0x08 /* function bits */ #define IDR_FCN2 0x04 #define IDR_FCN1 0x02 #define IDR_FMASK 0x0E /* all function bits */ #define IDRIO_READ_FCN _IOW('D',3,int) /* set fcn bits at read start - fcn write deferred until */ /* following (and all later) read calls */ #define IDRIO_WRITE_FCN _IOW('D',4,int) /* set fcn bits at write time - as in read_fcn above */ #define IDRIO_IMM_PULSE _IOW('D',5,int) /* issue pulses NOW - immediate write to pulse reg */ /* masked to allow only device pulses */ /* define old bits for Sbus driver compatibility */ #define IDR_RATN 0x40 /* clear attention flag */ #define IDR_TERM 0x20 /* set ready true */ #define IDR_MCLR 0x10 /* master clear idr logic - NOT FOR FRIVOLOUS USE */ #define IDR_INIT 0x08 /* send init pulse to device */ #define IDR_ACF2 0x04 /* send acf2 pulse to device */ #define IDR_CYCL 0x02 /* force cycle */ #define IDR_GO 0x01 /* send go and enable dr11 transfers */ #define OLD_PULSE_MASK IDR_RATN | IDR_TERM | IDR_INIT | IDR_ACF2 | IDR_CYCL | IDR_GO #define IDRIO_READ_PULSE _IOW('D',6,int) /* issue pulses at read start - pulses deferred until */ /* next(and later) read calls */ #define IDRIO_WRITE_PULSE _IOW('D',7,int) /* issue pulses at write time - as in read_pulse above */ #define IDRIO_SET_DMA_TIME _IOW('D',8,int) /* DVMA block timeout in secs - also controls EOR */ /* timeout in auto mode. manual eor timeout uses */ /* RDY_TIME */ #define IDRIO_SET_ATTN_TIME _IOW('D',9,int) /* ATTENTION wait timeout # */ #define IDRIO_SET_RDY_TIME _IOW('D',10,int) /* READY wait timeout in secsonds */ #define IDRIO_ATTN_WAIT _IO('D',11) /* wait for ATTENTION flag */ #define IDRIO_RDY_WAIT _IO('D',12) /* wait for DR11 READY */ #define IDRIO_GET_STATUS _IOR('D',13,int) /* returns Sbus style DR11 status & flags reg */ /* Sbus compatibility ioctl - define old bits */ #define IDR_EORF 0x80 /* DR11 end-of-range flag */ #define IDR_ATTF 0x40 /* attention flag */ #define IDR_ATTN 0x20 /* actual attention signal */ #define IDR_MCER 0x10 /* multi-cycle error flag */ #define IDR_STAT_MASK 0x0E /* mask for device status bits */ #define IDR_STTA 0x08 /* status bits from device */ #define IDR_STTB 0x04 #define IDR_STTC 0x02 #define IDR_REDY 0x01 /* master DR11 ready bit (0=block xfer enabled) */ #define IDRIO_GET_RANGE _IOR('D',14,int) /* returns DR11 range register - gets actual register */ /* bits which are initially set to the WORD count */ /* minus 1 and decrement per DR11 16 bit transfer */ /* * return array size used here and in ioctl code * _IORN takes the direct byte count, nto a type to be * passed to the sizeof() operator, as is done with * the other _IOx macros */ #define IDR_RETURN_ARRAY_SIZE 19 /* in long words!! */ #define IDRIO_GET_REGS _IORN('D',15,IDR_RETURN_ARRAY_SIZE * 4) /* puts registers in arg */ /* * returns all "IDR" registers and some "PLX" registers. * also returns device IDs and revision level. * PLX registers are all 32 bits wide. DR11 registers are 8 * bits wide. except the data in register which is 16 bits wide. * each is returned in a 32 bit longword in as shown below. * the DMA registers returned are either channel 0 or 1, depending * on which is used in the particular revision of the board. * * (u_long larg[19]) * * larg[0] = device ID (high 16 bits) & vendor ID (low 16 bits) * larg[1] = revision ID * larg[2] = PLX interrupt control/status * larg[3] = PLX EEPROM control and user bits * larg[4] = PLX DMA mode * larg[5] = PLX DMA PCI address * larg[6] = PLX DMA local address * larg[7] = PLX DMA transfer count * larg[8] = PLX DMA descriptor pointer * larg[9] = PLX DMA command/status register * larg[10] = latched functions * larg[11] = flags * larg[12] = status * larg[13] = mode * larg[14] = DR11 range counter low byte * larg[15] = DR11 range counter mid byte * larg[16] = DR11 range counter high byte * larg[17] = fifo status * larg[18] = data in * */ #define IDRIO_GET_FLAGS _IOR('D',16,int) /* returns driver unit_flags */ #define IDR_DVMA_WAIT 0x80000000 /* waiting for t/c */ #define IDR_EOR_WAIT 0x40000000 /* waiting for EOR */ #define IDR_ATTN_WAIT 0x20000000 /* waiting for attention */ #define IDR_RDY_WAIT 0x10000000 /* waiting for ready */ #define IDR_DVMA_TIMEOUT 0x08000000 /* dma wait timed out */ #define IDR_EOR_TIMEOUT 0x04000000 /* eor wait timeout */ #define IDR_ATTN_TIMEOUT 0x02000000 /* attn timeout */ #define IDR_RDY_TIMEOUT 0x01000000 /* ready wait timeout */ #define IDR_MANUAL 0x00800000 /* manual mode */ #define IDR_INPUT 0x00400000 /* used w/waiting-for */ /* indicates xfer dir */ #define IDR_MCYL_ERR 0x00200000 /* multicycle error */ #define IDR_SIG_RECEIVED 0x00100000 /* sleep term'd by signal */ #define IDR_PAR_ERR 0x00080000 /* DR11 parity error - new for pci */ /* * idr_clear_flags used to reset the above flags (INSIDE DRIVER ONLY!!!!!!) */ #define IDR_CLEAR_FLAGS ~(IDR_DVMA_WAIT|IDR_EOR_WAIT|IDR_ATTN_WAIT|\ IDR_RDY_WAIT|IDR_DVMA_TIMEOUT|IDR_EOR_TIMEOUT|IDR_ATTN_TIMEOUT|IDR_RDY_TIMEOUT\ |IDR_MCYL_ERR|IDR_SIG_RECEIVED|IDR_PAR_ERR) #define IDRIO_DATA_OUT _IOW('D',17,int) /* 16 bits to DR11 output reg */ #define IDRIO_DATA_IN _IOR('D',18,int) /* reads DR11 input reg */ #define IDRIO_SET_RANGE _IOW('D',19,int) /* sets DR11 range register to arg - arg should be */ /* desired WORD count minus 1 */ #define IDRIO_AUTO _IO('D',20) /* selects manual mode - auto is defaule */ #define IDRIO_MANUAL _IO('D',21) /* selects manual mode - manual requires direct control */ /* of DR11 range count and block start and end by user */ #define IDRIO_START_READ _IO('D',22) /* enables overall block xfer - manual mode only! */ #define IDRIO_START_WRITE _IO('D',23) /* enables write block transfer */ #define IDRIO_BLOCK_END _IO('D',24) /* terminates block xfers - required in manual mode - */ /* not used in auto mode */ /* * start read or write enables the DR11 control logic. block end disables it * start read or write should be issued before the first unix read or write * call in manual mode. these ioctls do not issue a go or cycle pulse, that * must be done by the calling program AFTER the start ioctl. */ /* *** new ioctls for PCI DR11 *** */ #define IDRIO_DEV_AND_VEND_ID _IOR('D',25,int) /* returns device and vendor ids in arg */ /* vendor id is low 16 bits, device id is high 16 bits */ #define IDRIO_REVISION_ID _IOR('D',26,int) /* returns board revision level in arg */ #define IDRIO_SET_NEW_MODE _IOW('D',27,int) /* sets PCI DR11 mode */ #define IDRIO_GET_NEW_STATUS _IOR('D',28,int) /* gets PCI DR11 status register */ #define IDRIO_GET_NEW_FLAGS _IOR('D',29,int) /* gets PCI DR11 flags register */ #define IDRIO_MASTER_CLEAR _IO('D',30) /* full reset of board - emergencies only! */ /* * define various flags and constants * * the _DEF defines may be modified by the user to change te module install time * configuration and operating behavior of the board(s) and driver. * at this time, these parameters apply to all boards operating under * a particular driver * * THESE DEFINES SHOULD NOT BE MODIFIED HERE - TYESE ARE THE DEFAULTS USED WHEN THERE * ARE NO OVERRIDING VALUES SUPPLIED TO insmod * IF IT IS NECESSARY TO CHANGE THE DEFAULT BEHAVIOR OF THE DRIVER, * MAKE CHANGES BY SUPPLYING APPROPRIATE VALUES TO insmod * * the values that may be modified at module install time are; * * speed_def handshake speed - 0=fastest, 3=slowest * dma_time_def dma timeout in seconds * attn_time_def attention timeout in seconds * rdy_time_def ready timeout in seconds * byte_swap_def 0=no swap, 1=swap * cycle_pol_def cycle polarity - 0=rising edge, 1=falling edge * busy_pol_def busy polarity - 0=asserted low, 1=asserted high * write_cycle_def write cycle - 1=issue soft cycle, 0=no soft cycle * read_cycle_def read cycle - 0=no soft cycle, 1=issue soft cycle * read_acf2_def acf2 pulse - 0=no pulse on read, 1=pulse on read * * **the function bits are listed separately for historical reasons** * * open_f3_def function 3 bit on open - 0=clear, 1=set * open_f2_def function 2 bit on open - 0=clear, 1=set * open_f1_def function 1 bit on open - 0=clear, 1=set * write_f3_def function 3 bit on write - 0=clear, 1=set * write_f2_def function 2 bit on write - 0=clear, 1=set * write_f1_def function 1 bit on write - 0=clear, 1=set * read_f3_def function 3 bit on read - 0=clear, 1=set * read_f2_def function 2 bit on read - 0=clear, 1=set * read_f1_def function 1 but on read - 0=clear, 1=set * * * max_phys_def maximum dma transfer (in k) * max_phys_def * 1024 is largest user buffer that * will be transferred in one chunk * larger buffers will be broken into max_phys_def * sized dma blocks * * example: /sbin/insmod idr_debug.o max_boards=3 * * the above loads the version of the driver with debug printing enabled, and probes for * a maximum of three boards */ #define DMA_TIME_MAX 600 /* protect against hanging if caller asks for giant # */ #define ATTN_TIME_MAX 600 #define RDY_TIME_MAX 600 #define DMA_TIME_MIN 10 /* try to prevent timeout during legit dma or wait */ #define ATTN_TIME_MIN 10 #define RDY_TIME_MIN 10 #define SPEED_DEF 0 /* handshake speed default */ #define DMA_TIME_DEF 30 /* dma time-out default seconds */ #define ATTN_TIME_DEF 30 /* fifo empty and <1/2 full time-out */ #define RDY_TIME_DEF 30 /* ready wait time default */ #define BYTE_SWAP_DEF 0 /* don't swap bytes (for SPARC, this means swap bytes) */ #define CYCLE_POL_DEF 0 /* cycle req rising edge active */ #define BUSY_POL_DEF 0 /* busy asserted low */ #define WRITE_CYCLE_DEF 1 /* issue soft cycle on write */ #define READ_CYCLE_DEF 0 /* not on read */ #define READ_ACF2_DEF 0 /* do not pulse aclo fcn2 on read (&never on write) */ #define OPEN_F3_DEF 0 /* function bit defaults for idr_open() time */ #define OPEN_F2_DEF 0 #define OPEN_F1_DEF 0 #define WRITE_F3_DEF 1 /* function bit defaults for write block */ #define WRITE_F2_DEF 0 #define WRITE_F1_DEF 0 #define READ_F3_DEF 1 /* function bit defaults for read block */ #define READ_F2_DEF 0 #define READ_F1_DEF 1 #define MAX_PHYS_ORDER_DEF 4 /* 2**max_phys_order * PAGE_SIZE = max dma xfer */ /* larger user buf is xferred in chunks */ /* 4 -> 64K max w/4K pages */ #define MAX_BOARDS_DEF 1 /* default to one board, to save a little kernel memory */ ./idr.loop.test.c0000644000076400007640000002271010223360163013515 0ustar wdwwdw00000000000000/* * This is a very simple loop-back test program for the IKON 10116 or 10118 * PCI bus DR11-W emulator and its driver. * * It first tests the software timeout functions, and then loops continuously * on tests of various board and driver functions. After each 10 successful passes, * it outputs a pass count . In the event of a failure, it prints a brief * message describing the test that failed, and halts. * * THIS VERSION FOR LATER 10116 & 10118 BOARDS THAT CAN "UN-SWAP" THE BYTE LANE SWAPPING * THAT OCCURS BETWEEN THE SPARC PROCESSOR AND THE PCI BUS * * 4 February, 2004 minor updates for readability */ #include #include #include #include "./idr_io.h" u_short datarray[10000]; int ii, i, f; extern errno; char devname[16]; char devpath[32] = "/dev/\0"; u_long arg; int pass_count = 0; int count_by_100 = 0; main() { printf("\nIKON 10116/10118 PCI bus DR11-W emulator loop-back test\n\n"); printf ("THIS VERSION FOR LATER 10116 AND ALL 10118 BOARDS THAT CAN UN-SWAP DMA BYTES IN SPARC INSTALLATIONS\n\n"); printf("A suitable loop-back cable must be installed before this test is run.\n\n"); printf("Three timeout test will be done first - each takes 10 seconds.\n"); printf("The timeout tests will cause a DMA timeout message on the console.\n"); printf("Following that an endless loop of loop-back tests will be performed.\n"); printf("At the end of every 100 successfull passes, the pass count will be output.\n"); printf("An error will print a message and halt the tests.\n"); printf("Use control-C to terminate the tests.\n\n"); printf("Please enter the name of the device to be tested: (idr0...idr9, quit) "); scanf("%15s", devname); if (strcmp(devname, "quit\0") == 0) exit(0); strcat(devpath, devname); printf("\ntesting device %s\n", devpath); /* * open the device */ f = open(devpath, O_RDWR); if (f == -1) { printf("could not open device, errno = 0x%x\n", errno); exit(0); } /* * set the timeout values to 10 seconds each and test */ arg = 10; if (ioctl(f, IDRIO_SET_ATTN_TIME, &arg) == -1) { printf("test# 00.10: error setting attention timeout, errno = 0x%x\n", errno); exit(0); } /* * do a wait for attention - should time out */ if (ioctl(f, IDRIO_ATTN_WAIT, &arg) != -1) { printf("test#00.20: attention wait timeout test did not time out\n"); exit(0); } printf("attention wait timeout test OK\n"); arg = 10; if (ioctl(f, IDRIO_SET_RDY_TIME, &arg) == -1) { printf("test# 00.30: error setting ready timeout, errno = 0x%x\n", errno); exit(0); } /* * issue a go - to clear ready */ arg = IDR_GO; if (ioctl(f, IDRIO_IMM_PULSE, &arg) == -1) { printf("test# 00.40: error sending go pulse, errno = 0x%x\n", errno); exit(0); } /* * do a ready wait - should time out */ if (ioctl(f, IDRIO_RDY_WAIT, &arg) != -1) { printf("test# 00.50: ready wait timeout test did not time out\n"); exit(0); } printf("ready wait timeout test OK\n"); /* * pulse term to set ready again */ arg = IDR_TERM; if (ioctl(f, IDRIO_IMM_PULSE, &arg) == -1) { printf("test# 00.60: error issuing aclo-fnct2 pulse, errno = 0x%x\n", errno); exit(0); } arg = 10; if (ioctl(f, IDRIO_SET_DMA_TIME, &arg) == -1) { printf("test# 00.70: error setting dma timeout, errno = 0x%x\n", errno); exit(0); } /* * make sure that the cycle bit will not be set during dma - to ensure a timeout */ arg = IDR_GO; if (ioctl(f, IDRIO_READ_PULSE, &arg) == -1) { printf("test# 00.80: error setting read pulses, errno = 0x%x\n", errno); exit(0); } /* * do a dma input - should time out */ if (read(f, datarray, 10) != -1) { printf("test# 00.90: dma timeout test did not time out\n"); exit(0); } printf("dma timeout test OK\n"); /* * loop forever on other tests */ printf("starting endless loop-back tests\n"); while (1) { for (count_by_100 = 0; count_by_100 < 100; count_by_100++, pass_count++) { /* * "rewind" the driver - some versions of unix count total bytes transferred * and block further reads/writes when some giant number overflows */ if (lseek(f, (off_t) 0, 0) == -1) { printf("test# 10.00: lseek error, errno = 0x%x\n", errno); exit(0); } /* * clear dr11 logic - MASTER CLEAR SHOULD NOT BE USED BY MOST PROGRAMS!!!!! */ arg = IDR_MCLR; if (ioctl(f, IDRIO_IMM_PULSE, &arg) == -1) { printf("test# 10.01: error issuing master clear pulse, errno = 0x%x\n", errno); exit(0); } /* * test function/status bits */ /* * set function bits */ arg = IDR_FCN1 | IDR_FCN2 | IDR_FCN3; if (ioctl(f, IDRIO_IMM_FCN, &arg) == -1) { printf("test# 10.10: error setting function bits, errno = 0x%x\n", errno); exit(0); } /* * read status bits */ if (ioctl(f, IDRIO_GET_STATUS, &arg) == -1) { printf("test# 10.20: error reading status, errno = 0x%x\n", errno); exit(0); } /* * should have status a, b, and c, ready, attention, and attention flag set */ if (arg != (IDR_STTA | IDR_STTB | IDR_STTC | IDR_REDY | IDR_ATTN | IDR_ATTF)) { printf("test# 10.30: incorrect status bits: status = 0x%x\n", arg); exit(0); } /* * clear function bits */ arg = 0; if (ioctl(f, IDRIO_IMM_FCN, &arg) == -1) { printf("test# 10.40: error setting function bits, errno = 0x%x\n", errno); exit(0); } /* * attention flag should still be set */ if (ioctl(f, IDRIO_GET_STATUS, &arg) == -1) { printf("test# 10.50: error reading status, errno = 0x%x\n", errno); exit(0); } if (arg != (IDR_REDY | IDR_ATTF)) { printf("test# 10.60: incorrect status bits: status = 0x%x\n", arg); exit(0); } /* * do a wait for attention flag - it should find the ATTF and clear it */ if (ioctl(f, IDRIO_ATTN_WAIT, &arg) == -1) { printf("test# 10.70: error waiting for attention, errno = 0x%x\n", errno); exit(0); } if (ioctl(f, IDRIO_GET_STATUS, &arg) == -1) { printf("test# 10.80: error reading status, errno = 0x%x\n", errno); exit(0); } if (arg != IDR_REDY) { printf("test# 10.90: incorrect status bits: status = 0x%x\n", arg); exit(0); } /* * test pulse commands - actually most pulses are used in normal operation * of other portions of the driver - particularly for dma transfers and interrupts * the only testable pulses in loopback are aclo-fnct2 and cycl (which is used for dma) */ arg = IDR_ACF2; if (ioctl(f, IDRIO_IMM_PULSE, &arg) == -1) { printf("test# 20.10: error pulsing acf2, errno = 0x%x\n", errno); exit(0); } /* * should have caused an attention pulse - and set attention flag */ if (ioctl(f, IDRIO_GET_STATUS, &arg) == -1) { printf("test# 20.20: error reading status, errno = 0x%x\n", errno); exit(0); } if (arg != (IDR_REDY | IDR_ATTF)) { printf("test# 20.30: incorrect status bits: status = 0x%x\n", arg); exit(0); } /* * test data out and in registers */ arg = 0x5555; if (ioctl(f, IDRIO_DATA_OUT, &arg) == -1) { printf("test# 30.10: error writing data out, errno = 0x%x\n", errno); exit(0); } if (ioctl(f, IDRIO_DATA_IN, &arg) == -1) { printf("test# 30.20: error reading data in, errno = 0x%x\n", errno); exit(0); } if (arg != 0x5555) { printf("test# 30.30: incorrect data in: data = 0x%x\n", arg); exit(0); } arg = 0xaaaa; if (ioctl(f, IDRIO_DATA_OUT, &arg) == -1) { printf("test# 30.40: error writing data out, errno = 0x%x\n", errno); exit(0); } if (ioctl(f, IDRIO_DATA_IN, &arg) == -1) { printf("test# 30.50: error reading data in, errno = 0x%x\n", errno); exit(0); } if (arg != 0xaaaa) { printf("test# 30.60: incorrect data in: data = 0x%x\n", arg); exit(0); } /* * test dma output * we will write a counting pattern to the buffer, and output it * after the block transfer, the last value sent should be readable * in the input data register */ for (i = 0; i < 0x1000; i++) datarray[i] = i; arg = IDR_GO | IDR_CYCL; if (ioctl(f, IDRIO_WRITE_PULSE, &arg) == -1) { printf("test# 40.01: error setting write pulse, errno = 0x%x\n", errno); exit(0); } if (write(f, datarray, 0x2000) == -1) { printf("test# 40.10: error doing dma write, errno = 0x%x\n", errno); exit(0); } if (ioctl(f, IDRIO_DATA_IN, &arg) == -1) { printf("test# 40.20: error reading data in, errno = 0x%x\n", errno); exit(0); } if (arg != 0xfff) { printf("test# 40.30: incorrect data in: data = 0x%x, should be 0xfff\n", arg); exit(0); } /* * test dma input * we will write a fixed value to the data in register and then do a dma input * the value written should be duplicated throughout the input buffer */ arg = 0x1234; if (ioctl(f, IDRIO_DATA_OUT, &arg) == -1) { printf("test# 50.10: error writing data out, errno = 0x%x\n", errno); exit(0); } arg = IDR_GO | IDR_CYCL; if (ioctl(f, IDRIO_READ_PULSE, &arg) == -1) { printf("test# 50.11: error setting read pulse, errno = 0x%x\n", errno); exit(0); } if (read(f, datarray, 0x2000) == -1) { printf("test# 50.10: error doing dma read, errno = 0x%x\n", errno); exit(0); } for (i = 0; i < 0x1000; i++) if (datarray[i] != 0x1234) { printf ("test# 50.20: incorrect read buffer data: datarray[0x%x]=0x%x, should be 0x1234\n", i, datarray[i]); exit(0); } } printf("pass count = %d\n", pass_count); } } ./idr_reg.h0000644000076400007640000005305610223360163012440 0ustar wdwwdw00000000000000/* * idr_reg.h * Register definition file for Linux driver for PCI DR11 boards * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * * 22 February, 1999 start of development - initial target Linux 2.0.35 (R.H. 5.1) * * 14 Sept, 1999 re-code for linux 2.2 -- no longer compatible with earlier * linux revs * * 11 November, 1999 update MODINFOBANNER * * 13 March, 2000 update MODINFOBANNER * * 24 March, 2000 modified for kiobuf user buffer dma capability * added IOPB put and get macros to allow for possible byte * lane swizzling of dma access to iopb list memory on other architectures * added IOPB defines * updated MODINFOBANNER * * added local add space 0/expansion rom bus region * descriptor register, so we can turn off address space * 0 pre-fetch * * 30 August, 2000 use readl, writel, rather than direct dereference on GETL, PUTL macros * direct access ok on x86, but not other archs * updated MODINFOBANNER * * 14 November, 2000 updated MODINFOBANNER * * 18 June, 2001 updated MODINFOBANNER * * 1 August, 2002 moved "software" macros from this file to idr_var.h * * deleted MODINFOBANNER (replaced w/macros in idr_var.h) */ /* * This code released under the GPL, and in the public domain * References to IKON left in place for compatibility and historical reasons */ #ifndef _IDR_REG_H #define _IDR_REG_H #endif /* * * IKON's PCI DR11-W emulators include the following: * * Model 10116 full size card * Model 10118 half size card * * The boards share a common register set, and operate under * the same software driver. The boards have almost identical features - * the driver will verify that a particular board supports a requested function. * * In particular, the 10118 does not have local memory for dma chain lists, and may in some * versions use DMA channel 1, rather than dma channel 0 * * All boards use some version of PLX technology's 9060 or 9080 PCI interface chip. Various * version of this chip determine the exact DMA setup requirements for the board. Some versions * support chaining DMA from local memory only, some support chaining lists in PCI memory, * and some implement DMA channel 1 only (channel 0 is used in most versions of the board). * Some versions of the chip require that we access the dma registers via a window in * the idr address range, and some require direct access via the plx address range. * * * There are three address ranges supported (and required) by the hardcopy boards: * configuration space, PLX register space, and IDR register space. The board's registers * are accesses through various ddi_... functions, which take an address as one of the arguments. * We will access the registers using defined offsets from the appropriate address range * base addresses. Macros will be used to shorten the typing task somewhat. * */ /* * the configuration space offsets and bit definitions are * provided in pci.h */ /* * plx run-time register offsets - when accessed directly via the * plx address range */ #define PLX_ENDIAN_REG 0x0C /* endian control - actually a local config reg - 9080 ONLY */ #define PLX_ADD_0_ROM_DESC 0x18 /* add space 0/rom descriptor register */ #define PLX_MBX_0 0x40 /* mailbox registers */ #define PLX_MBX_1 0x44 #define PLX_MBX_2 0x48 #define PLX_MBX_3 0x4C #define PLX_MBX_4 0x50 #define PLX_MBX_5 0x54 #define PLX_MBX_6 0x58 #define PLX_MBX_7 0x5C #define PLX_PTOL_BELL 0x60 /* doorbell regsiters */ #define PLX_LTOP_BELL 0x64 #define PLX_INT_CSTAT 0x68 /* interrupt control/status regsiter */ #define PLX_EEPROM_USER 0x6C /* eeprom control and user bits */ #define PLX_DMA_MODE_0 0x80 /* dma channel 0 mode register */ #define PLX_DMA_PCI_ADD_0 0x84 /* pci address for dma transfer */ #define PLX_DMA_LOC_ADD_0 0x88 /* local address for dma transfer */ #define PLX_DMA_COUNT_0 0x8C /* dma transfer count in bytes */ #define PLX_DMA_DESC_PTR_0 0x90 /* dma descriptor pointer */ #define PLX_DMA_MODE_1 0x94 /* dma channel 1 mode register */ #define PLX_DMA_PCI_ADD_1 0x98 /* pci address for dma transfer */ #define PLX_DMA_LOC_ADD_1 0x9C /* local address for dma transfer */ #define PLX_DMA_COUNT_1 0xA0 /* dma transfer count in bytes */ #define PLX_DMA_DESC_PTR_1 0xA4 /* dma descriptor pointer */ #define PLX_DMA_CMD_STAT_BOTH 0xA8 /* dma command/status for both channels combined */ #define PLX_DMA_CMD_STAT_0 0xA8 /* dma command/status for channel 0 */ #define PLX_DMA_CMD_STAT_1 0xA9 /* dma command/status for channel 1 */ #define PLX_DMA_ARB_REG 0xAC /* local/dma arbitration register */ #define PLX_DMA_THRESH 0xB0 /* DMA threshold register */ /* * plx run-time register offsets when accesses via window * in idr address space */ #define IPLX_ENDIAN_REG 0x8C /* endian control - actually a local config reg - 9080 ONLY */ #define IPLX_ADD_0_ROM_DESC 0x98 /* add space 0/rom descriptor register */ #define IPLX_MBX_0 0xC0 /* mailbox registers */ #define IPLX_MBX_1 0xC4 #define IPLX_MBX_2 0xC8 #define IPLX_MBX_3 0xCC #define IPLX_MBX_4 0xD0 #define IPLX_MBX_5 0xD4 #define IPLX_MBX_6 0xD8 #define IPLX_MBX_7 0xDC #define IPLX_PTOL_BELL 0xE0 /* doorbell regsiters */ #define IPLX_LTOP_BELL 0xE4 #define IPLX_INT_CSTAT 0xE8 /* interrupt control/status regsiter */ #define IPLX_EEPROM_USER 0xEC /* eeprom control and user bits */ #define IPLX_DMA_MODE_0 0x100 /* dma channel 0 mode register */ #define IPLX_DMA_PCI_ADD_0 0x104 /* pci address for dma transfer */ #define IPLX_DMA_LOC_ADD_0 0x108 /* local address for dma transfer */ #define IPLX_DMA_COUNT_0 0x10C /* dma transfer count in bytes */ #define IPLX_DMA_DESC_PTR_0 0x110 /* dma descriptor pointer */ #define IPLX_DMA_MODE_1 0x114 /* dma channel 1 mode register */ #define IPLX_DMA_PCI_ADD_1 0x118 /* pci address for dma transfer */ #define IPLX_DMA_LOC_ADD_1 0x11C /* local address for dma transfer */ #define IPLX_DMA_COUNT_1 0x120 /* dma transfer count in bytes */ #define IPLX_DMA_DESC_PTR_1 0x124 /* dma descriptor pointer */ #define IPLX_DMA_CMD_STAT_BOTH 0x128 /* dma command/status for both channels */ #define IPLX_DMA_CMD_STAT_0 0x128 /* individual dma cmd/stat offsets */ #define IPLX_DMA_CMD_STAT_1 0x129 #define IPLX_DMA_ARB_REG 0x12C /* local/dma arbitration register */ #define IPLX_DMA_THRESH 0x130 /* DMA threshold register */ /* * bit definitions for the PLX run-time registers */ /* * endian control register - 9080 ONLY */ #define CONFIG_BIG_ENDIAN 0x00000001 /* causes local accesses to config space to be big endian - NOT USED */ #define MASTER_BIG_ENDIAN 0x00000002 /* direct master big endian - NOT USED */ #define SLAVE_BIG_ENDIAN 0x00000004 /* direct slave access to idr space big endian */ #define SLAVE_ROM_BIG_ENDIAN 0x00000008 /* direct slave accesses to rom space big endian - NOT USED */ #define BYTE_LANE_ENDIAN_MODE 0x00000010 /* causes upper word or byte to be used for access to 16 or 8 bit local bus */ #define SLAVE_1_BIG_ENDIAN 0x00000020 /* direct slave accesses to add space 1 big endian - NOT USED */ #define DMA_1_BIG_ENDIAN 0x00000040 /* dma channel 1 accesses big endian */ #define DMA_0_BIG_ENDIAN 0x00000080 /* dma channel 0 accesses big endian */ /* * address space 0/rom descriptor register * * for now, we only define the one bit we need */ #define ADD_0_PREFETCH_DISABLE 0x00000100 /* disable any prefetch reads to address space 0 */ /* * interrupt control/status register */ #define LSERR_ABORT_INT_ENABLE 0x00000001 /* enables local interrupt on target or master abort */ #define LSERR_PARITY_INT_ENABLE 0x00000002 * enable local interrupt on PCI parity error */ #define PCI_SERR 0x00000004 /* forces assertion of PCI SERR# signal */ #define MAILBOX_INT_ENABLE 0x00000008 /* enables local interrupt on mailbox write from PCI 9080 ONLY */ #define DMA_0_INTERRUPT_SELECT 0x00000010 /* 1 causes dma 0 interrupt to assert PCI interrupt */ /* 0 causes dma 0 interrupt to assert local interrupt */ /* 9080 ONLY for other chips loop local out to local in on board */ #define DMA_1_INTERRUPT_SELECT 0x00000020 /* same as above - for dma channel 1 */ #define PCI_INTERRUPT_ENABLE 0x00000100 /* enables pci interrupts */ #define PCI_DB_INT_ENABLE 0x00000200 /* enables pci doorbell interrupts w/pci int enable */ #define PCI_ABORT_INT_ENABLE 0x00000400 /* enables pci interrupt on master or target abort w/pci int enb */ #define PCI_LOCAL_INT_ENABLE 0x00000800 /* enbles local interrupt to cause pci interrupt w/pci int enb */ #define RETRY_ABORT_ENABLE 0x00001000 /* 256 consecutive master retries will cause target abort */ #define PCI_DB_INTERRUPT 0x00002000 /* 1 indicates pci doorbell interrupt is active */ #define PCI_ABORT_INTERRUPT 0x00004000 /* 1 indicates pci abort interrupt is active */ #define LOCAL_INTERRUPT 0x00008000 /* 1 indicates local interrupt is active */ #define LOCAL_INT_OUT_ENABLE 0x00010000 /* enables local interrupt output */ #define LOCAL_DOORBELL_INT_ENB 0x00020000 /* enables local doorbell interrupts w/local int enb */ #define LOCAL_DMA_0_INT_ENABLE 0x00040000 /* enables dma chan 0 local interrupt w/local int enb */ #define LOCAL_DMA_1_INT_ENABLE 0x00080000 /* enables dma chan 1 local interrupt w/local int enb */ #define LOCAL_DB_INTERRRUPT 0x00100000 /* 1 indicates local doorbell interrupt is active */ #define DMA_0_INTERRUPT 0x00200000 /* 1 indicates dma channel 0 interrupt is active */ #define DMA_1_INTERRUPT 0x00400000 /* 1 indicates dma channel 1 interrupt is active */ #define BIST_INTERRUPT 0x00800000 /* 1 indicates that bilt in self test int is active */ #define DIRECT_MASTER_ABORT_FLAG 0x01000000 /* 1 indicates abort during direct master xfer */ #define DMA_0_ABORT_FLAG 0x02000000 /* 1 indicates abort during dma chan 0 xfer */ #define DMA_1_ABORT_FLAG 0x04000000 /* 1 indicates abort during dma chan 1 xfer */ #define RETRY_ABORT_FLAG 0x08000000 /* 1 indicates abort after 256 retries */ #define MBX_0_FLAG 0x10000000 /* 1 indicates PCI wrote to mailbox 0 9080 ONLY */ #define MBX_1_FLAG 0x20000000 /* 1 indicates PCI wrote to mailbox 1 9080 ONLY */ #define MBX_2_FLAG 0x40000000 /* 1 indicates PCI wrote to mailbox 2 9080 ONLY */ #define MBX_3_FLAG 0x80000000 /* 1 indicates PCI wrote to mailbox 3 9080 ONLY */ /* * eeprom control, pci command codes, user bits, init control */ #define DMA_READ_CMD_MASK 0x0000000F /* masks bits used as pci command during dma read */ #define DMA_READ_CMD_DEF 0x0000000E /* power up default for dma reads */ #define DMA_WRITE_CMD_MASK 0x000000F0 /* masks bits used as pci command during dma write */ #define DMA_WRITE_CMD_DEF 0x00000070 /* power up default for dma writes */ #define MASTER_READ_CMD_MASK 0x00000F00 /* masks bits used as pci command during direct master read */ #define MASTER_READ_CMD_DEF 0x00000600 /* power up default for direct master reads */ #define MASTER_WRITE_CMD_MASK 0x0000F000 /* masks bits used as pci command during direct master write */ #define MASTER_WRITE_CMD_DEF 0x00007000 /* power up default for direct master writes */ #define USER_OUTPUT 0x00010000 /* user output bit */ #define USER_INPUT 0x00020000 /* user input bit */ #define EEPROM_CLOCK 0x01000000 /* toggling this bit generates an eeprom clock */ #define EEPROM_CHIP_SELECT 0x02000000 /* drives the eeprom chip select */ #define EEPROM_WRITE_BIT 0x04000000 /* write bit to eeprom */ #define EEPROM_READ_BIT 0x08000000 /* read bit from eeprom */ #define EEPROM_PRESENT 0x10000000 /* 1 indicates that an eeprom is present */ #define CONFIGURATION_RELOAD 0x20000000 /* 0-to-1 causes reload of configuration registers from eeprom */ #define PLX_SOFT_RESET 0x40000000 /* holds plx chip reset and aserts local reset out (big hammer!) */ #define LOCAL_INIT_DONE 0x80000000 /* local init done - since no local master - this is held at 1 by #NB pin */ /* * dma channels 0 and 1 mode register bits */ #define DMA_BUS_WIDTH_MASK 0x00000003 /* mask for dma local bus width bits */ #define DMA_BUS_8_BIT 0x00000000 /* 8 bit dma bus */ #define DMA_BUS_16_BIT 0x00000001 /* 16 bit dma bus */ #define DMA_BUS_32_BIT 0x00000002 /* 32 bit dma bus */ #define DMA_WAIT_STATE_MASK 0x0000003C /* mask for local bus wait state count */ #define DMA_WAIT_1 0x00000004 /* one wait state used for current boards */ #define DMA_READY_IN_ENABLE 0x00000040 /* enable ready input during dma */ #define DMA_BTERM_IN_ENABLE 0x00000080 /* enable bterm input during dma */ #define DMA_BURST_ENABLE 0x00000100 /* enable local bursting during dma */ #define DMA_CHAIN_ENABLE 0x00000200 /* enable dma chaining */ #define DMA_DONE_INTERRUPT_ENB 0x00000400 /* enable dma done interrupt */ #define DMA_LOCAL_ADD_HOLD 0x00000800 /* 0 causes local add to increment during dma */ #define DMA_DEMAND_MODE 0x00001000 /* enables demand mode dma (DREQ# input) */ #define DMA_WRITE_INVALID 0x00002000 /* perform write and invalidate cycles 9080 ONLY */ #define DMA_EOT_ENABLE 0x00004000 /* enable eot input 9060SD and 9080 only */ #define DMA_STOP_MODE 0x00008000 /* 0=blast terminates xfer, 1=eot on, */ /* or dreq off in demand mode terminates 9060SD and 9080 ONLY */ #define DMA_CLEAR_COUNT_MODE 0x00010000 /* causes byte count in each descriptor to be zeroed when done 9080 ONLY */ /* * dma pci address registers, local address registers, and * transfer size registers have no special control bits * * transfer count register is limited to lower 23 bits * =8Mbyte max xfer per chain list element */ #define DMA_XFER_COUNT_MASK 0x007fffff /* * dma channels 0 and 1 descriptor pointer register bits */ #define DMA_CHAIN_IN_PCI_MEM 0x00000001 /* if set, dma chain list in pci memory, otherwise local memory */ /* some boards do not have local memory */ #define DMA_END_OF_CHAIN 0x00000002 /* if set, this chain element is last in list */ #define DMA_LINK_INTERRUPT 0x00000004 /* if set, interrupt when this element's count is exhausted */ #define DMA_INPUT 0x00000008 /* if set, direction of xfer is local to PCI */ #define DMA_NEXT_ADDRESS_MASK 0xfffffff0 /* bit mask for address of next quad word list element */ /* * dma channels 0 and 1 have identical BYTE wide command/status registers * which may be accessed at separate byte addresses, or as a single 16 or 32 * bit register * * dma channel 0 is at the lower offset, channel 1 is the next higher byte offset * * the following defines assume that both registers are accessed at the same time * as a 16 or 32 bit value * * if for some reason it is necessary to do an access to a single byte-wide register, * the DMA_0... defines should be used for both registers (and obviously getb and putb should be used */ #define DMA_0_ENABLE 0x01 /* enables the dma channel when 1 - disables (pauses) when 0 */ #define DMA_0_START 0x02 /* 1 starts the dma transfer if enable true */ #define DMA_0_ABORT 0x04 /* 1 aborts the transfer, leaves enable true */ #define DMA_0_CLEAR_INTERRUPT 0x08 /* 1 clears the dma interrupt */ #define DMA_0_DONE 0x10 /* 1 indicates that the dma transfer is complete */ #define DMA_1_ENABLE 0x0100 #define DMA_1_START 0x0200 #define DMA_1_ABORT 0x0400 #define DMA_1_CLEAR_INTERRUPT 0x0800 #define DMA_1_DONE 0x1000 /* * dma arbitration and threshold registers left at * default of 0 for current board implementations */ /* * idr (ikon logic portion of board) register offsets * registers are byte wide in low byte of 32 bit longword * * all idr registers may be accessed as 32 bit values */ #define IDR_LATCHED_FUNCTIONS 0x00 /* (read/write) latched function and mask bits */ #define IDR_116_PULSES 0x04 /* (write) internal pulses */ #define IDR_FLAGS 0x04 /* (read) flag bits */ #define IDR_DEVICE_PULSES 0x08 /* (write) external pulses */ #define IDR_STATUS 0x08 /* (read) status bits */ #define IDR_MODE 0x0C /* (read/write) mode control bits */ #define IDR_RANGE_LOW 0x10 /* (read/write) range register low 8 bits */ #define IDR_RANGE_MID 0x14 /* (read/write) range register mid 8 bits */ #define IDR_RANGE_HIGH 0x18 /* (read/write) range register high 8 bits */ #define IDR_FIFO_STATUS 0x1C /* (read) fifo status bits */ #define IDR_DATA_OUT 0x40 /* (write) p-i/o data to device */ #define IDR_DATA_IN 0x40 /* (read) p-i/o data from device */ #define IDR_PLX_READBACK 0x60 /* (read) not used w/current boards */ #define IDR_PLX_RUNTIME_BASE 0x80 /* base of plx window in idr address range */ #define IDR_MEMORY_BASE 0x140 /* base of local memory (some models) */ /* * bit definitions for the ikon (idr) regsiters */ /* * latched function register bits */ #define END_OF_RANGE_INT_ENB 0x80 /* end of range interrupt mask */ #define ATTENTION_INT_ENB 0x40 /* attention interrupt mask */ #define IDR_DMA_ENABLE 0x20 /* enable idr logic to rq dma transfers */ #define DMA_INPUT_MODE 0x10 /* 1 = dma input to memory, 0 = output */ #define FUNCTION_MASK 0x0E /* mask for function bits */ #define FUNCTION_3 0x08 /* function bits */ #define FUNCTION_2 0x04 #define FUNCTION_1 0x02 /* * 116 pulse register bit definitions */ #define RESET_EOR_FLAG 0x80 /* resets end of range flag */ #define RESET_ATTENTION_FLAG 0x40 /* resets attention flag */ #define RESET_ERROR_FLAGS 0x20 /* resets parity and multi-cycle error flags */ #define MASTER_CLEAR 0x10 /* resets the idr logic (not plx or device) */ #define CLEAR_OUTPUT_FIFO 0x08 /* resets the output fifo */ #define CLEAR_INPUT_FIFO 0x04 /* resets the input fifo */ #define READ_OUTPUT_FIFO 0x02 /* moves 16 bit word from fifo to output latches */ #define WRITE_INPUT_FIFO 0x01 /* moves 16 bit word from input to input fifo */ /* * flags register bit definitions */ #define END_OF_RANGE_FLAG 0x80 /* sets at idr counter underflow (not dma ctr) */ #define ATTENTION_FLAG 0x40 /* sets when attention goes true (edge) */ #define PARITY_ERROR_FLAG 0x20 /* sets w/ dma or p-i/o parity error */ #define MULTI_CYCLE_ERROR_FLAG 0x10 /* sets if multi-cycle cycle request error */ /* * device pulses register bit definitions */ #define SET_READY 0x80 /* forces ready and drains input fifo if input */ #define DEVICE_INIT 0x10 /* pulses init line to device */ #define ACLO_FCN_2 0x04 /* pulses aclo fnct2 line to device */ #define SOFT_CYCLE 0x02 /* forces cycle request */ #define GO 0x01 /* pulses go to device and enables block xfer */ /* * status register bit definitions */ #define READY 0x80 /* ready for another command or block xfer */ #define ATTENTION 0x40 /* attention input - not latched */ #define STATUS_MASK 0x0E /* mask for device status bits */ #define STATUS_A 0x08 /* status bits from device */ #define STATUS_B 0x04 #define STATUS_C 0x02 #define A00_INPUT 0x01 /* a00 input from device */ /* * mode register bit definitions */ #define LONG_READY 0x80 /* 1 = ready true at busy t.e 0 = ready true during busy */ #define DISABLE_RANGE 0x40 /* 1 disables dr11 range counter */ #define DISABLE_CRQ_B 0x20 /* 1 disables crq b input */ #define CYCLE_POLARITY 0x08 /* 0 = crq rising active, 1 = falling active */ #define BUSY_POLARITY 0x04 /* 0 = busy low true, 1 = high true */ #define TIMING_MASK 0x03 /* mask for handshake timing bits */ #define SPEED_3 0x03 /* slowest speed */ #define SPEED_2 0x02 #define SPEED_1 0x01 #define SPEED_0 0x00 /* * fifo status register bit definitions */ #define OUTPUT_FIFO_FULL 0x80 /* fifo status bits */ #define OUTPUT_FIFO_HALF_FULL 0x40 #define OUTPUT_FIFO_EMPTY 0x20 #define INPUT_FIFO_FULL 0x08 #define INPUT_FIFO_HALF_FULL 0x04 #define INPUT_FIFO_EMPTY 0x02 /* * other useful definitions */ /* * define max DR11 counter size */ #define IDR_DR11_MAXBLOCK 0xFFFFFF #define IKON_VENDOR_ID 0x11d5 /* our pci vendor id */ #define IDR_DEVICE_ID 0x0118 /* model 10118 device id */ #define PLX_REG_SIZE 0x100 /* size of plx 9080 register set */ #define IDR_REG_SIZE 0x2000 /* size of ikon register set */ /* * mask for revision id portion of class code & rev id config longword */ #define REV_ID_MASK 0xFF ./idr_var.h0000644000076400007640000002177110223360164012453 0ustar wdwwdw00000000000000/* * idr_var.h * structure and "software" macro definition file for Linux 2.6.x driver for PCI DR11 boards * * Tahoma Technology * (formerly Ikon Corporation) * 107 2nd Avenue North * Seattle, WA, USA 98109 * * 206.728.6465 * http://www.tahomatech.com * tahoma@tahomatech.com * * * 22 February, 1999 start of development - initial target Linux 2.0.35 (R.H. 5.1) * * 14 Sept, 1999 re-code for 2.2 * this version no longer compatible with earlier linux revs * * 13 March, 2000 started working w/2.3.x * changed wait_queue type * * 24 March, 2000 incorporated kiobuf mechanism for dma to/from user buffers * replaced copy buf w iopb list to do sg dma * added pci_dev_int_level to distinguish between the value * read from the board (to use in restore after eeprom reload) * and the (possibly) mapped version from pci_dev * incorporated modified verson of Kaz Kylheku's mutux stuff * for SMP * * requires 2.3/2.4 kernel * * 1 August, 2002 moved conditional variable and mutex struct defines * here from ikon_mutex.h * * deleted * box disclaimer * * added pointer to (possible) copy buffer * * moved "software" macros from idr_reg.h to this file * * modified IDR_SG_LINK macro to generate .page correctly * in HIGHMEM kernel case (not via page_address()) * * detect highmem kernel && old scatterlist, and enable * copy buffer pages * * got rid of MODINFBANNER, updated MODULE_DESCRIPTION date * * 4 February, 2004 major modifications for 2.6 kernel * * new module description date * * 8 October, 2004 new module description date * * 1 April, 2005 changed some pointer types and PUTL/GETL for x86-64 * compatibility * * new module description date */ #ifndef _IDR_VAR_H #define _IDR_VAR_H #endif /* * conditional variable and mutex typedefs */ typedef struct { spinlock_t spin; long flags; } mutex_t; typedef struct { wait_queue_head_t queue; } cond_t; enum { COND_WAIT_SUCCESS, COND_WAIT_SIGNAL, COND_WAIT_TIMEOUT }; /* * unit structure for boards */ struct idr_unit_t { u_int instance; /* equivalent to minor number & board number */ mutex_t mutex; /* used w/cond var to sleep (SMP compatibility) */ cond_t cond; /* cond var used w/mutex */ volatile int sleeping; /* 1 if sleeping (helps detect timeout) */ struct pci_dev *pci_dev_p; /* handle for this board's pci dev structure */ void *plx_page_base_p; /* virtual address of plx reg page base */ void *idr_page_base_p; /* virtual address of idr reg page base */ volatile void *plx_base_p; /* virtual address of plx regs (avoid optimizer)*/ volatile void *idr_base_p; /* virtual address of idr regs (avoid optimizer)*/ void *iopb_base_p; /* virtual add of start of io parameter blocks */ int nr_iopbs; /* number of iopbs */ struct scatterlist *sg_list_p; /* points to scatterlist (for DMA mappings) */ dma_addr_t dma_handle; /* contains bus address of start of iopbs */ struct page **maplist_p; /* ptr to array of ptrs to page structs */ char *maplist_user_buf_p; /* ptr to current user buffer (or portion of) */ int maplist_nr_pages; /* number of pages in maplist */ int maplist_offset; /* offset from base of buffer */ int maplist_length; /* number of bytes in buffer */ int maplist_rw_mode; /* READ or WRITE */ char *dma_copy_buf_p; /* ptr to copy buffer or NULL */ volatile u_int unit_open; /* 1 = open */ volatile u_int unit_attached; /* 1 = attached */ u_long unit_flags; /* our flag bits here */ u_long dma_time; /* dma timeout # in secs */ u_long attn_time; /* attention wait timeout # */ u_long rdy_time; /* ready wait timeout # */ u_long read_fcn; /* use at read start */ u_long write_fcn; /* use at write start */ u_long read_pulse; /* pulse at read start */ u_long write_pulse; /* pulse at write start */ u_long dev_and_vendor_id; /* device and vendor id from config regs */ u_long revision_id; /* revision id from config regs */ u_int range_resid; /* remaining count to xfer */ /* may be used for multi-dma */ /* per dr11 blocks (unused?) */ u_int dma_time_def; /* default dma wait - IN SECONDS */ u_int attn_time_def; /* default attn wait - IN SECONDS */ u_int rdy_time_def; /* default rdy wait - IN SECONDS */ u_long read_fcn_def; /* read fcn default */ u_long write_fcn_def; /* write fcn default */ u_long read_pulse_def; /* read pulse default */ u_long write_pulse_def; /* write pulse default */ u_long endian_def; /* endian default */ u_long latch_reg_def; /* latched functions default */ u_long mode_reg_def; /* mode register default */ u_char int_level; /* int level read from config register */ u_char pci_dev_int_level; /* (possibly) mapped int level */ char minor_node_name[16]; /* will contain this board's node name */ }; /* * this code was originally placed in the public domain * in order to satisfy the "tainted" test, we will declare ourselved GPL'd * we certainly meet the spirit of the GPL, since our source is always * freely available and included with the driver */ MODULE_DESCRIPTION("Tahoma Technology (formerly Ikon Corporation) Pci DR11 Driver - 2005.04.01 13:19"); MODULE_AUTHOR("Tahoma Technology (formerly Ikon Corporation) http://www.tahomatech.com"); MODULE_LICENSE("GPL"); /* * debug macro - expands to nothing unless IDR_DEBUG is defined * * requires double quoted arg list - printk can take * variable # of arguments */ #ifdef IDR_DEBUG #define DPRINT(args) printk args #else #define DPRINT(args) #endif /* * if user dma mode is permitted by install script (AUTO) and kernel * is not configured for more than 4G RAM, use direct user buffer dma. * otherwise use a copy buffer */ #if defined(CONFIG_HIGHMEM64G) || defined(IDR_DMA_MODE_COPY) #define IDR_COPY_BUF 1 #define IDR_DMA_MODE_MSG "user buffer DMA disabled, copy buffer in use\n" #else #define IDR_COPY_BUF 0 #define IDR_DMA_MODE_MSG "user buffer DMA enabled\n" #endif /* * we are 32 bit DMA capable */ #define IDR_DMA_MASK 0xffffffff /* * macro definitions for memory mapped board register access * and io control block memory access * * these are gathered here to try to allow for easy portability to * arch's other than i386, where access to Pci board registers, and * the board's access to DMA chain list memory may be byte swizzled * * the PLXx macors are used to access plx registers directly * the IPLXx macors are used to access the plx dma registers via idr space - * which is necessary with early revs of the plx chip - we could use the IDRx macros * to do this, but IPLX should be more readable, and it will be easier to find them * to mod later if necessary for some future rev of the chip * * the configuration accesses will be done explicitly, without macros * * unit_p->xxx_base is defined as a pointer to a 32 bit value, offsets are byte offsets, so /4 * we could also do things with fancy casts * * offset to IOPB_XXXX will be iopb # * IOPB_SIZE + byte offset within the iopb so also /4 * * the previous driver version used direct pointer dereferencing (OK on x86) * we leave the pointer math as it was rather than try to cast the bases as u_ints or ? * to try to avoid future 64 bit pointer issues */ #define PLX_GETL(offset) readl(unit_p->plx_base_p + offset) #define PLX_PUTL(offset,value) writel(value, unit_p->plx_base_p + offset) #define IPLX_GETL(offset) readl(unit_p->idr_base_p + offset) #define IPLX_PUTL(offset,value) writel(value, unit_p->idr_base_p + offset) #define IDR_GETL(offset) readl(unit_p->idr_base_p + offset) #define IDR_PUTL(offset,value) writel(value, unit_p->idr_base_p + offset) #define IOPB_GETL(offset) readl(unit_p->iopb_base_p + offset) #define IOPB_PUTL(offset,value) writel(value, unit_p->iopb_base_p + offset) /* * define size of i/o parameter block (link of dma chain list) * and the offsets of elements of the block from the base address */ #define IOPB_SIZE 0x10 /* 16 bytes per block */ #define IOPB_PCI_ADDRESS 0x00 /* starting pci address of user buffer block */ #define IOPB_LOCAL_ADDRESS 0x04 /* starting local address */ #define IOPB_TRANSFER_SIZE 0x08 /* number of bytes in this block */ #define IOPB_NEXT_IOPB 0x0c /* physical address of next iopb in list */ ./link.master.c0000644000076400007640000001335110223360164013242 0ustar wdwwdw00000000000000/* * This is a very simple example of a DR11 interprocessor link using the IKON * model 10103 Sbus or 10116 PCI bus dr11 emulator. It uses the standard interprocessor link * protocol, although without some of the bells and whistles that a robust link * would probably incporate. The strange block size of 0x2002 bytes matches the * block size used at IKON for factory link testing of various DR11 emulators * * This is the master end of the link - it generates a pattern in a buffer, then sends * an attention to the slave end of the link (as a request to send). It waits for * an attention back (clear to send), then sends the data block. It then waits for an * attention from the slave indicating that the slave wants to send the block back. The master * then starts a block read (with an attention at the start of the read previously enabled) * and waits for the data block. The master compares the two buffers, and halts if there is a * data error. If not, the process continues indefinitely, with a changing data pattern. * * In order for handshaking to begin correctly, the slave end of the link must be * started before the master end. * * 4 February, 2004 minor updates for readability */ #include #include #include #include "./idr_io.h" u_short inarray[10000], outarray[10000]; int i, j, filedes, pass_count; u_short pattern; extern errno; char devname[16]; char devpath[32] = "/dev/\0"; u_long arg; main() { printf("\nMaster Link Test for IKON 10116/10118 PCI bus DR11-W emulators.\n\n"); printf("This program provides the master end of an interprocessor link test.\n"); printf("When used with the Slave Link Test program running in this or another host,\n"); printf("it allows two IKON DR11-W emulators to be tested in link mode.\n\n"); printf("Link mode requires that two DR11s be connected J1 to J2, and J2 to J1 using\n"); printf("appropriate link cable(s)\n\n"); printf("The master end of the link generates a changing data buffer, which is sent to\n"); printf("the slave end of the link, which returns it. The master compares the buffer\n"); printf("with the buffer received, and flags any errors. This test will thoroughly\n"); printf("exercise and test both boards, and the interconnecting cable, and runs\n"); printf("indefinitely, until terminated with control-C.\n\n"); printf("THE SLAVE END OF THE LINK MUST BE STARTED FIRST!!\n\n"); printf("Please enter the name of the device to be tested: (idr0...idr9, quit) "); scanf("%15s", devname); if (strcmp(devname, "quit\0") == 0) exit(0); strcat(devpath, devname); printf("\ntesting device %s\n", devpath); /* * open the device */ if ((filedes = open(devpath, O_RDWR)) == -1) { printf("error on open, errno = %d\n", errno); exit(0); } printf("file descriptor = 0x%x\n", filedes); /* * pre-configure the read and write logic to send an attention * (clear to send) to the other * end when DVMA reads are enabled, and to force a local cycle when writes are started */ arg = IDR_ACF2; if (ioctl(filedes, IDRIO_READ_PULSE, &arg) == -1) { printf("error on READ_PULSE, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = IDR_CYCL; if (ioctl(filedes, IDRIO_WRITE_PULSE, &arg) == -1) { printf("error on WRITE_PULSE, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = IDR_FCN3 | IDR_FCN1; if (ioctl(filedes, IDRIO_READ_FCN, &arg) == -1) { printf("error on READ_FCN, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = 0; if (ioctl(filedes, IDRIO_WRITE_FCN, &arg) == -1) { printf("error on WRITE_FCN, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = 30; if (ioctl(filedes, IDRIO_SET_ATTN_TIME, &arg) == -1) { printf("error on SET_ATTN_TIME, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = 30; if (ioctl(filedes, IDRIO_SET_DMA_TIME, &arg) == -1) { printf("error on SET_DMA_TIME, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * loop forever */ while (1) { /* * we must reset the invisible UNIX counter to 0 every so often * at least, we had to with SunOS */ lseek(filedes, (off_t) 0, 0); for (i = 0; i < 100; i++, pass_count++) { /* * generate a pattern * since the buffer size is not a power of two, the pattern in each buffer will * be somewhat different */ for (j = 0; j < 0x1001; j++, pattern++) outarray[j] = pattern; /* * issue a request to send */ arg = IDR_ACF2; if (ioctl(filedes, IDRIO_IMM_PULSE, &arg) == -1) { printf("error at IMM_PULSE, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * wait for clear to send */ if (ioctl(filedes, IDRIO_ATTN_WAIT, &arg) == -1) { printf("error at ATTN_WAIT, errno = %d, pass_count = %d\n", errno, pass_count); exit(0); } /* * do the write */ if (write(filedes, outarray, 0x2002) == -1) { printf("error on write, errno = %d, pass_count = %d\n", errno, pass_count); exit(0); } /* * wait for the request to send from the other end */ if (ioctl(filedes, IDRIO_ATTN_WAIT, &arg) == -1) { printf("error ATTN_WAIT, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * issue the read (and clear to send) */ if (read(filedes, inarray, 0x2002) == -1) { printf("error on read, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * compare the two buffers */ for (j = 0; j < 0x1001; j++) if (inarray[j] != outarray[j]) { printf ("data mismatch! index = 0x%x, inarray[index] = 0x%x, outarray[index] = 0x%x\n", j, inarray[j], outarray[j]); exit(0); } } printf("master pass count = %d\n", pass_count); } } ./link.master.manual.c0000644000076400007640000001620410223360164014516 0ustar wdwwdw00000000000000/* * This is a very simple example of a DR11 interprocessor link using the IKON * model 10103 Sbus or 10116 PCI bus dr11 emulator. It uses the standard interprocessor link * protocol, although without some of the bells and whistles that a robust link * would probably incporate. The strange block size of 0x2002 bytes matches the * block size used at IKON for factory link testing of various DR11 emulators * * This is the master end of the link - it generates a pattern in a buffer, then sends * an attention to the slave end of the link (as a request to send). It waits for * an attention back (clear to send), then sends the data block. It then waits for an * attention from the slave indicating that the slave wants to send the block back. The master * then starts a block read (with an attention at the start of the read previously enabled) * and waits for the data block. The master compares the two buffers, and halts if there is a * data error. If not, the process continues indefinitely, with a changing data pattern. * * In order for handshaking to begin correctly, the slave end of the link must be * started before the master end. * * 4 February, 2004 minor updates for readability */ #include #include #include #include "./idr_io.h" u_short inarray[0x2000], outarray[0x2000]; int i, j, filedes, pass_count; u_short pattern; extern errno; char devname[16]; char devpath[32] = "/dev/\0"; u_long arg; main() { printf("\nMaster Link Test for IKON 10116/10118 PCI bus DR11-W emulators.\n\n"); printf("This program provides the master end of an interprocessor link test.\n"); printf("When used with the Slave Link Test program running in this or another host,\n"); printf("it allows two IKON DR11-W emulators to be tested in link mode.\n\n"); printf("Link mode requires that two DR11s be connected J1 to J2, and J2 to J1 using\n"); printf("appropriate link cable(s)\n\n"); printf("The master end of the link generates a changing data buffer, which is sent to\n"); printf("the slave end of the link, which returns it. The master compares the buffer\n"); printf("with the buffer received, and flags any errors. This test will thoroughly\n"); printf("exercise and test both boards, and the interconnecting cable, and runs\n"); printf("indefinitely, until terminated with control-C.\n\n"); printf("This version of Link Master operates the board in manual mode - it is intended\n"); printf("as an example of how to use the board and driver in manual mode (see source code)\n\n"); printf("THE SLAVE END OF THE LINK MUST BE STARTED FIRST!!\n\n"); printf("Please enter the name of the device to be tested: (idr0...idr9, quit) "); scanf("%15s", devname); if (strcmp(devname, "quit\0") == 0) exit(0); strcat(devpath, devname); printf("\ntesting device %s\n", devpath); /* * open the device */ if ((filedes = open(devpath, O_RDWR)) == -1) { printf("error on open, errno = %d\n", errno); exit(0); } printf("file descriptor = 0x%x\n", filedes); /* * set to manual mode & set timeout values */ if (ioctl(filedes, IDRIO_MANUAL, &arg) == -1) { printf("error on MANUAL, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = 30; if (ioctl(filedes, IDRIO_SET_ATTN_TIME, &arg) == -1) { printf("error on SET_ATTN_TIME, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = 30; if (ioctl(filedes, IDRIO_SET_RDY_TIME, &arg) == -1) { printf("error on SET_RDY_TIME, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = 30; if (ioctl(filedes, IDRIO_SET_DMA_TIME, &arg) == -1) { printf("error on SET_DMA_TIME, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * loop forever */ while (1) { /* * we must reset the invisible UNIX counter to 0 every so often * at least, we had to with SunOS */ lseek(filedes, (off_t) 0, 0); for (i = 0; i < 100; i++, pass_count++) { /* * generate a pattern * since the buffer size is not a power of two, the pattern in each buffer will * be somewhat different */ for (j = 0; j < 0x1001; j++, pattern++) outarray[j] = pattern; /* * issue a request to send */ arg = IDR_ACF2; if (ioctl(filedes, IDRIO_IMM_PULSE, &arg) == -1) { printf("error at IMM_PULSE, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * wait for clear to send */ if (ioctl(filedes, IDRIO_ATTN_WAIT, &arg) == -1) { printf("error at ATTN_WAIT, errno = %d, pass_count = %d\n", errno, pass_count); exit(0); } /* * do the write in manual mode using two write calls */ arg = 0x1000; if (ioctl(filedes, IDRIO_SET_RANGE, &arg) == -1) { printf("error on set range, errno = %d\n", errno); exit(0); } if (ioctl(filedes, IDRIO_START_WRITE, &arg) == -1) { printf("error on start write, errno = %d\n", errno); exit(0); } arg = IDR_GO | IDR_CYCL; if (ioctl(filedes, IDRIO_IMM_PULSE, &arg) == -1) { printf("error on write pulse ioctl, errno = %d\n", errno); exit(0); } if (write(filedes, outarray, 0x1000) == -1) { printf("error on write #1, errno = %d\n", errno); exit(0); } if (write(filedes, (u_short *) (outarray + 0x800), 0x1002) == -1) { printf("error on write #2, errno = %d\n", errno); exit(0); } if (ioctl(filedes, IDRIO_RDY_WAIT, &arg) == -1) { printf("error on rdy wait, errno = %d\n", errno); exit(0); } if (ioctl(filedes, IDRIO_BLOCK_END, &arg) == -1) { printf("error on block end, errno = %d\n", errno); exit(0); } /* * wait for the request to send from the other end */ if (ioctl(filedes, IDRIO_ATTN_WAIT, &arg) == -1) { printf("error ATTN_WAIT, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * do the read in manual mode using two read calls * * issue acf2 (clear to send) to the other side of the link */ arg = 0x1000; if (ioctl(filedes, IDRIO_SET_RANGE, &arg) == -1) { printf("error on set range, errno = %d\n", errno); exit(0); } if (ioctl(filedes, IDRIO_START_READ, &arg) == -1) { printf("error on start read, errno = %d\n", errno); exit(0); } arg = IDR_GO | IDR_ACF2; if (ioctl(filedes, IDRIO_IMM_PULSE, &arg) == -1) { printf("error on write pulse ioctl, errno = %d\n", errno); exit(0); } if (read(filedes, inarray, 0x1000) == -1) { printf("error on read #1, errno = %d\n", errno); exit(0); } if (read(filedes, (u_short *) (inarray + 0x800), 0x1002) == -1) { printf("error on read #2, errno = %d\n", errno); exit(0); } if (ioctl(filedes, IDRIO_RDY_WAIT, &arg) == -1) { printf("error on rdy wait, errno = %d\n", errno); exit(0); } if (ioctl(filedes, IDRIO_BLOCK_END, &arg) == -1) { printf("error on block end, errno = %d\n", errno); exit(0); } /* * compare the two buffers */ for (j = 0; j < 0x1001; j++) if (inarray[j] != outarray[j]) { printf ("data mismatch! index = 0x%x, inarray[index] = 0x%x, outarray[index] = 0x%x\n", j, inarray[j], outarray[j]); exit(0); } } printf("master pass count = %d\n", pass_count); } } ./link.slave.c0000644000076400007640000001160010223360164013054 0ustar wdwwdw00000000000000/* * This is a very simple example of a DR11 interprocessor link using the IKON * model 10103 Sbus or 10116 PCI bus dr11 emulator. It uses the standard interprocessor link * protocol, although without some of the bells and whistles that a robust link * would probably incporate. The strange block size of 0x2002 bytes matches the * block size used at IKON for factory link testing of various DR11 emulators * * This is the slave end of the link - it waits for an attention from the other * end of the link, then sets up to receive a block of data. Once the data has been * received, it sends an attention to the other dr11, waits for an attention back, * then sends back the block of data. * * In order for handshaking to begin correctly, the slave end of the link must be * started before the master end. * * 4 February, 2004 minor updates for readability */ #include #include #include #include "./idr_io.h" char datarray[16000]; int ii, i, filedes, pass_count; extern errno; char devname[16]; char devpath[32] = "/dev/\0"; u_long arg; main() { printf("\nSlave Link Test for IKON 10116/10118 PCI bus DR11-W emulators.\n\n"); printf("This program provides the slave end of an interprocessor link test.\n"); printf("When used with the Master Link Test program running in this or another host,\n"); printf("it allows two IKON DR11-W emulators to be tested in link mode.\n\n"); printf("Link mode requires that two DR11s be connected J1 to J2, and J2 to J1 using\n"); printf("appropriate link cable(s)\n\n"); printf("The master end of the link generates a changing data buffer, which is sent to\n"); printf("the slave end of the link, which returns it. The master compares the buffer\n"); printf("with the buffer received, and flags any errors. This test will thoroughly\n"); printf("exercise and test both boards, and the interconnecting cable, and runs\n"); printf("indefinitely, until terminated with control-C.\n\n"); printf("THE SLAVE END OF THE LINK MUST BE STARTED FIRST!!\n\n"); printf("Please enter the name of the device to be tested: (idr0...idr9, quit) "); scanf("%15s", devname); if (strcmp(devname, "quit\0") == 0) exit(0); strcat(devpath, devname); printf("\ntesting device %s\n", devpath); /* * open the device */ if ((filedes = open(devpath, O_RDWR)) == -1) { printf("error on open, errno = %d\n", errno); exit(0); } printf("file descriptor = 0x%x\n", filedes); /* * pre-configure the read and write logic to send an attention * (clear to send) to the other * end when DVMA reads are enabled, and to force a local cycle when writes are started */ arg = IDR_ACF2; if (ioctl(filedes, IDRIO_READ_PULSE, &arg) == -1) { printf("error on READ_PULSE, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = IDR_CYCL; if (ioctl(filedes, IDRIO_WRITE_PULSE, &arg) == -1) { printf("error on WRITE_PULSE, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = IDR_FCN3 | IDR_FCN1; if (ioctl(filedes, IDRIO_READ_FCN, &arg) == -1) { printf("error on READ_FCN, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = 0; if (ioctl(filedes, IDRIO_WRITE_FCN, &arg) == -1) { printf("error on WRITE_FCN, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = 30; if (ioctl(filedes, IDRIO_SET_ATTN_TIME, &arg) == -1) { printf("error on SET_ATTN_TIME, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } arg = 30; if (ioctl(filedes, IDRIO_SET_DMA_TIME, &arg) == -1) { printf("error on SET_DMA_TIME, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * loop forever */ while (1) { /* * we must reset the invisible UNIX counter to 0 every so often * at least, we had to with SunOS */ lseek(filedes, (off_t) 0, 0); for (i = 0; i < 100; i++, pass_count++) { /* * wait for the request to send from the other end */ if (ioctl(filedes, IDRIO_ATTN_WAIT, &arg) == -1) { printf("error ATTN_WAIT, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * issue the read (and clear to send) */ if (read(filedes, datarray, 0x2002) == -1) { printf("error on read, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * issue a request to send */ arg = IDR_ACF2; if (ioctl(filedes, IDRIO_IMM_PULSE, &arg) == -1) { printf("error at IMM_PULSE, errno = %d, pass count = %d\n", errno, pass_count); exit(0); } /* * wait for clear to send */ if (ioctl(filedes, IDRIO_ATTN_WAIT, &arg) == -1) { printf("error at ATTN_WAIT, errno = %d, pass_count = %d\n", errno, pass_count); exit(0); } /* * do the write */ if (write(filedes, datarray, 0x2002) == -1) { printf("error on write, errno = %d, pass_count = %d\n", errno, pass_count); exit(0); } } printf("slave pass count = %d\n", pass_count); } } ./Makefile0000644000076400007640000000047710223360164012314 0ustar wdwwdw00000000000000# kbuild style makefile for Tahoma Technology PCI DR11 driver # # Tahoma Technology # (formerly Ikon Corporation) # 107 2nd Avenue North # Seattle, WA, USA 98109 # # 206.728.6465 # http://www.tahomatech.com # tahoma@tahomatech.com # # 4 February, 2004 obj-m := idr.o idr-objs := idr_driver.o clean-files := *.o *.ko ./README.idr-2.60000644000076400007640000003775510223360164012625 0ustar wdwwdw00000000000000Linux 2.6.x driver installation instructions and notes for Tahoma Technology's PCI DR11-W interface Model 10118. Tahoma Technology (formerly IKON Corporation) 107 2nd Avenue, Seattle, WA, USA 98109 206.728.6465 http://www.tahomatech.com tahoma@tahomatech.com 4 february, 2004 4 September, 2004 corrected a few typos 1 April, 2005 driver now compatible with x86_64 About Tahoma Technology: Tahoma Technology has purchased certain of the assets of Ikon Corporation from IKON Office Solutions (IOS). That's the legal version. What has happened is that the people who started and operated Ikon Corporation initially and under IOS have purchased the division back. We continue the same product line and support. References to "Ikon" here and elsewhere are left in place for historical and compatibility reasons. Tahoma/Ikon code and documentation has been previously, and remains, in the public domain. Driver Installation & Notes: This version of the driver was tested under Linux 2.6 It is NOT compatible with earlier versions of LInux. The initial release of this driver was the first that did DMA direct from user buffers, using the kiobuf capability in Linux 2.3/2.4. This version moves the user buffer mapping code into the driver itself - since kiobufs are not available in 2.6x. If the kernel has been configured for greater than 4G of RAM, the driver will be compiled to use a kernel copy buffer instead of mapping user buffers for DMA. Copy buffer mode may also be forced by an install script option. In x86_64 systems, the available iommu is used to map high memory addresses to 32 bit address space, so a copy buffer is not necessary. Hopefully our HIGHMEM config detection won't trip on x86_64 systems. The install script also attempts to autodetect the appropriate include directory: /lib/modules/`uname -r/build/inlcude and /usr/src/linux, in that order. Other install script options are described below. Other files provided in this release include: idr_driver.c code source module for the Linux PCI DR11-W driver idr_var.h structure definition file for the PCI DR11-W driver idr_reg.h register definition file for the PCI DR11-W driver idr_io.h ioctl command definition file for the PCI DR11-W driver - to be #include'd' in the calling program Makefile simple makefile for the new kbuild system idr_install-2.6 used to compile and load driver module link.master.c very simple interprocessor link program (master end of link) that calls the DR11-W driver link.slave.c simple interprocessor link program (slave end of link) link.master.manual.c version of link master that demonstrates use of MANUAL mode idr.loop.test.c loop-back test source for PCI DR11-W board 118notes-2.6 information on using the PCI DR11-W driver The DR11 driver is a module that can be loaded into a running kernel. Before loading, the driver should be compiled on the target machine, and the object module placed in an appropriate /lib/module directory. idr_install-2.6 can be used to compile, load, and enable boot loading of the driver,or may be used to manually perform each of these steps individually. The idr_install-2.6 script provided can be used to compile the standard or debug version of the driver and place it in the correct directory. To compile the object and place in /lib/modules/`uname -r`/misc: (as super user) ./idr_install-2.6 compile To compile the debug version of the driver: (as super user) ./idr_install-2.6 compile debug Note that the debug version of the driver generates numerous printk() calls, and should only be used for diagnostic purposes! The idr_install-2.6 script provided will load the module and create the device node(s) in the /dev directory. Node names will be idr0, idr1, ... To load the driver module into a running kernel: (as super user) idr_install-2.6 load Once loaded, the driver will be available until unloaded or the system is rebooted. To cause the driver to be loaded when the system is booted: (as super user) idr_install-2.6 autoload This will create /etc/idr (if not already present) and copy idr_install-2.6 to /etc/idr. It also adds a line to the end of /etc/rc.d/rc.local which will invoke "/etc/idr/idr_install-2.6 load" at boot time. Directory and script permissions are set to rwx access for root, and read only for everyone else. When the install script is invoked via rc.local at boot time, standard and error outputs are logged to /etc/idr/idr_install-2.6.bootlog. In the event of problems with autoloading the driver at boot time, inspect this log for possible diagnostic information. NOTE that any changes made to the local copy of idr_install-2.6 after this point will not be reflected in the copy in /etc/idr which is invoked at boot time. Use the idr_install-2.6 "autoload" or "all" option to update the copy in /etc/idr. To remove the driver module from the kernel, delete the driver object from the modules directory, remove the rc.local entry, remove /etc/idr (if empty after removing idr_install-2.6*), and delete the device node(s): (as super user) idr_install-2.6 remove To remove, compile, load, and enable autoloading of the driver with a single command : (as super user) idr_install-2.6 all or (as super user) idr_install-2.6 all debug The script will try to determine the appropriate compile include directory: This is somewhat of a moving target. Tested for, in order, are: /lib/modules/`uname -r`/build/include and /usr/src/linux. If no directory in the list is detected, the compiler is allowed to use its default include directory (probably /usr/include). The default directory is usually not suitable for compiling driver modules, and will cause warnings and errors. The install script will attach a single board and create a single device node unless instructed otherwise. The variable IDR_BOARDS may be modified by editing the script. IDR_BOARDS determines the maximum number of boards probed for, and the number of device nodes created. The script will create the number of device nodes requested, regardless of the number of boards found. The device node(s) will be created with owner=bin group=bin and permissions=666. Other script variables that control the default configuration of the driver and board, as well as the behavior of the script itself may be modified. See the Driver Default Parameters section, below, for information on driver "defines". See the script code for script variable information. NOTES: Residual Byte Counts: It is a unix convention that the read and write routines return a count of the number of bytes actually transferred. Unfortunately, the Pci interface chip on the 10118 does not have a readable DMA range counter. Therefore, the DR11 driver always indicates that all bytes requested in a read or write call were transferred. Unless an error was encountered, this will in fact always be the case. Errors will be indicated by the driver returning an error code after a read or write call. In general, bytes lost in an aborted read or write cannot be re-sent successfully anyway, so the "actual" byte count would serve little purpose. There are some applications that do not know in advance how many bytes will be received by a read call. In this case, a read is issued with a count larger than the anticipated maximum. The read is typically terminated by an ATTENTION signal from the attached device. AT this time, the application can read the DR11 range counter (not the dma counter) to determine how many bytes were received. IOCTL parameters: The PCI DR11-W driver's ioctl() commands are compatible with Tahoma's SunOS and Solaris drivers for Sbus boards, and with the Solaris and Solaris 7 drivers for Pci boards. See the idr_io.h file and the 118notes-2.6 file for ioctl command descriptions. Note that the idr_io.h file for Linux is slightly different from the idr_io.h files provided with the drivers for Solaris 1 and 2. The old and new ....io.h files will generate the same ioctl commands, but are not interchangeable. Use the new idr_io.h file when compiling on a Linux machine. DR11 Driver IOCTL Calls: See the file called 118notes-2.6 and the idr_io.h file for info on driver calls and defaults. DR11 Byte Swapping: All 10118 boards use a PLX chip (9060 or 9080) that provides hardware byte swapping. The driver - Linux or Solaris - will default to the swap mode appropriate for the host architecture. DR11 Sample Link Programs: The link.master and link.slave programs implement a simple example of a DR11 to DR11 interprocessor link using the 10118 and driver. These programs are compatible with the equivalent Solaris programs for the Sbus and Pci boards and drivers, and with Tahoma's in house test link software for Tahoma's other DR11 emulator products. They may be used for testing, or as examples of the link protocol, and how to communicate with the driver. Also included on this diskette is a version of the link master program - link.master.manual - that runs in manual mode. This is a simple demonstration of how to use multiple read and write calls per DR11 block. This technique is useful in continuous data collection applications. A link connection is accomplished by cross-connecting J1 and J2 between two DR11 emulators (or the real thing!). J1 connects to J2, and J2 to J1. This can be done using short transition cables from the DR11s to a pair of ground-plane ribbon cables, or in the case of Tahoma's Pci or Sbus boards, by using a single round, jacketed, shielded link cable. DR11 Loop-back Test Program The idr.loop.test program can be used to perform a repeating loop-back test of the 10118. It requires that an appropriate loop-back cable be installed. This cable accomplishes a pin-for-pin connection between the two sides of the 80-pin high-density connector, or between J1 and J2 of the transition cable assembly. The test program first tests the three software timeout functions of the driver, and then loops indefinitely on several tests of the features of the board and driver. At the end of each 100 passes, it outputs a pass count. In the event of an error, it prints a brief message and exits. This test works correctly only on boards that can "un-swap" DMA bytes that are swapped by the pci-memory path. This includes all 10118s and later 10116 boards that use the PLX 9060SD or 9080 chips. Driver Default Parameters: Several driver defaults may be changed by editing the idr_install-2.6 script and reloading the driver. Many defaults may also be overridden by the application, using ioctl() calls. IDR_BOARDS: The driver init_module code will probe for the number of boards specified by IDR_BOARDS. The pci bios probe function will be called repeatedly until no further boards are found, or IDR_BOARDS is exhausted. Specifying a board count larger than the number of boards present will be somewhat wasteful of kernel memory, as each (potential) board requires a kmalloc'd state structure. The install script wil create as many device nodes as specified by IDR_BOARDS, regardless of the number of boards actually found. Node names will be idr0, idr1, ... The default value is 1. IDR_SPEED: The timing of the handshake used between the DR11 emulator and the attached device is set by the IDR_SPEED variable. Permissible values are 0, 1, 2, and 3, with 0 giving the fastest timing. In general, the fastest timing that gives reliable data transfers should be used. See the manual for details. IDR_DMA_TIME: IDR_RDY_TIME: IDR_ATTN_TIME: There are three software timeout clocks used by the driver during waits: a dma timer, a wait-for-ready timer, and a wait for ATTENTION timer. The defualts for these timers are set by the IDR_DMA_TIME, IDR_RDY_TIME, and IDR_ATTN_TIME variables, respectively. All default times are in seconds. IDR_BYTE_SWAP: The default byte ordering mode used during DMA transfers is set by the IDR_BYTE_SWAP variable. When 0, it gives un-swapped transfers. When one, bytes within 16 bit words will be swapped. IDR_BUSY_POL: IDR_CYCLE_POL: The default polarities of the BUSY and CYCLE REQUEST signals are determined by the IDR_BUSY_POL and IDR_CYCLE_POL variables. IDR_CYCLE_POL = 0 causes the CYCLE REQUEST input to assert on a rising edge; 1 selects falling edge active. IDR_BUSY_POL = 0 causes busy to be asserted low; 1 selects high assertion. These polarities should be selected to match the requirements of the attached device. IDR_WRITE_CYCLE: IDR_READ_CYCLE: At the beginning of each DR11 block transfer, some applications may require that the software cycle pulse be used to start the transfer (in link mode, it is required only for the sending end of the link). There are separate soft cycle variables for read and write operations. IDR_WRITE_CYCLE controls the write soft cycle, and IDR_READ_CYCLE controls reads. Setting either to 1 will cause a soft cycle pulse to be issued at the start of EACH corresponding type of DR11 block. IDR_READ_ACF2: Link mode also requires that the driver issue an ACLO FCN2 pulse (which becomes ATTENTION at the other end of the link) at the start of each DR11 read block. This tells the other end of the link that reads are enabled, and a cycle request from the sender would be honored. This pulse is enabled by setting the IDR_READ_ACF2 variable to 1. The default will be 0 for now - to stay consistent with the Sbus version of the driver. IDR_READ_F3: IDR_READ_F2: IDR_READ_F1: IDR_WRITE_F3: IDR_WRITE_F3: IDR_WRITE_F1: IDR_OPEN_F3: IDR_OPEN_F2: IDR_OPEN_F1: Several properties set the default states of the DR11 function bit outputs, FCN3, FCN2, and FCN1. This is done separately for read and write blocks, and when the device is opened. Each function bit has a separate property for each situation. The names are (hopefully) self- explanatory. Permitted valued are 0 or 1. IDR_MAX_PHYS_ORDER: This version of the driver does DMA directly to/from mapped and pinned user buffer when possible. It will do a maximum of 2**IDR_MAX_PHYS_ORDER * PAGE_SIZE bytes per DMA transfer. User buffers larger than this value will be transferred in multiple DMA blocks, transparently to the calling program. In the case of a HIGHMEM_64G (but not x86_64) enabled kernel a copy buffer of 2**IDR_MAX_PHYS_ORDER * PAGE_SIZE bytes will be allocated. In auto mode, one-time operations like pulsing GO, and setting the read or write function bits will be done at the beginning of the write or read, not with each DMA block, so the attached device will see what appears to be a single large block transfer of the size specified in the read or write call. In manual mode, GO, and function bits are not manipulated at all in a read or write operation, but left for the user to control via ioctl calls. Larger DMA transfers will improve overall efficiency somewhat, by minimizing the number of interrupts taken per user buffer (one per DMA block transfer). The tradeoff, of course, is the kernel memory and mapping resources used to map and pin the buffer, and build the scatter-gather list for the board's DMA mechanism. (On non-PC architectures - not tested yet - there will also be increased usage of iommu mapping resources). Given the quantities of cheap memory available on PC's, one should probably just choose a buffer size that keeps the attached device happy. IDR_MAX_PHYS_ORDER is specified in "orders". That is, 2**IDR_MAX_PHYS_ORDER * PAGE_SIZE gives the max transfer size in bytes. The default is 4, which with the current 4K page size gives 64k max bytes per transfer. Compile Options: IDR_DEBUG: Causes the driver to emit _many_ diagnostic printfs during operation. Do not use during normal production plotting! IDR_USER_DMA: IDR_USER_DMA = AUTO allows the driver to compile in user buffer DMA mode, as long as CONFIG_HIGHMEM_64G is not true. If the kernel supports > 4G of RMA (CONFIG_HIGHMEM_64G true) a copy buffer will be used - except in x86_64 systems. If IDR_USER_DMA = NO, a copy buffer will always be used. Load Options: IDR_FORCE_LOAD: If this option = YES, the -f (force) option will be used when the driver is loaded by insmod. This is probably not a good idea as it will ignore some important driver/kernel compatibility checks. It may be helpful during driver development.