ncr5380.c

来自「linux 内核源代码」· C语言 代码 · 共 2,036 行 · 第 1/5 页

C
2,036
字号
/*  * 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. */#include <scsi/scsi_dbg.h>#include <scsi/scsi_transport_spi.h>#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 (cmd->use_sg) {		cmd->SCp.buffer = (struct scatterlist *) cmd->request_buffer;		cmd->SCp.buffers_residual = cmd->use_sg - 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 = (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())			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 NDEBUGstatic 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}};

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?