742 lines
20 KiB
C
742 lines
20 KiB
C
/* Written by Phil Nelson for the pc532. Used source with the following
|
|
* copyrights as a model.
|
|
*
|
|
* dp.c: A NCR DP8490 driver for the pc532.
|
|
*
|
|
* dp.c,v 1.2 1993/10/01 22:59:31 phil Exp
|
|
*/
|
|
|
|
/*
|
|
* (Mostly) Written by Julian Elischer (julian@tfs.com)
|
|
* for TRW Financial Systems for use under the MACH(2.5) operating system.
|
|
*
|
|
* TRW Financial Systems, in accordance with their agreement with Carnegie
|
|
* Mellon University, makes this software available to CMU to distribute
|
|
* or use in any manner that they see fit as long as this message is kept with
|
|
* the software. For this reason TFS also grants any other persons or
|
|
* organisations permission to use or modify this software.
|
|
*
|
|
* TFS supplies this software to be publicly redistributed
|
|
* on the understanding that TFS is not responsible for the correct
|
|
* functioning of this software in any circumstances.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* a FEW lines in this driver come from a MACH adaptec-disk driver
|
|
* so the copyright below is included:
|
|
*
|
|
* Copyright 1990 by Open Software Foundation,
|
|
* Grenoble, FRANCE
|
|
*
|
|
* All Rights Reserved
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software and
|
|
* its documentation for any purpose and without fee is hereby granted,
|
|
* provided that the above copyright notice appears in all copies and
|
|
* that both the copyright notice and this permission notice appear in
|
|
* supporting documentation, and that the name of OSF or Open Software
|
|
* Foundation not be used in advertising or publicity pertaining to
|
|
* distribution of the software without specific, written prior
|
|
* permission.
|
|
*
|
|
* OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
|
|
* IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
|
|
* NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
|
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "sys/types.h"
|
|
#include "sys/param.h"
|
|
#include "sys/systm.h"
|
|
#include "sys/errno.h"
|
|
#include "sys/ioctl.h"
|
|
#include "sys/buf.h"
|
|
#include "machine/stdarg.h"
|
|
#include "sys/proc.h"
|
|
#include "sys/user.h"
|
|
#include "sys/dkbad.h"
|
|
#include "sys/disklabel.h"
|
|
#include "scsi/scsi_all.h"
|
|
#include "scsi/scsiconf.h"
|
|
|
|
#include "machine/frame.h"
|
|
#include "device.h"
|
|
#include "dpreg.h"
|
|
|
|
#include "../pc532/icu.h"
|
|
|
|
/* Some constants (may need to be changed!) */
|
|
#define DP_NSEG 16
|
|
|
|
int dpprobe(struct pc532_device *);
|
|
int dpattach(struct pc532_device *);
|
|
int dp_scsi_cmd(struct scsi_xfer *);
|
|
void dpminphys(struct buf *);
|
|
long int dp_adapter_info(int);
|
|
void dp_intr(void);
|
|
|
|
struct scsidevs *
|
|
scsi_probe(int masunit, struct scsi_switch *sw, int physid, int type, int want);
|
|
|
|
struct pc532_driver dpdriver = {
|
|
dpprobe, dpattach, "dp",
|
|
};
|
|
|
|
struct scsi_switch dp_switch = {
|
|
"dp",
|
|
dp_scsi_cmd,
|
|
dpminphys,
|
|
0,
|
|
0,
|
|
dp_adapter_info,
|
|
0, 0, 0
|
|
};
|
|
|
|
/* Sense command. */
|
|
u_char sense_cmd[] = { 3, 0, 0, 0, sizeof(struct scsi_sense_data), 0};
|
|
|
|
/* Do we need to initialize. */
|
|
int dp_needs_init = 1;
|
|
|
|
/* Do we need a reset . */
|
|
int dp_needs_reset = 1;
|
|
|
|
/* SCSI phase we are currently in . . . */
|
|
int dp_scsi_phase;
|
|
|
|
/* SCSI Driver state */
|
|
int dp_dvr_state = DP_DVR_READY;
|
|
|
|
/* For polled error reporting. */
|
|
int dp_intr_retval;
|
|
|
|
/* For counting the retries. */
|
|
int dp_try_count;
|
|
|
|
/* Give the interrupt routine access to the current scsi_xfer info block. */
|
|
struct scsi_xfer *cur_xs = NULL;
|
|
|
|
/* Initial probe for a device. If it is using the dp controller,
|
|
just say yes so that attach can be the one to find the real drive. */
|
|
|
|
int dpprobe(struct pc532_device *dvp)
|
|
{
|
|
/* If we call this, we need to add SPL_DP to the bio mask! */
|
|
PL_bio |= SPL_DP;
|
|
PL_zero |= PL_bio;
|
|
|
|
if (dp_needs_init)
|
|
dp_initialize();
|
|
|
|
if (dp_needs_reset)
|
|
dp_reset();
|
|
|
|
/* All pc532s should have one, so we don't check ! :) */
|
|
return (1);
|
|
}
|
|
|
|
|
|
int dpattach(struct pc532_device *dvp)
|
|
{
|
|
int r;
|
|
|
|
r = scsi_attach(0, 7, &dp_switch,
|
|
&dvp->pd_drive, &dvp->pd_unit, dvp->pd_flags);
|
|
|
|
return(r);
|
|
}
|
|
|
|
void dpminphys(struct buf *bp)
|
|
{
|
|
if(bp->b_bcount > ((DP_NSEG - 1) * NBPG))
|
|
bp->b_bcount = ((DP_NSEG - 1) * NBPG);
|
|
}
|
|
|
|
long int dp_adapter_info(int unit)
|
|
{
|
|
return (1); /* only 1 outstanding request. */
|
|
}
|
|
|
|
|
|
/* Do a scsi command. */
|
|
|
|
int dp_scsi_cmd(struct scsi_xfer *xs)
|
|
{
|
|
struct iovec *iovp;
|
|
int flags;
|
|
int retval; /* Return values from functions. */
|
|
int x; /* for splbio() & splhigh() */
|
|
int dvr_state;
|
|
int ti_val; /* For timeouts. */
|
|
|
|
x = splhigh();
|
|
dvr_state = dp_dvr_state;
|
|
if (dvr_state == DP_DVR_READY)
|
|
dvr_state = dp_dvr_state = DP_DVR_STARTED;
|
|
splx(x);
|
|
|
|
if (dvr_state != DP_DVR_STARTED)
|
|
return (TRY_AGAIN_LATER);
|
|
|
|
cur_xs = xs;
|
|
|
|
/* Some initial checks. */
|
|
flags = xs->flags;
|
|
if (!(flags & INUSE)) {
|
|
printf("dp xs not in use!\n");
|
|
xs->flags |= INUSE;
|
|
}
|
|
if (flags & ITSDONE) {
|
|
printf("dp xs already done!\n");
|
|
xs->flags &= ~ITSDONE;
|
|
}
|
|
|
|
if (dp_needs_reset)
|
|
dp_reset();
|
|
|
|
/* info ... */
|
|
printf ("\nscsi_cmd: flags=0x%x, targ=%d, lu=%d, cmdlen=%d, cmd=%x\n",
|
|
xs->flags, xs->targ, xs->lu, xs->cmdlen, xs->cmd->opcode);
|
|
|
|
retval = dp_start_cmd (xs);
|
|
|
|
if (retval == SUCCESSFULLY_QUEUED && (xs->flags & SCSI_NOMASK)) {
|
|
/* No interrupts available! */
|
|
ti_val = WAIT_MUL * xs->timeout;
|
|
while (dp_dvr_state != DP_DVR_READY) {
|
|
if (RD_ADR(u_char, DP_STAT2) & DP_S_IRQ) {
|
|
dp_intr();
|
|
ti_val = WAIT_MUL * xs->timeout;
|
|
}
|
|
if (--ti_val == 0) {
|
|
/* Software Timeout! */
|
|
printf ("scsi timeout! stat1=0x%x stat2=0x%x\n",
|
|
RD_ADR(u_char,DP_STAT1), RD_ADR(u_char,DP_STAT2));
|
|
dp_reset();
|
|
xs->error = XS_SWTIMEOUT;
|
|
dp_dvr_state = DP_DVR_READY;
|
|
return HAD_ERROR;
|
|
}
|
|
}
|
|
retval = dp_intr_retval;
|
|
}
|
|
|
|
return (retval);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* dp_intr *
|
|
*===========================================================================*/
|
|
|
|
/* This is where a lot of the work happens! This is called in non-interrupt
|
|
mode when an interrupt would have happened. It is also the real interrupt
|
|
routine. It uses dp_dvr_state to determine the next actions along with
|
|
cur_xs->flags. */
|
|
|
|
void dp_intr (void)
|
|
{
|
|
u_char isr;
|
|
u_char new_phase;
|
|
u_char status;
|
|
u_char stat1;
|
|
u_char stat2;
|
|
u_char message;
|
|
int ret;
|
|
|
|
scsi_select_ctlr (DP8490);
|
|
WR_ADR(u_char, DP_EMR_ISR, DP_EF_ISR_NEXT);
|
|
isr = RD_ADR (u_char, DP_EMR_ISR);
|
|
dp_clear_isr();
|
|
|
|
|
|
if (isr & DP_ISR_BSYERR) {
|
|
printf ("Busy error?\n");
|
|
}
|
|
|
|
if (!(isr & DP_ISR_APHS)) {
|
|
printf ("Not an APHS!\n");
|
|
return;
|
|
}
|
|
|
|
stat1 = RD_ADR(u_char, DP_STAT1);
|
|
new_phase = (stat1 >> 2) & 7;
|
|
|
|
printf ("dp_intr dvr_state %d isr=0x%x new_phase = 0x%x stat1 = 0x%x\n",
|
|
dp_dvr_state, isr, new_phase, stat1);
|
|
|
|
switch (dp_dvr_state) {
|
|
|
|
case DP_DVR_ARB: /* Next comes the command phase! */
|
|
if (new_phase != DP_PHASE_CMD) {
|
|
printf ("Phase mismatch cmd!\n");
|
|
goto phase_mismatch;
|
|
}
|
|
dp_dvr_state = DP_DVR_CMD;
|
|
ret = dp_pdma_out (cur_xs->cmd, cur_xs->cmdlen, DP_PHASE_CMD);
|
|
dp_clear_isr();
|
|
break;
|
|
|
|
case DP_DVR_CMD: /* Next comes the data i/o phase if needed. */
|
|
/*
|
|
* This state can potentially accept data in, data out,
|
|
* or status for new_phase. data in or data out could
|
|
* be skipped (new_phase is status) if an error was detected
|
|
* in the command.
|
|
*/
|
|
if (cur_xs->flags & SCSI_DATA_UIO) {
|
|
/* UIO work. */
|
|
panic ("scsi uio");
|
|
}
|
|
if (new_phase == DP_PHASE_DATAI) {
|
|
if (!(cur_xs->flags & SCSI_DATA_IN)) {
|
|
printf ("Phase mismatch in.\n");
|
|
goto phase_mismatch;
|
|
}
|
|
/* expect STAT phase next */
|
|
dp_dvr_state = DP_DVR_DATA;
|
|
ret = dp_pdma_in (cur_xs->data, cur_xs->datalen,
|
|
DP_PHASE_DATAI);
|
|
dp_clear_isr();
|
|
break;
|
|
}
|
|
else if (new_phase == DP_PHASE_DATAO) {
|
|
if (!(cur_xs->flags & SCSI_DATA_OUT)) {
|
|
printf ("Phase mismatch out.\n");
|
|
goto phase_mismatch;
|
|
}
|
|
/* expect STAT phase next */
|
|
dp_dvr_state = DP_DVR_DATA;
|
|
ret = dp_pdma_out (cur_xs->data, cur_xs->datalen,
|
|
DP_PHASE_DATAO);
|
|
dp_clear_isr();
|
|
break;
|
|
}
|
|
/* Fall through to next phase. */
|
|
case DP_DVR_DATA: /* Next comes the stat phase */
|
|
if (new_phase != DP_PHASE_STATUS) {
|
|
printf ("Phase mismatch stat.\n");
|
|
goto phase_mismatch;
|
|
}
|
|
dp_dvr_state = DP_DVR_STAT;
|
|
dp_pdma_in (&status, 1, DP_PHASE_STATUS);
|
|
dp_clear_isr();
|
|
while (!((stat2 = RD_ADR(u_char, DP_STAT2)) & DP_S_IRQ))
|
|
;
|
|
stat1 = RD_ADR(u_char, DP_STAT1);
|
|
new_phase = (stat1 >> 2) & 7;
|
|
if (new_phase != DP_PHASE_MSGI) {
|
|
printf ("msgi phase mismatch\n");
|
|
}
|
|
dp_pdma_in (&message, 1, DP_PHASE_MSGI);
|
|
dp_clear_isr();
|
|
while (!((stat2 = RD_ADR(u_char, DP_STAT2)) & DP_S_IRQ))
|
|
;
|
|
dp_clear_isr();
|
|
printf ("status = 0x%x, message = 0x%x\n", status, message);
|
|
if (status != SCSI_OK && dp_try_count < cur_xs->retries) {
|
|
printf ("dp_intr: retry: dp_try_count = %d\n", dp_try_count);
|
|
dp_restart_cmd();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
phase_mismatch:
|
|
/* TEMP error generation!!! */
|
|
dp_reset();
|
|
dp_dvr_state = DP_DVR_READY;
|
|
cur_xs->error = XS_DRIVER_STUFFUP;
|
|
dp_intr_retval = HAD_ERROR;
|
|
/* If this true interrupt code, call the done routine. */
|
|
if (cur_xs->when_done) {
|
|
(*(cur_xs->when_done))(cur_xs->done_arg, cur_xs->done_arg2);
|
|
}
|
|
}
|
|
|
|
if (dp_dvr_state == DP_DVR_STAT) {
|
|
printf ("dvr_stat: dp_try_count = %d\n", dp_try_count);
|
|
WR_ADR (u_char, DP_MODE, 0); /* Turn off monbsy, dma, ... */
|
|
if (status == SCSI_OK) {
|
|
cur_xs->error = XS_NOERROR;
|
|
dp_intr_retval = COMPLETE;
|
|
} else if (status & SCSI_BUSY) {
|
|
cur_xs->error = XS_BUSY;
|
|
dp_intr_retval = HAD_ERROR;
|
|
} else if (status & SCSI_CHECK) {
|
|
/* Do a sense command. */
|
|
cur_xs->error = XS_SENSE;
|
|
dp_intr_retval = HAD_ERROR;
|
|
dp_get_sense (cur_xs);
|
|
}
|
|
cur_xs->flags |= ITSDONE;
|
|
dp_dvr_state = DP_DVR_READY;
|
|
/* If this true interrupt code, call the done routine. */
|
|
if (cur_xs->when_done) {
|
|
printf("dp_intr: calling when_done\n");
|
|
(*(cur_xs->when_done))(cur_xs->done_arg, cur_xs->done_arg2);
|
|
}
|
|
}
|
|
printf ("exit dp_intr.\n");
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* dp_initialize *
|
|
*===========================================================================*/
|
|
|
|
dp_initialize()
|
|
{
|
|
printf("dp_initialize()\n");
|
|
scsi_select_ctlr (DP8490);
|
|
WR_ADR (u_char, DP_ICMD, DP_EMODE); /* Set Enhanced mode */
|
|
WR_ADR (u_char, DP_MODE, 0); /* Disable everything. */
|
|
WR_ADR (u_char, DP_EMR_ISR, DP_EF_RESETIP);
|
|
WR_ADR (u_char, DP_EMR_ISR, DP_EF_NOP);
|
|
WR_ADR (u_char, DP_SER, 0x80); /* scsi adr 7. */
|
|
dp_scsi_phase = DP_PHASE_NONE;
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* dp_reset *
|
|
*===========================================================================*/
|
|
|
|
/*
|
|
* Reset dp SCSI bus.
|
|
*/
|
|
|
|
dp_reset()
|
|
{
|
|
volatile int i;
|
|
int x = splbio();
|
|
|
|
scsi_select_ctlr (DP8490);
|
|
WR_ADR (u_char, DP_MODE, 0); /* get into harmless state */
|
|
WR_ADR (u_char, DP_OUTDATA, 0);
|
|
WR_ADR (u_char, DP_ICMD, DP_A_RST|DP_EMODE); /* assert RST on SCSI bus */
|
|
for (i = 55; i; --i); /* wait 25 usec */
|
|
WR_ADR (u_char, DP_ICMD, DP_EMODE); /* deassert RST, get off bus */
|
|
WR_ADR (u_char, DP_EMR_ISR, DP_EF_ISR_NEXT | DP_EMR_APHS);
|
|
WR_ADR (u_char, DP_EMR_ISR, DP_INT_MASK); /* set interrupt mask */
|
|
splx(x);
|
|
for (i = 800000; i; --i); /* wait 360 msec */
|
|
dp_needs_reset = 0;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dp_wait_bus_free *
|
|
*===========================================================================*/
|
|
/* Wait for the SCSI bus to become free. Currently polled because I am
|
|
* assuming a single initiator configuration -- so this code would not be
|
|
* running if the bus were busy.
|
|
*/
|
|
int
|
|
dp_wait_bus_free()
|
|
{
|
|
int i;
|
|
u_char stat1;
|
|
volatile int j;
|
|
|
|
/* get into a harmless state */
|
|
WR_ADR (u_char, DP_TCMD, 0);
|
|
WR_ADR (u_char, DP_MODE, 0); /* return to initiator mode */
|
|
WR_ADR (u_char, DP_ICMD, DP_EMODE); /* clear SEL, disable data out */
|
|
i = WAIT_MUL * 2000;
|
|
while (i--) {
|
|
/* Must be clear for 2 usec, so read twice */
|
|
stat1 = RD_ADR (u_char, DP_STAT1);
|
|
if (stat1 & (DP_S_BSY | DP_S_SEL)) continue;
|
|
for (j = 5; j; j--);
|
|
stat1 = RD_ADR (u_char, DP_STAT1);
|
|
if (stat1 & (DP_S_BSY | DP_S_SEL)) continue;
|
|
return OK;
|
|
}
|
|
printf("wait bus free failed, stat1 = 0x%x\n", stat1);
|
|
dp_needs_reset = 1;
|
|
return NOT_OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dp_select *
|
|
*===========================================================================*/
|
|
/* Select SCSI device, set up for command transfer.
|
|
*/
|
|
int
|
|
dp_select(adr)
|
|
long adr;
|
|
{
|
|
int i, stat1;
|
|
|
|
printf("dp_select(0x%x)\n", adr);
|
|
WR_ADR (u_char, DP_TCMD, 0); /* get to harmless state */
|
|
WR_ADR (u_char, DP_MODE, 0); /* get to harmless state */
|
|
WR_ADR (u_char, DP_OUTDATA, adr); /* SCSI bus address */
|
|
WR_ADR (u_char, DP_ICMD, DP_A_SEL | DP_ENABLE_DB | DP_EMODE);
|
|
for (i = 0;; ++i) { /* wait for target to assert SEL */
|
|
stat1 = RD_ADR (u_char, DP_STAT1);
|
|
if (stat1 & DP_S_BSY) break; /* select successful */
|
|
if (i > WAIT_MUL * 2000) { /* timeout */
|
|
u_char isr;
|
|
WR_ADR(u_char, DP_EMR_ISR, DP_EF_ISR_NEXT);
|
|
isr = RD_ADR (u_char, DP_EMR_ISR);
|
|
printf ("SCSI: SELECT timeout adr %d\n", adr);
|
|
printf("STAT1 = 0x%x ICMD = 0x%x isr = 0x%x\n", stat1,
|
|
RD_ADR(u_char, DP_ICMD), isr);
|
|
dp_reset();
|
|
return NOT_OK;
|
|
}
|
|
}
|
|
WR_ADR (u_char, DP_ICMD, DP_EMODE); /* clear SEL, disable data out */
|
|
WR_ADR (u_char, DP_OUTDATA, 0);
|
|
dp_clear_isr();
|
|
WR_ADR (u_char, DP_TCMD, 4); /* bogus phase, guarantee mismatch */
|
|
WR_ADR (u_char, DP_MODE, DP_M_BSY | DP_M_DMA);
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* scsi_select_ctlr
|
|
*===========================================================================*/
|
|
/* Select a SCSI device.
|
|
*/
|
|
scsi_select_ctlr (ctlr)
|
|
int ctlr;
|
|
{
|
|
/* May need other stuff here to syncronize between dp & aic. */
|
|
|
|
RD_ADR (u_char, ICU_IO) &= ~ICU_SCSI_BIT; /* i/o, not port */
|
|
RD_ADR (u_char, ICU_DIR) &= ~ICU_SCSI_BIT; /* output */
|
|
if (ctlr == DP8490)
|
|
RD_ADR (u_char, ICU_DATA) &= ~ICU_SCSI_BIT; /* select = 0 for 8490 */
|
|
else
|
|
RD_ADR (u_char, ICU_DATA) |= ICU_SCSI_BIT; /* select = 1 for AIC6250 */
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dp_start_cmd *
|
|
*===========================================================================*/
|
|
|
|
int dp_start_cmd(struct scsi_xfer *xs)
|
|
{
|
|
#if 0
|
|
WR_ADR (u_char, DP_OUTDATA, 1 << xs->targ); /* SCSI bus address */
|
|
WR_ADR (u_char, DP_EMR_ISR, DP_EF_ARB);
|
|
dp_dvr_state = DP_DVR_ARB;
|
|
#else
|
|
|
|
/* This is not the "right" way to start it. We should just have the
|
|
chip do the select for us and interrupt at the end. */
|
|
|
|
if (!dp_wait_bus_free()) {
|
|
xs->error = XS_BUSY;
|
|
return TRY_AGAIN_LATER;
|
|
}
|
|
|
|
if (!dp_select (1 << xs->targ)) {
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
return HAD_ERROR;
|
|
}
|
|
#endif
|
|
|
|
/* After selection, we now wait for the APHS interrupt! */
|
|
dp_dvr_state = DP_DVR_ARB; /* Just finished the select/arbitration */
|
|
dp_try_count = 1;
|
|
|
|
if (!(xs->flags & SCSI_NOMASK)) {
|
|
/* Set up the timeout! */
|
|
printf ("dp timeouts not done\n");
|
|
}
|
|
return SUCCESSFULLY_QUEUED;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dp_restart_cmd *
|
|
*===========================================================================*/
|
|
|
|
int dp_restart_cmd()
|
|
{
|
|
#if 0
|
|
WR_ADR (u_char, DP_OUTDATA, xs->targ); /* SCSI bus address */
|
|
WR_ADR (u_char, DP_EMR_ISR, DP_EF_ARB);
|
|
dp_dvr_state = DP_DVR_ARB;
|
|
#endif
|
|
|
|
/* This is not the "right" way to start it. We should just have the
|
|
chip do the select for us and interrupt at the end. */
|
|
|
|
DELAY(50);
|
|
printf ("restart .. stat1=0x%x stat2=0x%x\n", RD_ADR(u_char, DP_STAT1),
|
|
RD_ADR(u_char, DP_STAT2));
|
|
if (!dp_wait_bus_free()) {
|
|
cur_xs->error = XS_BUSY;
|
|
return;
|
|
}
|
|
|
|
printf ("restart .1 stat1=0x%x stat2=0x%x\n", RD_ADR(u_char, DP_STAT1),
|
|
RD_ADR(u_char, DP_STAT2));
|
|
printf ("cur_xs->targ=%d\n",cur_xs->targ);
|
|
if (!dp_select (1 << cur_xs->targ)) {
|
|
cur_xs->error = XS_DRIVER_STUFFUP;
|
|
return;
|
|
}
|
|
|
|
printf ("restart .2 stat1=0x%x stat2=0x%x\n", RD_ADR(u_char, DP_STAT1),
|
|
RD_ADR(u_char, DP_STAT2));
|
|
/* After selection, we now wait for the APHS interrupt! */
|
|
dp_dvr_state = DP_DVR_ARB; /* Just finished the select/arbitration */
|
|
dp_try_count++;
|
|
|
|
if (!(cur_xs->flags & SCSI_NOMASK)) {
|
|
/* Set up the timeout! */
|
|
printf ("dp timeouts not done\n");
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dp_pdma_out *
|
|
*===========================================================================*/
|
|
|
|
/* Note: in NetBSD, the scsi dma addresses are set by the mapping hardware
|
|
to inhibit cache. There is therefore, no need to worry about cache hits
|
|
during access to dma addresses. */
|
|
|
|
int dp_pdma_out(char *buf, int count, int phase)
|
|
{
|
|
u_int stat2;
|
|
|
|
/* Set it up. */
|
|
WR_ADR(u_char, DP_TCMD, phase);
|
|
RD_ADR(u_char, DP_MODE) |= DP_M_DMA;
|
|
WR_ADR(u_char, DP_ICMD, DP_ENABLE_DB | DP_EMODE);
|
|
WR_ADR(u_char, DP_START_SEND, 0);
|
|
|
|
/* Do the pdma */
|
|
while (count > sizeof(long)) {
|
|
WR_ADR(long, DP_DMA, *(((long *)buf)++));
|
|
count -= sizeof(long);
|
|
}
|
|
|
|
/* All but the last byte. */
|
|
while (count-- > 1) {
|
|
WR_ADR(u_char, DP_DMA, *(buf++));
|
|
}
|
|
|
|
while (1) {
|
|
stat2 = RD_ADR(u_char, DP_STAT2);
|
|
if (stat2 & (DP_S_IRQ | DP_S_DRQ)) break;
|
|
}
|
|
|
|
if (stat2 & DP_S_DRQ)
|
|
WR_ADR(u_char, DP_DMA_EOP, *buf);
|
|
else {
|
|
/* dma error! */
|
|
printf ("dma write error!\n");
|
|
cur_xs->error = XS_DRIVER_STUFFUP;
|
|
/* Clear dma mode, just in case, and disable the bus. */
|
|
RD_ADR (u_char, DP_MODE) &= ~DP_M_DMA;
|
|
WR_ADR (u_char, DP_ICMD, DP_EMODE);
|
|
return NOT_OK;
|
|
}
|
|
|
|
/* Clear dma mode, just in case, and disable the bus. */
|
|
RD_ADR (u_char, DP_MODE) &= ~DP_M_DMA;
|
|
WR_ADR (u_char, DP_ICMD, DP_EMODE);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dp_pdma_in *
|
|
*===========================================================================*/
|
|
|
|
/* Note: in NetBSD, the scsi dma addresses are set by the mapping hardware
|
|
to inhibit cache. There is therefore, no need to worry about cache hits
|
|
during access to dma addresses. */
|
|
|
|
int dp_pdma_in(char *buf, int count, int phase)
|
|
{
|
|
u_char *dma_adr = (u_char *) DP_DMA; /* Address for last few bytes. */
|
|
|
|
/* Set it up. */
|
|
WR_ADR(u_char, DP_TCMD, phase);
|
|
RD_ADR(u_char, DP_MODE) |= DP_M_DMA;
|
|
WR_ADR(u_char, DP_EMR_ISR, DP_EF_START_RCV | DP_EMR_APHS);
|
|
|
|
/* Do the pdma */
|
|
while (count >= sizeof(long)) {
|
|
*(((long *)buf)++) = RD_ADR(long, DP_DMA);
|
|
count -= sizeof(long);
|
|
}
|
|
|
|
while (count-- > 0) {
|
|
*(buf++) = RD_ADR(u_char, (dma_adr++));
|
|
}
|
|
|
|
/* Clear dma mode, just in case, and disable the bus. */
|
|
RD_ADR (u_char, DP_MODE) &= ~DP_M_DMA;
|
|
WR_ADR (u_char, DP_ICMD, DP_EMODE);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dp_get_sense *
|
|
*===========================================================================*/
|
|
|
|
dp_get_sense (struct scsi_xfer *xs)
|
|
{
|
|
u_char status;
|
|
u_char message;
|
|
int ret;
|
|
|
|
printf ("sense 1.\n");
|
|
if (!dp_wait_bus_free()) {
|
|
xs->error = XS_BUSY;
|
|
return;
|
|
}
|
|
|
|
printf ("sense 2.\n");
|
|
if (!dp_select (1 << xs->targ)) {
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
return;
|
|
}
|
|
|
|
printf ("sense 3.\n");
|
|
sense_cmd[1] = xs->lu << 5;
|
|
ret = dp_pdma_out (sense_cmd, 6, DP_PHASE_CMD);
|
|
printf ("dp_pdma_out ret=%d\n", ret);
|
|
dp_clear_isr();
|
|
|
|
printf ("sense 4. ");
|
|
printf ("stat1=0x%x stat2=0x%x\n", RD_ADR(u_char, DP_STAT1),
|
|
RD_ADR(u_char, DP_STAT2));
|
|
while (!(RD_ADR(u_char, DP_STAT2) & DP_S_IRQ)) /* wait */;
|
|
|
|
printf ("sense 5.\n");
|
|
dp_pdma_in ((u_char *)&xs->sense, sizeof(struct scsi_sense_data), DP_PHASE_DATAI);
|
|
dp_clear_isr();
|
|
|
|
printf ("sense 6.\n");
|
|
while (!(RD_ADR(u_char, DP_STAT2) & DP_S_IRQ)) ;
|
|
|
|
printf ("sense 7.\n");
|
|
dp_pdma_in (&status, 1, DP_PHASE_STATUS);
|
|
dp_clear_isr();
|
|
|
|
printf ("sense 8.\n");
|
|
while (!(RD_ADR(u_char, DP_STAT2) & DP_S_IRQ)) /* wait */;
|
|
dp_pdma_in (&message, 1, DP_PHASE_MSGI);
|
|
dp_clear_isr();
|
|
|
|
printf ("sense status = 0x%x\n", status);
|
|
|
|
if (status & SCSI_BUSY) {
|
|
xs->error = XS_BUSY;
|
|
}
|
|
WR_ADR (u_char, DP_MODE, 0); /* Turn off monbsy, dma, ... */
|
|
}
|