NetBSD/sys/arch/pc532/dev/dp.c

312 lines
8.4 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.
*/
/*
* (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.
*
* $Id: dp.c,v 1.1.1.1 1993/09/09 23:53:52 phil Exp $
*/
/*
* 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);
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
};
/* Do we need to initialize. */
int dp_needs_init = 1;
/* Do we need a reset . */
int dp_needs_reset = 1;
/* 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. */
struct scsi_xfer *cur_xs = NULL;
int dp_scsi_cmd(struct scsi_xfer *xs)
{
struct iovec *iovp;
int flags;
cur_xs = xs;
/* Some initial checks. */
flags = xs->flags;
if (!(flags & INUSE)) {
printf("dp xs not in use!");
xs->flags |= INUSE;
}
if (flags & ITSDONE) {
printf("dp xs already done!");
xs->flags &= ~ITSDONE;
}
if (dp_needs_reset)
dp_reset();
/* info ... */
printf ("flags=0x%x, targ=%d, lu=%d, cmdlen=%d, cmd=%x\n", xs->flags,
xs->targ, xs->lu, xs->cmdlen, xs->cmd->opcode);
if (xs->cmd->opcode == 0) return (COMPLETE);
xs->error = XS_DRIVER_STUFFUP;
return (HAD_ERROR);
}
/*===========================================================================*
* dp_intr *
*===========================================================================*/
void dp_intr (struct intrframe frame)
{
u_char stat1, stat2;
scsi_select_ctlr (DP8490);
stat1 = RD_ADR(u_char, DP_STAT1);
stat2 = RD_ADR(u_char, DP_STAT2);
printf ("dp_intr stat1=0x%x, stat2=0x%x\n", stat1, stat2);
dp_clear_isr();
}
/*===========================================================================*
* dp_initialize *
*===========================================================================*/
dp_initialize()
{
scsi_select_ctlr (DP8490);
WR_ADR (u_char, DP_ICMD, 0); /* Nothing happening. */
WR_ADR (u_char, DP_MODE, 0); /* Disable everything. */
RD_ADR (u_char, DP_EMR_ISR); /* Clear any pending interrupt. */
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_STAT2, 0x80); /* scsi adr 7. */
}
/*===========================================================================*
* 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 = MAX_WAIT;
volatile int j;
while (i--) {
/* Must be clear for 2 usec, so read twice */
if (RD_ADR (u_char, DP_STAT1) & (DP_S_BSY | DP_S_SEL)) continue;
for (j = 5; j; j--);
if (RD_ADR (u_char, DP_STAT1) & (DP_S_BSY | DP_S_SEL)) continue;
return OK;
}
dp_needs_reset = 0;
return NOT_OK;
}
/*===========================================================================*
* dp_select *
*===========================================================================*/
/* Select SCSI device, set up for command transfer.
*/
int
dp_select(adr)
long adr;
{
int i, stat1;
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 > MAX_WAIT) { /* timeout */
printf ("SCSI: SELECT timeout adr %d\n", adr);
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 processes.... */
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 */
}