Skip to content
atari_NCR5380.c 86.5 KiB
Newer Older
	unsigned long flags;

	/*
	 * ++roman: Just disabling the NCR interrupt isn't sufficient here,
	 * because also a timer int can trigger an abort or reset, which can
	 * alter queues and touch the Falcon lock.
	 */

	local_save_flags(flags);
	do {
		local_irq_disable();	/* Freeze request queues */
		done = 1;

		if (!hostdata->connected) {
			dprintk(NDEBUG_MAIN, "scsi%d: not connected\n", HOSTNO);
			/*
			 * Search through the issue_queue for a command destined
			 * for a target that's not busy.
			 */
Linus Torvalds's avatar
Linus Torvalds committed
#if (NDEBUG & NDEBUG_LISTS)
			for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, prev = NULL;
			     tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp))
				;
			/*printk("%p  ", tmp);*/
			if ((tmp == prev) && tmp)
				printk(" LOOP\n");
			/* else printk("\n"); */
Linus Torvalds's avatar
Linus Torvalds committed
#endif
			for (tmp = (struct scsi_cmnd *) hostdata->issue_queue,
			     prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp)) {
Hannes Reinecke's avatar
Hannes Reinecke committed
				u8 lun = tmp->device->lun;
Linus Torvalds's avatar
Linus Torvalds committed

				dprintk(NDEBUG_LISTS,
				        "MAIN tmp=%p target=%d busy=%d lun=%d\n",
				        tmp, scmd_id(tmp), hostdata->busy[scmd_id(tmp)],
				        lun);
				/*  When we find one, remove it from the issue queue. */
				/* ++guenther: possible race with Falcon locking */
				if (
Linus Torvalds's avatar
Linus Torvalds committed
#ifdef SUPPORT_TAGS
				    !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE)
Linus Torvalds's avatar
Linus Torvalds committed
#else
Hannes Reinecke's avatar
Hannes Reinecke committed
				    !(hostdata->busy[tmp->device->id] & (1 << lun))
Linus Torvalds's avatar
Linus Torvalds committed
#endif
				    ) {
					/* ++guenther: just to be sure, this must be atomic */
					local_irq_disable();
					if (prev) {
						REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
						SET_NEXT(prev, NEXT(tmp));
					} else {
						REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp));
						hostdata->issue_queue = NEXT(tmp);
					}
					SET_NEXT(tmp, NULL);
					hostdata->retain_dma_intr++;

					/* reenable interrupts after finding one */
					local_irq_restore(flags);

					/*
					 * Attempt to establish an I_T_L nexus here.
					 * On success, instance->hostdata->connected is set.
					 * On failure, we must add the command back to the
					 *   issue queue so we can keep trying.
					 */
					dprintk(NDEBUG_MAIN, "scsi%d: main(): command for target %d "
						    "lun %d removed from issue_queue\n",
Hannes Reinecke's avatar
Hannes Reinecke committed
						    HOSTNO, tmp->device->id, lun);
					/*
					 * REQUEST SENSE commands are issued without tagged
					 * queueing, even on SCSI-II devices because the
					 * contingent allegiance condition exists for the
					 * entire unit.
					 */
					/* ++roman: ...and the standard also requires that
					 * REQUEST SENSE command are untagged.
					 */

Linus Torvalds's avatar
Linus Torvalds committed
#ifdef SUPPORT_TAGS
					cmd_get_tag(tmp, tmp->cmnd[0] != REQUEST_SENSE);
Linus Torvalds's avatar
Linus Torvalds committed
#endif
					if (!NCR5380_select(instance, tmp)) {
						local_irq_disable();
						hostdata->retain_dma_intr--;
						maybe_release_dma_irq(instance);
						local_irq_restore(flags);
						local_irq_disable();
						LIST(tmp, hostdata->issue_queue);
						SET_NEXT(tmp, hostdata->issue_queue);
						hostdata->issue_queue = tmp;
Linus Torvalds's avatar
Linus Torvalds committed
#ifdef SUPPORT_TAGS
						cmd_free_tag(tmp);
Linus Torvalds's avatar
Linus Torvalds committed
#endif
						hostdata->retain_dma_intr--;
						local_irq_restore(flags);
						dprintk(NDEBUG_MAIN, "scsi%d: main(): select() failed, "
							    "returned to issue_queue\n", HOSTNO);
					}
				} /* if target/lun/target queue is not busy */
			} /* for issue_queue */
		} /* if (!hostdata->connected) */

		if (hostdata->connected
Linus Torvalds's avatar
Linus Torvalds committed
#ifdef REAL_DMA
		    && !hostdata->dma_len
Linus Torvalds's avatar
Linus Torvalds committed
#endif
		    ) {
			local_irq_restore(flags);
			dprintk(NDEBUG_MAIN, "scsi%d: main: performing information transfer\n",
				    HOSTNO);
			NCR5380_information_transfer(instance);
			dprintk(NDEBUG_MAIN, "scsi%d: main: done set false\n", HOSTNO);
			done = 0;
		}
	} while (!done);
	local_irq_restore(flags);
Linus Torvalds's avatar
Linus Torvalds committed
}


#ifdef REAL_DMA
/*
 * 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).
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * Inputs : instance - this instance of the NCR5380.
 *
 */

static void NCR5380_dma_complete(struct Scsi_Host *instance)
Linus Torvalds's avatar
Linus Torvalds committed
{
	SETUP_HOSTDATA(instance);
	int transferred;
	unsigned char **data;
	volatile int *count;
	int saved_data = 0, overrun = 0;
	unsigned char p;

	if (!hostdata->connected) {
		printk(KERN_WARNING "scsi%d: received end of DMA interrupt with "
		       "no connected cmd\n", HOSTNO);
		return;
Linus Torvalds's avatar
Linus Torvalds committed
	}
	if (hostdata->read_overruns) {
		p = hostdata->connected->SCp.phase;
		if (p & SR_IO) {
			udelay(10);
			if ((NCR5380_read(BUS_AND_STATUS_REG) &
			     (BASR_PHASE_MATCH|BASR_ACK)) ==
			    (BASR_PHASE_MATCH|BASR_ACK)) {
				saved_data = NCR5380_read(INPUT_DATA_REG);
				overrun = 1;
				dprintk(NDEBUG_DMA, "scsi%d: read overrun handled\n", HOSTNO);
	dprintk(NDEBUG_DMA, "scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n",
		   HOSTNO, NCR5380_read(BUS_AND_STATUS_REG),
		   NCR5380_read(STATUS_REG));

#if defined(CONFIG_SUN3)
	if ((sun3scsi_dma_finish(rq_data_dir(hostdata->connected->request)))) {
		pr_err("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n",
		       instance->host_no);
		BUG();
	}

	/* make sure we're not stuck in a data phase */
	if ((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) ==
	    (BASR_PHASE_MATCH | BASR_ACK)) {
		pr_err("scsi%d: BASR %02x\n", instance->host_no,
		       NCR5380_read(BUS_AND_STATUS_REG));
		pr_err("scsi%d: bus stuck in data phase -- probably a single byte overrun!\n",
		       instance->host_no);
		BUG();
	}
#endif

	(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
	NCR5380_write(MODE_REG, MR_BASE);
	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);

	transferred = hostdata->dma_len - NCR5380_dma_residual(instance);
	hostdata->dma_len = 0;

	data = (unsigned char **)&hostdata->connected->SCp.ptr;
	count = &hostdata->connected->SCp.this_residual;
	*data += transferred;
	*count -= transferred;
	if (hostdata->read_overruns) {
		if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) {
			cnt = toPIO = hostdata->read_overruns;
			if (overrun) {
				dprintk(NDEBUG_DMA, "Got an input overrun, using saved byte\n");
				*(*data)++ = saved_data;
				(*count)--;
				cnt--;
				toPIO--;
			}
			dprintk(NDEBUG_DMA, "Doing %d-byte PIO to 0x%08lx\n", cnt, (long)*data);
			NCR5380_transfer_pio(instance, &p, &cnt, data);
			*count -= toPIO - cnt;
		}
Linus Torvalds's avatar
Linus Torvalds committed
	}
}
#endif /* REAL_DMA */


/**
 * NCR5380_intr - generic NCR5380 irq handler
 * @irq: interrupt number
 * @dev_id: device info
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * Handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
 * from the disconnected queue, and restarting NCR5380_main()
 * as required.
static irqreturn_t NCR5380_intr(int irq, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
{
	struct Scsi_Host *instance = dev_id;
	struct NCR5380_hostdata *hostdata = shost_priv(instance);
	int done = 1, handled = 0;
	unsigned char basr;

	dprintk(NDEBUG_INTR, "scsi%d: NCR5380 irq triggered\n", HOSTNO);

	/* Look for pending interrupts */
	basr = NCR5380_read(BUS_AND_STATUS_REG);
	dprintk(NDEBUG_INTR, "scsi%d: BASR=%02x\n", HOSTNO, basr);
	/* dispatch to appropriate routine if found and done=0 */
	if (basr & BASR_IRQ) {
		NCR5380_dprint(NDEBUG_INTR, instance);
		if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) {
			done = 0;
			dprintk(NDEBUG_INTR, "scsi%d: SEL interrupt\n", HOSTNO);
			NCR5380_reselect(instance);
			(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
		} else if (basr & BASR_PARITY_ERROR) {
			dprintk(NDEBUG_INTR, "scsi%d: PARITY interrupt\n", HOSTNO);
			(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
		} else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
			dprintk(NDEBUG_INTR, "scsi%d: RESET interrupt\n", HOSTNO);
			(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
		} else {
			/*
			 * The rest of the interrupt conditions can occur only during a
			 * DMA transfer
			 */
Linus Torvalds's avatar
Linus Torvalds committed

#if defined(REAL_DMA)
			/*
			 * We should only get PHASE MISMATCH and EOP interrupts if we have
			 * DMA enabled, so do a sanity check based on the current setting
			 * of the MODE register.
			 */

			if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) &&
			    ((basr & BASR_END_DMA_TRANSFER) ||
			     !(basr & BASR_PHASE_MATCH))) {

				dprintk(NDEBUG_INTR, "scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO);
				NCR5380_dma_complete( instance );
				done = 0;
			} else
Linus Torvalds's avatar
Linus Torvalds committed
#endif /* REAL_DMA */
Linus Torvalds's avatar
Linus Torvalds committed
/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
				if (basr & BASR_PHASE_MATCH)
					dprintk(NDEBUG_INTR, "scsi%d: unknown interrupt, "
					       "BASR 0x%x, MR 0x%x, SR 0x%x\n",
					       HOSTNO, basr, NCR5380_read(MODE_REG),
					       NCR5380_read(STATUS_REG));
				(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
#ifdef SUN3_SCSI_VME
				dregs->csr |= CSR_DMA_ENABLE;
#endif
			}
		} /* if !(SELECTION || PARITY) */
		handled = 1;
	} /* BASR & IRQ */ else {
		printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, "
		       "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
		       NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
		(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
#ifdef SUN3_SCSI_VME
		dregs->csr |= CSR_DMA_ENABLE;
#endif
	if (!done)
		queue_work(hostdata->work_q, &hostdata->main_task);

	return IRQ_RETVAL(handled);
 * Function : int NCR5380_select(struct Scsi_Host *instance,
 *                               struct scsi_cmnd *cmd)
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
 *	including ARBITRATION, SELECTION, and initial message out for
 *	IDENTIFY and queue messages.
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * Inputs : instance - instantiation of the 5380 driver on which this
 *	target lives, cmd - SCSI command to execute.
 * Returns : -1 if selection failed but should be retried.
 *      0 if selection failed and should not be retried.
 *      0 if selection succeeded completely (hostdata->connected == cmd).
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * Side effects :
 *	If bus busy, arbitration failed, etc, NCR5380_select() will exit
Linus Torvalds's avatar
Linus Torvalds committed
 *		with registers as they should have been on entry - ie
 *		SELECT_ENABLE will be set appropriately, the NCR5380
 *		will cease to drive any SCSI bus signals.
 *
 *	If successful : I_T_L or I_T_L_Q nexus will be established,
 *		instance->connected will be set to cmd.
 *		SELECT interrupt will be disabled.
Linus Torvalds's avatar
Linus Torvalds committed
 *
 *	If failed (no target) : cmd->scsi_done() will be called, and the
Linus Torvalds's avatar
Linus Torvalds committed
 *		cmd->result host byte set to DID_BAD_TARGET.
 */

static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
Linus Torvalds's avatar
Linus Torvalds committed
{
	SETUP_HOSTDATA(instance);
	unsigned char tmp[3], phase;
	unsigned char *data;
	int len;
	int err;
	unsigned long flags;
	unsigned long timeout;
	NCR5380_dprint(NDEBUG_ARBITRATION, instance);
	dprintk(NDEBUG_ARBITRATION, "scsi%d: starting arbitration, id = %d\n", HOSTNO,
		   instance->this_id);

	/*
	 * Set the phase bits to 0, otherwise the NCR5380 won't drive the
	 * data bus during SELECTION.
	 */

	local_irq_save(flags);
	if (hostdata->connected) {
		local_irq_restore(flags);
		return -1;
	}
	NCR5380_write(TARGET_COMMAND_REG, 0);
Linus Torvalds's avatar
Linus Torvalds committed

	/*
	 * Start arbitration.
	 */
Linus Torvalds's avatar
Linus Torvalds committed

	NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
	NCR5380_write(MODE_REG, MR_ARBITRATE);
Linus Torvalds's avatar
Linus Torvalds committed

	/* The chip now waits for BUS FREE phase. Then after the 800 ns
	 * Bus Free Delay, arbitration will begin.
	 */
	local_irq_restore(flags);
	timeout = jiffies + HZ;
	while (1) {
		if (time_is_before_jiffies(timeout)) {
			NCR5380_write(MODE_REG, MR_BASE);
			shost_printk(KERN_ERR, instance,
			             "select: arbitration timeout\n");
		if (!(NCR5380_read(MODE_REG) & MR_ARBITRATE)) {
			/* Reselection interrupt */
			return -1;
		}
		if (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
			break;
	/* The SCSI-2 arbitration delay is 2.4 us */
	udelay(3);

	/* Check for lost arbitration */
	if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
	    (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
	    (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
	    hostdata->connected) {
		NCR5380_write(MODE_REG, MR_BASE);
		dprintk(NDEBUG_ARBITRATION, "scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
			   HOSTNO);
		return -1;
	}
Linus Torvalds's avatar
Linus Torvalds committed

	/* After/during arbitration, BSY should be asserted.
	 * IBM DPES-31080 Version S31Q works now
	 * Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman)
	 */
	NCR5380_write(INITIATOR_COMMAND_REG,
		      ICR_BASE | ICR_ASSERT_SEL | ICR_ASSERT_BSY);

	if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
	    hostdata->connected) {
		NCR5380_write(MODE_REG, MR_BASE);
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
		dprintk(NDEBUG_ARBITRATION, "scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n",
			   HOSTNO);
		return -1;
	}

	/*
	 * Again, bus clear + bus settle time is 1.2us, however, this is
	 * a minimum so we'll udelay ceil(1.2)
	 */
Linus Torvalds's avatar
Linus Torvalds committed

	if (hostdata->flags & FLAG_TOSHIBA_DELAY)
		udelay(15);
	else
		udelay(2);

	if (hostdata->connected) {
		NCR5380_write(MODE_REG, MR_BASE);
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
		return -1;
	}

	dprintk(NDEBUG_ARBITRATION, "scsi%d: won arbitration\n", HOSTNO);

	/*
	 * Now that we have won arbitration, start Selection process, asserting
	 * the host and target ID's on the SCSI bus.
	 */

	NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->device->id)));

	/*
	 * Raise ATN while SEL is true before BSY goes false from arbitration,
	 * since this is the only way to guarantee that we'll get a MESSAGE OUT
	 * phase immediately after selection.
	 */

	NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
		      ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
Linus Torvalds's avatar
Linus Torvalds committed
	NCR5380_write(MODE_REG, MR_BASE);

	/*
	 * Reselect interrupts must be turned off prior to the dropping of BSY,
	 * otherwise we will trigger an interrupt.
	 */

	if (hostdata->connected) {
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
		return -1;
	}

	NCR5380_write(SELECT_ENABLE_REG, 0);
Linus Torvalds's avatar
Linus Torvalds committed

	/*
	 * The initiator shall then wait at least two deskew delays and release
	 * the BSY signal.
	 */
	udelay(1);        /* wingel -- wait two bus deskew delay >2*45ns */

	/* Reset BSY */
	NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
		      ICR_ASSERT_ATN | ICR_ASSERT_SEL));

	/*
	 * Something weird happens when we cease to drive BSY - looks
	 * like the board/chip is letting us do another read before the
	 * appropriate propagation delay has expired, and we're confusing
	 * a BSY signal from ourselves as the target's response to SELECTION.
	 *
	 * A small delay (the 'C++' frontend breaks the pipeline with an
	 * unnecessary jump, making it work on my 386-33/Trantor T128, the
	 * tighter 'C' code breaks and requires this) solves the problem -
	 * the 1 us delay is arbitrary, and only used because this delay will
	 * be the same on other platforms and since it works here, it should
	 * work there.
	 *
	 * wingel suggests that this could be due to failing to wait
	 * one deskew delay.
	 */
Linus Torvalds's avatar
Linus Torvalds committed

Linus Torvalds's avatar
Linus Torvalds committed

	dprintk(NDEBUG_SELECTION, "scsi%d: selecting target %d\n", HOSTNO, cmd->device->id);
Linus Torvalds's avatar
Linus Torvalds committed

	/*
	 * The SCSI specification calls for a 250 ms timeout for the actual
	 * selection.
	 */
Linus Torvalds's avatar
Linus Torvalds committed

	err = NCR5380_poll_politely(instance, STATUS_REG, SR_BSY, SR_BSY,
	                            msecs_to_jiffies(250));

	if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) {
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
		NCR5380_reselect(instance);
		printk(KERN_ERR "scsi%d: reselection after won arbitration?\n",
		       HOSTNO);
		NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
		return -1;
	}
Linus Torvalds's avatar
Linus Torvalds committed

	if (err < 0) {
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
		cmd->result = DID_BAD_TARGET << 16;
Linus Torvalds's avatar
Linus Torvalds committed
#ifdef SUPPORT_TAGS
		cmd_free_tag(cmd);
Linus Torvalds's avatar
Linus Torvalds committed
#endif
		cmd->scsi_done(cmd);
		NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
		dprintk(NDEBUG_SELECTION, "scsi%d: target did not respond within 250ms\n", HOSTNO);
	/*
	 * No less than two deskew delays after the initiator detects the
	 * BSY signal is true, it shall release the SEL signal and may
	 * change the DATA BUS.                                     -wingel
	 */

	udelay(1);

	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);

	/*
	 * Since we followed the SCSI spec, and raised ATN while SEL
	 * was true but before BSY was false during selection, the information
	 * transfer phase should be a MESSAGE OUT phase so that we can send the
	 * IDENTIFY message.
	 *
	 * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
	 * message (2 bytes) with a tag ID that we increment with every command
	 * until it wraps back to 0.
	 *
	 * XXX - it turns out that there are some broken SCSI-II devices,
	 *	     which claim to support tagged queuing but fail when more than
	 *	     some number of commands are issued at once.
	 */

	/* Wait for start of REQ/ACK handshake */

	err = NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, HZ);
	if (err < 0) {
		shost_printk(KERN_ERR, instance, "select: REQ timeout\n");
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
		NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
		return -1;
	}
	dprintk(NDEBUG_SELECTION, "scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
		   HOSTNO, cmd->device->id);
	tmp[0] = IDENTIFY(1, cmd->device->lun);
Linus Torvalds's avatar
Linus Torvalds committed

#ifdef SUPPORT_TAGS
	if (cmd->tag != TAG_NONE) {
		tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG;
		tmp[2] = cmd->tag;
		len = 3;
	} else
		len = 1;
Linus Torvalds's avatar
Linus Torvalds committed
#else
	len = 1;
	cmd->tag = 0;
Linus Torvalds's avatar
Linus Torvalds committed
#endif /* SUPPORT_TAGS */

	/* Send message(s) */
	data = tmp;
	phase = PHASE_MSGOUT;
	NCR5380_transfer_pio(instance, &phase, &len, &data);
	dprintk(NDEBUG_SELECTION, "scsi%d: nexus established.\n", HOSTNO);
	/* XXX need to handle errors here */
	hostdata->connected = cmd;
Linus Torvalds's avatar
Linus Torvalds committed
#ifndef SUPPORT_TAGS
	hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
#endif
#ifdef SUN3_SCSI_VME
	dregs->csr |= CSR_INTR;
#endif
Linus Torvalds's avatar
Linus Torvalds committed

	initialize_SCp(cmd);
Linus Torvalds's avatar
Linus Torvalds committed

/*
 * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
Linus Torvalds's avatar
Linus Torvalds committed
 *      unsigned char *phase, int *count, unsigned char **data)
 *
 * Purpose : transfers data in given phase using polled I/O
 *
 * Inputs : instance - instance of driver, *phase - pointer to
 *	what phase is expected, *count - pointer to number of
Linus Torvalds's avatar
Linus Torvalds committed
 *	bytes to transfer, **data - pointer to data pointer.
Linus Torvalds's avatar
Linus Torvalds committed
 * Returns : -1 when different phase is entered without transferring
Lucas De Marchi's avatar
Lucas De Marchi committed
 *	maximum number of bytes, 0 if all bytes are transferred or exit
Linus Torvalds's avatar
Linus Torvalds committed
 *	is in same phase.
 *
 *	Also, *phase, *count, *data are modified in place.
Linus Torvalds's avatar
Linus Torvalds committed
 *
 * XXX Note : handling for bus free may be useful.
 */

/*
 * Note : this code is not as quick as it could be, however it
Linus Torvalds's avatar
Linus Torvalds committed
 * IS 100% reliable, and for the actual data transfer where speed
 * counts, we will always do a pseudo DMA or DMA transfer.
 */

static int NCR5380_transfer_pio(struct Scsi_Host *instance,
				unsigned char *phase, int *count,
				unsigned char **data)
Linus Torvalds's avatar
Linus Torvalds committed
{
	register unsigned char p = *phase, tmp;
	register int c = *count;
	register unsigned char *d = *data;

	/*
	 * The NCR5380 chip will only drive the SCSI bus when the
	 * phase specified in the appropriate bits of the TARGET COMMAND
	 * REGISTER match the STATUS REGISTER
	NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
Linus Torvalds's avatar
Linus Torvalds committed

	do {
		/*
		 * Wait for assertion of REQ, after which the phase bits will be
		 * valid
		 */

		if (NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, HZ) < 0)
			break;
Linus Torvalds's avatar
Linus Torvalds committed

		dprintk(NDEBUG_HANDSHAKE, "scsi%d: REQ detected\n", HOSTNO);
Linus Torvalds's avatar
Linus Torvalds committed

		/* Check for phase mismatch */
		if ((NCR5380_read(STATUS_REG) & PHASE_MASK) != p) {
			dprintk(NDEBUG_PIO, "scsi%d: phase mismatch\n", HOSTNO);
			NCR5380_dprint_phase(NDEBUG_PIO, instance);
Linus Torvalds's avatar
Linus Torvalds committed

		/* Do actual transfer from SCSI bus to / from memory */
		if (!(p & SR_IO))
			NCR5380_write(OUTPUT_DATA_REG, *d);
		else
			*d = NCR5380_read(CURRENT_SCSI_DATA_REG);
Linus Torvalds's avatar
Linus Torvalds committed

Linus Torvalds's avatar
Linus Torvalds committed

		/*
		 * The SCSI standard suggests that in MSGOUT phase, the initiator
		 * should drop ATN on the last byte of the message phase
		 * after REQ has been asserted for the handshake but before
		 * the initiator raises ACK.
		 */
Linus Torvalds's avatar
Linus Torvalds committed

		if (!(p & SR_IO)) {
			if (!((p & SR_MSG) && c > 1)) {
				NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
				NCR5380_dprint(NDEBUG_PIO, instance);
				NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
					      ICR_ASSERT_DATA | ICR_ASSERT_ACK);
			} else {
				NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
					      ICR_ASSERT_DATA | ICR_ASSERT_ATN);
				NCR5380_dprint(NDEBUG_PIO, instance);
				NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
					      ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
			}
		} else {
			NCR5380_dprint(NDEBUG_PIO, instance);
			NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
		}
Linus Torvalds's avatar
Linus Torvalds committed

		if (NCR5380_poll_politely(instance,
		                          STATUS_REG, SR_REQ, 0, 5 * HZ) < 0)
			break;
		dprintk(NDEBUG_HANDSHAKE, "scsi%d: req false, handshake complete\n", HOSTNO);

		/*
		 * We have several special cases to consider during REQ/ACK handshaking :
		 * 1.  We were in MSGOUT phase, and we are on the last byte of the
		 *	message.  ATN must be dropped as ACK is dropped.
		 *
		 * 2.  We are in a MSGIN phase, and we are on the last byte of the
		 *	message.  We must exit with ACK asserted, so that the calling
		 *	code may raise ATN before dropping ACK to reject the message.
		 *
		 * 3.  ACK and ATN are clear and the target may proceed as normal.
		 */
		if (!(p == PHASE_MSGIN && c == 1)) {
			if (p == PHASE_MSGOUT && c > 1)
				NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
			else
				NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
		}
	} while (--c);

	dprintk(NDEBUG_PIO, "scsi%d: residual %d\n", HOSTNO, c);

	*count = c;
	*data = d;
	tmp = NCR5380_read(STATUS_REG);
	/* The phase read from the bus is valid if either REQ is (already)
	 * asserted or if ACK hasn't been released yet. The latter applies if
	 * we're in MSG IN, DATA IN or STATUS and all bytes have been received.
	if ((tmp & SR_REQ) || ((tmp & SR_IO) && c == 0))
		*phase = tmp & PHASE_MASK;
	else
		*phase = PHASE_UNKNOWN;

	if (!c || (*phase == p))
		return 0;
	else
		return -1;
/**
 * do_reset - issue a reset command
 * @instance: adapter to reset
 *
 * Issue a reset sequence to the NCR5380 and try and get the bus
 * back into sane shape.
 *
 * This clears the reset interrupt flag because there may be no handler for
 * it. When the driver is initialized, the NCR5380_intr() handler has not yet
 * been installed. And when in EH we may have released the ST DMA interrupt.
 */

static void do_reset(struct Scsi_Host *instance)
{
	unsigned long flags;

	local_irq_save(flags);
	NCR5380_write(TARGET_COMMAND_REG,
	              PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK));
	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
	udelay(50);
	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
	(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
	local_irq_restore(flags);
}

/**
 * do_abort - abort the currently established nexus by going to
 * MESSAGE OUT phase and sending an ABORT message.
 * @instance: relevant scsi host instance
 * Returns 0 on success, -1 on failure.
static int do_abort(struct Scsi_Host *instance)
Linus Torvalds's avatar
Linus Torvalds committed
{
	unsigned char tmp, *msgptr, phase;
	int len;

	/* Request message out phase */
Linus Torvalds's avatar
Linus Torvalds committed
	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);

	/*
	 * Wait for the target to indicate a valid phase by asserting
	 * REQ.  Once this happens, we'll have either a MSGOUT phase
	 * and can immediately send the ABORT message, or we'll have some
	 * other phase and will have to source/sink data.
	 *
	 * We really don't care what value was on the bus or what value
	 * the target sees, so we just handshake.
	 */

	rc = NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, 10 * HZ);
	if (rc < 0)
		goto timeout;

	tmp = NCR5380_read(STATUS_REG) & PHASE_MASK;

	NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));

	if (tmp != PHASE_MSGOUT) {
		NCR5380_write(INITIATOR_COMMAND_REG,
		              ICR_BASE | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
		rc = NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, 0, 3 * HZ);
		if (rc < 0)
			goto timeout;
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
	}

	tmp = ABORT;
	msgptr = &tmp;
	len = 1;
	phase = PHASE_MSGOUT;
	NCR5380_transfer_pio(instance, &phase, &len, &msgptr);

	/*
	 * If we got here, and the command completed successfully,
	 * we're about to go into bus free state.
	 */

	return len ? -1 : 0;

timeout:
	NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
	return -1;
Linus Torvalds's avatar
Linus Torvalds committed
}

#if defined(REAL_DMA)
/*
 * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
Linus Torvalds's avatar
Linus Torvalds committed
 *      unsigned char *phase, int *count, unsigned char **data)
 *
 * Purpose : transfers data in given phase using either real
 *	or pseudo DMA.
 *
 * Inputs : instance - instance of driver, *phase - pointer to
 *	what phase is expected, *count - pointer to number of
Linus Torvalds's avatar
Linus Torvalds committed
 *	bytes to transfer, **data - pointer to data pointer.
Linus Torvalds's avatar
Linus Torvalds committed
 * Returns : -1 when different phase is entered without transferring
Lucas De Marchi's avatar
Lucas De Marchi committed
 *	maximum number of bytes, 0 if all bytes or transferred or exit
Linus Torvalds's avatar
Linus Torvalds committed
 *	is in same phase.
 *
 *	Also, *phase, *count, *data are modified in place.
static int NCR5380_transfer_dma(struct Scsi_Host *instance,
				unsigned char *phase, int *count,
				unsigned char **data)
Linus Torvalds's avatar
Linus Torvalds committed
{
	SETUP_HOSTDATA(instance);
	register int c = *count;
	register unsigned char p = *phase;
	unsigned long flags;

#if defined(CONFIG_SUN3)
	/* sanity check */
	if (!sun3_dma_setup_done) {
		pr_err("scsi%d: transfer_dma without setup!\n",
		       instance->host_no);
		BUG();
	}
	hostdata->dma_len = c;

	dprintk(NDEBUG_DMA, "scsi%d: initializing DMA for %s, %d bytes %s %p\n",
		instance->host_no, (p & SR_IO) ? "reading" : "writing",
		c, (p & SR_IO) ? "to" : "from", *data);

	/* netbsd turns off ints here, why not be safe and do it too */
	local_irq_save(flags);

	/* send start chain */
	sun3scsi_dma_start(c, *data);

	if (p & SR_IO) {
		NCR5380_write(TARGET_COMMAND_REG, 1);
		NCR5380_read(RESET_PARITY_INTERRUPT_REG);
		NCR5380_write(INITIATOR_COMMAND_REG, 0);
		NCR5380_write(MODE_REG,
			      (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
		NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
	} else {
		NCR5380_write(TARGET_COMMAND_REG, 0);
		NCR5380_read(RESET_PARITY_INTERRUPT_REG);
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_DATA);
		NCR5380_write(MODE_REG,
			      (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
		NCR5380_write(START_DMA_SEND_REG, 0);
	}

#ifdef SUN3_SCSI_VME
	dregs->csr |= CSR_DMA_ENABLE;
#endif

	local_irq_restore(flags);

	sun3_dma_active = 1;

#else /* !defined(CONFIG_SUN3) */
	register unsigned char *d = *data;
	unsigned char tmp;

	if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
		*phase = tmp;
		return -1;
	}
Linus Torvalds's avatar
Linus Torvalds committed

	if (hostdata->read_overruns && (p & SR_IO))
		c -= hostdata->read_overruns;
Linus Torvalds's avatar
Linus Torvalds committed

	dprintk(NDEBUG_DMA, "scsi%d: initializing DMA for %s, %d bytes %s %p\n",
		   HOSTNO, (p & SR_IO) ? "reading" : "writing",
		   c, (p & SR_IO) ? "to" : "from", d);
Linus Torvalds's avatar
Linus Torvalds committed

	NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
Linus Torvalds's avatar
Linus Torvalds committed

#ifdef REAL_DMA
	NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
Linus Torvalds's avatar
Linus Torvalds committed
#endif /* def REAL_DMA  */

	if (!(hostdata->flags & FLAG_LATE_DMA_SETUP)) {
		/* On the Medusa, it is a must to initialize the DMA before
		 * starting the NCR. This is also the cleaner way for the TT.
		 */
		local_irq_save(flags);
		hostdata->dma_len = (p & SR_IO) ?
			NCR5380_dma_read_setup(instance, d, c) :
			NCR5380_dma_write_setup(instance, d, c);
		local_irq_restore(flags);
	}

	if (p & SR_IO)
		NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
	else {
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
		NCR5380_write(START_DMA_SEND_REG, 0);
	}

	if (hostdata->flags & FLAG_LATE_DMA_SETUP) {
		/* On the Falcon, the DMA setup must be done after the last */
		/* NCR access, else the DMA setup gets trashed!
		 */
		local_irq_save(flags);
		hostdata->dma_len = (p & SR_IO) ?
			NCR5380_dma_read_setup(instance, d, c) :
			NCR5380_dma_write_setup(instance, d, c);
		local_irq_restore(flags);
	}
#endif /* !defined(CONFIG_SUN3) */

Linus Torvalds's avatar
Linus Torvalds committed
}
#endif /* defined(REAL_DMA) */

/*
 * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
 *
 * Purpose : run through the various SCSI phases and do as the target
 *	directs us to.  Operates on the currently connected command,
Linus Torvalds's avatar
Linus Torvalds committed
 *	instance->connected.
 *
 * Inputs : instance, instance for which we are doing commands
 *
 * Side effects : SCSI things happen, the disconnected queue will be
Linus Torvalds's avatar
Linus Torvalds committed
 *	modified if a command disconnects, *instance->connected will
 *	change.
 *
 * XXX Note : we need to watch for bus free or a reset condition here
 *	to recover from an unexpected bus free condition.
Linus Torvalds's avatar
Linus Torvalds committed
 */

static void NCR5380_information_transfer(struct Scsi_Host *instance)
Linus Torvalds's avatar
Linus Torvalds committed
{
	SETUP_HOSTDATA(instance);
	unsigned long flags;
	unsigned char msgout = NOP;
	int sink = 0;
	int len;
Linus Torvalds's avatar
Linus Torvalds committed
#if defined(REAL_DMA)
	int transfersize;
Linus Torvalds's avatar
Linus Torvalds committed
#endif
	unsigned char *data;
	unsigned char phase, tmp, extended_msg[10], old_phase = 0xff;
	struct scsi_cmnd *cmd = (struct scsi_cmnd *) hostdata->connected;
#ifdef SUN3_SCSI_VME
	dregs->csr |= CSR_INTR;
#endif

	while (1) {
		tmp = NCR5380_read(STATUS_REG);
		/* We only have a valid SCSI phase when REQ is asserted */
		if (tmp & SR_REQ) {
			phase = (tmp & PHASE_MASK);
			if (phase != old_phase) {
				old_phase = phase;
				NCR5380_dprint_phase(NDEBUG_INFORMATION, instance);
#if defined(CONFIG_SUN3)
			if (phase == PHASE_CMDOUT) {
#if defined(REAL_DMA)