diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/NCR5380.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/NCR5380.c')
-rw-r--r-- | drivers/scsi/NCR5380.c | 2862 |
1 files changed, 2862 insertions, 0 deletions
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c new file mode 100644 index 00000000000..5e71a0beafc --- /dev/null +++ b/drivers/scsi/NCR5380.c @@ -0,0 +1,2862 @@ +/* + * NCR 5380 generic driver routines. These should make it *trivial* + * to implement 5380 SCSI drivers under Linux with a non-trantor + * architecture. + * + * Note that these routines also work with NR53c400 family chips. + * + * Copyright 1993, Drew Eckhardt + * Visionary Computing + * (Unix and Linux consulting and custom programming) + * drew@colorado.edu + * +1 (303) 666-5836 + * + * DISTRIBUTION RELEASE 6. + * + * For more information, please consult + * + * NCR 5380 Family + * SCSI Protocol Controller + * Databook + * + * NCR Microelectronics + * 1635 Aeroplaza Drive + * Colorado Springs, CO 80916 + * 1+ (719) 578-3400 + * 1+ (800) 334-5454 + */ + +/* + * $Log: NCR5380.c,v $ + + * Revision 1.10 1998/9/2 Alan Cox + * (alan@redhat.com) + * Fixed up the timer lockups reported so far. Things still suck. Looking + * forward to 2.3 and per device request queues. Then it'll be possible to + * SMP thread this beast and improve life no end. + + * Revision 1.9 1997/7/27 Ronald van Cuijlenborg + * (ronald.van.cuijlenborg@tip.nl or nutty@dds.nl) + * (hopefully) fixed and enhanced USLEEP + * added support for DTC3181E card (for Mustek scanner) + * + + * Revision 1.8 Ingmar Baumgart + * (ingmar@gonzo.schwaben.de) + * added support for NCR53C400a card + * + + * Revision 1.7 1996/3/2 Ray Van Tassle (rayvt@comm.mot.com) + * added proc_info + * added support needed for DTC 3180/3280 + * fixed a couple of bugs + * + + * Revision 1.5 1994/01/19 09:14:57 drew + * Fixed udelay() hack that was being used on DATAOUT phases + * instead of a proper wait for the final handshake. + * + * Revision 1.4 1994/01/19 06:44:25 drew + * *** empty log message *** + * + * Revision 1.3 1994/01/19 05:24:40 drew + * Added support for TCR LAST_BYTE_SENT bit. + * + * Revision 1.2 1994/01/15 06:14:11 drew + * REAL DMA support, bug fixes. + * + * Revision 1.1 1994/01/15 06:00:54 drew + * Initial revision + * + */ + +/* + * Further development / testing that should be done : + * 1. Cleanup the NCR5380_transfer_dma function and DMA operation complete + * code so that everything does the same thing that's done at the + * end of a pseudo-DMA read operation. + * + * 2. Fix REAL_DMA (interrupt driven, polled works fine) - + * basically, transfer size needs to be reduced by one + * and the last byte read as is done with PSEUDO_DMA. + * + * 4. Test SCSI-II tagged queueing (I have no devices which support + * tagged queueing) + * + * 5. Test linked command handling code after Eric is ready with + * the high level code. + */ + +#if (NDEBUG & NDEBUG_LISTS) +#define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); } +#define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); } +#else +#define LIST(x,y) +#define REMOVE(w,x,y,z) +#endif + +#ifndef notyet +#undef LINKED +#undef REAL_DMA +#endif + +#ifdef REAL_DMA_POLL +#undef READ_OVERRUNS +#define READ_OVERRUNS +#endif + +#ifdef BOARD_REQUIRES_NO_DELAY +#define io_recovery_delay(x) +#else +#define io_recovery_delay(x) udelay(x) +#endif + +/* + * Design + * + * This is a generic 5380 driver. To use it on a different platform, + * one simply writes appropriate system specific macros (ie, data + * transfer - some PC's will use the I/O bus, 68K's must use + * memory mapped) and drops this file in their 'C' wrapper. + * + * (Note from hch: unfortunately it was not enough for the different + * m68k folks and instead of improving this driver they copied it + * and hacked it up for their needs. As a consequence they lost + * most updates to this driver. Maybe someone will fix all these + * drivers to use a common core one day..) + * + * As far as command queueing, two queues are maintained for + * each 5380 in the system - commands that haven't been issued yet, + * and commands that are currently executing. This means that an + * unlimited number of commands may be queued, letting + * more commands propagate from the higher driver levels giving higher + * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported, + * allowing multiple commands to propagate all the way to a SCSI-II device + * while a command is already executing. + * + * + * Issues specific to the NCR5380 : + * + * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead + * piece of hardware that requires you to sit in a loop polling for + * the REQ signal as long as you are connected. Some devices are + * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect + * while doing long seek operations. + * + * The workaround for this is to keep track of devices that have + * disconnected. If the device hasn't disconnected, for commands that + * should disconnect, we do something like + * + * while (!REQ is asserted) { sleep for N usecs; poll for M usecs } + * + * Some tweaking of N and M needs to be done. An algorithm based + * on "time to data" would give the best results as long as short time + * to datas (ie, on the same track) were considered, however these + * broken devices are the exception rather than the rule and I'd rather + * spend my time optimizing for the normal case. + * + * Architecture : + * + * At the heart of the design is a coroutine, NCR5380_main, + * which is started from a workqueue for each NCR5380 host in the + * system. It attempts to establish I_T_L or I_T_L_Q nexuses by + * removing the commands from the issue queue and calling + * NCR5380_select() if a nexus is not established. + * + * Once a nexus is established, the NCR5380_information_transfer() + * phase goes through the various phases as instructed by the target. + * if the target goes into MSG IN and sends a DISCONNECT message, + * the command structure is placed into the per instance disconnected + * queue, and NCR5380_main tries to find more work. If the target is + * idle for too long, the system will try to sleep. + * + * If a command has disconnected, eventually an interrupt will trigger, + * calling NCR5380_intr() which will in turn call NCR5380_reselect + * to reestablish a nexus. This will run main if necessary. + * + * On command termination, the done function will be called as + * appropriate. + * + * SCSI pointers are maintained in the SCp field of SCSI command + * structures, being initialized after the command is connected + * in NCR5380_select, and set as appropriate in NCR5380_information_transfer. + * Note that in violation of the standard, an implicit SAVE POINTERS operation + * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS. + */ + +/* + * Using this file : + * This file a skeleton Linux SCSI driver for the NCR 5380 series + * of chips. To use it, you write an architecture specific functions + * and macros and include this file in your driver. + * + * These macros control options : + * AUTOPROBE_IRQ - if defined, the NCR5380_probe_irq() function will be + * defined. + * + * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically + * for commands that return with a CHECK CONDITION status. + * + * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential + * transceivers. + * + * DONT_USE_INTR - if defined, never use interrupts, even if we probe or + * override-configure an IRQ. + * + * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512 + * bytes at a time. Since interrupts are disabled by default during + * these transfers, we might need this to give reasonable interrupt + * service time if the transfer size gets too large. + * + * LINKED - if defined, linked commands are supported. + * + * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases. + * + * REAL_DMA - if defined, REAL DMA is used during the data transfer phases. + * + * REAL_DMA_POLL - if defined, REAL DMA is used but the driver doesn't + * rely on phase mismatch and EOP interrupts to determine end + * of phase. + * + * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You + * only really want to use this if you're having a problem with + * dropped characters during high speed communications, and even + * then, you're going to be better off twiddling with transfersize + * in the high level code. + * + * Defaults for these will be provided although the user may want to adjust + * these to allocate CPU resources to the SCSI driver or "real" code. + * + * USLEEP_SLEEP - amount of time, in jiffies, to sleep + * + * USLEEP_POLL - amount of time, in jiffies, to poll + * + * These macros MUST be defined : + * NCR5380_local_declare() - declare any local variables needed for your + * transfer routines. + * + * NCR5380_setup(instance) - initialize any local variables needed from a given + * instance of the host adapter for NCR5380_{read,write,pread,pwrite} + * + * NCR5380_read(register) - read from the specified register + * + * NCR5380_write(register, value) - write to the specific register + * + * NCR5380_implementation_fields - additional fields needed for this + * specific implementation of the NCR5380 + * + * Either real DMA *or* pseudo DMA may be implemented + * REAL functions : + * NCR5380_REAL_DMA should be defined if real DMA is to be used. + * Note that the DMA setup functions should return the number of bytes + * that they were able to program the controller for. + * + * Also note that generic i386/PC versions of these macros are + * available as NCR5380_i386_dma_write_setup, + * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual. + * + * NCR5380_dma_write_setup(instance, src, count) - initialize + * NCR5380_dma_read_setup(instance, dst, count) - initialize + * NCR5380_dma_residual(instance); - residual count + * + * PSEUDO functions : + * NCR5380_pwrite(instance, src, count) + * NCR5380_pread(instance, dst, count); + * + * The generic driver is initialized by calling NCR5380_init(instance), + * after setting the appropriate host specific fields and ID. If the + * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance, + * possible) function may be used. + */ + +static int do_abort(struct Scsi_Host *host); +static void do_reset(struct Scsi_Host *host); + +/* + * initialize_SCp - init the scsi pointer field + * @cmd: command block to set up + * + * Set up the internal fields in the SCSI command. + */ + +static __inline__ void initialize_SCp(Scsi_Cmnd * cmd) +{ + /* + * Initialize the Scsi Pointer field so that all of the commands in the + * various queues are valid. + */ + + if (cmd->use_sg) { + cmd->SCp.buffer = (struct scatterlist *) cmd->buffer; + cmd->SCp.buffers_residual = cmd->use_sg - 1; + cmd->SCp.ptr = page_address(cmd->SCp.buffer->page)+ + cmd->SCp.buffer->offset; + cmd->SCp.this_residual = cmd->SCp.buffer->length; + } else { + cmd->SCp.buffer = NULL; + cmd->SCp.buffers_residual = 0; + cmd->SCp.ptr = (char *) cmd->request_buffer; + cmd->SCp.this_residual = cmd->request_bufflen; + } +} + +/** + * NCR5380_poll_politely - wait for NCR5380 status bits + * @instance: controller to poll + * @reg: 5380 register to poll + * @bit: Bitmask to check + * @val: Value required to exit + * + * Polls the NCR5380 in a reasonably efficient manner waiting for + * an event to occur, after a short quick poll we begin giving the + * CPU back in non IRQ contexts + * + * Returns the value of the register or a negative error code. + */ + +static int NCR5380_poll_politely(struct Scsi_Host *instance, int reg, int bit, int val, int t) +{ + NCR5380_local_declare(); + int n = 500; /* At about 8uS a cycle for the cpu access */ + unsigned long end = jiffies + t; + int r; + + NCR5380_setup(instance); + + while( n-- > 0) + { + r = NCR5380_read(reg); + if((r & bit) == val) + return 0; + cpu_relax(); + } + + /* t time yet ? */ + while(time_before(jiffies, end)) + { + r = NCR5380_read(reg); + if((r & bit) == val) + return 0; + if(!in_interrupt()) + yield(); + else + cpu_relax(); + } + return -ETIMEDOUT; +} + +static struct { + unsigned char value; + const char *name; +} phases[] = { + {PHASE_DATAOUT, "DATAOUT"}, + {PHASE_DATAIN, "DATAIN"}, + {PHASE_CMDOUT, "CMDOUT"}, + {PHASE_STATIN, "STATIN"}, + {PHASE_MSGOUT, "MSGOUT"}, + {PHASE_MSGIN, "MSGIN"}, + {PHASE_UNKNOWN, "UNKNOWN"} +}; + +#ifdef NDEBUG +static struct { + unsigned char mask; + const char *name; +} signals[] = { + {SR_DBP, "PARITY"}, + {SR_RST, "RST"}, + {SR_BSY, "BSY"}, + {SR_REQ, "REQ"}, + {SR_MSG, "MSG"}, + {SR_CD, "CD"}, + {SR_IO, "IO"}, + {SR_SEL, "SEL"}, + {0, NULL} +}, +basrs[] = { + {BASR_ATN, "ATN"}, + {BASR_ACK, "ACK"}, + {0, NULL} +}, +icrs[] = { + {ICR_ASSERT_RST, "ASSERT RST"}, + {ICR_ASSERT_ACK, "ASSERT ACK"}, + {ICR_ASSERT_BSY, "ASSERT BSY"}, + {ICR_ASSERT_SEL, "ASSERT SEL"}, + {ICR_ASSERT_ATN, "ASSERT ATN"}, + {ICR_ASSERT_DATA, "ASSERT DATA"}, + {0, NULL} +}, +mrs[] = { + {MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, + {MR_TARGET, "MODE TARGET"}, + {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, + {MR_ENABLE_PAR_INTR, "MODE PARITY INTR"}, + {MR_MONITOR_BSY, "MODE MONITOR BSY"}, + {MR_DMA_MODE, "MODE DMA"}, + {MR_ARBITRATE, "MODE ARBITRATION"}, + {0, NULL} +}; + +/** + * NCR5380_print - print scsi bus signals + * @instance: adapter state to dump + * + * Print the SCSI bus signals for debugging purposes + * + * Locks: caller holds hostdata lock (not essential) + */ + +static void NCR5380_print(struct Scsi_Host *instance) +{ + NCR5380_local_declare(); + unsigned char status, data, basr, mr, icr, i; + NCR5380_setup(instance); + + data = NCR5380_read(CURRENT_SCSI_DATA_REG); + status = NCR5380_read(STATUS_REG); + mr = NCR5380_read(MODE_REG); + icr = NCR5380_read(INITIATOR_COMMAND_REG); + basr = NCR5380_read(BUS_AND_STATUS_REG); + + printk("STATUS_REG: %02x ", status); + for (i = 0; signals[i].mask; ++i) + if (status & signals[i].mask) + printk(",%s", signals[i].name); + printk("\nBASR: %02x ", basr); + for (i = 0; basrs[i].mask; ++i) + if (basr & basrs[i].mask) + printk(",%s", basrs[i].name); + printk("\nICR: %02x ", icr); + for (i = 0; icrs[i].mask; ++i) + if (icr & icrs[i].mask) + printk(",%s", icrs[i].name); + printk("\nMODE: %02x ", mr); + for (i = 0; mrs[i].mask; ++i) + if (mr & mrs[i].mask) + printk(",%s", mrs[i].name); + printk("\n"); +} + + +/* + * NCR5380_print_phase - show SCSI phase + * @instance: adapter to dump + * + * Print the current SCSI phase for debugging purposes + * + * Locks: none + */ + +static void NCR5380_print_phase(struct Scsi_Host *instance) +{ + NCR5380_local_declare(); + unsigned char status; + int i; + NCR5380_setup(instance); + + status = NCR5380_read(STATUS_REG); + if (!(status & SR_REQ)) + printk("scsi%d : REQ not asserted, phase unknown.\n", instance->host_no); + else { + for (i = 0; (phases[i].value != PHASE_UNKNOWN) && (phases[i].value != (status & PHASE_MASK)); ++i); + printk("scsi%d : phase %s\n", instance->host_no, phases[i].name); + } +} +#endif + +/* + * These need tweaking, and would probably work best as per-device + * flags initialized differently for disk, tape, cd, etc devices. + * People with broken devices are free to experiment as to what gives + * the best results for them. + * + * USLEEP_SLEEP should be a minimum seek time. + * + * USLEEP_POLL should be a maximum rotational latency. + */ +#ifndef USLEEP_SLEEP +/* 20 ms (reasonable hard disk speed) */ +#define USLEEP_SLEEP (20*HZ/1000) +#endif +/* 300 RPM (floppy speed) */ +#ifndef USLEEP_POLL +#define USLEEP_POLL (200*HZ/1000) +#endif +#ifndef USLEEP_WAITLONG +/* RvC: (reasonable time to wait on select error) */ +#define USLEEP_WAITLONG USLEEP_SLEEP +#endif + +/* + * Function : int should_disconnect (unsigned char cmd) + * + * Purpose : decide weather a command would normally disconnect or + * not, since if it won't disconnect we should go to sleep. + * + * Input : cmd - opcode of SCSI command + * + * Returns : DISCONNECT_LONG if we should disconnect for a really long + * time (ie always, sleep, look for REQ active, sleep), + * DISCONNECT_TIME_TO_DATA if we would only disconnect for a normal + * time-to-data delay, DISCONNECT_NONE if this command would return + * immediately. + * + * Future sleep algorithms based on time to data can exploit + * something like this so they can differentiate between "normal" + * (ie, read, write, seek) and unusual commands (ie, * format). + * + * Note : We don't deal with commands that handle an immediate disconnect, + * + */ + +static int should_disconnect(unsigned char cmd) +{ + switch (cmd) { + case READ_6: + case WRITE_6: + case SEEK_6: + case READ_10: + case WRITE_10: + case SEEK_10: + return DISCONNECT_TIME_TO_DATA; + case FORMAT_UNIT: + case SEARCH_HIGH: + case SEARCH_LOW: + case SEARCH_EQUAL: + return DISCONNECT_LONG; + default: + return DISCONNECT_NONE; + } +} + +static void NCR5380_set_timer(struct NCR5380_hostdata *hostdata, unsigned long timeout) +{ + hostdata->time_expires = jiffies + timeout; + schedule_delayed_work(&hostdata->coroutine, timeout); +} + + +static int probe_irq __initdata = 0; + +/** + * probe_intr - helper for IRQ autoprobe + * @irq: interrupt number + * @dev_id: unused + * @regs: unused + * + * Set a flag to indicate the IRQ in question was received. This is + * used by the IRQ probe code. + */ + +static irqreturn_t __init probe_intr(int irq, void *dev_id, + struct pt_regs *regs) +{ + probe_irq = irq; + return IRQ_HANDLED; +} + +/** + * NCR5380_probe_irq - find the IRQ of an NCR5380 + * @instance: NCR5380 controller + * @possible: bitmask of ISA IRQ lines + * + * Autoprobe for the IRQ line used by the NCR5380 by triggering an IRQ + * and then looking to see what interrupt actually turned up. + * + * Locks: none, irqs must be enabled on entry + */ + +static int __init NCR5380_probe_irq(struct Scsi_Host *instance, int possible) +{ + NCR5380_local_declare(); + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; + unsigned long timeout; + int trying_irqs, i, mask; + NCR5380_setup(instance); + + for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1) + if ((mask & possible) && (request_irq(i, &probe_intr, SA_INTERRUPT, "NCR-probe", NULL) == 0)) + trying_irqs |= mask; + + timeout = jiffies + (250 * HZ / 1000); + probe_irq = SCSI_IRQ_NONE; + + /* + * A interrupt is triggered whenever BSY = false, SEL = true + * and a bit set in the SELECT_ENABLE_REG is asserted on the + * SCSI bus. + * + * Note that the bus is only driven when the phase control signals + * (I/O, C/D, and MSG) match those in the TCR, so we must reset that + * to zero. + */ + + NCR5380_write(TARGET_COMMAND_REG, 0); + NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); + NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_SEL); + + while (probe_irq == SCSI_IRQ_NONE && time_before(jiffies, timeout)) + { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + + NCR5380_write(SELECT_ENABLE_REG, 0); + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + + for (i = 0, mask = 1; i < 16; ++i, mask <<= 1) + if (trying_irqs & mask) + free_irq(i, NULL); + + return probe_irq; +} + +/** + * NCR58380_print_options - show options + * @instance: unused for now + * + * Called by probe code indicating the NCR5380 driver options that + * were selected. At some point this will switch to runtime options + * read from the adapter in question + * + * Locks: none + */ + +static void __init NCR5380_print_options(struct Scsi_Host *instance) +{ + printk(" generic options" +#ifdef AUTOPROBE_IRQ + " AUTOPROBE_IRQ" +#endif +#ifdef AUTOSENSE + " AUTOSENSE" +#endif +#ifdef DIFFERENTIAL + " DIFFERENTIAL" +#endif +#ifdef REAL_DMA + " REAL DMA" +#endif +#ifdef REAL_DMA_POLL + " REAL DMA POLL" +#endif +#ifdef PARITY + " PARITY" +#endif +#ifdef PSEUDO_DMA + " PSEUDO DMA" +#endif +#ifdef UNSAFE + " UNSAFE " +#endif + ); + printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP); + printk(" generic release=%d", NCR5380_PUBLIC_RELEASE); + if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400) { + printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE); + } +} + +/** + * NCR5380_print_status - dump controller info + * @instance: controller to dump + * + * Print commands in the various queues, called from NCR5380_abort + * and NCR5380_debug to aid debugging. + * + * Locks: called functions disable irqs + */ + +static void NCR5380_print_status(struct Scsi_Host *instance) +{ + NCR5380_dprint(NDEBUG_ANY, instance); + NCR5380_dprint_phase(NDEBUG_ANY, instance); +} + +/******************************************/ +/* + * /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED] + * + * *buffer: I/O buffer + * **start: if inout == FALSE pointer into buffer where user read should start + * offset: current offset + * length: length of buffer + * hostno: Scsi_Host host_no + * inout: TRUE - user is writing; FALSE - user is reading + * + * Return the number of bytes read from or written + */ + +#undef SPRINTF +#define SPRINTF(args...) do { if(pos < buffer + length-80) pos += sprintf(pos, ## args); } while(0) +static +char *lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, char *pos, char *buffer, int length); +static +char *lprint_command(unsigned char *cmd, char *pos, char *buffer, int len); +static +char *lprint_opcode(int opcode, char *pos, char *buffer, int length); + +static +int NCR5380_proc_info(struct Scsi_Host *instance, char *buffer, char **start, off_t offset, int length, int inout) +{ + char *pos = buffer; + struct NCR5380_hostdata *hostdata; + Scsi_Cmnd *ptr; + + hostdata = (struct NCR5380_hostdata *) instance->hostdata; + + if (inout) { /* Has data been written to the file ? */ +#ifdef DTC_PUBLIC_RELEASE + dtc_wmaxi = dtc_maxi = 0; +#endif +#ifdef PAS16_PUBLIC_RELEASE + pas_wmaxi = pas_maxi = 0; +#endif + return (-ENOSYS); /* Currently this is a no-op */ + } + SPRINTF("NCR5380 core release=%d. ", NCR5380_PUBLIC_RELEASE); + if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400) + SPRINTF("ncr53c400 release=%d. ", NCR53C400_PUBLIC_RELEASE); +#ifdef DTC_PUBLIC_RELEASE + SPRINTF("DTC 3180/3280 release %d", DTC_PUBLIC_RELEASE); +#endif +#ifdef T128_PUBLIC_RELEASE + SPRINTF("T128 release %d", T128_PUBLIC_RELEASE); +#endif +#ifdef GENERIC_NCR5380_PUBLIC_RELEASE + SPRINTF("Generic5380 release %d", GENERIC_NCR5380_PUBLIC_RELEASE); +#endif +#ifdef PAS16_PUBLIC_RELEASE + SPRINTF("PAS16 release=%d", PAS16_PUBLIC_RELEASE); +#endif + + SPRINTF("\nBase Addr: 0x%05lX ", (long) instance->base); + SPRINTF("io_port: %04x ", (int) instance->io_port); + if (instance->irq == SCSI_IRQ_NONE) + SPRINTF("IRQ: None.\n"); + else + SPRINTF("IRQ: %d.\n", instance->irq); + +#ifdef DTC_PUBLIC_RELEASE + SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", dtc_wmaxi, dtc_maxi); +#endif +#ifdef PAS16_PUBLIC_RELEASE + SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", pas_wmaxi, pas_maxi); +#endif + spin_lock_irq(instance->host_lock); + if (!hostdata->connected) + SPRINTF("scsi%d: no currently connected command\n", instance->host_no); + else + pos = lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, pos, buffer, length); + SPRINTF("scsi%d: issue_queue\n", instance->host_no); + for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) + pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length); + + SPRINTF("scsi%d: disconnected_queue\n", instance->host_no); + for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) + pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length); + spin_unlock_irq(instance->host_lock); + + *start = buffer; + if (pos - buffer < offset) + return 0; + else if (pos - buffer - offset < length) + return pos - buffer - offset; + return length; +} + +static char *lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, char *pos, char *buffer, int length) +{ + SPRINTF("scsi%d : destination target %d, lun %d\n", cmd->device->host->host_no, cmd->device->id, cmd->device->lun); + SPRINTF(" command = "); + pos = lprint_command(cmd->cmnd, pos, buffer, length); + return (pos); +} + +static char *lprint_command(unsigned char *command, char *pos, char *buffer, int length) +{ + int i, s; + pos = lprint_opcode(command[0], pos, buffer, length); + for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i) + SPRINTF("%02x ", command[i]); + SPRINTF("\n"); + return (pos); +} + +static char *lprint_opcode(int opcode, char *pos, char *buffer, int length) +{ + SPRINTF("%2d (0x%02x)", opcode, opcode); + return (pos); +} + + +/** + * NCR5380_init - initialise an NCR5380 + * @instance: adapter to configure + * @flags: control flags + * + * Initializes *instance and corresponding 5380 chip, + * with flags OR'd into the initial flags value. + * + * Notes : I assume that the host, hostno, and id bits have been + * set correctly. I don't care about the irq and other fields. + * + * Returns 0 for success + * + * Locks: interrupts must be enabled when we are called + */ + +static int __devinit NCR5380_init(struct Scsi_Host *instance, int flags) +{ + NCR5380_local_declare(); + int i, pass; + unsigned long timeout; + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; + + if(in_interrupt()) + printk(KERN_ERR "NCR5380_init called with interrupts off!\n"); + /* + * On NCR53C400 boards, NCR5380 registers are mapped 8 past + * the base address. + */ + +#ifdef NCR53C400 + if (flags & FLAG_NCR53C400) + instance->NCR5380_instance_name += NCR53C400_address_adjust; +#endif + + NCR5380_setup(instance); + + hostdata->aborted = 0; + hostdata->id_mask = 1 << instance->this_id; + for (i = hostdata->id_mask; i <= 0x80; i <<= 1) + if (i > hostdata->id_mask) + hostdata->id_higher_mask |= i; + for (i = 0; i < 8; ++i) + hostdata->busy[i] = 0; +#ifdef REAL_DMA + hostdata->dmalen = 0; +#endif + hostdata->targets_present = 0; + hostdata->connected = NULL; + hostdata->issue_queue = NULL; + hostdata->disconnected_queue = NULL; + + INIT_WORK(&hostdata->coroutine, NCR5380_main, hostdata); + +#ifdef NCR5380_STATS + for (i = 0; i < 8; ++i) { + hostdata->time_read[i] = 0; + hostdata->time_write[i] = 0; + hostdata->bytes_read[i] = 0; + hostdata->bytes_write[i] = 0; + } + hostdata->timebase = 0; + hostdata->pendingw = 0; + hostdata->pendingr = 0; +#endif + + /* The CHECK code seems to break the 53C400. Will check it later maybe */ + if (flags & FLAG_NCR53C400) + hostdata->flags = FLAG_HAS_LAST_BYTE_SENT | flags; + else + hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags; + + hostdata->host = instance; + hostdata->time_expires = 0; + +#ifndef AUTOSENSE + if ((instance->cmd_per_lun > 1) || instance->can_queue > 1) + printk(KERN_WARNING "scsi%d : WARNING : support for multiple outstanding commands enabled\n" " without AUTOSENSE option, contingent allegiance conditions may\n" + " be incorrectly cleared.\n", instance->host_no); +#endif /* def AUTOSENSE */ + + NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); + NCR5380_write(MODE_REG, MR_BASE); + NCR5380_write(TARGET_COMMAND_REG, 0); + NCR5380_write(SELECT_ENABLE_REG, 0); + +#ifdef NCR53C400 + if (hostdata->flags & FLAG_NCR53C400) { + NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE); + } +#endif + + /* + * Detect and correct bus wedge problems. + * + * If the system crashed, it may have crashed in a state + * where a SCSI command was still executing, and the + * SCSI bus is not in a BUS FREE STATE. + * + * If this is the case, we'll try to abort the currently + * established nexus which we know nothing about, and that + * failing, do a hard reset of the SCSI bus + */ + + for (pass = 1; (NCR5380_read(STATUS_REG) & SR_BSY) && pass <= 6; ++pass) { + switch (pass) { + case 1: + case 3: + case 5: + printk(KERN_INFO "scsi%d: SCSI bus busy, waiting up to five seconds\n", instance->host_no); + timeout = jiffies + 5 * HZ; + NCR5380_poll_politely(instance, STATUS_REG, SR_BSY, 0, 5*HZ); + break; + case 2: + printk(KERN_WARNING "scsi%d: bus busy, attempting abort\n", instance->host_no); + do_abort(instance); + break; + case 4: + printk(KERN_WARNING "scsi%d: bus busy, attempting reset\n", instance->host_no); + do_reset(instance); + break; + case 6: + printk(KERN_ERR "scsi%d: bus locked solid or invalid override\n", instance->host_no); + return -ENXIO; + } + } + return 0; +} + +/** + * NCR5380_exit - remove an NCR5380 + * @instance: adapter to remove + */ + +static void __devexit NCR5380_exit(struct Scsi_Host *instance) +{ + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; + + cancel_delayed_work(&hostdata->coroutine); + flush_scheduled_work(); +} + +/** + * NCR5380_queue_command - queue a command + * @cmd: SCSI command + * @done: completion handler + * + * cmd is added to the per instance issue_queue, with minor + * twiddling done to the host specific fields of cmd. If the + * main coroutine is not running, it is restarted. + * + * Locks: host lock taken by caller + */ + +static int NCR5380_queue_command(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) +{ + struct Scsi_Host *instance = cmd->device->host; + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; + Scsi_Cmnd *tmp; + +#if (NDEBUG & NDEBUG_NO_WRITE) + switch (cmd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n", instance->host_no); + cmd->result = (DID_ERROR << 16); + done(cmd); + return 0; + } +#endif /* (NDEBUG & NDEBUG_NO_WRITE) */ + +#ifdef NCR5380_STATS + switch (cmd->cmnd[0]) { + case WRITE: + case WRITE_6: + case WRITE_10: + hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase); + hostdata->bytes_write[cmd->device->id] += cmd->request_bufflen; + hostdata->pendingw++; + break; + case READ: + case READ_6: + case READ_10: + hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase); + hostdata->bytes_read[cmd->device->id] += cmd->request_bufflen; + hostdata->pendingr++; + break; + } +#endif + + /* + * We use the host_scribble field as a pointer to the next command + * in a queue + */ + + cmd->host_scribble = NULL; + cmd->scsi_done = done; + cmd->result = 0; + + /* + * Insert the cmd into the issue queue. Note that REQUEST SENSE + * commands are added to the head of the queue since any command will + * clear the contingent allegiance condition that exists and the + * sense data is only guaranteed to be valid while the condition exists. + */ + + if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { + LIST(cmd, hostdata->issue_queue); + cmd->host_scribble = (unsigned char *) hostdata->issue_queue; + hostdata->issue_queue = cmd; + } else { + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble); + LIST(cmd, tmp); + tmp->host_scribble = (unsigned char *) cmd; + } + dprintk(NDEBUG_QUEUES, ("scsi%d : command added to %s of queue\n", instance->host_no, (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail")); + + /* Run the coroutine if it isn't already running. */ + /* Kick off command processing */ + schedule_work(&hostdata->coroutine); + return 0; +} + + +/** + * NCR5380_main - NCR state machines + * + * NCR5380_main is a coroutine that runs as long as more work can + * be done on the NCR5380 host adapters in a system. Both + * NCR5380_queue_command() and NCR5380_intr() will try to start it + * in case it is not running. + * + * Locks: called as its own thread with no locks held. Takes the + * host lock and called routines may take the isa dma lock. + */ + +static void NCR5380_main(void *p) +{ + struct NCR5380_hostdata *hostdata = p; + struct Scsi_Host *instance = hostdata->host; + Scsi_Cmnd *tmp, *prev; + int done; + + spin_lock_irq(instance->host_lock); + do { + /* Lock held here */ + done = 1; + if (!hostdata->connected && !hostdata->selecting) { + dprintk(NDEBUG_MAIN, ("scsi%d : not connected\n", instance->host_no)); + /* + * Search through the issue_queue for a command destined + * for a target that's not busy. + */ + for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) + { + if (prev != tmp) + dprintk(NDEBUG_LISTS, ("MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->target, hostdata->busy[tmp->target], tmp->lun)); + /* When we find one, remove it from the issue queue. */ + if (!(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun))) { + if (prev) { + REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble); + prev->host_scribble = tmp->host_scribble; + } else { + REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble); + hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble; + } + tmp->host_scribble = NULL; + + /* + * Attempt to establish an I_T_L nexus here. + * On success, instance->hostdata->connected is set. + * On failure, we must add the command back to the + * issue queue so we can keep trying. + */ + dprintk(NDEBUG_MAIN|NDEBUG_QUEUES, ("scsi%d : main() : command for target %d lun %d removed from issue_queue\n", instance->host_no, tmp->target, tmp->lun)); + + /* + * A successful selection is defined as one that + * leaves us with the command connected and + * in hostdata->connected, OR has terminated the + * command. + * + * With successful commands, we fall through + * and see |