mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-23 11:18:54 +00:00
9d92f16230
A nsp chip does suspend I/O write by 512bytes burst write, though the chip only has 48 bytes FIFO. The chip assert I/O WAIT signal to PC-Card bus after the CPU writes more than 48 bytes to the chip if the SCSI device does not respond immediately in supsend I/O burst write. If the device does not respond for a while it might cause PC-Card bus timeout. The previous work around was to wait the request from SCSI device. But there are some devices which request bytes for synchronous transfer immediately. So current work aound is to fill 32bytes FIFO, wait for FIFO empty and burst write 512-32 bytes for every 512 bytes block. Submitted-by: Honda-san (the author of the driver) Obtained-from: NetBSD/pc98
1939 lines
45 KiB
C
1939 lines
45 KiB
C
/* $FreeBSD$ */
|
|
/* $NecBSD: nsp.c,v 1.21.12.6 2001/06/29 06:27:52 honda Exp $ */
|
|
/* $NetBSD$ */
|
|
|
|
#define NSP_DEBUG
|
|
#define NSP_STATICS
|
|
#define NSP_IO_CONTROL_FLAGS \
|
|
(NSP_READ_SUSPEND_IO | NSP_WRITE_SUSPEND_IO | \
|
|
NSP_READ_FIFO_INTERRUPTS | NSP_WRITE_FIFO_INTERRUPTS | \
|
|
NSP_USE_MEMIO | NSP_WAIT_FOR_SELECT)
|
|
|
|
/*
|
|
* Copyright (c) 1998, 1999, 2000, 2001
|
|
* NetBSD/pc98 porting staff. All rights reserved.
|
|
*
|
|
* Copyright (c) 1998, 1999, 2000, 2001
|
|
* Naofumi HONDA. All rights reserved.
|
|
*
|
|
* 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. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
*/
|
|
#include "opt_ddb.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#if defined(__FreeBSD__) && __FreeBSD_version > 500001
|
|
#include <sys/bio.h>
|
|
#endif /* __ FreeBSD__ */
|
|
#include <sys/buf.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/errno.h>
|
|
|
|
#ifdef __NetBSD__
|
|
#include <sys/device.h>
|
|
#include <machine/bus.h>
|
|
#include <machine/intr.h>
|
|
|
|
#include <dev/scsipi/scsi_all.h>
|
|
#include <dev/scsipi/scsipi_all.h>
|
|
#include <dev/scsipi/scsiconf.h>
|
|
#include <dev/scsipi/scsi_disk.h>
|
|
|
|
#include <machine/dvcfg.h>
|
|
#include <machine/physio_proc.h>
|
|
|
|
#include <i386/Cbus/dev/scsi_low.h>
|
|
#include <i386/Cbus/dev/nspreg.h>
|
|
#include <i386/Cbus/dev/nspvar.h>
|
|
#endif /* __NetBSD__ */
|
|
|
|
#ifdef __FreeBSD__
|
|
#include <machine/clock.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/bus_pio.h>
|
|
#include <machine/bus_memio.h>
|
|
#include <machine/bus.h>
|
|
|
|
#include <machine/dvcfg.h>
|
|
#include <machine/physio_proc.h>
|
|
|
|
#include <cam/scsi/scsi_low.h>
|
|
#include <dev/nsp/nspreg.h>
|
|
#include <dev/nsp/nspvar.h>
|
|
|
|
#if __FreeBSD_version < 400001
|
|
#include "nsp.h"
|
|
struct nsp_softc *nspdata[NNSP];
|
|
#endif
|
|
#endif /* __FreeBSD__ */
|
|
|
|
/***************************************************
|
|
* USER SETTINGS
|
|
***************************************************/
|
|
/* DEVICE CONFIGURATION FLAGS (MINOR)
|
|
*
|
|
* 0x01 DISCONECT OFF
|
|
* 0x02 PARITY LINE OFF
|
|
* 0x04 IDENTIFY MSG OFF ( = single lun)
|
|
* 0x08 SYNC TRANSFER OFF
|
|
*/
|
|
|
|
/***************************************************
|
|
* PARAMS
|
|
***************************************************/
|
|
#define NSP_NTARGETS 8
|
|
#define NSP_NLUNS 8
|
|
|
|
#define NSP_MAX_DATA_SIZE (64 * 1024)
|
|
#define NSP_SELTIMEOUT (200)
|
|
#define NSP_DELAY_MAX (2 * 1000 * 1000)
|
|
#define NSP_DELAY_INTERVAL (1)
|
|
#define NSP_TIMER_1MS (1000 / 51)
|
|
|
|
/***************************************************
|
|
* DEBUG
|
|
***************************************************/
|
|
#ifdef NSP_DEBUG
|
|
int nsp_debug;
|
|
#endif /* NSP_DEBUG */
|
|
|
|
#ifdef NSP_STATICS
|
|
struct nsp_statics {
|
|
int arbit_conflict_1;
|
|
int arbit_conflict_2;
|
|
int device_data_write;
|
|
int device_busy;
|
|
int disconnect;
|
|
int reselect;
|
|
int data_phase_bypass;
|
|
} nsp_statics;
|
|
#endif /* NSP_STATICS */
|
|
|
|
/***************************************************
|
|
* IO control
|
|
***************************************************/
|
|
#define NSP_READ_SUSPEND_IO 0x0001
|
|
#define NSP_WRITE_SUSPEND_IO 0x0002
|
|
#define NSP_USE_MEMIO 0x0004
|
|
#define NSP_READ_FIFO_INTERRUPTS 0x0010
|
|
#define NSP_WRITE_FIFO_INTERRUPTS 0x0020
|
|
#define NSP_WAIT_FOR_SELECT 0x0100
|
|
|
|
u_int nsp_io_control = NSP_IO_CONTROL_FLAGS;
|
|
int nsp_read_suspend_bytes = DEV_BSIZE;
|
|
int nsp_write_suspend_bytes = DEV_BSIZE;
|
|
int nsp_read_interrupt_bytes = 4096;
|
|
int nsp_write_interrupt_bytes = 4096;
|
|
|
|
/***************************************************
|
|
* DEVICE STRUCTURE
|
|
***************************************************/
|
|
extern struct cfdriver nsp_cd;
|
|
|
|
/**************************************************************
|
|
* DECLARE
|
|
**************************************************************/
|
|
#define NSP_FIFO_ON 1
|
|
#define NSP_FIFO_OFF 0
|
|
static void nsp_pio_read __P((struct nsp_softc *, int));
|
|
static void nsp_pio_write __P((struct nsp_softc *, int));
|
|
static int nsp_xfer __P((struct nsp_softc *, u_int8_t *, int, int, int));
|
|
static int nsp_msg __P((struct nsp_softc *, struct targ_info *, u_int));
|
|
static int nsp_reselected __P((struct nsp_softc *));
|
|
static int nsp_disconnected __P((struct nsp_softc *, struct targ_info *));
|
|
static void nsp_pdma_end __P((struct nsp_softc *, struct targ_info *));
|
|
static void nsphw_init __P((struct nsp_softc *));
|
|
static int nsp_target_nexus_establish __P((struct nsp_softc *));
|
|
static int nsp_lun_nexus_establish __P((struct nsp_softc *));
|
|
static int nsp_ccb_nexus_establish __P((struct nsp_softc *));
|
|
static int nsp_world_start __P((struct nsp_softc *, int));
|
|
static int nsphw_start_selection __P((struct nsp_softc *sc, struct slccb *));
|
|
static void nsphw_bus_reset __P((struct nsp_softc *));
|
|
static void nsphw_attention __P((struct nsp_softc *));
|
|
static u_int nsp_fifo_count __P((struct nsp_softc *));
|
|
static u_int nsp_request_count __P((struct nsp_softc *));
|
|
static int nsp_negate_signal __P((struct nsp_softc *, u_int8_t, u_char *));
|
|
static int nsp_expect_signal __P((struct nsp_softc *, u_int8_t, u_int8_t));
|
|
static void nsp_start_timer __P((struct nsp_softc *, int));
|
|
static void nsp_setup_fifo __P((struct nsp_softc *, int, int, int));
|
|
static int nsp_targ_init __P((struct nsp_softc *, struct targ_info *, int));
|
|
static void nsphw_selection_done_and_expect_msgout __P((struct nsp_softc *));
|
|
static void nsp_data_padding __P((struct nsp_softc *, int, u_int));
|
|
static int nsp_timeout __P((struct nsp_softc *));
|
|
static int nsp_read_fifo __P((struct nsp_softc *, int));
|
|
static int nsp_write_fifo __P((struct nsp_softc *, int));
|
|
static int nsp_phase_match __P((struct nsp_softc *, u_int8_t, u_int8_t));
|
|
static int nsp_wait_interrupt __P((struct nsp_softc *));
|
|
|
|
struct scsi_low_funcs nspfuncs = {
|
|
SC_LOW_INIT_T nsp_world_start,
|
|
SC_LOW_BUSRST_T nsphw_bus_reset,
|
|
SC_LOW_TARG_INIT_T nsp_targ_init,
|
|
SC_LOW_LUN_INIT_T NULL,
|
|
|
|
SC_LOW_SELECT_T nsphw_start_selection,
|
|
SC_LOW_NEXUS_T nsp_lun_nexus_establish,
|
|
SC_LOW_NEXUS_T nsp_ccb_nexus_establish,
|
|
|
|
SC_LOW_ATTEN_T nsphw_attention,
|
|
SC_LOW_MSG_T nsp_msg,
|
|
|
|
SC_LOW_TIMEOUT_T nsp_timeout,
|
|
SC_LOW_POLL_T nspintr,
|
|
|
|
NULL,
|
|
};
|
|
|
|
/****************************************************
|
|
* hwfuncs
|
|
****************************************************/
|
|
static __inline u_int8_t nsp_cr_read_1 __P((bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t ofs));
|
|
static __inline void nsp_cr_write_1 __P((bus_space_tag_t bst, bus_space_handle_t bsh, bus_addr_t ofs, u_int8_t va));
|
|
|
|
static __inline u_int8_t
|
|
nsp_cr_read_1(bst, bsh, ofs)
|
|
bus_space_tag_t bst;
|
|
bus_space_handle_t bsh;
|
|
bus_addr_t ofs;
|
|
{
|
|
|
|
bus_space_write_1(bst, bsh, nsp_idxr, ofs);
|
|
return bus_space_read_1(bst, bsh, nsp_datar);
|
|
}
|
|
|
|
static __inline void
|
|
nsp_cr_write_1(bst, bsh, ofs, va)
|
|
bus_space_tag_t bst;
|
|
bus_space_handle_t bsh;
|
|
bus_addr_t ofs;
|
|
u_int8_t va;
|
|
{
|
|
|
|
bus_space_write_1(bst, bsh, nsp_idxr, ofs);
|
|
bus_space_write_1(bst, bsh, nsp_datar, va);
|
|
}
|
|
|
|
static int
|
|
nsp_expect_signal(sc, curphase, mask)
|
|
struct nsp_softc *sc;
|
|
u_int8_t curphase, mask;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
int wc;
|
|
u_int8_t ph, isrc;
|
|
|
|
for (wc = 0; wc < NSP_DELAY_MAX / NSP_DELAY_INTERVAL; wc ++)
|
|
{
|
|
ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON);
|
|
if (ph == (u_int8_t) -1)
|
|
return -1;
|
|
|
|
isrc = bus_space_read_1(bst, bsh, nsp_irqsr);
|
|
if (isrc & IRQSR_SCSI)
|
|
return 0;
|
|
|
|
if ((ph & mask) != 0 && (ph & SCBUSMON_PHMASK) == curphase)
|
|
return 1;
|
|
|
|
SCSI_LOW_DELAY(NSP_DELAY_INTERVAL);
|
|
}
|
|
|
|
printf("%s: nsp_expect_signal timeout\n", slp->sl_xname);
|
|
return -1;
|
|
}
|
|
|
|
static void
|
|
nsphw_init(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
|
|
/* block all interrupts */
|
|
bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_ALLMASK);
|
|
|
|
/* setup SCSI interface */
|
|
bus_space_write_1(bst, bsh, nsp_ifselr, IFSELR_IFSEL);
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCIENR, 0);
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_XFERMR, XFERMR_IO8);
|
|
nsp_cr_write_1(bst, bsh, NSPR_CLKDIVR, sc->sc_iclkdiv);
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCIENR, sc->sc_icr);
|
|
nsp_cr_write_1(bst, bsh, NSPR_PARITYR, sc->sc_parr);
|
|
nsp_cr_write_1(bst, bsh, NSPR_PTCLRR,
|
|
PTCLRR_ACK | PTCLRR_REQ | PTCLRR_HOST | PTCLRR_RSS);
|
|
|
|
/* setup fifo asic */
|
|
bus_space_write_1(bst, bsh, nsp_ifselr, IFSELR_REGSEL);
|
|
nsp_cr_write_1(bst, bsh, NSPR_TERMPWRC, 0);
|
|
if ((nsp_cr_read_1(bst, bsh, NSPR_OCR) & OCR_TERMPWRS) == 0)
|
|
nsp_cr_write_1(bst, bsh, NSPR_TERMPWRC, TERMPWRC_POWON);
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_XFERMR, XFERMR_IO8);
|
|
nsp_cr_write_1(bst, bsh, NSPR_CLKDIVR, sc->sc_clkdiv);
|
|
nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, 0);
|
|
nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, 0);
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_SYNCR, 0);
|
|
nsp_cr_write_1(bst, bsh, NSPR_ACKWIDTH, 0);
|
|
|
|
/* enable interrupts and ack them */
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCIENR, sc->sc_icr);
|
|
bus_space_write_1(bst, bsh, nsp_irqcr, IRQSR_MASK);
|
|
|
|
nsp_setup_fifo(sc, NSP_FIFO_OFF, SCSI_LOW_READ, 0);
|
|
}
|
|
|
|
/****************************************************
|
|
* scsi low interface
|
|
****************************************************/
|
|
static void
|
|
nsphw_attention(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
u_int8_t cr;
|
|
|
|
cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR)/* & ~SCBUSCR_ACK */;
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr | SCBUSCR_ATN);
|
|
SCSI_LOW_DELAY(10);
|
|
}
|
|
|
|
static void
|
|
nsphw_bus_reset(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
int i;
|
|
|
|
bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_ALLMASK);
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, SCBUSCR_RST);
|
|
SCSI_LOW_DELAY(100 * 1000); /* 100ms */
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, 0);
|
|
for (i = 0; i < 5; i ++)
|
|
(void) nsp_cr_read_1(bst, bsh, NSPR_IRQPHS);
|
|
|
|
bus_space_write_1(bst, bsh, nsp_irqcr, IRQSR_MASK);
|
|
}
|
|
|
|
static void
|
|
nsphw_selection_done_and_expect_msgout(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
|
|
/* clear ack counter */
|
|
sc->sc_cnt = 0;
|
|
nsp_cr_write_1(bst, bsh, NSPR_PTCLRR, PTCLRR_PT | PTCLRR_ACK |
|
|
PTCLRR_REQ | PTCLRR_HOST);
|
|
|
|
/* deassert sel and assert atten */
|
|
sc->sc_seltout = 0;
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, sc->sc_busc);
|
|
SCSI_LOW_DELAY(1);
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR,
|
|
sc->sc_busc | SCBUSCR_ADIR | SCBUSCR_ACKEN);
|
|
SCSI_LOW_ASSERT_ATN(slp);
|
|
}
|
|
|
|
static int
|
|
nsphw_start_selection(sc, cb)
|
|
struct nsp_softc *sc;
|
|
struct slccb *cb;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
struct targ_info *ti = cb->ti;
|
|
register u_int8_t arbs, ph;
|
|
int s, wc;
|
|
|
|
wc = sc->sc_tmaxcnt = cb->ccb_tcmax * 1000 * 1000;
|
|
sc->sc_dataout_timeout = 0;
|
|
|
|
/* check bus free */
|
|
s = splhigh();
|
|
ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON);
|
|
if (ph != SCBUSMON_FREE)
|
|
{
|
|
splx(s);
|
|
#ifdef NSP_STATICS
|
|
nsp_statics.arbit_conflict_1 ++;
|
|
#endif /* NSP_STATICS */
|
|
return SCSI_LOW_START_FAIL;
|
|
}
|
|
|
|
/* start arbitration */
|
|
nsp_cr_write_1(bst, bsh, NSPR_ARBITS, ARBITS_EXEC);
|
|
splx(s);
|
|
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_ARBSTART);
|
|
do
|
|
{
|
|
/* XXX: what a stupid chip! */
|
|
arbs = nsp_cr_read_1(bst, bsh, NSPR_ARBITS);
|
|
SCSI_LOW_DELAY(1);
|
|
}
|
|
while ((arbs & (ARBITS_WIN | ARBITS_FAIL)) == 0 && wc -- > 0);
|
|
|
|
if ((arbs & ARBITS_WIN) == 0)
|
|
{
|
|
nsp_cr_write_1(bst, bsh, NSPR_ARBITS, ARBITS_CLR);
|
|
#ifdef NSP_STATICS
|
|
nsp_statics.arbit_conflict_2 ++;
|
|
#endif /* NSP_STATICS */
|
|
return SCSI_LOW_START_FAIL;
|
|
}
|
|
|
|
/* assert select line */
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_SELSTART);
|
|
scsi_low_arbit_win(slp);
|
|
|
|
s = splhigh();
|
|
SCSI_LOW_DELAY(3);
|
|
nsp_cr_write_1(bst, bsh, NSPR_DATA,
|
|
sc->sc_idbit | (1 << ti->ti_id));
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR,
|
|
SCBUSCR_SEL | SCBUSCR_BSY | sc->sc_busc);
|
|
SCSI_LOW_DELAY(3);
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, SCBUSCR_SEL |
|
|
SCBUSCR_BSY | SCBUSCR_DOUT | sc->sc_busc);
|
|
nsp_cr_write_1(bst, bsh, NSPR_ARBITS, ARBITS_CLR);
|
|
SCSI_LOW_DELAY(3);
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR,
|
|
SCBUSCR_SEL | SCBUSCR_DOUT | sc->sc_busc);
|
|
SCSI_LOW_DELAY(1);
|
|
|
|
if ((nsp_io_control & NSP_WAIT_FOR_SELECT) != 0)
|
|
{
|
|
#define NSP_FIRST_SEL_WAIT 300
|
|
#define NSP_SEL_CHECK_INTERVAL 10
|
|
|
|
/* wait for a selection response */
|
|
for (wc = 0; wc < NSP_FIRST_SEL_WAIT / NSP_SEL_CHECK_INTERVAL;
|
|
wc ++)
|
|
{
|
|
ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON);
|
|
if ((ph & SCBUSMON_BSY) == 0)
|
|
{
|
|
SCSI_LOW_DELAY(NSP_SEL_CHECK_INTERVAL);
|
|
continue;
|
|
}
|
|
|
|
SCSI_LOW_DELAY(1);
|
|
ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON);
|
|
if ((ph & SCBUSMON_BSY) != 0)
|
|
{
|
|
nsphw_selection_done_and_expect_msgout(sc);
|
|
splx(s);
|
|
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED);
|
|
return SCSI_LOW_START_OK;
|
|
}
|
|
}
|
|
}
|
|
splx(s);
|
|
|
|
/* check a selection timeout */
|
|
nsp_start_timer(sc, NSP_TIMER_1MS);
|
|
sc->sc_seltout = 1;
|
|
return SCSI_LOW_START_OK;
|
|
}
|
|
|
|
static int
|
|
nsp_world_start(sc, fdone)
|
|
struct nsp_softc *sc;
|
|
int fdone;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
|
|
sc->sc_cnt = 0;
|
|
sc->sc_seltout = 0;
|
|
|
|
if ((slp->sl_cfgflags & CFG_NOATTEN) == 0)
|
|
sc->sc_busc = SCBUSCR_ATN;
|
|
else
|
|
sc->sc_busc = 0;
|
|
|
|
if ((slp->sl_cfgflags & CFG_NOPARITY) == 0)
|
|
sc->sc_parr = PARITYR_ENABLE | PARITYR_CLEAR;
|
|
else
|
|
sc->sc_parr = 0;
|
|
|
|
sc->sc_icr = (SCIENR_SCCHG | SCIENR_RESEL | SCIENR_RST);
|
|
|
|
nsphw_init(sc);
|
|
scsi_low_bus_reset(slp);
|
|
|
|
SOFT_INTR_REQUIRED(slp);
|
|
return 0;
|
|
}
|
|
|
|
struct ncp_synch_data {
|
|
u_int min_period;
|
|
u_int max_period;
|
|
u_int chip_period;
|
|
u_int ack_width;
|
|
};
|
|
|
|
static struct ncp_synch_data ncp_sync_data_40M[] = {
|
|
{0x0c,0x0c,0x1,0}, /* 20MB 50ns*/
|
|
{0x19,0x19,0x3,1}, /* 10MB 100ns*/
|
|
{0x1a,0x25,0x5,2}, /* 7.5MB 150ns*/
|
|
{0x26,0x32,0x7,3}, /* 5MB 200ns*/
|
|
{0x0, 0, 0, 0}
|
|
};
|
|
|
|
static struct ncp_synch_data ncp_sync_data_20M[] = {
|
|
{0x19,0x19,0x1,0}, /* 10MB 100ns*/
|
|
{0x1a,0x25,0x2,0}, /* 7.5MB 150ns*/
|
|
{0x26,0x32,0x3,1}, /* 5MB 200ns*/
|
|
{0x0, 0, 0, 0}
|
|
};
|
|
|
|
static int
|
|
nsp_msg(sc, ti, msg)
|
|
struct nsp_softc *sc;
|
|
struct targ_info *ti;
|
|
u_int msg;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
struct ncp_synch_data *sdp;
|
|
struct nsp_targ_info *nti = (void *) ti;
|
|
u_int period, offset;
|
|
int i, error;
|
|
|
|
if ((msg & SCSI_LOW_MSG_WIDE) != 0)
|
|
{
|
|
if (ti->ti_width != SCSI_LOW_BUS_WIDTH_8)
|
|
{
|
|
ti->ti_width = SCSI_LOW_BUS_WIDTH_8;
|
|
return EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if ((msg & SCSI_LOW_MSG_SYNCH) == 0)
|
|
return 0;
|
|
|
|
period = ti->ti_maxsynch.period;
|
|
offset = ti->ti_maxsynch.offset;
|
|
if (sc->sc_iclkdiv == CLKDIVR_20M)
|
|
sdp = &ncp_sync_data_20M[0];
|
|
else
|
|
sdp = &ncp_sync_data_40M[0];
|
|
|
|
for (i = 0; sdp->max_period != 0; i ++, sdp ++)
|
|
{
|
|
if (period >= sdp->min_period && period <= sdp->max_period)
|
|
break;
|
|
}
|
|
|
|
if (period != 0 && sdp->max_period == 0)
|
|
{
|
|
/*
|
|
* NO proper period/offset found,
|
|
* Retry neg with the target.
|
|
*/
|
|
ti->ti_maxsynch.period = 0;
|
|
ti->ti_maxsynch.offset = 0;
|
|
nti->nti_reg_syncr = 0;
|
|
nti->nti_reg_ackwidth = 0;
|
|
error = EINVAL;
|
|
}
|
|
else
|
|
{
|
|
nti->nti_reg_syncr = (sdp->chip_period << SYNCR_PERS) |
|
|
(offset & SYNCR_OFFM);
|
|
nti->nti_reg_ackwidth = sdp->ack_width;
|
|
error = 0;
|
|
}
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_SYNCR, nti->nti_reg_syncr);
|
|
nsp_cr_write_1(bst, bsh, NSPR_ACKWIDTH, nti->nti_reg_ackwidth);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
nsp_targ_init(sc, ti, action)
|
|
struct nsp_softc *sc;
|
|
struct targ_info *ti;
|
|
int action;
|
|
{
|
|
struct nsp_targ_info *nti = (void *) ti;
|
|
|
|
if (action == SCSI_LOW_INFO_ALLOC || action == SCSI_LOW_INFO_REVOKE)
|
|
{
|
|
ti->ti_width = SCSI_LOW_BUS_WIDTH_8;
|
|
ti->ti_maxsynch.period = 100 / 4;
|
|
ti->ti_maxsynch.offset = 15;
|
|
nti->nti_reg_syncr = 0;
|
|
nti->nti_reg_ackwidth = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
nsp_start_timer(sc, time)
|
|
struct nsp_softc *sc;
|
|
int time;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
|
|
sc->sc_timer = time;
|
|
nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, time);
|
|
}
|
|
|
|
/**************************************************************
|
|
* General probe attach
|
|
**************************************************************/
|
|
int
|
|
nspprobesubr(iot, ioh, dvcfg)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
u_int dvcfg;
|
|
{
|
|
u_int8_t regv;
|
|
|
|
regv = bus_space_read_1(iot, ioh, nsp_fifosr);
|
|
if (regv < 0x11 || regv >= 0x20)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
nspprint(aux, name)
|
|
void *aux;
|
|
const char *name;
|
|
{
|
|
|
|
if (name != NULL)
|
|
printf("%s: scsibus ", name);
|
|
return UNCONF;
|
|
}
|
|
|
|
void
|
|
nspattachsubr(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
|
|
printf("\n");
|
|
|
|
sc->sc_idbit = (1 << slp->sl_hostid);
|
|
slp->sl_flags |= HW_READ_PADDING;
|
|
slp->sl_funcs = &nspfuncs;
|
|
sc->sc_tmaxcnt = SCSI_LOW_MIN_TOUT * 1000 * 1000; /* default */
|
|
|
|
(void) scsi_low_attach(slp, 0, NSP_NTARGETS, NSP_NLUNS,
|
|
sizeof(struct nsp_targ_info), 0);
|
|
}
|
|
|
|
/**************************************************************
|
|
* PDMA functions
|
|
**************************************************************/
|
|
static u_int
|
|
nsp_fifo_count(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
u_int count;
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_PTCLRR, PTCLRR_RSS_ACK | PTCLRR_PT);
|
|
count = bus_space_read_1(bst, bsh, nsp_datar);
|
|
count += (((u_int) bus_space_read_1(bst, bsh, nsp_datar)) << 8);
|
|
count += (((u_int) bus_space_read_1(bst, bsh, nsp_datar)) << 16);
|
|
return count;
|
|
}
|
|
|
|
static u_int
|
|
nsp_request_count(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
u_int count;
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_PTCLRR, PTCLRR_RSS_REQ | PTCLRR_PT);
|
|
count = bus_space_read_1(bst, bsh, nsp_datar);
|
|
count += (((u_int) bus_space_read_1(bst, bsh, nsp_datar)) << 8);
|
|
count += (((u_int) bus_space_read_1(bst, bsh, nsp_datar)) << 16);
|
|
return count;
|
|
}
|
|
|
|
static void
|
|
nsp_setup_fifo(sc, on, direction, datalen)
|
|
struct nsp_softc *sc;
|
|
int on;
|
|
int direction;
|
|
int datalen;
|
|
{
|
|
u_int8_t xfermode;
|
|
|
|
sc->sc_suspendio = 0;
|
|
if (on == NSP_FIFO_OFF)
|
|
{
|
|
xfermode = XFERMR_IO8;
|
|
goto out;
|
|
}
|
|
|
|
/* check if suspend io OK ? */
|
|
if (datalen > 0)
|
|
{
|
|
if (direction == SCSI_LOW_READ)
|
|
{
|
|
if ((nsp_io_control & NSP_READ_SUSPEND_IO) != 0 &&
|
|
(datalen % nsp_read_suspend_bytes) == 0)
|
|
sc->sc_suspendio = nsp_read_suspend_bytes;
|
|
}
|
|
else
|
|
{
|
|
if ((nsp_io_control & NSP_WRITE_SUSPEND_IO) != 0 &&
|
|
(datalen % nsp_write_suspend_bytes) == 0)
|
|
sc->sc_suspendio = nsp_write_suspend_bytes;
|
|
}
|
|
}
|
|
|
|
/* determine a transfer type */
|
|
if (datalen < DEV_BSIZE || (datalen & 3) != 0)
|
|
{
|
|
if (sc->sc_memh != NULL &&
|
|
(nsp_io_control & NSP_USE_MEMIO) != 0)
|
|
xfermode = XFERMR_XEN | XFERMR_MEM8;
|
|
else
|
|
xfermode = XFERMR_XEN | XFERMR_IO8;
|
|
}
|
|
else
|
|
{
|
|
if (sc->sc_memh != NULL &&
|
|
(nsp_io_control & NSP_USE_MEMIO) != 0)
|
|
xfermode = XFERMR_XEN | XFERMR_MEM32;
|
|
else
|
|
xfermode = XFERMR_XEN | XFERMR_IO32;
|
|
|
|
if (sc->sc_suspendio > 0)
|
|
xfermode |= XFERMR_FIFOEN;
|
|
}
|
|
|
|
out:
|
|
sc->sc_xfermr = xfermode;
|
|
nsp_cr_write_1(sc->sc_iot, sc->sc_ioh, NSPR_XFERMR, sc->sc_xfermr);
|
|
}
|
|
|
|
static void
|
|
nsp_pdma_end(sc, ti)
|
|
struct nsp_softc *sc;
|
|
struct targ_info *ti;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
struct slccb *cb = slp->sl_Qnexus;
|
|
u_int len = 0, cnt;
|
|
|
|
sc->sc_dataout_timeout = 0;
|
|
slp->sl_flags &= ~HW_PDMASTART;
|
|
nsp_setup_fifo(sc, NSP_FIFO_OFF, SCSI_LOW_READ, 0);
|
|
if ((sc->sc_icr & SCIENR_FIFO) != 0)
|
|
{
|
|
sc->sc_icr &= ~SCIENR_FIFO;
|
|
nsp_cr_write_1(sc->sc_iot, sc->sc_ioh, NSPR_SCIENR, sc->sc_icr);
|
|
}
|
|
|
|
if (cb == NULL)
|
|
{
|
|
slp->sl_error |= PDMAERR;
|
|
return;
|
|
}
|
|
|
|
if (ti->ti_phase == PH_DATA)
|
|
{
|
|
cnt = nsp_fifo_count(sc);
|
|
if (slp->sl_scp.scp_direction == SCSI_LOW_WRITE)
|
|
{
|
|
len = sc->sc_cnt - cnt;
|
|
if (sc->sc_cnt >= cnt &&
|
|
slp->sl_scp.scp_datalen + len <=
|
|
cb->ccb_scp.scp_datalen)
|
|
{
|
|
slp->sl_scp.scp_data -= len;
|
|
slp->sl_scp.scp_datalen += len;
|
|
}
|
|
else
|
|
{
|
|
slp->sl_error |= PDMAERR;
|
|
printf("%s len %x >= datalen %x\n",
|
|
slp->sl_xname,
|
|
len, slp->sl_scp.scp_datalen);
|
|
}
|
|
}
|
|
else if (slp->sl_scp.scp_direction == SCSI_LOW_READ)
|
|
{
|
|
if (sc->sc_cnt != cnt ||
|
|
sc->sc_cnt > cb->ccb_scp.scp_datalen)
|
|
{
|
|
slp->sl_error |= PDMAERR;
|
|
printf("%s: data read count error %x != %x (%x)\n",
|
|
slp->sl_xname, sc->sc_cnt, cnt,
|
|
cb->ccb_scp.scp_datalen);
|
|
}
|
|
}
|
|
sc->sc_cnt = cnt;
|
|
scsi_low_data_finish(slp);
|
|
}
|
|
else
|
|
{
|
|
|
|
printf("%s data phase miss\n", slp->sl_xname);
|
|
slp->sl_error |= PDMAERR;
|
|
}
|
|
}
|
|
|
|
#define RFIFO_CRIT 64
|
|
#define WFIFO_CRIT 32
|
|
|
|
static void
|
|
nsp_data_padding(sc, direction, count)
|
|
struct nsp_softc *sc;
|
|
int direction;
|
|
u_int count;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
|
|
if (count > NSP_MAX_DATA_SIZE)
|
|
count = NSP_MAX_DATA_SIZE;
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_XFERMR, XFERMR_XEN | XFERMR_IO8);
|
|
if (direction == SCSI_LOW_READ)
|
|
{
|
|
while (count -- > 0)
|
|
(void) bus_space_read_1(bst, bsh, nsp_fifodr);
|
|
}
|
|
else
|
|
{
|
|
while (count -- > 0)
|
|
(void) bus_space_write_1(bst, bsh, nsp_fifodr, 0);
|
|
}
|
|
nsp_cr_write_1(bst, bsh, NSPR_XFERMR, sc->sc_xfermr);
|
|
}
|
|
|
|
static int
|
|
nsp_read_fifo(sc, suspendio)
|
|
struct nsp_softc *sc;
|
|
int suspendio;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
u_int res;
|
|
|
|
res = nsp_fifo_count(sc);
|
|
if (res == sc->sc_cnt)
|
|
return 0;
|
|
|
|
#ifdef NSP_DEBUG
|
|
if (res < sc->sc_cnt || res == (u_int) -1)
|
|
{
|
|
printf("%s: strange fifo ack count 0x%x < 0x%x\n",
|
|
slp->sl_xname, res, sc->sc_cnt);
|
|
return 0;
|
|
}
|
|
#endif /* NSP_DEBUG */
|
|
|
|
res = res - sc->sc_cnt;
|
|
if (res > slp->sl_scp.scp_datalen)
|
|
{
|
|
if ((slp->sl_error & PDMAERR) == 0)
|
|
{
|
|
printf("%s: data overrun 0x%x > 0x%x\n",
|
|
slp->sl_xname, res, slp->sl_scp.scp_datalen);
|
|
}
|
|
|
|
slp->sl_error |= PDMAERR;
|
|
slp->sl_scp.scp_datalen = 0;
|
|
|
|
if ((slp->sl_flags & HW_READ_PADDING) == 0)
|
|
{
|
|
printf("%s: read padding required\n", slp->sl_xname);
|
|
return 0;
|
|
}
|
|
|
|
nsp_data_padding(sc, SCSI_LOW_READ, res);
|
|
sc->sc_cnt += res;
|
|
return 1; /* padding start */
|
|
}
|
|
|
|
if (suspendio > 0 && slp->sl_scp.scp_datalen >= suspendio)
|
|
res = suspendio;
|
|
|
|
if ((sc->sc_xfermr & (XFERMR_MEM32 | XFERMR_MEM8)) != 0)
|
|
{
|
|
if ((sc->sc_xfermr & XFERMR_MEM32) != 0)
|
|
{
|
|
res &= ~3;
|
|
bus_space_read_region_4(sc->sc_memt, sc->sc_memh, 0,
|
|
(u_int32_t *) slp->sl_scp.scp_data, res >> 2);
|
|
}
|
|
else
|
|
{
|
|
bus_space_read_region_1(sc->sc_memt, sc->sc_memh, 0,
|
|
(u_int8_t *) slp->sl_scp.scp_data, res);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((sc->sc_xfermr & XFERMR_IO32) != 0)
|
|
{
|
|
res &= ~3;
|
|
bus_space_read_multi_4(bst, bsh, nsp_fifodr,
|
|
(u_int32_t *) slp->sl_scp.scp_data, res >> 2);
|
|
}
|
|
else
|
|
{
|
|
bus_space_read_multi_1(bst, bsh, nsp_fifodr,
|
|
(u_int8_t *) slp->sl_scp.scp_data, res);
|
|
}
|
|
}
|
|
|
|
if (nsp_cr_read_1(bst, bsh, NSPR_PARITYR) & PARITYR_PE)
|
|
{
|
|
nsp_cr_write_1(bst, bsh, NSPR_PARITYR,
|
|
PARITYR_ENABLE | PARITYR_CLEAR);
|
|
scsi_low_assert_msg(slp, slp->sl_Tnexus, SCSI_LOW_MSG_ERROR, 1);
|
|
}
|
|
|
|
slp->sl_scp.scp_data += res;
|
|
slp->sl_scp.scp_datalen -= res;
|
|
sc->sc_cnt += res;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nsp_write_fifo(sc, suspendio)
|
|
struct nsp_softc *sc;
|
|
int suspendio;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
u_int res;
|
|
register u_int8_t stat;
|
|
|
|
if (suspendio > 0)
|
|
{
|
|
#ifdef NSP_DEBUG
|
|
if ((slp->sl_scp.scp_datalen % WFIFO_CRIT) != 0)
|
|
{
|
|
printf("%s: strange write length 0x%x\n",
|
|
slp->sl_xname, slp->sl_scp.scp_datalen);
|
|
}
|
|
#endif /* NSP_DEBUG */
|
|
res = slp->sl_scp.scp_datalen % suspendio;
|
|
if (res == 0)
|
|
{
|
|
res = suspendio;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
res = WFIFO_CRIT;
|
|
}
|
|
|
|
if (res > slp->sl_scp.scp_datalen)
|
|
res = slp->sl_scp.scp_datalen;
|
|
|
|
/* XXX: reconfirm! */
|
|
stat = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON) & SCBUSMON_PHMASK;
|
|
if (stat != PHASE_DATAOUT)
|
|
return 0;
|
|
|
|
if ((sc->sc_xfermr & (XFERMR_MEM32 | XFERMR_MEM8)) != 0)
|
|
{
|
|
if ((sc->sc_xfermr & XFERMR_MEM32) != 0)
|
|
{
|
|
bus_space_write_region_4(sc->sc_memt, sc->sc_memh, 0,
|
|
(u_int32_t *) slp->sl_scp.scp_data, res >> 2);
|
|
}
|
|
else
|
|
{
|
|
bus_space_write_region_1(sc->sc_memt, sc->sc_memh, 0,
|
|
(u_int8_t *) slp->sl_scp.scp_data, res);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((sc->sc_xfermr & XFERMR_IO32) != 0)
|
|
{
|
|
bus_space_write_multi_4(bst, bsh, nsp_fifodr,
|
|
(u_int32_t *) slp->sl_scp.scp_data, res >> 2);
|
|
}
|
|
else
|
|
{
|
|
bus_space_write_multi_1(bst, bsh, nsp_fifodr,
|
|
(u_int8_t *) slp->sl_scp.scp_data, res);
|
|
}
|
|
}
|
|
|
|
slp->sl_scp.scp_datalen -= res;
|
|
slp->sl_scp.scp_data += res;
|
|
sc->sc_cnt += res;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nsp_wait_interrupt(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
int tout;
|
|
register u_int8_t isrc;
|
|
|
|
for (tout = 0; tout < DEV_BSIZE / 10; tout ++)
|
|
{
|
|
isrc = bus_space_read_1(bst, bsh, nsp_irqsr);
|
|
if ((isrc & (IRQSR_SCSI | IRQSR_FIFO)) != 0)
|
|
{
|
|
if ((isrc & IRQSR_FIFO) != 0)
|
|
{
|
|
bus_space_write_1(bst, bsh,
|
|
nsp_irqcr, IRQCR_FIFOCL);
|
|
}
|
|
return 1;
|
|
}
|
|
SCSI_LOW_DELAY(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
nsp_pio_read(sc, suspendio)
|
|
struct nsp_softc *sc;
|
|
int suspendio;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
int tout, padding, datalen;
|
|
register u_int8_t stat, fstat;
|
|
|
|
padding = 0;
|
|
tout = sc->sc_tmaxcnt;
|
|
slp->sl_flags |= HW_PDMASTART;
|
|
datalen = slp->sl_scp.scp_datalen;
|
|
|
|
ReadLoop:
|
|
while (1)
|
|
{
|
|
stat = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON);
|
|
if (stat == (u_int8_t) -1)
|
|
return;
|
|
|
|
/* out of data phase */
|
|
if ((stat & SCBUSMON_PHMASK) != PHASE_DATAIN)
|
|
{
|
|
nsp_read_fifo(sc, 0);
|
|
return;
|
|
}
|
|
|
|
/* data phase */
|
|
fstat = bus_space_read_1(bst, bsh, nsp_fifosr);
|
|
if ((fstat & FIFOSR_FULLEMP) != 0)
|
|
{
|
|
if ((sc->sc_icr & SCIENR_FIFO) != 0)
|
|
{
|
|
bus_space_write_1(bst, bsh, nsp_irqcr,
|
|
IRQCR_FIFOCL);
|
|
}
|
|
|
|
if (suspendio > 0)
|
|
{
|
|
padding |= nsp_read_fifo(sc, suspendio);
|
|
}
|
|
else
|
|
{
|
|
padding |= nsp_read_fifo(sc, 0);
|
|
}
|
|
|
|
if ((sc->sc_icr & SCIENR_FIFO) != 0)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (padding == 0 && slp->sl_scp.scp_datalen <= 0)
|
|
return;
|
|
|
|
if ((sc->sc_icr & SCIENR_FIFO) != 0)
|
|
break;
|
|
|
|
SCSI_LOW_DELAY(1);
|
|
}
|
|
|
|
if ((-- tout) <= 0)
|
|
{
|
|
printf("%s: nsp_pio_read: timeout\n", slp->sl_xname);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (slp->sl_scp.scp_datalen > 0 &&
|
|
slp->sl_scp.scp_datalen > datalen - nsp_read_interrupt_bytes)
|
|
{
|
|
if (nsp_wait_interrupt(sc) != 0)
|
|
goto ReadLoop;
|
|
}
|
|
}
|
|
|
|
static void
|
|
nsp_pio_write(sc, suspendio)
|
|
struct nsp_softc *sc;
|
|
int suspendio;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
u_int rcount, acount;
|
|
int tout, datalen;
|
|
register u_int8_t stat, fstat;
|
|
|
|
tout = sc->sc_tmaxcnt;
|
|
slp->sl_flags |= HW_PDMASTART;
|
|
datalen = slp->sl_scp.scp_datalen;
|
|
|
|
WriteLoop:
|
|
while (1)
|
|
{
|
|
stat = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON) & SCBUSMON_PHMASK;
|
|
if (stat != PHASE_DATAOUT)
|
|
return;
|
|
|
|
if (slp->sl_scp.scp_datalen <= 0)
|
|
{
|
|
if (sc->sc_dataout_timeout == 0)
|
|
sc->sc_dataout_timeout = SCSI_LOW_TIMEOUT_HZ;
|
|
return;
|
|
}
|
|
|
|
fstat = bus_space_read_1(bst, bsh, nsp_fifosr);
|
|
if ((fstat & FIFOSR_FULLEMP) != 0)
|
|
{
|
|
if ((sc->sc_icr & SCIENR_FIFO) != 0)
|
|
{
|
|
bus_space_write_1(bst, bsh, nsp_irqcr,
|
|
IRQCR_FIFOCL);
|
|
}
|
|
|
|
if (suspendio > 0)
|
|
{
|
|
/* XXX:IMPORTANT:
|
|
* To avoid timeout of pcmcia bus
|
|
* (not scsi bus!), we should check
|
|
* the scsi device sends us request
|
|
* signals, which means the scsi device
|
|
* is ready to recieve data without
|
|
* heavy delays.
|
|
*/
|
|
if ((slp->sl_scp.scp_datalen % suspendio) == 0)
|
|
{
|
|
/* Step I:
|
|
* fill the nsp fifo, and waiting for
|
|
* the fifo empty.
|
|
*/
|
|
nsp_write_fifo(sc, 0);
|
|
}
|
|
else
|
|
{
|
|
/* Step II:
|
|
* check the request singals.
|
|
*/
|
|
acount = nsp_fifo_count(sc);
|
|
rcount = nsp_request_count(sc);
|
|
if (rcount <= acount)
|
|
{
|
|
nsp_write_fifo(sc, 0);
|
|
#ifdef NSP_STATICS
|
|
nsp_statics.device_busy ++;
|
|
#endif /* NSP_STATICS */
|
|
}
|
|
else
|
|
{
|
|
nsp_write_fifo(sc, suspendio);
|
|
#ifdef NSP_STATICS
|
|
nsp_statics.device_data_write ++;
|
|
#endif /* NSP_STATICS */
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nsp_write_fifo(sc, 0);
|
|
}
|
|
|
|
if ((sc->sc_icr & SCIENR_FIFO) != 0)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if ((sc->sc_icr & SCIENR_FIFO) != 0)
|
|
break;
|
|
|
|
SCSI_LOW_DELAY(1);
|
|
}
|
|
|
|
if ((-- tout) <= 0)
|
|
{
|
|
printf("%s: nsp_pio_write: timeout\n", slp->sl_xname);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (slp->sl_scp.scp_datalen > 0 &&
|
|
slp->sl_scp.scp_datalen > datalen - nsp_write_interrupt_bytes)
|
|
{
|
|
if (nsp_wait_interrupt(sc) != 0)
|
|
goto WriteLoop;
|
|
}
|
|
}
|
|
|
|
static int
|
|
nsp_negate_signal(sc, mask, s)
|
|
struct nsp_softc *sc;
|
|
u_int8_t mask;
|
|
u_char *s;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
int wc;
|
|
u_int8_t regv;
|
|
|
|
for (wc = 0; wc < NSP_DELAY_MAX / NSP_DELAY_INTERVAL; wc ++)
|
|
{
|
|
regv = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON);
|
|
if (regv == (u_int8_t) -1)
|
|
return -1;
|
|
if ((regv & mask) == 0)
|
|
return 1;
|
|
SCSI_LOW_DELAY(NSP_DELAY_INTERVAL);
|
|
}
|
|
|
|
printf("%s: %s nsp_negate_signal timeout\n", slp->sl_xname, s);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
nsp_xfer(sc, buf, len, phase, clear_atn)
|
|
struct nsp_softc *sc;
|
|
u_int8_t *buf;
|
|
int len;
|
|
int phase;
|
|
int clear_atn;
|
|
{
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
int ptr, rv;
|
|
|
|
for (ptr = 0; len > 0; len --, ptr ++)
|
|
{
|
|
rv = nsp_expect_signal(sc, phase, SCBUSMON_REQ);
|
|
if (rv <= 0)
|
|
goto out;
|
|
|
|
if (len == 1 && clear_atn != 0)
|
|
{
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR,
|
|
SCBUSCR_ADIR | SCBUSCR_ACKEN);
|
|
SCSI_LOW_DEASSERT_ATN(&sc->sc_sclow);
|
|
}
|
|
|
|
if (phase & SCBUSMON_IO)
|
|
{
|
|
buf[ptr] = nsp_cr_read_1(bst, bsh, NSPR_DATAACK);
|
|
}
|
|
else
|
|
{
|
|
nsp_cr_write_1(bst, bsh, NSPR_DATAACK, buf[ptr]);
|
|
}
|
|
nsp_negate_signal(sc, SCBUSMON_ACK, "xfer<ACK>");
|
|
}
|
|
|
|
out:
|
|
return len;
|
|
}
|
|
|
|
/**************************************************************
|
|
* disconnect & reselect (HW low)
|
|
**************************************************************/
|
|
static int
|
|
nsp_reselected(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
struct targ_info *ti;
|
|
u_int sid;
|
|
u_int8_t cr;
|
|
|
|
sid = (u_int) nsp_cr_read_1(bst, bsh, NSPR_RESELR);
|
|
sid &= ~sc->sc_idbit;
|
|
sid = ffs(sid) - 1;
|
|
if ((ti = scsi_low_reselected(slp, sid)) == NULL)
|
|
return EJUSTRETURN;
|
|
|
|
nsp_negate_signal(sc, SCBUSMON_SEL, "reselect<SEL>");
|
|
|
|
cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR);
|
|
cr &= ~(SCBUSCR_BSY | SCBUSCR_ATN);
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr);
|
|
cr |= SCBUSCR_ADIR | SCBUSCR_ACKEN;
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr);
|
|
|
|
#ifdef NSP_STATICS
|
|
nsp_statics.reselect ++;
|
|
#endif /* NSP_STATCIS */
|
|
return EJUSTRETURN;
|
|
}
|
|
|
|
static int
|
|
nsp_disconnected(sc, ti)
|
|
struct nsp_softc *sc;
|
|
struct targ_info *ti;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_PTCLRR, PTCLRR_PT | PTCLRR_ACK |
|
|
PTCLRR_REQ | PTCLRR_HOST);
|
|
if ((sc->sc_icr & SCIENR_FIFO) != 0)
|
|
{
|
|
sc->sc_icr &= ~SCIENR_FIFO;
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCIENR, sc->sc_icr);
|
|
}
|
|
sc->sc_cnt = 0;
|
|
sc->sc_dataout_timeout = 0;
|
|
#ifdef NSP_STATICS
|
|
nsp_statics.disconnect ++;
|
|
#endif /* NSP_STATICS */
|
|
scsi_low_disconnected(slp, ti);
|
|
return 1;
|
|
}
|
|
|
|
/**************************************************************
|
|
* SEQUENCER
|
|
**************************************************************/
|
|
static void nsp_error __P((struct nsp_softc *, u_char *, u_int8_t, u_int8_t, u_int8_t));
|
|
|
|
static void
|
|
nsp_error(sc, s, isrc, ph, irqphs)
|
|
struct nsp_softc *sc;
|
|
u_char *s;
|
|
u_int8_t isrc, ph, irqphs;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
|
|
printf("%s: %s\n", slp->sl_xname, s);
|
|
printf("%s: isrc 0x%x scmon 0x%x irqphs 0x%x\n",
|
|
slp->sl_xname, (u_int) isrc, (u_int) ph, (u_int) irqphs);
|
|
}
|
|
|
|
static int
|
|
nsp_target_nexus_establish(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
struct targ_info *ti = slp->sl_Tnexus;
|
|
struct nsp_targ_info *nti = (void *) ti;
|
|
|
|
/* setup synch transfer registers */
|
|
nsp_cr_write_1(bst, bsh, NSPR_SYNCR, nti->nti_reg_syncr);
|
|
nsp_cr_write_1(bst, bsh, NSPR_ACKWIDTH, nti->nti_reg_ackwidth);
|
|
|
|
/* setup pdma fifo (minimum) */
|
|
nsp_setup_fifo(sc, NSP_FIFO_ON, SCSI_LOW_READ, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nsp_lun_nexus_establish(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nsp_ccb_nexus_establish(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
struct slccb *cb = slp->sl_Qnexus;
|
|
|
|
sc->sc_tmaxcnt = cb->ccb_tcmax * 1000 * 1000;
|
|
|
|
/* setup pdma fifo */
|
|
nsp_setup_fifo(sc, NSP_FIFO_ON,
|
|
slp->sl_scp.scp_direction, slp->sl_scp.scp_datalen);
|
|
|
|
if (slp->sl_scp.scp_direction == SCSI_LOW_READ)
|
|
{
|
|
if (sc->sc_suspendio > 0 &&
|
|
(nsp_io_control & NSP_READ_FIFO_INTERRUPTS) != 0)
|
|
{
|
|
sc->sc_icr |= SCIENR_FIFO;
|
|
nsp_cr_write_1(sc->sc_iot, sc->sc_ioh,
|
|
NSPR_SCIENR, sc->sc_icr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sc->sc_suspendio > 0 &&
|
|
(nsp_io_control & NSP_WRITE_FIFO_INTERRUPTS) != 0)
|
|
{
|
|
sc->sc_icr |= SCIENR_FIFO;
|
|
nsp_cr_write_1(sc->sc_iot, sc->sc_ioh,
|
|
NSPR_SCIENR, sc->sc_icr);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nsp_phase_match(sc, phase, stat)
|
|
struct nsp_softc *sc;
|
|
u_int8_t phase;
|
|
u_int8_t stat;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
|
|
if ((stat & SCBUSMON_PHMASK) != phase)
|
|
{
|
|
printf("%s: phase mismatch 0x%x != 0x%x\n",
|
|
slp->sl_xname, (u_int) phase, (u_int) stat);
|
|
return EINVAL;
|
|
}
|
|
|
|
if ((stat & SCBUSMON_REQ) == 0)
|
|
return EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
nspintr(arg)
|
|
void *arg;
|
|
{
|
|
struct nsp_softc *sc = arg;
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t bst = sc->sc_iot;
|
|
bus_space_handle_t bsh = sc->sc_ioh;
|
|
struct targ_info *ti;
|
|
struct physio_proc *pp;
|
|
struct buf *bp;
|
|
u_int derror, flags;
|
|
int len, rv;
|
|
u_int8_t isrc, ph, irqphs, cr, regv;
|
|
|
|
/*******************************************
|
|
* interrupt check
|
|
*******************************************/
|
|
if (slp->sl_flags & HW_INACTIVE)
|
|
return 0;
|
|
|
|
bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_IRQDIS);
|
|
isrc = bus_space_read_1(bst, bsh, nsp_irqsr);
|
|
if (isrc == (u_int8_t) -1 || (isrc & IRQSR_MASK) == 0)
|
|
{
|
|
bus_space_write_1(bst, bsh, nsp_irqcr, 0);
|
|
return 0;
|
|
}
|
|
|
|
/* XXX: IMPORTANT
|
|
* Do not read an irqphs register if no scsi phase interrupt.
|
|
* Unless, you should lose a scsi phase interrupt.
|
|
*/
|
|
ph = nsp_cr_read_1(bst, bsh, NSPR_SCBUSMON);
|
|
if ((isrc & IRQSR_SCSI) != 0)
|
|
{
|
|
irqphs = nsp_cr_read_1(bst, bsh, NSPR_IRQPHS);
|
|
}
|
|
else
|
|
irqphs = 0;
|
|
|
|
/*
|
|
* timer interrupt handler (scsi vs timer interrupts)
|
|
*/
|
|
if (sc->sc_timer != 0)
|
|
{
|
|
nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, 0);
|
|
nsp_cr_write_1(bst, bsh, NSPR_TIMERCNT, 0);
|
|
sc->sc_timer = 0;
|
|
}
|
|
|
|
/* check a timer interrupt */
|
|
regv = 0;
|
|
if ((isrc & IRQSR_TIMER) != 0)
|
|
{
|
|
if ((isrc & IRQSR_MASK) == IRQSR_TIMER && sc->sc_seltout == 0)
|
|
{
|
|
bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_TIMERCL);
|
|
return 1;
|
|
}
|
|
regv |= IRQCR_TIMERCL;
|
|
}
|
|
|
|
/* check a fifo interrupt */
|
|
if ((isrc & IRQSR_FIFO) != 0)
|
|
{
|
|
regv |= IRQCR_FIFOCL;
|
|
}
|
|
|
|
/* OK. enable all interrupts */
|
|
bus_space_write_1(bst, bsh, nsp_irqcr, regv);
|
|
|
|
/*******************************************
|
|
* debug section
|
|
*******************************************/
|
|
#ifdef NSP_DEBUG
|
|
if (nsp_debug)
|
|
{
|
|
nsp_error(sc, "current status", isrc, ph, irqphs);
|
|
scsi_low_print(slp, NULL);
|
|
#ifdef DDB
|
|
if (nsp_debug > 1)
|
|
SCSI_LOW_DEBUGGER("nsp");
|
|
#endif /* DDB */
|
|
}
|
|
#endif /* NSP_DEBUG */
|
|
|
|
/*******************************************
|
|
* Parse hardware SCSI irq reasons register
|
|
*******************************************/
|
|
if ((isrc & IRQSR_SCSI) != 0)
|
|
{
|
|
if ((irqphs & IRQPHS_RST) != 0)
|
|
{
|
|
scsi_low_restart(slp, SCSI_LOW_RESTART_SOFT,
|
|
"bus reset (power off?)");
|
|
return 1;
|
|
}
|
|
|
|
if ((irqphs & IRQPHS_RSEL) != 0)
|
|
{
|
|
bus_space_write_1(bst, bsh, nsp_irqcr, IRQCR_RESCL);
|
|
if (nsp_reselected(sc) == EJUSTRETURN)
|
|
return 1;
|
|
}
|
|
|
|
if ((irqphs & (IRQPHS_PCHG | IRQPHS_LBF)) == 0)
|
|
return 1;
|
|
}
|
|
|
|
/*******************************************
|
|
* nexus check
|
|
*******************************************/
|
|
if ((ti = slp->sl_Tnexus) == NULL)
|
|
{
|
|
/* unknown scsi phase changes */
|
|
nsp_error(sc, "unknown scsi phase changes", isrc, ph, irqphs);
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************
|
|
* aribitration & selection
|
|
*******************************************/
|
|
switch (ti->ti_phase)
|
|
{
|
|
case PH_SELSTART:
|
|
if ((ph & SCBUSMON_BSY) == 0)
|
|
{
|
|
if (sc->sc_seltout >= NSP_SELTIMEOUT)
|
|
{
|
|
sc->sc_seltout = 0;
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, 0);
|
|
return nsp_disconnected(sc, ti);
|
|
}
|
|
sc->sc_seltout ++;
|
|
nsp_start_timer(sc, NSP_TIMER_1MS);
|
|
return 1;
|
|
}
|
|
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED);
|
|
nsphw_selection_done_and_expect_msgout(sc);
|
|
return 1;
|
|
|
|
case PH_SELECTED:
|
|
if ((isrc & IRQSR_SCSI) == 0)
|
|
return 1;
|
|
|
|
nsp_target_nexus_establish(sc);
|
|
break;
|
|
|
|
case PH_RESEL:
|
|
if ((isrc & IRQSR_SCSI) == 0)
|
|
return 1;
|
|
|
|
nsp_target_nexus_establish(sc);
|
|
if ((ph & SCBUSMON_PHMASK) != PHASE_MSGIN)
|
|
{
|
|
printf("%s: unexpected phase after reselect\n",
|
|
slp->sl_xname);
|
|
slp->sl_error |= FATALIO;
|
|
scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1);
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case PH_DATA:
|
|
if ((isrc & IRQSR_SCSI) != 0)
|
|
break;
|
|
if ((isrc & IRQSR_FIFO) != 0)
|
|
{
|
|
if (NSP_IS_PHASE_DATA(ph) == 0)
|
|
return 1;
|
|
irqphs = (ph & IRQPHS_PHMASK);
|
|
break;
|
|
}
|
|
return 1;
|
|
|
|
default:
|
|
if ((isrc & IRQSR_SCSI) == 0)
|
|
return 1;
|
|
break;
|
|
}
|
|
|
|
/*******************************************
|
|
* data phase control
|
|
*******************************************/
|
|
if (slp->sl_flags & HW_PDMASTART)
|
|
{
|
|
if ((isrc & IRQSR_SCSI) != 0 &&
|
|
NSP_IS_IRQPHS_DATA(irqphs) == 0)
|
|
{
|
|
if (slp->sl_scp.scp_direction == SCSI_LOW_READ)
|
|
nsp_pio_read(sc, 0);
|
|
nsp_pdma_end(sc, ti);
|
|
}
|
|
}
|
|
|
|
/*******************************************
|
|
* scsi seq
|
|
*******************************************/
|
|
if (slp->sl_msgphase != 0 && (irqphs & IRQPHS_LBF) != 0)
|
|
return nsp_disconnected(sc, ti);
|
|
|
|
/* check unexpected bus free state */
|
|
if (ph == 0)
|
|
{
|
|
nsp_error(sc, "unexpected bus free", isrc, ph, irqphs);
|
|
return nsp_disconnected(sc, ti);
|
|
}
|
|
|
|
/* check normal scsi phase */
|
|
switch (irqphs & IRQPHS_PHMASK)
|
|
{
|
|
case IRQPHS_CMD:
|
|
if (nsp_phase_match(sc, PHASE_CMD, ph) != 0)
|
|
return 1;
|
|
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_CMD);
|
|
if (scsi_low_cmd(slp, ti) != 0)
|
|
{
|
|
scsi_low_attention(slp);
|
|
}
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_CMDCR, CMDCR_PTCLR);
|
|
for (len = 0; len < slp->sl_scp.scp_cmdlen; len ++)
|
|
nsp_cr_write_1(bst, bsh, NSPR_CMDDR,
|
|
slp->sl_scp.scp_cmd[len]);
|
|
|
|
nsp_cr_write_1(bst, bsh, NSPR_CMDCR, CMDCR_PTCLR | CMDCR_EXEC);
|
|
break;
|
|
|
|
case IRQPHS_DATAOUT:
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_DATA);
|
|
if (scsi_low_data(slp, ti, &bp, SCSI_LOW_WRITE) != 0)
|
|
{
|
|
scsi_low_attention(slp);
|
|
}
|
|
|
|
pp = physio_proc_enter(bp);
|
|
nsp_pio_write(sc, sc->sc_suspendio);
|
|
physio_proc_leave(pp);
|
|
break;
|
|
|
|
case IRQPHS_DATAIN:
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_DATA);
|
|
if (scsi_low_data(slp, ti, &bp, SCSI_LOW_READ) != 0)
|
|
{
|
|
scsi_low_attention(slp);
|
|
}
|
|
|
|
pp = physio_proc_enter(bp);
|
|
nsp_pio_read(sc, sc->sc_suspendio);
|
|
physio_proc_leave(pp);
|
|
break;
|
|
|
|
case IRQPHS_STATUS:
|
|
if (nsp_phase_match(sc, PHASE_STATUS, ph) != 0)
|
|
return 1;
|
|
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_STAT);
|
|
regv = nsp_cr_read_1(bst, bsh, NSPR_DATA);
|
|
if (nsp_cr_read_1(bst, bsh, NSPR_PARITYR) & PARITYR_PE)
|
|
{
|
|
nsp_cr_write_1(bst, bsh, NSPR_PARITYR,
|
|
PARITYR_ENABLE | PARITYR_CLEAR);
|
|
derror = SCSI_LOW_DATA_PE;
|
|
}
|
|
else
|
|
derror = 0;
|
|
|
|
/* assert ACK */
|
|
cr = SCBUSCR_ACK | nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR);
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr);
|
|
|
|
if (scsi_low_statusin(slp, ti, derror | regv) != 0)
|
|
{
|
|
scsi_low_attention(slp);
|
|
}
|
|
|
|
/* check REQ nagated */
|
|
nsp_negate_signal(sc, SCBUSMON_REQ, "statin<REQ>");
|
|
|
|
/* deassert ACK */
|
|
cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR) & (~SCBUSCR_ACK);
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr);
|
|
break;
|
|
|
|
case IRQPHS_MSGOUT:
|
|
if (nsp_phase_match(sc, PHASE_MSGOUT, ph) != 0)
|
|
return 1;
|
|
|
|
#ifdef NSP_MSGOUT_SERIALIZE
|
|
/*
|
|
* XXX: NSP QUIRK
|
|
* NSP invoke interrupts only in the case of scsi phase changes,
|
|
* therefore we should poll the scsi phase here to catch
|
|
* the next "msg out" if exists (no scsi phase changes).
|
|
*/
|
|
rv = len = 16;
|
|
do {
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT);
|
|
flags = (ti->ti_ophase != ti->ti_phase) ?
|
|
SCSI_LOW_MSGOUT_INIT : 0;
|
|
len = scsi_low_msgout(slp, ti, flags);
|
|
|
|
if (len > 1 && slp->sl_atten == 0)
|
|
{
|
|
scsi_low_attention(slp);
|
|
}
|
|
|
|
if (nsp_xfer(sc, ti->ti_msgoutstr, len, PHASE_MSGOUT,
|
|
slp->sl_clear_atten) != 0)
|
|
{
|
|
slp->sl_error |= FATALIO;
|
|
nsp_error(sc, "MSGOUT: xfer short",
|
|
isrc, ph, irqphs);
|
|
}
|
|
|
|
/* catch a next signal */
|
|
rv = nsp_expect_signal(sc, PHASE_MSGOUT, SCBUSMON_REQ);
|
|
}
|
|
while (rv > 0 && len -- > 0);
|
|
|
|
#else /* !NSP_MSGOUT_SERIALIZE */
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT);
|
|
flags = SCSI_LOW_MSGOUT_UNIFY;
|
|
if (ti->ti_ophase != ti->ti_phase)
|
|
flags |= SCSI_LOW_MSGOUT_INIT;
|
|
len = scsi_low_msgout(slp, ti, flags);
|
|
|
|
if (len > 1 && slp->sl_atten == 0)
|
|
{
|
|
scsi_low_attention(slp);
|
|
}
|
|
|
|
if (nsp_xfer(sc, ti->ti_msgoutstr, len, PHASE_MSGOUT,
|
|
slp->sl_clear_atten) != 0)
|
|
{
|
|
nsp_error(sc, "MSGOUT: xfer short", isrc, ph, irqphs);
|
|
}
|
|
|
|
#endif /* !NSP_MSGOUT_SERIALIZE */
|
|
break;
|
|
|
|
case IRQPHS_MSGIN:
|
|
if (nsp_phase_match(sc, PHASE_MSGIN, ph) != 0)
|
|
return 1;
|
|
|
|
/*
|
|
* XXX: NSP QUIRK
|
|
* NSP invoke interrupts only in the case of scsi phase changes,
|
|
* therefore we should poll the scsi phase here to catch
|
|
* the next "msg in" if exists (no scsi phase changes).
|
|
*/
|
|
rv = len = 16;
|
|
do {
|
|
SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN);
|
|
|
|
/* read a data */
|
|
regv = nsp_cr_read_1(bst, bsh, NSPR_DATA);
|
|
if (nsp_cr_read_1(bst, bsh, NSPR_PARITYR) & PARITYR_PE)
|
|
{
|
|
nsp_cr_write_1(bst, bsh,
|
|
NSPR_PARITYR,
|
|
PARITYR_ENABLE | PARITYR_CLEAR);
|
|
derror = SCSI_LOW_DATA_PE;
|
|
}
|
|
else
|
|
{
|
|
derror = 0;
|
|
}
|
|
|
|
/* assert ack */
|
|
cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR) | SCBUSCR_ACK;
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr);
|
|
|
|
if (scsi_low_msgin(slp, ti, regv | derror) == 0)
|
|
{
|
|
if (scsi_low_is_msgout_continue(ti, 0) != 0)
|
|
{
|
|
scsi_low_attention(slp);
|
|
}
|
|
}
|
|
|
|
/* check REQ nagated */
|
|
nsp_negate_signal(sc, SCBUSMON_REQ, "msgin<REQ>");
|
|
|
|
/* deassert ack */
|
|
cr = nsp_cr_read_1(bst, bsh, NSPR_SCBUSCR) & (~SCBUSCR_ACK);
|
|
nsp_cr_write_1(bst, bsh, NSPR_SCBUSCR, cr);
|
|
|
|
/* catch a next signal */
|
|
rv = nsp_expect_signal(sc, PHASE_MSGIN, SCBUSMON_REQ);
|
|
}
|
|
while (rv > 0 && len -- > 0);
|
|
break;
|
|
|
|
default:
|
|
slp->sl_error |= FATALIO;
|
|
nsp_error(sc, "unknown scsi phase", isrc, ph, irqphs);
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
|
|
#if 0
|
|
timerout:
|
|
nsp_start_timer(sc, NSP_TIMER_1MS);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
nsp_timeout(sc)
|
|
struct nsp_softc *sc;
|
|
{
|
|
struct scsi_low_softc *slp = &sc->sc_sclow;
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
int tout;
|
|
u_int8_t ph, regv;
|
|
|
|
if (slp->sl_Tnexus == NULL)
|
|
return 0;
|
|
|
|
ph = nsp_cr_read_1(iot, ioh, NSPR_SCBUSMON);
|
|
switch (ph & SCBUSMON_PHMASK)
|
|
{
|
|
case PHASE_DATAOUT:
|
|
if (sc->sc_dataout_timeout == 0)
|
|
break;
|
|
|
|
/* check a fifo empty */
|
|
regv = bus_space_read_1(iot, ioh, nsp_fifosr);
|
|
if ((regv & FIFOSR_FULLEMP) == 0)
|
|
break;
|
|
bus_space_write_1(iot, ioh, nsp_irqcr, IRQCR_FIFOCL);
|
|
|
|
/* check still requested */
|
|
ph = nsp_cr_read_1(iot, ioh, NSPR_SCBUSMON);
|
|
if ((ph & SCBUSMON_REQ) == 0)
|
|
break;
|
|
/* check timeout */
|
|
if ((-- sc->sc_dataout_timeout) > 0)
|
|
break;
|
|
|
|
slp->sl_error |= PDMAERR;
|
|
if ((slp->sl_flags & HW_WRITE_PADDING) == 0)
|
|
{
|
|
printf("%s: write padding required\n", slp->sl_xname);
|
|
break;
|
|
}
|
|
|
|
tout = NSP_DELAY_MAX;
|
|
while (tout -- > 0)
|
|
{
|
|
ph = nsp_cr_read_1(iot, ioh, NSPR_SCBUSMON);
|
|
if ((ph & SCBUSMON_PHMASK) != PHASE_DATAOUT)
|
|
break;
|
|
regv = bus_space_read_1(iot, ioh, nsp_fifosr);
|
|
if ((regv & FIFOSR_FULLEMP) == 0)
|
|
{
|
|
SCSI_LOW_DELAY(1);
|
|
continue;
|
|
}
|
|
|
|
bus_space_write_1(iot, ioh, nsp_irqcr, IRQCR_FIFOCL);
|
|
nsp_data_padding(sc, SCSI_LOW_WRITE, 32);
|
|
}
|
|
ph = nsp_cr_read_1(iot, ioh, NSPR_SCBUSMON);
|
|
if ((ph & SCBUSMON_PHMASK) == PHASE_DATAOUT)
|
|
sc->sc_dataout_timeout = SCSI_LOW_TIMEOUT_HZ;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|