179697Snon/*	$NecBSD: ncr53c500.c,v 1.30.12.3 2001/06/26 07:31:41 honda Exp $	*/
267468Snon/*	$NetBSD$	*/
367468Snon
467468Snon#define	NCV_DEBUG
567468Snon#define	NCV_STATICS
679697Snon#define	NCV_IO_CONTROL_FLAGS	(0)
767468Snon
8139749Simp/*-
967468Snon * [NetBSD for NEC PC-98 series]
1079697Snon *  Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001
1167468Snon *	NetBSD/pc98 porting staff. All rights reserved.
1279697Snon *  Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001
1367468Snon *	Naofumi HONDA. All rights reserved.
1467468Snon *
1567468Snon *  Redistribution and use in source and binary forms, with or without
1667468Snon *  modification, are permitted provided that the following conditions
1767468Snon *  are met:
1867468Snon *  1. Redistributions of source code must retain the above copyright
1967468Snon *     notice, this list of conditions and the following disclaimer.
2067468Snon *  2. Redistributions in binary form must reproduce the above copyright
2167468Snon *     notice, this list of conditions and the following disclaimer in the
2267468Snon *     documentation and/or other materials provided with the distribution.
2367468Snon *  3. The name of the author may not be used to endorse or promote products
2467468Snon *     derived from this software without specific prior written permission.
2567468Snon *
2667468Snon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2767468Snon * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2867468Snon * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2967468Snon * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
3067468Snon * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3167468Snon * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
3267468Snon * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3367468Snon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3467468Snon * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
3567468Snon * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3667468Snon * POSSIBILITY OF SUCH DAMAGE.
3767468Snon */
38119418Sobrien
39119418Sobrien#include <sys/cdefs.h>
40119418Sobrien__FBSDID("$FreeBSD$");
4167468Snon
4267468Snon#include <sys/param.h>
4367468Snon#include <sys/systm.h>
4467468Snon#include <sys/kernel.h>
4567468Snon#include <sys/bio.h>
4667468Snon#include <sys/buf.h>
4767468Snon#include <sys/queue.h>
4867468Snon#include <sys/malloc.h>
4967468Snon#include <sys/errno.h>
5067468Snon
5167468Snon#include <machine/cpu.h>
5267468Snon#include <machine/bus.h>
5367468Snon
54126928Speter#include <compat/netbsd/dvcfg.h>
5567468Snon
5667468Snon#include <cam/scsi/scsi_low.h>
5767468Snon
5867468Snon#include <dev/ncv/ncr53c500reg.h>
5967468Snon#include <dev/ncv/ncr53c500hw.h>
6067468Snon#include <dev/ncv/ncr53c500var.h>
6167468Snon
6267468Snon#include <dev/ncv/ncr53c500hwtab.h>
6367468Snon
6479697Snon#define	NCV_MAX_DATA_SIZE	(64 * 1024)
6579697Snon#define	NCV_DELAY_MAX		(2 * 1000 * 1000)
6679697Snon#define	NCV_DELAY_INTERVAL	(1)
6779697Snon#define	NCV_PADDING_SIZE	(32)
6879697Snon
6967468Snon/***************************************************
7079697Snon * IO control
7179697Snon ***************************************************/
7279697Snon#define	NCV_READ_INTERRUPTS_DRIVEN	0x0001
7379697Snon#define	NCV_WRITE_INTERRUPTS_DRIVEN	0x0002
7479697Snon#define	NCV_ENABLE_FAST_SCSI		0x0010
7579697Snon#define	NCV_FAST_INTERRUPTS		0x0100
7679697Snon
7779697Snonu_int ncv_io_control = NCV_IO_CONTROL_FLAGS;
7879697Snonint ncv_data_read_bytes = 4096;
7979697Snonint ncv_data_write_bytes = 4096;
8079697Snon
8179697Snon/***************************************************
8267468Snon * DEBUG
8367468Snon ***************************************************/
8467468Snon#ifdef	NCV_DEBUG
8589093Smsmithstatic int ncv_debug;
8667468Snon#endif	/* NCV_DEBUG */
8767468Snon
8867468Snon#ifdef	NCV_STATICS
8989093Smsmithstatic struct ncv_statics {
9067468Snon	int disconnect;
9167468Snon	int reselect;
9279697Snon} ncv_statics;
9367468Snon#endif	/* NCV_STATICS */
9467468Snon
9567468Snon/***************************************************
9679697Snon * DEVICE STRUCTURE
9767468Snon ***************************************************/
9867468Snonextern struct cfdriver ncv_cd;
9967468Snon
10067468Snon/**************************************************************
10167468Snon * DECLARE
10267468Snon **************************************************************/
10367468Snon/* static */
10492739Salfredstatic void ncv_pio_read(struct ncv_softc *, u_int8_t *, u_int);
10592739Salfredstatic void ncv_pio_write(struct ncv_softc *, u_int8_t *, u_int);
10692739Salfredstatic int ncv_msg(struct ncv_softc *, struct targ_info *, u_int);
10792739Salfredstatic int ncv_reselected(struct ncv_softc *);
10892739Salfredstatic int ncv_disconnected(struct ncv_softc *, struct targ_info *);
10967468Snon
11092739Salfredstatic __inline void ncvhw_set_count(bus_space_tag_t, bus_space_handle_t, int);
11192739Salfredstatic __inline u_int ncvhw_get_count(bus_space_tag_t, bus_space_handle_t);
11292739Salfredstatic __inline void ncvhw_select_register_0(bus_space_tag_t, bus_space_handle_t, struct ncv_hw *);
11392739Salfredstatic __inline void ncvhw_select_register_1(bus_space_tag_t, bus_space_handle_t, struct ncv_hw *);
11492739Salfredstatic __inline void ncvhw_fpush(bus_space_tag_t, bus_space_handle_t, u_int8_t *, int);
11567468Snon
11692739Salfredstatic void ncv_pdma_end(struct ncv_softc *sc, struct targ_info *);
11792739Salfredstatic int ncv_world_start(struct ncv_softc *, int);
11892739Salfredstatic void ncvhw_bus_reset(struct ncv_softc *);
11992739Salfredstatic void ncvhw_reset(bus_space_tag_t, bus_space_handle_t, struct ncv_hw *);
12092739Salfredstatic int ncvhw_check(bus_space_tag_t, bus_space_handle_t, struct ncv_hw *);
12192739Salfredstatic void ncvhw_init(bus_space_tag_t, bus_space_handle_t, struct ncv_hw *);
12292739Salfredstatic int ncvhw_start_selection(struct ncv_softc *sc, struct slccb *);
12392739Salfredstatic void ncvhw_attention(struct ncv_softc *);
12492739Salfredstatic int ncv_ccb_nexus_establish(struct ncv_softc *);
12592739Salfredstatic int ncv_lun_nexus_establish(struct ncv_softc *);
12692739Salfredstatic int ncv_target_nexus_establish(struct ncv_softc *);
12792739Salfredstatic int ncv_targ_init(struct ncv_softc *, struct targ_info *, int);
12892739Salfredstatic int ncv_catch_intr(struct ncv_softc *);
12967468Snon#ifdef	NCV_POWER_CONTROL
13092739Salfredstatic int ncvhw_power(struct ncv_softc *, u_int);
13179697Snon#endif	/* NCV_POWER_CONTROL */
13292739Salfredstatic __inline void ncv_setup_and_start_pio(struct ncv_softc *, u_int);
13367468Snon
13467468Snonstruct scsi_low_funcs ncv_funcs = {
13567468Snon	SC_LOW_INIT_T ncv_world_start,
13667468Snon	SC_LOW_BUSRST_T ncvhw_bus_reset,
13773025Snon	SC_LOW_TARG_INIT_T ncv_targ_init,
13879697Snon	SC_LOW_LUN_INIT_T NULL,
13967468Snon
14067468Snon	SC_LOW_SELECT_T ncvhw_start_selection,
14179697Snon	SC_LOW_NEXUS_T ncv_lun_nexus_establish,
14279697Snon	SC_LOW_NEXUS_T ncv_ccb_nexus_establish,
14367468Snon
14467468Snon	SC_LOW_ATTEN_T ncvhw_attention,
14567468Snon	SC_LOW_MSG_T ncv_msg,
14667468Snon
14779697Snon	SC_LOW_TIMEOUT_T NULL,
14867468Snon	SC_LOW_POLL_T ncvintr,
14967468Snon
15067468Snon	NULL,	/* SC_LOW_POWER_T ncvhw_power, */
15167468Snon};
15267468Snon
15367468Snon/**************************************************************
15467468Snon * hwfuncs
15567468Snon **************************************************************/
15667468Snonstatic __inline void
15767468Snonncvhw_select_register_0(iot, ioh, hw)
15867468Snon	bus_space_tag_t iot;
15967468Snon	bus_space_handle_t ioh;
16067468Snon	struct ncv_hw *hw;
16167468Snon{
16267468Snon
16379697Snon	bus_space_write_1(iot, ioh, cr0_cfg4, hw->hw_cfg4);
16467468Snon}
16567468Snon
16667468Snonstatic __inline void
16767468Snonncvhw_select_register_1(iot, ioh, hw)
16867468Snon	bus_space_tag_t iot;
16967468Snon	bus_space_handle_t ioh;
17067468Snon	struct ncv_hw *hw;
17167468Snon{
17267468Snon
17379697Snon	bus_space_write_1(iot, ioh, cr1_cfg5, hw->hw_cfg5);
17467468Snon}
17567468Snon
17667468Snonstatic __inline void
17767468Snonncvhw_fpush(iot, ioh, buf, len)
17867468Snon	bus_space_tag_t iot;
17967468Snon	bus_space_handle_t ioh;
18067468Snon	u_int8_t *buf;
18167468Snon	int len;
18267468Snon{
18367468Snon	int ptr;
18467468Snon
18567468Snon	for (ptr = 0; ptr < len; ptr ++)
18667468Snon		bus_space_write_1(iot, ioh, cr0_sfifo, buf[ptr]);
18767468Snon}
18867468Snon
18979697Snonstatic __inline void
19079697Snonncvhw_set_count(iot, ioh, count)
19179697Snon	bus_space_tag_t iot;
19279697Snon	bus_space_handle_t ioh;
19379697Snon	int count;
19479697Snon{
19579697Snon
19679697Snon	bus_space_write_1(iot, ioh, cr0_tclsb, (u_int8_t) count);
19779697Snon	bus_space_write_1(iot, ioh, cr0_tcmsb, (u_int8_t) (count >> NBBY));
19879697Snon	bus_space_write_1(iot, ioh, cr0_tchsb, (u_int8_t) (count >> (NBBY * 2)));
19979697Snon}
20079697Snon
20179697Snonstatic __inline u_int
20279697Snonncvhw_get_count(iot, ioh)
20379697Snon	bus_space_tag_t iot;
20479697Snon	bus_space_handle_t ioh;
20579697Snon{
20679697Snon	u_int count;
20779697Snon
20879697Snon	count = (u_int) bus_space_read_1(iot, ioh, cr0_tclsb);
20979697Snon	count |= ((u_int) bus_space_read_1(iot, ioh, cr0_tcmsb)) << NBBY;
21079697Snon	count |= ((u_int) bus_space_read_1(iot, ioh, cr0_tchsb)) << (NBBY * 2);
21179697Snon	return count;
21279697Snon}
21379697Snon
21467468Snonstatic int
21567468Snonncvhw_check(iot, ioh, hw)
21667468Snon	bus_space_tag_t iot;
21767468Snon	bus_space_handle_t ioh;
21867468Snon	struct ncv_hw *hw;
21967468Snon{
22067468Snon	u_int8_t stat;
22167468Snon
22267468Snon	ncvhw_select_register_0(iot, ioh, hw);
22367468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP | CMD_DMA);
22467468Snon	if (bus_space_read_1(iot, ioh, cr0_cmd) != (CMD_NOP | CMD_DMA))
22567468Snon	{
22667468Snon#ifdef	NCV_DEBUG
22767468Snon		printf("ncv: cr0_cmd CMD_NOP|CMD_DMA failed\n");
22867468Snon#endif	/* NCV_DEBUG */
22967468Snon		return ENODEV;
23067468Snon	}
23167468Snon
23267468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP);
23367468Snon	if (bus_space_read_1(iot, ioh, cr0_cmd) != CMD_NOP)
23467468Snon	{
23567468Snon#ifdef	NCV_DEBUG
23667468Snon		printf("ncv: cr0_cmd CMD_NOP failed\n");
23767468Snon#endif	/* NCV_DEBUG */
23867468Snon		return ENODEV;
23967468Snon	}
24067468Snon
24167468Snon	/* hardware reset */
24267468Snon	ncvhw_reset(iot, ioh, hw);
24367468Snon	ncvhw_init(iot, ioh, hw);
24467468Snon
24567468Snon	/* bus reset */
24667468Snon	ncvhw_select_register_0(iot, ioh, hw);
24767468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH);
24867468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_RSTSCSI);
24967468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP | CMD_DMA);
250240172Sjhb	DELAY(100 * 1000);
25167468Snon
25267468Snon	/* check response */
25367468Snon	bus_space_read_1(iot, ioh, cr0_stat);
25467468Snon	stat = bus_space_read_1(iot, ioh, cr0_istat);
255240172Sjhb	DELAY(1000);
25667468Snon
25767468Snon	if (((stat & INTR_SBR) == 0) ||
25867468Snon	    (bus_space_read_1(iot, ioh, cr0_istat) & INTR_SBR))
25967468Snon	{
26067468Snon#ifdef	NCV_DEBUG
26167468Snon		printf("ncv: cr0_istat SCSI BUS RESET failed\n");
26267468Snon#endif	/* NCV_DEBUG */
26367468Snon		return ENODEV;
26467468Snon	}
26567468Snon
26667468Snon	return 0;
26767468Snon}
26867468Snon
26967468Snonstatic void
27067468Snonncvhw_reset(iot, ioh, hw)
27167468Snon	bus_space_tag_t iot;
27267468Snon	bus_space_handle_t ioh;
27367468Snon	struct ncv_hw *hw;
27467468Snon{
27567468Snon
27667468Snon	ncvhw_select_register_0(iot, ioh, hw);
27767468Snon
27867468Snon	/* dummy cmd twice */
27967468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP);
28067468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP);
28167468Snon
28267468Snon	/* chip reset */
28367468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_RSTCHIP);
28467468Snon
28567468Snon	/* again dummy cmd twice */
28667468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP);
28767468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP);
28867468Snon}
28967468Snon
29067468Snonstatic void
29167468Snonncvhw_init(iot, ioh, hw)
29267468Snon	bus_space_tag_t iot;
29367468Snon	bus_space_handle_t ioh;
29467468Snon	struct ncv_hw *hw;
29567468Snon{
29667468Snon
29767468Snon	ncvhw_select_register_0(iot, ioh, hw);
29879697Snon	bus_space_write_1(iot, ioh, cr0_clk, hw->hw_clk);
29967468Snon	bus_space_write_1(iot, ioh, cr0_srtout, SEL_TOUT);
30067468Snon	bus_space_write_1(iot, ioh, cr0_period, 0);
30167468Snon	bus_space_write_1(iot, ioh, cr0_offs, 0);
30267468Snon
30379697Snon	bus_space_write_1(iot, ioh, cr0_cfg1, hw->hw_cfg1);
30479697Snon	bus_space_write_1(iot, ioh, cr0_cfg2, hw->hw_cfg2);
30579697Snon	bus_space_write_1(iot, ioh, cr0_cfg3, hw->hw_cfg3);
30667468Snon	bus_space_write_1(iot, ioh, cr0_tchsb, 0);
30767468Snon
30867468Snon	ncvhw_select_register_1(iot, ioh, hw);
30967468Snon	bus_space_write_1(iot, ioh, cr1_fstat, 0x0);
31067468Snon	bus_space_write_1(iot, ioh, cr1_pflag, 0x0);
31167468Snon	bus_space_write_1(iot, ioh, cr1_atacmd, ATACMD_ENGAGE);
31267468Snon
31367468Snon	ncvhw_select_register_0(iot, ioh, hw);
31467468Snon}
31567468Snon
31667468Snon#ifdef	NCV_POWER_CONTROL
31767468Snonstatic int
31867468Snonncvhw_power(sc, flags)
31967468Snon	struct ncv_softc *sc;
32067468Snon	u_int flags;
32167468Snon{
32267468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
32367468Snon	bus_space_tag_t iot = sc->sc_iot;
32467468Snon	bus_space_handle_t ioh = sc->sc_ioh;
32567468Snon
32667468Snon	if (flags == SCSI_LOW_POWDOWN)
32767468Snon	{
328240325Sjhb		device_printf(slp->sl_dev, "power down\n");
32967468Snon		ncvhw_select_register_1(iot, ioh, &sc->sc_hw);
33067468Snon		bus_space_write_1(iot, ioh, cr1_atacmd, ATACMD_POWDOWN);
33167468Snon	}
33267468Snon	else
33367468Snon	{
33467468Snon		switch (sc->sc_rstep)
33567468Snon		{
33667468Snon		case 0:
337240325Sjhb			device_printf(slp->sl_dev, "resume step O\n");
33867468Snon			ncvhw_select_register_1(iot, ioh, &sc->sc_hw);
33967468Snon			bus_space_write_1(iot, ioh, cr1_atacmd, ATACMD_ENGAGE);
34067468Snon			break;
34167468Snon
34267468Snon		case 1:
343240325Sjhb			device_printf(slp->sl_dev, "resume step I\n");
34467468Snon			ncvhw_reset(iot, ioh, &sc->sc_hw);
34567468Snon			ncvhw_init(iot, ioh, &sc->sc_hw);
34667468Snon			break;
34767468Snon		}
34867468Snon	}
34967468Snon
35067468Snon	return 0;
35167468Snon}
35267468Snon#endif	/* NCV_POWER_CONTROL */
35367468Snon
35467468Snon/**************************************************************
35567468Snon * scsi low interface
35667468Snon **************************************************************/
35767468Snonstatic void
35867468Snonncvhw_attention(sc)
35967468Snon	struct ncv_softc *sc;
36067468Snon{
36167468Snon
36267468Snon	bus_space_write_1(sc->sc_iot, sc->sc_ioh, cr0_cmd, CMD_SETATN);
363240172Sjhb	DELAY(10);
36467468Snon}
36567468Snon
36667468Snonstatic void
36767468Snonncvhw_bus_reset(sc)
36867468Snon	struct ncv_softc *sc;
36967468Snon{
37067468Snon	bus_space_tag_t iot = sc->sc_iot;
37167468Snon	bus_space_handle_t ioh = sc->sc_ioh;
37267468Snon
37367468Snon	ncvhw_select_register_0(iot, ioh, &sc->sc_hw);
37467468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH);
37567468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_RSTSCSI);
37667468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_NOP | CMD_DMA);
37767468Snon}
37867468Snon
37967468Snonstatic int
38067468Snonncvhw_start_selection(sc, cb)
38167468Snon	struct ncv_softc *sc;
38267468Snon	struct slccb *cb;
38367468Snon{
38467468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
38567468Snon	bus_space_tag_t iot = sc->sc_iot;
38667468Snon	bus_space_handle_t ioh = sc->sc_ioh;
38767468Snon	struct targ_info *ti = cb->ti;
38879697Snon	int s, len;
38979697Snon	u_int flags;
39079697Snon	u_int8_t cmd;
39167468Snon
39279697Snon	sc->sc_tmaxcnt = cb->ccb_tcmax * 1000 * 1000;
39367468Snon	sc->sc_compseq = 0;
39479697Snon	if (scsi_low_is_msgout_continue(ti, SCSI_LOW_MSG_IDENTIFY) == 0)
39579697Snon	{
39679697Snon		cmd = CMD_SELATN;
39779697Snon		sc->sc_selstop = 0;
39879697Snon		flags = SCSI_LOW_MSGOUT_UNIFY | SCSI_LOW_MSGOUT_INIT;
39979697Snon	}
40079697Snon	else if (scsi_low_is_msgout_continue(ti,
40179697Snon			SCSI_LOW_MSG_IDENTIFY | SCSI_LOW_MSG_SIMPLE_QTAG) == 0)
40279697Snon	{
40379697Snon		cmd = CMD_SELATN3;
40479697Snon		sc->sc_selstop = 0;
40579697Snon		flags = SCSI_LOW_MSGOUT_UNIFY | SCSI_LOW_MSGOUT_INIT;
40679697Snon	}
40779697Snon	else
40879697Snon	{
40979697Snon		cmd = CMD_SELATNS;
41079697Snon		sc->sc_selstop = 1;
41179697Snon		flags = SCSI_LOW_MSGOUT_INIT;
41279697Snon	}
41379697Snon
41467468Snon	ncvhw_select_register_0(iot, ioh, &sc->sc_hw);
41579697Snon	if ((bus_space_read_1(iot, ioh, cr0_stat) & STAT_INT) != 0)
41679697Snon		return SCSI_LOW_START_FAIL;
41767468Snon
41879697Snon	ncv_target_nexus_establish(sc);
41979697Snon
42079697Snon	len = scsi_low_msgout(slp, ti, flags);
42179697Snon	if (sc->sc_selstop == 0)
42279697Snon		scsi_low_cmd(slp, ti);
42379697Snon
42467468Snon	s = splhigh();
42579697Snon	if ((bus_space_read_1(iot, ioh, cr0_stat) & STAT_INT) != 0)
42667468Snon	{
42767468Snon		splx(s);
42867468Snon		return SCSI_LOW_START_FAIL;
42967468Snon	}
43067468Snon
43167468Snon	bus_space_write_1(iot, ioh, cr0_dstid, ti->ti_id);
43267468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH);
43379697Snon	ncvhw_fpush(iot, ioh, ti->ti_msgoutstr, len);
43479697Snon	if (sc->sc_selstop == 0)
43567468Snon	{
43667468Snon		ncvhw_fpush(iot, ioh,
43767468Snon			    slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen);
43867468Snon	}
43979697Snon	bus_space_write_1(iot, ioh, cr0_cmd, cmd);
44067468Snon	splx(s);
44167468Snon
44267468Snon	SCSI_LOW_SETUP_PHASE(ti, PH_SELSTART);
44367468Snon	return SCSI_LOW_START_OK;
44467468Snon}
44567468Snon
44667468Snonstatic int
44767468Snonncv_world_start(sc, fdone)
44867468Snon	struct ncv_softc *sc;
44967468Snon	int fdone;
45067468Snon{
45167468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
45267468Snon	bus_space_tag_t iot = sc->sc_iot;
45367468Snon	bus_space_handle_t ioh = sc->sc_ioh;
45467468Snon	u_int8_t stat;
45567468Snon
45679697Snon	if ((slp->sl_cfgflags & CFG_NOPARITY) == 0)
45779697Snon		sc->sc_hw.hw_cfg1 |= C1_PARENB;
45879697Snon	else
45979697Snon		sc->sc_hw.hw_cfg1 &= ~C1_PARENB;
46079697Snon
46167468Snon	ncvhw_reset(iot, ioh, &sc->sc_hw);
46267468Snon	ncvhw_init(iot, ioh, &sc->sc_hw);
46367468Snon
46471466Sjhb	scsi_low_bus_reset(slp);
46567468Snon
46667468Snon	ncvhw_select_register_0(iot, ioh, &sc->sc_hw);
46767468Snon	bus_space_read_1(sc->sc_iot, sc->sc_ioh, cr0_stat);
46867468Snon	stat = bus_space_read_1(sc->sc_iot, sc->sc_ioh, cr0_istat);
469240172Sjhb	DELAY(1000);
47067468Snon
47167468Snon	if (((stat & INTR_SBR) == 0) ||
47267468Snon	    (bus_space_read_1(sc->sc_iot, sc->sc_ioh, cr0_istat) & INTR_SBR))
47367468Snon		return ENODEV;
47467468Snon
47567468Snon	return 0;
47667468Snon}
47767468Snon
47867468Snonstatic int
47967468Snonncv_msg(sc, ti, msg)
48067468Snon	struct ncv_softc *sc;
48167468Snon	struct targ_info *ti;
48267468Snon	u_int msg;
48367468Snon{
48479697Snon	bus_space_tag_t iot = sc->sc_iot;
48579697Snon	bus_space_handle_t ioh = sc->sc_ioh;
48673025Snon	struct ncv_targ_info *nti = (void *) ti;
48767468Snon	u_int hwcycle, period;
48867468Snon
48979697Snon	if ((msg & SCSI_LOW_MSG_WIDE) != 0)
49079697Snon	{
49179697Snon		if (ti->ti_width != SCSI_LOW_BUS_WIDTH_8)
49279697Snon		{
49379697Snon			ti->ti_width = SCSI_LOW_BUS_WIDTH_8;
49479697Snon			return EINVAL;
49579697Snon		}
49679697Snon		return 0;
49779697Snon	}
49879697Snon
49967468Snon	if ((msg & SCSI_LOW_MSG_SYNCH) == 0)
50067468Snon		return 0;
50167468Snon
50273025Snon	period = ti->ti_maxsynch.period;
50379697Snon	hwcycle = (sc->sc_hw.hw_clk == 0) ? 40 : (5 * sc->sc_hw.hw_clk);
50479697Snon	hwcycle = 1000 / hwcycle;
50567468Snon
50667468Snon	if (period < 200 / 4 && period >= 100 / 4)
50779697Snon		nti->nti_reg_cfg3 |= sc->sc_hw.hw_cfg3_fscsi;
50867468Snon	else
50979697Snon		nti->nti_reg_cfg3 &= ~sc->sc_hw.hw_cfg3_fscsi;
51067468Snon
51167468Snon	period = ((period * 40 / hwcycle) + 5) / 10;
51273025Snon	nti->nti_reg_period = period & 0x1f;
51373025Snon	nti->nti_reg_offset = ti->ti_maxsynch.offset;
51479697Snon
51579697Snon	bus_space_write_1(iot, ioh, cr0_period, nti->nti_reg_period);
51679697Snon	bus_space_write_1(iot, ioh, cr0_offs, nti->nti_reg_offset);
51779697Snon	bus_space_write_1(iot, ioh, cr0_cfg3, nti->nti_reg_cfg3);
51867468Snon	return 0;
51967468Snon}
52067468Snon
52167468Snonstatic int
52279697Snonncv_targ_init(sc, ti, action)
52367468Snon	struct ncv_softc *sc;
52467468Snon	struct targ_info *ti;
52579697Snon	int action;
52667468Snon{
52773025Snon	struct ncv_targ_info *nti = (void *) ti;
52867468Snon
52979697Snon	if (action == SCSI_LOW_INFO_ALLOC || action == SCSI_LOW_INFO_REVOKE)
53079697Snon	{
53179697Snon		ti->ti_width = SCSI_LOW_BUS_WIDTH_8;
53279697Snon		ti->ti_maxsynch.period = sc->sc_hw.hw_mperiod;
53379697Snon		ti->ti_maxsynch.offset = sc->sc_hw.hw_moffset;
53467468Snon
53579697Snon		nti->nti_reg_cfg3 = sc->sc_hw.hw_cfg3;
53679697Snon		nti->nti_reg_period = 0;
53779697Snon		nti->nti_reg_offset = 0;
53879697Snon	}
53967468Snon	return 0;
54067468Snon}
54167468Snon
54267468Snon/**************************************************************
54367468Snon * General probe attach
54467468Snon **************************************************************/
54592739Salfredstatic int ncv_setup_img(struct ncv_hw *, u_int, int);
54667468Snon
54767468Snonstatic int
54879697Snonncv_setup_img(hw, dvcfg, hostid)
54967468Snon	struct ncv_hw *hw;
55067468Snon	u_int dvcfg;
55179697Snon	int hostid;
55267468Snon{
55367468Snon
55467468Snon	if (NCV_CLKFACTOR(dvcfg) > CLK_35M_F)
55567468Snon	{
55667468Snon		printf("ncv: invalid dvcfg flags\n");
55767468Snon		return EINVAL;
55867468Snon	}
55967468Snon
56067468Snon	if (NCV_C5IMG(dvcfg) != 0)
56167468Snon	{
56279697Snon		hw->hw_cfg5 = NCV_C5IMG(dvcfg);
56379697Snon		hw->hw_clk = NCV_CLKFACTOR(dvcfg);
56467468Snon
56579697Snon		if ((ncv_io_control & NCV_ENABLE_FAST_SCSI) != 0 &&
56679697Snon		    (NCV_SPECIAL(dvcfg) & NCVHWCFG_MAX10M) != 0)
56779697Snon			hw->hw_mperiod = 100 / 4;
56867468Snon
56967468Snon		if (NCV_SPECIAL(dvcfg) & NCVHWCFG_FIFOBUG)
57079697Snon			hw->hw_cfg3_fclk = 0x04;
57167468Snon
57267468Snon		if (NCV_SPECIAL(dvcfg) & NCVHWCFG_SCSI1)
57379697Snon			hw->hw_cfg2 &= ~C2_SCSI2;
57467468Snon
57567468Snon		if (NCV_SPECIAL(dvcfg) & NCVHWCFG_SLOW)
57679697Snon			hw->hw_cfg1 |= C1_SLOW;
57767468Snon	}
57867468Snon
57967468Snon	/* setup configuration image 3 */
58079697Snon	if (hw->hw_clk != CLK_40M_F && hw->hw_clk <= CLK_25M_F)
58179697Snon		hw->hw_cfg3 &= ~hw->hw_cfg3_fclk;
58279697Snon	else
58379697Snon		hw->hw_cfg3 |= hw->hw_cfg3_fclk;
58467468Snon
58567468Snon	/* setup configuration image 1 */
58679697Snon	hw->hw_cfg1 = (hw->hw_cfg1 & 0xf0) | hostid;
58767468Snon	return 0;
58867468Snon}
58967468Snon
59067468Snonint
59167468Snonncvprobesubr(iot, ioh, dvcfg, hsid)
59267468Snon	bus_space_tag_t iot;
59367468Snon	bus_space_handle_t ioh;
59467468Snon	u_int dvcfg;
59567468Snon	int hsid;
59667468Snon{
59767468Snon	struct ncv_hw hwtab;
59867468Snon
59967468Snon	hwtab = ncv_template;
60067468Snon	if (ncv_setup_img(&hwtab, dvcfg, hsid))
60167468Snon		return 0;
60267468Snon	if (ncvhw_check(iot, ioh, &hwtab) != 0)
60367468Snon		return 0;
60467468Snon
60567468Snon	return 1;
60667468Snon}
60767468Snon
60867468Snonvoid
60967468Snonncvattachsubr(sc)
61067468Snon	struct ncv_softc *sc;
61167468Snon{
61267468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
61367468Snon
61467468Snon	printf("\n");
61567468Snon	sc->sc_hw = ncv_template;
61667468Snon	ncv_setup_img(&sc->sc_hw, slp->sl_cfgflags, slp->sl_hostid);
61767468Snon	slp->sl_funcs = &ncv_funcs;
61879697Snon	slp->sl_flags |= HW_READ_PADDING;
61979697Snon	sc->sc_tmaxcnt = SCSI_LOW_MIN_TOUT * 1000 * 1000; /* default */
62079697Snon
62179697Snon	(void) scsi_low_attach(slp, 0, NCV_NTARGETS, NCV_NLUNS,
62279697Snon			       sizeof(struct ncv_targ_info), 0);
62367468Snon}
62467468Snon
62567468Snon/**************************************************************
62667468Snon * PDMA
62767468Snon **************************************************************/
62867468Snonstatic __inline void
62979697Snonncv_setup_and_start_pio(sc, reqlen)
63079697Snon	struct ncv_softc *sc;
63179697Snon	u_int reqlen;
63267468Snon{
63379697Snon	bus_space_tag_t iot = sc->sc_iot;
63479697Snon	bus_space_handle_t ioh = sc->sc_ioh;
63567468Snon
63679697Snon	ncvhw_select_register_0(iot, ioh, &sc->sc_hw);
63779697Snon	ncvhw_set_count(iot, ioh, reqlen);
63879697Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_TRANS | CMD_DMA);
63967468Snon
64079697Snon	ncvhw_select_register_1(iot, ioh, &sc->sc_hw);
64179697Snon	bus_space_write_1(iot, ioh, cr1_fstat, FIFO_EN);
64267468Snon}
64367468Snon
64479697Snonstatic void
64567468Snonncv_pdma_end(sc, ti)
64667468Snon	struct ncv_softc *sc;
64767468Snon	struct targ_info *ti;
64867468Snon{
64967468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
65067468Snon	bus_space_tag_t iot = sc->sc_iot;
65167468Snon	bus_space_handle_t ioh = sc->sc_ioh;
65267468Snon	int len;
65367468Snon
65467468Snon	slp->sl_flags &= ~HW_PDMASTART;
65579697Snon	if (slp->sl_Qnexus == NULL)
65679697Snon	{
65779697Snon		slp->sl_error |= PDMAERR;
65879697Snon		goto out;
65979697Snon	}
66079697Snon
66167468Snon	if (ti->ti_phase == PH_DATA)
66267468Snon	{
66367468Snon		len = ncvhw_get_count(sc->sc_iot, sc->sc_ioh);
66467468Snon		if (slp->sl_scp.scp_direction == SCSI_LOW_WRITE)
66567468Snon			len += (bus_space_read_1(sc->sc_iot, sc->sc_ioh,
66667468Snon				cr0_sffl) & CR0_SFFLR_BMASK);
66767468Snon
66879697Snon		if ((u_int) len <= (u_int) sc->sc_sdatalen)
66967468Snon		{
67067468Snon			if ((slp->sl_scp.scp_direction == SCSI_LOW_READ) &&
67167468Snon			    sc->sc_tdatalen != len)
67267468Snon				goto bad;
67379697Snon
67479697Snon			len = sc->sc_sdatalen - len;
67579697Snon			if ((u_int) len > (u_int) slp->sl_scp.scp_datalen)
67679697Snon				goto bad;
67779697Snon
67879697Snon			slp->sl_scp.scp_data += len;
67979697Snon			slp->sl_scp.scp_datalen -= len;
68067468Snon		}
68167468Snon		else
68267468Snon		{
68367468Snonbad:
68479697Snon			if ((slp->sl_error & PDMAERR) == 0)
68579697Snon			{
686240325Sjhb				device_printf(slp->sl_dev,
687240325Sjhb				    "strange cnt hw 0x%x soft 0x%x\n", len,
688240325Sjhb				    slp->sl_scp.scp_datalen);
68979697Snon			}
69067468Snon			slp->sl_error |= PDMAERR;
69167468Snon		}
69279697Snon		scsi_low_data_finish(slp);
69367468Snon	}
69467468Snon	else
69567468Snon	{
696240325Sjhb		device_printf(slp->sl_dev, "data phase miss\n");
69767468Snon		slp->sl_error |= PDMAERR;
69867468Snon	}
69967468Snon
70079697Snonout:
70167468Snon	ncvhw_select_register_1(iot, ioh, &sc->sc_hw);
70267468Snon	bus_space_write_1(iot, ioh, cr1_fstat, 0);
70367468Snon	ncvhw_select_register_0(iot, ioh, &sc->sc_hw);
70467468Snon}
70567468Snon
70667468Snonstatic void
70767468Snonncv_pio_read(sc, buf, reqlen)
70867468Snon	struct ncv_softc *sc;
70967468Snon	u_int8_t *buf;
71067468Snon	u_int reqlen;
71167468Snon{
71267468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
71367468Snon	bus_space_tag_t iot = sc->sc_iot;
71467468Snon	bus_space_handle_t ioh = sc->sc_ioh;
71579697Snon	int tout;
71667468Snon	register u_int8_t fstat;
71767468Snon
71879697Snon	ncv_setup_and_start_pio(sc, reqlen);
71967468Snon	slp->sl_flags |= HW_PDMASTART;
72079697Snon	sc->sc_sdatalen = reqlen;
72179697Snon	tout = sc->sc_tmaxcnt;
72267468Snon
72379697Snon	while (reqlen >= FIFO_F_SZ && tout -- > 0)
72467468Snon	{
72567468Snon		fstat = bus_space_read_1(iot, ioh, cr1_fstat);
72679697Snon		if (fstat == (u_int8_t) -1)
72779697Snon			goto out;
72867468Snon		if (fstat & FIFO_F)
72967468Snon		{
73067468Snon#define	NCV_FAST32_ACCESS
73167468Snon#ifdef	NCV_FAST32_ACCESS
73267468Snon			bus_space_read_multi_4(iot, ioh, cr1_fdata,
73367468Snon				(u_int32_t *) buf, FIFO_F_SZ / 4);
73467468Snon#else	/* !NCV_FAST32_ACCESS */
73567468Snon			bus_space_read_multi_2(iot, ioh, cr1_fdata,
73667468Snon				(u_int16_t *) buf, FIFO_F_SZ / 2);
73767468Snon#endif	/* !NCV_FAST32_ACCESS */
73867468Snon			buf += FIFO_F_SZ;
73967468Snon			reqlen -= FIFO_F_SZ;
74067468Snon		}
74179697Snon		else
74279697Snon		{
74379697Snon			if (fstat & FIFO_BRK)
74479697Snon				break;
74567468Snon
746240172Sjhb			DELAY(1);
74767468Snon		}
74867468Snon	}
74967468Snon
75079697Snon	while (reqlen > 0 && tout -- > 0)
75167468Snon	{
75267468Snon		fstat = bus_space_read_1(iot, ioh, cr1_fstat);
75367468Snon		if ((fstat & FIFO_E) == 0)
75467468Snon		{
75567468Snon			*buf++ = bus_space_read_1(iot, ioh, cr1_fdata);
75667468Snon			reqlen --;
75767468Snon		}
75879697Snon		else
75979697Snon		{
76079697Snon			 if (fstat & FIFO_BRK)
76179697Snon				break;
76267468Snon
763240172Sjhb			DELAY(1);
76479697Snon		}
76567468Snon	}
76667468Snon
76779697Snonout:
76867468Snon	ncvhw_select_register_0(iot, ioh, &sc->sc_hw);
76967468Snon	sc->sc_tdatalen = reqlen;
77067468Snon}
77167468Snon
77267468Snonstatic void
77367468Snonncv_pio_write(sc, buf, reqlen)
77467468Snon	struct ncv_softc *sc;
77567468Snon	u_int8_t *buf;
77667468Snon	u_int reqlen;
77767468Snon{
77867468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
77967468Snon	bus_space_tag_t iot = sc->sc_iot;
78067468Snon	bus_space_handle_t ioh = sc->sc_ioh;
78179697Snon	int tout;
78267468Snon	register u_int8_t fstat;
78367468Snon
78479697Snon	ncv_setup_and_start_pio(sc, reqlen);
78579697Snon	sc->sc_sdatalen = reqlen;
78679697Snon	tout = sc->sc_tmaxcnt;
78767468Snon	slp->sl_flags |= HW_PDMASTART;
78867468Snon
78979697Snon	while (reqlen >= FIFO_F_SZ && tout -- > 0)
79067468Snon	{
79167468Snon		fstat = bus_space_read_1(iot, ioh, cr1_fstat);
79267468Snon		if (fstat & FIFO_BRK)
79367468Snon			goto done;
79467468Snon
79579697Snon		if ((fstat & FIFO_E) != 0)
79667468Snon		{
79767468Snon#ifdef	NCV_FAST32_ACCESS
79867468Snon			bus_space_write_multi_4(iot, ioh, cr1_fdata,
79967468Snon				(u_int32_t *) buf, FIFO_F_SZ / 4);
80067468Snon#else	/* !NCV_FAST32_ACCESS */
80167468Snon			bus_space_write_multi_2(iot, ioh, cr1_fdata,
80267468Snon				(u_int16_t *) buf, FIFO_F_SZ / 2);
80367468Snon#endif	/* !NCV_FAST32_ACCESS */
80467468Snon			buf += FIFO_F_SZ;
80567468Snon			reqlen -= FIFO_F_SZ;
80667468Snon		}
80773025Snon		else
80879697Snon		{
809240172Sjhb			DELAY(1);
81079697Snon		}
81167468Snon	}
81267468Snon
81379697Snon	while (reqlen > 0 && tout -- > 0)
81467468Snon	{
81567468Snon		fstat = bus_space_read_1(iot, ioh, cr1_fstat);
81667468Snon		if (fstat & FIFO_BRK)
81767468Snon			break;
81867468Snon
81967468Snon		if ((fstat & FIFO_F) == 0) /* fifo not full */
82067468Snon		{
82167468Snon			bus_space_write_1(iot, ioh, cr1_fdata, *buf++);
82267468Snon			reqlen --;
82367468Snon		}
82473025Snon		else
82579697Snon		{
826240172Sjhb			DELAY(1);
82779697Snon		}
82867468Snon	}
82967468Snon
83067468Snondone:
83167468Snon	ncvhw_select_register_0(iot, ioh, &sc->sc_hw);
83267468Snon}
83367468Snon
83467468Snon/**************************************************************
83567468Snon * disconnect & reselect (HW low)
83667468Snon **************************************************************/
83779697Snonstatic int
83867468Snonncv_reselected(sc)
83967468Snon	struct ncv_softc *sc;
84067468Snon{
84167468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
84267468Snon	bus_space_tag_t iot = sc->sc_iot;
84367468Snon	bus_space_handle_t ioh = sc->sc_ioh;
84467468Snon	struct targ_info *ti;
84567468Snon	u_int sid;
84667468Snon
84767468Snon	if ((bus_space_read_1(iot, ioh, cr0_sffl) & CR0_SFFLR_BMASK) != 2)
84867468Snon	{
849240325Sjhb		device_printf(slp->sl_dev, "illegal fifo bytes\n");
85067468Snon		scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "chip confused");
85167468Snon		return EJUSTRETURN;
85267468Snon	}
85367468Snon
85467468Snon	sid = (u_int) bus_space_read_1(iot, ioh, cr0_sfifo);
85579697Snon	sid &= ~(1 << slp->sl_hostid);
85667468Snon	sid = ffs(sid) - 1;
85767468Snon	ti = scsi_low_reselected((struct scsi_low_softc *) sc, sid);
85867468Snon	if (ti == NULL)
85967468Snon		return EJUSTRETURN;
86067468Snon
86167468Snon#ifdef	NCV_STATICS
86279697Snon	ncv_statics.reselect ++;
86367468Snon#endif	/* NCV_STATICS */
86467468Snon	bus_space_write_1(iot, ioh, cr0_dstid, sid);
86567468Snon	return 0;
86667468Snon}
86767468Snon
86879697Snonstatic int
86967468Snonncv_disconnected(sc, ti)
87067468Snon	struct ncv_softc *sc;
87167468Snon	struct targ_info *ti;
87267468Snon{
87367468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
87467468Snon	bus_space_tag_t iot = sc->sc_iot;
87567468Snon	bus_space_handle_t ioh = sc->sc_ioh;
87667468Snon
87767468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH);
87867468Snon	bus_space_write_1(iot, ioh, cr0_cmd, CMD_ENSEL);
87967468Snon
88067468Snon#ifdef	NCV_STATICS
88179697Snon	ncv_statics.disconnect ++;
88267468Snon#endif	/* NCV_STATICS */
88367468Snon
88467468Snon	scsi_low_disconnected(slp, ti);
88567468Snon	return 1;
88667468Snon}
88767468Snon
88867468Snon/**************************************************************
88967468Snon * SEQUENCER
89067468Snon **************************************************************/
89167468Snonstatic int
89279697Snonncv_target_nexus_establish(sc)
89367468Snon	struct ncv_softc *sc;
89467468Snon{
89579697Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
89679697Snon	struct targ_info *ti = slp->sl_Tnexus;
89779697Snon	struct ncv_targ_info *nti = (void *) ti;
89867468Snon	bus_space_tag_t iot = sc->sc_iot;
89967468Snon	bus_space_handle_t ioh = sc->sc_ioh;
90067468Snon
90173025Snon	bus_space_write_1(iot, ioh, cr0_period, nti->nti_reg_period);
90273025Snon	bus_space_write_1(iot, ioh, cr0_offs, nti->nti_reg_offset);
90373025Snon	bus_space_write_1(iot, ioh, cr0_cfg3, nti->nti_reg_cfg3);
90467468Snon	return 0;
90567468Snon}
90667468Snon
90779697Snonstatic int
90879697Snonncv_lun_nexus_establish(sc)
90979697Snon	struct ncv_softc *sc;
91079697Snon{
91179697Snon
91279697Snon	return 0;
91379697Snon}
91479697Snon
91579697Snonstatic int
91679697Snonncv_ccb_nexus_establish(sc)
91779697Snon	struct ncv_softc *sc;
91879697Snon{
91979697Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
92079697Snon	struct slccb *cb = slp->sl_Qnexus;
92179697Snon
92279697Snon	sc->sc_tmaxcnt = cb->ccb_tcmax * 1000 * 1000;
92379697Snon	return 0;
92479697Snon}
92579697Snon
92679697Snonstatic int
92779697Snonncv_catch_intr(sc)
92879697Snon	struct ncv_softc *sc;
92979697Snon{
93079697Snon	bus_space_tag_t iot = sc->sc_iot;
93179697Snon	bus_space_handle_t ioh = sc->sc_ioh;
93279697Snon	int wc;
93379697Snon	register u_int8_t status;
93479697Snon
93579697Snon	for (wc = 0; wc < NCV_DELAY_MAX / NCV_DELAY_INTERVAL; wc ++)
93679697Snon	{
93779697Snon		status = bus_space_read_1(iot, ioh, cr0_stat);
93879697Snon		if ((status & STAT_INT) != 0)
93979697Snon			return 0;
94079697Snon
941240172Sjhb		DELAY(NCV_DELAY_INTERVAL);
94279697Snon	}
94379697Snon	return EJUSTRETURN;
94479697Snon}
94579697Snon
94667468Snonint
94767468Snonncvintr(arg)
94867468Snon	void *arg;
94967468Snon{
95067468Snon	struct ncv_softc *sc = arg;
95167468Snon	struct scsi_low_softc *slp = &sc->sc_sclow;
95267468Snon	bus_space_tag_t iot = sc->sc_iot;
95367468Snon	bus_space_handle_t ioh = sc->sc_ioh;
95467468Snon	struct targ_info *ti;
95567468Snon	struct buf *bp;
95679697Snon	u_int derror, flags;
95779697Snon	int len;
95867468Snon	u_int8_t regv, status, ireason;
95967468Snon
96079697Snonagain:
96167468Snon	if (slp->sl_flags & HW_INACTIVE)
96267468Snon		return 0;
96367468Snon
96467468Snon	/********************************************
96567468Snon	 * Status
96667468Snon	 ********************************************/
96767468Snon	ncvhw_select_register_0(iot, ioh, &sc->sc_hw);
96867468Snon	status = bus_space_read_1(iot, ioh, cr0_stat);
96979697Snon	if ((status & STAT_INT) == 0 || status == (u_int8_t) -1)
97067468Snon		return 0;
97167468Snon
97267468Snon	ireason = bus_space_read_1(iot, ioh, cr0_istat);
97379697Snon	if ((ireason & INTR_SBR) != 0)
97467468Snon	{
97567468Snon		u_int8_t val;
97667468Snon
97767468Snon		/* avoid power off hangup */
97867468Snon		val = bus_space_read_1(iot, ioh, cr0_cfg1);
97967468Snon		bus_space_write_1(iot, ioh, cr0_cfg1, val | C1_SRR);
98067468Snon
98167468Snon		/* status init */
98267468Snon		scsi_low_restart(slp, SCSI_LOW_RESTART_SOFT,
98367468Snon				 "bus reset (power off?)");
98467468Snon		return 1;
98567468Snon	}
98667468Snon
98767468Snon	/********************************************
98867468Snon	 * Debug section
98967468Snon	 ********************************************/
99067468Snon#ifdef	NCV_DEBUG
99167468Snon	if (ncv_debug)
99267468Snon	{
99367468Snon		scsi_low_print(slp, NULL);
994240325Sjhb		device_printf(slp->sl_dev, "st %x ist %x\n\n",
99567468Snon			status, ireason);
996131914Smarcel#ifdef	KDB
99767468Snon		if (ncv_debug > 1)
998240172Sjhb			kdb_enter(KDB_WHY_CAM, "ncv");
999131914Smarcel#endif	/* KDB */
100067468Snon	}
100167468Snon#endif	/* NCV_DEBUG */
100267468Snon
100367468Snon	/********************************************
100467468Snon	 * Reselect or Disconnect or Nexus check
100567468Snon	 ********************************************/
100667468Snon	/* (I) reselect */
100767468Snon	if (ireason == INTR_RESELECT)
100867468Snon	{
100967468Snon		if (ncv_reselected(sc) == EJUSTRETURN)
101067468Snon			return 1;
101167468Snon	}
101267468Snon
101367468Snon	/* (II) nexus */
101479697Snon	if ((ti = slp->sl_Tnexus) == NULL)
101567468Snon		return 0;
101667468Snon
101779697Snon	derror = 0;
101867468Snon	if ((status & (STAT_PE | STAT_GE)) != 0)
101967468Snon	{
102067468Snon		slp->sl_error |= PARITYERR;
102179697Snon		if ((status & PHASE_MASK) == MESSAGE_IN_PHASE)
102279697Snon			scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_PARITY, 0);
102367468Snon		else
102467468Snon			scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ERROR, 1);
102579697Snon		derror = SCSI_LOW_DATA_PE;
102667468Snon	}
102767468Snon
102867468Snon	if ((ireason & (INTR_DIS | INTR_ILL)) != 0)
102967468Snon	{
103067468Snon		if ((ireason & INTR_ILL) == 0)
103167468Snon			return ncv_disconnected(sc, ti);
103267468Snon
103367468Snon		slp->sl_error |= FATALIO;
103467468Snon		scsi_low_restart(slp, SCSI_LOW_RESTART_HARD, "illegal cmd");
103567468Snon		return 1;
103667468Snon	}
103767468Snon
103867468Snon	/********************************************
103967468Snon	 * Internal scsi phase
104067468Snon	 ********************************************/
104167468Snon	switch (ti->ti_phase)
104267468Snon	{
104367468Snon	case PH_SELSTART:
104479697Snon		scsi_low_arbit_win(slp);
104567468Snon		SCSI_LOW_SETUP_PHASE(ti, PH_SELECTED);
104667468Snon
104767468Snon		if (sc->sc_selstop == 0)
104867468Snon		{
104967468Snon			/* XXX:
105067468Snon		 	 * Here scsi phases expected are
105167468Snon			 * DATA PHASE:
105267468Snon		 	 * MSGIN     : target wants to disconnect the host.
105367468Snon			 * STATUSIN  : immediate command completed.
105479697Snon			 * CMD PHASE : command out failed
105567468Snon			 * MSGOUT    : identify command failed.
105667468Snon			 */
105767468Snon			if ((status & PHASE_MASK) != MESSAGE_OUT_PHASE)
105867468Snon				break;
105967468Snon		}
106067468Snon		else
106167468Snon		{
106267468Snon			if ((status & PHASE_MASK) != MESSAGE_OUT_PHASE)
106379697Snon				break;
106479697Snon			if ((ireason & INTR_FC) != 0)
106567468Snon			{
106679697Snon				SCSI_LOW_ASSERT_ATN(slp);
106767468Snon			}
106867468Snon		}
106979697Snon		SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT);
107067468Snon		break;
107167468Snon
107267468Snon	case PH_RESEL:
107379697Snon		ncv_target_nexus_establish(sc);
107467468Snon		if ((status & PHASE_MASK) != MESSAGE_IN_PHASE)
107567468Snon		{
1076240325Sjhb			device_printf(slp->sl_dev,
1077240325Sjhb			    "unexpected phase after reselect\n");
107879697Snon			slp->sl_error |= FATALIO;
107967468Snon			scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1);
108067468Snon			return 1;
108167468Snon		}
108267468Snon		break;
108367468Snon
108467468Snon	default:
108579697Snon		if ((slp->sl_flags & HW_PDMASTART) != 0)
108679697Snon		{
108767468Snon			ncv_pdma_end(sc, ti);
108879697Snon		}
108967468Snon		break;
109067468Snon	}
109167468Snon
109267468Snon	/********************************************
109367468Snon	 * Scsi phase sequencer
109467468Snon	 ********************************************/
109567468Snon	switch (status & PHASE_MASK)
109667468Snon	{
109767468Snon	case DATA_OUT_PHASE: /* data out */
109867468Snon		SCSI_LOW_SETUP_PHASE(ti, PH_DATA);
109967468Snon		if (scsi_low_data(slp, ti, &bp, SCSI_LOW_WRITE) != 0)
110079697Snon		{
110179697Snon			scsi_low_attention(slp);
110279697Snon		}
110367468Snon
110479697Snon		if (slp->sl_scp.scp_datalen <= 0)
110579697Snon		{
110679697Snon			if ((ireason & INTR_BS) == 0)
110779697Snon				break;
110879697Snon
110979697Snon			if ((slp->sl_error & PDMAERR) == 0)
1110240325Sjhb				device_printf(slp->sl_dev, "data underrun\n");
111179697Snon			slp->sl_error |= PDMAERR;
111279697Snon
111379697Snon			if ((slp->sl_flags & HW_WRITE_PADDING) != 0)
111479697Snon			{
111579697Snon				u_int8_t padding[NCV_PADDING_SIZE];
111679697Snon
1117240172Sjhb				bzero(padding, sizeof(padding));
111879697Snon				ncv_pio_write(sc, padding, sizeof(padding));
111979697Snon			}
112079697Snon			else
112179697Snon			{
1122240325Sjhb				device_printf(slp->sl_dev,
1123240325Sjhb				    "write padding required\n");
112479697Snon			}
112579697Snon		}
112679697Snon		else
112779697Snon		{
112879697Snon			len = slp->sl_scp.scp_datalen;
112979697Snon			if ((ncv_io_control & NCV_WRITE_INTERRUPTS_DRIVEN) != 0)
113079697Snon			{
113179697Snon				if (len > ncv_data_write_bytes)
113279697Snon					len = ncv_data_write_bytes;
113379697Snon			}
113479697Snon			ncv_pio_write(sc, slp->sl_scp.scp_data, len);
113579697Snon		}
113667468Snon		break;
113767468Snon
113867468Snon	case DATA_IN_PHASE: /* data in */
113967468Snon		SCSI_LOW_SETUP_PHASE(ti, PH_DATA);
114067468Snon		if (scsi_low_data(slp, ti, &bp, SCSI_LOW_READ) != 0)
114179697Snon		{
114279697Snon			scsi_low_attention(slp);
114379697Snon		}
114467468Snon
114579697Snon		if (slp->sl_scp.scp_datalen <= 0)
114679697Snon		{
114779697Snon			if ((ireason & INTR_BS) == 0)
114879697Snon				break;
114979697Snon
115079697Snon			if ((slp->sl_error & PDMAERR) == 0)
1151240325Sjhb				device_printf(slp->sl_dev, "data overrun\n");
115279697Snon			slp->sl_error |= PDMAERR;
115379697Snon
115479697Snon			if ((slp->sl_flags & HW_READ_PADDING) != 0)
115579697Snon			{
115679697Snon				u_int8_t padding[NCV_PADDING_SIZE];
115779697Snon
115879697Snon				ncv_pio_read(sc, padding, sizeof(padding));
115979697Snon			}
116079697Snon			else
116179697Snon			{
1162240325Sjhb				device_printf(slp->sl_dev,
1163240325Sjhb				    "read padding required\n");
116479697Snon				break;
116579697Snon			}
116679697Snon		}
116779697Snon		else
116879697Snon		{
116979697Snon			len = slp->sl_scp.scp_datalen;
117079697Snon			if ((ncv_io_control & NCV_READ_INTERRUPTS_DRIVEN) != 0)
117179697Snon			{
117279697Snon				if (len > ncv_data_read_bytes)
117379697Snon					len = ncv_data_read_bytes;
117479697Snon			}
117579697Snon			ncv_pio_read(sc, slp->sl_scp.scp_data, len);
117679697Snon		}
117767468Snon		break;
117867468Snon
117967468Snon	case COMMAND_PHASE: /* cmd out */
118067468Snon		SCSI_LOW_SETUP_PHASE(ti, PH_CMD);
118167468Snon		if (scsi_low_cmd(slp, ti) != 0)
118279697Snon		{
118379697Snon			scsi_low_attention(slp);
118479697Snon		}
118567468Snon
118667468Snon		bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH);
118767468Snon		ncvhw_fpush(iot, ioh,
118867468Snon			    slp->sl_scp.scp_cmd, slp->sl_scp.scp_cmdlen);
118967468Snon		bus_space_write_1(iot, ioh, cr0_cmd, CMD_TRANS);
119067468Snon		break;
119167468Snon
119267468Snon	case STATUS_PHASE: /* status in */
119367468Snon		SCSI_LOW_SETUP_PHASE(ti, PH_STAT);
119467468Snon		bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH);
119567468Snon		bus_space_write_1(iot, ioh, cr0_cmd, CMD_ICCS);
119667468Snon		sc->sc_compseq = 1;
119767468Snon		break;
119867468Snon
119967468Snon	default:
120067468Snon		break;
120167468Snon
120267468Snon	case MESSAGE_OUT_PHASE: /* msg out */
120367468Snon		SCSI_LOW_SETUP_PHASE(ti, PH_MSGOUT);
120467468Snon		bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH);
120567468Snon
120679697Snon		flags = SCSI_LOW_MSGOUT_UNIFY;
120779697Snon		if (ti->ti_ophase != ti->ti_phase)
120879697Snon			flags |= SCSI_LOW_MSGOUT_INIT;
120979697Snon		len = scsi_low_msgout(slp, ti, flags);
121079697Snon
121179697Snon		if (len > 1 && slp->sl_atten == 0)
121279697Snon		{
121379697Snon			scsi_low_attention(slp);
121479697Snon		}
121579697Snon
121667468Snon		ncvhw_fpush(iot, ioh, ti->ti_msgoutstr, len);
121767468Snon		bus_space_write_1(iot, ioh, cr0_cmd, CMD_TRANS);
121879697Snon		SCSI_LOW_DEASSERT_ATN(slp);
121967468Snon		break;
122067468Snon
122167468Snon	case MESSAGE_IN_PHASE: /* msg in */
122267468Snon		SCSI_LOW_SETUP_PHASE(ti, PH_MSGIN);
122367468Snon
122467468Snon		len = bus_space_read_1(iot, ioh, cr0_sffl) & CR0_SFFLR_BMASK;
122567468Snon		if (sc->sc_compseq != 0)
122667468Snon		{
122767468Snon			sc->sc_compseq = 0;
122867468Snon			if ((ireason & INTR_FC) && len == 2)
122967468Snon			{
123079697Snon				regv = bus_space_read_1(iot, ioh, cr0_sfifo);
123179697Snon				scsi_low_statusin(slp, ti, regv | derror);
123267468Snon				len --;
123367468Snon			}
123467468Snon			else
123567468Snon			{
123679697Snon				slp->sl_error |= FATALIO;
123779697Snon				scsi_low_assert_msg(slp, ti,
123879697Snon						    SCSI_LOW_MSG_ABORT, 1);
123979697Snon				bus_space_write_1(sc->sc_iot, sc->sc_ioh,
124079697Snon						  cr0_cmd, CMD_MSGOK);
124167468Snon				break;
124267468Snon			}
124367468Snon		}
124467468Snon		else if (ireason & INTR_BS)
124567468Snon		{
124667468Snon			bus_space_write_1(iot, ioh, cr0_cmd, CMD_FLUSH);
124767468Snon			bus_space_write_1(iot, ioh, cr0_cmd, CMD_TRANS);
124879697Snon			if ((ncv_io_control & NCV_FAST_INTERRUPTS) != 0)
124979697Snon			{
125079697Snon				if (ncv_catch_intr(sc) == 0)
125179697Snon					goto again;
125279697Snon			}
125367468Snon			break;
125467468Snon		}
125567468Snon
125667468Snon		if ((ireason & INTR_FC) && len == 1)
125767468Snon		{
125867468Snon			regv = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
125967468Snon					        cr0_sfifo);
126079697Snon			if (scsi_low_msgin(slp, ti, regv | derror) == 0)
126179697Snon			{
126279697Snon				if (scsi_low_is_msgout_continue(ti, 0) != 0)
126379697Snon				{
126479697Snon					scsi_low_attention(slp);
126579697Snon				}
126679697Snon			}
126767468Snon			bus_space_write_1(sc->sc_iot, sc->sc_ioh, cr0_cmd,
126867468Snon				CMD_MSGOK);
126979697Snon			if ((ncv_io_control & NCV_FAST_INTERRUPTS) != 0)
127079697Snon			{
127179697Snon				/* XXX:
127279697Snon				 * clear a pending interrupt and sync with
127379697Snon				 * a next interrupt!
127479697Snon				 */
127579697Snon				ncv_catch_intr(sc);
127679697Snon			}
127767468Snon		}
127867468Snon		else
127967468Snon		{
128079697Snon			slp->sl_error |= FATALIO;
128179697Snon			scsi_low_assert_msg(slp, ti, SCSI_LOW_MSG_ABORT, 1);
128279697Snon			bus_space_write_1(sc->sc_iot, sc->sc_ioh, cr0_cmd,
128379697Snon				CMD_MSGOK);
128467468Snon		}
128567468Snon		break;
128667468Snon	}
128767468Snon
128867468Snon	return 1;
128967468Snon}
1290