isp_sbus.c revision 289812
119370Spst/*-
219370Spst * Copyright (c) 1997-2006 by Matthew Jacob
319370Spst * All rights reserved.
419370Spst *
519370Spst * Redistribution and use in source and binary forms, with or without
619370Spst * modification, are permitted provided that the following conditions
719370Spst * are met:
819370Spst * 1. Redistributions of source code must retain the above copyright
919370Spst *    notice immediately at the beginning of the file, without modification,
1019370Spst *    this list of conditions, and the following disclaimer.
1119370Spst * 2. The name of the author may not be used to endorse or promote products
1219370Spst *    derived from this software without specific prior written permission.
1319370Spst *
1419370Spst * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1519370Spst * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1619370Spst * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1719370Spst * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
1819370Spst * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1919370Spst * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2019370Spst * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2119370Spst * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2219370Spst * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2319370Spst * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2419370Spst * SUCH DAMAGE.
2519370Spst */
2619370Spst/*
2719370Spst * SBus specific probe and attach routines for Qlogic ISP SCSI adapters.
2819370Spst * FreeBSD Version.
2919370Spst */
3019370Spst
3119370Spst#include <sys/cdefs.h>
3219370Spst__FBSDID("$FreeBSD: head/sys/dev/isp/isp_sbus.c 289812 2015-10-23 08:26:45Z mav $");
3319370Spst
3419370Spst#include <sys/param.h>
3519370Spst#include <sys/systm.h>
3619370Spst#include <sys/linker.h>
3719370Spst#include <sys/firmware.h>
3819370Spst#include <sys/bus.h>
3919370Spst#include <sys/kernel.h>
4019370Spst#include <sys/module.h>
4119370Spst#include <sys/resource.h>
4219370Spst
4319370Spst#include <dev/ofw/ofw_bus.h>
4419370Spst#include <dev/ofw/openfirm.h>
4519370Spst
4619370Spst#include <machine/bus.h>
4719370Spst#include <machine/ofw_machdep.h>
4819370Spst#include <machine/resource.h>
4919370Spst#include <sys/rman.h>
5019370Spst#include <sparc64/sbus/sbusvar.h>
5119370Spst
5219370Spst#include <dev/isp/isp_freebsd.h>
5319370Spst
5419370Spststatic uint32_t isp_sbus_rd_reg(ispsoftc_t *, int);
5519370Spststatic void isp_sbus_wr_reg(ispsoftc_t *, int, uint32_t);
5619370Spststatic int isp_sbus_rd_isr(ispsoftc_t *, uint32_t *, uint16_t *, uint16_t *);
5719370Spststatic int isp_sbus_mbxdma(ispsoftc_t *);
5819370Spststatic int isp_sbus_dmasetup(ispsoftc_t *, XS_T *, void *);
5919370Spst
6019370Spst
6119370Spststatic void isp_sbus_reset0(ispsoftc_t *);
6219370Spststatic void isp_sbus_reset1(ispsoftc_t *);
6319370Spststatic void isp_sbus_dumpregs(ispsoftc_t *, const char *);
6419370Spst
6519370Spststatic struct ispmdvec mdvec = {
6619370Spst	isp_sbus_rd_isr,
6719370Spst	isp_sbus_rd_reg,
6819370Spst	isp_sbus_wr_reg,
6919370Spst	isp_sbus_mbxdma,
7019370Spst	isp_sbus_dmasetup,
7119370Spst	isp_common_dmateardown,
7219370Spst	isp_sbus_reset0,
7319370Spst	isp_sbus_reset1,
7419370Spst	isp_sbus_dumpregs,
7519370Spst	NULL,
7619370Spst	BIU_BURST_ENABLE|BIU_PCI_CONF1_FIFO_64
7719370Spst};
7819370Spst
7919370Spststatic int isp_sbus_probe (device_t);
8019370Spststatic int isp_sbus_attach (device_t);
8119370Spststatic int isp_sbus_detach (device_t);
8219370Spst
8319370Spst
8419370Spst#define	ISP_SBD(isp)	((struct isp_sbussoftc *)isp)->sbus_dev
8519370Spststruct isp_sbussoftc {
8619370Spst	ispsoftc_t			sbus_isp;
8719370Spst	device_t			sbus_dev;
8819370Spst	struct resource *		regs;
8919370Spst	void *				irq;
9019370Spst	int				iqd;
9119370Spst	int				rgd;
9219370Spst	void *				ih;
9319370Spst	int16_t				sbus_poff[_NREG_BLKS];
9419370Spst	sdparam				sbus_param;
9519370Spst	struct isp_spi			sbus_spi;
9619370Spst	struct ispmdvec			sbus_mdvec;
9719370Spst};
9819370Spst
9919370Spst
10019370Spststatic device_method_t isp_sbus_methods[] = {
10119370Spst	/* Device interface */
10219370Spst	DEVMETHOD(device_probe,		isp_sbus_probe),
10319370Spst	DEVMETHOD(device_attach,	isp_sbus_attach),
10419370Spst	DEVMETHOD(device_detach,	isp_sbus_detach),
10519370Spst	{ 0, 0 }
10619370Spst};
10719370Spst
10819370Spststatic driver_t isp_sbus_driver = {
10919370Spst	"isp", isp_sbus_methods, sizeof (struct isp_sbussoftc)
11019370Spst};
11119370Spststatic devclass_t isp_devclass;
11219370SpstDRIVER_MODULE(isp, sbus, isp_sbus_driver, isp_devclass, 0, 0);
11319370SpstMODULE_DEPEND(isp, cam, 1, 1, 1);
11419370SpstMODULE_DEPEND(isp, firmware, 1, 1, 1);
11519370Spst
11619370Spststatic int
11719370Spstisp_sbus_probe(device_t dev)
11819370Spst{
11919370Spst	int found = 0;
12019370Spst	const char *name = ofw_bus_get_name(dev);
12119370Spst	if (strcmp(name, "SUNW,isp") == 0 ||
12219370Spst	    strcmp(name, "QLGC,isp") == 0 ||
12319370Spst	    strcmp(name, "ptisp") == 0 ||
12419370Spst	    strcmp(name, "PTI,ptisp") == 0) {
12519370Spst		found++;
12619370Spst	}
12719370Spst	if (!found)
12819370Spst		return (ENXIO);
12919370Spst
13019370Spst	if (isp_announced == 0 && bootverbose) {
13119370Spst		printf("Qlogic ISP Driver, FreeBSD Version %d.%d, "
13219370Spst		    "Core Version %d.%d\n",
13319370Spst		    ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR,
13419370Spst		    ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR);
13519370Spst		isp_announced++;
13619370Spst	}
13719370Spst	return (0);
13819370Spst}
13919370Spst
14019370Spststatic int
14119370Spstisp_sbus_attach(device_t dev)
14219370Spst{
14319370Spst	int tval, isp_debug, role, ispburst, default_id;
14419370Spst	struct isp_sbussoftc *sbs;
14519370Spst	ispsoftc_t *isp = NULL;
14619370Spst	int locksetup = 0;
14719370Spst	int ints_setup = 0;
14819370Spst
14919370Spst	sbs = device_get_softc(dev);
15019370Spst	if (sbs == NULL) {
15119370Spst		device_printf(dev, "cannot get softc\n");
15219370Spst		return (ENOMEM);
15319370Spst	}
15419370Spst
15519370Spst	sbs->sbus_dev = dev;
15619370Spst	sbs->sbus_mdvec = mdvec;
15719370Spst
15819370Spst	/*
15919370Spst	 * Figure out if we're supposed to skip this one.
16019370Spst	 * If we are, we actually go to ISP_ROLE_NONE.
16119370Spst	 */
16219370Spst
16319370Spst	tval = 0;
16419370Spst	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
16519370Spst	    "disable", &tval) == 0 && tval) {
16619370Spst		device_printf(dev, "device is disabled\n");
16719370Spst		/* but return 0 so the !$)$)*!$*) unit isn't reused */
16819370Spst		return (0);
16919370Spst	}
17019370Spst
17119370Spst	role = 0;
17219370Spst	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
17319370Spst	    "role", &role) == 0 &&
17419370Spst	    ((role & ~(ISP_ROLE_INITIATOR|ISP_ROLE_TARGET)) == 0)) {
17519370Spst		device_printf(dev, "setting role to 0x%x\n", role);
17619370Spst	} else {
17719370Spst		role = ISP_DEFAULT_ROLES;
17819370Spst	}
17919370Spst
18019370Spst	sbs->irq = sbs->regs = NULL;
18119370Spst	sbs->rgd = sbs->iqd = 0;
18219370Spst
18319370Spst	sbs->regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sbs->rgd,
18419370Spst	    RF_ACTIVE);
18519370Spst	if (sbs->regs == NULL) {
18619370Spst		device_printf(dev, "unable to map registers\n");
18719370Spst		goto bad;
18819370Spst	}
18919370Spst
19019370Spst	sbs->sbus_poff[BIU_BLOCK >> _BLK_REG_SHFT] = BIU_REGS_OFF;
19119370Spst	sbs->sbus_poff[MBOX_BLOCK >> _BLK_REG_SHFT] = SBUS_MBOX_REGS_OFF;
19219370Spst	sbs->sbus_poff[SXP_BLOCK >> _BLK_REG_SHFT] = SBUS_SXP_REGS_OFF;
19319370Spst	sbs->sbus_poff[RISC_BLOCK >> _BLK_REG_SHFT] = SBUS_RISC_REGS_OFF;
19419370Spst	sbs->sbus_poff[DMA_BLOCK >> _BLK_REG_SHFT] = DMA_REGS_OFF;
19519370Spst	isp = &sbs->sbus_isp;
19619370Spst	isp->isp_bus_tag = rman_get_bustag(sbs->regs);
19719370Spst	isp->isp_bus_handle = rman_get_bushandle(sbs->regs);
19819370Spst	isp->isp_mdvec = &sbs->sbus_mdvec;
19919370Spst	isp->isp_bustype = ISP_BT_SBUS;
20019370Spst	isp->isp_type = ISP_HA_SCSI_UNKNOWN;
20119370Spst	isp->isp_param = &sbs->sbus_param;
20219370Spst	isp->isp_osinfo.pc.ptr = &sbs->sbus_spi;
20319370Spst	isp->isp_revision = 0;	/* XXX */
20419370Spst	isp->isp_dev = dev;
20519370Spst	isp->isp_nchan = 1;
20619370Spst	ISP_SET_PC(isp, 0, def_role, role);
20719370Spst
20819370Spst	/*
20919370Spst	 * Get the clock frequency and convert it from HZ to MHz,
21019370Spst	 * rounding up. This defaults to 25MHz if there isn't a
21119370Spst	 * device specific one in the OFW device tree.
21219370Spst	 */
21319370Spst	sbs->sbus_mdvec.dv_clock = (sbus_get_clockfreq(dev) + 500000)/1000000;
21419370Spst
21519370Spst	/*
21619370Spst	 * Now figure out what the proper burst sizes, etc., to use.
21719370Spst	 * Unfortunately, there is no ddi_dma_burstsizes here which
21819370Spst	 * walks up the tree finding the limiting burst size node (if
21919370Spst	 * any). We just use what's here for isp.
22019370Spst	 */
22119370Spst	ispburst = sbus_get_burstsz(dev);
22219370Spst	if (ispburst == 0) {
22319370Spst		ispburst = SBUS_BURST_32 - 1;
22419370Spst	}
22519370Spst	sbs->sbus_mdvec.dv_conf1 =  0;
22619370Spst	if (ispburst & (1 << 5)) {
22719370Spst		sbs->sbus_mdvec.dv_conf1 = BIU_SBUS_CONF1_FIFO_32;
22819370Spst	} else if (ispburst & (1 << 4)) {
22919370Spst		sbs->sbus_mdvec.dv_conf1 = BIU_SBUS_CONF1_FIFO_16;
23019370Spst	} else if (ispburst & (1 << 3)) {
23119370Spst		sbs->sbus_mdvec.dv_conf1 =
23219370Spst		    BIU_SBUS_CONF1_BURST8 | BIU_SBUS_CONF1_FIFO_8;
23319370Spst	}
23419370Spst	if (sbs->sbus_mdvec.dv_conf1) {
23519370Spst		sbs->sbus_mdvec.dv_conf1 |= BIU_BURST_ENABLE;
23619370Spst	}
23719370Spst
23819370Spst	/*
23919370Spst	 * We don't trust NVRAM on SBus cards
24019370Spst	 */
24119370Spst	isp->isp_confopts |= ISP_CFG_NONVRAM;
24219370Spst
24319370Spst	/*
24419370Spst	 * Mark things if we're a PTI SBus adapter.
24519370Spst	 */
24619370Spst	if (strcmp("PTI,ptisp", ofw_bus_get_name(dev)) == 0 ||
24719370Spst	    strcmp("ptisp", ofw_bus_get_name(dev)) == 0) {
24819370Spst		SDPARAM(isp, 0)->isp_ptisp = 1;
24919370Spst	}
25019370Spst
25119370Spst	isp->isp_osinfo.fw = firmware_get("isp_1000");
25219370Spst	if (isp->isp_osinfo.fw) {
25319370Spst		union {
25419370Spst			const void *cp;
25519370Spst			uint16_t *sp;
25619370Spst		} stupid;
25719370Spst		stupid.cp = isp->isp_osinfo.fw->data;
25819370Spst		isp->isp_mdvec->dv_ispfw = stupid.sp;
25919370Spst	}
26019370Spst
26119370Spst	tval = 0;
26219370Spst        if (resource_int_value(device_get_name(dev), device_get_unit(dev),
26319370Spst            "fwload_disable", &tval) == 0 && tval != 0) {
26419370Spst		isp->isp_confopts |= ISP_CFG_NORELOAD;
26519370Spst	}
26619370Spst
26719370Spst	default_id = -1;
26819370Spst	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
26919370Spst            "iid", &tval) == 0) {
27019370Spst		default_id = tval;
27119370Spst		isp->isp_confopts |= ISP_CFG_OWNLOOPID;
27219370Spst	}
27319370Spst	if (default_id == -1) {
27419370Spst		default_id = OF_getscsinitid(dev);
27519370Spst	}
27619370Spst	ISP_SPI_PC(isp, 0)->iid = default_id;
27719370Spst
27819370Spst	isp_debug = 0;
27919370Spst        (void) resource_int_value(device_get_name(dev), device_get_unit(dev),
28019370Spst            "debug", &isp_debug);
28119370Spst
28219370Spst	/* Make sure the lock is set up. */
28319370Spst	mtx_init(&isp->isp_osinfo.lock, "isp", NULL, MTX_DEF);
28419370Spst	locksetup++;
28519370Spst
28619370Spst	sbs->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sbs->iqd,
28719370Spst	    RF_ACTIVE | RF_SHAREABLE);
28819370Spst	if (sbs->irq == NULL) {
28919370Spst		device_printf(dev, "could not allocate interrupt\n");
29019370Spst		goto bad;
29119370Spst	}
29219370Spst
29319370Spst	if (isp_setup_intr(dev, sbs->irq, ISP_IFLAGS, NULL, isp_platform_intr,
29419370Spst	    isp, &sbs->ih)) {
29519370Spst		device_printf(dev, "could not setup interrupt\n");
29619370Spst		goto bad;
29719370Spst	}
29819370Spst	ints_setup++;
29919370Spst
30019370Spst	/*
30119370Spst	 * Set up logging levels.
30219370Spst	 */
30319370Spst	if (isp_debug) {
30419370Spst		isp->isp_dblev = isp_debug;
30519370Spst	} else {
30619370Spst		isp->isp_dblev = ISP_LOGWARN|ISP_LOGERR;
30719370Spst	}
30819370Spst	if (bootverbose) {
30919370Spst		isp->isp_dblev |= ISP_LOGCONFIG|ISP_LOGINFO;
31019370Spst	}
31119370Spst
31219370Spst	/*
31319370Spst	 * Make sure we're in reset state.
31419370Spst	 */
31519370Spst	ISP_LOCK(isp);
31619370Spst	isp_reset(isp, 1);
31719370Spst	if (isp->isp_state != ISP_RESETSTATE) {
31819370Spst		isp_uninit(isp);
31919370Spst		ISP_UNLOCK(isp);
32019370Spst		goto bad;
32119370Spst	}
32219370Spst	isp_init(isp);
32319370Spst	if (isp->isp_state == ISP_INITSTATE) {
32419370Spst		isp->isp_state = ISP_RUNSTATE;
32519370Spst	}
32619370Spst	ISP_UNLOCK(isp);
32719370Spst	if (isp_attach(isp)) {
32819370Spst		ISP_LOCK(isp);
32919370Spst		isp_uninit(isp);
33019370Spst		ISP_UNLOCK(isp);
33119370Spst		goto bad;
33219370Spst	}
33319370Spst	return (0);
33419370Spst
33519370Spstbad:
33619370Spst
33719370Spst	if (sbs && ints_setup) {
33819370Spst		(void) bus_teardown_intr(dev, sbs->irq, sbs->ih);
33919370Spst	}
34019370Spst
34119370Spst	if (sbs && sbs->irq) {
34219370Spst		bus_release_resource(dev, SYS_RES_IRQ, sbs->iqd, sbs->irq);
34319370Spst	}
34419370Spst
34519370Spst	if (locksetup && isp) {
34619370Spst		mtx_destroy(&isp->isp_osinfo.lock);
34719370Spst	}
34819370Spst
34919370Spst	if (sbs->regs) {
35019370Spst		(void) bus_release_resource(dev, SYS_RES_MEMORY, sbs->rgd,
35119370Spst		    sbs->regs);
35219370Spst	}
35319370Spst	return (ENXIO);
35419370Spst}
35519370Spst
35619370Spststatic int
35719370Spstisp_sbus_detach(device_t dev)
35819370Spst{
35919370Spst	struct isp_sbussoftc *sbs;
36019370Spst	ispsoftc_t *isp;
36119370Spst	int status;
36219370Spst
36319370Spst	sbs = device_get_softc(dev);
36419370Spst	if (sbs == NULL) {
36519370Spst		return (ENXIO);
36619370Spst	}
36719370Spst	isp = (ispsoftc_t *) sbs;
36819370Spst	status = isp_detach(isp);
36919370Spst	if (status)
37019370Spst		return (status);
37119370Spst	ISP_LOCK(isp);
37219370Spst	isp_uninit(isp);
37319370Spst	if (sbs->ih) {
37419370Spst		(void) bus_teardown_intr(dev, sbs->irq, sbs->ih);
37519370Spst	}
37619370Spst	ISP_UNLOCK(isp);
37719370Spst	mtx_destroy(&isp->isp_osinfo.lock);
37819370Spst	(void) bus_release_resource(dev, SYS_RES_IRQ, sbs->iqd, sbs->irq);
37919370Spst	(void) bus_release_resource(dev, SYS_RES_MEMORY, sbs->rgd, sbs->regs);
38019370Spst	return (0);
38119370Spst}
38219370Spst
38319370Spst#define	IspVirt2Off(a, x)	\
38419370Spst	(((struct isp_sbussoftc *)a)->sbus_poff[((x) & _BLK_REG_MASK) >> \
38519370Spst	_BLK_REG_SHFT] + ((x) & 0xff))
38619370Spst
38719370Spst#define	BXR2(sbc, off)		\
38819370Spst	bus_space_read_2(isp->isp_bus_tag, isp->isp_bus_handle, off)
38919370Spst
39019370Spststatic int
39119370Spstisp_sbus_rd_isr(ispsoftc_t *isp, uint16_t *isrp, uint16_t *semap, uint16_t *info)
39219370Spst{
39319370Spst	uint16_t isr, sema;
39419370Spst
39519370Spst	isr = BXR2(sbc, IspVirt2Off(isp, BIU_ISR));
39619370Spst	sema = BXR2(sbc, IspVirt2Off(isp, BIU_SEMA));
39719370Spst	isp_prt(isp, ISP_LOGDEBUG3, "ISR 0x%x SEMA 0x%x", isr, sema);
39819370Spst	isr &= INT_PENDING_MASK(isp);
39919370Spst	sema &= BIU_SEMA_LOCK;
40019370Spst	if (isr == 0 && sema == 0) {
40119370Spst		return (0);
40219370Spst	}
40319370Spst	*isrp = isr;
40419370Spst	if ((*semap = sema) != 0)
40519370Spst		*info = BXR2(sbc, IspVirt2Off(isp, OUTMAILBOX0));
40619370Spst	return (1);
40719370Spst}
40819370Spst
40919370Spststatic uint32_t
41019370Spstisp_sbus_rd_reg(ispsoftc_t *isp, int regoff)
41119370Spst{
41219370Spst	uint16_t rval;
41319370Spst	struct isp_sbussoftc *sbs = (struct isp_sbussoftc *) isp;
41419370Spst	int offset = sbs->sbus_poff[(regoff & _BLK_REG_MASK) >> _BLK_REG_SHFT];
41519370Spst	offset += (regoff & 0xff);
41619370Spst	rval = bus_space_read_2(isp->isp_bus_tag, isp->isp_bus_handle, offset);
41719370Spst	isp_prt(isp, ISP_LOGDEBUG3,
41819370Spst	    "isp_sbus_rd_reg(off %x) = %x", regoff, rval);
41919370Spst	return (rval);
42019370Spst}
42119370Spst
42219370Spststatic void
42319370Spstisp_sbus_wr_reg(ispsoftc_t *isp, int regoff, uint32_t val)
42419370Spst{
42519370Spst	struct isp_sbussoftc *sbs = (struct isp_sbussoftc *) isp;
42619370Spst	int offset = sbs->sbus_poff[(regoff & _BLK_REG_MASK) >> _BLK_REG_SHFT];
42719370Spst	offset += (regoff & 0xff);
42819370Spst	isp_prt(isp, ISP_LOGDEBUG3,
42919370Spst	    "isp_sbus_wr_reg(off %x) = %x", regoff, val);
43019370Spst	bus_space_write_2(isp->isp_bus_tag, isp->isp_bus_handle, offset, val);
43119370Spst	MEMORYBARRIER(isp, SYNC_REG, offset, 2, -1);
43219370Spst}
43319370Spst
43419370Spststruct imush {
43519370Spst	ispsoftc_t *isp;
43619370Spst	int error;
43719370Spst};
43819370Spst
43919370Spststatic void imc(void *, bus_dma_segment_t *, int, int);
44019370Spst
44119370Spststatic void
44219370Spstimc(void *arg, bus_dma_segment_t *segs, int nseg, int error)
44319370Spst{
44419370Spst	struct imush *imushp = (struct imush *) arg;
44519370Spst	if (error) {
44619370Spst		imushp->error = error;
44719370Spst	} else {
44819370Spst		ispsoftc_t *isp =imushp->isp;
44919370Spst		bus_addr_t addr = segs->ds_addr;
45019370Spst
45119370Spst		isp->isp_rquest_dma = addr;
45219370Spst		addr += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
45319370Spst		isp->isp_result_dma = addr;
45419370Spst	}
45519370Spst}
45619370Spst
45719370Spststatic int
45819370Spstisp_sbus_mbxdma(ispsoftc_t *isp)
45919370Spst{
46019370Spst	caddr_t base;
46119370Spst	uint32_t len;
46219370Spst	int i, error;
46319370Spst	struct imush im;
46419370Spst
46519370Spst	/*
46619370Spst	 * Already been here? If so, leave...
46719370Spst	 */
46819370Spst	if (isp->isp_rquest) {
46919370Spst		return (0);
47019370Spst	}
47119370Spst
47219370Spst	ISP_UNLOCK(isp);
47319370Spst
47419370Spst	len = sizeof (struct isp_pcmd) * isp->isp_maxcmds;
47519370Spst	isp->isp_osinfo.pcmd_pool = (struct isp_pcmd *)
47619370Spst	    malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
47719370Spst	if (isp->isp_osinfo.pcmd_pool == NULL) {
47819370Spst		isp_prt(isp, ISP_LOGERR, "cannot alloc pcmd pool");
47919370Spst		ISP_LOCK(isp);
48019370Spst		return (1);
48119370Spst	}
48219370Spst
48319370Spst	len = sizeof (isp_hdl_t *) * isp->isp_maxcmds;
48419370Spst	isp->isp_xflist = (isp_hdl_t *) malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
48519370Spst	if (isp->isp_xflist == NULL) {
48619370Spst		isp_prt(isp, ISP_LOGERR, "cannot alloc xflist array");
48719370Spst		ISP_LOCK(isp);
48819370Spst		return (1);
48919370Spst	}
49019370Spst	for (len = 0; len < isp->isp_maxcmds - 1; len++) {
49119370Spst		isp->isp_xflist[len].cmd = &isp->isp_xflist[len+1];
49219370Spst	}
49319370Spst	isp->isp_xffree = isp->isp_xflist;
49419370Spst	len = sizeof (bus_dmamap_t) * isp->isp_maxcmds;
49519370Spst
49619370Spst	if (isp_dma_tag_create(BUS_DMA_ROOTARG(ISP_SBD(isp)), 1,
49719370Spst	    BUS_SPACE_MAXADDR_24BIT+1, BUS_SPACE_MAXADDR_32BIT,
49819370Spst	    BUS_SPACE_MAXADDR_32BIT, NULL, NULL, BUS_SPACE_MAXSIZE_32BIT,
49919370Spst	    ISP_NSEG_MAX, BUS_SPACE_MAXADDR_24BIT, 0, &isp->isp_osinfo.dmat)) {
50019370Spst		isp_prt(isp, ISP_LOGERR, "could not create master dma tag");
50119370Spst		free(isp->isp_osinfo.pcmd_pool, M_DEVBUF);
50219370Spst		free(isp->isp_xflist, M_DEVBUF);
50319370Spst		ISP_LOCK(isp);
50419370Spst		return(1);
50519370Spst	}
50619370Spst
50719370Spst	/*
50819370Spst	 * Allocate and map the request, result queues, plus FC scratch area.
50919370Spst	 */
51019370Spst	len = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
51119370Spst	len += ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp));
51219370Spst
51319370Spst	if (isp_dma_tag_create(isp->isp_osinfo.dmat, QENTRY_LEN,
51419370Spst	    BUS_SPACE_MAXADDR_24BIT+1, BUS_SPACE_MAXADDR_32BIT,
51519370Spst	    BUS_SPACE_MAXADDR_32BIT, NULL, NULL, len, 1,
51619370Spst	    BUS_SPACE_MAXADDR_24BIT, 0, &isp->isp_osinfo.cdmat)) {
51719370Spst		isp_prt(isp, ISP_LOGERR,
51819370Spst		    "cannot create a dma tag for control spaces");
51919370Spst		free(isp->isp_osinfo.pcmd_pool, M_DEVBUF);
52019370Spst		free(isp->isp_xflist, M_DEVBUF);
52119370Spst		ISP_LOCK(isp);
52219370Spst		return (1);
52319370Spst	}
52419370Spst
52519370Spst	if (bus_dmamem_alloc(isp->isp_osinfo.cdmat, (void **)&base, BUS_DMA_NOWAIT | BUS_DMA_COHERENT,
52619370Spst	    &isp->isp_osinfo.cdmap) != 0) {
52719370Spst		isp_prt(isp, ISP_LOGERR,
52819370Spst		    "cannot allocate %d bytes of CCB memory", len);
52919370Spst		bus_dma_tag_destroy(isp->isp_osinfo.cdmat);
53019370Spst		free(isp->isp_osinfo.pcmd_pool, M_DEVBUF);
53119370Spst		free(isp->isp_xflist, M_DEVBUF);
53219370Spst		ISP_LOCK(isp);
53319370Spst		return (1);
53419370Spst	}
53519370Spst
53619370Spst	for (i = 0; i < isp->isp_maxcmds; i++) {
53719370Spst		struct isp_pcmd *pcmd = &isp->isp_osinfo.pcmd_pool[i];
53819370Spst		error = bus_dmamap_create(isp->isp_osinfo.dmat, 0, &pcmd->dmap);
53919370Spst		if (error) {
54019370Spst			isp_prt(isp, ISP_LOGERR,
54119370Spst			    "error %d creating per-cmd DMA maps", error);
54219370Spst			while (--i >= 0) {
54319370Spst				bus_dmamap_destroy(isp->isp_osinfo.dmat,
54419370Spst				    isp->isp_osinfo.pcmd_pool[i].dmap);
54519370Spst			}
54619370Spst			goto bad;
54719370Spst		}
54819370Spst		callout_init_mtx(&pcmd->wdog, &isp->isp_osinfo.lock, 0);
54919370Spst		if (i == isp->isp_maxcmds-1) {
55019370Spst			pcmd->next = NULL;
55119370Spst		} else {
55219370Spst			pcmd->next = &isp->isp_osinfo.pcmd_pool[i+1];
55319370Spst		}
55419370Spst	}
55519370Spst	isp->isp_osinfo.pcmd_free = &isp->isp_osinfo.pcmd_pool[0];
55619370Spst
55719370Spst	im.isp = isp;
55819370Spst	im.error = 0;
55919370Spst	bus_dmamap_load(isp->isp_osinfo.cdmat, isp->isp_osinfo.cdmap, base, len, imc, &im, 0);
56019370Spst	if (im.error) {
56119370Spst		isp_prt(isp, ISP_LOGERR,
56219370Spst		    "error %d loading dma map for control areas", im.error);
56319370Spst		goto bad;
56419370Spst	}
56519370Spst
56619370Spst	isp->isp_rquest = base;
56719370Spst	base += ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
56819370Spst	isp->isp_result = base;
56919370Spst	ISP_LOCK(isp);
57019370Spst	return (0);
57119370Spst
57219370Spstbad:
57319370Spst	bus_dmamem_free(isp->isp_osinfo.cdmat, base, isp->isp_osinfo.cdmap);
57419370Spst	bus_dma_tag_destroy(isp->isp_osinfo.cdmat);
57519370Spst	free(isp->isp_xflist, M_DEVBUF);
57619370Spst	free(isp->isp_osinfo.pcmd_pool, M_DEVBUF);
57719370Spst	isp->isp_rquest = NULL;
57819370Spst	ISP_LOCK(isp);
57919370Spst	return (1);
58019370Spst}
58119370Spst
58219370Spsttypedef struct {
58319370Spst	ispsoftc_t *isp;
58419370Spst	void *cmd_token;
58519370Spst	void *rq;	/* original request */
58619370Spst	int error;
58719370Spst	bus_size_t mapsize;
58819370Spst} mush_t;
58919370Spst
59019370Spst#define	MUSHERR_NOQENTRIES	-2
59119370Spst
59219370Spststatic void dma2(void *, bus_dma_segment_t *, int, int);
59319370Spst
59419370Spststatic void
59519370Spstdma2(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
59619370Spst{
59719370Spst	mush_t *mp;
59819370Spst	ispsoftc_t *isp;
59919370Spst	struct ccb_scsiio *csio;
60019370Spst	isp_ddir_t ddir;
60119370Spst	ispreq_t *rq;
60219370Spst
60319370Spst	mp = (mush_t *) arg;
60419370Spst	if (error) {
60519370Spst		mp->error = error;
60619370Spst		return;
60719370Spst	}
60819370Spst	csio = mp->cmd_token;
60919370Spst	isp = mp->isp;
61019370Spst	rq = mp->rq;
61119370Spst	if (nseg) {
61219370Spst		if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
61319370Spst			bus_dmamap_sync(isp->isp_osinfo.dmat, PISP_PCMD(csio)->dmap, BUS_DMASYNC_PREREAD);
61419370Spst			ddir = ISP_FROM_DEVICE;
61519370Spst		} else if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) {
61619370Spst			bus_dmamap_sync(isp->isp_osinfo.dmat, PISP_PCMD(csio)->dmap, BUS_DMASYNC_PREWRITE);
61719370Spst			ddir = ISP_TO_DEVICE;
61819370Spst		} else {
61919370Spst			ddir = ISP_NOXFR;
62019370Spst		}
62119370Spst	} else {
62219370Spst		dm_segs = NULL;
62319370Spst		nseg = 0;
62419370Spst		ddir = ISP_NOXFR;
62519370Spst	}
62619370Spst
62719370Spst	if (isp_send_cmd(isp, rq, dm_segs, nseg, XS_XFRLEN(csio), ddir, NULL) != CMD_QUEUED) {
62819370Spst		mp->error = MUSHERR_NOQENTRIES;
62919370Spst	}
63019370Spst}
63119370Spst
63219370Spststatic int
63319370Spstisp_sbus_dmasetup(ispsoftc_t *isp, struct ccb_scsiio *csio, void *ff)
63419370Spst{
63519370Spst	mush_t mush, *mp;
63619370Spst	void (*eptr)(void *, bus_dma_segment_t *, int, int);
63719370Spst	int error;
63819370Spst
63919370Spst	mp = &mush;
64019370Spst	mp->isp = isp;
64119370Spst	mp->cmd_token = csio;
64219370Spst	mp->rq = ff;
64319370Spst	mp->error = 0;
64419370Spst	mp->mapsize = 0;
64519370Spst
64619370Spst	eptr = dma2;
64719370Spst
64819370Spst	error = bus_dmamap_load_ccb(isp->isp_osinfo.dmat,
64919370Spst	    PISP_PCMD(csio)->dmap, (union ccb *)csio, eptr, mp, 0);
65019370Spst	if (error == EINPROGRESS) {
65119370Spst		bus_dmamap_unload(isp->isp_osinfo.dmat, PISP_PCMD(csio)->dmap);
65219370Spst		mp->error = EINVAL;
65319370Spst		isp_prt(isp, ISP_LOGERR,
65419370Spst		    "deferred dma allocation not supported");
65519370Spst	} else if (error && mp->error == 0) {
65619370Spst#ifdef	DIAGNOSTIC
65719370Spst		isp_prt(isp, ISP_LOGERR, "error %d in dma mapping code", error);
65819370Spst#endif
65919370Spst		mp->error = error;
66019370Spst	}
66119370Spst	if (mp->error) {
66219370Spst		int retval = CMD_COMPLETE;
66319370Spst		if (mp->error == MUSHERR_NOQENTRIES) {
66419370Spst			retval = CMD_EAGAIN;
66519370Spst		} else if (mp->error == EFBIG) {
66619370Spst			XS_SETERR(csio, CAM_REQ_TOO_BIG);
66719370Spst		} else if (mp->error == EINVAL) {
66819370Spst			XS_SETERR(csio, CAM_REQ_INVALID);
66919370Spst		} else {
67019370Spst			XS_SETERR(csio, CAM_UNREC_HBA_ERROR);
67119370Spst		}
67219370Spst		return (retval);
67319370Spst	}
67419370Spst	return (CMD_QUEUED);
67519370Spst}
67619370Spst
67719370Spststatic void
67819370Spstisp_sbus_reset0(ispsoftc_t *isp)
67919370Spst{
68019370Spst	ISP_DISABLE_INTS(isp);
68119370Spst}
68219370Spst
68319370Spststatic void
68419370Spstisp_sbus_reset1(ispsoftc_t *isp)
68519370Spst{
68619370Spst	ISP_ENABLE_INTS(isp);
68719370Spst}
68819370Spst
68919370Spststatic void
69019370Spstisp_sbus_dumpregs(ispsoftc_t *isp, const char *msg)
69119370Spst{
69219370Spst	if (msg)
69319370Spst		printf("%s: %s\n", device_get_nameunit(isp->isp_dev), msg);
69419370Spst	else
69519370Spst		printf("%s:\n", device_get_nameunit(isp->isp_dev));
69619370Spst	printf("    biu_conf1=%x", ISP_READ(isp, BIU_CONF1));
69719370Spst	printf(" biu_icr=%x biu_isr=%x biu_sema=%x ", ISP_READ(isp, BIU_ICR),
69819370Spst	    ISP_READ(isp, BIU_ISR), ISP_READ(isp, BIU_SEMA));
69919370Spst	printf("risc_hccr=%x\n", ISP_READ(isp, HCCR));
70019370Spst
70119370Spst
70219370Spst	ISP_WRITE(isp, HCCR, HCCR_CMD_PAUSE);
70319370Spst	printf("    cdma_conf=%x cdma_sts=%x cdma_fifostat=%x\n",
70419370Spst		ISP_READ(isp, CDMA_CONF), ISP_READ(isp, CDMA_STATUS),
70519370Spst		ISP_READ(isp, CDMA_FIFO_STS));
70619370Spst	printf("    ddma_conf=%x ddma_sts=%x ddma_fifostat=%x\n",
70719370Spst		ISP_READ(isp, DDMA_CONF), ISP_READ(isp, DDMA_STATUS),
70819370Spst		ISP_READ(isp, DDMA_FIFO_STS));
70919370Spst	printf("    sxp_int=%x sxp_gross=%x sxp(scsi_ctrl)=%x\n",
71019370Spst		ISP_READ(isp, SXP_INTERRUPT),
71119370Spst		ISP_READ(isp, SXP_GROSS_ERR),
71219370Spst		ISP_READ(isp, SXP_PINS_CTRL));
71319370Spst	ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE);
71419370Spst	printf("    mbox regs: %x %x %x %x %x\n",
71519370Spst	    ISP_READ(isp, OUTMAILBOX0), ISP_READ(isp, OUTMAILBOX1),
71619370Spst	    ISP_READ(isp, OUTMAILBOX2), ISP_READ(isp, OUTMAILBOX3),
71719370Spst	    ISP_READ(isp, OUTMAILBOX4));
71819370Spst}
71919370Spst