/* * 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@lxorguk.ukuu.org.uk) * 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. */ #include #include #ifndef NDEBUG #define NDEBUG 0 #endif #ifndef NDEBUG_ABORT #define NDEBUG_ABORT 0 #endif #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 (scsi_bufflen(cmd)) { cmd->SCp.buffer = scsi_sglist(cmd); cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1; cmd->SCp.ptr = sg_virt(cmd->SCp.buffer); cmd->SCp.this_residual = cmd->SCp.buffer->length; } else { cmd->SCp.buffer = NULL; cmd->SCp.buffers_residual = 0; cmd->SCp.ptr = NULL; cmd->SCp.this_residual = 0; } } /** * 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()) cond_resched(); else cpu_relax(); } return -ETIMEDOUT; } static struct { unsigned char value; const char *name; } phases[] __maybe_unused = { {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"}, {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"}, {PHASE_UNKNOWN, "UNKNOWN"} }; #if 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 whether 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) { 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 __maybe_unused 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, IRQF_DISABLED, "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)) schedule_timeout_uninterruptible(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 __maybe_unused 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 __maybe_unused 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_DELAYED_WORK(&hostdata->coroutine, NCR5380_main); #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 void patch_branch(unsigned int *addr, unsigned long target, int flags) { patch_instruction(addr, create_branch(addr, target, flags)); } unsigned int create_branch(const unsigned int *addr, unsigned long target, int flags) { unsigned int instruction; long offset; offset = target; if (! (flags & BRANCH_ABSOLUTE)) offset = offset - (unsigned long)addr; /* Check we can represent the target in the instruction format */ if (offset < -0x2000000 || offset > 0x1fffffc || offset & 0x3) return 0; /* Mask out the flags and target, so they don't step on each other. */ instruction = 0x48000000 | (flags & 0x3) | (offset & 0x03FFFFFC); return instruction; } unsigned int create_cond_branch(const unsigned int *addr, unsigned long target, int flags) { unsigned int instruction; long offset; offset = target; if (! (flags & BRANCH_ABSOLUTE)) offset = offset - (unsigned long)addr; /* Check we can represent the target in the instruction format */ if (offset < -0x8000 || offset > 0x7FFF || offset & 0x3) return 0; /* Mask out the flags and target, so they don't step on each other. */ instruction = 0x40000000 | (flags & 0x3FF0003) | (offset & 0xFFFC); return instruction; } static unsigned int branch_opcode(unsigned int instr) { return (instr >> 26) & 0x3F; } static int instr_is_branch_iform(unsigned int instr) { return branch_opcode(instr) == 18; } static int instr_is_branch_bform(unsigned int instr) { return branch_opcode(instr) == 16; } int instr_is_relative_branch(unsigned int instr) { if (instr & BRANCH_ABSOLUTE) return 0; return instr_is_branch_iform(instr) || instr_is_branch_bform(instr); } static unsigned long branch_iform_target(const unsigned int *instr) { signed long imm; imm = *instr & 0x3FFFFFC; /* If the top bit of the immediate value is set this is negative */ if (imm & 0x2000000) imm -= 0x4000000; if ((*instr & BRANCH_ABSOLUTE) == 0) imm += (unsigned long)instr; return (unsigned long)imm; } static unsigned long branch_bform_target(const unsigned int *instr) { signed long imm; imm = *instr & 0xFFFC; /* If the top bit of the immediate value is set this is negative */ if (imm & 0x8000) imm -= 0x10000; if ((*instr & BRANCH_ABSOLUTE) == 0) imm += (unsigned long)instr; return (unsigned long)imm; } unsigned long branch_target(const unsigned int *instr) { if (instr_is_branch_iform(*instr)) return branch_iform_target(instr); else if (instr_is_branch_bform(*instr)) return branch_bform_target(instr); return 0; } int instr_is_branch_to_addr(const unsigned int *instr, unsigned long addr) { if (instr_is_branch_iform(*instr) || instr_is_branch_bform(*instr)) return branch_target(instr) == addr; return 0; } unsigned int translate_branch(const unsigned int *dest, const unsigned int *src) { unsigned long target; target = branch_target(src); if (instr_is_branch_iform(*src)) return create_branch(dest, target, *src); else if (instr_is_branch_bform(*src)) return create_cond_branch(dest, target, *src); return 0; } #ifdef CONFIG_CODE_PATCHING_SELFTEST static void __init test_trampoline(void) { asm ("nop;\n"); } #define check(x) \ if (!(x)) printk("code-patching: test failed at line %d\n", __LINE__); static void __init test_branch_iform(void) { unsigned int instr; unsigned long addr; addr = (unsigned long)&instr; /* The simplest case, branch to self, no flags */ check(instr_is_branch_iform(0x48000000)); /* All bits of target set, and flags */ check(instr_is_branch_iform(0x4bffffff)); /* High bit of opcode set, which is wrong */ check(!instr_is_branch_iform(0xcbffffff)); /* Middle bits of opcode set, which is wrong */ check(!instr_is_branch_iform(0x7bffffff)); /* Simplest case, branch to self with link */ check(instr_is_branch_iform(0x48000001)); /* All bits of targets set */ check(instr_is_branch_iform(0x4bfffffd)); /* Some bits of targets set */ check(instr_is_branch_iform(0x4bff00fd)); /* Must be a valid branch to start with */ check(!instr_is_branch_iform(0x7bfffffd)); /* Absolute branch to 0x100 */ instr = 0x48000103; check(instr_is_branch_to_addr(&instr, 0x100)); /* Absolute branch to 0x420fc */ instr = 0x480420ff; check(instr_is_branch_to_addr(&instr, 0x420fc)); /* Maximum positive relative branch, + 20MB - 4B */ instr = 0x49fffffc; check(instr_is_branch_to_addr(&instr, addr + 0x1FFFFFC)); /* Smallest negative relative branch, - 4B */ instr = 0x4bfffffc; check(instr_is_branch_to_addr(&instr, addr - 4)); /* Largest negative relative branch, - 32 MB */ instr = 0x4a000000; check(instr_is_branch_to_addr(&instr, addr - 0x2000000)); /* Branch to self, with link */ instr = create_branch(&instr, addr, BRANCH_SET_LINK); check(instr_is_branch_to_addr(&instr, addr)); /* Branch to self - 0x100, with link */ instr = create_branch(&instr, addr - 0x100, BRANCH_SET_LINK); check(instr_is_branch_to_addr(&instr, addr - 0x100)); /* Branch to self + 0x100, no link */ instr = create_branch(&instr, addr + 0x100, 0); check(instr_is_branch_to_addr(&instr, addr + 0x100)); /* Maximum relative negative offset, - 32 MB */ instr = create_branch(&instr, addr - 0x2000000, BRANCH_SET_LINK); check(instr_is_branch_to_addr(&instr, addr - 0x2000000)); /* Out of range relative negative offset, - 32 MB + 4*/ instr = create_branch(&instr, addr - 0x2000004, BRANCH_SET_LINK); check(instr == 0); /* Out of range relative positive offset, + 32 MB */ instr = create_branch(&instr, addr + 0x2000000, BRANCH_SET_LINK); check(instr == 0); /* Unaligned target */ instr = create_branch(&instr, addr + 3, BRANCH_SET_LINK); check(instr == 0); /* Check flags are masked correctly */ instr = create_branch(&instr, addr, 0xFFFFFFFC); check(instr_is_branch_to_addr(&instr, addr)); check(instr == 0x48000000); } static void __init test_create_function_call(void) { unsigned int *iptr; unsigned long dest; /* Check we can create a function call */ iptr = (unsigned int *)ppc_function_entry(test_trampoline); dest = ppc_function_entry(test_create_function_call); patch_instruction(iptr, create_branch(iptr, dest, BRANCH_SET_LINK)); check(instr_is_branch_to_addr(iptr, dest)); } static void __init test_branch_bform(void) { unsigned long addr; unsigned int *iptr, instr, flags; iptr = &instr; addr = (unsigned long)iptr; /* The simplest case, branch to self, no flags */ check(instr_is_branch_bform(0x40000000)); /* All bits of target set, and flags */ check(instr_is_branch_bform(0x43ffffff)); /* High bit of opcode set, which is wrong */ check(!instr_is_branch_bform(0xc3ffffff)); /* Middle bits of opcode set, which is wrong */ check(!instr_is_branch_bform(0x7bffffff)); /* Absolute conditional branch to 0x100 */ instr = 0x43ff0103; check(instr_is_branch_to_addr(&instr, 0x100)); /* Absolute conditional branch to 0x20fc */ instr = 0x43ff20ff; check(instr_is_branch_to_addr(&instr, 0x20fc)); /* Maximum positive relative conditional branch, + 32 KB - 4B */ instr = 0x43ff7ffc; check(instr_is_branch_to_addr(&instr, addr + 0x7FFC)); /* Smallest negative relative conditional branch, - 4B */ instr = 0x43fffffc; check(instr_is_branch_to_addr(&instr, addr - 4)); /* Largest negative relative conditional branch, - 32 KB */ instr = 0x43ff8000; check(instr_is_branch_to_addr(&instr, addr - 0x8000)); /* All condition code bits set & link */ flags = 0x3ff000 | BRANCH_SET_LINK; /* Branch to self */ instr = create_cond_branch(iptr, addr, flags); check(instr_is_branch_to_addr(&instr, addr)); /* Branch to self - 0x100 */ instr = create_cond_branch(iptr, addr - 0x100, flags); check(instr_is_branch_to_addr(&instr, addr - 0x100)); /* Branch to self + 0x100 */ instr = create_cond_branch(iptr, addr + 0x100, flags); check(instr_is_branch_to_addr(&instr, addr + 0x100)); /* Maximum relative negative offset, - 32 KB */ instr = create_cond_branch(iptr, addr - 0x8000, flags); check(instr_is_branch_to_addr(&instr, addr - 0x8000)); /* Out of range relative negative offset, - 32 KB + 4*/ instr = create_cond_branch(iptr, addr - 0x8004, flags); check(instr == 0); /* Out of range relative positive offset, + 32 KB */ instr = create_cond_branch(iptr, addr + 0x8000, flags); check(instr == 0); /* Unaligned target */ instr = create_cond_branch(iptr, addr + 3, flags); check(instr == 0); /* Check flags are masked correctly */ instr = create_cond_branch(iptr, addr, 0xFFFFFFFC); check(instr_is_branch_to_addr(&instr, addr)); check(instr == 0x43FF0000); } static void __init test_translate_branch(void) { unsigned long addr; unsigned int *p, *q; void *buf; buf = vmalloc(PAGE_ALIGN(0x2000000 + 1)); check(buf); if (!buf) return; /* Simple case, branch to self moved a little */ p = buf; addr = (unsigned long)p; patch_branch(p, addr, 0); check(instr_is_branch_to_addr(p, addr)); q = p + 1; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(q, addr)); /* Maximum negative case, move b . to addr + 32 MB */ p = buf; addr = (unsigned long)p; patch_branch(p, addr, 0); q = buf + 0x2000000; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(p, addr)); check(instr_is_branch_to_addr(q, addr)); check(*q == 0x4a000000); /* Maximum positive case, move x to x - 32 MB + 4 */ p = buf + 0x2000000; addr = (unsigned long)p; patch_branch(p, addr, 0); q = buf + 4; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(p, addr)); check(instr_is_branch_to_addr(q, addr)); check(*q == 0x49fffffc); /* Jump to x + 16 MB moved to x + 20 MB */ p = buf; addr = 0x1000000 + (unsigned long)buf; patch_branch(p, addr, BRANCH_SET_LINK); q = buf + 0x1400000; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(p, addr)); check(instr_is_branch_to_addr(q, addr)); /* Jump to x + 16 MB moved to x - 16 MB + 4 */ p = buf + 0x1000000; addr = 0x2000000 + (unsigned long)buf; patch_branch(p, addr, 0); q = buf + 4; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(p, addr)); check(instr_is_branch_to_addr(q, addr)); /* Conditional branch tests */ /* Simple case, branch to self moved a little */ p = buf; addr = (unsigned long)p; patch_instruction(p, create_cond_branch(p, addr, 0)); check(instr_is_branch_to_addr(p, addr)); q = p + 1; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(q, addr)); /* Maximum negative case, move b . to addr + 32 KB */ p = buf; addr = (unsigned long)p; patch_instruction(p, create_cond_branch(p, addr, 0xFFFFFFFC)); q = buf + 0x8000; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(p, addr)); check(instr_is_branch_to_addr(q, addr)); check(*q == 0x43ff8000); /* Maximum positive case, move x to x - 32 KB + 4 */ p = buf + 0x8000; addr = (unsigned long)p; patch_instruction(p, create_cond_branch(p, addr, 0xFFFFFFFC)); q = buf + 4; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(p, addr)); check(instr_is_branch_to_addr(q, addr)); check(*q == 0x43ff7ffc); /* Jump to x + 12 KB moved to x + 20 KB */ p = buf; addr = 0x3000 + (unsigned long)buf; patch_instruction(p, create_cond_branch(p, addr, BRANCH_SET_LINK)); q = buf + 0x5000; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(p, addr)); check(instr_is_branch_to_addr(q, addr)); /* Jump to x + 8 KB moved to x - 8 KB + 4 */ p = buf + 0x2000; addr = 0x4000 + (unsigned long)buf; patch_instruction(p, create_cond_branch(p, addr, 0)); q = buf + 4; patch_instruction(q, translate_branch(q, p)); check(instr_is_branch_to_addr(p, addr)); check(instr_is_branch_to_addr(q, addr)); /* Free the buffer we were using */ vfree(buf); } static int __init test_code_patching(void) { printk(KERN_DEBUG "Running code patching self-tests ...\n"); test_branch_iform(); test_branch_bform(); test_create_function_call(); test_translate_branch(); return 0; } late_initcall(test_code_patching); #endif /* CONFIG_CODE_PATCHING_SELFTEST */ yte by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); dprintk(NDEBUG_EXTENDED, ("scsi%d : receiving extended message\n", instance->host_no)); len = 2; data = extended_msg + 1; phase = PHASE_MSGIN; NCR5380_transfer_pio(instance, &phase, &len, &data); dprintk(NDEBUG_EXTENDED, ("scsi%d : length=%d, code=0x%02x\n", instance->host_no, (int) extended_msg[1], (int) extended_msg[2])); if (!len && extended_msg[1] <= (sizeof(extended_msg) - 1)) { /* Accept third byte by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); len = extended_msg[1] - 1; data = extended_msg + 3; phase = PHASE_MSGIN; NCR5380_transfer_pio(instance, &phase, &len, &data); dprintk(NDEBUG_EXTENDED, ("scsi%d : message received, residual %d\n", instance->host_no, len)); switch (extended_msg[2]) { case EXTENDED_SDTR: case EXTENDED_WDTR: case EXTENDED_MODIFY_DATA_POINTER: case EXTENDED_EXTENDED_IDENTIFY: tmp = 0; } } else if (len) { printk("scsi%d: error receiving extended message\n", instance->host_no); tmp = 0; } else { printk("scsi%d: extended message code %02x length %d is too long\n", instance->host_no, extended_msg[2], extended_msg[1]); tmp = 0; } /* Fall through to reject message */ /* * If we get something weird that we aren't expecting, * reject it. */ default: if (!tmp) { printk("scsi%d: rejecting message ", instance->host_no); spi_print_msg(extended_msg); printk("\n"); } else if (tmp != EXTENDED_MESSAGE) scmd_printk(KERN_INFO, cmd, "rejecting unknown message %02x\n",tmp); else scmd_printk(KERN_INFO, cmd, "rejecting unknown extended message code %02x, length %d\n", extended_msg[1], extended_msg[0]); msgout = MESSAGE_REJECT; NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN); break; } /* switch (tmp) */ break; case PHASE_MSGOUT: len = 1; data = &msgout; hostdata->last_message = msgout; NCR5380_transfer_pio(instance, &phase, &len, &data); if (msgout == ABORT) { hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun); hostdata->connected = NULL; cmd->result = DID_ERROR << 16; collect_stats(hostdata, cmd); cmd->scsi_done(cmd); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); return; } msgout = NOP; break; case PHASE_CMDOUT: len = cmd->cmd_len; data = cmd->cmnd; /* * XXX for performance reasons, on machines with a * PSEUDO-DMA architecture we should probably * use the dma transfer function. */ NCR5380_transfer_pio(instance, &phase, &len, &data); if (!cmd->device->disconnect && should_disconnect(cmd->cmnd[0])) { NCR5380_set_timer(hostdata, USLEEP_SLEEP); dprintk(NDEBUG_USLEEP, ("scsi%d : issued command, sleeping until %ul\n", instance->host_no, hostdata->time_expires)); return; } break; case PHASE_STATIN: len = 1; data = &tmp; NCR5380_transfer_pio(instance, &phase, &len, &data); cmd->SCp.Status = tmp; break; default: printk("scsi%d : unknown phase\n", instance->host_no); NCR5380_dprint(NDEBUG_ALL, instance); } /* switch(phase) */ } /* if (tmp * SR_REQ) */ else { /* RvC: go to sleep if polling time expired */ if (!cmd->device->disconnect && time_after_eq(jiffies, poll_time)) { NCR5380_set_timer(hostdata, USLEEP_SLEEP); dprintk(NDEBUG_USLEEP, ("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no, hostdata->time_expires)); return; } } } /* while (1) */ } /* * Function : void NCR5380_reselect (struct Scsi_Host *instance) * * Purpose : does reselection, initializing the instance->connected * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q * nexus has been reestablished, * * Inputs : instance - this instance of the NCR5380. * * Locks: io_request_lock held by caller if IRQ driven */ static void NCR5380_reselect(struct Scsi_Host *instance) { NCR5380_local_declare(); struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; unsigned char target_mask; unsigned char lun, phase; int len; unsigned char msg[3]; unsigned char *data; Scsi_Cmnd *tmp = NULL, *prev; int abort = 0; NCR5380_setup(instance); /* * Disable arbitration, etc. since the host adapter obviously * lost, and tell an interrupted NCR5380_select() to restart. */ NCR5380_write(MODE_REG, MR_BASE); hostdata->restart_select = 1; target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask); dprintk(NDEBUG_SELECTION, ("scsi%d : reselect\n", instance->host_no)); /* * At this point, we have detected that our SCSI ID is on the bus, * SEL is true and BSY was false for at least one bus settle delay * (400 ns). * * We must assert BSY ourselves, until the target drops the SEL * signal. */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY); /* FIXME: timeout too long, must fail to workqueue */ if(NCR5380_poll_politely(instance, STATUS_REG, SR_SEL, 0, 2*HZ)<0) abort = 1; NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); /* * Wait for target to go into MSGIN. * FIXME: timeout needed and fail to work queeu */ if(NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, 2*HZ)) abort = 1; len = 1; data = msg; phase = PHASE_MSGIN; NCR5380_transfer_pio(instance, &phase, &len, &data); if (!(msg[0] & 0x80)) { printk(KERN_ERR "scsi%d : expecting IDENTIFY message, got ", instance->host_no); spi_print_msg(msg); abort = 1; } else { /* Accept message by clearing ACK */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); lun = (msg[0] & 0x07); /* * We need to add code for SCSI-II to track which devices have * I_T_L_Q nexuses established, and which have simple I_T_L * nexuses so we can chose to do additional data transfer. */ /* * Find the command corresponding to the I_T_L or I_T_L_Q nexus we * just reestablished, and remove it from the disconnected queue. */ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble) if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun) ) { if (prev) { REMOVE(prev, prev->host_scribble, tmp, tmp->host_scribble); prev->host_scribble = tmp->host_scribble; } else { REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble); hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble; } tmp->host_scribble = NULL; break; } if (!tmp) { printk(KERN_ERR "scsi%d : warning : target bitmask %02x lun %d not in disconnect_queue.\n", instance->host_no, target_mask, lun); /* * Since we have an established nexus that we can't do anything with, * we must abort it. */ abort = 1; } } if (abort) { do_abort(instance); } else { hostdata->connected = tmp; dprintk(NDEBUG_RESELECTION, ("scsi%d : nexus established, target = %d, lun = %d, tag = %d\n", instance->host_no, tmp->target, tmp->lun, tmp->tag)); } } /* * Function : void NCR5380_dma_complete (struct Scsi_Host *instance) * * Purpose : called by interrupt handler when DMA finishes or a phase * mismatch occurs (which would finish the DMA transfer). * * Inputs : instance - this instance of the NCR5380. * * Returns : pointer to the Scsi_Cmnd structure for which the I_T_L * nexus has been reestablished, on failure NULL is returned. */ #ifdef REAL_DMA static void NCR5380_dma_complete(NCR5380_instance * instance) { NCR5380_local_declare(); struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; int transferred; NCR5380_setup(instance); /* * XXX this might not be right. * * Wait for final byte to transfer, ie wait for ACK to go false. * * We should use the Last Byte Sent bit, unfortunately this is * not available on the 5380/5381 (only the various CMOS chips) * * FIXME: timeout, and need to handle long timeout/irq case */ NCR5380_poll_politely(instance, BUS_AND_STATUS_REG, BASR_ACK, 0, 5*HZ); NCR5380_write(MODE_REG, MR_BASE); NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); /* * The only places we should see a phase mismatch and have to send * data from the same set of pointers will be the data transfer * phases. So, residual, requested length are only important here. */ if (!(hostdata->connected->SCp.phase & SR_CD)) { transferred = instance->dmalen - NCR5380_dma_residual(); hostdata->connected->SCp.this_residual -= transferred; hostdata->connected->SCp.ptr += transferred; } } #endif /* def REAL_DMA */ /* * Function : int NCR5380_abort (Scsi_Cmnd *cmd) * * Purpose : abort a command * * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the * host byte of the result field to, if zero DID_ABORTED is * used. * * Returns : 0 - success, -1 on failure. * * XXX - there is no way to abort the command that is currently * connected, you have to wait for it to complete. If this is * a problem, we could implement longjmp() / setjmp(), setjmp() * called where the loop started in NCR5380_main(). * * Locks: host lock taken by caller */ static int NCR5380_abort(Scsi_Cmnd * cmd) { NCR5380_local_declare(); struct Scsi_Host *instance = cmd->device->host; struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; Scsi_Cmnd *tmp, **prev; printk(KERN_WARNING "scsi%d : aborting command\n", instance->host_no); scsi_print_command(cmd); NCR5380_print_status(instance); NCR5380_setup(instance); dprintk(NDEBUG_ABORT, ("scsi%d : abort called\n", instance->host_no)); dprintk(NDEBUG_ABORT, (" basr 0x%X, sr 0x%X\n", NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG))); #if 0 /* * Case 1 : If the command is the currently executing command, * we'll set the aborted flag and return control so that * information transfer routine can exit cleanly. */ if (hostdata->connected == cmd) { dprintk(NDEBUG_ABORT, ("scsi%d : aborting connected command\n", instance->host_no)); hostdata->aborted = 1; /* * We should perform BSY checking, and make sure we haven't slipped * into BUS FREE. */ NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); /* * Since we can't change phases until we've completed the current * handshake, we have to source or sink a byte of data if the current * phase is not MSGOUT. */ /* * Return control to the executing NCR drive so we can clear the * aborted flag and get back into our main loop. */ return 0; } #endif /* * Case 2 : If the command hasn't been issued yet, we simply remove it * from the issue queue. */ dprintk(NDEBUG_ABORT, ("scsi%d : abort going into loop.\n", instance->host_no)); for (prev = (Scsi_Cmnd **) & (hostdata->issue_queue), tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble) if (cmd == tmp) { REMOVE(5, *prev, tmp, tmp->host_scribble); (*prev) = (Scsi_Cmnd *) tmp->host_scribble; tmp->host_scribble = NULL; tmp->result = DID_ABORT << 16; dprintk(NDEBUG_ABORT, ("scsi%d : abort removed command from issue queue.\n", instance->host_no)); tmp->scsi_done(tmp); return SUCCESS; } #if (NDEBUG & NDEBUG_ABORT) /* KLL */ else if (prev == tmp) printk(KERN_ERR "scsi%d : LOOP\n", instance->host_no); #endif /* * Case 3 : If any commands are connected, we're going to fail the abort * and let the high level SCSI driver retry at a later time or * issue a reset. * * Timeouts, and therefore aborted commands, will be highly unlikely * and handling them cleanly in this situation would make the common * case of noresets less efficient, and would pollute our code. So, * we fail. */ if (hostdata->connected) { dprintk(NDEBUG_ABORT, ("scsi%d : abort failed, command connected.\n", instance->host_no)); return FAILED; } /* * Case 4: If the command is currently disconnected from the bus, and * there are no connected commands, we reconnect the I_T_L or * I_T_L_Q nexus associated with it, go into message out, and send * an abort message. * * This case is especially ugly. In order to reestablish the nexus, we * need to call NCR5380_select(). The easiest way to implement this * function was to abort if the bus was busy, and let the interrupt * handler triggered on the SEL for reselect take care of lost arbitrations * where necessary, meaning interrupts need to be enabled. * * When interrupts are enabled, the queues may change - so we * can't remove it from the disconnected queue before selecting it * because that could cause a failure in hashing the nexus if that * device reselected. * * Since the queues may change, we can't use the pointers from when we * first locate it. * * So, we must first locate the command, and if NCR5380_select() * succeeds, then issue the abort, relocate the command and remove * it from the disconnected queue. */ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble) if (cmd == tmp) { dprintk(NDEBUG_ABORT, ("scsi%d : aborting disconnected command.\n", instance->host_no)); if (NCR5380_select(instance, cmd, (int) cmd->tag)) return FAILED; dprintk(NDEBUG_ABORT, ("scsi%d : nexus reestablished.\n", instance->host_no)); do_abort(instance); for (prev = (Scsi_Cmnd **) & (hostdata->disconnected_queue), tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble) if (cmd == tmp) { REMOVE(5, *prev, tmp, tmp->host_scribble); *prev = (Scsi_Cmnd *) tmp->host_scribble; tmp->host_scribble = NULL; tmp->result = DID_ABORT << 16; tmp->scsi_done(tmp); return SUCCESS; } } /* * Case 5 : If we reached this point, the command was not found in any of * the queues. * * We probably reached this point because of an unlikely race condition * between the command completing successfully and the abortion code, * so we won't panic, but we will notify the user in case something really * broke. */ printk(KERN_WARNING "scsi%d : warning : SCSI command probably completed successfully\n" " before abortion\n", instance->host_no); return FAILED; } /* * Function : int NCR5380_bus_reset (Scsi_Cmnd *cmd) * * Purpose : reset the SCSI bus. * * Returns : SUCCESS * * Locks: host lock taken by caller */ static int NCR5380_bus_reset(Scsi_Cmnd * cmd) { struct Scsi_Host *instance = cmd->device->host; NCR5380_local_declare(); NCR5380_setup(instance); NCR5380_print_status(instance); spin_lock_irq(instance->host_lock); do_reset(instance); spin_unlock_irq(instance->host_lock); return SUCCESS; }