/* IKON Corporation 2617 Western Ave. Seattle, WA 98121 (206) 728-6265 */ /* 12 September 1991 BETA version */ /* 2 January 1992 modified to accomodate 24 bit range counter and multicycle error flag in FPGA3 and later hardware. also made sleeps breakable, and put sleep and iodone in strategy. the original code was adapted "in place" to allow the sleep in strategy. this was done because of the rather complicated relationship between strategy, interrupt, and timeout routines - when combined with sleeps that can catch signals. a cleaner version would probably re-structure the code to do more of the error checking and flagging in strategy after the sleep, rather than in timeout and interrupt code. 21 February 1992 The earlier changes to the sleep logic "awoke" a sleeping bug left over from the sun sample code (i think). The board's internal priority was supposedly saved already aligned to be plugged into the cpu's interrupt priority bits. Unfortuneately the bits were in byte 1, and the value was stored in a u_char, which ended up just being 0. This caused START_CRITICAL to fail to raise the priority, which meant that short transfers could interrupt before the sleep call was made, with the result that the interrupt code's wakeup was lost. The fix was to make the interrupt_pri part of the unit structure into a u_int. 15 September 1994 replaced the idr_delay(iregs_p) calls after writes to ikon registers with calls to internal routine idr_delay(). Newer sun machines - sparcstations 5 and 20, apparently enabled their write posting buffers. In cases where a register write was held up in the write buffer because of other bus activity, it was possible for the next write to arrive in the posting buffer while the first was still there (the idr_delay(iregs_p) calls only delay the writes TO the write buffer, not the writes FROM the buffer to our registers) causing the writes to be issued back-to-back on the bus. Since the "ikon" registers on the 10103 require 300ns after a write to complete their internal operations, this caused occasional corruption of register writes. The fix is to use an internal delay routine that reads an "ikon" register. This accomplishes two things: a read to the same page as a posted write suspends until the write completes, and the read adds delay without corrupting the write data in the "ikon" logic. The delay is added in a function, rather than a macro for several reasons. It is not possible to use any read to flush the write buffer - it must be a read to the same register or page that was written, so it is necessary to pass some kind of register or page pointer to the read. There are also concerns about the compiler optimizer deleting reads to just-written registers, or reads that don't seem to be used by later code. Putting the reads in a function seemed the best way to out-smart the optimizer. Interrupt code has also been modified to include a test for the error pending bit, along with the interrupt pending bit. Contrary to the sample code, it appears that the LSI chip will interrupt on an error without setting the interrupt pending bit. An interrupt caused by a hardware error during DMA will not be recognized by the interrupt code, and will cause the kernel's interrupt polling code to loop indefinitely. With the bug fix, an error will be reported, but it will not lock up the machine. 1 November 1994 corrected a bug in IDRIO_RDY_WAIT ioctl. we had used |= to turn off the interrupt masks, rather than &= (as it should be!). */ /************************************************************************ * * * This driver is provided at no charge to IKON's customers * * in the hope that it will assist them in understanding and * * using IKON's Sbus DR11-W emulator. This code * * is intended to be a working and (relatively!) bug free driver * * when running on the machine and OS rev available to IKON. * * IKON will attempt to keep this code running on current OS and * * hardware from SUN - and others - but does not guarantee this. * * The user is encouraged to contact IKON with comments, * * suggestions, and BUG REPORTS. * ************************************************************************/ /*--- much of the code and comments in this driver are left over from the sample driver provided by sun in the Sbus device drivers manual. ---*/ /*================ INCLUDED FILES ===================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "idrio.h" /*================ minor device number encoding ========================*/ #define IDR_UNIT(dev) minor(dev) /*================ DEBUGGING SWITCHES ===================================*/ /* There are three levels of messages from the idr driver: IDR_ERROR_MSG: Errors that should not happen IDR_DEBUG_MSG: Status messages useful when debugging IDR_TRACK_MSG: Path and data tracking through the driver When IDR_DEBUG and IDR_TRACK are not defined, the code compacts in the loadable case. The loadable case is recommended. */ #ifdef IDR_TRACK # define IDR_DEBUG #endif /*VARARGS0*/ static void no_message() {} /*VARARGS0*/ extern printf() ; #ifdef IDR_DEBUG # define IDR_ERROR_MSG log # define IDR_DEBUG_MSG log #ifdef IDR_TRACK # define IDR_TRACK_MSG log #else IDR_TRACK # define IDR_TRACK_MSG no_message #endif IDR_TRACK #else IDR_DEBUG # define IDR_ERROR_MSG log # define IDR_DEBUG_MSG no_message # define IDR_TRACK_MSG no_message #endif IDR_DEBUG /*================ DATA STRUCTURES ===================================*/ struct dmaio_regs { u_long Dmacsr ; /* control and status register */ u_long Dmaar ; /* address register */ u_long Dmabcr ; /* byte count register */ u_long dma_unused ; /* unused register */ }; /* THERE IS PROBABLY A MORE ELEGANT WAY TO ACCOMPLISH THE FOLLOWING - BUT RATHER THAN WORRY ABOUT ALIGNMENT - WE WILL DO IT THE HARD WAY */ struct idr_regs { /* DR11 working registers */ u_char Idr_mode ; /* mode register */ u_char idr_unused0 ; /* pad to next reg */ u_char idr_unused1 ; u_char idr_unused2 ; u_char idr_unused3[12] ; /* 16 byte offset between regs */ u_char Idr_latch ; /* latched functions */ u_char idr_unused4 ; u_char idr_unused5 ; u_char idr_unused6 ; u_char idr_unused7[12] ; u_char Idr_p_stat ; /* w=pulses, r=status/flags */ u_char idr_unused8 ; u_char idr_unused9 ; u_char idr_unused10 ; u_char idr_unused11[12] ; u_char Idr_dhi ; /* data, high byte */ u_char idr_unused12 ; u_char idr_unused13 ; u_char idr_unused14 ; u_char idr_unused15[12] ; u_char Idr_dlow ; /* data, low byte */ u_char idr_unused16 ; u_char idr_unused17 ; u_char idr_unused18 ; u_char idr_unused19[12] ; u_char Idr_rhi ; /* high 4 bits of range */ u_char idr_unused20 ; u_char idr_unused21 ; u_char idr_unused22 ; u_char idr_unused23[12] ; u_char Idr_rmid ; /* mid 8 bits of range */ u_char idr_unused24 ; u_char idr_unused25 ; u_char idr_unused26 ; u_char idr_unused27[12] ; u_char Idr_rlow ; /* low 8 bits of range */ u_char idr_unused28 ; u_char idr_unused29 ; u_char idr_unused30 ; u_char idr_unused31[12] ; /* pad to 128 bytes. */ } ; struct idr_unit { struct dev_info *devinfo_p ; /* Node for this unit */ struct dmaio_regs *dma_regs_p ; /* device control regs */ struct idr_regs *idr_regs_p ; /* points to working regs */ int saved_spl ; /* Saved spl while in critical sections for this unit only. At all other times the value is minus one*/ u_char unit_open ; /* 1 = open */ u_char open_inhibit ; /* 1 = inhibit */ u_char spare_0 ; /* byte spacers */ u_char spare_1 ; u_int interrupt_pri ; /* Highest interrupt pri for this unit (spl) */ u_int unit_flags ; /* our flag bits here */ u_int dma_time ; /* dma timeout # in secs */ u_int attn_time ; /* attention wait timeout # */ u_int rdy_time ; /* ready wait timeout # */ u_int range_resid ; /* remaining count to xfer */ /* may be used for multi-dma*/ /* per dr11 blocks (unused?)*/ u_int read_fcn ; /* use at read start */ u_int write_fcn ; /* use at write start */ u_int read_pulse ; /* pulse at read start */ u_int write_pulse ; /* pulse at write start */ struct buf buf ; /* buf for physio */ } ; /* interrupt_pri was originally a u_char, which wasn't wide enough to hold the necessary bits! */ /*================ DATA DECLARATIONS ===================================*/ static struct idr_unit *idr_unit_array = NULL ; static u_char nidrs_counting = 0 ; static u_char nidrs = 0 ; static int logprie = LOG_ERR; static int logprid = LOG_INFO; static int logprit = LOG_DEBUG; extern int hz ; /* number of ticks per second */ static u_char delay_dump_0; /* dummy variables for read delay function */ static u_char delay_dump_1; /*================ PROCEDURE DECLARATIONS ==============================*/ void START_CRITICAL() ; void END_CRITICAL() ; void see_devinfo() ; void idr_decommission() ; char* get_property() ; void idr_strategy() ; int idr_identify() ; int idr_attach() ; void idr_intr() ; int idr_poll() ; int idr_timeout() ; void idr_dma_init() ; void idr_delay(); int idr_open() ; /* these were in xxxio.h in example */ int idr_close() ; int idr_read() ; int idr_write() ; int idr_ioctl() ; /*================ AUTOCONFIG DECLARATIONS ==============================*/ /* See notes on p 134 of "Writing SBus Device Drivers" */ struct dev_ops idr_ops = { 1, /* rev no of dev_ops structure */ idr_identify, idr_attach, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /* other values in idr_ops are filled in later */ /* For reference: the dev_ops struct This structure identifies the driver entry points struct dev_ops { int devo_rev; int (*devo_identify)(); ... confirm device id int (*devo_attach)(); ... attach routine of driver int (*devo_open)(); int (*devo_close)(); int (*devo_read)(); int (*devo_write)(); int (*devo_strategy)(); int (*devo_dump)(); int (*devo_psize)(); int (*devo_ioctl)(); int (*devo_reset)(); int (*devo_mmap)(); }; */ /*================ AUTOCONFIGURATION SUPPORT ============================*/ /*------------------------------------ Function: idr_identify(name) This routine is called by the kernel autoconfig code with the namestring fetched from the ID PROM as its argument. Its purpose is to determine whether this is the right driver to work with the named device. Static Vars: This routine counts the number of times it returns acceptance so that it can size its array of unit structures accordingly. The count is kept at the variable nidrs_counting. Inputs: A pointer to a string which is a device name as fetched from the SBus ID PROM. In our case 'idr'. Returns: 1 on successful match and acceptance. 0 if not -------------------------------------*/ static int idr_identify(name) char *name ; { IDR_TRACK_MSG(logprit, "Entering idr_identify, name: %s \n", name) ; if ((strcmp(name, "IKON,idr")) == 0) { /*-- Limit the number of units that we accept. Unlikely that this check is really needed */ if (nidrs_counting < MAX_NIDRS) { nidrs_counting++ ; IDR_TRACK_MSG(logprit, "Leaving idr_identify, ACCECPTED (#%d) \n", nidrs_counting) ; return(1) ; /*-- good match: accepted */ } else { IDR_ERROR_MSG(logprie, "idr_identify: too many idr units (#%d) \n",nidrs_counting) ; } } IDR_TRACK_MSG(logprit, "Leaving idr_identify, REJECTED \n") ; return(0) ; /*-- No match or over limit: device is rejected */ } /*------------------------------------ Function: idr_attach(kdevinfo_p) This routine is called by the kernel autoconfig code once for each node of the devinfo tree that this driver services (once for each of its units). Actions performed are: 1. Maps the device registers into kernal virtual memory 2. Puts the driver on any needed interrupt service chains 3. Turns on interrupts, if desired 4. Initializes the device 5. Allocates the unit structures Inputs: kdevinfo_p A pointer to the devinfo structure that gives access to the name and unit number Returns: 0 on successful attach -1 if not -------------------------------------*/ static int idr_attach(kdevinfo_p) register struct dev_info *kdevinfo_p ; { struct dev_reg *dev_reg_p ; struct dev_intr *dev_intr_p ; u_int counter ; u_int unit_no ; u_int dsize ; int ip, idr_ip ; /* The static variable unit_count is the source of unit numbers to assign to the devinfo structure. It is the responsibility of the attach routine to fill in the value of kdevinfo_p->devi_unit */ static int unit_count = 0 ; IDR_TRACK_MSG(logprit, "Entering idr_attach, kdevinfo_p: hex %x \n", kdevinfo_p) ; if (kdevinfo_p == NULL) { /*-- Should this unlikely event occur, make the attach fail: a physical device should have been found by the PROM, and therefore have a devinfo pointer. The code below requires it */ IDR_ERROR_MSG(logprie, "idr_attach: kdevinfo_p is NULL! \n") ; goto ATTACH_FAILED ; } /*-- Allocate zeroed memory for the unit structures */ if (idr_unit_array == NULL) { nidrs = nidrs_counting ; idr_unit_array = (struct idr_unit *) kmem_zalloc(nidrs * sizeof (struct idr_unit)); } IDR_TRACK_MSG(logprit, "At Attach point idr.1000, idr_unit_array IS 0x%x\n",idr_unit_array); /* Assign a unit structure and fill in the value of devinfo_p->devi_unit */ unit_no = unit_count++ ; kdevinfo_p->devi_unit = unit_no ; /*-- save the devinfo pointer for decommissioning */ idr_unit_array[unit_no].devinfo_p = kdevinfo_p ; /*-- initialize saved_spl */ idr_unit_array[unit_no].saved_spl = -1 ; IDR_TRACK_MSG(logprit, "At idr_unit_array[].saved_spl IS 0x%x\n",idr_unit_array[unit_no].saved_spl); /*-- look at the properties in the devinfo structure. Demonstrate how to get other properties by getting the value of the name property the hard way */ see_devinfo(kdevinfo_p) ; get_property(kdevinfo_p, "name", "NO NAME PROPERTY?") ; IDR_TRACK_MSG(logprit, "At Attach point idr.2000\n"); /*--- map in the board's registers. there are two sets of regs, the DMAIO chip's registers, and the DR11 registers. There had better be EXACTLY two sets of registers!!!!!!! ---*/ dev_reg_p = kdevinfo_p->devi_reg ; IDR_TRACK_MSG(logprit, "At Attach point idr.3000\n"); /*-- dev_reg_p may be null, but only if devi_nreg is zero. we will stick with the loop type register mapping code used in the sample driver. it might make more sense to just code two mapping operations, but we will leave that as an exercise for the user - who is no doubt much better at this than the poor IKON folks. ---*/ if (kdevinfo_p->devi_nreg != 2) { IDR_ERROR_MSG(logprie, "idr_attach: nreg != 2!! \n") ; goto ATTACH_FAILED ; } for (counter = 1; counter <= kdevinfo_p->devi_nreg; ++counter, ++dev_reg_p) { IDR_TRACK_MSG(logprit, "At Attach point idr.4000\n"); if (dev_reg_p == NULL) { /* not very likely */ IDR_ERROR_MSG(logprie, "idr_attach: dev_reg_p is NULL! \n") ; goto ATTACH_FAILED ; } /* map in both sets of registers - the dma chip's and the DR11 stuff */ switch(counter) { case 1: /*-- DMAio regs ---*/ /*-- Check that the size of the register set is the same as the size of the structure that overlays it */ dsize = sizeof(struct dmaio_regs) ; /*--- for (t=0; t<10000; t++) t=t ; ---*/ /* kill some time (in sample!?) */ IDR_TRACK_MSG(logprit, "At Attach point idr.5000\n"); if (dsize != dev_reg_p->reg_size) { IDR_ERROR_MSG(logprie, "Struct (%d bytes) != reg (%d bytes) !\n", dsize , dev_reg_p->reg_size) ; IDR_TRACK_MSG(logprit, "At Attach point idr.6000\n"); goto ATTACH_FAILED ; } /* --- map the structure into kernel virtual space */ IDR_TRACK_MSG(logprit, "At Attach point idr.7000\n"); idr_unit_array[unit_no].dma_regs_p = (struct dmaio_regs *) map_regs(dev_reg_p->reg_addr, dsize, dev_reg_p->reg_bustype) ; IDR_TRACK_MSG(logprit, "At Attach point idr.8000, regs_p is 0x%x\n", idr_unit_array[unit_no].dma_regs_p); if (idr_unit_array[unit_no].dma_regs_p == NULL) { IDR_TRACK_MSG(logprit, "At Attach point idr.8000\n"); IDR_ERROR_MSG(logprie, "idr_attach: map regs failed\n") ; goto ATTACH_FAILED ; } break ; /* end case 1 */ case 2: /*-- idr working registers */ dsize = sizeof(struct idr_regs) ; IDR_TRACK_MSG(logprit, "At Attach point idr.9000\n"); if (dsize != dev_reg_p->reg_size) { IDR_ERROR_MSG(logprie, "Struct (%d bytes) != reg (%d bytes) !\n", dsize , dev_reg_p->reg_size) ; IDR_TRACK_MSG(logprit, "At Attach point idr.10000\n"); goto ATTACH_FAILED ; } /* --- map the structure into kernel virtual space */ IDR_TRACK_MSG(logprit, "At Attach point idr.11000\n"); idr_unit_array[unit_no].idr_regs_p = (struct idr_regs *) map_regs(dev_reg_p->reg_addr, dsize, dev_reg_p->reg_bustype) ; if (idr_unit_array[unit_no].idr_regs_p == NULL) { IDR_TRACK_MSG(logprit, "At Attach point idr.12000\n"); IDR_ERROR_MSG(logprie, "idr_attach: map regs failed\n") ; goto ATTACH_FAILED ; } break ; /* end case 2 */ default: /* if we get here we are really sick !!!! */ IDR_ERROR_MSG(logprie, "idr_attach: too many registers (#%d) \n", counter) ; goto ATTACH_FAILED ; } /* end switch */ } /* end for (counter == 1 etc. */ /* Next install the interrupt vectors, if any -----*/ if (kdevinfo_p->devi_nintr == 0) goto SKIP_INTR_INSTALLATION ; /*-- For devices that issue interrupts the driver installs itself on the interrupt chain of each interrupt level that the hardware uses */ dev_intr_p = kdevinfo_p->devi_intr ; IDR_TRACK_MSG(logprit, "At Attach point idr.13000\n"); if (dev_intr_p == NULL) { /* not very likely */ IDR_TRACK_MSG(logprit, "At Attach point idr.14000 dev_intr_p is NULL!\n"); IDR_ERROR_MSG(logprie, "idr_attach: dev_intr_p is NULL! \n") ; goto ATTACH_FAILED ; } /*-- dev_intr_p may be null, but only if devi_nintr is zero */ for (counter = 1; counter <= kdevinfo_p->devi_nintr; ++counter, ++dev_intr_p) { IDR_TRACK_MSG(logprit, "At Attach point idr.15000\n"); /*-- Convert the hardware interrupt priority level (ipl, 0-15) into an spl for critical sections of code. The routine ipltospl returns a value that is properly shifted for direct insertion into the psr. If there is more than one interrupt level, use the highest one. Note that each unit may use a different level */ IDR_TRACK_MSG(logprit, "At Attach point idr.16000 dev_intr_p is 0x%x\n",dev_intr_p); ip = ipltospl(dev_intr_p->int_pri) ; IDR_TRACK_MSG(logprit, "At Attach point idr.17000\n"); idr_ip = idr_unit_array[unit_no].interrupt_pri ; IDR_TRACK_MSG(logprit, "At Attach point idr.18000\n"); if (ip > idr_ip) { idr_unit_array[unit_no].interrupt_pri = ip ; IDR_DEBUG_MSG(logprid, "Set interrupt pri to hex %x for unit %d. \n", ip, unit_no) ; } IDR_TRACK_MSG(logprit, "At Attach point idr.18010: final interrupt_pri = 0x%x\n",idr_unit_array[unit_no].interrupt_pri) ; /*-- if the device can do DVMA then the DVMA routines need to know the highest interrupt level at which the device can interrupt. The routine adddma is used to gather this info */ IDR_TRACK_MSG(logprit, "At Attach point idr.19000\n"); /**** addma arg in original was idr_ip - which followed sun's example which was the wrong type !!!! corrected 10 Sept 1991 ****/ adddma(dev_intr_p->int_pri) ; IDR_TRACK_MSG(logprit, "At Attach point idr.20000\n"); START_CRITICAL(unit_no) ; /*-- addintr() does the registration. Duplicate registrations (same level from several units) is ignored except for data collection for vmstat. addintr() panics if this registration would make too many devices on the interrupt chain. So it goes */ addintr(dev_intr_p->int_pri, idr_poll, kdevinfo_p->devi_name, kdevinfo_p->devi_unit) ; END_CRITICAL(unit_no) ; IDR_TRACK_MSG(logprit, "At Attach point idr.21000\n"); IDR_DEBUG_MSG(logprid, "Installed idr_poll at SBus int pri level hex %x\n", dev_intr_p->int_pri) ; } /* end for (counter == 1 etc. */ SKIP_INTR_INSTALLATION: /*-- The routine report_dev() writes a line at the console and system log to announce the attachment of the driver. This will happen at modload time, one line for each unit. Call report_dev() before contacting the device so the message is there if the system freezes at this point */ report_dev(kdevinfo_p) ; IDR_TRACK_MSG(logprit, "At Attach point idr.22000n") ; /*--- reset DVMA and DR11 logic ---*/ idr_dma_init(unit_no) ; /*--- interupts are not enabled until we are waiting for DMA end or attention ---*/ /*--- set open inhibit and opened flags to 0 ---*/ idr_unit_array[unit_no].open_inhibit = 0 ; idr_unit_array[unit_no].unit_open = 0 ; IDR_TRACK_MSG(logprit, "ATTACH SUCCEEDED.vi\n") ; return(0) ; ATTACH_FAILED: IDR_TRACK_MSG(logprit, "ATTACH FAILED.vi\n") ; idr_unit_array[unit_no].open_inhibit = 1 ; /* prevent open */ return(-1) ; } /* end idr_attach() */ /*================ DRIVER ENTRY POINTS ===================================*/ /*------------------------------------ Function: idr_open(dev, flags) Open system call. Allow an open for read or write. Inputs: dev The compound device number. Only the minor portion is of value to the driver. openflags Flags encoding the open mode requested. Returns: 0 on success, errno on failure. -------------------------------------*/ int idr_open(dev, openflags) dev_t dev; int openflags; { u_char unit_no; u_short retval = 0; /*-- return value (errno) for system call*/ u_int t ; /* temp variable */ unit_no = IDR_UNIT(dev); IDR_TRACK_MSG(logprit, "idr%d: Entering idr_open, flags %d.\n", unit_no, openflags); /*-- Check for how many are allocated. At this point, n idr must be set.*/ if (unit_no >= nidrs) { retval = ENXIO; goto EXITOPEN; } /*-- Check for allocation of unit structures.*/ if (idr_unit_array == NULL) { retval = ENXIO; /* attach failled ?? */ goto EXITOPEN; } /*-- Check if open is inhibited. The idr is a read/write device so we don't care what the flags are! --*/ if (idr_unit_array[unit_no].open_inhibit !=0 ) { IDR_DEBUG_MSG(logprid, "idr%d open inhibit.\n", unit_no); retval = ENXIO; goto EXITOPEN; } if(idr_unit_array[unit_no].unit_open !=0) { /* exclusive open! */ uprintf("idr%d: already open!\n",unit_no); retval = EBUSY ; /*THIS MAY NOT BE THE RIGHT ERRNO*/ goto EXITOPEN ; } /*--- call dma init to clear board's logic ---*/ idr_dma_init(unit_no) ; /*--- initialize time-out values to default ---*/ t = hz * DMA_TIME_DEF ; idr_unit_array[unit_no].dma_time = t ; t = hz * ATTN_TIME_DEF ; idr_unit_array[unit_no].attn_time = t ; t = hz * RDY_TIME_DEF ; idr_unit_array[unit_no].rdy_time = t ; /*--- set read and write pulse and function values to defaults ---*/ idr_unit_array[unit_no].read_fcn = READ_FCN_DEF ; idr_unit_array[unit_no].write_fcn = WRITE_FCN_DEF ; idr_unit_array[unit_no].read_pulse = READ_PULSE_DEF ; idr_unit_array[unit_no].write_pulse = WRITE_PULSE_DEF ; /*--- set the mode and latch registers to their default values ---*/ idr_unit_array[unit_no].idr_regs_p->Idr_mode = MODE_REG_DEF ; idr_delay(idr_unit_array[unit_no].idr_regs_p) ; idr_unit_array[unit_no].idr_regs_p->Idr_latch = LATCH_REG_DEF ; idr_delay(idr_unit_array[unit_no].idr_regs_p) ; /*--- clear the various flags in unit_flags ---*/ idr_unit_array[unit_no].unit_flags = 0 ; /*-- Finally, mark the idr as opened.*/ idr_unit_array[unit_no].unit_open = 1; EXITOPEN: IDR_TRACK_MSG(logprit, "Leaving idr_open, unit %d: errno %d.\n", unit_no, retval); return (retval); } /*------------------------------------ Function: idr_close(dev, openflags) Close the device. Inputs: dev The compound device number. Only the minor portion is of value to the driver. openflags Flags encloding the open mode requested, and possibly granted. Returns: 0 on success, errno on failure. -------------------------------------*/ int idr_close(dev, openflags) dev_t dev; int openflags; { u_char unit_no; u_short retval = 0; /*-- return value (errno) for system call*/ unit_no = IDR_UNIT(dev); IDR_TRACK_MSG(logprit, "Entering idr_close, unit number %d, flags %d.\n", unit_no, openflags); /*--- reset dma logic, DR11 logic, and flush fifo ---*/ idr_dma_init(unit_no) ; /*-- Mark unit closed.*/ idr_unit_array[unit_no].unit_open = 0; IDR_TRACK_MSG(logprit, "Leaving idr_close, unit %d: errno %d.\n", unit_no, retval); return (retval); } /*------------------------------------ Function: idr_read(dev, uioptr) Read data from the idr. Block until the read is finished. Inputs: dev The compound device number. Only the minor portion is of value to the driver. uioptr Pointer to the uio structure which may contain a table of iovec structures. Returns: 0 on success, errno on failure -------------------------------------*/ int idr_read(dev, uioptr) dev_t dev; struct uio *uioptr; { u_char unit_no; u_short retval; retval = 0; /*-- return value (errno) for system call*/ unit_no = IDR_UNIT(dev); IDR_TRACK_MSG(logprit, "Entering idr_read, unit number %d.\n", unit_no); if (unit_no >= nidrs) { retval = ENXIO; goto EXITREAD; } /*--- clear the timeout, multicycle, sig received, and "waiting for" flags. strategy will set the waiting for dma or eor flag. ---*/ idr_unit_array[unit_no].unit_flags &= IDR_CLEAR_FLAGS ; /* clear flags = ~(a bunch of flags or'd together) */ /*--- set input flag ---*/ idr_unit_array[unit_no].unit_flags |= IDR_INPUT ; /***** we will use the system minphys number as a maximum *****/ retval = physio(idr_strategy, &idr_unit_array[unit_no].buf, dev, B_READ,minphys, uioptr); /*--- check flags for timeout or multicycle error or sig received ---*/ if(idr_unit_array[unit_no].unit_flags & (IDR_DVMA_TIMEOUT|IDR_EOR_TIMEOUT)) { retval = EIO ; IDR_ERROR_MSG(logprie, "idr_read: unit idr%d: DMA timeout! \n",unit_no) ; } if(idr_unit_array[unit_no].unit_flags & IDR_MCYL_ERR) { retval = EIO ; IDR_ERROR_MSG(logprie, "idr_read: unit idr%d: Multicycle error! \n",unit_no) ; } if(idr_unit_array[unit_no].unit_flags & IDR_SIG_RECEIVED) { retval = EINTR ; IDR_ERROR_MSG(logprie, "idr_read: unit idr%d: signal received! \n",unit_no) ; } EXITREAD: IDR_TRACK_MSG(logprit, "Leaving idr_read, unit %d: errno %d.\n", unit_no, retval); return (retval); } /*------------------------------------ Function: idr_write(dev, uioptr) Write data to the idr. Block until the write is finished. Inputs: dev The compound device number. Only the minor portion is of value to the driver. uioptr Pointer to the uio structure which may contain a table of iovec structures. Returns: 0 on success, errno on failure -------------------------------------*/ int idr_write(dev, uioptr) dev_t dev; struct uio *uioptr; { u_char unit_no; u_short retval; retval = 0; /*-- return value (errno) for system call*/ unit_no = IDR_UNIT(dev); IDR_TRACK_MSG(logprit, "Entering idr_write, unit number %d.\n", unit_no); if (unit_no >= nidrs) { retval = ENXIO; goto EXITWRITE; } /*--- clear the timeout, multicycle error, sig received, and "waiting for" flags strategy will set the waiting for dvma or eor flag. ---*/ idr_unit_array[unit_no].unit_flags &= IDR_CLEAR_FLAGS ; /*--- clear the input flag (=output) ---*/ idr_unit_array[unit_no].unit_flags &= ~IDR_INPUT ; /***** we will use the system minphys number *****/ retval = physio(idr_strategy, &idr_unit_array[unit_no].buf, dev, B_WRITE,minphys, uioptr); /*--- check flags for timeout or multicycle error or sig received ---*/ if(idr_unit_array[unit_no].unit_flags & (IDR_DVMA_TIMEOUT|IDR_EOR_TIMEOUT)) { retval = EIO ; IDR_ERROR_MSG(logprie, "idr_write: unit idr%d: DMA timeout! \n",unit_no) ; } if(idr_unit_array[unit_no].unit_flags & IDR_MCYL_ERR) { retval = EIO ; IDR_ERROR_MSG(logprie, "idr_write: unit idr%d: Multicycle error! \n",unit_no) ; } if(idr_unit_array[unit_no].unit_flags & IDR_SIG_RECEIVED) { retval = EINTR ; IDR_ERROR_MSG(logprie, "idr_write: unit idr%d: signal received! \n",unit_no) ; } EXITWRITE: IDR_TRACK_MSG(logprit, "Leaving idr_write, unit %d: errno %d.\n", unit_no, retval); return (retval); } /*------------------------------------ Function: idr_ioctl(dev, cmd, arg, flag) Handle control requests. Inputs: dev The compound device number. Only the minor portion is of value to the driver. cmd Device specific. SEE iohcpio.h arg Char pointer to caller's data buffer flag Not used by the idr driver. Returns: 0 on success, errno on failure. -------------------------------------*/ /*ARGSUSED*/ int idr_ioctl(dev, cmd, arg, flag) dev_t dev; int cmd; caddr_t arg; int flag; { union pointers { u_short *shptr ; u_long *lgptr ; caddr_t chptr ; } upt ; struct dmaio_regs *dregs_p; struct idr_regs *iregs_p ; struct buf *bp ; u_long *lg_p ; u_long bits ; u_char ct ; /* temp char */ u_short retval ; /*-- return value (errno) for system call*/ u_char unit_no; u_int command ; int sleepval ; /*-------------------- */ retval = 0; /* init to OK */ upt.chptr = arg ; /* char pointer at arg */ lg_p = upt.lgptr ; /* and long pointer */ unit_no = IDR_UNIT(dev); /* unit number */ iregs_p = idr_unit_array[unit_no].idr_regs_p ;/*points at ikon regs */ dregs_p = idr_unit_array[unit_no].dma_regs_p; /*points at dmaio regs */ command = cmd & IDRIO_CMD_MASK ; /* get command */ bits = *lg_p ; /* get long arg[0] */ IDR_TRACK_MSG(logprit, "Entering idr_ioctl, idr_unit_array is 0x%x .\n", idr_unit_array) ; IDR_TRACK_MSG(logprit, "Entering idr_ioctl, unit number %d.\n", unit_no); IDR_TRACK_MSG(logprit, "nidrs is 0x%x\n", nidrs) ; IDR_TRACK_MSG(logprit, "idr_unit_array is 0x%x \n", idr_unit_array ) ; IDR_TRACK_MSG(logprit, "At idr_ioctl, cmd is 0x%x, arg(pointer) is 0x%x .\n", cmd, arg); IDR_TRACK_MSG(logprit, "At idr_ioctl, bits = 0x%x\n",bits) ; IDR_TRACK_MSG(logprit, "At idr_ioctl, dregs_p is 0x%x, iregs_p is 0x%x .\n", dregs_p, iregs_p); idr_unit_array[unit_no].unit_flags &= IDR_CLEAR_FLAGS ; /* just in case */ #define MASK IDRIO_CMD_MASK /* use to strip the count from cases */ switch(command) { /* go do the deed */ case MASK & IDRIO_SET_MODE: /* set mode reg bits & rdis bit in latch reg */ /* mode bits are in bottom 8 of bits, rdis */ /* is l shifted by 8 in bits (rdis*256) */ IDR_TRACK_MSG(logprit,"ichp_ioctl: at SET_MODE \n") ; iregs_p->Idr_mode = (u_char)bits ; /* set mode reg */ idr_delay(iregs_p) ; iregs_p->Idr_latch &= (u_char)~IDR_RDIS ; /* mask out rdis bit */ idr_delay(iregs_p) ; /* set rdis if necessary */ iregs_p->Idr_latch |= (u_char)(IDR_RDIS & (bits >> 8)) ; idr_delay(iregs_p) ; break ; case MASK & IDRIO_IMM_FCN: /* set function bits immediately */ IDR_TRACK_MSG(logprit,"idr_ioctl: at IMM_FCN \n") ; /* read latch into ct & calculate result & then write to latch */ /* reg all at one shot, rather than first masking out in latch */ /* reg to keep fcn bits from going off then on again - some */ /* devices may be sensitive to this */ ct = (u_char)(iregs_p->Idr_latch & ~IDR_FMASK) ; iregs_p->Idr_latch = ct | (u_char)(bits & IDR_FMASK) ; idr_delay(iregs_p) ; break ; case MASK & IDRIO_READ_FCN: /* save fcn bits for use at read start */ IDR_TRACK_MSG(logprit,"idr_ioctl: at READ_FCN \n") ; idr_unit_array[unit_no].read_fcn = (bits & IDR_FMASK) ; break ; case MASK & IDRIO_WRITE_FCN: /* save fcn bits for use at write start */ IDR_TRACK_MSG(logprit,"idr_ioctl: at WRITE_FCN \n") ; idr_unit_array[unit_no].write_fcn = (bits & IDR_FMASK) ; break ; case MASK & IDRIO_IMM_PULSE: /* issue pulses asap */ IDR_TRACK_MSG(logprit,"idr_ioctl: at IMM_PULSE \n") ; if(bits & IDR_GO) { /* wait for attn off if issuing go */ /* wait 100uS max - ????????????? */ CDELAY(!(iregs_p->Idr_p_stat & IDR_ATTN),100) ; if(iregs_p->Idr_p_stat & IDR_ATTN) { IDR_ERROR_MSG(logprie,"idr_ioctl: unit idr%d at IMM_PULSE: attempt to issue GO while ATTENTION true!\n",unit_no) ; retval = EIO ; goto IOCTLEXIT ; } } iregs_p->Idr_p_stat = (u_char)bits ; idr_delay(iregs_p) ; break ; case MASK & IDRIO_READ_PULSE: /* save pulses for read start */ IDR_TRACK_MSG(logprit,"idr_ioctl: at READ_PULSE \n") ; idr_unit_array[unit_no].read_pulse = bits ; break ; case MASK & IDRIO_WRITE_PULSE: /* save pulses for write start */ IDR_TRACK_MSG(logprit,"idr_ioctl: at WRITE_PULSE \n") ; idr_unit_array[unit_no].write_pulse = bits ; break ; case MASK & IDRIO_SET_DMA_TIME: /* set dma timeout parameter */ IDR_TRACK_MSG(logprit,"idr_ioctl: at SET_DMA_TIME \n") ; 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 */ idr_unit_array[unit_no].dma_time = (u_int)(bits * hz) ; /* set ticks */ break ; case MASK & IDRIO_SET_ATTN_TIME: /* set wait for attn timeout value */ /* or fifo empty & dev rdy wait */ IDR_TRACK_MSG(logprit,"idr_ioctl: at SET_ATTN_TIME \n") ; if(bits < ATTN_TIME_MIN) bits = ATTN_TIME_MIN ; /* not less than */ if(bits > ATTN_TIME_MAX) bits = ATTN_TIME_MAX ; /* or more than..*/ idr_unit_array[unit_no].attn_time = (u_int)(bits * hz) ; /* ticks*/ break ; case MASK & IDRIO_SET_RDY_TIME: /* set wait for ready timeout value */ /* or fifo empty & dev rdy wait */ IDR_TRACK_MSG(logprit,"idr_ioctl: at SET_RDY_TIME \n") ; if(bits < RDY_TIME_MIN) bits = RDY_TIME_MIN ; /* not less than */ if(bits > RDY_TIME_MAX) bits = RDY_TIME_MAX ; /* or more than..*/ idr_unit_array[unit_no].rdy_time = (u_int)(bits * hz) ; /* ticks*/ break ; case MASK & IDRIO_ATTN_WAIT: /* wait for attention to set attf - looking */ /* for an EDGE on attention. if flag is */ /* already set - return clear flag */ /* the interrupt routine will not touch */ /* the flags - we will clear here on return */ IDR_TRACK_MSG(logprit,"idr_ioctl: at ATTN_WAIT \n") ; idr_unit_array[unit_no].unit_flags &= IDR_CLEAR_FLAGS ; if(iregs_p->Idr_p_stat & IDR_ATTF) { /* test for attf already set*/ iregs_p->Idr_p_stat = IDR_RATN ; /* clear flag */ idr_delay(iregs_p) ; goto IOCTLEXIT ; } idr_unit_array[unit_no].unit_flags |= IDR_ATTN_WAIT ; /*set wait flag*/ bp = &idr_unit_array[unit_no].buf ; /* make pointer to wait on */ /* start timeout routine ticking - will call idr_timeout if timeout */ timeout(idr_timeout,(caddr_t)bp,idr_unit_array[unit_no].attn_time) ; START_CRITICAL(unit_no) ; iregs_p->Idr_latch |= IDR_ATTM ; /* enable attf interrupts */ idr_delay(iregs_p) ; dregs_p->Dmacsr = DMAIO_INT_ENABLE ; /* master int enb for board */ sleepval = sleep(bp,(PCATCH|IDRPRI)) ; /* sleep on bp -until int or */ /* timeout or signal */ dregs_p->Dmacsr &= ~DMAIO_INT_ENABLE ; /* disable interrupts */ iregs_p->Idr_latch &= ~IDR_ATTM ; /* attention mask off */ idr_delay(iregs_p) ; END_CRITICAL(unit_no) ; untimeout(idr_timeout,bp) ; /* turn off timer */ /* check for signal received during sleep */ if(sleepval) { IDR_ERROR_MSG(logprie, "idr_ioctl: unit idr%d: at ATTN wait: signal received! \n", unit_no) ; retval = EINTR ; goto IOCTLEXIT ; } /* check for timeout return */ if(idr_unit_array[unit_no].unit_flags & IDR_ATTN_TIMEOUT) { /* attn timeout may not be an error - so write to debug log */ IDR_DEBUG_MSG(logprid, "idr_ioctl: at ATTN_WAIT - timeout waiting for attention! \n") ; retval = EIO ; goto IOCTLEXIT ; } iregs_p->Idr_p_stat = IDR_RATN ; /* clear attn flag here */ idr_delay(iregs_p) ; 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 */ IDR_TRACK_MSG(logprit,"idr_ioctl: at RDY_WAIT \n") ; idr_unit_array[unit_no].unit_flags &= IDR_CLEAR_FLAGS ; if(iregs_p->Idr_p_stat & IDR_REDY) { /* test for redy already set*/ goto RDYWAITEXIT ; } idr_unit_array[unit_no].unit_flags |= IDR_RDY_WAIT ; /*set wait flag*/ bp = &idr_unit_array[unit_no].buf ; /* make pointer to wait on */ /* start timeout routine ticking - will call idr_timeout if timeout */ timeout(idr_timeout,(caddr_t)bp,idr_unit_array[unit_no].rdy_time) ; START_CRITICAL(unit_no) ; iregs_p->Idr_latch |= (IDR_ATTM | IDR_EORM) ; /* enb attn or eor int */ idr_delay(iregs_p) ; dregs_p->Dmacsr = DMAIO_INT_ENABLE ; /* master int enb for board */ sleepval = sleep(bp,(PCATCH|IDRPRI)) ; /* sleep on bp -until int or */ /* timeout or signal */ dregs_p->Dmacsr &= ~DMAIO_INT_ENABLE ; iregs_p->Idr_latch &= ~(IDR_ATTM | IDR_EORM) ; idr_delay(iregs_p) ; END_CRITICAL(unit_no) ; untimeout(idr_timeout,bp) ; /* check for signal received during sleep */ if(sleepval) { IDR_ERROR_MSG(logprie, "idr_ioctl: unit idr%d: at RDY wait: signal received! \n", unit_no) ; retval = EINTR ; goto IOCTLEXIT ; } /* check for timeout return */ if(idr_unit_array[unit_no].unit_flags & IDR_RDY_TIMEOUT) { /* timeout may not be an error - so dump to debug log */ IDR_DEBUG_MSG(logprid, "idr_ioctl: at RDY_WAIT - timeout waiting for READY! \n") ; retval = EIO ; goto IOCTLEXIT ; } RDYWAITEXIT: /* examine dr11 flag bits to figure out why ready was set */ /* if attf is set and eor is not - clear attf */ if((iregs_p->Idr_p_stat & IDR_ATTF) && !(iregs_p->Idr_p_stat & IDR_EORF)) { iregs_p->Idr_p_stat = IDR_RATN ; idr_delay(iregs_p) ; } iregs_p->Idr_p_stat = IDR_REOR ; /* always clear eorf*/ idr_delay(iregs_p) ; break ; case MASK & IDRIO_GET_STATUS: /* return dr11 status register */ IDR_TRACK_MSG(logprit,"idr_ioctl: at GET_STATUS \n") ; *lg_p = (u_long)iregs_p->Idr_p_stat ; break ; case MASK & IDRIO_GET_RANGE: /* gets 24 bit range counter value */ /* in raw form: = word count minus 1 */ IDR_TRACK_MSG(logprit,"idr_ioctl: at GET_RANGE \n") ; *lg_p = (iregs_p->Idr_rhi << 16) | (iregs_p->Idr_rmid << 8) | iregs_p->Idr_rlow ; break ; case MASK & IDRIO_GET_REGS: /* get all the board's registers*/ IDR_TRACK_MSG(logprit,"idr_ioctl: at GET_REGS \n") ; iregs_p->Idr_latch |= IDR_DMIN ; /* force input mode */ idr_delay(iregs_p) ; *lg_p = dregs_p->Dmacsr ; /* get dma csr */ *(lg_p + 1) = dregs_p->Dmaar ; /* get add register */ *(lg_p + 2) = dregs_p->Dmabcr ; /* get count register */ *(lg_p + 3) = (u_long)iregs_p->Idr_mode ; /* get mode reg */ *(lg_p + 4) = (u_long)iregs_p->Idr_latch ; /* get latch reg */ *(lg_p + 5) = (u_long)iregs_p->Idr_p_stat ; /* status register */ *(lg_p + 6) = (u_long)iregs_p->Idr_dhi ; /* data high byte */ *(lg_p + 7) = (u_long)iregs_p->Idr_dlow ; /* data low byte */ *(lg_p + 8) = (u_long)iregs_p->Idr_rhi ; /* range hi byte */ *(lg_p + 9) = (u_long)iregs_p->Idr_rmid ; /* middle range byte */ *(lg_p + 10) = (u_long)iregs_p->Idr_rlow ; /* low range byte */ break ; case MASK & IDRIO_GET_FLAGS: /* return unit flags to caller */ IDR_TRACK_MSG(logprit,"idr_ioctl: at GET_FLAGS \n") ; *lg_p = idr_unit_array[unit_no].unit_flags ; break ; case MASK & IDRIO_DATA_OUT: /* output 16 bit word to data out reg */ IDR_TRACK_MSG(logprit,"idr_ioctl: at DATA_OUT \n") ; iregs_p->Idr_latch &= ~IDR_DMIN ; /* force output mode */ idr_delay(iregs_p) ; iregs_p->Idr_dhi = (u_char)(bits >> 8) ; /* high byte */ idr_delay(iregs_p) ; iregs_p->Idr_dlow = (u_char)bits ; /* low byte */ idr_delay(iregs_p) ; break ; case MASK & IDRIO_DATA_IN: /* read dr11 input data register */ IDR_TRACK_MSG(logprit,"idr_ioctl: at DATA_IN \n") ; iregs_p->Idr_latch |= IDR_DMIN ; /* force input mode */ idr_delay(iregs_p) ; *lg_p = (u_long)((iregs_p->Idr_dhi << 8) | iregs_p->Idr_dlow) ; 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!!! */ IDR_TRACK_MSG(logprit,"idr_ioctl: at SET_RANGE \n") ; if(bits > IDR_DR11_MAXBLOCK) { IDR_ERROR_MSG(logprie,"idr_ioctl: unit idr%d at SET_RANGE - desired range count greater than maximum allowed! \n",unit_no) ; retval = EINVAL ; goto IOCTLEXIT ; } iregs_p->Idr_rhi = (u_char)(bits >> 16) ; idr_delay(iregs_p) ; iregs_p->Idr_rmid = (u_char)(bits >> 8) ; idr_delay(iregs_p) ; iregs_p->Idr_rlow = (u_char)bits ; idr_delay(iregs_p) ; break ; case MASK & IDRIO_AUTO: /* set automatic mode (default) */ IDR_TRACK_MSG(logprit,"idr_ioctl: at AUTO \n") ; idr_unit_array[unit_no].unit_flags &= ~IDR_MANUAL ; break ; case MASK & IDRIO_MANUAL: /* set manual mode */ IDR_TRACK_MSG(logprit,"idr_ioctl: at MANUAL \n") ; idr_unit_array[unit_no].unit_flags |= IDR_MANUAL ; break ; case MASK & IDRIO_START_READ: /* enables block read in man mode */ IDR_TRACK_MSG(logprit,"idr_ioctl: at START_READ \n") ; iregs_p->Idr_p_stat = (IDR_REOR | IDR_RATN) ; /* force fags off */ iregs_p->Idr_latch &= ~IDR_DMON ; /* make sure dmon is off! */ idr_delay(iregs_p) ; iregs_p->Idr_latch |= IDR_DMIN ; /* set to input mode */ idr_delay(iregs_p) ; iregs_p->Idr_latch |= IDR_DMON ; /* now enable w/DMON */ idr_delay(iregs_p) ; break ; case MASK & IDRIO_START_WRITE: /* enables block write in man mode*/ IDR_TRACK_MSG(logprit,"idr_ioctl: at START_WRITE \n") ; iregs_p->Idr_p_stat = (IDR_REOR | IDR_RATN) ; /* force fags off */ iregs_p->Idr_latch &= ~IDR_DMON ; /* make sure dmon is off! */ idr_delay(iregs_p) ; iregs_p->Idr_latch &= ~IDR_DMIN ; /* set to output mode */ idr_delay(iregs_p) ; iregs_p->Idr_latch |= IDR_DMON ; /* now enable w/DMON */ idr_delay(iregs_p) ; break ; case MASK & IDRIO_BLOCK_END: /* disable overall block transfer */ IDR_TRACK_MSG(logprit,"idr_ioctl: at BLOCK_END \n") ; /* turn off dmon and int masks in dr11 logic */ iregs_p->Idr_latch &= ~(IDR_DMON | IDR_EORM | IDR_ATTM) ; idr_delay(iregs_p) ; iregs_p->Idr_p_stat = IDR_TERM ; /* make sure REDY true */ idr_delay(iregs_p) ; dregs_p->Dmacsr = DMAIO_DEV_RESET ; /* force DVMA logic off!*/ dregs_p->Dmacsr = 0 ; dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /* old chip req'd this */ /* to clear TC bit */ break ; default: IDR_DEBUG_MSG(logprid, "idr_ioctl: bad command \n"); retval = EINVAL; break; } IOCTLEXIT: IDR_TRACK_MSG(logprit, "Leaving idr_ioctl, unit %d: errno %d.\n", unit_no, retval); return (retval); } /*================ BOTTOM HALF INTERRUPT HANDLER ========================*/ /*------------------------------------ Function: idr_intr(unit_no) Interrupt routine for the DVMA hardware - terminal count & error - and DR11 end-of-range and attention. _intr is called from idr_poll with the unit number. When this routine is called, we know that this unit issued an interrupt that needs service. Inputs: unit_no The unit number representing the interrupting device. Returns: None -----------------------------------*/ static void idr_intr(unit_no) u_int unit_no; { register struct dmaio_regs *dregs_p ; register struct idr_regs *iregs_p ; register struct buf *bp; u_long csr ; /* control and status register */ u_int flags ; /* unit array flags for this unit */ /*-- Set pointer to DVMA & idr registers. Set pointer to buf structure.*/ dregs_p = idr_unit_array[unit_no].dma_regs_p ; iregs_p = idr_unit_array[unit_no].idr_regs_p ; bp = &idr_unit_array[unit_no].buf ; /***** untimeout called here in earlier versions - called by higher level code in this version *****/ /************************************************************************/ /* */ /* the dvma controller is capable of screaming data to and from */ /* the fifos at 3 sbus clocks per transfer. this is so fast that */ /* slave accesses from the cpu to the DR11 registers are locked */ /* out during this time(if the fifos are accepting or delivering */ /* data at full speed). if the cpu tries to read or write a dr11 */ /* register during full speed dvma, it will probably get a timeout */ /* error, which will cause a KERNEL PANIC(!) if it happens within */ /* driver!! it is absulutely necessary that the dvma enable bit be */ /* OFF when the driver accesses the DR11 registers in the interrupt*/ /* code - and in most other places as well. the problem is that */ /* in input mode, we may get an attention interrupt while there */ /* is still data in the dvma cache. the interrupt code will need */ /* to check the draining bits and leave dvma on until the caches */ /* are empty. for output, we can just hammer the enable bit off */ /* as soon as we get into the interrupt routine. */ /* */ /* a possible complication occurs if a stray attention pulse */ /* arrives just as we are starting an block. it will set */ /* the attf flag - which will cause an interrupt when enabled. */ /* if the external device requests input transfers, we could still */ /* end up screaming fifo data into memory (very unlikely, since a */ /* attention pulse would not normally be followed with dma data!) */ /* or - more likely - if we are in output mode, the dvma chip will */ /* fill the empty output fifo at full speed, and if we try to read */ /* or write the dr11 regs in the int routine, we will get into */ /* trouble. */ /* */ /* during normal input operation, any external interrupts are held */ /* off until all data has been emptied from the fifo - which */ /* avoids the problem. in normal output it is legit to just shut */ /* off dvma when the int is received. in input mode - we will */ /* check for cache draining before turning off dvma. */ /************************************************************************/ /*--- get flags ---*/ flags = idr_unit_array[unit_no].unit_flags ; csr = dregs_p->Dmacsr ; /* get first copy of the csr */ #ifdef IDR_TRACK printf( "IDR_INTR: entering interrupt handler \n") ; printf( "IDR_INTR: idr_unit_array is 0x%x .\n", idr_unit_array) ; printf( "IDR_INTR: unit number %d.\n", unit_no) ; printf( "IDR_INTR: bp is 0x%x\n", bp) ; printf( "IDR_INTR: csr is 0x%x, dvmamap is 0x%x \n", csr, dvmamap ) ; printf( "IDR_INTR: unit_flags is 0x%x\n",flags) ; #endif IDR_TRACK /* check for multicycle error - don't set B_ERROR since bp might not point to a buffer read/write code will check for MCYL_ERR and set EIO at higher level we will check here since multicycle errors occur on the dr11 side, and may not be associated with specific DVMA activity - so DVMA, EOR, and RDY wait should be tested. Multicycle err during attnwait is probably meaningless- but setting the flag shouldn't cause a problem */ if(iregs_p->Idr_p_stat & IDR_MCER) idr_unit_array[unit_no].unit_flags |= IDR_MCYL_ERR ; /* ATTENTION OR EOR INTERRUPT MAY SLIVER OFF WHEN DMON IS TURNED OFF SINCE A FIFO RESET MAY CAUSE A SPIKE ON FIFO NOT EMPTY - WHICH HOLDS OFF INTERRUPTS. MAKE SURE INTERRUPTS ARE OFF BEFORE DMON TURNED OFF!!! also, since the Sbus pull-up resistors are so light, it takes time for the int lines to rise to the off state, and we don't want the kernel to re-enable while a line is still on (or seeming to be on). */ dregs_p->Dmacsr &= ~DMAIO_INT_ENABLE ; /* ints off as soon as possible!! */ /* do a dummy read an a delay to make absolutely sure that the int line has floated to off by the time we exit from this code! */ delay_dump_0 = dregs_p->Dmaar; DELAY(2); flags &= (IDR_DVMA_WAIT | IDR_EOR_WAIT | IDR_ATTN_WAIT | IDR_RDY_WAIT) ; /*--- each case in this switch is complete - that is, it doesn't count on later code to disable ints, or do the return. I prefer to keep the wakeup and iodone as close to the return(s) as possible, so each case contains all the goodies. this also makes it easier to modify a case without unexpected side effects. ---*/ switch(flags) { case IDR_DVMA_WAIT: /* we are waiting for dma t/c interrupt */ /* attn interrupts will also be enabled */ /* so we will have to check for who */ /* caused the int - and whether the xfer*/ /* is complete */ case IDR_EOR_WAIT: /* or maybe we are waiting for an eor */ /* in auto output mode, eor is the */ /* desired interrupt. in input mode, or*/ /* manual mode, tc is the correct one */ #ifdef IDR_TRACK printf("IDR INTR: case DVMA_WAIT or EOR_WAIT\n") ; #endif IDR_TRACK if(!(bp->b_flags & B_READ)) { /* if we are waiting for an output int */ /* force off the DVMA controller. if */ /* this is an attention terminated out */ /* we could still be screaming data into*/ /* the output data fifo - and we might */ /* get a slave access timeout when we */ /* attempt to access the DR11 registers */ /* if this is an input, eor and attn */ /* inputs will be held off until all */ /* data is transferred into fifo anyway */ /* so we don't(and shouldn't) turn off */ /* the DVMA controller may be cache data*/ /* still to be transferred to memory */ dregs_p->Dmacsr &= ~DMAIO_DMA_ENABLE ; } /*--- check for error pending ---*/ if(csr & DMAIO_ERR_PENDING) { IDR_ERROR_MSG(logprie,"IDR INTR: unit idr%d: error pending - flush_bfr issued! \n",unit_no) ; bp->b_flags |= B_ERROR ; /* what else to do ??? */ } /* attention ints held off until fifo empty in input mode - so even if */ /* we don't get to t/c, all data will be in cache by the time we get */ /* the interrupt. also - the interrupt will force auto-drain of cache */ CDELAY(!(dregs_p->Dmacsr & DMAIO_DRAINING),100) ; /* wait until cache drained */ /* 100 uS max */ csr = dregs_p->Dmacsr ; /* get fresh CSR */ if(csr & DMAIO_DRAINING) { /* hasn't drained yet - flag error */ IDR_ERROR_MSG(logprie,"IDR INTR: unit idr%d: DVMA cache failed to drain!\n",unit_no) ; bp->b_flags |= B_ERROR ; } /* we will force dvma enable bit off here - so it will be safe to touch */ /* the dr11 registers. it is already off if output, and if input, */ /* the cache had better be drained by now!!!! */ dregs_p->Dmacsr &= ~DMAIO_DMA_ENABLE ; if(!(csr & DMAIO_TC | (iregs_p->Idr_p_stat & IDR_EORF))) { /* we must have */ /* an attn that */ /* stopped xfers*/ iregs_p->Idr_p_stat = IDR_RATN ; /* if attn terminated - do */ /* NOT save attn flag */ idr_delay(iregs_p) ; } if(idr_unit_array[unit_no].unit_flags & IDR_EOR_WAIT) { iregs_p->Idr_p_stat = IDR_REOR ; /* if waiting for eor */ /* turn it off. if not, */ /* it may be useful */ /* later so leave alone */ idr_delay(iregs_p) ; } /*--- turn off "waiting-for-dma & eor" flags bit ---*/ idr_unit_array[unit_no].unit_flags &= ~(IDR_DVMA_WAIT | IDR_EOR_WAIT) ; /*--- do a flush-bfr to turn off tc, err pending, and int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /* turn off DR11 interrupt masks */ iregs_p->Idr_latch &= ~(IDR_EORM | IDR_ATTM) ; idr_delay(iregs_p) ; /* pulse TERM & turn off DMON if auto mode */ if(!(idr_unit_array[unit_no].unit_flags & IDR_MANUAL)) { iregs_p->Idr_p_stat = IDR_TERM ; idr_delay(iregs_p) ; iregs_p->Idr_latch &= ~IDR_DMON ; idr_delay(iregs_p) ; } wakeup(bp) ; return ; case IDR_ATTN_WAIT: /* waiting for attention interrupt */ #ifdef IDR_TRACK printf("IDR INTR: at case ATTN_WAIT\n") ; #endif IDR_TRACK /* make sure that the ATTF int flag is set - GROSS ERROR if not */ if(!(iregs_p->Idr_p_stat & IDR_ATTF)) { IDR_ERROR_MSG(logprie,"IDR INTR: unit idr%d: ATTN_WAIT interrupt w/ \ ATTF not set \n",unit_no) ; /* no way to pass this up */ /* to higher level !!! */ } /*--- clear the "waiting-for" flag ---*/ idr_unit_array[unit_no].unit_flags &= ~IDR_ATTN_WAIT ; /*--- turn off dr11 int enable bit ---*/ iregs_p->Idr_latch &= ~IDR_ATTM ; idr_delay(iregs_p) ; /*--- do a flush-bfr to turn off tc, err pending, and int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /*--- wake up the code that is waiting for fifo/rdy interrupt ---*/ wakeup(bp) ; return ; case IDR_RDY_WAIT: /* waiting for READY - either EOR or ATTN int */ #ifdef IDR_TRACK printf("IDR INTR: at case RDY_WAIT\n") ; #endif IDR_TRACK /* make sure that the ATTF or EOR flag is set - GROSS ERROR if not */ if(!(iregs_p->Idr_p_stat & (IDR_ATTF | IDR_EORF))) { IDR_ERROR_MSG(logprie,"IDR INTR: unit idr%d: RDY_WAIT interrupt w/ no flag set!\n",unit_no) ; /* no way to pass this up */ /* to higher level !!! */ } /*--- clear the "waiting-for" flag ---*/ idr_unit_array[unit_no].unit_flags &= ~IDR_RDY_WAIT ; /*--- turn off dr11 int enable bits ---*/ iregs_p->Idr_latch &= ~(IDR_ATTM | IDR_EORM) ; idr_delay(iregs_p) ; /*--- do a flush-bfr to turn off tc, err pending, and int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /*--- wake up the code that is waiting for fifo/rdy interrupt ---*/ wakeup(bp) ; return ; default: /**** we got an actual interrupt when not waiting for anything!!! since no bp assigned - we don't have any way to flag it - so just print a message, and try to turn off the offending interrupt. ****/ IDR_ERROR_MSG(logprie,"IDR INTR: unit idr%d: at case default - interrupt when not waiting! \n",unit_no); /*--- turn off idr int enable bits ---*/ iregs_p->Idr_latch &= ~(IDR_EORM | IDR_ATTM) ; idr_delay(iregs_p) ; /*--- do a flush-bfr to turn off tc, err pending, and int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; return; } } /*------------------------------------ Function: Handle an interrupt or interrupts that may or may not be from one or more of the idr units. This routine is safely declared static because it is explicitly registered by address. Inputs: None. Returns:: 1 if one or more interrupts has been serviced 0 if not -------------------------------------*/ static int idr_poll() { struct dmaio_regs *dregs_p; u_long csr ; /* control and status register */ u_int idr_unit_no; u_int int_serviced = 0; #ifdef IDR_TRACK printf( "IDR_POLL: idr_unit_array is 0x%x .\n", idr_unit_array) ; printf( "IDR_POLL: nidrs is 0x%x\n", nidrs) ; #endif IDR_TRACK /*-- March through the units, checking for an interrupt pending or error pending ---*/ for (idr_unit_no = 0; idr_unit_no < nidrs; ++idr_unit_no) { dregs_p = idr_unit_array[idr_unit_no].dma_regs_p ; csr = dregs_p->Dmacsr ; #ifdef IDR_TRACK printf( "IDR_POLL: csr is 0x%x\n", csr) ; #endif IDR_TRACK if ( csr & (DMAIO_INT_PENDING | DMAIO_ERR_PENDING)) { /*-- Mark that we found an interrupting device. Call the interrupt service routine.*/ int_serviced = 1; (void) idr_intr(idr_unit_no); } } return (int_serviced); } /*================ LOADABLE DEVICE DRIVER SUPPORT ========================*/ extern int nulldev(); extern int seltrue(); /*------- Block device entry points for this driver. These are in bdevsw[] in sun/conf.c for preconfigured drivers.*/ #ifdef NOT_IN_CHARACTER_ONLY_DRIVERS struct bdevsw idr_bdevsw = { idr_open, idr_close, idr_strategy, idr_dump, idr_size, 0 }; #endif NOT_IN_CHARACTER_ONLY_DRIVERS /*------ Character device entry points for this driver. These are in cdevsw[] in sun/conf.c for preconfigured drivers.*/ struct cdevsw idr_cdevsw = { idr_open, idr_close, idr_read, idr_write, idr_ioctl, nulldev, seltrue, 0, 0 }; /*----- The vd driver is a non-loadable pseudo driver that loads loadable modules. The following initialized structure is used by the vd driver to locate entry points and data when loading and configuring the loadable driver. The fields are explained below. VDMAGIC_DRV is the magic number in the Drv_magic field that tells the vd driver that this loadable module is a device driver. If there was no physical SBus device associated with this driver, then VDMAGIC_PSEUDO would be used to indicate that this is a pseudo driver. Drv_name is a pointer to a string which is the name of the driver. Drv_dev_ops is a pointer to the dev_ops structure defined in sun/openprom.h which contains all of the entry points to driver routines which are known outside of the driver. The driver is responsible for having the dev_ops structure initialized. The address of Drv_bdevsw can be NULL if there is no block device for this driver. The address of Drv_cdevsw can be NULL if there is no character device for this driver. If the driver has a preconfigured location in the bedvsw or cdevsw structures, then that location index for the bdevsw goes in Drv_blockmajor, and that location index for the cdevsw goes in Drv_charmajor. This is the case for ll devices that are already supported by sun/conf.c. For drivers that do not have a preconfigured location in the bdevsw, Drv_vlockmajor should be 0, and the system will choose a major device number (location index) when the module is loaded by the vd driver. For drivers that do not have a preconfigured location in the cdevsw, Drv_charmajor should be 0, and the system will choose a major device number (location index) when the module is loaded by the vd driver.*/ struct vdldrv idr_drv = { VDMAGIC_DRV, /* Drv_magic */ "idr\0", /* *Drv_name */ &idr_ops, /* *Drv_dev_ops init to &idr_ops */ NULL, /* *Drv_bdevsw */ &idr_cdevsw, /* *Drv_cdevsw init to &idr_cdevsw */ 0, /* Drv_blockmajor */ 0 /* Drv_charmajor */ }; /*------------------------------------ Function: idr_vdcmd(command, vdp, vdi, vds) Support for a loadable device driver. Required by the utilities modload modstat & modunload. The vd driver is a non-loadable pseudo driver that loads loadable modules. The vd driver is used by modload modstat & modunload. All aloadable modules are required to supply a routine that is called by the vd driver when a module is loaded, unloaded, or the object of a VDSTAT status request ioctl on the vd driver. On the modload man page this routine is referred to as the entry point. Note that the default entry point name "xxxinit()" should never be used, as the names will collide if two or more drivers use this name. Inputs: command Identifies why this module was called. During loading (command == VDLOAD) the driver should do any initialization that is wants to do, get any desired config- uration information that may be available from modload, and then fill in the linkage pointer as shown below. Before unloading (command == VDUNLOAD) the driver is called to determine if it is OK to be unloaded, and to allow the driver to do any cleanup that it needs to do. During a status request (command == VDSTAT) the driver is called to fill in any type specific information into vds_modinfo. vdp Is a pointer to the vddrv structure. This is where the vd driver (think of it as the kernel) collects all information about a loaded module. The driver being loaded will fill in vddrv.vdd_vdtab, which is a pointer to the vdldrv structure initialized above. Since this pointer is not passed to the kernel until the VDLOAD command is handled by this interface routine, these initializations need not be static. vdi Is a pointer to one of the following structures: If the command is VDLOAD: The vdioctl_load structure contains configuration information if any was passed in from modload. If the command is VDUNLOAD: The vdioctl_unload structure contains only the module id. If the command is VDSTAT: The vdioctl_stat structure contains hooks to allow you to build an elaborate status function if you wish. vds Is a pointer to a status structure that may be filled in when the command is VDSTAT. This pointer is NULL when command has a value other than VDSTAT. For more information see /usr/include/sun/vddrv.h. Return: 0 on success, errno value on failure. -------------------------------------*/ int idr_vdcmd(command, vdp, vdi, vds) int command; /*-- Identify why module called*/ struct vddrv *vdp; /*-- Pointer to kernel's structure*/ struct vdioctl_load *vdi; /*-- Pointer to struct vdioctl_* */ struct vdstat *vds; /*-- ID and status information*/ { struct vdconf *vdconf_p; char *string_p; u_int idr_unit_no; int status = 0; IDR_TRACK_MSG(logprit,"---------------(%d)-------------------- \n", vdp->vdd_id); IDR_TRACK_MSG(logprit,"Entering idr_vdcmd, vdp 0x%x, vdi 0x%x,\ vds 0x%x.\n", vdp, vdi, vds); switch (command) { case VDLOAD: IDR_TRACK_MSG(logprit, "Command is VDLOAD.\n"); /*-- For the driver of an SBus device, there are generally only two things to do in the VDLOAD step. The first is to get any available configruation information that may have passed in from a modload configuration file. The second is to fill in the linkage pointer to the vdldrv structure. Most initialization of the driver or its devices must wait until the driver's attach() routine is called because this is when (1) the count of physical devices is known as all calls to the driver's identify() routine have taken place, and (2) the dev_info pointer and therefore the devices themselves become available. Get the configuration info from the vd driver if any was passed in from modload. Some of this code cannot be useful to the idr driver. It is included for illustration. */ if (vdi !=NULL) { IDR_TRACK_MSG(logprit, "vdi_id: %d\n", vdi->vdi_id); IDR_TRACK_MSG(logprit, "vdi_status: %d\n", vdi->vdi_status); IDR_TRACK_MSG(logprit, "vdi_mmapaddr: 0x%x\n", vdi->vdi_mmapaddr); IDR_TRACK_MSG(logprit, "vdi_symtabsize: %d\n", vdi->vdi_symtabsize); IDR_TRACK_MSG(logprit, "vdi_userconf: 0x%x\n", vdi->vdi_userconf); if (vdi->vdi_userconf != NULL) { vdconf_p = vdi->vdi_userconf; if (vdconf_p->vdc_type == VDCEND) { IDR_TRACK_MSG(logprit,"No config info available.\n"); } while (vdconf_p->vdc_type != VDCEND) { switch (vdconf_p->vdc_type) { case VDCCONTROLLER: string_p = "(VDCCONTROLLER)"; break; case VDCDEVICE: string_p = "(VDCDEVICE)"; break; case VDCBLOCKMAJOR: string_p = "(VDCBLOCKMAJOR)"; break; case VDCCHARMAJOR: string_p = "(VDCCHARMAJOR)"; break; case VDCSYSCALLNUM: string_p = "(VDCSYSCALLNUM)"; break; default: IDR_DEBUG_MSG(logprid, "vdconf\ switch error!\n"); string_p = "(SWITCH ERROR!)"; break; } /* end switch() */ IDR_TRACK_MSG(logprit, "vdc_type: %d %s %s 0x%x\n", vdconf_p->vdc_type, string_p, "vdc_data:", vdconf_p->vdc_data); /*-- Move to next element of array*/ vdconf_p++; } /* end while (vdconf_p->vdc_type != VDCEND) */ } /* end if (vdi->vdi_userconf != NULL) */ else { IDR_DEBUG_MSG(logprid, "No config info,\ vdi_userconf is NULL\n"); } } /* end if (vdi !=NULL) */ if (vdi == NULL) { IDR_DEBUG_MSG(logprid, "No config info, vdi is NULL\n"); } /*-- Configure the idr driver from the available information. If no information was passed in from modload, then use the default info from this file. DEVICE SPECIFIC Fill in pointer to the vdldrv structure, which must be already initialized at this point.*/ vdp->vdd_vdtab = (struct vdlinkage *)&idr_drv; IDR_TRACK_MSG(logprit, "Done VDLOAD.\n"); break; /* end case VDLOAD: */ case VDUNLOAD: IDR_TRACK_MSG(logprit, "Command is VDUNLOAD.\n"); /*Check if the driver is in a condition to allow safe unloading. If it is, then cleanly shut the driver down, clean up, and release all dynamically allocated space. BE SURE to remove the driver from any interrupt chains that it is on.*/ for (idr_unit_no = 0; idr_unit_no < nidrs; idr_unit_no++) { /*-- Inhibit further opens on the unit*/ idr_unit_array[idr_unit_no].open_inhibit = 1; /*-- If any any unit is open, cannot unload.*/ if (idr_unit_array[idr_unit_no].unit_open !=0) { status = EBUSY; } } /*-- If it looks good so far, turn off interrupts, remove registration of interrupt vector, release all memory, and permit unload.*/ if (status == 0) { (void) idr_decommission(); } else { /*-- Refuse unload, return to business as usual.*/ for (idr_unit_no = 0; idr_unit_no < nidrs; idr_unit_no++) { /*-- Release the open inhibits*/ idr_unit_array[idr_unit_no].open_inhibit = 0; } } IDR_TRACK_MSG(logprit, "Done VDUNLOAD.\n"); break; /* end case VDUNLOAD: */ case VDSTAT: IDR_TRACK_MSG(logprit, "Command is VDSTAT.\n"); IDR_TRACK_MSG(logprit, "nidrs is 0x%x\n", nidrs) ; IDR_TRACK_MSG(logprit, "idr_unit_array is 0x%x \n", idr_unit_array ) ; /* DEVICE SPECIFIC */ IDR_TRACK_MSG(logprit, "Done VDSTAT.\n"); break; /* end case VDSTAT: */ default: IDR_TRACK_MSG(logprit, "idr_vdcmd: unknown command 0x%x\n",command); status = EINVAL; break; /* end case default */ } /* end switch (command) */ IDR_TRACK_MSG(logprit, "Leaving idr_vdcmd, status (errno): %d.\n", status); return (status); } /*====================== PRIVATE SUPPORT ROUTINES ======================*/ /*------------------------------------ Function: START_CRITICAL(unit_no) Protect a critical section of code from interruption by the unit whose data structures are currently being serviced. In IDR_DEBUG mode, assertion checking is done to assure the proper pairing of START_CRITICAL() and END_CRITICAL() calls. Input: unit_no The current unit number, an index into the idr_unit_array. Returns: None -------------------------------------*/ static void START_CRITICAL(unit_no) register u_int unit_no; { register int temp_saved_spl; IDR_TRACK_MSG(logprit, "Entering START_CRITICAL: unit %d\n", unit_no); /*-- Raise the priority level to (n) and return the previous n */ temp_saved_spl = splr(idr_unit_array[unit_no].interrupt_pri); #ifdef IDR_DEBUG /*-- Do assertion checking. This is a critical section*/ if (idr_unit_array[unit_no].saved_spl != (-1)) { IDR_DEBUG_MSG(logprid, "START_CRITICAL: ASSERT failed,\ saved_spl: %d\n", idr_unit_array[unit_no].saved_spl); } #endif IDR_DEBUG idr_unit_array[unit_no].saved_spl = temp_saved_spl; IDR_TRACK_MSG(logprit, "Leaving START_CRITICAL: unit %d.\n", unit_no); return; } /*------------------------------------ Function: END_CRITICAL(unit_no) End the protection of code from interruption by the unit whose data structures are currently being serviced. In IDR_DEBUG mode, assertion checking is done to assure the proper pairing of START_CRITICAL() and END_CRITICAL calls. Inputs: unit_no The current unit number, an index into the idr_unit. Returns: None. -------------------------------------*/ static void END_CRITICAL(unit_no) register u_int unit_no; { register int temp_saved_spl; IDR_TRACK_MSG(logprit, "Entering END_CRITICAL: unit %d\n", unit_no); temp_saved_spl = idr_unit_array[unit_no].saved_spl; #ifdef IDR_DEBUG /*-- Do assertion checking. This is a critical section*/ if (idr_unit_array[unit_no].saved_spl == (-1)) { IDR_DEBUG_MSG(logprid, "END_CRITICAL: ASSERT failed, saved_spl: %d\n", idr_unit_array[unit_no].saved_spl); } idr_unit_array[unit_no].saved_spl = (-1); #endif IDR_DEBUG /*-- Release the critical section last. The above is a critical section in itself.*/ /*-- Restore the previous priority level */ splx(temp_saved_spl); IDR_TRACK_MSG(logprit, "Leaving END_CRITICAL: unit %d\n", unit_no); return; } /*------------------------------------ Function: see_devinfo(devinfo_p) Show interesting information stored in this node of the devinfo tree for developing and debugging. Inputs: devinfo_p Pointer to a node of the tree. Returns: None. -------------------------------------*/ static void see_devinfo(devinfo_p) register struct dev_info *devinfo_p; { struct dev_reg *dev_reg_p; struct dev_intr *dev_intr_p; u_int counter; IDR_TRACK_MSG(logprit, "Entering see_devinfo: devinfo_p 0x%x\n", devinfo_p); IDR_DEBUG_MSG(logprid, "devinfo_p->devi_name: %s\n", devinfo_p->devi_name); IDR_DEBUG_MSG(logprid, "devinfo_p->devi_unit: %d\n", devinfo_p->devi_unit); IDR_DEBUG_MSG(logprid, "devinfo_p->devi_nreg: %d\n", devinfo_p->devi_nreg); IDR_DEBUG_MSG(logprid, "devinfo_p->devi_reg: 0x%x\n", devinfo_p->devi_reg); /*-- Show all device registers.*/ dev_reg_p = devinfo_p->devi_reg; /*-- dev_reg_p may be NULL, but only if devi_nreg is zero*/ for (counter = 1; counter <= devinfo_p->devi_nreg; ++counter, ++dev_reg_p) { if (dev_reg_p == NULL) break; IDR_DEBUG_MSG(logprid, "REGISTER #%d: devi_reg->reg_bustype:\ 0x%x\n", counter, dev_reg_p->reg_bustype); IDR_DEBUG_MSG(logprid, "REGISTER #%d: devi_reg->reg_addr:\ 0x%x\n", counter, dev_reg_p->reg_addr); IDR_DEBUG_MSG(logprid, "REGISTER #%d: devi_reg->reg_size:\ 0x%x\n", counter, dev_reg_p->reg_size); } IDR_DEBUG_MSG(logprid, "devinfo_p->devi_nintr: %d\n", devinfo_p->devi_nintr); IDR_DEBUG_MSG(logprid, "devinfo_p->devi_intr: %d\n", devinfo_p->devi_intr); /*-- Show all the interrupt data.*/ dev_intr_p = devinfo_p->devi_intr; /*-- dev_intr_p may be NULL, but only if devi_nintr is zero*/ for (counter = 1; counter <= devinfo_p->devi_nintr; ++counter, ++dev_intr_p) { if (dev_intr_p == NULL) { break; } IDR_DEBUG_MSG(logprid, "INTERRUPT #%d: devi_intr->int_pri: 0x%x\n", counter, dev_intr_p->int_pri); IDR_DEBUG_MSG(logprid, "INTERRUPT #%d: devi_intr->int_vec: 0x%x\n", counter, dev_intr_p->int_vec); } IDR_DEBUG_MSG(logprid, "deviinfo_p->devi_data: 0x%x\n", devinfo_p->devi_data); IDR_DEBUG_MSG(logprid, "deviinfo_p->devi_nodeid: 0x%x\n", devinfo_p->devi_nodeid); IDR_DEBUG_MSG(logprid, "Leaving see_devinfo: devinfo_p 0x%x\n", devinfo_p); return; } /*------------------------------------ Function: idr_decomission() Turn off interrupts, remove registration of all interrupt vectors, and relaease all memory. Note that the info for the interrupt levels came from the FCode PROM on each device, and therefore may vary from unit to unit. Be sure to remove all interrupt vectors, or the kernel will bus error if the device interrupts while the driver is unloaded. Inputs: None. Returns: None -------------------------------------*/ static void idr_decommission() { struct dev_intr *dev_intr_p; u_int idr_unit_no; int counter; IDR_TRACK_MSG(logprit, "Entering idr_decommission.\n"); IDR_TRACK_MSG(logprit, "At Decomm point zzxxx11yyyy\n"); /*-- Turn off interrupts for each unit.*/ IDR_TRACK_MSG(logprit, "nidrs is 0x%x\n", nidrs) ; IDR_TRACK_MSG(logprit, "idr_unit_array is 0x%x \n", idr_unit_array ) ; if (nidrs == 0) goto SKIP_DECOMM ; IDR_TRACK_MSG(logprit, "[]Dmacsr is 0x%x \n", idr_unit_array[0].dma_regs_p->Dmacsr) ; IDR_TRACK_MSG(logprit, "At Decomm point zzxxx15yyyy\n"); /*********** this is comented out - it was in example, but why?? ***********/ /* for (t=0; t<10000; t++) t=t ;*/ /* kill some time */ /* WHY!!!!!!! */ for (idr_unit_no = 0; idr_unit_no < nidrs; idr_unit_no++) { /*-- ALL FIELDS CLEARED, idle state*/ idr_dma_init(idr_unit_no) ; } IDR_TRACK_MSG(logprit, "At Decomm point zzxxx2yyyy\n"); /*--Everything that we need to know is in the unit structures. From there we access the devinfo struct to find the interrupt levels that we are (might be) installed on. Since duplicate registrations with addintr() are ingnored, duplicate unregistrations cannot be considered an error. March through and do everything meticulously. Do not take short cuts or you die.*/ for (idr_unit_no = 0; idr_unit_no < nidrs; idr_unit_no++) { IDR_TRACK_MSG(logprit, "At Decomm point zzxxx3yyyy\n"); START_CRITICAL(idr_unit_no); IDR_TRACK_MSG(logprit, "At Decomm point zzxxx4yyyy\n"); dev_intr_p = idr_unit_array[idr_unit_no]. devinfo_p->devi_intr; /*-- dev_intr_p may be NULL, but only if devi_nintr is zero*/ IDR_TRACK_MSG(logprit, "At Decomm point zzxxx5yyyy\n"); for (counter = idr_unit_array[idr_unit_no].devinfo_p->devi_nintr; counter > 0; --counter, ++dev_intr_p){ if (dev_intr_p == NULL) { /*-- A "can't happen"*/ IDR_ERROR_MSG(logprie, "idr_decommission ASSERT FAILED.\n"); break; } IDR_TRACK_MSG(logprit, "At Decomm point zzxxx6yyyy\n"); (void) remintr(dev_intr_p->int_pri, idr_poll); } IDR_TRACK_MSG(logprit, "At Decomm point zzxxx7yyyy\n"); END_CRITICAL(idr_unit_no); } /*-- Release the memory allocated for the unit structures*/ IDR_TRACK_MSG(logprit, "At Decomm point zzxxx8yyyy\n"); if ((idr_unit_array !=NULL) && (nidrs >0)) { IDR_TRACK_MSG(logprit, "At Decomm point zzxxx9yyyy\n"); (void) kmem_free ((caddr_t)idr_unit_array, (u_long)(nidrs * sizeof (struct idr_unit))); idr_unit_array = NULL; } SKIP_DECOMM: IDR_TRACK_MSG(logprit, "Leaving idr_decommission.\n"); return; } /*------------------------------------ Function: get_property(devinfo_p, propname, defaultvalue) Get a property from the FCode PROM on the SBus card, or if the property is not available, substitute the assigned default. Only string valued properties are handled here. You have to know the name of a property that you want to get. There is no way to get all defined properties. The kernel support routine getlongprop() allows drivers to retrieve the value of a property for arbitrary sized properties. A pointer to the value of 'name' in the property list for 'devi' is returned. If no value is found, NULL is returned. The space is allocated using kmem_zalloc, so it is assumed that this routine is NOT called from an interrupt routine. If getlongprop() is called often, you should consider recycling the space with kmem_free(). Inputs: propname A pointer to string which is the assigned name of the desired property. defaultvalue A pointer to string which is the default value to use if the named property is not defined in the devinfo structure. Returns: A pointer to the value string selected. This pointer may have any value that was passed in for the default, including a NULL string or a NULL pointer. -------------------------------------*/ static char * get_property(devinfo_p, propname, defaultvalue) struct dev_info *devinfo_p; char *propname; char *defaultvalue; { char *ret_getlongprop; char *propval; char *msg_string; IDR_TRACK_MSG(logprit, "Entering get_property: devinfo_p: 0x%x\n", devinfo_p); ret_getlongprop = (char *) getlongprop(devinfo_p->devi_nodeid, propname); if (ret_getlongprop == NULL) { propval = defaultvalue; msg_string = "default"; } else { propval = ret_getlongprop; msg_string = "from prom"; } IDR_TRACK_MSG(logprit, "Leaving get_property: %s=%s (%s)\n", propname, propval, msg_string); } /*------------------------------------ Function: idr_strategy(bp) Setup and start a transfer on the device. Inputs: bp Pointer to the buf struct in use. Returns: None -------------------------------------*/ static void idr_strategy(bp) register struct buf *bp; { struct dmaio_regs *dregs_p ; struct idr_regs *iregs_p ; u_int unit_no ; u_long csr ; /* control and status register */ u_long ar ; /* address register */ u_long bcr ; /* byte count register */ u_long range ; /* dr11 range counter */ u_char ct ; /* temp char storage */ int sleepval ; /* value returned from sleep */ /*-- Get the unit number to locate our data structures. Set pointer to DVMA registers.*/ unit_no = IDR_UNIT(bp->b_dev) ; dregs_p = idr_unit_array[unit_no].dma_regs_p ; iregs_p = idr_unit_array[unit_no].idr_regs_p ; /*---- clear the dr11 interrupt flags!! ----*/ iregs_p->Idr_latch &= ~(IDR_ATTM | IDR_EORM) ; idr_delay(iregs_p) ; /*---- pulse flags off if auto mode - leave for other levels if not----*/ if(!(idr_unit_array[unit_no].unit_flags & IDR_MANUAL)) { iregs_p->Idr_p_stat = IDR_RATN | IDR_REOR ; idr_delay(iregs_p) ; } /*---- force off int and dma enable & flush to clear t/c ----*/ dregs_p->Dmacsr &= ~(DMAIO_INT_ENABLE | DMAIO_DMA_ENABLE) ; dregs_p->Dmacsr |= DMAIO_FLUSH_BFR ; IDR_TRACK_MSG(logprit, "IDR_STRGY: idr_unit_array is 0x%x .\n", idr_unit_array) ; IDR_TRACK_MSG(logprit, "IDR_STRGY: unit number %d.\n", unit_no) ; IDR_TRACK_MSG(logprit, "IDR_STRGY: dvmamap is 0x%x\n", dvmamap ) ; /*-- Get DVMA bus resource, sleeping if necessary.*/ bp->b_mbinfo = mb_mapalloc(dvmamap, bp, 0, ((int (*) ())0), (caddr_t)0); IDR_TRACK_MSG(logprit, "IDR_STRGY: bp->b_mbinfo is 0x%x\n", bp->b_mbinfo) ; IDR_TRACK_MSG(logprit, "IDR_STRGY: DVMA is 0x%x\n", DVMA) ; /*-- Add the offset into DVMA space to the start of DVMA space. .*/ ar = (u_long) (MBI_ADDR(bp->b_mbinfo) + DVMA); bcr = bp->b_bcount; IDR_TRACK_MSG(logprit, "IDR_STRGY: ar is 0x%x, bcr is 0x%x\n", ar, bcr) ; /*-- Write the DVMA start address and byte count to the hardware.*/ dregs_p->Dmaar = ar; IDR_TRACK_MSG(logprit, "IDR_STRGY: checkpoint xxxx1 Wrote ar\n") ; dregs_p->Dmabcr = bcr ; IDR_TRACK_MSG(logprit, "IDR_STRGY: checkpoint xxxx2 Wrote bcr\n") ; /* if automatic, set dr11 range counter, but check for max word count */ if(!(idr_unit_array[unit_no].unit_flags & IDR_MANUAL)) { /* automatic */ IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d at autostart\n",unit_no) ; range = (bcr >> 1) - 1 ; /* make word count -1 */ if(range > IDR_DR11_MAXBLOCK) { IDR_ERROR_MSG(logprie,"idr_strategy: unit idr%d DR11 range count too large!\n",unit_no) ; goto ERRORSTRATEGY ; } iregs_p->Idr_rhi = (u_char)(range >> 16) ; /* set up range */ idr_delay(iregs_p) ; iregs_p->Idr_rmid = (u_char)(range >> 8) ; idr_delay(iregs_p) ; iregs_p->Idr_rlow = (u_char)range ; idr_delay(iregs_p) ; iregs_p->Idr_latch &= (u_char)~(IDR_DMIN | IDR_DMON) ; /* dmin & dmon off */ idr_delay(iregs_p) ; if(bp->b_flags & B_READ) { /* set read function bits and force DMIN to input */ ct = iregs_p->Idr_latch & ~IDR_FMASK ; iregs_p->Idr_latch = (u_char)(IDR_DMIN | idr_unit_array[unit_no].read_fcn | ct) ; idr_delay(iregs_p) ; idr_unit_array[unit_no].unit_flags |= IDR_DVMA_WAIT ; /* set csr value to be issued later */ csr = DMAIO_FASTER | DMAIO_COUNT_ENABLE | DMAIO_DMA_ENABLE | DMAIO_DMA_MEM_WRITE | DMAIO_INT_ENABLE ; IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d at autostart read: csr is 0x%x\n",unit_no,csr) ; /* set attm but not eorm for read */ iregs_p->Idr_latch |= IDR_ATTM ; idr_delay(iregs_p) ; } /* end of if read */ else { /* set write fcn bits - leave dmin off */ /* set write function bits and leave DMIN to off */ ct = iregs_p->Idr_latch & ~IDR_FMASK ; iregs_p->Idr_latch = (u_char)(idr_unit_array[unit_no].write_fcn | ct) ; idr_delay(iregs_p) ; /* if output and automatic, wait for dr11 eor */ idr_unit_array[unit_no].unit_flags |= IDR_EOR_WAIT ; /* set csr value, to be issued later */ csr = DMAIO_TCI_DIS | DMAIO_FASTER | DMAIO_COUNT_ENABLE | DMAIO_DMA_ENABLE | DMAIO_INT_ENABLE ; /* disable tci - eor will interrupt */ IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d at autostart write: csr is 0x%x\n",unit_no,csr) ; /* set attm and eorm for write */ iregs_p->Idr_latch |= (IDR_ATTM | IDR_EORM) ; } /* end of else */ iregs_p->Idr_latch |= IDR_DMON ; /* enable dr11 block */ idr_delay(iregs_p) ; } /* end of if automatic (!manual) */ else { /* else --- manual */ IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d at manual start\n",unit_no); idr_unit_array[unit_no].unit_flags |= IDR_DVMA_WAIT ; /* dvma wait for r&w */ if(bp->b_flags & B_READ) { /* set csr for read */ csr = DMAIO_FASTER | DMAIO_COUNT_ENABLE | DMAIO_DMA_ENABLE | DMAIO_INT_ENABLE | DMAIO_DMA_MEM_WRITE ; IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d at manual start read: csr = 0x%x\n",unit_no,csr) ; } else { /* set csr for write */ csr = DMAIO_FASTER | DMAIO_COUNT_ENABLE | DMAIO_DMA_ENABLE | DMAIO_INT_ENABLE ; IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d at manual start write: csr = 0x%x\n",unit_no,csr) ; } /* end of if write */ iregs_p->Idr_latch |= IDR_ATTM ; /* attention always enabled */ idr_delay(iregs_p) ; } /* end of else (if manual) */ /*--- error flags not cleared here so they will last between multiple calls to strategy by physio. this way if physio breaks up a buffer into multiple buffers, a timeout will still show up in flags when we get back to the _write routine. ---*/ /* it is assumed that go will be issued here if auto mode. go */ /* is required to enable the DR11 logic. in manual mode go will*/ /* be handled by issuing pulses directly. we will force go on */ /* here - FIRST MAKE SURE THAT ATTENTION IS NOT ON - attention */ /* disables dma. attention may be a software generated pulse */ /* from the other end - and may be multiple microseconds long. */ /* wait for a reasonable amount of time - THIS MAY NEED TO BE */ /* MODIFIED FOR A PARTICULAR APPLICATION - or the calling */ /* program could wait for attention to go away before calling */ /* read or write. attention here in manual mode could be legit! */ /************************ this is a little tricky ***************/ /* */ /* we will issue the DR11 pulses before starting DVMA if we are */ /* doing output, and after starting DVMA if input. */ /* */ /* the reasoning is as follows: if we do the go before DVMA is */ /* started for input, a very fast device, especially one that */ /* just stuffs data at us without the ability to wait for a */ /* handshake between words may fill up the input fifo before we */ /* get the dvma logic fired up - particularly if this code gets */ /* interrupted or otherwise delayed. */ /* */ /* HOWEVER - if we start dvma first for output, it will start */ /* screaming data at the fifo at three sbus clocks per byte, and*/ /* the CPU will not be able to access the D channel to do the */ /* slave write to the DR11 logic to issue the go pulse!!!! */ /* the result is a data access timeout, and a very unsightly */ /* kernel PANIC. */ /****************************************************************/ if(!(idr_unit_array[unit_no].unit_flags & IDR_MANUAL)) { /* test here for attention - for both input and output modes */ IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d auto mode-testing for ATTENTION\n",unit_no) ; CDELAY(!(iregs_p->Idr_p_stat & IDR_ATTN),100) ; /* 100uS max wait */ if(iregs_p->Idr_p_stat & IDR_ATTN) { IDR_ERROR_MSG(logprie,"idr_strategy: unit idr%d attempt to issue GO while ATTENTION true!",unit_no) ; goto ERRORSTRATEGY ; } if(!(bp->b_flags & B_READ)) { /* auto write-issue pulses B4 DVMA on */ IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d auto write-issuing DR11 pulses. pulse = 0x%x\n",unit_no,idr_unit_array[unit_no].write_pulse|IDR_GO); iregs_p->Idr_p_stat =(u_char)(idr_unit_array[unit_no].write_pulse | IDR_GO) ; idr_delay(iregs_p) ; } } /*--- fire up the dvma logic ---*/ IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d at DVMA csr write (DVMA start): csr = 0x%x\n",unit_no,csr) ; /*--- start timer ---*/ timeout(idr_timeout,(caddr_t)bp,idr_unit_array[unit_no].dma_time) ; START_CRITICAL(unit_no) ; dregs_p->Dmacsr = csr ; /* issue the selected bits to DVMA CSR */ /* if automatic and read mode - issue go AFTER starting DVMA!!!! we won't do a delay call (& iregs read) after issuing go since the GO may cause data to start screaming across the bus, and we don't want an access timeout! */ if(!(idr_unit_array[unit_no].unit_flags & IDR_MANUAL)){ if(bp->b_flags & B_READ) { IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d auto read-issuing DR11 pulses. pulse = 0x%x\n",unit_no,idr_unit_array[unit_no].read_pulse|IDR_GO); iregs_p->Idr_p_stat =(u_char)(idr_unit_array[unit_no].read_pulse | IDR_GO) ; } } sleepval = sleep(bp,(IDRPRI | PCATCH)) ; /* sleep until done, timeout, or signal */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /* disable DVMA chip ints and DVMA enb and flush buffer */ /* basically a no-op unless we got here by signal */ /* we will do a dummy read to force a wait until the flush buffer actually gets to the bus, then we will delay. if we got an int after the sleep, but before the flush, flush will turn it off, but because of the weak pull-ups on the bus, it may take a while before it floats to zero, and we don't want it around when we do the END_CRITICAL call. we hope that the compiler doesn't optimize this read out of existence! */ delay_dump_0 = dregs_p->Dmaar; DELAY(2); /****** we must also do flush in int code and timeout code. if int pending bit is left on and another card interrupts on the same level, and we are polled first, there is a chance of an endless loop! ******/ iregs_p->Idr_latch &= ~(IDR_EORM | IDR_ATTM) ; /* and turn off internal dr11 int mask bits */ END_CRITICAL(unit_no) ; untimeout(idr_timeout,bp) ; /* timer off */ if(sleepval) { /* if awakened by signal */ idr_unit_array[unit_no].unit_flags |= IDR_SIG_RECEIVED ; /* alert the troops */ dregs_p->Dmacsr = DMAIO_DEV_RESET ; /* hammer the DVMA chip off - just flush might do it */ dregs_p->Dmacsr = 0 ; dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; iregs_p->Idr_latch &= ~(IDR_DMON | IDR_ATTM | IDR_EORM) ; idr_delay(iregs_p) ; iregs_p->Idr_p_stat = IDR_TERM ; idr_delay(iregs_p) ; bp->b_flags |= B_ERROR ; /* just for good measure */ } IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d leaving strategy routine\n",unit_no) ; bp->b_resid = dregs_p->Dmabcr ; /* report # of bytes transferred */ (void)mb_mapfree(dvmamap,&bp->b_mbinfo) ; /* release DVMA space */ (void)iodone(bp) ; /* tell physio we are done */ return ; ERRORSTRATEGY: IDR_TRACK_MSG(logprit,"IDR_STRGY: unit idr%d at ERRORSTRATEGY\n",unit_no) ; /* release (unused) dvma resource, reset dvma and dr11, flag error, and issue iodone to physio */ dregs_p->Dmacsr = DMAIO_DEV_RESET ; dregs_p->Dmacsr = 0 ; dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; iregs_p->Idr_latch &= ~(IDR_DMON | IDR_ATTM | IDR_EORM) ; idr_delay(iregs_p) ; iregs_p->Idr_p_stat = IDR_TERM ; idr_delay(iregs_p) ; bp->b_flags |= B_ERROR ; bp->b_resid = bcr ; /* report # of bytes xferred (none) DVMA counter may not be set */ /* so use previously calculated byte count */ (void) mb_mapfree(dvmamap,&bp->b_mbinfo) ; (void)iodone(bp) ; return; } /* end of stratety */ /*--- initialize the LSI dma chip and flush the idr fifo - also clears the latched function register ---*/ static void idr_dma_init(unit_no) u_int unit_no ; { /*-- set and clear the dma chip's reset bit. if left set the system will panic when a dma xfer is attempted --*/ idr_unit_array[unit_no].dma_regs_p->Dmacsr = DMAIO_DEV_RESET ; idr_unit_array[unit_no].dma_regs_p->Dmacsr = 0 ; /*-- pulse the Dmaio flush bit - reset doesn't clear the t/c flag ---*/ idr_unit_array[unit_no].dma_regs_p->Dmacsr = DMAIO_FLUSH_BFR ; /*-- master clear the DR11 logic and fifo. this does not send a reset to the device. that is done by the application code if needed. it is also done when the bus is reset. ---*/ idr_unit_array[unit_no].idr_regs_p->Idr_p_stat = IDR_MCLR ; idr_delay(idr_unit_array[unit_no].idr_regs_p) ; } /* end of idr_dma_init */ /*----- function idr_timeout() called by the real time clock interrupt handler if timeout is reached. will issue iodone or wakeup, depending on what we were waiting for. also sets timeout flag in unit_array and sets B_ERROR in buf if we are waiting for dma to complete. -----*/ static int idr_timeout(bp) register struct buf *bp ; { struct dmaio_regs *dregs_p ; struct idr_regs *iregs_p ; u_int unit_no ; u_int flags ; unit_no = IDR_UNIT(bp->b_dev) ; /* get unit number */ /*--- the following code will look a lot like the interrupt handler ---*/ /*-- Set pointer to DVMA & idr registers. Set pointer to buf structure.*/ dregs_p = idr_unit_array[unit_no].dma_regs_p ; iregs_p = idr_unit_array[unit_no].idr_regs_p ; START_CRITICAL(unit_no) ; /* we are already in the clock's */ /* interrupt handler, so this is */ /* probably not necessary. */ /* IT SHOULD BE SAFE TO DO SINCE */ /* THE SPLR IN START CRIT WON'T */ /* LOWER THE PRIORITY LEVEL ???? */ /*--- turn off interrupts so we don't get rudely interrupted!! ---*/ /*--- and dvma so data being screamed to and from fifo won't ---*/ /*--- cause a slave access timeout!!! ---*/ dregs_p->Dmacsr &= ~(DMAIO_INT_ENABLE | DMAIO_DMA_ENABLE) ; /* force a dummy read to make sure int and dma disable has left the write posting buffer before we re-enable interrupts - in case we get an int after we enter the timeout routine */ delay_dump_0 = dregs_p->Dmaar; DELAY(2); END_CRITICAL(unit_no) ; flags = idr_unit_array[unit_no].unit_flags ; #ifdef IDR_TRACK printf("IDR TIMEOUT: called with flags = 0x%x\n",flags) ; #endif IDR_TRACK /* check for multicycle error - don't set B_ERROR since bp might not point to a buffer read/write code will check for MCYL_ERR and set EIO at higher level we will check here since multicycle errors occur on the dr11 side, and may not be associated with specific DVMA activity - so DVMA, EOR, and RDY wait should be tested. Multicycle err during attnwait is probably meaningless- but setting the flag shouldn't cause a problem */ if(iregs_p->Idr_p_stat & IDR_MCER) idr_unit_array[unit_no].unit_flags |= IDR_MCYL_ERR ; flags &= (IDR_DVMA_WAIT | IDR_EOR_WAIT | IDR_ATTN_WAIT | IDR_RDY_WAIT) ; /*--- each case in this switch is complete - that is, it doesn't count on later code to disable ints, or do the return. I prefer to keep the wakeup and iodone as close to the return(s) as possible, so each case contains all the goodies. this also makes it easier to modify a case without unexpected side effects. ---*/ switch(flags) { case IDR_DVMA_WAIT: /* we are waiting for dma t/c interrupt */ case IDR_EOR_WAIT: /* or waiting for EOR */ #ifdef IDR_TRACK printf("IDR TIMEOUT: case DVMA_WAIT or EOR_WAIT \n") ; #endif IDR_TRACK if(idr_unit_array[unit_no].unit_flags & IDR_DVMA_WAIT) { idr_unit_array[unit_no].unit_flags |= IDR_DVMA_TIMEOUT ; } else { idr_unit_array[unit_no].unit_flags |= IDR_EOR_TIMEOUT ; } /*--- turn off "waiting-for-dma & eor" flags bit ---*/ idr_unit_array[unit_no].unit_flags &= ~(IDR_DVMA_WAIT | IDR_EOR_WAIT) ; /*--- turn off dma enb and int enable bits ---*/ dregs_p->Dmacsr &= ~(DMAIO_DMA_ENABLE | DMAIO_INT_ENABLE) ; /*--- do a flush-bfr to turn off tc, err pending, and int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /* turn off DR11 interrupt masks & shut off DMON */ iregs_p->Idr_latch &= ~(IDR_EORM | IDR_ATTM | IDR_DMON ) ; idr_delay(iregs_p) ; /* issue term and pulse off any flags */ iregs_p->Idr_p_stat = IDR_TERM | IDR_RATN | IDR_REOR ; idr_delay(iregs_p) ; bp->b_flags |= B_ERROR ; /* tell physio we didn't make it */ wakeup(bp) ; /* wake up strategy */ return(0) ; /* do it right after wakeup (?) */ case IDR_RDY_WAIT: /* waiting for READY - eor or attn int */ #ifdef IDR_TRACK printf("IDR TIMEOUT: at case RDY_WAIT \n") ; #endif IDR_TRACK /*--- clear appropriate "waiting-for" flag ---*/ idr_unit_array[unit_no].unit_flags &= ~IDR_RDY_WAIT ; /*--- set appropriate "waiting for" timeout ---*/ idr_unit_array[unit_no].unit_flags |= IDR_RDY_TIMEOUT ; /*--- turn off idr int enable bits ---*/ iregs_p->Idr_latch &= ~(IDR_EORM | IDR_ATTM); /*--- do a flush-bfr to turn off tc, err pending, and int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /*--- wake up the code that is waiting for READY ---*/ /*--- this may or may not be an error!! ---*/ wakeup(bp) ; return(0) ; case IDR_ATTN_WAIT: /* waiting for ATTENTION */ #ifdef IDR_TRACK printf("IDR TIMEOUT: at case ATTN_WAIT \n") ; #endif IDR_TRACK /*--- clear appropriate "waiting-for" flag ---*/ idr_unit_array[unit_no].unit_flags &= ~IDR_ATTN_WAIT ; /*--- set appropriate "waiting for" timeout ---*/ idr_unit_array[unit_no].unit_flags |= IDR_ATTN_TIMEOUT ; /*--- turn off idr int enable bits ---*/ iregs_p->Idr_latch &= ~(IDR_EORM | IDR_ATTM); /*--- do a flush-bfr to turn off tc, err pending, and int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; /*--- wake up the code that is waiting for ATTENTION ---*/ /*--- this may or may not be an error!! ---*/ wakeup(bp) ; return(0) ; default: /* if we get here things are really cooked !!! */ printf("IDR TIMEOUT: at case default: timeout when not waiting on anything!!!!\n") ; /*--- do a flush-bfr to turn off tc, err pending, and int pending */ dregs_p->Dmacsr = DMAIO_FLUSH_BFR ; return(0) ; } } /* idr_delay routine does two reads of the "ikon" registers to force any posted writes to these registers to be flushed to the bus, and to add delay so the writes have time to complete (the "ikon" registers need 300ns following a write to finish up internally). This routine gets called often - but relative to the time taken to transfer dma blocks, it will have an (almost) unmeasureable effect on transfer rate. */ static void idr_delay(iregs_p) struct idr_regs *iregs_p; { delay_dump_0 = iregs_p->Idr_rlow; delay_dump_1 = iregs_p->Idr_rhi; }