Skip to content
atari_NCR5380.c 93.7 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
/* 
 * 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
 */

/*
 * ++roman: To port the 5380 driver to the Atari, I had to do some changes in
 * this file, too:
 *
 *  - Some of the debug statements were incorrect (undefined variables and the
 *    like). I fixed that.
 *
 *  - In information_transfer(), I think a #ifdef was wrong. Looking at the
 *    possible DMA transfer size should also happen for REAL_DMA. I added this
 *    in the #if statement.
 *
 *  - When using real DMA, information_transfer() should return in a DATAOUT
 *    phase after starting the DMA. It has nothing more to do.
 *
 *  - The interrupt service routine should run main after end of DMA, too (not
 *    only after RESELECTION interrupts). Additionally, it should _not_ test
 *    for more interrupts after running main, since a DMA process may have
 *    been started and interrupts are turned on now. The new int could happen
 *    inside the execution of NCR5380_intr(), leading to recursive
 *    calls.
 *
 *  - I've added a function merge_contiguous_buffers() that tries to
 *    merge scatter-gather buffers that are located at contiguous
 *    physical addresses and can be processed with the same DMA setup.
 *    Since most scatter-gather operations work on a page (4K) of
 *    4 buffers (1K), in more than 90% of all cases three interrupts and
 *    DMA setup actions are saved.
 *
 * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
 *    and USLEEP, because these were messing up readability and will never be
 *    needed for Atari SCSI.
 * 
 * - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
 *   stuff), and 'main' is executed in a bottom half if awoken by an
 *   interrupt.
 *
 * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
 *   constructs. In my eyes, this made the source rather unreadable, so I
 *   finally replaced that by the *_PRINTK() macros.
 *
 */

/*
 * Further development / testing that should be done : 
 * 1.  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>
Linus Torvalds's avatar
Linus Torvalds committed

#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
#endif

/*
 * Design
 * Issues :
 *
 * The other Linux SCSI drivers were written when Linux was Intel PC-only,
 * and specifically for each board rather than each chip.  This makes their
 * adaptation to platforms like the Mac (Some of which use NCR5380's)
 * more difficult than it has to be.
 *
 * Also, many of the SCSI drivers were written before the command queuing
 * routines were implemented, meaning their implementations of queued 
 * commands were hacked on rather than designed in from the start.
 *
 * When I designed the Linux SCSI drivers I figured that 
 * while having two different SCSI boards in a system might be useful
 * for debugging things, two of the same type wouldn't be used.
 * Well, I was wrong and a number of users have mailed me about running
 * multiple high-performance SCSI boards in a server.
 *
 * Finally, when I get questions from users, I have no idea what 
 * revision of my driver they are running.
 *
 * This driver attempts to address these problems :
 * 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.
 *
 * 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.
 *
 * To solve the multiple-boards-in-the-same-system problem, 
 * there is a separate instance structure for each instance
 * of a 5380 in the system.  So, multiple NCR5380 drivers will
 * be able to coexist with appropriate changes to the high level
 * SCSI code.  
 *
 * A NCR5380_PUBLIC_REVISION macro is provided, with the release
 * number (updated for each public release) printed by the 
 * NCR5380_print_options command, which should be called from the 
 * wrapper detect function, so that I know what release of the driver
 * users are using.
 *
 * 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 when not running by the interrupt handler,
 * timer, and queue command function.  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 USLEEP
 * was defined, and 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 : 
 * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
 *	for commands that return with a CHECK CONDITION status. 
 *
 * LINKED - if defined, linked commands are supported.
 *
 * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
 *
 * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible
 *
 * These macros MUST be defined :
 * 
 * NCR5380_read(register)  - read from the specified register
 *
 * NCR5380_write(register, value) - write to the specific register 
 *
 * 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);
 *
 * If nothing specific to this implementation needs doing (ie, with external
 * hardware), you must also define 
 *  
 * NCR5380_queue_command
 * NCR5380_reset
 * NCR5380_abort
 * NCR5380_proc_info
 *
 * to be the global entry points into the specific driver, ie 
 * #define NCR5380_queue_command t128_queue_command.
 *
 * If this is not done, the routines will be defined as static functions
 * with the NCR5380* names and the user must provide a globally
 * accessible wrapper function.
 *
 * 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.  Before the specific driver initialization
 * code finishes, NCR5380_print_options should be called.
 */

static struct Scsi_Host *first_instance = NULL;
static struct scsi_host_template *the_template = NULL;
Linus Torvalds's avatar
Linus Torvalds committed

/* Macros ease life... :-) */
#define	SETUP_HOSTDATA(in)				\
    struct NCR5380_hostdata *hostdata =			\
	(struct NCR5380_hostdata *)(in)->hostdata
#define	HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)

#define	NEXT(cmd)	((cmd)->host_scribble)
Linus Torvalds's avatar
Linus Torvalds committed
#define	NEXTADDR(cmd)	((Scsi_Cmnd **)&((cmd)->host_scribble))

#define	HOSTNO		instance->host_no
#define	H_NO(cmd)	(cmd)->device->host->host_no

#ifdef SUPPORT_TAGS

/*
 * Functions for handling tagged queuing
 * =====================================
 *
 * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes:
 *
 * Using consecutive numbers for the tags is no good idea in my eyes. There
 * could be wrong re-usings if the counter (8 bit!) wraps and some early
 * command has been preempted for a long time. My solution: a bitfield for
 * remembering used tags.
 *
 * There's also the problem that each target has a certain queue size, but we
 * cannot know it in advance :-( We just see a QUEUE_FULL status being
 * returned. So, in this case, the driver internal queue size assumption is
 * reduced to the number of active tags if QUEUE_FULL is returned by the
 * target. The command is returned to the mid-level, but with status changed
 * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
 * correctly.
 *
 * We're also not allowed running tagged commands as long as an untagged
 * command is active. And REQUEST SENSE commands after a contingent allegiance
 * condition _must_ be untagged. To keep track whether an untagged command has
 * been issued, the host->busy array is still employed, as it is without
 * support for tagged queuing.
 *
 * One could suspect that there are possible race conditions between
 * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the
 * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(),
 * which already guaranteed to be running at most once. It is also the only
 * place where tags/LUNs are allocated. So no other allocation can slip
 * between that pair, there could only happen a reselection, which can free a
 * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes
 * important: the tag bit must be cleared before 'nr_allocated' is decreased.
 */

/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
#undef TAG_NONE
#define TAG_NONE 0xff

typedef struct {
    DECLARE_BITMAP(allocated, MAX_TAGS);
    int		nr_allocated;
    int		queue_size;
} TAG_ALLOC;

static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */


static void __init init_tags( void )
{
    int target, lun;
    TAG_ALLOC *ta;
    
    if (!setup_use_tagged_queuing)
	return;
    
    for( target = 0; target < 8; ++target ) {
	for( lun = 0; lun < 8; ++lun ) {
	    ta = &TagAlloc[target][lun];
	    bitmap_zero(ta->allocated, MAX_TAGS);
	    ta->nr_allocated = 0;
	    /* At the beginning, assume the maximum queue size we could
	     * support (MAX_TAGS). This value will be decreased if the target
	     * returns QUEUE_FULL status.
	     */
	    ta->queue_size = MAX_TAGS;
	}
    }
}


/* Check if we can issue a command to this LUN: First see if the LUN is marked
 * busy by an untagged command. If the command should use tagged queuing, also
 * check that there is a free tag and the target's queue won't overflow. This
 * function should be called with interrupts disabled to avoid race
 * conditions.
 */ 

static int is_lun_busy( Scsi_Cmnd *cmd, int should_be_tagged )
{
    SETUP_HOSTDATA(cmd->device->host);

    if (hostdata->busy[cmd->device->id] & (1 << cmd->device->lun))
	return( 1 );
    if (!should_be_tagged ||
	!setup_use_tagged_queuing || !cmd->device->tagged_supported)
	return( 0 );
    if (TagAlloc[cmd->device->id][cmd->device->lun].nr_allocated >=
	TagAlloc[cmd->device->id][cmd->device->lun].queue_size ) {
	TAG_PRINTK( "scsi%d: target %d lun %d: no free tags\n",
		    H_NO(cmd), cmd->device->id, cmd->device->lun );
	return( 1 );
    }
    return( 0 );
}


/* Allocate a tag for a command (there are no checks anymore, check_lun_busy()
 * must be called before!), or reserve the LUN in 'busy' if the command is
 * untagged.
 */

static void cmd_get_tag( Scsi_Cmnd *cmd, int should_be_tagged )
{
    SETUP_HOSTDATA(cmd->device->host);

    /* If we or the target don't support tagged queuing, allocate the LUN for
     * an untagged command.
     */
    if (!should_be_tagged ||
	!setup_use_tagged_queuing || !cmd->device->tagged_supported) {
	cmd->tag = TAG_NONE;
	hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
	TAG_PRINTK( "scsi%d: target %d lun %d now allocated by untagged "
		    "command\n", H_NO(cmd), cmd->device->id, cmd->device->lun );
    }
    else {
	TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];

	cmd->tag = find_first_zero_bit( ta->allocated, MAX_TAGS );
	set_bit( cmd->tag, ta->allocated );
	ta->nr_allocated++;
	TAG_PRINTK( "scsi%d: using tag %d for target %d lun %d "
		    "(now %d tags in use)\n",
		    H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun,
		    ta->nr_allocated );
    }
}


/* Mark the tag of command 'cmd' as free, or in case of an untagged command,
 * unlock the LUN.
 */

static void cmd_free_tag( Scsi_Cmnd *cmd )
{
    SETUP_HOSTDATA(cmd->device->host);

    if (cmd->tag == TAG_NONE) {
	hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
	TAG_PRINTK( "scsi%d: target %d lun %d untagged cmd finished\n",
		    H_NO(cmd), cmd->device->id, cmd->device->lun );
    }
    else if (cmd->tag >= MAX_TAGS) {
	printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
		H_NO(cmd), cmd->tag );
    }
    else {
	TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
	clear_bit( cmd->tag, ta->allocated );
	ta->nr_allocated--;
	TAG_PRINTK( "scsi%d: freed tag %d for target %d lun %d\n",
		    H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun );
    }
}


static void free_all_tags( void )
{
    int target, lun;
    TAG_ALLOC *ta;

    if (!setup_use_tagged_queuing)
	return;
    
    for( target = 0; target < 8; ++target ) {
	for( lun = 0; lun < 8; ++lun ) {
	    ta = &TagAlloc[target][lun];
	    bitmap_zero(ta->allocated, MAX_TAGS);
	    ta->nr_allocated = 0;
	}
    }
}

#endif /* SUPPORT_TAGS */


/*
 * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd )
 *
 * Purpose: Try to merge several scatter-gather requests into one DMA
 *    transfer. This is possible if the scatter buffers lie on
 *    physical contiguous addresses.
 *
 * Parameters: Scsi_Cmnd *cmd
 *    The command to work on. The first scatter buffer's data are
 *    assumed to be already transfered into ptr/this_residual.
 */

static void merge_contiguous_buffers( Scsi_Cmnd *cmd )
{
    unsigned long endaddr;
#if (NDEBUG & NDEBUG_MERGING)
    unsigned long oldlen = cmd->SCp.this_residual;
    int		  cnt = 1;
#endif

    for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
	 cmd->SCp.buffers_residual &&
	 virt_to_phys(page_address(cmd->SCp.buffer[1].page)+
		      cmd->SCp.buffer[1].offset) == endaddr; ) {
	MER_PRINTK("VTOP(%p) == %08lx -> merging\n",
		   cmd->SCp.buffer[1].address, endaddr);
#if (NDEBUG & NDEBUG_MERGING)
	++cnt;
#endif
	++cmd->SCp.buffer;
	--cmd->SCp.buffers_residual;
	cmd->SCp.this_residual += cmd->SCp.buffer->length;
	endaddr += cmd->SCp.buffer->length;
    }
#if (NDEBUG & NDEBUG_MERGING)
    if (oldlen != cmd->SCp.this_residual)
	MER_PRINTK("merged %d buffers from %p, new length %08x\n",
		   cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
#endif
}

/*
 * Function : void initialize_SCp(Scsi_Cmnd *cmd)
 *
 * Purpose : initialize the saved data pointers for cmd to point to the 
 *	start of the buffer.
 *
 * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
 */

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;
Linus Torvalds's avatar
Linus Torvalds committed
	cmd->SCp.buffers_residual = cmd->use_sg - 1;
	cmd->SCp.ptr = (char *)page_address(cmd->SCp.buffer->page)+
		       cmd->SCp.buffer->offset;
	cmd->SCp.this_residual = cmd->SCp.buffer->length;
	/* ++roman: Try to merge some scatter-buffers if they are at
	 * contiguous physical addresses.
	 */
	merge_contiguous_buffers( cmd );
    } else {
	cmd->SCp.buffer = NULL;
	cmd->SCp.buffers_residual = 0;
	cmd->SCp.ptr = (char *) cmd->request_buffer;
	cmd->SCp.this_residual = cmd->request_bufflen;
    }
}

#include <linux/delay.h>

#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_ENABLE_EOP_INTR,"MODE EOP INTR"},
    {MR_MONITOR_BSY, "MODE MONITOR BSY"},
    {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"}, 
    {0, NULL}};

/*
 * Function : void NCR5380_print(struct Scsi_Host *instance)
 *
 * Purpose : print the SCSI bus signals for debugging purposes
 *
 * Input : instance - which NCR5380
 */

static void NCR5380_print(struct Scsi_Host *instance) {
    unsigned char status, data, basr, mr, icr, i;
    unsigned long flags;

    local_irq_save(flags);
    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);
    local_irq_restore(flags);
    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");
}

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"}};

/* 
 * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
 *
 * Purpose : print the current SCSI phase for debugging purposes
 *
 * Input : instance - which NCR5380
 */

static void NCR5380_print_phase(struct Scsi_Host *instance)
{
    unsigned char status;
    int i;

    status = NCR5380_read(STATUS_REG);
    if (!(status & SR_REQ)) 
	printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
    else {
	for (i = 0; (phases[i].value != PHASE_UNKNOWN) && 
	    (phases[i].value != (status & PHASE_MASK)); ++i); 
	printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
    }
}

#else /* !NDEBUG */

/* dummies... */
__inline__ void NCR5380_print(struct Scsi_Host *instance) { };
__inline__ void NCR5380_print_phase(struct Scsi_Host *instance) { };

#endif

/*
 * ++roman: New scheme of calling NCR5380_main()
 * 
 * If we're not in an interrupt, we can call our main directly, it cannot be
 * already running. Else, we queue it on a task queue, if not 'main_running'
 * tells us that a lower level is already executing it. This way,
 * 'main_running' needs not be protected in a special way.
 *
 * queue_main() is a utility function for putting our main onto the task
 * queue, if main_running is false. It should be called only from a
 * interrupt or bottom half.
 */

#include <linux/workqueue.h>
#include <linux/interrupt.h>

static volatile int main_running = 0;
static DECLARE_WORK(NCR5380_tqueue, (void (*)(void*))NCR5380_main, NULL);

static __inline__ void queue_main(void)
{
    if (!main_running) {
	/* If in interrupt and NCR5380_main() not already running,
	   queue it on the 'immediate' task queue, to be processed
	   immediately after the current interrupt processing has
	   finished. */
	schedule_work(&NCR5380_tqueue);
    }
    /* else: nothing to do: the running NCR5380_main() will pick up
       any newly queued command. */
}


static inline void NCR5380_all_init (void)
{
    static int done = 0;
    if (!done) {
	INI_PRINTK("scsi : NCR5380_all_init()\n");
	done = 1;
    }
}

 
/*
 * Function : void NCR58380_print_options (struct Scsi_Host *instance)
 *
 * Purpose : called by probe code indicating the NCR5380 driver
 *	     options that were selected.
 *
 * Inputs : instance, pointer to this instance.  Unused.
 */

static void __init NCR5380_print_options (struct Scsi_Host *instance)
{
    printk(" generic options"
#ifdef AUTOSENSE 
    " AUTOSENSE"
#endif
#ifdef REAL_DMA
    " REAL DMA"
#endif
#ifdef PARITY
    " PARITY"
#endif
#ifdef SUPPORT_TAGS
    " SCSI-2 TAGGED QUEUING"
#endif
    );
    printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
}

/*
 * Function : void NCR5380_print_status (struct Scsi_Host *instance)
 *
 * Purpose : print commands in the various queues, called from
 *	NCR5380_abort and NCR5380_debug to aid debugging.
 *
 * Inputs : instance, pointer to this instance.  
 */

static void NCR5380_print_status (struct Scsi_Host *instance)
{
    char *pr_bfr;
    char *start;
    int len;

    NCR_PRINT(NDEBUG_ANY);
    NCR_PRINT_PHASE(NDEBUG_ANY);

    pr_bfr = (char *) __get_free_page(GFP_ATOMIC);
    if (!pr_bfr) {
	printk("NCR5380_print_status: no memory for print buffer\n");
	return;
    }
    len = NCR5380_proc_info(instance, pr_bfr, &start, 0, PAGE_SIZE, 0);
Linus Torvalds's avatar
Linus Torvalds committed
    pr_bfr[len] = 0;
    printk("\n%s\n", pr_bfr);
    free_page((unsigned long) pr_bfr);
}


/******************************************/
/*
 * /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(fmt,args...) \
  do { if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \
	 pos += sprintf(pos, fmt , ## args); } while(0)
static
char *lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, 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;
    unsigned long flags;
    off_t begin = 0;
#define check_offset()				\
    do {					\
	if (pos - buffer < offset - begin) {	\
	    begin += pos - buffer;		\
	    pos = buffer;			\
	}					\
    } while (0)

    hostdata = (struct NCR5380_hostdata *)instance->hostdata;

    if (inout) { /* Has data been written to the file ? */
	return(-ENOSYS);  /* Currently this is a no-op */
    }
    SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
    check_offset();
    local_irq_save(flags);
    SPRINTF("NCR5380: coroutine is%s running.\n", main_running ? "" : "n't");
    check_offset();
    if (!hostdata->connected)
	SPRINTF("scsi%d: no currently connected command\n", HOSTNO);
    else
	pos = lprint_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected,
				pos, buffer, length);
    SPRINTF("scsi%d: issue_queue\n", HOSTNO);
    check_offset();
    for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = NEXT(ptr)) {
	pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
	check_offset();
    }

    SPRINTF("scsi%d: disconnected_queue\n", HOSTNO);
    check_offset();
    for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
	 ptr = NEXT(ptr)) {
	pos = lprint_Scsi_Cmnd (ptr, pos, buffer, length);
	check_offset();
    }

    local_irq_restore(flags);
    *start = buffer + (offset - begin);
    if (pos - buffer < offset - begin)
	return 0;
    else if (pos - buffer - (offset - begin) < length)
	return pos - buffer - (offset - begin);
    return length;
}

static char *
lprint_Scsi_Cmnd (Scsi_Cmnd *cmd, char *pos, char *buffer, int length)
{
    int i, s;
    unsigned char *command;
    SPRINTF("scsi%d: destination target %d, lun %d\n",
	    H_NO(cmd), cmd->device->id, cmd->device->lun);
    SPRINTF("        command = ");
    command = cmd->cmnd;
    SPRINTF("%2d (0x%02x)", command[0], command[0]);
    for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
	SPRINTF(" %02x", command[i]);
    SPRINTF("\n");
    return pos;
}


/* 
 * Function : void NCR5380_init (struct Scsi_Host *instance)
 *
 * Purpose : initializes *instance and corresponding 5380 chip.
 *
 * Inputs : instance - instantiation of the 5380 driver.  
 *
 * Notes : I assume that the host, hostno, and id bits have been
 * 	set correctly.  I don't care about the irq and other fields. 
 * 
 */

static int NCR5380_init (struct Scsi_Host *instance, int flags)
{
    int i;
    SETUP_HOSTDATA(instance);

    NCR5380_all_init();

    hostdata->aborted = 0;
    hostdata->id_mask = 1 << instance->this_id;
    hostdata->id_higher_mask = 0;
    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 SUPPORT_TAGS
    init_tags();
#endif
#if defined (REAL_DMA)
    hostdata->dma_len = 0;
#endif
    hostdata->targets_present = 0;
    hostdata->connected = NULL;
    hostdata->issue_queue = NULL;
    hostdata->disconnected_queue = NULL;
    hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;

    if (!the_template) {
	the_template = instance->hostt;
	first_instance = instance;
    }
	

#ifndef AUTOSENSE
    if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
	 printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
	        "        without AUTOSENSE option, contingent allegiance conditions may\n"
	        "        be incorrectly cleared.\n", HOSTNO);
#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);

    return 0;
}

/* 
 * our own old-style timeout update
 */
/*
 * The strategy is to cause the timer code to call scsi_times_out()
 * when the soonest timeout is pending.
 * The arguments are used when we are queueing a new command, because
 * we do not want to subtract the time used from this time, but when we
 * set the timer, we want to take this value into account.
 */

int atari_scsi_update_timeout(Scsi_Cmnd * SCset, int timeout)
{
    int rtn;

    /*
     * We are using the new error handling code to actually register/deregister
     * timers for timeout.
     */

    if (!timer_pending(&SCset->eh_timeout)) {
	rtn = 0;
    } else {
	rtn = SCset->eh_timeout.expires - jiffies;
    }

    if (timeout == 0) {
        del_timer(&SCset->eh_timeout);
        SCset->eh_timeout.data = (unsigned long) NULL;
        SCset->eh_timeout.expires = 0;
    } else {
        if (SCset->eh_timeout.data != (unsigned long) NULL) 
            del_timer(&SCset->eh_timeout);
        SCset->eh_timeout.data = (unsigned long) SCset;
        SCset->eh_timeout.expires = jiffies + timeout;
        add_timer(&SCset->eh_timeout);
    }
	return rtn;
}

Linus Torvalds's avatar
Linus Torvalds committed
/* 
 * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, 
 *	void (*done)(Scsi_Cmnd *)) 
 *
 * Purpose :  enqueues a SCSI command
 *
 * Inputs : cmd - SCSI command, done - function called on completion, with
 *	a pointer to the command descriptor.
 * 
 * Returns : 0
 *
 * Side effects : 
 *      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.
 *
 */

static
int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
{
    SETUP_HOSTDATA(cmd->device->host);
    Scsi_Cmnd *tmp;
    int oldto;
    unsigned long flags;
    // extern int update_timeout(Scsi_Cmnd * SCset, int timeout);
Linus Torvalds's avatar
Linus Torvalds committed

#if (NDEBUG & NDEBUG_NO_WRITE)
    switch (cmd->cmnd[0]) {
    case WRITE_6:
    case WRITE_10:
	printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
	       H_NO(cmd));
	cmd->result = (DID_ERROR << 16);
	done(cmd);
	return 0;
    }
#endif /* (NDEBUG & NDEBUG_NO_WRITE) */


#ifdef NCR5380_STATS
# if 0
    if (!hostdata->connected && !hostdata->issue_queue &&
	!hostdata->disconnected_queue) {
	hostdata->timebase = jiffies;
    }
# endif
# ifdef NCR5380_STAT_LIMIT
    if (cmd->request_bufflen > NCR5380_STAT_LIMIT)
# endif
	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 
     */

    NEXT(cmd) = NULL;
    cmd->scsi_done = done;

    cmd->result = 0;


    /*