⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sii.c

📁 早期freebsd实现
💻 C
📖 第 1 页 / 共 3 页
字号:
/*- * Copyright (c) 1992, 1993 *	The Regents of the University of California.  All rights reserved. * * This code is derived from software contributed to Berkeley by * Ralph Campbell and Rick Macklem. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *	@(#)sii.c	8.2 (Berkeley) 11/30/93 * * from: $Header: /sprite/src/kernel/dev/ds3100.md/RCS/devSII.c, *	v 9.2 89/09/14 13:37:41 jhh Exp $ SPRITE (DECWRL)"; */#include "sii.h"#if NSII > 0/* * SCSI interface driver */#include <sys/param.h>#include <sys/systm.h>#include <sys/dkstat.h>#include <sys/buf.h>#include <sys/conf.h>#include <sys/errno.h>#include <machine/machConst.h>#include <pmax/dev/device.h>#include <pmax/dev/scsi.h>#include <pmax/dev/siireg.h>#include <pmax/pmax/kn01.h>int	siiprobe();void	siistart();struct	driver siidriver = {	"sii", siiprobe, siistart, 0,};typedef struct scsi_state {	int	statusByte;	/* status byte returned during STATUS_PHASE */	int	dmaDataPhase;	/* which data phase to expect */	int	dmaCurPhase;	/* SCSI phase if DMA is in progress */	int	dmaPrevPhase;	/* SCSI phase of DMA suspended by disconnect */	u_short	*dmaAddr[2];	/* DMA buffer memory address */	int	dmaBufIndex;	/* which of the above is currently in use */	int	dmalen;		/* amount to transfer in this chunk */	int	cmdlen;		/* total remaining amount of cmd to transfer */	u_char	*cmd;		/* current pointer within scsicmd->cmd */	int	buflen;		/* total remaining amount of data to transfer */	char	*buf;		/* current pointer within scsicmd->buf */	u_short	flags;		/* see below */	u_short	prevComm;	/* command reg before disconnect */	u_short	dmaCtrl;	/* DMA control register if disconnect */	u_short	dmaAddrL;	/* DMA address register if disconnect */	u_short	dmaAddrH;	/* DMA address register if disconnect */	u_short	dmaCnt;		/* DMA count if disconnect */	u_short	dmaByte;	/* DMA byte if disconnect on odd boundary */	u_short	dmaReqAck;	/* DMA synchronous xfer offset or 0 if async */} State;/* state flags */#define FIRST_DMA	0x01	/* true if no data DMA started yet */#define SII_NCMD	7struct siisoftc {	SIIRegs	*sc_regs;		/* HW address of SII controller chip */	int	sc_flags;	int	sc_target;		/* target SCSI ID if connected */	ScsiCmd	*sc_cmd[SII_NCMD];	/* active command indexed by ID */	State	sc_st[SII_NCMD];	/* state info for each active command */} sii_softc[NSII];/*  * MACROS for timing out spin loops. * * Wait until expression is true. * * Control register bits can change at any time so when the CPU * reads a register, the bits might change and * invalidate the setup and hold times for the CPU. * This macro reads the register twice to be sure the value is stable. * *	args:	var 		- variable to save control register contents *		reg		- control register to read *		expr 		- expression to spin on *		spincount 	- maximum number of times through the loop *		cntr		- variable for number of tries */#define	SII_WAIT_UNTIL(var, reg, expr, spincount, cntr) {	\		register unsigned tmp = reg;			\		for (cntr = 0; cntr < spincount; cntr++) {	\			while (tmp != (var = reg))		\				tmp = var;			\			if (expr)				\				break;				\			if (cntr >= 100)			\				DELAY(100);			\		}						\	}#ifdef DEBUGint	sii_debug = 1;int	sii_debug_cmd;int	sii_debug_bn;int	sii_debug_sz;#define NLOG 16struct sii_log {	u_short	cstat;	u_short	dstat;	u_short	comm;	u_short	msg;	int	target;} sii_log[NLOG], *sii_logp = sii_log;#endifu_char	sii_buf[256];	/* used for extended messages */#define NORESET	0#define RESET	1#define NOWAIT	0#define WAIT	1/* define a safe address in the SCSI buffer for doing status & message DMA */#define SII_BUF_ADDR	(MACH_PHYS_TO_UNCACHED(KN01_SYS_SII_B_START) \		+ SII_MAX_DMA_XFER_LENGTH * 14)static void sii_Reset();static void sii_StartCmd();static void sii_CmdDone();static void sii_DoIntr();static void sii_StateChg();static void sii_DoSync();static void sii_StartDMA();static int sii_GetByte();/* * Test to see if device is present. * Return true if found and initialized ok. */siiprobe(cp)	register struct pmax_ctlr *cp;{	register struct siisoftc *sc;	register int i;	if (cp->pmax_unit >= NSII)		return (0);	sc = &sii_softc[cp->pmax_unit];	sc->sc_regs = (SIIRegs *)cp->pmax_addr;	sc->sc_flags = cp->pmax_flags;	sc->sc_target = -1;	/* no command active */	/*	 * Give each target its own DMA buffer region.	 * Make it big enough for 2 max transfers so we can ping pong buffers	 * while we copy the data.	 */	for (i = 0; i < SII_NCMD; i++) {		sc->sc_st[i].dmaAddr[0] = (u_short *)			MACH_PHYS_TO_UNCACHED(KN01_SYS_SII_B_START) +			2 * SII_MAX_DMA_XFER_LENGTH * i;		sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] +			SII_MAX_DMA_XFER_LENGTH;	}	printf("sii%d at nexus0 csr 0x%x\n", cp->pmax_unit, cp->pmax_addr);	sii_Reset(sc->sc_regs, RESET);	return (1);}/* * Start activity on a SCSI device. * We maintain information on each device separately since devices can * connect/disconnect during an operation. */voidsiistart(scsicmd)	register ScsiCmd *scsicmd;	/* command to start */{	register struct scsi_device *sdp = scsicmd->sd;	register struct siisoftc *sc = &sii_softc[sdp->sd_ctlr];	int s;	s = splbio();	/*	 * Check if another command is already in progress.	 * We may have to change this if we allow SCSI devices with	 * separate LUNs.	 */	if (sc->sc_cmd[sdp->sd_drive]) {		printf("sii%d: device %s busy at start\n", sdp->sd_ctlr,			sdp->sd_driver->d_name);		(*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY,			scsicmd->buflen, 0);		splx(s);	}	sc->sc_cmd[sdp->sd_drive] = scsicmd;	sii_StartCmd(sc, sdp->sd_drive);	splx(s);}/* * Check to see if any SII chips have pending interrupts * and process as appropriate. */voidsiiintr(unit)	int unit;{	register struct siisoftc *sc = &sii_softc[unit];	unsigned dstat;	/*	 * Find which controller caused the interrupt.	 */	dstat = sc->sc_regs->dstat;	if (dstat & (SII_CI | SII_DI))		sii_DoIntr(sc, dstat);}/* * Reset the SII chip and do a SCSI reset if 'reset' is true. * NOTE: if !cold && reset, should probably probe for devices * since a SCSI bus reset will set UNIT_ATTENTION. */static voidsii_Reset(regs, reset)	register SIIRegs *regs;	int reset;				/* TRUE => reset SCSI bus */{#ifdef DEBUG	if (sii_debug > 1)		printf("sii: RESET\n");#endif	/*	 * Reset the SII chip.	 */	regs->comm = SII_CHRESET;	/*	 * Set arbitrated bus mode.	 */	regs->csr = SII_HPM;	/*	 * SII is always ID 7.	 */	regs->id = SII_ID_IO | 7;	/*	 * Enable SII to drive the SCSI bus.	 */	regs->dictrl = SII_PRE;	regs->dmctrl = 0;	if (reset) {		register int i;		/*		 * Assert SCSI bus reset for at least 25 Usec to clear the 		 * world. SII_DO_RST is self clearing.		 * Delay 250 ms before doing any commands.		 */		regs->comm = SII_DO_RST;		MachEmptyWriteBuffer();		DELAY(250000);		/* rearbitrate synchronous offset */		for (i = 0; i < SII_NCMD; i++)			sii_softc[0].sc_st[i].dmaReqAck = 0;	}	/*	 * Clear any pending interrupts from the reset.	 */	regs->cstat = regs->cstat;	regs->dstat = regs->dstat;	/*	 * Set up SII for arbitrated bus mode, SCSI parity checking,	 * Reselect Enable, and Interrupt Enable.	 */	regs->csr = SII_HPM | SII_RSE | SII_PCE | SII_IE;	MachEmptyWriteBuffer();}/* * Start a SCSI command by sending the cmd data * to a SCSI controller via the SII. * Call the device done proceedure if it can't be started. * NOTE: we should be called with interrupts disabled. */static voidsii_StartCmd(sc, target)	register struct siisoftc *sc;	/* which SII to use */	register int target;		/* which command to start */{	register SIIRegs *regs;		register ScsiCmd *scsicmd;	register State *state;	register unsigned status;	int error, retval;	/* if another command is currently in progress, just wait */	if (sc->sc_target >= 0)		return;	/* initialize state information for this command */	scsicmd = sc->sc_cmd[target];	state = &sc->sc_st[target];	state->flags = FIRST_DMA;	state->prevComm = 0;	state->dmalen = 0;	state->dmaCurPhase = -1;	state->dmaPrevPhase = -1;	state->dmaBufIndex = 0;	state->cmd = scsicmd->cmd;	state->cmdlen = scsicmd->cmdlen;	if ((state->buflen = scsicmd->buflen) == 0) {		state->dmaDataPhase = -1; /* illegal phase. shouldn't happen */		state->buf = (char *)0;	} else {		state->dmaDataPhase =			(scsicmd->flags & SCSICMD_DATA_TO_DEVICE) ?			SII_DATA_OUT_PHASE : SII_DATA_IN_PHASE;		state->buf = scsicmd->buf;	}#ifdef DEBUG	if (sii_debug > 1) {		printf("sii_StartCmd: %s target %d cmd 0x%x addr %x size %d dma %d\n",			scsicmd->sd->sd_driver->d_name, target,			scsicmd->cmd[0], scsicmd->buf, scsicmd->buflen,			state->dmaDataPhase);	}	sii_debug_cmd = scsicmd->cmd[0];	if (scsicmd->cmd[0] == SCSI_READ_EXT) {		sii_debug_bn = (scsicmd->cmd[2] << 24) |			(scsicmd->cmd[3] << 16) |			(scsicmd->cmd[4] << 8) |			scsicmd->cmd[5];		sii_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8];	}#endif	/* try to select the target */	regs = sc->sc_regs;	/*	 * Another device may have selected us; in which case,	 * this command will be restarted later.	 */	if ((status = regs->dstat) & (SII_CI | SII_DI)) {		sii_DoIntr(sc, status);		return;	}	sc->sc_target = target;	if (scsicmd->flags & SCSICMD_USE_SYNC) {		printf("sii_StartCmd: doing extended msg\n"); /* XXX */		/*		 * Setup to send both the identify message and the synchronous		 * data transfer request.		 */		sii_buf[0] = SCSI_DIS_REC_IDENTIFY;		sii_buf[1] = SCSI_EXTENDED_MSG;		sii_buf[2] = 3;		/* message length */		sii_buf[3] = SCSI_SYNCHRONOUS_XFER;		sii_buf[4] = 0;		sii_buf[5] = 3;		/* maximum SII chip supports */		state->dmaCurPhase = SII_MSG_OUT_PHASE,		state->dmalen = 6;		CopyToBuffer((u_short *)sii_buf,			(volatile u_short *)SII_BUF_ADDR, 6);		regs->slcsr = target;		regs->dmctrl = 0;		regs->dmaddrl = ((u_short)SII_BUF_ADDR >> 1);		regs->dmaddrh = ((u_short)SII_BUF_ADDR >> 17) & 03;		regs->dmlotc = 6;		regs->comm = SII_DMA | SII_INXFER | SII_SELECT | SII_ATN |			SII_CON | SII_MSG_OUT_PHASE;	} else {		/* do a chained, select with ATN and programmed I/O command */		regs->data = SCSI_DIS_REC_IDENTIFY;		regs->slcsr = target;		regs->dmctrl = 0;		regs->comm = SII_INXFER | SII_SELECT | SII_ATN | SII_CON |			SII_MSG_OUT_PHASE;	}	MachEmptyWriteBuffer();	/*	 * Wait for something to happen	 * (should happen soon or we would use interrupts).	 */	SII_WAIT_UNTIL(status, regs->cstat, status & (SII_CI | SII_DI),		SII_WAIT_COUNT/4, retval);	/* check to see if we are connected OK */	if ((status & (SII_RST | SII_SCH | SII_STATE_MSK)) ==	    (SII_SCH | SII_CON)) {		regs->cstat = status;		MachEmptyWriteBuffer();#ifdef DEBUG		sii_logp->target = target;		sii_logp->cstat = status;		sii_logp->dstat = 0;		sii_logp->comm = regs->comm;		sii_logp->msg = -1;		if (++sii_logp >= &sii_log[NLOG])			sii_logp = sii_log;#endif		/* wait a short time for command phase */		SII_WAIT_UNTIL(status, regs->dstat, status & SII_MIS,			SII_WAIT_COUNT, retval);#ifdef DEBUG		if (sii_debug > 2)			printf("sii_StartCmd: ds %x cnt %d\n", status, retval);#endif		if ((status & (SII_CI | SII_MIS | SII_PHASE_MSK)) !=		    (SII_MIS | SII_CMD_PHASE)) {			printf("sii_StartCmd: timeout cs %x ds %x cnt %d\n",				regs->cstat, status, retval); /* XXX */			/* process interrupt or continue until it happens */			if (status & (SII_CI | SII_DI))				sii_DoIntr(sc, status);			return;		}		regs->dstat = SII_DNE;	/* clear Msg Out DMA done */		/* send command data */		CopyToBuffer((u_short *)state->cmd,			(volatile u_short *)state->dmaAddr[0], state->cmdlen);		sii_StartDMA(regs, state->dmaCurPhase = SII_CMD_PHASE,			state->dmaAddr[0], state->dmalen = scsicmd->cmdlen);		/* wait a little while for DMA to finish */		SII_WAIT_UNTIL(status, regs->dstat, status & (SII_CI | SII_DI),			SII_WAIT_COUNT, retval);#ifdef DEBUG		if (sii_debug > 2)			printf("sii_StartCmd: ds %x, cnt %d\n", status, retval);#endif		if (status & (SII_CI | SII_DI))			sii_DoIntr(sc, status);#ifdef DEBUG		if (sii_debug > 2)			printf("sii_StartCmd: DONE ds %x\n", regs->dstat);#endif		return;	}	/*	 * Another device may have selected us; in which case,	 * this command will be restarted later.	 */	if (status & (SII_CI | SII_DI)) {		sii_DoIntr(sc, regs->dstat);		return;	}	/*	 * Disconnect if selection command still in progress.	 */	if (status & SII_SIP) {		error = ENXIO;	/* device didn't respond */		regs->comm = SII_DISCON;		MachEmptyWriteBuffer();		SII_WAIT_UNTIL(status, regs->cstat,			!(status & (SII_CON | SII_SIP)),			SII_WAIT_COUNT, retval);	} else		error = EBUSY;	/* couldn't get the bus */#ifdef DEBUG	if (sii_debug > 1)		printf("sii_StartCmd: Couldn't select target %d error %d\n",			target, error);#endif	sc->sc_target = -1;	regs->cstat = 0xffff;	regs->dstat = 0xffff;	regs->comm = 0;	MachEmptyWriteBuffer();	sii_CmdDone(sc, target, error);}/* * Process interrupt conditions. */static voidsii_DoIntr(sc, dstat)	register struct siisoftc *sc;	register unsigned dstat;{	register SIIRegs *regs = sc->sc_regs;	register State *state;	register unsigned cstat;	register int i;	unsigned comm, msg;again:	comm = regs->comm;#ifdef DEBUG	if (sii_debug > 3)		printf("sii_DoIntr: cs %x, ds %x cm %x ",			regs->cstat, dstat, comm);	sii_logp->target = sc->sc_target;	sii_logp->cstat = regs->cstat;	sii_logp->dstat = dstat;	sii_logp->comm = comm;	sii_logp->msg = -1;	if (++sii_logp >= &sii_log[NLOG])		sii_logp = sii_log;#endif	regs->dstat = dstat;	/* acknowledge everything */	MachEmptyWriteBuffer();	if (dstat & SII_CI) {		/* deglitch cstat register */		msg = regs->cstat;		while (msg != (cstat = regs->cstat))			msg = cstat;		regs->cstat = cstat;	/* acknowledge everything */

⌨️ 快捷键说明

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