OpenSolaris_b135/uts/sun/io/socal.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * socal - Serial Optical Channel Arbitrated Loop host adapter driver.
 */

#include <sys/types.h>
#include <sys/note.h>
#include <sys/devops.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/fcntl.h>

#include <sys/cmn_err.h>
#include <sys/stropts.h>
#include <sys/kmem.h>

#include <sys/errno.h>
#include <sys/open.h>
#include <sys/varargs.h>
#include <sys/var.h>
#include <sys/thread.h>
#include <sys/debug.h>
#include <sys/cpu.h>
#include <sys/autoconf.h>
#include <sys/conf.h>
#include <sys/stat.h>

#include <sys/file.h>
#include <sys/syslog.h>

#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/ksynch.h>
#include <sys/ddidmareq.h>
#include <sys/dditypes.h>
#include <sys/ethernet.h>
#include <sys/socalreg.h>
#include <sys/socalmap.h>
#include <sys/fc4/fcal.h>
#include <sys/socal_cq_defs.h>
#include <sys/fc4/fcal_linkapp.h>
#include <sys/fc4/fcal_transport.h>
#include <sys/socalio.h>
#include <sys/socalvar.h>

/*
 * Local Macros
 */

#ifdef DEBUG
#define	SOCAL_DEBUG 1
#else
#define	SOCAL_DEBUG 0
#endif
static uchar_t	socal_xrambuf[0x40000];
static int 	socal_core = SOCAL_TAKE_CORE;
#if SOCAL_DEBUG > 0 && !defined(lint)
static	int soc_debug = SOCAL_DEBUG;
static  int socal_read_stale_data = 0;
#define	DEBUGF(level, args) \
	if (soc_debug >= (level)) cmn_err args;
#define	SOCALDEBUG(level, args) \
	if (soc_debug >= level) args;
#else
#define	DEBUGF(level, args)	/* Nothing */
#define	SOCALDEBUG(level, args)	/* Nothing */
#endif


/* defines for properties */
#define	SOCAL_PORT_NO_PROP		"socal_port"
#define	SOCAL_ALT_PORT_NO_PROP		"port#"

/* for socal_force_reset() */
#define	RESET_PORT			1
#define	DONT_RESET_PORT			0

/*
 * Driver Entry points.
 */
static int socal_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int socal_bus_ctl(dev_info_t *dip, dev_info_t *rip,
	ddi_ctl_enum_t op, void *a, void *v);
static int socal_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int socal_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
	void *arg, void **result);
static unsigned int socal_intr(caddr_t arg);
static unsigned int socal_dummy_intr(caddr_t arg);
static int socal_open(dev_t *devp, int flag, int otyp,
	cred_t *cred_p);
static int socal_close(dev_t dev, int flag, int otyp,
	cred_t *cred_p);
static int socal_ioctl(dev_t dev, int cmd, intptr_t arg,
	int mode, cred_t *cred_p, int *rval_p);

/*
 * FC_AL transport functions.
 */
static uint_t socal_transport(fcal_packet_t *, fcal_sleep_t, int);
static uint_t socal_transport_poll(fcal_packet_t *, uint_t, int);
static uint_t socal_lilp_map(void *, uint_t, uint32_t, uint_t);
static uint_t socal_force_lip(void *, uint_t, uint_t, uint_t);
static uint_t socal_force_offline(void *, uint_t, uint_t);
static uint_t socal_abort_cmd(void *, uint_t, fcal_packet_t *, uint_t);
static uint_t socal_doit(fcal_packet_t *, socal_port_t *, int,
    void (*)(), int, int, uint_t *);
static uint_t socal_els(void *, uint_t, uint_t, uint_t,
	void (*callback)(), void *, caddr_t, caddr_t *, uint_t);
static uint_t socal_bypass_dev(void *, uint_t, uint_t);
static void socal_force_reset(void *, uint_t, uint_t);
static void socal_add_ulp(void *, uint_t, uchar_t, void (*)(),
	void (*)(), void (*)(), void *);
static void socal_remove_ulp(void *, uint_t, uchar_t, void *);
static void socal_take_core(void *);

/*
 * Driver internal functions.
 */
static void socal_intr_solicited(socal_state_t *, uint32_t srq);
static void socal_intr_unsolicited(socal_state_t *, uint32_t urq);
static void socal_lilp_map_done(fcal_packet_t *);
static void socal_force_lip_done(fcal_packet_t *);
static void socal_force_offline_done(fcal_packet_t *);
static void socal_abort_done(fcal_packet_t *);
static void socal_bypass_dev_done(fcal_packet_t *);
static fcal_packet_t *socal_packet_alloc(socal_state_t *, fcal_sleep_t);
static void socal_packet_free(fcal_packet_t *);
static void socal_disable(socal_state_t *socalp);
static void socal_init_transport_interface(socal_state_t *socalp);
static int socal_cqalloc_init(socal_state_t *socalp, uint32_t index);
static void socal_cqinit(socal_state_t *socalp, uint32_t index);
static int socal_start(socal_state_t *socalp);
static void socal_doreset(socal_state_t *socalp);
static int socal_dodetach(dev_info_t *dip);
static int socal_diag_request(socal_state_t *socalp, uint32_t port,
	uint_t *diagcode, uint32_t cmd);
static void socal_download_ucode(socal_state_t *socalp);
static void socal_init_cq_desc(socal_state_t *socalp);
static void socal_init_wwn(socal_state_t *socalp);
static void socal_enable(socal_state_t *socalp);
static int socal_establish_pool(socal_state_t *socalp, uint32_t poolid);
static int socal_add_pool_buffer(socal_state_t *socalp, uint32_t poolid);
static int socal_issue_adisc(socal_state_t *socalp, uint32_t port, uint32_t
	dest, la_els_adisc_t *adisc_pl, uint32_t polled);
static int socal_issue_lbf(socal_state_t *socalp, uint32_t port,
	uchar_t *flb_pl, size_t length, uint32_t polled);
static int socal_issue_rls(socal_state_t *socalp, uint32_t port, uint32_t
	dest, la_els_rls_reply_t *rls_pl, uint32_t polled);
static void socal_us_els(socal_state_t *, cqe_t *, caddr_t);
static fcal_packet_t *socal_els_alloc(socal_state_t *, uint32_t, uint32_t,
	uint32_t, uint32_t, caddr_t *, uint32_t);
static fcal_packet_t *socal_lbf_alloc(socal_state_t *, uint32_t,
	uint32_t, uint32_t, caddr_t *, uint32_t);
static void socal_els_free(socal_priv_cmd_t *);
static void socal_lbf_free(socal_priv_cmd_t *);
static int socal_getmap(socal_state_t *socalp, uint32_t port, caddr_t arg,
	uint32_t polled, int);
static void socal_flush_overflowq(socal_state_t *, int, int);
static void socal_deferred_intr(void *);
static void socal_fix_harda(socal_state_t *socalp, int port);

/*
 * SOC+ Circular Queue Management routines.
 */
static int socal_cq_enque(socal_state_t *, socal_port_t *, cqe_t *, int,
	fcal_sleep_t, fcal_packet_t *, int);

/*
 * Utility functions
 */
static void socal_disp_err(socal_state_t *, uint_t level, char *mid, char *msg);
static void socal_wcopy(uint_t *, uint_t *, int);

/*
 *  Set this bit to enable 64-bit sus mode
 */
static	int socal_64bitsbus = 1;

/*
 * Default soc dma limits
 */

static ddi_dma_lim_t default_socallim = {
	(ulong_t)0, (ulong_t)0xffffffff, (uint_t)0xffffffff,
	DEFAULT_BURSTSIZE | BURST32 | BURST64, 1, (25*1024)
};

static struct ddi_dma_attr socal_dma_attr = {
	DMA_ATTR_V0,			/* version */
	(unsigned long long)0,		/* addr_lo */
	(unsigned long long)0xffffffff,	/* addr_hi */
	(unsigned long long)0xffffffff,	/* count max */
	(unsigned long long)4,		/* align */
	DEFAULT_BURSTSIZE | BURST32 | BURST64, 	/* burst size */
	1,				/* minxfer */
	(unsigned long long)0xffffffff,	/* maxxfer */
	(unsigned long long)0xffffffff,	/* seg */
	1,				/* sgllen */
	4,				/* granularity */
	0				/* flags */
};

static struct ddi_device_acc_attr socal_acc_attr = {
	(ushort_t)DDI_DEVICE_ATTR_V0,	/* version */
	(uchar_t)DDI_STRUCTURE_BE_ACC,	/* endian flags */
	(uchar_t)DDI_STRICTORDER_ACC	/* data order */
};

static struct fcal_transport_ops socal_transport_ops = {
	socal_transport,
	socal_transport_poll,
	socal_lilp_map,
	socal_force_lip,
	socal_abort_cmd,
	socal_els,
	socal_bypass_dev,
	socal_force_reset,
	socal_add_ulp,
	socal_remove_ulp,
	socal_take_core
};

/*
 * Table used for setting the burst size in the soc+ config register
 */
static int socal_burst32_table[] = {
	SOCAL_CR_BURST_4,
	SOCAL_CR_BURST_4,
	SOCAL_CR_BURST_4,
	SOCAL_CR_BURST_4,
	SOCAL_CR_BURST_16,
	SOCAL_CR_BURST_32,
	SOCAL_CR_BURST_64
};

/*
 * Table for setting the burst size for 64-bit sbus mode in soc+'s CR
 */
static int socal_burst64_table[] = {
	(SOCAL_CR_BURST_8 << 8),
	(SOCAL_CR_BURST_8 << 8),
	(SOCAL_CR_BURST_8 << 8),
	(SOCAL_CR_BURST_8 << 8),
	(SOCAL_CR_BURST_8 << 8),
	(SOCAL_CR_BURST_32 << 8),
	(SOCAL_CR_BURST_64 << 8),
	(SOCAL_CR_BURST_128 << 8)
};

/*
 * Tables used to define the sizes of the Circular Queues
 *
 * To conserve DVMA/IOPB space, we make some of these queues small...
 */
static int socal_req_entries[] = {
	SOCAL_SMALL_CQ_ENTRIES,		/* Error (reset, lip) requests */
	SOCAL_MAX_CQ_ENTRIES,		/* Most commands */
	0,				/* Not currently used */
	0				/* Not currently used */
};

static int socal_rsp_entries[] = {
	SOCAL_MAX_CQ_ENTRIES,		/* Solicited  "SOC_OK" responses */
	SOCAL_SMALL_CQ_ENTRIES,		/* Solicited error responses */
	0,			/* Unsolicited responses */
	0				/* Not currently used */
};

/*
 * Bus ops vector
 */

static struct bus_ops socal_bus_ops = {
	BUSO_REV,		/* rev */
	nullbusmap,		/* int (*bus_map)() */
	0,			/* ddi_intrspec_t (*bus_get_intrspec)(); */
	0,			/* int (*bus_add_intrspec)(); */
	0,			/* void	(*bus_remove_intrspec)(); */
	i_ddi_map_fault,	/* int (*bus_map_fault)() */
	ddi_dma_map,		/* int (*bus_dma_map)() */
	ddi_dma_allochdl,
	ddi_dma_freehdl,
	ddi_dma_bindhdl,
	ddi_dma_unbindhdl,
	ddi_dma_flush,
	ddi_dma_win,
	ddi_dma_mctl,		/* int (*bus_dma_ctl)() */
	socal_bus_ctl,		/* int (*bus_ctl)() */
	ddi_bus_prop_op,	/* int (*bus_prop_op*)() */
};

static struct cb_ops socal_cb_ops = {
	socal_open,		/* int (*cb_open)() */
	socal_close,		/* int (*cb_close)() */
	nodev,			/* int (*cb_strategy)() */
	nodev,			/* int (*cb_print)() */
	nodev,			/* int (*cb_dump)() */
	nodev,			/* int (*cb_read)() */
	nodev,			/* int (*cb_write)() */
	socal_ioctl,		/* int (*cb_ioctl)() */
	nodev,			/* int (*cb_devmap)() */
	nodev,			/* int (*cb_mmap)() */
	nodev,			/* int (*cb_segmap)() */
	nochpoll,		/* int (*cb_chpoll)() */
	ddi_prop_op,		/* int (*cb_prop_op)() */
	0,			/* struct streamtab *cb_str */
	D_MP|D_NEW|D_HOTPLUG,	/* cb_flag */
	CB_REV,			/* rev */
	nodev,			/* int (*cb_aread)() */
	nodev			/* int (*cb_awrite)() */
};

/*
 * Soc driver ops structure.
 */

static struct dev_ops socal_ops = {
	DEVO_REV,		/* devo_rev, */
	0,			/* refcnt */
	socal_getinfo,		/* get_dev_info */
	nulldev,		/* identify */
	nulldev,		/* probe */
	socal_attach,		/* attach */
	socal_detach,		/* detach */
	nodev,			/* reset */
	&socal_cb_ops,		/* driver operations */
	&socal_bus_ops,		/* bus operations */
	NULL,			/* power */
	ddi_quiesce_not_supported,	/* quiesce */
};

/*
 * Driver private variables.
 */

static void *socal_soft_state_p = NULL;
static ddi_dma_lim_t *socallim = NULL;

static uchar_t socal_switch_to_alpa[] = {
	0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda, 0xd9, 0xd6,
	0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce, 0xcd, 0xcc, 0xcb, 0xca,
	0xc9, 0xc7, 0xc6, 0xc5, 0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5,
	0xb4, 0xb3, 0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9,
	0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b, 0x98, 0x97,
	0x90, 0x8f, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7c, 0x7a, 0x79,
	0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b,
	0x6a, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56,
	0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a,
	0x49, 0x47, 0x46, 0x45, 0x43, 0x3c, 0x3a, 0x39, 0x36, 0x35,
	0x34, 0x33, 0x32, 0x31, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
	0x27, 0x26, 0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17,
	0x10, 0x0f, 0x08, 0x04, 0x02, 0x01, 0x00
};

/*
 * Firmware related externs
 */
extern uint32_t socal_ucode[];
extern size_t socal_ucode_size;

/*
 * This is the loadable module wrapper: "module configuration section".
 */

#include <sys/modctl.h>
extern struct mod_ops mod_driverops;

/*
 * Module linkage information for the kernel.
 */
#define	SOCAL_NAME "SOC+ FC-AL Host Adapter Driver"
static	char	socal_version[] = "1.62 08/19/2008";
static struct modldrv modldrv = {
	&mod_driverops,		/* Type of module.  This one is a driver */
	SOCAL_NAME,
	&socal_ops,		/* driver ops */
};

static struct modlinkage modlinkage = {
	MODREV_1, (void *)&modldrv, NULL
};

/*
 * This is the module initialization/completion routines
 */

#if !defined(lint)
static char socal_initmsg[] = "socal _init: socal.c\t1.62\t08/19/2008\n";
#endif

int
_init(void)
{
	int stat;

	DEBUGF(4, (CE_CONT, socal_initmsg));

	/* Allocate soft state.  */
	stat = ddi_soft_state_init(&socal_soft_state_p,
	    sizeof (socal_state_t), SOCAL_INIT_ITEMS);
	if (stat != 0)
		return (stat);

	/* Install the module */
	stat = mod_install(&modlinkage);
	if (stat != 0)
		ddi_soft_state_fini(&socal_soft_state_p);

	DEBUGF(4, (CE_CONT, "socal: _init: return=%d\n", stat));
	return (stat);
}

int
_fini(void)
{
	int stat;

	if ((stat = mod_remove(&modlinkage)) != 0)
		return (stat);

	DEBUGF(4, (CE_CONT, "socal: _fini: \n"));

	ddi_soft_state_fini(&socal_soft_state_p);

	DEBUGF(4, (CE_CONT, "socal: _fini: return=%d\n", stat));
	return (stat);
}

int
_info(struct modinfo *modinfop)
{
	return (mod_info(&modlinkage, modinfop));
}


int
socal_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	int			instance;
	socal_state_t		*socalp;
	struct ether_addr	ourmacaddr;
	socal_port_t		*porta, *portb;
	char			buf[MAXPATHLEN];
	char			*cptr, *wwn;
	int			y;
	int			i, j;
	int			burstsize;
	short			s;
	int			loop_id;

	int			rval;


	instance = ddi_get_instance(dip);

	DEBUGF(4, (CE_CONT, "socal%d entering attach: cmd=%x\n", instance,
	    cmd));

	if (cmd == DDI_RESUME) {
		if ((socalp = ddi_get_driver_private(dip)) == NULL)
			return (DDI_FAILURE);

		if (!socalp->socal_shutdown) {
			/* our work is already done */
			return (DDI_SUCCESS);
		}
		if (socal_start(socalp) != FCAL_SUCCESS) {
			return	(DDI_FAILURE);
		}
		DEBUGF(4, (CE_CONT, "socal%d resumed\n", instance));
		return (DDI_SUCCESS);
	}

	if (cmd != DDI_ATTACH) {
		return (DDI_FAILURE);
	}

	if (ddi_dev_is_sid(dip) != DDI_SUCCESS) {
		cmn_err(CE_WARN, "socal%d probe: Not self-identifying",
		    instance);
		return (DDI_FAILURE);
	}

	/* If we are in a slave-slot, then we can't be used. */
	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
		cmn_err(CE_WARN,
		    "socal%d attach failed: device in slave-only slot",
		    instance);
		return (DDI_FAILURE);
	}

	if (ddi_intr_hilevel(dip, 0)) {
		/*
		 * Interrupt number '0' is a high-level interrupt.
		 * At this point you either add a special interrupt
		 * handler that triggers a soft interrupt at a lower level,
		 * or - more simply and appropriately here - you just
		 * fail the attach.
		 */
		cmn_err(CE_WARN,
		"socal%d attach failed: hilevel interrupt unsupported",
		    instance);
		return (DDI_FAILURE);
	}

	/* Allocate soft state. */
	if (ddi_soft_state_zalloc(socal_soft_state_p, instance)
	    != DDI_SUCCESS) {
		cmn_err(CE_WARN, "socal%d attach failed: alloc soft state",
		    instance);
		return (DDI_FAILURE);
	}
	DEBUGF(4, (CE_CONT, "socal%d attach: allocated soft state\n",
	    instance));

	/*
	 * Initialize the state structure.
	 */
	socalp = ddi_get_soft_state(socal_soft_state_p, instance);
	if (socalp == (socal_state_t *)NULL) {
		cmn_err(CE_WARN, "socal%d attach failed: bad soft state",
		    instance);
		return (DDI_FAILURE);
	}
	DEBUGF(4, (CE_CONT, "socal%d: attach: soc soft state ptr=0x%p\n",
	    instance, socalp));

	socalp->dip = dip;
	socallim = &default_socallim;
	porta = &socalp->port_state[0];
	portb = &socalp->port_state[1];

	/* Get the full path name for displaying error messages */
	cptr = ddi_pathname(dip, buf);
	(void) strcpy(socalp->socal_name, cptr);

	porta->sp_unsol_cb = NULL;
	portb->sp_unsol_cb = NULL;
	porta->sp_port = 0;
	portb->sp_port = 1;
	porta->sp_board = socalp;
	portb->sp_board = socalp;

	porta->sp_lilpmap_valid = 0;
	portb->sp_lilpmap_valid = 0;

	/*
	 * If an hard loop-id property is present, then the port is going
	 * to be used in target-mode so set the target-mode flag.
	 */
	loop_id = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "port0-loop-id", 127);
	if (loop_id >= 0 && loop_id <= 126) {
		porta->sp_status |= PORT_TARGET_MODE;
		porta->sp_hard_alpa = socal_switch_to_alpa[loop_id];
	} else porta->sp_hard_alpa = 0xfe;

	loop_id = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
	    "port1-loop-id", 127);
	if (loop_id >= 0 && loop_id <= 126) {
		portb->sp_status |= PORT_TARGET_MODE;
		portb->sp_hard_alpa = socal_switch_to_alpa[loop_id];
	} else portb->sp_hard_alpa = 0xfe;

	/* Get out Node wwn and calculate port wwns */
	rval = ddi_prop_op(DDI_DEV_T_ANY, dip,
	    PROP_LEN_AND_VAL_ALLOC, DDI_PROP_DONTPASS |
	    DDI_PROP_CANSLEEP, "wwn", (caddr_t)&wwn, &i);

	if ((rval != DDI_PROP_SUCCESS) || (i < FC_WWN_SIZE) ||
	    (bcmp(wwn, "00000000", FC_WWN_SIZE) == 0)) {
		(void) localetheraddr((struct ether_addr *)NULL, &ourmacaddr);

		bcopy((caddr_t)&ourmacaddr, (caddr_t)&s, sizeof (short));
		socalp->socal_n_wwn.w.wwn_hi = s;
		bcopy((caddr_t)&ourmacaddr+2,
		    (caddr_t)&socalp->socal_n_wwn.w.wwn_lo,
		    sizeof (uint_t));
		socalp->socal_n_wwn.w.naa_id = NAA_ID_IEEE;
		socalp->socal_n_wwn.w.nport_id = 0;
	} else {
		bcopy((caddr_t)wwn, (caddr_t)&socalp->socal_n_wwn, FC_WWN_SIZE);
	}

	if (rval == DDI_SUCCESS)
		kmem_free((void *)wwn, i);

	for (i = 0; i < FC_WWN_SIZE; i++) {
		(void) sprintf(&socalp->socal_stats.node_wwn[i << 1],
		    "%02x", socalp->socal_n_wwn.raw_wwn[i]);
	}
	DEBUGF(4, (CE_CONT, "socal%d attach: node wwn: %s\n",
	    instance, socalp->socal_stats.node_wwn));

	bcopy((caddr_t)&socalp->socal_n_wwn, (caddr_t)&porta->sp_p_wwn,
	    sizeof (la_wwn_t));
	bcopy((caddr_t)&socalp->socal_n_wwn, (caddr_t)&portb->sp_p_wwn,
	    sizeof (la_wwn_t));
	porta->sp_p_wwn.w.naa_id = NAA_ID_IEEE_EXTENDED;
	portb->sp_p_wwn.w.naa_id = NAA_ID_IEEE_EXTENDED;
	porta->sp_p_wwn.w.nport_id = instance*2;
	portb->sp_p_wwn.w.nport_id = instance*2+1;

	for (i = 0; i < FC_WWN_SIZE; i++) {
		(void) sprintf(&socalp->socal_stats.port_wwn[0][i << 1],
		    "%02x", porta->sp_p_wwn.raw_wwn[i]);
		(void) sprintf(&socalp->socal_stats.port_wwn[1][i << 1],
		    "%02x", portb->sp_p_wwn.raw_wwn[i]);
	}
	DEBUGF(4, (CE_CONT, "socal%d attach: porta wwn: %s\n",
	    instance, socalp->socal_stats.port_wwn[0]));
	DEBUGF(4, (CE_CONT, "socal%d attach: portb wwn: %s\n",
	    instance, socalp->socal_stats.port_wwn[1]));

	if ((porta->sp_transport = (fcal_transport_t *)
	    kmem_zalloc(sizeof (fcal_transport_t), KM_SLEEP)) == NULL) {
		socal_disp_err(socalp, CE_WARN, "attach.4011",
		    "attach failed: unable to alloc xport struct");
		goto fail;
	}

	if ((portb->sp_transport = (fcal_transport_t *)
	    kmem_zalloc(sizeof (fcal_transport_t), KM_SLEEP)) == NULL) {
		socal_disp_err(socalp, CE_WARN, "attach.4012",
		    "attach failed: unable to alloc xport struct");
		goto fail;
	}
	DEBUGF(4, (CE_CONT, "socal%d attach: allocated transport structs\n",
	    instance));

	/*
	 * Map the external ram and registers for SOC+.
	 * Note: Soc+ sbus host adapter provides 3 register definition
	 * but on-board Soc+'s  may have only one register definition.
	 */
	if ((ddi_dev_nregs(dip, &i) == DDI_SUCCESS) && (i == 1)) {
		/* Map XRAM */
		if (ddi_map_regs(dip, 0, &socalp->socal_xrp, 0, 0)
		    != DDI_SUCCESS) {
			socalp->socal_xrp = NULL;
			socal_disp_err(socalp, CE_WARN, "attach.4020",
			    "attach failed: unable to map XRAM");
			goto fail;
		}
		/* Map registers */
		socalp->socal_rp = (socal_reg_t *)(socalp->socal_xrp +
		    SOCAL_XRAM_SIZE);
	} else {
		/* Map EEPROM */
		if (ddi_map_regs(dip, 0, &socalp->socal_eeprom, 0, 0) !=
		    DDI_SUCCESS) {
			socalp->socal_eeprom = NULL;
			socal_disp_err(socalp, CE_WARN, "attach.4010",
			    "attach failed: unable to map eeprom");
			goto fail;
		}
	DEBUGF(4, (CE_CONT, "socal%d attach: mapped eeprom 0x%p\n",
	    instance, socalp->socal_eeprom));
		/* Map XRAM */
		if (ddi_map_regs(dip, 1, &socalp->socal_xrp, 0, 0) !=
		    DDI_SUCCESS) {
			socalp->socal_xrp = NULL;
			socal_disp_err(socalp, CE_WARN, "attach.4020",
			    "attach failed: unable to map XRAM");
			goto fail;
		}
	DEBUGF(4, (CE_CONT, "socal%d attach: mapped xram 0x%p\n",
	    instance, socalp->socal_xrp));
		/* Map registers */
		if (ddi_map_regs(dip, 2, (caddr_t *)&socalp->socal_rp, 0, 0) !=
		    DDI_SUCCESS) {
			socalp->socal_rp = NULL;
			socal_disp_err(socalp, CE_WARN, "attach.4030",
			    "attach failed: unable to map registers");
			goto fail;
		}
	DEBUGF(4, (CE_CONT, "socal%d attach: mapped regs 0x%p\n",
	    instance, socalp->socal_rp));
	}
	/*
	 * Check to see we really have a SOC+ Host Adapter card installed
	 */
	if (ddi_peek32(dip, (int32_t *)&socalp->socal_rp->socal_csr.w,
	    (int32_t *)NULL) != DDI_SUCCESS) {
		socal_disp_err(socalp, CE_WARN, "attach.4040",
		    "attach failed: unable to access status register");
		goto fail;
	}
	/* now that we have our registers mapped make sure soc+ reset */
	socal_disable(socalp);

	/* try defacing a spot in XRAM */
	if (ddi_poke32(dip, (int32_t *)(socalp->socal_xrp + SOCAL_XRAM_UCODE),
	    0xdefaced) != DDI_SUCCESS) {
		socal_disp_err(socalp, CE_WARN, "attach.4050",
		    "attach failed: unable to write host adapter XRAM");
		goto fail;
	}

	/* see if it stayed defaced */
	if (ddi_peek32(dip, (int32_t *)(socalp->socal_xrp + SOCAL_XRAM_UCODE),
	    (int32_t *)&y)
	    != DDI_SUCCESS) {
		socal_disp_err(socalp, CE_WARN, "attach.4051",
		    "attach failed: unable to access host adapter XRAM");
		goto fail;
	}

#ifdef DEBUG
	for (i = 0; i < 4; i++) {
		socalp->socal_rp->socal_cr.w &=
		    ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
		socalp->socal_rp->socal_cr.w |= i<<24;
		cptr = (char *)(socal_xrambuf + (i*0x10000));
		bcopy((caddr_t)socalp->socal_xrp, (caddr_t)cptr, 0x10000);
	}
	socalp->socal_rp->socal_cr.w &= ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
#endif

	DEBUGF(4, (CE_CONT, "socal%d attach: read xram\n", instance));

	if (y != 0xdefaced) {
		socal_disp_err(socalp, CE_WARN, "attach.4052",
		    "attach failed: read/write mismatch in XRAM");
		goto fail;
	}

	/* Point to the SOC XRAM CQ Descriptor locations. */
	socalp->xram_reqp = (soc_cq_t *)(socalp->socal_xrp +
	    SOCAL_XRAM_REQ_DESC);
	socalp->xram_rspp = (soc_cq_t *)(socalp->socal_xrp +
	    SOCAL_XRAM_RSP_DESC);

	if ((socalp->socal_ksp = kstat_create("socal", instance, "statistics",
	    "controller", KSTAT_TYPE_RAW, sizeof (struct socal_stats),
	    KSTAT_FLAG_VIRTUAL)) == NULL) {
		socal_disp_err(socalp, CE_WARN, "attach.4053",
		    "unable to create kstats");
	} else {
		socalp->socal_stats.version = 2;
		(void) sprintf(socalp->socal_stats.drvr_name,
		    "%s: %s", SOCAL_NAME, socal_version);
		socalp->socal_stats.pstats[0].port = 0;
		socalp->socal_stats.pstats[1].port = 1;
		socalp->socal_ksp->ks_data = (void *)&socalp->socal_stats;
		kstat_install(socalp->socal_ksp);
	}

	/*
	 * Install a dummy interrupt routine.
	 */
	if (ddi_add_intr(dip,
	    (uint_t)0,
	    &socalp->iblkc,
	    &socalp->idevc,
	    socal_dummy_intr,
	    (caddr_t)socalp) != DDI_SUCCESS) {
			socal_disp_err(socalp, CE_WARN, "attach.4060",
			"attach failed: unable to install interrupt handler");
			goto fail;
	}

	ddi_set_driver_private(dip, socalp);

	/* initialize the interrupt mutex */
	mutex_init(&socalp->k_imr_mtx, NULL, MUTEX_DRIVER,
	    (void *)socalp->iblkc);

	mutex_init(&socalp->board_mtx, NULL, MUTEX_DRIVER,
	    (void *)socalp->iblkc);
	mutex_init(&socalp->ioctl_mtx, NULL, MUTEX_DRIVER,
	    (void *)socalp->iblkc);

	/* initialize the abort mutex */
	mutex_init(&socalp->abort_mtx, NULL, MUTEX_DRIVER,
	    (void *)socalp->iblkc);

	cv_init(&socalp->board_cv, NULL, CV_DRIVER, NULL);
	DEBUGF(4, (CE_CONT,
	    "socal%d: attach: inited imr mutex, board mutex, board cv\n",
	    instance));

	/* init the port mutexes */
	mutex_init(&porta->sp_mtx, NULL, MUTEX_DRIVER, socalp->iblkc);
	cv_init(&porta->sp_cv, NULL, CV_DRIVER, NULL);
	mutex_init(&portb->sp_mtx, NULL, MUTEX_DRIVER, socalp->iblkc);
	cv_init(&portb->sp_cv, NULL, CV_DRIVER, NULL);
	DEBUGF(4, (CE_CONT, "socal%d: attach: inited port mutexes and cvs\n",
	    instance));

	/* get local copy of service params */
	socal_wcopy((uint_t *)socalp->socal_xrp + SOCAL_XRAM_SERV_PARAMS,
	    (uint_t *)socalp->socal_service_params, SOCAL_SVC_LENGTH);
	DEBUGF(4, (CE_CONT, "socal%d: attach: got service params\n", instance));
	/*
	 * Initailize the FCAL transport interface.
	 */
	socal_init_transport_interface(socalp);
	DEBUGF(4, (CE_CONT, "socal%d: attach: initalized transport interface\n",
	    instance));

	/*
	 * Allocate request and response queues and init their mutexs.
	 */
	for (i = 0; i < SOCAL_N_CQS; i++) {
		if (socal_cqalloc_init(socalp, i) != FCAL_SUCCESS) {
			goto fail;
		}
	}
	DEBUGF(4, (CE_CONT, "socal%d: attach: allocated cqs\n", instance));

	/*
	 * Adjust the burst size we'll use.
	 */
	burstsize = ddi_dma_burstsizes(socalp->request[0].skc_dhandle);
	DEBUGF(4, (CE_CONT, "socal%d: attach: burstsize = 0x%x\n",
	    instance, burstsize));
	j = burstsize & BURSTSIZE_MASK;
	for (i = 0; socal_burst32_table[i] != SOCAL_CR_BURST_64; i++)
		if (!(j >>= 1)) break;

	socalp->socal_cfg = (socalp->socal_cfg & ~SOCAL_CR_SBUS_BURST_SIZE_MASK)
	    | socal_burst32_table[i];

	if (socal_64bitsbus) {
		if (ddi_dma_set_sbus64(socalp->request[0].skc_dhandle,
		    socal_dma_attr.dma_attr_burstsizes | BURST128) ==
		    DDI_SUCCESS) {
			DEBUGF(4, (CE_CONT, "socal%d: enabled 64 bit sbus\n",
			    instance));
			socalp->socal_cfg |= SOCAL_CR_SBUS_ENHANCED;
			burstsize = ddi_dma_burstsizes(socalp->request[0].
			    skc_dhandle);
		DEBUGF(4, (CE_CONT, "socal%d: attach: 64bit burstsize = 0x%x\n",
		    instance, burstsize));
			j = burstsize & BURSTSIZE_MASK;
			for (i = 0; socal_burst64_table[i] !=
			    (SOCAL_CR_BURST_128 << 8); i++)
				if (!(j >>= 1))
					break;

			socalp->socal_cfg = (socalp->socal_cfg &
			    ~SOCAL_CR_SBUS_BURST_SIZE_64BIT_MASK) |
			    socal_burst64_table[i];
		}
	}

	ddi_remove_intr(dip, 0, socalp->iblkc);
	socalp->iblkc = (void *)NULL;
	/*
	 * Install the interrupt routine.
	 */
	if (ddi_add_intr(dip,
	    (uint_t)0,
	    &socalp->iblkc,
	    &socalp->idevc,
	    socal_intr,
	    (caddr_t)socalp) != DDI_SUCCESS) {
			socal_disp_err(socalp, CE_WARN, "attach.4060",
			"attach failed: unable to install interrupt handler");
			goto fail;
	}

	DEBUGF(4, (CE_CONT, "socal%d: attach: set config reg %x\n",
	    instance, socalp->socal_cfg));

	if (ddi_create_minor_node(dip, SOCAL_PORTA_NAME, S_IFCHR,
	    instance*N_SOCAL_NPORTS, SOCAL_NT_PORT, 0) != DDI_SUCCESS)
		goto fail;
	if (ddi_create_minor_node(dip, SOCAL_PORTB_NAME, S_IFCHR,
	    instance*N_SOCAL_NPORTS+1, SOCAL_NT_PORT, 0) != DDI_SUCCESS)
		goto fail;

	if (socal_start(socalp) != FCAL_SUCCESS)
		goto fail;
	DEBUGF(4, (CE_CONT, "socal%d: attach: soc+ started\n", instance));

	ddi_report_dev(dip);

	DEBUGF(2, (CE_CONT, "socal%d: attach O.K.\n\n", instance));

	return (DDI_SUCCESS);

fail:
	DEBUGF(4, (CE_CONT, "socal%d: attach: DDI_FAILURE\n", instance));

	/* Make sure soc reset */
	socal_disable(socalp);

	/* let detach do the dirty work */
	(void) socal_dodetach(dip);

	return (DDI_FAILURE);
}

static int
socal_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	int		resp;
	socal_state_t	*socalp;
	int		i;


	switch (cmd) {

	case DDI_SUSPEND:
		DEBUGF(4, (CE_CONT, "socal: suspend called\n"));

		if ((socalp = ddi_get_driver_private(dip)) == NULL)
			return (DDI_FAILURE);

		/*
		 * If any of the ports are in target-mode, don't suspend
		 */
		for (i = 0; i < N_SOCAL_NPORTS; i++) {
			if (socalp->port_state[i].sp_status & PORT_TARGET_MODE)
				return (DDI_FAILURE);
		}

		/* do not restart socal after reset */
		socal_force_reset((void *)socalp, 0, DONT_RESET_PORT);

		return (DDI_SUCCESS);

	case DDI_DETACH:
		DEBUGF(4, (CE_CONT, "socal: detach called\n"));
		resp = socal_dodetach(dip);
		if (resp == DDI_SUCCESS)
			ddi_set_driver_private(dip, NULL);
		return (resp);

	default:
		return (DDI_FAILURE);
	}
}

static int
socal_dodetach(dev_info_t *dip)
{

	int		instance = ddi_get_instance(dip);
	int		i;
	socal_state_t	*socalp;
	socal_port_t	*portp;
	socal_unsol_cb_t	*cb, *cbn = NULL;

	/* Get the soft state struct. */
	if ((socalp = ddi_get_soft_state(socal_soft_state_p, instance)) == 0) {
		return (DDI_FAILURE);
	}

	/*
	 * If somebody is still attached to us from above fail
	 * detach.
	 */
	mutex_enter(&socalp->board_mtx);
	if (socalp->socal_busy > 0) {
		mutex_exit(&socalp->board_mtx);
		return (DDI_FAILURE);
	}
	/* mark socal_busy = -1 to disallow sftm attach */
	socalp->socal_busy = -1;
	mutex_exit(&socalp->board_mtx);

	/* Make sure soc+ reset */
	mutex_enter(&socalp->k_imr_mtx);
	socal_disable(socalp);
	mutex_exit(&socalp->k_imr_mtx);

	/* remove soc+ interrupt */
	if (socalp->iblkc != (void *)NULL) {
		ddi_remove_intr(dip, (uint_t)0, socalp->iblkc);
		DEBUGF(2, (CE_CONT,
		    "socal%d: detach: Removed SOC+ interrupt from ddi\n",
		    instance));
	}

	for (i = 0; i < N_SOCAL_NPORTS; i++) {
		portp = &socalp->port_state[i];
		mutex_destroy(&portp->sp_mtx);
		cv_destroy(&portp->sp_cv);
		mutex_destroy(&portp->sp_transport->fcal_mtx);
		cv_destroy(&portp->sp_transport->fcal_cv);
		kmem_free((void *)portp->sp_transport,
		    sizeof (fcal_transport_t));
		for (cb = portp->sp_unsol_cb; cb != (socal_unsol_cb_t *)NULL;
		    cb = cbn) {
			cbn = cb->next;
			kmem_free((void *)cb, sizeof (socal_unsol_cb_t));
		}
		portp->sp_unsol_cb = (socal_unsol_cb_t *)NULL;
	}

	/*
	 * Free request queues, if allocated
	 */
	for (i = 0; i < SOCAL_N_CQS; i++) {
		/* Free the queues and destroy their mutexes. */
		mutex_destroy(&socalp->request[i].skc_mtx);
		mutex_destroy(&socalp->response[i].skc_mtx);
		cv_destroy(&socalp->request[i].skc_cv);
		cv_destroy(&socalp->response[i].skc_cv);

		if (socalp->request[i].skc_dhandle) {
			(void) ddi_dma_unbind_handle(socalp->
			    request[i].skc_dhandle);
			ddi_dma_free_handle(&socalp->request[i].skc_dhandle);
		}
		if (socalp->request[i].skc_cq_raw) {
			ddi_dma_mem_free(&socalp->request[i].skc_acchandle);
			socalp->request[i].skc_cq_raw = NULL;
			socalp->request[i].skc_cq = NULL;
		}
		if (socalp->response[i].skc_dhandle) {
			(void) ddi_dma_unbind_handle(socalp->
			    response[i].skc_dhandle);
			ddi_dma_free_handle(&socalp->response[i].skc_dhandle);
		}
		if (socalp->response[i].skc_cq_raw) {
			ddi_dma_mem_free(&socalp->response[i].skc_acchandle);
			socalp->response[i].skc_cq_raw = NULL;
			socalp->response[i].skc_cq = NULL;
		}
		if (socalp->request[i].deferred_intr_timeoutid) {
			(void) untimeout(socalp->
			    request[i].deferred_intr_timeoutid);
		}
		if (socalp->response[i].deferred_intr_timeoutid) {
			(void) untimeout(socalp->
			    response[i].deferred_intr_timeoutid);
		}
	}

	mutex_destroy(&socalp->abort_mtx);
	mutex_destroy(&socalp->board_mtx);
	mutex_destroy(&socalp->ioctl_mtx);
	cv_destroy(&socalp->board_cv);

	/*
	 * Free soc data buffer pool
	 */
	if (socalp->pool_dhandle) {
		(void) ddi_dma_unbind_handle(socalp->pool_dhandle);
		ddi_dma_free_handle(&socalp->pool_dhandle);
	}
	if (socalp->pool) {
		ddi_dma_mem_free(&socalp->pool_acchandle);
	}

	/* release register maps */
	/* Unmap EEPROM */
	if (socalp->socal_eeprom != NULL) {
		ddi_unmap_regs(dip, 0, &socalp->socal_eeprom, 0, 0);
	}

	/* Unmap XRAM */
	if (socalp->socal_xrp != NULL) {
		ddi_unmap_regs(dip, 1, &socalp->socal_xrp, 0, 0);
	}

	/* Unmap registers */
	if (socalp->socal_rp != NULL) {
		ddi_unmap_regs(dip, 2, (caddr_t *)&socalp->socal_rp, 0, 0);
	}

	if (socalp->socal_ksp != NULL)
		kstat_delete(socalp->socal_ksp);

	mutex_destroy(&socalp->k_imr_mtx);

	ddi_remove_minor_node(dip, NULL);

	ddi_soft_state_free(socal_soft_state_p, instance);

	return (DDI_SUCCESS);
}


int
socal_bus_ctl(dev_info_t *dip, dev_info_t *rip, ddi_ctl_enum_t op,
	void *a, void *v)
{
	int		port;


	switch (op) {
	case DDI_CTLOPS_REPORTDEV:
		port = ddi_getprop(DDI_DEV_T_ANY, rip, DDI_PROP_DONTPASS,
		    SOCAL_PORT_NO_PROP, -1);
		if ((port < 0) || (port > 1)) {
			port = ddi_getprop(DDI_DEV_T_ANY, rip,
			    DDI_PROP_DONTPASS, SOCAL_ALT_PORT_NO_PROP, -1);
		}
		/* log text identifying this driver (d) & its child (r) */
		cmn_err(CE_CONT, "?%s%d at %s%d: socal_port %d\n",
		    ddi_driver_name(rip), ddi_get_instance(rip),
		    ddi_driver_name(dip), ddi_get_instance(dip),
		    port);
		break;

	case DDI_CTLOPS_INITCHILD: {
		dev_info_t	*child_dip = (dev_info_t *)a;
		char		name[MAXNAMELEN];
		socal_state_t	*socalp;

		if ((socalp = ddi_get_driver_private(dip)) == NULL)
			return (DDI_FAILURE);

		port = ddi_getprop(DDI_DEV_T_ANY, child_dip,
		    DDI_PROP_DONTPASS, SOCAL_PORT_NO_PROP, -1);

		if ((port < 0) || (port > 1)) {
			port = ddi_getprop(DDI_DEV_T_ANY, child_dip,
			    DDI_PROP_DONTPASS, SOCAL_ALT_PORT_NO_PROP, -1);
			if ((port < 0) || (port > 1)) {
				return (DDI_NOT_WELL_FORMED);
			}
		}
		mutex_enter(&socalp->board_mtx);
		mutex_enter(&socalp->port_state[port].sp_mtx);
		if (socalp->port_state[port].sp_status &
		    (PORT_CHILD_INIT | PORT_TARGET_MODE)) {
			mutex_exit(&socalp->port_state[port].sp_mtx);
			mutex_exit(&socalp->board_mtx);
			return (DDI_FAILURE);
		}
		socalp->socal_busy++;
		socalp->port_state[port].sp_status |= PORT_CHILD_INIT;
		mutex_exit(&socalp->port_state[port].sp_mtx);
		mutex_exit(&socalp->board_mtx);
		ddi_set_parent_data(child_dip,
		    socalp->port_state[port].sp_transport);
		(void) sprintf((char *)name, "%x,0", port);
		ddi_set_name_addr(child_dip, name);
		break;
	}

	case DDI_CTLOPS_UNINITCHILD: {
		dev_info_t	*child_dip = (dev_info_t *)a;
		socal_state_t	*socalp;

		socalp = ddi_get_driver_private(dip);
		port = ddi_getprop(DDI_DEV_T_ANY, child_dip,
		    DDI_PROP_DONTPASS, SOCAL_PORT_NO_PROP, -1);

		if ((port < 0) || (port > 1)) {
			port = ddi_getprop(DDI_DEV_T_ANY, child_dip,
			    DDI_PROP_DONTPASS, SOCAL_ALT_PORT_NO_PROP, -1);
			if ((port < 0) || (port > 1)) {
				return (DDI_NOT_WELL_FORMED);
			}
		}

		ddi_set_parent_data(child_dip, NULL);
		(void) ddi_set_name_addr(child_dip, NULL);
		mutex_enter(&socalp->board_mtx);
		mutex_enter(&socalp->port_state[port].sp_mtx);
		socalp->socal_busy--;
		socalp->port_state[port].sp_status &= ~PORT_CHILD_INIT;
		mutex_exit(&socalp->port_state[port].sp_mtx);
		mutex_exit(&socalp->board_mtx);

		break;
	}

	case DDI_CTLOPS_IOMIN: {
		int val;

		val = *((int *)v);
		val = maxbit(val, socallim->dlim_minxfer);
		/*
		 * The 'arg' value of nonzero indicates 'streaming' mode.
		 * If in streaming mode, pick the largest of our burstsizes
		 * available and say that that is our minimum value (modulo
		 * what minxfer is).
		 */
		if ((int)(uintptr_t)a) {
			val = maxbit(val,
			    1<<(ddi_fls(socallim->dlim_burstsizes)-1));
		} else {
			val = maxbit(val,
			    1<<(ddi_ffs(socallim->dlim_burstsizes)-1));
		}

		*((int *)v) = val;
		return (ddi_ctlops(dip, rip, op, a, v));
	}

	/*
	 * These ops are not available on this nexus.
	 */

	case DDI_CTLOPS_DMAPMAPC:
	case DDI_CTLOPS_REGSIZE:
	case DDI_CTLOPS_NREGS:
	case DDI_CTLOPS_AFFINITY:
	case DDI_CTLOPS_SIDDEV:
	case DDI_CTLOPS_POKE:
	case DDI_CTLOPS_PEEK:
		return (DDI_FAILURE);

	case DDI_CTLOPS_SLAVEONLY:
	case DDI_CTLOPS_REPORTINT:
	default:
		/*
		 * Remaining requests get passed up to our parent
		 */
		DEBUGF(2, (CE_CONT, "%s%d: op (%d) from %s%d\n",
		    ddi_get_name(dip), ddi_get_instance(dip),
		    op, ddi_get_name(rip), ddi_get_instance(rip)));
		return (ddi_ctlops(dip, rip, op, a, v));
	}

	return (DDI_SUCCESS);
}


/*ARGSUSED*/
/*
 * int
 * socal_getinfo() - Given the device number, return the devinfo
 * 	pointer or the instance number.  Note: this routine must be
 * 	successful on DDI_INFO_DEVT2INSTANCE even before attach.
 */
int
socal_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
	void **result)
{
	int instance;
	socal_state_t *socalp;

	instance = getminor((dev_t)arg) / 2;

	switch (cmd) {
	case DDI_INFO_DEVT2DEVINFO:
		socalp = ddi_get_soft_state(socal_soft_state_p, instance);
		if (socalp)
			*result = socalp->dip;
		else
			*result = NULL;
		break;

	case DDI_INFO_DEVT2INSTANCE:
		*result = (void *)(uintptr_t)instance;
		break;

	default:
		return (DDI_FAILURE);
	}

	return (DDI_SUCCESS);
}

/*ARGSUSED*/
int
socal_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
{
	int 	instance = getminor(*devp)/2;
	socal_state_t	*socalp =
	    ddi_get_soft_state(socal_soft_state_p, instance);
	socal_port_t	*port_statep;
	int		port;

	if (socalp == NULL)
		return (ENXIO);

	port = getminor(*devp)%2;
	port_statep = &socalp->port_state[port];

	mutex_enter(&port_statep->sp_mtx);
	port_statep->sp_status |= PORT_OPEN;
	mutex_exit(&port_statep->sp_mtx);
	DEBUGF(2, (CE_CONT,
	    "socal%d: open of port %d\n", instance, port));
	return (0);
}

/*ARGSUSED*/
int
socal_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
{
	int 	instance = getminor(dev)/2;
	socal_state_t	*socalp =
	    ddi_get_soft_state(socal_soft_state_p, instance);
	socal_port_t	*port_statep;
	int		port;

	port = getminor(dev)%2;
	port_statep = &socalp->port_state[port];

	mutex_enter(&port_statep->sp_mtx);
	port_statep->sp_status &= ~PORT_OPEN;
	mutex_exit(&port_statep->sp_mtx);
	DEBUGF(2, (CE_CONT,
	    "socal%d: clsoe of port %d\n", instance, port));
	return (0);
}

/*ARGSUSED*/
int
socal_ioctl(dev_t dev,
    int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
{
	int 	instance = getminor(dev)/2;
	socal_state_t	*socalp =
	    ddi_get_soft_state(socal_soft_state_p, instance);
	int		port;
	socal_port_t	*port_statep;
	int 		i;
	uint_t		r;
	int		offset;
	int 		retval = FCAL_SUCCESS;
	la_els_adisc_t		*adisc_pl;
	la_els_rls_reply_t	*rls_pl;
	dev_info_t	*dip;
	char		*buffer, tmp[10];
	struct socal_fm_version ver;
#ifdef _MULTI_DATAMODEL
	struct socal_fm_version32 {
		uint_t	fcode_ver_len;
		uint_t	mcode_ver_len;
		uint_t	prom_ver_len;
		caddr32_t	fcode_ver;
		caddr32_t	mcode_ver;
		caddr32_t	prom_ver;
	} ver32;
	uint_t		dm32 = 0;
#endif

	uchar_t		*flb_pl;
	flb_hdr_t	*flb_hdr;
	uint_t		flb_size;

	if (socalp == NULL)
		return (ENXIO);

	DEBUGF(4, (CE_CONT, "socal%d ioctl: got command %x\n", instance, cmd));
	port = getminor(dev)%2;

	switch (cmd) {
	case FCIO_FCODE_MCODE_VERSION:
#ifdef _MULTI_DATAMODEL
		switch (ddi_model_convert_from(mode & FMODELS)) {
			case DDI_MODEL_ILP32:
				dm32 = 1;
				if (ddi_copyin((caddr_t)arg,
				    (caddr_t)&ver32, sizeof (ver32),
				    mode) == -1)
					return (EFAULT);
				ver.fcode_ver_len =
				    ver32.fcode_ver_len;
				ver.mcode_ver_len =
				    ver32.mcode_ver_len;
				ver.prom_ver_len =
				    ver32.prom_ver_len;
				ver.fcode_ver =
				    (caddr_t)(uintptr_t)ver32.fcode_ver;
				ver.mcode_ver =
				    (caddr_t)(uintptr_t)ver32.mcode_ver;
				ver.prom_ver =
				    (caddr_t)(uintptr_t)ver32.prom_ver;
				break;
			case DDI_MODEL_NONE:
				if (ddi_copyin((caddr_t)arg,
				    (caddr_t)&ver, sizeof (ver),
				    mode) == -1)
					return (EFAULT);
		}
#else /* _MULTI_DATAMODEL */
		if (ddi_copyin((caddr_t)arg, (caddr_t)&ver,
		    sizeof (ver), mode) == -1)
			return (EFAULT);
#endif /* _MULTI_DATAMODEL */
		dip = socalp->dip;
		if (ddi_prop_op(DDI_DEV_T_ANY, dip,
		    PROP_LEN_AND_VAL_ALLOC, DDI_PROP_DONTPASS |
		    DDI_PROP_CANSLEEP, "version", (caddr_t)&buffer,
		    &i) != DDI_PROP_SUCCESS)
			return (EIO);
		if (i < ver.fcode_ver_len)
			ver.fcode_ver_len = i;
		if (ddi_copyout((caddr_t)buffer,
		    (caddr_t)ver.fcode_ver, ver.fcode_ver_len,
		    mode) == -1) {
			kmem_free((caddr_t)buffer, i);
			return (EFAULT);
		}
		kmem_free((caddr_t)buffer, i);
		if (socalp->socal_eeprom) {
			for (i = 0; i < SOCAL_N_CQS; i++) {
				mutex_enter(
				    &socalp->request[i].skc_mtx);
				mutex_enter(
				    &socalp->response[i].skc_mtx);
			}
			i = socalp->socal_rp->socal_cr.w;
			socalp->socal_rp->socal_cr.w &=
			    ~SOCAL_CR_EEPROM_BANK_MASK;
			socalp->socal_rp->socal_cr.w |= 3 << 16;
			if (ver.prom_ver_len > 10)
				ver.prom_ver_len = 10;
			bcopy((caddr_t)socalp->socal_eeprom + (unsigned)
			    0xfff6, tmp, 10);
			socalp->socal_rp->socal_cr.w  = i;
			for (i = SOCAL_N_CQS-1; i >= 0; i--) {
				mutex_exit(&socalp->request[i].skc_mtx);
				mutex_exit(
				    &socalp->response[i].skc_mtx);
			}
			if (ddi_copyout((caddr_t)tmp,
			    (caddr_t)ver.prom_ver,
			    ver.prom_ver_len, mode) == -1)
				return (EFAULT);
		} else {
			ver.prom_ver_len = 0;
		}
		ver.mcode_ver_len = 0;
#ifdef _MULTI_DATAMODEL
		if (dm32) {
			ver32.fcode_ver_len = ver.fcode_ver_len;
			ver32.mcode_ver_len = ver.mcode_ver_len;
			ver32.prom_ver_len = ver.prom_ver_len;
			ver32.fcode_ver = (caddr32_t)(uintptr_t)
			    ver.fcode_ver;
			ver32.mcode_ver = (caddr32_t)(uintptr_t)
			    ver.mcode_ver;
			ver32.prom_ver = (caddr32_t)(uintptr_t)
			    ver.prom_ver;
			if (ddi_copyout((caddr_t)&ver32,
			    (caddr_t)arg, sizeof (ver32),
			    mode) == -1)
				return (EFAULT);
		} else
#endif /* _MULTI_DATAMODEL */
		if (ddi_copyout((caddr_t)&ver, (caddr_t)arg,
		    sizeof (struct socal_fm_version), mode) == -1)
			return (EFAULT);
		break;
	case FCIO_LOADUCODE:
		mutex_enter(&socalp->k_imr_mtx);
		socal_disable(socalp);
		mutex_exit(&socalp->k_imr_mtx);
		if (copyin((caddr_t)arg, (caddr_t)socal_ucode, 0x10000)
		    == -1)
			return (EFAULT);
		/* restart socal after resetting */
		(void) socal_force_reset((void *)socalp, 0,
		    RESET_PORT);
		break;
	case FCIO_DUMPXRAM:
		for (i = 0; i < SOCAL_N_CQS; i++) {
			mutex_enter(&socalp->request[i].skc_mtx);
			mutex_enter(&socalp->response[i].skc_mtx);
		}
		for (i = 0; i < 4; i++) {
			offset = arg+(0x10000 * i);
			socalp->socal_rp->socal_cr.w &=
			    ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
			socalp->socal_rp->socal_cr.w |= i<<24;
			(void) copyout((caddr_t)socalp->socal_xrp,
			    (caddr_t)(uintptr_t)offset, 0x10000);
		}
		socalp->socal_rp->socal_cr.w &=
		    ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
		for (i = SOCAL_N_CQS-1; i >= 0; i--) {
			mutex_exit(&socalp->request[i].skc_mtx);
			mutex_exit(&socalp->response[i].skc_mtx);
		}
		break;
#ifdef DEBUG
	case FCIO_DUMPXRAMBUF:
		(void) copyout((caddr_t)socal_xrambuf, (caddr_t)arg,
		    0x40000);
		break;
#endif
	case FCIO_GETMAP:
		mutex_enter(&socalp->ioctl_mtx);
		if (socal_getmap(socalp, port, (caddr_t)arg, 0, 0) ==
		    -1)
			retval = FCAL_ALLOC_FAILED;
		mutex_exit(&socalp->ioctl_mtx);
		break;
	case FCIO_BYPASS_DEV:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_bypass_dev((void *)socalp, port, arg);
		mutex_exit(&socalp->ioctl_mtx);
		break;
	case FCIO_FORCE_LIP:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_force_lip((void *)socalp, port, 0,
		    FCAL_FORCE_LIP);
		mutex_exit(&socalp->ioctl_mtx);
		break;
	case FCIO_FORCE_OFFLINE:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_force_offline((void *)socalp, port, 0);
		mutex_exit(&socalp->ioctl_mtx);
		break;
	case FCIO_ADISC_ELS:
	{
		if ((adisc_pl =
		    (la_els_adisc_t *)kmem_zalloc(
		    sizeof (la_els_adisc_t),
		    KM_NOSLEEP)) == NULL)
			return (ENOMEM);

		if (copyin((caddr_t)arg, (caddr_t)adisc_pl,
		    sizeof (la_els_adisc_t)) == -1) {
			kmem_free((void *)adisc_pl,
			    sizeof (la_els_adisc_t));
			return (EFAULT);
		}
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_issue_adisc(socalp, port,
		    adisc_pl->nport_id,
		    adisc_pl, 0);
		mutex_exit(&socalp->ioctl_mtx);

		if (retval == FCAL_SUCCESS) {
		if (copyout((caddr_t)adisc_pl, (caddr_t)arg,
		    sizeof (la_els_adisc_t)) == -1) {
			kmem_free((void *)adisc_pl,
			    sizeof (la_els_adisc_t));
			return (EFAULT);
		}
		}

		kmem_free((void *)adisc_pl, sizeof (la_els_adisc_t));
		break;
	}
	case FCIO_LINKSTATUS:
	{
		int dest;
		if ((rls_pl =
		    (la_els_rls_reply_t *)
		    kmem_zalloc(sizeof (la_els_rls_reply_t),
		    KM_NOSLEEP)) == NULL)
			return (ENOMEM);

		if (copyin((caddr_t)arg, (caddr_t)rls_pl,
		    sizeof (la_els_rls_reply_t)) == -1) {
			kmem_free((void *)rls_pl,
			    sizeof (la_els_rls_reply_t));
		return (EFAULT);
		}
		dest = (rls_pl->mbz[0] << 16) + (rls_pl->mbz[1] << 8) +
		    rls_pl->mbz[2];
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_issue_rls(socalp, port, dest,
		    rls_pl, 0);
		mutex_exit(&socalp->ioctl_mtx);

		if (retval == FCAL_SUCCESS) {
		if (copyout((caddr_t)rls_pl, (caddr_t)arg,
		    sizeof (la_els_rls_reply_t)) == -1) {
			kmem_free((void *)rls_pl,
			    sizeof (la_els_rls_reply_t));
			return (EFAULT);
		}
		}
		kmem_free((void *)rls_pl, sizeof (la_els_rls_reply_t));
		break;
	}
	case FCIO_LOOPBACK_INTERNAL:
		/*
		 * If userland doesn't provide a location for a return
		 * value the driver will permanently offline the port,
		 * ignoring any checks for devices on the loop.
		 */
		mutex_enter(&socalp->ioctl_mtx);
		if (arg == 0) {
			port_statep = &socalp->port_state[port];
			mutex_enter(&port_statep->sp_mtx);
			if (port_statep->sp_status & PORT_DISABLED) {
				/* Already disabled */
				mutex_exit(&port_statep->sp_mtx);
				mutex_exit(&socalp->ioctl_mtx);
				return (EALREADY);
			}
			port_statep->sp_status |= PORT_DISABLED;
			mutex_exit(&port_statep->sp_mtx);
		}
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_INT_LOOP);
		mutex_exit(&socalp->ioctl_mtx);
		if (arg == 0) break;
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_LOOPBACK_MANUAL:
		mutex_enter(&socalp->ioctl_mtx);
		port_statep = &socalp->port_state[port];
		mutex_enter(&port_statep->sp_mtx);
		if (port_statep->sp_status & PORT_DISABLED) {
			mutex_exit(&port_statep->sp_mtx);
			mutex_exit(&socalp->ioctl_mtx);
			return (EBUSY);
		}
		mutex_exit(&port_statep->sp_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_EXT_LOOP);
		mutex_exit(&socalp->ioctl_mtx);
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_NO_LOOPBACK:
		mutex_enter(&socalp->ioctl_mtx);
		port_statep = &socalp->port_state[port];
		mutex_enter(&port_statep->sp_mtx);
		/* Do not allow online if we're disabled */
		if (port_statep->sp_status & PORT_DISABLED) {
			if (arg != 0) {
				mutex_exit(&port_statep->sp_mtx);
				mutex_exit(&socalp->ioctl_mtx);
				/*
				 * It's permanently disabled -- Need to
				 * enable it first
				 */
				return (EBUSY);
			}
			/* This was a request to online. */
			port_statep->sp_status &= ~PORT_DISABLED;
		}
		mutex_exit(&port_statep->sp_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_REM_LOOP);
		mutex_exit(&socalp->ioctl_mtx);
		if (arg == 0) break;
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_DIAG_NOP:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_NOP);
		mutex_exit(&socalp->ioctl_mtx);
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_DIAG_XRAM:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_XRAM_TEST);
		mutex_exit(&socalp->ioctl_mtx);
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_DIAG_SOC:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_SOC_TEST);
		mutex_exit(&socalp->ioctl_mtx);
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_DIAG_HCB:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_HCB_TEST);
		mutex_exit(&socalp->ioctl_mtx);
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_DIAG_SOCLB:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_SOCLB_TEST);
		mutex_exit(&socalp->ioctl_mtx);
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_DIAG_SRDSLB:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_SRDSLB_TEST);
		mutex_exit(&socalp->ioctl_mtx);
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_DIAG_EXTLB:
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    SOC_DIAG_EXTOE_TEST);
		mutex_exit(&socalp->ioctl_mtx);
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_DIAG_RAW:
		if (copyin((caddr_t)arg, (caddr_t)&i, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_diag_request((void *)socalp, port, &r,
		    (uint_t)i);
		mutex_exit(&socalp->ioctl_mtx);
		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
		    == -1)
			return (EFAULT);
		break;
	case FCIO_LOOPBACK_FRAME:
		if ((flb_hdr = (flb_hdr_t *)kmem_zalloc(sizeof (flb_hdr_t),
		    KM_NOSLEEP)) == NULL)
			return (ENOMEM);

		if (copyin((caddr_t)arg,
		    (caddr_t)flb_hdr, sizeof (flb_hdr_t)) == -1) {
		kmem_free((void *)flb_hdr, sizeof (flb_hdr_t));
		return (EFAULT);
		}

		flb_size = flb_hdr->length;

		if ((flb_pl =
		    (uchar_t *)kmem_zalloc(flb_size, KM_NOSLEEP)) == NULL)
			return (ENOMEM);

		if (copyin((caddr_t)(arg + sizeof (flb_hdr_t)),
		    (caddr_t)flb_pl, flb_size) == -1) {
			kmem_free((void *)flb_pl, flb_size);
			return (EFAULT);
		}
		mutex_enter(&socalp->ioctl_mtx);
		retval = socal_issue_lbf(socalp, port, flb_pl,
		    flb_size, 1);
		mutex_exit(&socalp->ioctl_mtx);

		if (retval == FCAL_SUCCESS) {
		if (copyout((caddr_t)flb_pl,
		    (caddr_t)(arg + sizeof (flb_hdr_t) +
		    flb_hdr->max_length), flb_size) == -1) {
			kmem_free((void *)flb_pl, flb_size);
			kmem_free((void *)flb_hdr, sizeof (flb_hdr_t));
			return (EFAULT);
		}
		}

		kmem_free((void *)flb_pl, flb_size);
		kmem_free((void *)flb_hdr, sizeof (flb_hdr_t));
		break;
	default:
		return (ENOTTY);

	}
	switch (retval) {
		case FCAL_SUCCESS:
			return (0);
		case FCAL_ALLOC_FAILED:
			return (ENOMEM);
		case FCAL_STATUS_DIAG_BUSY:
			return (EALREADY);
		case FCAL_STATUS_DIAG_INVALID:
			return (EINVAL);
		default:
			return (EIO);
	}

}

/*
 * Function name : socal_disable()
 *
 * Return Values :  none
 *
 * Description	 : Reset the soc+
 *
 * Context	 : Can be called from different kernel process threads.
 *		   Can be called by interrupt thread.
 *
 * Note:  before calling this, the interface should be locked down
 * so that it is guaranteed that no other threads are accessing
 * the hardware.
 */
static	void
socal_disable(socal_state_t *socalp)
{
#if !defined(lint)
	int i;
#endif
	/* Don't touch the hardware if the registers aren't mapped */
	if (!socalp->socal_rp)
		return;

	socalp->socal_rp->socal_imr = socalp->socal_k_imr = 0;
	socalp->socal_rp->socal_csr.w = SOCAL_CSR_SOFT_RESET;
#if !defined(lint)
	i = socalp->socal_rp->socal_csr.w;
#endif
	DEBUGF(9, (CE_CONT, "csr.w = %x\n", i));
}

/*
 * Function name : socal_init_transport_interface()
 *
 * Return Values :  none
 *
 * Description	 : Fill up the fcal_tranpsort struct for ULPs
 *
 *
 * Note:  Only called during attach, so no protection
 */
static void
socal_init_transport_interface(socal_state_t *socalp)
{
	int			i;
	fcal_transport_t	*xport;

	for (i = 0; i < N_SOCAL_NPORTS; i++) {
		xport = socalp->port_state[i].sp_transport;
		mutex_init(&xport->fcal_mtx, NULL, MUTEX_DRIVER,
		    (void *)(socalp->iblkc));

		cv_init(&xport->fcal_cv, NULL, CV_DRIVER, NULL);

		xport->fcal_handle = (void *)socalp;
		xport->fcal_dmalimp = socallim;
		xport->fcal_iblock = socalp->iblkc;
		xport->fcal_dmaattr = &socal_dma_attr;
		xport->fcal_accattr = &socal_acc_attr;
		xport->fcal_loginparms = socalp->socal_service_params;
		bcopy((caddr_t)&socalp->socal_n_wwn,
		    (caddr_t)&xport->fcal_n_wwn, sizeof (la_wwn_t));
		bcopy((caddr_t)&socalp->port_state[i].sp_p_wwn,
		    (caddr_t)&xport->fcal_p_wwn, sizeof (la_wwn_t));
		xport->fcal_portno = i;
		xport->fcal_cmdmax = SOCAL_MAX_XCHG;
		xport->fcal_ops = &socal_transport_ops;
	}
}

/*
 * static int
 * socal_cqalloc_init() - Inialize the circular queue tables.
 *	Also, init the locks that are associated with the tables.
 *
 *	Returns:	FCAL_SUCCESS, if able to init properly.
 *			FCAL_FAILURE, if unable to init properly.
 */

static int
socal_cqalloc_init(socal_state_t *socalp, uint32_t index)
{
	uint32_t cq_size;
	size_t real_len;
	uint_t ccount;
	socal_kcq_t *cqp;
	int	req_bound = 0, rsp_bound = 0;

	/*
	 * Initialize the Request and Response Queue locks.
	 */

	mutex_init(&socalp->request[index].skc_mtx, NULL, MUTEX_DRIVER,
	    (void *)socalp->iblkc);
	mutex_init(&socalp->response[index].skc_mtx, NULL, MUTEX_DRIVER,
	    (void *)socalp->iblkc);
	cv_init(&socalp->request[index].skc_cv, NULL, CV_DRIVER, NULL);
	cv_init(&socalp->response[index].skc_cv, NULL, CV_DRIVER, NULL);

	/* Allocate DVMA resources for the Request Queue. */
	cq_size = socal_req_entries[index] * sizeof (cqe_t);
	if (cq_size) {
		cqp = &socalp->request[index];

		if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
		    DDI_DMA_DONTWAIT, NULL,
		    &cqp->skc_dhandle) != DDI_SUCCESS) {
			socal_disp_err(socalp, CE_WARN, "driver.4020",
			    "!alloc of dma handle failed");
			goto fail;
		}

		if (ddi_dma_mem_alloc(cqp->skc_dhandle,
		    cq_size + SOCAL_CQ_ALIGN, &socal_acc_attr,
		    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
		    (caddr_t *)&cqp->skc_cq_raw, &real_len,
		    &cqp->skc_acchandle) != DDI_SUCCESS) {
			socal_disp_err(socalp, CE_WARN, "driver.4030",
			    "!alloc of dma space failed");
				goto fail;
		}

		if (real_len < (cq_size + SOCAL_CQ_ALIGN)) {
			socal_disp_err(socalp, CE_WARN, "driver.4035",
			    "!alloc of dma space failed");
			goto fail;
		}
		cqp->skc_cq = (cqe_t *)(((uintptr_t)cqp->skc_cq_raw +
		    (uintptr_t)SOCAL_CQ_ALIGN - 1) &
		    ((uintptr_t)(~(SOCAL_CQ_ALIGN-1))));

		if (ddi_dma_addr_bind_handle(cqp->skc_dhandle,
		    (struct as *)NULL, (caddr_t)cqp->skc_cq, cq_size,
		    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT,
		    NULL, &cqp->skc_dcookie, &ccount) != DDI_DMA_MAPPED) {
			socal_disp_err(socalp, CE_WARN, "driver.4040",
			    "!bind of dma handle failed");
			goto fail;
		}

		req_bound = 1;
		if (ccount != 1) {
			socal_disp_err(socalp, CE_WARN, "driver.4045",
			    "!bind of dma handle failed");
			goto fail;
		}

	} else {
		socalp->request[index].skc_cq_raw = NULL;
		socalp->request[index].skc_cq = (cqe_t *)NULL;
		socalp->request[index].skc_dhandle = 0;
	}

	/* Allocate DVMA resources for the response Queue. */
	cq_size = socal_rsp_entries[index] * sizeof (cqe_t);
	if (cq_size) {
		cqp = &socalp->response[index];

		if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
		    DDI_DMA_DONTWAIT, NULL,
		    &cqp->skc_dhandle) != DDI_SUCCESS) {
			socal_disp_err(socalp, CE_WARN, "driver.4050",
			    "!alloc of dma handle failed");
			goto fail;
		}

		if (ddi_dma_mem_alloc(cqp->skc_dhandle,
		    cq_size + SOCAL_CQ_ALIGN, &socal_acc_attr,
		    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
		    (caddr_t *)&cqp->skc_cq_raw, &real_len,
		    &cqp->skc_acchandle) != DDI_SUCCESS) {
			socal_disp_err(socalp, CE_WARN, "driver.4060",
			    "!alloc of dma space failed");
				goto fail;
		}

		if (real_len < (cq_size + SOCAL_CQ_ALIGN)) {
			socal_disp_err(socalp, CE_WARN, "driver.4065",
			    "!alloc of dma space failed");
			goto fail;
		}

		cqp->skc_cq = (cqe_t *)(((uintptr_t)cqp->skc_cq_raw +
		    (uintptr_t)SOCAL_CQ_ALIGN - 1) &
		    ((uintptr_t)(~(SOCAL_CQ_ALIGN-1))));

		if (ddi_dma_addr_bind_handle(cqp->skc_dhandle,
		    (struct as *)NULL, (caddr_t)cqp->skc_cq, cq_size,
		    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT,
		    NULL, &cqp->skc_dcookie, &ccount) != DDI_DMA_MAPPED) {
			socal_disp_err(socalp, CE_WARN, "driver.4070",
			    "!bind of dma handle failed");
			goto fail;
		}

		rsp_bound = 1;
		if (ccount != 1) {
			socal_disp_err(socalp, CE_WARN, "driver.4075",
			    "!bind of dma handle failed");
			goto fail;
		}

	} else {
		socalp->response[index].skc_cq_raw = NULL;
		socalp->response[index].skc_cq = (cqe_t *)NULL;
		socalp->response[index].skc_dhandle = 0;
	}

	/*
	 * Initialize the queue pointers
	 */
	socal_cqinit(socalp, index);

	return (FCAL_SUCCESS);
fail:
	if (socalp->request[index].skc_dhandle) {
		if (req_bound)
			(void) ddi_dma_unbind_handle(socalp->
			    request[index].skc_dhandle);
		ddi_dma_free_handle(&socalp->request[index].skc_dhandle);
	}
	if (socalp->request[index].skc_cq_raw)
		ddi_dma_mem_free(&socalp->request[index].skc_acchandle);

	if (socalp->response[index].skc_dhandle) {
		if (rsp_bound)
			(void) ddi_dma_unbind_handle(socalp->
			    response[index].skc_dhandle);
		ddi_dma_free_handle(&socalp->response[index].skc_dhandle);
	}
	if (socalp->response[index].skc_cq_raw)
		ddi_dma_mem_free(&socalp->response[index].skc_acchandle);

	socalp->request[index].skc_dhandle = NULL;
	socalp->response[index].skc_dhandle = NULL;
	socalp->request[index].skc_cq_raw = NULL;
	socalp->request[index].skc_cq = NULL;
	socalp->response[index].skc_cq_raw = NULL;
	socalp->response[index].skc_cq = NULL;
	mutex_destroy(&socalp->request[index].skc_mtx);
	mutex_destroy(&socalp->response[index].skc_mtx);
	cv_destroy(&socalp->request[index].skc_cv);
	cv_destroy(&socalp->response[index].skc_cv);
	return (FCAL_FAILURE);

}

/*
 * socal_cqinit() - initializes the driver's circular queue pointers, etc.
 */

static void
socal_cqinit(socal_state_t *socalp, uint32_t index)
{
	socal_kcq_t *kcq_req = &socalp->request[index];
	socal_kcq_t *kcq_rsp = &socalp->response[index];

	/*
	 * Initialize the Request and Response Queue pointers
	 */
	kcq_req->skc_seqno = 1;
	kcq_rsp->skc_seqno = 1;
	kcq_req->skc_in = 0;
	kcq_rsp->skc_in = 0;
	kcq_req->skc_out = 0;
	kcq_rsp->skc_out = 0;
	kcq_req->skc_last_index = socal_req_entries[index] - 1;
	kcq_rsp->skc_last_index = socal_rsp_entries[index] - 1;
	kcq_req->skc_full = 0;
	kcq_rsp->deferred_intr_timeoutid = 0;
	kcq_req->skc_socalp = socalp;
	kcq_rsp->skc_socalp = socalp;

	kcq_req->skc_xram_cqdesc =
	    (socalp->xram_reqp + (index * sizeof (struct cq))/8);
	kcq_rsp->skc_xram_cqdesc =
	    (socalp->xram_rspp + (index * sizeof (struct cq))/8);

	/*  Clear out memory we have allocated */
	if (kcq_req->skc_cq != NULL)
		bzero((caddr_t)kcq_req->skc_cq,
		    socal_req_entries[index] * sizeof (cqe_t));
	if (kcq_rsp->skc_cq != NULL)
		bzero((caddr_t)kcq_rsp->skc_cq,
		    socal_rsp_entries[index] * sizeof (cqe_t));
}


static int
socal_start(socal_state_t *socalp)
{
	uint_t r;

	if (!socalp)
		return (FCAL_FAILURE);

	socal_download_ucode(socalp);
	socal_init_cq_desc(socalp);
	socal_init_wwn(socalp);

	mutex_enter(&socalp->port_state[0].sp_mtx);
	socalp->port_state[0].sp_status
	    &= (PORT_OPEN|PORT_CHILD_INIT|PORT_DISABLED|PORT_TARGET_MODE);
	socalp->port_state[0].sp_status |= PORT_OFFLINE;
	mutex_exit(&socalp->port_state[0].sp_mtx);

	mutex_enter(&socalp->port_state[1].sp_mtx);
	socalp->port_state[1].sp_status
	    &= (PORT_OPEN|PORT_CHILD_INIT|PORT_DISABLED|PORT_TARGET_MODE);
	socalp->port_state[1].sp_status |= PORT_OFFLINE;
	mutex_exit(&socalp->port_state[1].sp_mtx);

	socal_enable(socalp);
	/* Make sure disabled ports stay disabled. */
	if (socalp->port_state[0].sp_status & PORT_DISABLED)
		(void) socal_diag_request((void *)socalp, 0, &r,
		    SOC_DIAG_INT_LOOP);
	if (socalp->port_state[1].sp_status & PORT_DISABLED)
		(void) socal_diag_request((void *)socalp, 1, &r,
		    SOC_DIAG_INT_LOOP);

	mutex_enter(&socalp->k_imr_mtx);
	socalp->socal_shutdown = 0;
	mutex_exit(&socalp->k_imr_mtx);

	mutex_enter(&socalp->board_mtx);
	if (socal_establish_pool(socalp, 1) != FCAL_SUCCESS) {
		mutex_exit(&socalp->board_mtx);
		return (FCAL_FAILURE);
	}
	if (socal_add_pool_buffer(socalp, 1) != FCAL_SUCCESS) {
		mutex_exit(&socalp->board_mtx);
		return (FCAL_FAILURE);
	}

	mutex_exit(&socalp->board_mtx);
	return (FCAL_SUCCESS);
}

static void
socal_doreset(socal_state_t *socalp)
{
	int		i;
	socal_port_t	*port_statep;
	socal_unsol_cb_t *scbp;

	for (i = 0; i < SOCAL_N_CQS; i++) {
		mutex_enter(&socalp->request[i].skc_mtx);
		mutex_enter(&socalp->response[i].skc_mtx);
	}

	mutex_enter(&socalp->k_imr_mtx);
	socal_disable(socalp);

	if (socalp->pool_dhandle) {
		(void) ddi_dma_unbind_handle(socalp->pool_dhandle);
		ddi_dma_free_handle(&socalp->pool_dhandle);
	}

	if (socalp->pool)
		ddi_dma_mem_free(&socalp->pool_acchandle);

	socalp->pool_dhandle = NULL;
	socalp->pool = NULL;

	for (i = 0; i < SOCAL_N_CQS; i++)
		socal_cqinit(socalp, i);

	for (i = 0; i < N_SOCAL_NPORTS; i++) {
		port_statep = &socalp->port_state[i];

		mutex_enter(&port_statep->sp_mtx);
		port_statep->sp_status &= ~ (PORT_STATUS_MASK |
		    PORT_LILP_PENDING | PORT_LIP_PENDING |
		    PORT_ABORT_PENDING | PORT_BYPASS_PENDING |
		    PORT_ELS_PENDING);
		mutex_exit(&port_statep->sp_mtx);
	}

	mutex_exit(&socalp->k_imr_mtx);

	for (i = SOCAL_N_CQS-1; i >= 0; i--) {
		mutex_exit(&socalp->request[i].skc_mtx);
		mutex_exit(&socalp->response[i].skc_mtx);
	}

	for (i = 0; i < N_SOCAL_NPORTS; i++) {
		for (scbp = socalp->port_state[i].sp_unsol_cb; scbp;
		    scbp = scbp->next)
			(scbp->statec_cb)(scbp->arg, FCAL_STATE_RESET);
	}

	for (i = 0; i < SOCAL_N_CQS; i++) {
		mutex_enter(&socalp->request[i].skc_mtx);
		mutex_enter(&socalp->response[i].skc_mtx);
	}


	for (i = 0; i < SOCAL_N_CQS; i++) {
		socalp->request[i].skc_overflowh = NULL;
		if (socalp->request[i].skc_full & SOCAL_SKC_SLEEP)
			cv_broadcast(&socalp->request[i].skc_cv);
	}

	for (i = SOCAL_N_CQS-1; i >= 0; i--) {
		mutex_exit(&socalp->request[i].skc_mtx);
		mutex_exit(&socalp->response[i].skc_mtx);
	}

}


/*
 * Function name : socal_download_ucode ()
 *
 * Return Values :
 *
 * Description	 : Copies firmware from code that has been linked into
 *		   the socal module into the soc+'s XRAM.  Prints the date
 *		   string
 *
 */
static void
socal_download_ucode(socal_state_t *socalp)
{
	uint_t	fw_len = 0;
	uint_t	date_str[16];
	auto	char buf[256];

	fw_len = (uint_t)socal_ucode_size;

	/* Copy the firmware image */
	socal_wcopy((uint_t *)&socal_ucode,
	    (uint_t *)socalp->socal_xrp, fw_len);

	socal_fix_harda(socalp, 0);
	socal_fix_harda(socalp, 1);

	/* Get the date string from the firmware image */
	socal_wcopy((uint_t *)(socalp->socal_xrp+SOCAL_XRAM_FW_DATE_STR),
	    date_str, sizeof (date_str));
	date_str[sizeof (date_str) / sizeof (uint_t) - 1] = 0;

	if (*(caddr_t)date_str != '\0') {
		(void) sprintf(buf,
		    "!Downloading host adapter, fw date code: %s\n",
		    (caddr_t)date_str);
		socal_disp_err(socalp, CE_CONT, "driver.1010", buf);
		(void) strcpy(socalp->socal_stats.fw_revision,
		    (char *)date_str);
	} else {
		(void) sprintf(buf,
		    "!Downloading host adapter fw, "
		    "date code: <not available>\n");
		socal_disp_err(socalp, CE_CONT, "driver.3010", buf);
		(void) strcpy(socalp->socal_stats.fw_revision,
		    "<Not Available>");
	}
}

/*
 * Function name : socal_disp_err()
 *
 * Return Values : none
 *
 * Description   : displays an error message on the system console
 *		   with the full device pathname displayed
 */
static void
socal_disp_err(
	socal_state_t	*socalp,
	uint_t		level,
	char		*mid,
	char		*msg)
{
	char c;
	int instance;

	instance = ddi_get_instance(socalp->dip);

	c = *msg;

	if (c == '!')		/* log only */
		cmn_err(level,
		"!ID[SUNWssa.socal.%s] socal%d: %s", mid, instance, msg+1);
	else if (c == '?')	/* boot message - log && maybe console */
		cmn_err(level,
		"?ID[SUNWssa.socal.%s] socal%d: %s", mid, instance, msg+1);
	else if (c == '^')	/* console only */
		cmn_err(level, "^socal%d: %s", instance, msg+1);
	else	{		/* log and console */
		cmn_err(level, "^socal%d: %s", instance, msg);
		cmn_err(level, "!ID[SUNWssa.socal.%s] socal%d: %s", mid,
		    instance, msg);
	}
}

/*
 * Function name : socal_init_cq_desc()
 *
 * Return Values : none
 *
 * Description	 : Initializes the request and response queue
 *		   descriptors in the SOC+'s XRAM
 *
 * Context	 : Should only be called during initialiation when
 *		   the SOC+ is reset.
 */
static void
socal_init_cq_desc(socal_state_t *socalp)
{
	soc_cq_t	que_desc[SOCAL_N_CQS];
	uint32_t	i;

	/*
	 * Finish CQ table initialization and give the descriptor
	 * table to the soc+.  Note that we don't use all of the queues
	 * provided by the hardware, but we make sure we initialize the
	 * quantities in the unused fields in the hardware to zeroes.
	 */

	/*
	 * Do request queues
	 */
	for (i = 0; i < SOCAL_N_CQS; i++) {
		if (socal_req_entries[i]) {
			que_desc[i].cq_address =
			    (uint32_t)socalp->request[i].
			    skc_dcookie.dmac_address;
			que_desc[i].cq_last_index = socal_req_entries[i] - 1;
		} else {
			que_desc[i].cq_address = (uint32_t)0;
			que_desc[i].cq_last_index = 0;
		}
		que_desc[i].cq_in = 0;
		que_desc[i].cq_out = 0;
		que_desc[i].cq_seqno = 1; /* required by SOC+ microcode */
	}

	/* copy to XRAM */
	socal_wcopy((uint_t *)que_desc,		/* pointer to kernel copy */
	    (uint_t *)socalp->xram_reqp,	/* pointer to xram location */
	    SOCAL_N_CQS * sizeof (soc_cq_t));

	/*
	 * Do response queues
	 */
	for (i = 0; i < SOCAL_N_CQS; i++) {
		if (socal_rsp_entries[i]) {
			que_desc[i].cq_last_index = socal_rsp_entries[i] - 1;
			que_desc[i].cq_address =
			    (uint32_t)socalp->response[i].
			    skc_dcookie.dmac_address;

		} else {
			que_desc[i].cq_address = 0;
			que_desc[i].cq_last_index = 0;
		}
	}

	/* copy to XRAM */
	socal_wcopy((uint_t *)que_desc,		/* pointer to kernel copy */
	    (uint_t *)socalp->xram_rspp,	/* pointer to xram location */
	    SOCAL_N_CQS * sizeof (soc_cq_t));
}

static void
socal_init_wwn(socal_state_t *socalp)
{
	/* copy the node wwn to xram */
	socal_wcopy((uint_t *)&socalp->socal_n_wwn,
	    (uint_t *)(socalp->socal_xrp +
	    SOCAL_XRAM_NODE_WWN), sizeof (la_wwn_t));

	/* copy port a's wwn to xram */
	socal_wcopy((uint_t *)&socalp->port_state[0].sp_p_wwn,
	    (uint_t *)(socalp->socal_xrp + SOCAL_XRAM_PORTA_WWN),
	    sizeof (la_wwn_t));

	/* copy port b's wwn to xram */
	socal_wcopy((uint_t *)&socalp->port_state[1].sp_p_wwn,
	    (uint_t *)(socalp->socal_xrp + SOCAL_XRAM_PORTB_WWN),
	    sizeof (la_wwn_t));

	/*
	 * need to avoid deadlock by assuring no other thread grabs both of
	 * these at once
	 */
	mutex_enter(&socalp->port_state[0].sp_transport->fcal_mtx);
	mutex_enter(&socalp->port_state[1].sp_transport->fcal_mtx);

	socal_wcopy((uint_t *)(socalp->socal_xrp + SOCAL_XRAM_SERV_PARAMS),
	    (uint_t *)&socalp->socal_service_params, SOCAL_SVC_LENGTH);
	mutex_exit(&socalp->port_state[1].sp_transport->fcal_mtx);
	mutex_exit(&socalp->port_state[0].sp_transport->fcal_mtx);
}

static void
socal_enable(socal_state_t *socalp)
{
	DEBUGF(2, (CE_CONT, "socal%d: enable:\n",
	    ddi_get_instance(socalp->dip)));

	socalp->socal_rp->socal_cr.w = socalp->socal_cfg;
	socalp->socal_rp->socal_csr.w = SOCAL_CSR_SOCAL_TO_HOST;

	socalp->socal_k_imr = (uint32_t)SOCAL_CSR_SOCAL_TO_HOST |
	    SOCAL_CSR_SLV_ACC_ERR;
	socalp->socal_rp->socal_imr = (uint32_t)socalp->socal_k_imr;
}

/*
 * static int
 * socal_establish_pool() - this routine tells the SOC+ of a buffer pool
 *	to place LINK ctl application data as it arrives.
 *
 *	Returns:
 *		FCAL_SUCCESS, upon establishing the pool.
 *		FCAL_FAILURE, if unable to establish the pool.
 */

static int
socal_establish_pool(socal_state_t *socalp, uint32_t poolid)
{
	soc_pool_request_t	*prq;
	int			result;

	if ((prq =
	    (soc_pool_request_t *)kmem_zalloc(sizeof (soc_pool_request_t),
	    KM_NOSLEEP)) == NULL)
			return (FCAL_FAILURE);
	/*
	 * Fill in the request structure.
	 */
	prq->spr_soc_hdr.sh_request_token = 1;
	prq->spr_soc_hdr.sh_flags = SOC_FC_HEADER | SOC_UNSOLICITED |
	    SOC_NO_RESPONSE;
	prq->spr_soc_hdr.sh_class = 0;
	prq->spr_soc_hdr.sh_seg_cnt = 1;
	prq->spr_soc_hdr.sh_byte_cnt = 0;

	prq->spr_pool_id = poolid;
	prq->spr_header_mask = SOCPR_MASK_RCTL;
	prq->spr_buf_size = SOCAL_POOL_SIZE;
	prq->spr_n_entries = 0;

	prq->spr_fc_frame_hdr.r_ctl = R_CTL_ELS_REQ;
	prq->spr_fc_frame_hdr.d_id = 0;
	prq->spr_fc_frame_hdr.s_id = 0;
	prq->spr_fc_frame_hdr.type = 0;
	prq->spr_fc_frame_hdr.f_ctl = 0;
	prq->spr_fc_frame_hdr.seq_id = 0;
	prq->spr_fc_frame_hdr.df_ctl = 0;
	prq->spr_fc_frame_hdr.seq_cnt = 0;
	prq->spr_fc_frame_hdr.ox_id = 0;
	prq->spr_fc_frame_hdr.rx_id = 0;
	prq->spr_fc_frame_hdr.ro = 0;

	prq->spr_cqhdr.cq_hdr_count = 1;
	prq->spr_cqhdr.cq_hdr_type = CQ_TYPE_ADD_POOL;
	prq->spr_cqhdr.cq_hdr_flags = 0;
	prq->spr_cqhdr.cq_hdr_seqno = 0;

	/* Enque the request. */
	result = socal_cq_enque(socalp, NULL, (cqe_t *)prq, CQ_REQUEST_1,
	    FCAL_NOSLEEP, NULL, 0);
	kmem_free((void *)prq, sizeof (soc_pool_request_t));
	return (result);

}


/*
 * static int
 * soc_add_pool_buffer() - this routine tells the SOC+ to add one buffer
 *	to an established pool of buffers
 *
 *	Returns:
 *		DDI_SUCCESS, upon establishing the pool.
 *		DDI_FAILURE, if unable to establish the pool.
 */

static int
socal_add_pool_buffer(socal_state_t *socalp, uint32_t poolid)
{
	soc_data_request_t	*drq;
	int			result;
	size_t			real_len;
	int			bound = 0;
	uint_t			ccount;

	if ((drq =
	    (soc_data_request_t *)kmem_zalloc(sizeof (soc_data_request_t),
	    KM_NOSLEEP)) == NULL)
			return (FCAL_FAILURE);

	/* Allocate DVMA resources for the buffer pool */
	if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
	    DDI_DMA_DONTWAIT, NULL, &socalp->pool_dhandle) != DDI_SUCCESS)
		goto fail;

	if (ddi_dma_mem_alloc(socalp->pool_dhandle, SOCAL_POOL_SIZE,
	    &socal_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
	    (caddr_t *)&socalp->pool, &real_len, &socalp->pool_acchandle)
	    != DDI_SUCCESS)
		goto fail;

	if (real_len < SOCAL_POOL_SIZE)
		goto fail;

	if (ddi_dma_addr_bind_handle(socalp->pool_dhandle, (struct as *)NULL,
	    (caddr_t)socalp->pool, SOCAL_POOL_SIZE,
	    DDI_DMA_READ | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT,
	    NULL, &socalp->pool_dcookie, &ccount) != DDI_DMA_MAPPED)
		goto fail;

	bound = 1;
	if (ccount != 1)
		goto fail;

	/*
	 * Fill in the request structure.
	 */
	drq->sdr_soc_hdr.sh_request_token = poolid;
	drq->sdr_soc_hdr.sh_flags = SOC_UNSOLICITED | SOC_NO_RESPONSE;
	drq->sdr_soc_hdr.sh_class = 0;
	drq->sdr_soc_hdr.sh_seg_cnt = 1;
	drq->sdr_soc_hdr.sh_byte_cnt = 0;

	drq->sdr_dataseg[0].fc_base =
	    (uint32_t)socalp->pool_dcookie.dmac_address;
	drq->sdr_dataseg[0].fc_count = SOCAL_POOL_SIZE;
	drq->sdr_dataseg[1].fc_base = 0;
	drq->sdr_dataseg[1].fc_count = 0;
	drq->sdr_dataseg[2].fc_base = 0;
	drq->sdr_dataseg[2].fc_count = 0;
	drq->sdr_dataseg[3].fc_base = 0;
	drq->sdr_dataseg[3].fc_count = 0;
	drq->sdr_dataseg[4].fc_base = 0;
	drq->sdr_dataseg[4].fc_count = 0;
	drq->sdr_dataseg[5].fc_base = 0;
	drq->sdr_dataseg[5].fc_count = 0;

	drq->sdr_cqhdr.cq_hdr_count = 1;
	drq->sdr_cqhdr.cq_hdr_type = CQ_TYPE_ADD_BUFFER;
	drq->sdr_cqhdr.cq_hdr_flags = 0;
	drq->sdr_cqhdr.cq_hdr_seqno = 0;

	/* Transport the request. */
	result = socal_cq_enque(socalp, NULL, (cqe_t *)drq, CQ_REQUEST_1,
	    FCAL_NOSLEEP, NULL, 0);
	kmem_free((void *)drq, sizeof (soc_data_request_t));
	return (result);

fail:
	socal_disp_err(socalp, CE_WARN, "driver.4110",
	    "!Buffer pool DVMA alloc failed");
	if (socalp->pool_dhandle) {
		if (bound)
			(void) ddi_dma_unbind_handle(socalp->pool_dhandle);
		ddi_dma_free_handle(&socalp->pool_dhandle);
	}
	if (socalp->pool)
		ddi_dma_mem_free(&socalp->pool_acchandle);
	socalp->pool_dhandle = NULL;
	return (FCAL_FAILURE);
}

static uint_t
socal_transport(fcal_packet_t *fcalpkt, fcal_sleep_t sleep, int req_q_no)
{
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
	socal_port_t	*port_statep;
#if defined(DEBUG) && !defined(lint)
	int		instance = ddi_get_instance(socalp->dip);
#endif
	int		port;
	soc_request_t	*sp = (soc_request_t *)&fcalpkt->fcal_socal_request;

	if (sp->sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	port_statep = &socalp->port_state[port];

	DEBUGF(4, (CE_CONT, "socal%d: transport: packet, sleep = %p, %d\n",
	    instance, fcalpkt, sleep));

	fcalpkt->fcal_cmd_state = 0;
	fcalpkt->fcal_pkt_flags &= ~(FCFLAG_COMPLETE | FCFLAG_ABORTING);

	return (socal_cq_enque(socalp, port_statep, (cqe_t *)sp,
	    req_q_no, sleep, fcalpkt, 0));
}

/*
 * Function name : socal_cq_enque()
 *
 * Return Values :
 *		FCAL_TRANSPORT_SUCCESS, if able to que the entry.
 *		FCAL_TRANSPORT_QFULL, if queue full & sleep not set
 *		FCAL_TRANSPORT_UNAVAIL if this port down
 *
 * Description	 : Enqueues an entry into the solicited request
 *		   queue
 *
 * Context	:
 */

/*ARGSUSED*/
static int
socal_cq_enque(socal_state_t *socalp, socal_port_t *port_statep, cqe_t *cqe,
		int rqix, fcal_sleep_t sleep, fcal_packet_t *to_queue,
		int mtxheld)
{
#if defined(DEBUG) && !defined(lint)
	int 		instance = ddi_get_instance(socalp->dip);
#endif
	socal_kcq_t	*kcq;
	cqe_t		*sp;
	uint_t		bitmask, wmask;
	uchar_t		out;
	uchar_t		s_out;
	longlong_t	*p, *q;

	kcq = &socalp->request[rqix];

	bitmask = SOCAL_CSR_1ST_H_TO_S << rqix;
	wmask = SOCAL_CSR_SOCAL_TO_HOST | bitmask;
	p = (longlong_t *)cqe;

	/*
	 * Since we're only reading we don't need a mutex.
	 */
	if (socalp->socal_shutdown) {
		return (FCAL_TRANSPORT_UNAVAIL);
	}
	/*
	 * Get a token early.  That way we won't sleep
	 * in id32_alloc() with a mutex held.
	 */
	if (to_queue) {
		if ((to_queue->fcal_socal_request.sr_soc_hdr.sh_request_token =
		    SOCAL_ID_GET(to_queue, mtxheld ? FCAL_NOSLEEP :
		    sleep)) == NULL) {
			return (FCAL_TRANSPORT_QFULL);
		}
	}
	/*
	 * Grab lock for request queue.
	 */

	if (!mtxheld)
		mutex_enter(&kcq->skc_mtx);

	/*
	 * Determine if the queue is full
	 */

	do {

		if (kcq->skc_full) {
		/*
		 * If soc's queue full, then we wait for an interrupt
		 * telling us we are not full.
		 */

			if (to_queue) {
			to_queue->fcal_pkt_next = NULL;
			if (!kcq->skc_overflowh) {
				DEBUGF(2, (CE_CONT,
				    "socal%d: cq_enque: request "
				    "que %d is full\n",
				    instance, rqix));
				kcq->skc_overflowh = to_queue;
				socalp->socal_stats.qfulls++;
			} else
				kcq->skc_overflowt->fcal_pkt_next = to_queue;
			kcq->skc_overflowt = to_queue;

			mutex_enter(&socalp->k_imr_mtx);
			socalp->socal_rp->socal_imr =
			    (socalp->socal_k_imr |= bitmask);
			mutex_exit(&socalp->k_imr_mtx);
			to_queue->fcal_cmd_state |= FCAL_CMD_IN_TRANSPORT;
			if (!mtxheld)
				mutex_exit(&kcq->skc_mtx);
			return (FCAL_TRANSPORT_SUCCESS);
			}

			if (!mtxheld)
			mutex_exit(&kcq->skc_mtx);
			return (FCAL_TRANSPORT_QFULL);
		}

		if (((kcq->skc_in + 1) & kcq->skc_last_index)
		    == (out = kcq->skc_out)) {
		/*
		 * get SOC+'s copy of out to update our copy of out
		 */
		s_out =
		    SOCAL_REQUESTQ_INDEX(rqix, socalp->socal_rp->socal_reqp.w);
		DEBUGF(2, (CE_CONT,
		    "socal%d: cq_enque: &XRAM cq_in: 0x%p s_out.out 0x%x\n",
		    instance, &kcq->skc_xram_cqdesc->cq_in, s_out));

		kcq->skc_out = out = s_out;
		/* if soc+'s que still full set flag */
		kcq->skc_full = ((((kcq->skc_in + 1) &
		    kcq->skc_last_index) == out)) ? SOCAL_SKC_FULL : 0;
		}

	} while (kcq->skc_full);

	/* Now enque the entry. */
	sp = &(kcq->skc_cq[kcq->skc_in]);
	cqe->cqe_hdr.cq_hdr_seqno = kcq->skc_seqno;

	/* Give the entry to the SOC. */
	q = (longlong_t *)sp;
	*q++ = *p++;
	*q++ = *p++;
	*q++ = *p++;
	*q++ = *p++;
	*q++ = *p++;
	*q++ = *p++;
	*q++ = *p++;
	*q = *p;
	(void) ddi_dma_sync(kcq->skc_dhandle, (int)((caddr_t)sp -
	    (caddr_t)kcq->skc_cq), sizeof (cqe_t), DDI_DMA_SYNC_FORDEV);
	if (to_queue)
		to_queue->fcal_cmd_state |= FCAL_CMD_IN_TRANSPORT;

	/*
	 * Update circular queue and ring SOC's doorbell.
	 */
	kcq->skc_in++;
	if ((kcq->skc_in & kcq->skc_last_index) == 0) {
		kcq->skc_in = 0;
		kcq->skc_seqno++;
	}

	socalp->socal_rp->socal_csr.w = wmask | (kcq->skc_in << 24);
	/* Let lock go for request queue. */
	if (!mtxheld)
		mutex_exit(&kcq->skc_mtx);

	return (FCAL_TRANSPORT_SUCCESS);
}

static uint_t
socal_transport_poll(fcal_packet_t *fcalpkt, uint_t timeout, int req_q_no)
{
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
	register volatile socal_reg_t *socalreg = socalp->socal_rp;
	uint_t			csr;
	socal_port_t	*port_statep;
	int		port;
	soc_request_t	*sp = (soc_request_t *)&fcalpkt->fcal_socal_request;
	uint32_t	retval;
	clock_t		ticker, t;

	/* make the timeout meaningful */
	timeout = drv_usectohz(timeout);
	if (sp->sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	port_statep = &socalp->port_state[port];

	fcalpkt->fcal_cmd_state = 0;
	fcalpkt->fcal_pkt_flags &= ~(FCFLAG_COMPLETE | FCFLAG_ABORTING);

	ticker = ddi_get_lbolt();

	if ((retval = socal_cq_enque(socalp, port_statep, (cqe_t *)sp,
	    req_q_no, FCAL_NOSLEEP, fcalpkt, 0)) != FCAL_TRANSPORT_SUCCESS) {
		return (retval);
	} else {
		while (!(fcalpkt->fcal_cmd_state & FCAL_CMD_COMPLETE)) {
			drv_usecwait(SOCAL_NOINTR_POLL_DELAY_TIME);
			t = ddi_get_lbolt();
			if ((ticker + timeout) < t)
				return (FCAL_TRANSPORT_TIMEOUT);
			csr = socalreg->socal_csr.w;
			if ((SOCAL_INTR_CAUSE(socalp, csr)) &
			    SOCAL_CSR_RSP_QUE_0) {
				socal_intr_solicited(socalp, 0);
			}
		}
	}
	return (FCAL_TRANSPORT_SUCCESS);
}

static uint_t
socal_doit(fcal_packet_t *fcalpkt, socal_port_t *port_statep, int polled,
    void (*func)(), int timo, int flag, uint_t *diagcode)
{
	clock_t lb;
	uint32_t retval, status;
	socal_state_t   *socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;

	if (polled) {
		fcalpkt->fcal_pkt_comp = NULL;
		status = socal_transport_poll(fcalpkt, timo, CQ_REQUEST_0);
	} else {
		fcalpkt->fcal_pkt_comp = func;
		mutex_enter(&port_statep->sp_mtx);
		port_statep->sp_status |= flag;
		if ((status = socal_transport(fcalpkt, FCAL_NOSLEEP,
		    CQ_REQUEST_0)) == FCAL_TRANSPORT_SUCCESS) {
			lb = ddi_get_lbolt();
			while (!(fcalpkt->fcal_cmd_state & FCAL_CMD_COMPLETE)) {
			if ((retval = cv_timedwait(&port_statep->sp_cv,
			    &port_statep->sp_mtx,
			    lb+drv_usectohz(timo))) == -1) {
				status = FCAL_TRANSPORT_TIMEOUT;
				break;
			}
			}
		}
		port_statep->sp_status &= ~flag;
		mutex_exit(&port_statep->sp_mtx);
	}

	switch (status) {
		case FCAL_TRANSPORT_SUCCESS:
			status = fcalpkt->fcal_pkt_status;
			if (diagcode)
				*diagcode = fcalpkt->fcal_diag_status;
			switch (status) {
				case FCAL_STATUS_ABORT_FAILED:
					if (flag == PORT_ABORT_PENDING)
						retval = FCAL_ABORT_FAILED;
					break;
				case FCAL_STATUS_OK:
					if (flag == PORT_ABORT_PENDING)
						retval = FCAL_ABORT_FAILED;
					else
						retval = FCAL_SUCCESS;
					break;
				case FCAL_STATUS_OLD_PORT:
					retval = FCAL_OLD_PORT;
					break;
				case FCAL_STATUS_ERR_OFFLINE:
					retval = FCAL_OFFLINE;
					break;
				case FCAL_STATUS_ABORTED:
					retval = FCAL_ABORTED;
					port_statep->sp_board->
					    socal_stats.pstats[port_statep
					    ->sp_port].abts_ok++;
					break;
				case FCAL_STATUS_BAD_XID:
					retval = FCAL_BAD_ABORT;
					break;
				case FCAL_STATUS_BAD_DID:
					retval = FCAL_BAD_PARAMS;
					break;
				case FCAL_STATUS_DIAG_BUSY:
				case FCAL_STATUS_DIAG_INVALID:
					retval = status;
					break;
				default:
					retval = FCAL_LINK_ERROR;
			}
			break;
		case FCAL_TRANSPORT_TIMEOUT:
			if (flag == PORT_LIP_PENDING ||
			    flag == PORT_LILP_PENDING) {
				if (socal_core &&
				    (socal_core & SOCAL_FAILED_LIP)) {
					socal_core = 0;
					socal_take_core(socalp);
				}
				socal_disp_err(socalp, CE_WARN, "link.6040",
				"SOCAL:Forcing SOC+ reset as LIP timed out\n");
				/* restart socal after resetting */
				(void) socal_force_reset(port_statep->sp_board,
				    polled, RESET_PORT);
			}
			else
				(void) socal_force_lip(port_statep->sp_board,
				    port_statep->sp_port, polled,
				    FCAL_FORCE_LIP);
			retval = FCAL_TIMEOUT;
			break;
		case FCAL_TRANSPORT_FAILURE:
		case FCAL_BAD_PACKET:
		case FCAL_TRANSPORT_UNAVAIL:
		case FCAL_TRANSPORT_QFULL:
			retval = status;
			break;
		default:
			retval = FCAL_LINK_ERROR;
	}
	socal_packet_free(fcalpkt);
	return (retval);
}

static uint_t
socal_lilp_map(void *ssp, uint_t port, uint32_t bufid, uint_t polled)
{
	fcal_packet_t		*fcalpkt;
	soc_data_request_t	*sdr;
	socal_state_t		*socalp = (socal_state_t *)ssp;
	socal_port_t		*port_statep = &socalp->port_state[port];

	if ((fcalpkt =
	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
	    == (fcal_packet_t *)NULL)
		return (FCAL_ALLOC_FAILED);

	sdr = (soc_data_request_t *)&fcalpkt->fcal_socal_request;
	if (port)
		sdr->sdr_soc_hdr.sh_flags = SOC_PORT_B;
	sdr->sdr_soc_hdr.sh_seg_cnt = 1;
	sdr->sdr_soc_hdr.sh_byte_cnt = 132;
	sdr->sdr_dataseg[0].fc_base = bufid;
	sdr->sdr_dataseg[0].fc_count = 132;
	sdr->sdr_cqhdr.cq_hdr_count = 1;
	sdr->sdr_cqhdr.cq_hdr_type = CQ_TYPE_REPORT_MAP;
	fcalpkt->fcal_pkt_cookie = (void *)socalp;

	return (socal_doit(fcalpkt, port_statep, polled, socal_lilp_map_done,
	    SOCAL_LILP_TIMEOUT, PORT_LILP_PENDING, NULL));
}

static uint_t
socal_force_lip(void *ssp, uint_t port, uint_t polled, uint_t lip_req)
{
	fcal_packet_t		*fcalpkt;
	soc_cmdonly_request_t	*scr;
	socal_state_t		*socalp = (socal_state_t *)ssp;
	socal_port_t		*port_statep = &socalp->port_state[port];


	if (lip_req == FCAL_NO_LIP) {
		mutex_enter(&port_statep->sp_mtx);
		if ((port_statep->sp_status & PORT_ONLINE_LOOP) &&
		    (port_statep->sp_unsol_cb->statec_cb != NULL)) {
				mutex_exit(&port_statep->sp_mtx);
				(*port_statep->sp_unsol_cb->statec_cb)
				    (port_statep->sp_unsol_cb->arg,
				    FCAL_STATUS_LOOP_ONLINE);
			return (FCAL_SUCCESS);

		} else
			mutex_exit(&port_statep->sp_mtx);
	}
	socalp->socal_stats.pstats[port].lips++;
	if ((fcalpkt =
	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
	    == (fcal_packet_t *)NULL)
		return (FCAL_ALLOC_FAILED);

	scr = (soc_cmdonly_request_t *)&fcalpkt->fcal_socal_request;
	if (port)
		scr->scr_soc_hdr.sh_flags = SOC_PORT_B;
	scr->scr_cqhdr.cq_hdr_count = 1;
	scr->scr_cqhdr.cq_hdr_type = CQ_TYPE_REQUEST_LIP;

	fcalpkt->fcal_pkt_cookie = (void *)socalp;
	return (socal_doit(fcalpkt, port_statep, polled, socal_force_lip_done,
	    SOCAL_LIP_TIMEOUT, PORT_LIP_PENDING, NULL));
}

static uint_t
socal_abort_cmd(void *ssp, uint_t port, fcal_packet_t *fcalpkt, uint_t polled)
{
	fcal_packet_t		*fcalpkt2, *fpkt;
	soc_cmdonly_request_t	*scr, *tscr;
	socal_state_t		*socalp = (socal_state_t *)ssp;
	socal_port_t		*port_statep = &socalp->port_state[port];
	socal_kcq_t		*kcq;

	socalp->socal_stats.pstats[port].abts++;
	kcq = &socalp->request[CQ_REQUEST_1];
	mutex_enter(&kcq->skc_mtx);
	fcalpkt2 = kcq->skc_overflowh;
	fpkt = NULL;
	while (fcalpkt2 != NULL) {
		if (fcalpkt2 == fcalpkt) {
			if (fpkt == NULL)
				kcq->skc_overflowh = fcalpkt->fcal_pkt_next;
			else {
				fpkt->fcal_pkt_next = fcalpkt->fcal_pkt_next;
				if (kcq->skc_overflowt == fcalpkt)
					kcq->skc_overflowt = fpkt;
			}
			mutex_exit(&kcq->skc_mtx);
			socalp->socal_stats.pstats[port].abts_ok++;
			SOCAL_ID_FREE(fcalpkt->fcal_socal_request.
			    sr_soc_hdr.sh_request_token);
			return (FCAL_ABORTED);
		} else {
			fpkt = fcalpkt2;
			fcalpkt2 = fcalpkt2->fcal_pkt_next;
		}
	}
	mutex_exit(&kcq->skc_mtx);
	if ((fcalpkt2 =
	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
	    == (fcal_packet_t *)NULL)
		return (FCAL_ALLOC_FAILED);

	mutex_enter(&socalp->abort_mtx);
	/* Too late? */
	if (fcalpkt->fcal_pkt_flags & FCFLAG_COMPLETE) {
		socal_packet_free(fcalpkt2);
		mutex_exit(&socalp->abort_mtx);
		return (FCAL_ABORTED);
		/* I lied.  So shoot me. */
	}
	/* Mark packet as being aborted and put it in the abort pending list. */
	fcalpkt->fcal_pkt_flags |= FCFLAG_ABORTING;

	scr = (soc_cmdonly_request_t *)&fcalpkt2->fcal_socal_request;
	tscr = (soc_cmdonly_request_t *)&fcalpkt->fcal_socal_request;
	scr->scr_soc_hdr.sh_byte_cnt = tscr->scr_soc_hdr.sh_request_token;
	scr->scr_cqhdr.cq_hdr_count = 1;
	scr->scr_cqhdr.cq_hdr_type = CQ_TYPE_REQUEST_ABORT;
	if (port)
		scr->scr_soc_hdr.sh_flags = SOC_PORT_B;
	fcalpkt2->fcal_pkt_cookie = (void *)socalp;
	mutex_exit(&socalp->abort_mtx);

	return (socal_doit(fcalpkt2, port_statep, polled, socal_abort_done,
	    SOCAL_ABORT_TIMEOUT, PORT_ABORT_PENDING, NULL));
}

/*ARGSUSED*/
static uint_t
socal_els(void *ssp, uint_t port, uint_t elscode, uint_t dest,
	void (*callback)(), void *arg, caddr_t reqpl, caddr_t *rsppl,
	uint_t sleep)
{
	return (FCAL_TRANSPORT_FAILURE);
}

static uint_t
socal_bypass_dev(void *ssp, uint_t port, uint_t dest)
{
	fcal_packet_t		*fcalpkt;
	soc_cmdonly_request_t	*scr;
	socal_state_t		*socalp = (socal_state_t *)ssp;
	socal_port_t		*port_statep = &socalp->port_state[port];

	if ((fcalpkt =
	    socal_packet_alloc(socalp, FCAL_SLEEP))
	    == (fcal_packet_t *)NULL)
		return (FCAL_ALLOC_FAILED);

	scr = (soc_cmdonly_request_t *)&fcalpkt->fcal_socal_request;
	if (port)
		scr->scr_soc_hdr.sh_flags = SOC_PORT_B;
	scr->scr_soc_hdr.sh_byte_cnt = dest;
	scr->scr_cqhdr.cq_hdr_count = 1;
	scr->scr_cqhdr.cq_hdr_type = CQ_TYPE_BYPASS_DEV;
	return (socal_doit(fcalpkt, port_statep, 0, socal_bypass_dev_done,
	    SOCAL_BYPASS_TIMEOUT, PORT_BYPASS_PENDING, NULL));
}


/*ARGSUSED*/
static void
socal_force_reset(void *ssp, uint_t port, uint_t restart)
{
	socal_state_t 	*socalp = (socal_state_t *)ssp;

	mutex_enter(&socalp->k_imr_mtx);
	if (socalp->socal_shutdown) {
		mutex_exit(&socalp->k_imr_mtx);
		return;
	} else {
		socalp->socal_shutdown = 1;
		mutex_exit(&socalp->k_imr_mtx);
	}
	socalp->socal_stats.resets++;
	socal_doreset(socalp);
	if (restart) {
		if (socal_start(socalp) != FCAL_SUCCESS) {
			cmn_err(CE_WARN, "socal: start failed.\n");
		}
	}
}


static void
socal_add_ulp(void *ssp, uint_t port, uchar_t type,
	void (*ulp_statec_callback)(), void (*ulp_els_callback)(),
	void (*ulp_data_callback)(), void *arg)
{
	socal_state_t	*socalp = (socal_state_t *)ssp;
	socal_port_t	*port_statep = &socalp->port_state[port];
	socal_unsol_cb_t *cbentry;

	mutex_enter(&port_statep->sp_mtx);
	for (cbentry = port_statep->sp_unsol_cb; cbentry;
	    cbentry = cbentry->next) {
		if (cbentry->type == type) {
			cbentry->statec_cb = ulp_statec_callback;
			cbentry->els_cb = ulp_els_callback;
			cbentry->data_cb = ulp_data_callback;
			cbentry->arg = arg;
			mutex_exit(&port_statep->sp_mtx);
			return;
		}
	}
	mutex_exit(&port_statep->sp_mtx);
	if ((cbentry =
	    (socal_unsol_cb_t *)kmem_zalloc(sizeof (socal_unsol_cb_t),
	    KM_SLEEP)) == (socal_unsol_cb_t *)NULL) {
		return;
	}
	mutex_enter(&port_statep->sp_mtx);
	cbentry->statec_cb = ulp_statec_callback;
	cbentry->els_cb = ulp_els_callback;
	cbentry->data_cb = ulp_data_callback;
	cbentry->arg = arg;
	cbentry->type = type;

	cbentry->next = port_statep->sp_unsol_cb;
	port_statep->sp_unsol_cb = cbentry;
	mutex_exit(&port_statep->sp_mtx);
}


/*
 * remove a ULP with matching type and arg
 */
static void
socal_remove_ulp(void *ssp, uint_t port, uchar_t type, void *arg)
{
	socal_state_t		*socalp = (socal_state_t *)ssp;
	socal_port_t		*port_statep;
	socal_unsol_cb_t	*cbentry;
	socal_unsol_cb_t	*p_cbentry;


	ASSERT(ssp != NULL);
	port_statep = &socalp->port_state[port];
	ASSERT(port_statep != NULL);

	/* scan the list of unsolicited callback entries */
	mutex_enter(&port_statep->sp_mtx);
	p_cbentry = NULL;
	for (cbentry = port_statep->sp_unsol_cb;
	    cbentry != NULL;
	    p_cbentry = cbentry, cbentry = cbentry->next) {
		if ((cbentry->type != type) || (cbentry->arg != arg)) {
			continue;	/* this entry  doesn't match */
		}
		/* found entry to remove */
		if (port_statep->sp_unsol_cb == cbentry) {
			/* remove first entry in list */
			port_statep->sp_unsol_cb = cbentry->next;
		} else {
			/* remove other entry in list */
			if (p_cbentry)
				p_cbentry->next = cbentry->next;
		}
		kmem_free((void *)cbentry, sizeof (socal_unsol_cb_t));
		DEBUGF(2, (CE_CONT, "socal port %d ULP removed\n", port));
		break;
	}
	mutex_exit(&port_statep->sp_mtx);
}


/*
 * static unsigned int
 * socal_intr() - this is the interrupt routine for the SOC. Process all
 *	possible incoming interrupts from the soc device.
 */

static unsigned int
socal_intr(caddr_t arg)
{
	socal_state_t *socalp = (socal_state_t *)arg;
	register volatile socal_reg_t *socalreg = socalp->socal_rp;
	unsigned csr;
	int cause = 0;
#if !defined(lint)
	int instance = ddi_get_instance(socalp->dip);
#endif
	int i, j, request;
	char full;
	struct fcal_packet *fpkt, *nfpkt;

	csr = socalreg->socal_csr.w;
	cause = (int)SOCAL_INTR_CAUSE(socalp, csr);

	DEBUGF(2, (CE_CONT,
	    "socal%d: intr: csr: 0x%x cause: 0x%x\n",
	    instance, csr, cause));

	if (!cause) {
		socalp->socal_on_intr = 0;
		return (DDI_INTR_UNCLAIMED);
	}

	socalp->socal_on_intr = 1;

	while (cause) {

	/*
	 * Process the unsolicited messages first in case there are some
	 * high priority async events that we should act on.
	 *
	 */

		if (cause & SOCAL_CSR_RSP_QUE_1) {
			socal_intr_unsolicited(socalp, 1);
	DEBUGF(4, (CE_CONT, "socal%d intr: did unsolicited\n", instance));
		}

		if (cause & SOCAL_CSR_RSP_QUE_0) {
			socal_intr_solicited(socalp, 0);
	DEBUGF(4, (CE_CONT, "socal%d intr: did solicited\n", instance));
		}

	/*
	 * for use with token-only response queues in the future
	 * if (cause & SOCAL_CSR_RSP_QUE_0) {
	 *	socal_intr_solicited(socalp, 0);
	 * }
	 */


	/*
	 * Process any request interrupts
	 * We only allow request interrupts when the request
	 * queue is full and we are waiting so we can enque
	 * another command.
	 */
		if ((request = (cause & SOCAL_CSR_HOST_TO_SOCAL)) != 0) {
		socalp->socal_stats.reqq_intrs++;
		for (i = SOCAL_CSR_1ST_H_TO_S, j = 0; j < SOCAL_N_CQS;
		    j++, i <<= 1) {
			if (request & i) {
			socal_kcq_t *kcq = &socalp->request[j];

			if (kcq->skc_full) {
				mutex_enter(&kcq->skc_mtx);
				full = kcq->skc_full;
				kcq->skc_full = 0;
				while ((fpkt = kcq->skc_overflowh) != NULL) {
				nfpkt = fpkt->fcal_pkt_next;
				fpkt->fcal_pkt_next = NULL;
				kcq->skc_overflowh = nfpkt;
				if (socal_cq_enque(socalp, (socal_port_t *)
				    fpkt->fcal_pkt_cookie,
				    (cqe_t *)&fpkt->fcal_socal_request,
				    j, FCAL_NOSLEEP, NULL, 1) !=
				    FCAL_TRANSPORT_SUCCESS) {
					break;
				}
				}
				if (!kcq->skc_overflowh) {
				if (full & SOCAL_SKC_SLEEP)
					cv_broadcast(&kcq->skc_cv);

			    /* Disable this queue's intrs */
				DEBUGF(2, (CE_CONT,
				    "socal%d: req que %d overflow cleared\n",
				    instance, j));
				mutex_enter(&socalp->k_imr_mtx);
				socalp->socal_rp->socal_imr =
				    (socalp->socal_k_imr &= ~i);
				mutex_exit(&socalp->k_imr_mtx);
				}
				mutex_exit(&kcq->skc_mtx);
			}
			}
		}
		}
		csr = socalreg->socal_csr.w;
		cause = (int)SOCAL_INTR_CAUSE(socalp, csr);
	DEBUGF(4, (CE_CONT, "socal%d intr: did request queues\n", instance));

	}

	socalp->socal_on_intr = 0;
	return (DDI_INTR_CLAIMED);
}

static void
socal_intr_solicited(socal_state_t *socalp, uint32_t srq)
{
	socal_kcq_t		*kcq;
	volatile socal_kcq_t	*kcqv;
	soc_response_t		*srp;
	cqe_t			*cqe;
	uint_t			status, i;
	fcal_packet_t		*fcalpkt = NULL;
	soc_header_t		*shp;
	register volatile socal_reg_t *socalreg = socalp->socal_rp;
	caddr_t			src, dst;
	uchar_t			index_in;
	cq_hdr_t		*cq_hdr;
	char			val;
	int			port;

#if defined(DEBUG) && !defined(lint)
	int instance = ddi_get_instance(socalp->dip);
#endif
	auto char buf[80];

	kcq = &socalp->response[srq];
	kcqv = (volatile socal_kcq_t *)kcq;
	DEBUGF(4, (CE_CONT, "socal%d intr_sol: entered \n", instance));

	/*
	 * Grab lock for request queue.
	 */
	mutex_enter(&kcq->skc_mtx);

	/*
	 * Process as many response queue entries as we can.
	 */
	cqe = &(kcq->skc_cq[kcqv->skc_out]);

	index_in = SOCAL_RESPONSEQ_INDEX(srq, socalreg->socal_rspp.w);

	if (index_in == kcqv->skc_out) {
		socalreg->socal_csr.w = ((kcqv->skc_out << 24) |
		    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_0));

		/* make sure the write completed */
		i = socalreg->socal_csr.w;

		index_in = SOCAL_RESPONSEQ_INDEX(srq, socalreg->socal_rspp.w);
	}

	kcqv->skc_in = index_in;

	while (kcqv->skc_out != index_in) {
		/* Find out where the newest entry lives in the queue */
		(void) ddi_dma_sync(kcq->skc_dhandle, 0, 0,
		    DDI_DMA_SYNC_FORKERNEL);

		srp = (soc_response_t *)cqe;
		port = srp->sr_soc_hdr.sh_flags & SOC_PORT_B;
		shp = &srp->sr_soc_hdr;
		cq_hdr = &srp->sr_cqhdr;
		/*
		 * It turns out that on faster CPU's we have a problem where
		 * the soc interrupts us before the response has been DMA'ed
		 * in. This should not happen but does !!. So to workaround
		 * the problem for now, check the sequence # of the response.
		 * If it does not match with what we have, we must be
		 * reading stale data
		 */
		if (cq_hdr->cq_hdr_seqno != kcqv->skc_seqno) {
#if defined(DEBUG) && !defined(lint)
			socal_read_stale_data++;
#endif
			if (kcq->deferred_intr_timeoutid) {
				mutex_exit(&kcq->skc_mtx);
				return;
			} else {
				kcq->skc_saved_out = kcqv->skc_out;
				kcq->skc_saved_seqno = kcqv->skc_seqno;
				kcq->deferred_intr_timeoutid = timeout(
				    socal_deferred_intr, (caddr_t)kcq,
				    drv_usectohz(10000));
				mutex_exit(&kcq->skc_mtx);
				return;
			}
		}

		fcalpkt = (fcal_packet_t *)
		    SOCAL_ID_LOOKUP(shp->sh_request_token);

		if ((socal_core & SOCAL_TAKE_CORE) && ddi_peek8(socalp->dip,
		    (char *)fcalpkt, &val) != DDI_SUCCESS) {
			cmn_err(CE_WARN, "bad token = %p\n", (void *)fcalpkt);
			mutex_exit(&kcq->skc_mtx);
			socal_take_core(socalp);
		}

		if ((fcalpkt == (fcal_packet_t *)NULL) ||
		    (fcalpkt->fcal_magic != FCALP_MAGIC)) {
			(void) sprintf(buf, "!invalid FC packet; \n\
			    in, out, seqno = 0x%x, 0x%x, 0x%x\n",
			    kcqv->skc_in, kcqv->skc_out, kcqv->skc_seqno);
			socal_disp_err(socalp, CE_WARN, "link.4060", buf);
			DEBUGF(4, (CE_CONT,
			    "\tsoc CR: 0x%x SAE: 0x%x CSR: 0x%x IMR: 0x%x\n",
			    socalreg->socal_cr.w,
			    socalreg->socal_sae.w,
			    socalreg->socal_csr.w,
			    socalreg->socal_imr));
		/*
		 * Update response queue ptrs and soc registers.
		 */
			kcqv->skc_out++;
			if ((kcqv->skc_out & kcq->skc_last_index) == 0) {
				kcqv->skc_out = 0;
				kcqv->skc_seqno++;
			}

		} else {

			DEBUGF(2, (CE_CONT, "packet 0x%p complete\n",
			    fcalpkt));
			status = srp->sr_soc_status;
			fcalpkt->fcal_pkt_status = status;
			DEBUGF(2, (CE_CONT, "SOC status: 0x%x\n", status));
			/*
			 * map soc status codes to
			 * transport status codes
			 */

			ASSERT((fcalpkt->fcal_cmd_state & FCAL_CMD_COMPLETE)
			    == 0);
			mutex_enter(&socalp->abort_mtx);
			fcalpkt->fcal_pkt_flags |= FCFLAG_COMPLETE;
			mutex_exit(&socalp->abort_mtx);

			/*
			 * Copy the response frame header (if there is one)
			 * so that the upper levels can use it.  Note that,
			 * for now, we'll copy the header only if there was
			 * some sort of non-OK status, to save the PIO reads
			 * required to get the header from the host adapter's
			 * xRAM.
			 */
			if (((status != FCAL_STATUS_OK) ||
			    (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags
			    & SOC_RESP_HEADER)) &&
			    (srp->sr_soc_hdr.sh_flags & SOC_FC_HEADER)) {
				src = (caddr_t)&srp->sr_fc_frame_hdr;
				dst = (caddr_t)&fcalpkt->fcal_resp_hdr;
				bcopy(src, dst, sizeof (fc_frame_header_t));
				fcalpkt->fcal_pkt_flags |= FCFLAG_RESP_HEADER;
				i = srp->sr_soc_hdr.sh_flags & SOC_PORT_B ?
				    1 : 0;
				if ((status != FCAL_STATUS_OK) &&
				    (status <= FCAL_STATUS_MAX_STATUS)) {
					socalp->socal_stats.pstats[i].
					    resp_status[status]++;
				} else {
					socalp->socal_stats.pstats[i].
					    resp_status[FCAL_STATUS_ERROR]++;
				}
			} else if (status == FCAL_STATUS_OK) {
				fcalpkt->fcal_socal_request.
				    sr_soc_hdr.sh_byte_cnt =
				    shp->sh_byte_cnt;
			}
			fcalpkt->fcal_diag_status =
			    (uint32_t)srp->sr_dataseg.fc_base;
			fcalpkt->fcal_ncmds = srp->sr_ncmds;

			/*
			 * Update response queue ptrs and soc registers.
			 */
			kcqv->skc_out++;
			if ((kcqv->skc_out & kcq->skc_last_index) == 0) {
				kcqv->skc_out = 0;
				kcqv->skc_seqno++;
			}

			/* For incmplt DMA offline loop by loopback */
			if (fcalpkt->fcal_pkt_status ==
			    FCAL_STATUS_INCOMPLETE_DMA_ERR) {
				socal_port_t	*port_statep;
				uint_t		r;

				/*
				 * Give up the mutex to avoid a deadlock
				 * with the loopback routine.
				 */
				mutex_exit(&kcq->skc_mtx);

				port_statep = &socalp->port_state[port];
				mutex_enter(&port_statep->sp_mtx);
				if (port_statep->sp_status &
				    PORT_DISABLED) {
					/* Already disabled */
					mutex_exit(&port_statep->sp_mtx);
				} else {
					port_statep->sp_status |=
					    PORT_DISABLED;
					mutex_exit(&port_statep->sp_mtx);
					(void) socal_diag_request(
					    (void *)socalp, port,
					    &r, SOC_DIAG_INT_LOOP);
				}
				/* reacquire mutex */
				mutex_enter(&kcq->skc_mtx);
			}

			/*
			 * Complete the packet *ONLY* if it not being aborted
			 * or the abort has already completed.  Otherwise it is
			 * not safe to free the ID.
			 */
			mutex_enter(&socalp->abort_mtx);
			if (!(fcalpkt->fcal_pkt_flags & FCFLAG_ABORTING)) {
				/*
				 * Call the completion routine
				 */
				SOCAL_ID_FREE(shp->sh_request_token);
				if (fcalpkt->fcal_pkt_comp != NULL) {
					fcalpkt->fcal_cmd_state |=
					    FCAL_CMD_COMPLETE;

					/*
					 * Give up the mutex to avoid a
					 * deadlock with the callback routine.
					 */
					mutex_exit(&socalp->abort_mtx);
					mutex_exit(&kcq->skc_mtx);

					/* callback */
					(*fcalpkt->fcal_pkt_comp)(fcalpkt);

					/* reacquire mutex */
					mutex_enter(&kcq->skc_mtx);
				} else {
					fcalpkt->fcal_cmd_state |=
					    FCAL_CMD_COMPLETE;
					mutex_exit(&socalp->abort_mtx);
				}
			} else {
				mutex_exit(&socalp->abort_mtx);
			}
		}


		if (kcq->skc_cq == NULL)
			/*
			 * This action averts a potential PANIC scenario
			 * where the SUSPEND code flow grabbed the kcq->skc_mtx
			 * when we let it go, to call our completion routine,
			 * and "initialized" the response queue.  We exit our
			 * processing loop here, thereby averting a PANIC due
			 * to a NULL de-reference from the response queue.
			 *
			 * Note that this is an interim measure that needs
			 * to be revisited when this driver is next revised
			 * for enhanced performance.
			 */
			break;

		/*
		 * We need to re-read the input and output pointers in
		 * case a polling routine should process some entries
		 * from the response queue while we're doing a callback
		 * routine with the response queue mutex dropped.
		 */
		cqe = &(kcq->skc_cq[kcqv->skc_out]);
		index_in = SOCAL_RESPONSEQ_INDEX(srq, socalreg->socal_rspp.w);

		/*
		 * Mess around with the hardware if we think we've run out
		 * of entries in the queue, just to make sure we've read
		 * all entries that are available.
		 */

		socalreg->socal_csr.w = ((kcqv->skc_out << 24) |
		    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_0));

		/* Make sure the csr write has completed */
		i = socalreg->socal_csr.w;
		DEBUGF(9, (CE_CONT, "csr.w = %x\n", i));

		/*
		 * Update our idea of where the host adapter has placed
		 * the most recent entry in the response queue and resync
		 * the response queue
		 */
		index_in = SOCAL_RESPONSEQ_INDEX(srq, socalreg->socal_rspp.w);

		kcqv->skc_in = index_in;
	}

	/* Drop lock for request queue. */
	mutex_exit(&kcq->skc_mtx);
}

/*
 * Function name : socal_intr_unsolicited()
 *
 * Return Values : none
 *
 * Description	 : Processes entries in the unsolicited response
 *		   queue
 *
 *	The SOC+ will give us an unsolicited response
 *	whenever its status changes: OFFLINE, ONLINE,
 *	or in response to a packet arriving from an originator.
 *
 *	When message requests come in they will be placed in our
 *	buffer queue or in the next "inline" packet by the SOC hardware.
 *
 * Context	: Unsolicited interrupts must be masked
 */

static void
socal_intr_unsolicited(socal_state_t *socalp, uint32_t urq)
{
	socal_kcq_t		*kcq;
	volatile socal_kcq_t	*kcqv;
	soc_response_t		*srp;
	volatile cqe_t		*cqe;
	int			port;
	register uchar_t		t_index, t_seqno;
	register volatile socal_reg_t *socalreg = socalp->socal_rp;
	volatile cqe_t		*cqe_cont = NULL;
	uint_t			i;
	int			hdr_count;
	int			status;
	ushort_t		flags;
	auto char		buf[256];
	socal_port_t		*port_statep;
#if defined(DEBUG) && !defined(lint)
	int			instance = ddi_get_instance(socalp->dip);
#endif
	uchar_t			index_in;
	socal_unsol_cb_t	*cblist;

	kcq = &socalp->response[urq];
	kcqv = (volatile socal_kcq_t *)kcq;

	/*
	 * Grab lock for response queue.
	 */
	mutex_enter(&kcq->skc_mtx);

	cqe = (volatile cqe_t *)&(kcq->skc_cq[kcqv->skc_out]);

	index_in = SOCAL_RESPONSEQ_INDEX(urq, socalreg->socal_rspp.w);

	kcqv->skc_in = index_in;

	while (kcqv->skc_out != index_in) {
		(void) ddi_dma_sync(kcq->skc_dhandle, 0, 0,
		    DDI_DMA_SYNC_FORKERNEL);

		/* Check for continuation entries */
		if ((hdr_count = cqe->cqe_hdr.cq_hdr_count) != 1) {

			t_seqno = kcqv->skc_seqno;
			t_index = kcqv->skc_out + hdr_count;

			i = index_in;
			if (kcqv->skc_out > index_in)
			i += kcq->skc_last_index + 1;

		/*
		 * If we think the continuation entries haven't yet
		 * arrived, try once more before giving up
		 */
			if (i < t_index) {

			socalreg->socal_csr.w =
			    ((kcqv->skc_out << 24) |
			    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_1));

			/* Make sure the csr write has completed */
			i = socalreg->socal_csr.w;

			/*
			 * Update our idea of where the host adapter has placed
			 * the most recent entry in the response queue
			 */
			i = index_in = SOCAL_RESPONSEQ_INDEX(urq,
			    socalreg->socal_rspp.w);
			if (kcqv->skc_out > index_in)
				i += kcq->skc_last_index + 1;

			/*
			 * Exit if the continuation entries haven't yet
			 * arrived
			 */
			if (i < t_index)
				break;
			}

			if (t_index > kcq->skc_last_index) {
			t_seqno++;
			t_index &= kcq->skc_last_index;
			}

			cqe_cont = (volatile cqe_t *)
			    &(kcq->skc_cq[t_index ? t_index - 1 :
			    kcq->skc_last_index]);


		    /* A cq_hdr_count > 2 is illegal; throw away the response */

		/*
		 * XXX - should probably throw out as many entries as the
		 * hdr_cout tells us there are
		 */
			if (hdr_count != 2) {
			socal_disp_err(socalp, CE_WARN, "driver.4030",
			    "!too many continuation entries");
			DEBUGF(4, (CE_CONT,
			    "socal%d: soc+ unsolicited entry count = %d\n",
			    instance, cqe->cqe_hdr.cq_hdr_count));

			if ((++t_index & kcq->skc_last_index) == 0) {
				t_index = 0;
				t_seqno++;
			}
			kcqv->skc_out = t_index;
			kcqv->skc_seqno = t_seqno;

			cqe = &(kcq->skc_cq[kcqv->skc_out]);
			cqe_cont = NULL;
			continue;
			}
		}

		/*
		 * Update unsolicited response queue ptrs
		 */
		kcqv->skc_out++;
		if ((kcqv->skc_out & kcq->skc_last_index) == 0) {
			kcqv->skc_out = 0;
			kcqv->skc_seqno++;
		}

		if (cqe_cont != NULL) {
			kcqv->skc_out++;
			if ((kcqv->skc_out & kcq->skc_last_index) == 0) {
				kcqv->skc_out = 0;
				kcqv->skc_seqno++;
			}
		}

		if (index_in == kcqv->skc_out) {
			socalreg->socal_csr.w = ((kcqv->skc_out << 24) |
			    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_1));

		/* Make sure the csr write has completed */
			i = socalreg->socal_csr.w;
		}

		srp = (soc_response_t *)cqe;
		flags = srp->sr_soc_hdr.sh_flags;
		port = flags & SOC_PORT_B;
		port_statep = &socalp->port_state[port];

		/*
		 * XXX need to deal buffer pool entries here
		 */
		switch (flags & ~SOC_PORT_B) {
		case SOC_UNSOLICITED | SOC_FC_HEADER:

			srp = (soc_response_t *)cqe;

			switch (srp->sr_fc_frame_hdr.r_ctl & R_CTL_ROUTING) {
			case R_CTL_EXTENDED_SVC:
			/*
			 * Extended Link Services frame received
			 */
			socalp->socal_stats.pstats[port].els_rcvd++;
			socal_us_els(socalp, (cqe_t *)cqe, (caddr_t)cqe_cont);

			/* do callbacks to any interested ULPs */
			mutex_enter(&port_statep->sp_mtx);
			for (cblist = port_statep->sp_unsol_cb; cblist;
			    cblist = cblist->next) {
				if (cblist->els_cb) {
					mutex_exit(&port_statep->sp_mtx);
					mutex_exit(&kcq->skc_mtx);
					cblist->els_cb(cblist->arg,
					    (cqe_t *)cqe,
					    (caddr_t)cqe_cont);
					mutex_enter(&kcq->skc_mtx);
					mutex_enter(&port_statep->sp_mtx);
				}
			}
			mutex_exit(&port_statep->sp_mtx);
			break;
			case R_CTL_BASIC_SVC:
			(void) sprintf(buf,
			    "!unsupported Link Service command: 0x%x",
			    srp->sr_fc_frame_hdr.type);
			socal_disp_err(socalp, CE_WARN, "link.4020", buf);
			break;
			case R_CTL_DEVICE_DATA:
			switch (srp->sr_fc_frame_hdr.type) {
			default:
				mutex_enter(&port_statep->sp_mtx);
				status = 1;
				for (cblist = port_statep->sp_unsol_cb; cblist;
				    cblist = cblist->next) {
				if (cblist->data_cb &&
				    (cblist->type ==
				    srp->sr_fc_frame_hdr.type)) {
					mutex_exit(&port_statep->sp_mtx);
					mutex_exit(&kcq->skc_mtx);
					cblist->data_cb(cblist->arg,
					    (cqe_t *)cqe, (caddr_t)cqe_cont);
					mutex_enter(&kcq->skc_mtx);
					mutex_enter(&port_statep->sp_mtx);
					status = 0;
				}
				}
				mutex_exit(&port_statep->sp_mtx);

				if (status == 0)
				break;

				(void) sprintf(buf,
				    "!unknown FC-4 command: 0x%x",
				    srp->sr_fc_frame_hdr.type);
				socal_disp_err(socalp, CE_WARN,
				    "link.4030", buf);
				break;
			}
			break;
			default:
			(void) sprintf(buf, "!unsupported FC frame R_CTL: 0x%x",
			    srp->sr_fc_frame_hdr.r_ctl);
			socal_disp_err(socalp, CE_WARN, "link.4040", buf);
			break;
			}
			break;

		case SOC_STATUS: {

			/*
			 * Note that only the lsbyte of the status has
			 * interesting information...
			 */
			status = srp->sr_soc_status;

			switch (status) {

			case FCAL_STATUS_ONLINE:
				(void) sprintf(buf,
				"!port %d: Fibre Channel is ONLINE\n", port);
				socal_disp_err(socalp, CE_CONT, "link.6010",
				    buf);
				mutex_enter(&port_statep->sp_mtx);
				port_statep->sp_status &= ~PORT_STATUS_MASK;
				port_statep->sp_status |= PORT_ONLINE;
				mutex_exit(&port_statep->sp_mtx);
				socalp->socal_stats.pstats[port].onlines++;
				DEBUGF(4, (CE_CONT,
				    "socal%d intr_unsol: ONLINE intr\n",
				    instance));
				break;

			case FCAL_STATUS_LOOP_ONLINE:
				(void) sprintf(buf,
				"!port %d: Fibre Channel Loop is ONLINE\n",
				    port);
				socal_disp_err(socalp, CE_CONT, "link.6010",
				    buf);
				mutex_enter(&port_statep->sp_mtx);
				port_statep->sp_status &= ~PORT_STATUS_MASK;
				port_statep->sp_status |= PORT_ONLINE_LOOP;
				mutex_exit(&port_statep->sp_mtx);
				socalp->socal_stats.pstats[port].online_loops++;
				DEBUGF(4, (CE_CONT,
				    "socal%d intr_unsol: ONLINE-LOOP intr\n",
				    instance));
				break;

			case FCAL_STATUS_ERR_OFFLINE:
				/*
				 * SOC and Responder will both flush
				 * all active commands.
				 * So I don't have to do anything
				 * until it comes back online.
				 */
				(void) sprintf(buf,
				"!port %d: Fibre Channel is OFFLINE\n", port);
				socal_disp_err(socalp, CE_CONT, "link.5010",
				    buf);

				mutex_enter(&port_statep->sp_mtx);
				port_statep->sp_status &= ~PORT_STATUS_MASK;
				port_statep->sp_status |= PORT_OFFLINE;
				port_statep->sp_lilpmap_valid = 0;
				mutex_exit(&port_statep->sp_mtx);
				socalp->socal_stats.pstats[port].offlines++;
				DEBUGF(4, (CE_CONT,
				    "socal%d intr_unsol: OFFLINE intr\n",
				    instance));

				break;
			default:
				(void) sprintf(buf, "!unknown status: 0x%x\n",
				    status);
				socal_disp_err(socalp, CE_WARN, "link.3020",
				    buf);
			}
			mutex_exit(&kcq->skc_mtx);
			mutex_enter(&port_statep->sp_mtx);
			for (cblist = port_statep->sp_unsol_cb; cblist;
			    cblist = cblist->next) {
				if (cblist->statec_cb) {
					mutex_exit(&port_statep->sp_mtx);
					(*cblist->statec_cb)(cblist->arg,
					    status);
					mutex_enter(&port_statep->sp_mtx);
				}
			}
			mutex_exit(&port_statep->sp_mtx);
			if (status == FCAL_STATUS_ERR_OFFLINE) {
				socal_flush_overflowq(socalp, port,
				    CQ_REQUEST_0);
				socal_flush_overflowq(socalp, port,
				    CQ_REQUEST_1);
			}
			mutex_enter(&kcq->skc_mtx);
			break;
		}
		default:
			(void) sprintf(buf, "!unexpected state: flags: 0x%x\n",
			    flags);
			socal_disp_err(socalp, CE_WARN, "link.4050", buf);
			DEBUGF(4, (CE_CONT,
			    "\tsoc CR: 0x%x SAE: 0x%x CSR: 0x%x IMR: 0x%x\n",
			    socalp->socal_rp->socal_cr.w,
			    socalp->socal_rp->socal_sae.w,
			    socalp->socal_rp->socal_csr.w,
			    socalp->socal_rp->socal_imr));
		}


		if (kcq->skc_cq == NULL)
			/*
			 * This action averts a potential PANIC scenario
			 * where the SUSPEND code flow grabbed the kcq->skc_mtx
			 * when we let it go, to call our completion routine,
			 * and "initialized" the response queue.  We exit our
			 * processing loop here, thereby averting a PANIC due
			 * to a NULL de-reference from the response queue.
			 *
			 * Note that this is an interim measure that needs
			 * to be revisited when this driver is next revised
			 * for enhanced performance.
			 */
			break;

		/*
		 * We need to re-read the input and output pointers in
		 * case a polling routine should process some entries
		 * from the response queue while we're doing a callback
		 * routine with the response queue mutex dropped.
		 */
		cqe = &(kcq->skc_cq[kcqv->skc_out]);
		index_in = SOCAL_RESPONSEQ_INDEX(urq, socalreg->socal_rspp.w);
		cqe_cont = NULL;

		/*
		 * Mess around with the hardware if we think we've run out
		 * of entries in the queue, just to make sure we've read
		 * all entries that are available.
		 */
		if (index_in == kcqv->skc_out) {

			socalreg->socal_csr.w =
			    ((kcqv->skc_out << 24) |
			    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_1));

		/* Make sure the csr write has completed */
			i = socalreg->socal_csr.w;

		/*
		 * Update our idea of where the host adapter has placed
		 * the most recent entry in the response queue
		 */
			index_in =
			    SOCAL_RESPONSEQ_INDEX(urq, socalreg->socal_rspp.w);
		}

		socalp->socal_stats.pstats[port].unsol_resps++;

		kcqv->skc_in = index_in;

	}

	/* Release lock for response queue. */
	mutex_exit(&kcq->skc_mtx);
}

/*
 * socal_us_els() - This function handles unsolicited extended link
 *	service responses received from the soc.
 */
static void
socal_us_els(socal_state_t *socalp, cqe_t *cqe, caddr_t payload)
{
	soc_response_t	*srp = (soc_response_t *)cqe;
	els_payload_t	*els = (els_payload_t *)payload;
	int	i;
	char   *bp;
	auto	char buf[256];

	/*
	 * There should be a CQE continuation entry for all
	 * extended link services
	 */
	if ((els == NULL) || ((i = srp->sr_soc_hdr.sh_byte_cnt) == 0)) {
		socal_disp_err(socalp, CE_WARN, "link.4010",
		"!incomplete continuation entry");
		return;
	}

	/* Quietly impose a maximum byte count */
	if (i > SOC_CQE_PAYLOAD)
		i = SOC_CQE_PAYLOAD;
	i -= sizeof (union els_cmd_u);

	/*
	 * Decode the LS_Command code
	 */
	switch (els->els_cmd.c.ls_command) {
		case LA_ELS_DISPLAY:
		els->els_data[i] = '\0';	/* terminate the string */
		for (bp = (char *)&(els->els_data[0]); *bp; bp++) {
			/* squash newlines */
			if (*bp == '\n') *bp = ' ';
		}
		(void) sprintf(buf, "!message: %s\n", els->els_data);
		socal_disp_err(socalp, CE_CONT, "link.1010", buf);
		break;

		default:
		DEBUGF(3, (CE_CONT, "!unknown LS_Command, %x\n",
		    els->els_cmd.i));
		break;
	}

}

/*ARGSUSED*/
static fcal_packet_t *
socal_packet_alloc(socal_state_t *socalp, fcal_sleep_t sleep)
{
	int flag;
	fcal_packet_t *pkt;

	if (sleep == FCAL_SLEEP)
		flag = KM_SLEEP;
	else
		flag = KM_NOSLEEP;

	pkt = (fcal_packet_t *)kmem_zalloc(sizeof (fcal_packet_t), flag);

	if (pkt != (fcal_packet_t *)NULL)
		pkt->fcal_magic = FCALP_MAGIC;

	return (pkt);
}

static void
socal_packet_free(fcal_packet_t *fcalpkt)
{
	kmem_free((void *)fcalpkt, sizeof (fcal_packet_t));
}

static void
socal_lilp_map_done(fcal_packet_t *fcalpkt)
{
	uint32_t	port;
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;

	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	mutex_enter(&socalp->port_state[port].sp_mtx);
	socalp->port_state[port].sp_status &= ~PORT_LILP_PENDING;
	cv_broadcast(&socalp->port_state[port].sp_cv);
	mutex_exit(&socalp->port_state[port].sp_mtx);
}

static void
socal_force_lip_done(fcal_packet_t *fcalpkt)
{
	uint32_t	port;
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;

	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	mutex_enter(&socalp->port_state[port].sp_mtx);
	socalp->port_state[port].sp_status &= ~PORT_LIP_PENDING;
	cv_broadcast(&socalp->port_state[port].sp_cv);
	mutex_exit(&socalp->port_state[port].sp_mtx);
}

static void
socal_adisc_done(fcal_packet_t *fcalpkt)
{
	uint32_t	port;
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;

	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	mutex_enter(&socalp->port_state[port].sp_mtx);
	socalp->port_state[port].sp_status &= ~PORT_ADISC_PENDING;
	cv_broadcast(&socalp->port_state[port].sp_cv);
	mutex_exit(&socalp->port_state[port].sp_mtx);
}

static void
socal_lbf_done(fcal_packet_t *fcalpkt)
{
	uint32_t	port;
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;

	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	mutex_enter(&socalp->port_state[port].sp_mtx);
	socalp->port_state[port].sp_status &= ~PORT_LBF_PENDING;
	cv_broadcast(&socalp->port_state[port].sp_cv);
	mutex_exit(&socalp->port_state[port].sp_mtx);
}

static void
socal_rls_done(fcal_packet_t *fcalpkt)
{
	uint32_t	port;
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;

	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	mutex_enter(&socalp->port_state[port].sp_mtx);
	socalp->port_state[port].sp_status &= ~PORT_RLS_PENDING;
	cv_broadcast(&socalp->port_state[port].sp_cv);
	mutex_exit(&socalp->port_state[port].sp_mtx);
}

static void
socal_force_offline_done(fcal_packet_t *fcalpkt)
{
	uint32_t	port;
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;

	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	mutex_enter(&socalp->port_state[port].sp_mtx);
	socalp->port_state[port].sp_status &= ~PORT_OFFLINE_PENDING;
	cv_broadcast(&socalp->port_state[port].sp_cv);
	mutex_exit(&socalp->port_state[port].sp_mtx);
}

static void
socal_abort_done(fcal_packet_t *fcalpkt)
{
	uint32_t	port;
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
	soc_header_t	*shp =
	    (soc_header_t *)&fcalpkt->fcal_socal_request.sr_soc_hdr;
	fcal_packet_t	*target = (fcal_packet_t *)
	    SOCAL_ID_LOOKUP(shp->sh_request_token);

	mutex_enter(&socalp->abort_mtx);
	ASSERT(target->fcal_pkt_flags & FCFLAG_ABORTING);
	if (!(target->fcal_pkt_flags & FCFLAG_COMPLETE)) {
		SOCAL_ID_FREE(shp->sh_request_token);
	}
	mutex_exit(&socalp->abort_mtx);
	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	mutex_enter(&socalp->port_state[port].sp_mtx);
	socalp->port_state[port].sp_status &= ~PORT_ABORT_PENDING;
	cv_broadcast(&socalp->port_state[port].sp_cv);
	mutex_exit(&socalp->port_state[port].sp_mtx);
}

static void
socal_bypass_dev_done(fcal_packet_t *fcalpkt)
{
	uint32_t	port;
	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
		port = 1;
	else
		port = 0;
	mutex_enter(&socalp->port_state[port].sp_mtx);
	socalp->port_state[port].sp_status &= ~PORT_BYPASS_PENDING;
	cv_broadcast(&socalp->port_state[port].sp_cv);
	mutex_exit(&socalp->port_state[port].sp_mtx);
}

/*ARGSUSED*/
static unsigned int
socal_dummy_intr(caddr_t arg)
{
	return (DDI_INTR_UNCLAIMED);
}

static int
socal_diag_request(socal_state_t *socalp, uint32_t port, uint_t *diagcode,
    uint32_t cmd)
{
	fcal_packet_t		*fcalpkt;
	soc_diag_request_t	*sdr;
	socal_port_t		*port_statep = &socalp->port_state[port];
	struct fcal_lilp_map	map;

	/* Grabbing the state mutex is totally unnecessary.... */
	if (!(port_statep->sp_status & PORT_DISABLED)) {
		if (socal_getmap(socalp, port, (caddr_t)&map, 0, FKIOCTL)
		    != -1) {
			if (map.lilp_length != 1 && ((port_statep->sp_status &
			    PORT_ONLINE_LOOP) && cmd != SOC_DIAG_REM_LOOP))
				return (FCAL_TRANSPORT_UNAVAIL);
		}
	}
	if ((fcalpkt = socal_packet_alloc(socalp, FCAL_SLEEP))
	    == (fcal_packet_t *)NULL)
		return (FCAL_ALLOC_FAILED);
	sdr = (soc_diag_request_t *)&fcalpkt->fcal_socal_request;
	if (port)
		sdr->sdr_soc_hdr.sh_flags = SOC_PORT_B;
	sdr->sdr_diag_cmd = cmd;
	sdr->sdr_cqhdr.cq_hdr_count = 1;
	sdr->sdr_cqhdr.cq_hdr_type = CQ_TYPE_DIAGNOSTIC;
	fcalpkt->fcal_pkt_cookie = (void *)socalp;
	return (socal_doit(fcalpkt, port_statep, 1, NULL,
	    SOCAL_DIAG_TIMEOUT, 0, diagcode));
}

static uint_t
socal_force_offline(void *ssp, uint_t port, uint_t polled)
{
	fcal_packet_t		*fcalpkt;
	soc_cmdonly_request_t	*scr;
	socal_state_t		*socalp = (socal_state_t *)ssp;
	socal_port_t		*port_statep = &socalp->port_state[port];

	if ((fcalpkt =
	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
	    == (fcal_packet_t *)NULL)
		return (FCAL_ALLOC_FAILED);

	scr = (soc_cmdonly_request_t *)&fcalpkt->fcal_socal_request;
	if (port)
		scr->scr_soc_hdr.sh_flags = SOC_PORT_B;
	scr->scr_cqhdr.cq_hdr_count = 1;
	scr->scr_cqhdr.cq_hdr_type = CQ_TYPE_OFFLINE;
	fcalpkt->fcal_pkt_cookie = (void *)socalp;
	return (socal_doit(fcalpkt, port_statep, 0, socal_force_offline_done,
	    SOCAL_OFFLINE_TIMEOUT, PORT_OFFLINE_PENDING, NULL));
}

static int
socal_issue_adisc(socal_state_t *socalp, uint32_t port, uint32_t dest,
	la_els_adisc_t *payload, uint32_t polled)
{
	int			retval;
	la_els_adisc_t		*buf;
	fcal_packet_t		*fcalpkt;
	socal_port_t		*port_statep;
	socal_priv_cmd_t 	*privp;

	port_statep = &socalp->port_state[port];

	if ((fcalpkt =
	    socal_els_alloc(socalp, port, dest, sizeof (la_els_adisc_t),
	    sizeof (la_els_adisc_t), (caddr_t *)&privp, polled))
	    == (fcal_packet_t *)NULL)
		return (FCAL_ALLOC_FAILED);

	privp = (socal_priv_cmd_t *)fcalpkt->fcal_pkt_private;
	buf = (la_els_adisc_t *)privp->cmd;
	buf->ls_code = LA_ELS_ADISC;
	buf->mbz[0] = 0;
	buf->mbz[1] = 0;
	buf->mbz[2] = 0;
	buf->hard_address = 0;
	bcopy((caddr_t)&port_statep->sp_p_wwn,
	    (caddr_t)&buf->port_wwn, sizeof (buf->port_wwn));
	bcopy((caddr_t)&socalp->socal_n_wwn,
	    (caddr_t)&buf->node_wwn, sizeof (buf->node_wwn));
	buf->nport_id = fcalpkt->fcal_socal_request.sr_fc_frame_hdr.s_id;
	(void) ddi_dma_sync(privp->cmd_handle, 0, 0, DDI_DMA_SYNC_FORDEV);

	retval = socal_doit(fcalpkt, port_statep, 0, socal_adisc_done,
	    SOCAL_ADISC_TIMEOUT, PORT_ADISC_PENDING, NULL);
	if (retval == FCAL_SUCCESS) {
		(void) ddi_dma_sync(privp->rsp_handle, 0, 0,
		    DDI_DMA_SYNC_FORKERNEL);
		bcopy(privp->rsp, (caddr_t)payload, sizeof (la_els_adisc_t));
	}
	privp->fapktp = NULL;
	socal_els_free(privp);
	return (retval);
}

static int
socal_issue_lbf(socal_state_t *socalp, uint32_t port,
    uchar_t *payload, size_t length, uint32_t polled)
{
	int			retval;
	fcal_packet_t		*fcalpkt;
	socal_port_t		*port_statep;
	socal_priv_cmd_t 	*privp;

	port_statep = &socalp->port_state[port];

	if ((fcalpkt = socal_lbf_alloc(socalp, port, length, length,
	    (caddr_t *)&privp, polled)) == (fcal_packet_t *)NULL)
		return (FCAL_ALLOC_FAILED);

	privp = (socal_priv_cmd_t *)fcalpkt->fcal_pkt_private;
	bcopy((caddr_t)payload, privp->cmd, length);
	(void) ddi_dma_sync(privp->cmd_handle, 0, 0, DDI_DMA_SYNC_FORDEV);

	retval = socal_doit(fcalpkt, port_statep, polled, socal_lbf_done,
	    SOCAL_LBF_TIMEOUT, PORT_LBF_PENDING, NULL);

	if (retval == FCAL_SUCCESS) {
		(void) ddi_dma_sync(privp->rsp_handle, 0, 0,
		    DDI_DMA_SYNC_FORKERNEL);
		bcopy(privp->rsp, (caddr_t)payload, length);
	}
	privp->fapktp = NULL;
	socal_lbf_free(privp);
	return (retval);
}

static int
socal_issue_rls(socal_state_t *socalp, uint32_t port, uint32_t dest,
	la_els_rls_reply_t *payload, uint32_t polled)
{
	int	retval;
	la_els_rls_t		*buf;
	fcal_packet_t		*fcalpkt;
	socal_port_t		*port_statep;
	socal_priv_cmd_t 	*privp;
	uint32_t		arg;

	port_statep = &socalp->port_state[port];

	if (dest == socal_getmap(socalp, port, NULL, 0, 0)) {
		/* load up the the struct with the local lesb */
		struct la_els_rjt *rsp = (struct la_els_rjt *)payload;

		rsp->ls_code = LA_ELS_RJT;
		rsp->mbz[0] = 0;
		rsp->mbz[1] = 0;
		rsp->mbz[2] = 0;
		rsp->reason_code = RJT_UNSUPPORTED;
		rsp->reserved = 0;
		rsp->explanation = 0;
		rsp->vendor = 0;
		return (FCAL_SUCCESS);
	}

	if ((fcalpkt =
	    socal_els_alloc(socalp, port, dest, sizeof (la_els_rls_t),
	    sizeof (la_els_rls_reply_t), (caddr_t *)&privp, polled))
	    == (fcal_packet_t *)NULL)
		return (FCAL_ALLOC_FAILED);

	privp = (socal_priv_cmd_t *)fcalpkt->fcal_pkt_private;

	if (payload->link_failure & 0xff000000)
		arg = payload->link_failure;
	else
		arg = dest;

	buf = (la_els_rls_t *)privp->cmd;
	buf->ls_code = LA_ELS_RLS;
	buf->mbz[0] = 0;
	buf->mbz[1] = 0;
	buf->mbz[2] = 0;
	buf->reserved = 0;
	buf->nport_id[0] = (arg >> 16) & 0xff;
	buf->nport_id[1] = (arg >> 8) & 0xff;
	buf->nport_id[2] = arg & 0xff;
	(void) ddi_dma_sync(privp->cmd_handle, 0, 0, DDI_DMA_SYNC_FORDEV);

	retval = socal_doit(fcalpkt, port_statep, 0, socal_rls_done,
	    SOCAL_RLS_TIMEOUT, PORT_RLS_PENDING, NULL);
	if (retval == FCAL_SUCCESS) {
		(void) ddi_dma_sync(privp->rsp_handle, 0, 0,
		    DDI_DMA_SYNC_FORKERNEL);
		bcopy(privp->rsp, (caddr_t)payload,
		    sizeof (la_els_rls_reply_t));
	}
	privp->fapktp = NULL;
	socal_els_free(privp);
	return (retval);
}

fcal_packet_t *
socal_els_alloc(socal_state_t *socalp, uint32_t port, uint32_t dest,
	uint32_t cmd_size, uint32_t rsp_size, caddr_t *rprivp, uint32_t polled)
{
	struct fcal_packet	*fcalpkt;
	ddi_dma_cookie_t	ccookie;
	ddi_dma_cookie_t	rcookie;
	socal_priv_cmd_t	*privp;
	ddi_dma_handle_t	chandle = NULL;
	ddi_dma_handle_t	rhandle = NULL;
	ddi_acc_handle_t	cacchandle;
	ddi_acc_handle_t	racchandle;
	soc_request_t		*srp;
	fc_frame_header_t	*fhp;
	uint_t			ccount, cmd_bound = 0, rsp_bound = 0;
	size_t			real_len;
	caddr_t			cmd;
	caddr_t			rsp;
	uint32_t		ouralpa;

	if ((fcalpkt =
	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
	    == (fcal_packet_t *)NULL)
		return (NULL);

	if ((privp =
	    (socal_priv_cmd_t *)kmem_zalloc(sizeof (socal_priv_cmd_t),
	    polled ? KM_NOSLEEP : KM_SLEEP)) == (socal_priv_cmd_t *)NULL) {
		goto fail;
	}

	rprivp = (caddr_t *)&privp;

	fcalpkt->fcal_pkt_private = (caddr_t)privp;
	privp->fapktp = (void *)fcalpkt;

	if ((ouralpa = socal_getmap(socalp, port, NULL, 0, 0)) == -1)
		goto fail;

	if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
	    DDI_DMA_DONTWAIT, NULL, &chandle) != DDI_SUCCESS)
		goto fail;
	privp->cmd_handle = chandle;

	if (ddi_dma_mem_alloc(chandle, cmd_size, &socal_acc_attr,
	    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
	    (caddr_t *)&cmd, &real_len, &cacchandle) != DDI_SUCCESS)
		goto fail;
	privp->cmd = cmd;
	privp->cmd_acchandle = cacchandle;

	if (real_len < cmd_size)
		goto fail;

	if (ddi_dma_addr_bind_handle(chandle, (struct as *)NULL,
	    (caddr_t)cmd, cmd_size,
	    DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
	    DDI_DMA_DONTWAIT, NULL, &ccookie, &ccount)
	    != DDI_DMA_MAPPED)
		goto fail;
	cmd_bound = 1;
	if (ccount != 1)
		goto fail;

	if (rsp_size) {
		if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
		    DDI_DMA_DONTWAIT, NULL, &rhandle) != DDI_SUCCESS)
		goto fail;

		privp->rsp_handle = rhandle;
		if (ddi_dma_mem_alloc(rhandle, rsp_size, &socal_acc_attr,
		    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
		    &rsp, &real_len, &racchandle) != DDI_SUCCESS)
			goto fail;
		privp->rsp = rsp;
		privp->rsp_acchandle = racchandle;
		if (real_len < rsp_size)
		goto fail;

		if (ddi_dma_addr_bind_handle(rhandle, (struct as *)NULL,
		    rsp, rsp_size,
		    DDI_DMA_READ | DDI_DMA_CONSISTENT,
		    DDI_DMA_DONTWAIT, NULL, &rcookie, &ccount)
		    != DDI_DMA_MAPPED)
		goto fail;

		rsp_bound = 1;
		if (ccount != 1)
		goto fail;
	}

	srp = (soc_request_t *)&fcalpkt->fcal_socal_request;
	srp->sr_soc_hdr.sh_flags = SOC_FC_HEADER;
	if (port)
		srp->sr_soc_hdr.sh_flags |= SOC_PORT_B;
	srp->sr_soc_hdr.sh_class = 3;
	srp->sr_soc_hdr.sh_byte_cnt = cmd_size;
	srp->sr_dataseg[0].fc_base = (uint32_t)ccookie.dmac_address;
	srp->sr_dataseg[0].fc_count = cmd_size;
	if (rsp_size == 0) {
		srp->sr_soc_hdr.sh_seg_cnt = 1;
	} else {
		srp->sr_soc_hdr.sh_seg_cnt = 2;
		srp->sr_dataseg[1].fc_base = (uint32_t)rcookie.dmac_address;
		srp->sr_dataseg[1].fc_count = rsp_size;
	}
	srp->sr_cqhdr.cq_hdr_count = 1;
	/* this will potentially be overwritten by the calling function */
	srp->sr_cqhdr.cq_hdr_type = CQ_TYPE_SIMPLE;

	fcalpkt->fcal_pkt_cookie = (void *)socalp;

	/* Fill in the Fabric Channel Header */
	fhp = &srp->sr_fc_frame_hdr;
	fhp->r_ctl = R_CTL_ELS_REQ;
	fhp->d_id = dest;
	fhp->s_id = ouralpa;
	fhp->type = TYPE_EXTENDED_LS;
	fhp->f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
	fhp->seq_id = 0;
	fhp->df_ctl  = 0;
	fhp->seq_cnt = 0;
	fhp->ox_id = 0xffff;
	fhp->rx_id = 0xffff;
	fhp->ro = 0;
	return (fcalpkt);
fail:
	socal_packet_free(fcalpkt);
	if (privp) {
		if (privp->cmd_handle) {
			if (cmd_bound)
				(void) ddi_dma_unbind_handle(privp->cmd_handle);
			ddi_dma_free_handle(&privp->cmd_handle);
		}
		if (privp->cmd)
			ddi_dma_mem_free(&privp->cmd_acchandle);
		if (privp->rsp_handle) {
			if (rsp_bound)
				(void) ddi_dma_unbind_handle(privp->rsp_handle);
			ddi_dma_free_handle(&privp->rsp_handle);
		}
		if (privp->rsp)
			ddi_dma_mem_free(&privp->rsp_acchandle);

		kmem_free(privp, sizeof (*privp));
	}
	return (NULL);
}

fcal_packet_t *
socal_lbf_alloc(socal_state_t *socalp, uint32_t port,
	uint32_t cmd_size, uint32_t rsp_size, caddr_t *rprivp,
	uint32_t polled)
{
	struct fcal_packet	*fcalpkt;
	ddi_dma_cookie_t	ccookie;
	ddi_dma_cookie_t	rcookie;
	socal_priv_cmd_t	*privp;
	ddi_dma_handle_t	chandle = NULL;
	ddi_dma_handle_t	rhandle = NULL;
	ddi_acc_handle_t	cacchandle;
	ddi_acc_handle_t	racchandle;
	soc_request_t		*srp;
	fc_frame_header_t	*fhp;
	uint_t			ccount, cmd_bound = 0, rsp_bound = 0;
	size_t			real_len;
	caddr_t			cmd;
	caddr_t			rsp;

	if ((fcalpkt =
	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
	    == (fcal_packet_t *)NULL)
		return (NULL);

	if ((privp =
	    (socal_priv_cmd_t *)kmem_zalloc(sizeof (socal_priv_cmd_t),
	    polled ? KM_NOSLEEP : KM_SLEEP)) == (socal_priv_cmd_t *)NULL) {
		goto fail;
	}

	rprivp = (caddr_t *)&privp;

	fcalpkt->fcal_pkt_private = (caddr_t)privp;
	privp->fapktp = (void *)fcalpkt;

	if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
	    DDI_DMA_DONTWAIT, NULL, &chandle) != DDI_SUCCESS)
		goto fail;
	privp->cmd_handle = chandle;

	if (ddi_dma_mem_alloc(chandle, cmd_size, &socal_acc_attr,
	    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
	    (caddr_t *)&cmd, &real_len, &cacchandle) != DDI_SUCCESS)
		goto fail;
	privp->cmd = cmd;
	privp->cmd_acchandle = cacchandle;

	if (real_len < cmd_size)
		goto fail;

	if (ddi_dma_addr_bind_handle(chandle, (struct as *)NULL,
	    (caddr_t)cmd, cmd_size,
	    DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
	    DDI_DMA_DONTWAIT, NULL, &ccookie, &ccount)
	    != DDI_DMA_MAPPED)
		goto fail;
	cmd_bound = 1;
	if (ccount != 1)
		goto fail;

	if (rsp_size) {
		if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
		    DDI_DMA_DONTWAIT, NULL, &rhandle) != DDI_SUCCESS)
		goto fail;

		privp->rsp_handle = rhandle;
		if (ddi_dma_mem_alloc(rhandle, rsp_size, &socal_acc_attr,
		    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
		    &rsp, &real_len, &racchandle) != DDI_SUCCESS)
			goto fail;

		privp->rsp = rsp;
		privp->rsp_acchandle = racchandle;
		if (real_len < rsp_size)
		goto fail;

		if (ddi_dma_addr_bind_handle(rhandle, (struct as *)NULL,
		    rsp, rsp_size,
		    DDI_DMA_READ | DDI_DMA_CONSISTENT,
		    DDI_DMA_DONTWAIT, NULL, &rcookie, &ccount)
		    != DDI_DMA_MAPPED)
			goto fail;

		rsp_bound = 1;
		if (ccount != 1)
		goto fail;
	}

	srp = (soc_request_t *)&fcalpkt->fcal_socal_request;
	srp->sr_soc_hdr.sh_flags = SOC_FC_HEADER;
	if (port)
		srp->sr_soc_hdr.sh_flags |= SOC_PORT_B;
	srp->sr_soc_hdr.sh_class = 3;
	srp->sr_soc_hdr.sh_byte_cnt = cmd_size;
	srp->sr_dataseg[0].fc_base = (uint32_t)ccookie.dmac_address;
	srp->sr_dataseg[0].fc_count = cmd_size;
	if (rsp_size == 0) {
		srp->sr_soc_hdr.sh_seg_cnt = 1;
	} else {
		srp->sr_soc_hdr.sh_seg_cnt = 2;
		srp->sr_dataseg[1].fc_base = (uint32_t)rcookie.dmac_address;
		srp->sr_dataseg[1].fc_count = rsp_size;
	}
	srp->sr_cqhdr.cq_hdr_count = 1;
	/* this will potentially be overwritten by the calling function */
	srp->sr_cqhdr.cq_hdr_type = CQ_TYPE_SIMPLE;

	fcalpkt->fcal_pkt_cookie = (void *)socalp;

	/* Fill in the Fabric Channel Header */
	fhp = &srp->sr_fc_frame_hdr;
	fhp->r_ctl = R_CTL_SOLICITED_DATA;
	fhp->d_id = socalp->port_state[port].sp_src_id;
	fhp->s_id = socalp->port_state[port].sp_src_id;
	fhp->type = TYPE_SCSI_FCP;
	fhp->f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ | F_CTL_LAST_SEQ;
	fhp->seq_id = 0;
	fhp->df_ctl  = 0;
	fhp->seq_cnt = 0;
	fhp->ox_id = 0xffff;
	fhp->rx_id = 0xffff;
	fhp->ro = 0;
	return (fcalpkt);
fail:
	socal_packet_free(fcalpkt);
	if (privp) {
		if (privp->cmd_handle) {
			if (cmd_bound)
				(void) ddi_dma_unbind_handle(privp->cmd_handle);
			ddi_dma_free_handle(&privp->cmd_handle);
		}
		if (privp->cmd)
			ddi_dma_mem_free(&privp->cmd_acchandle);
		if (privp->rsp_handle) {
			if (rsp_bound)
				(void) ddi_dma_unbind_handle(privp->rsp_handle);
			ddi_dma_free_handle(&privp->rsp_handle);
		}
		if (privp->rsp)
			ddi_dma_mem_free(&privp->rsp_acchandle);

		kmem_free(privp, sizeof (*privp));
	}
	return (NULL);
}

void
socal_els_free(socal_priv_cmd_t *privp)
{
	fcal_packet_t		*fcalpkt;

	if (privp)
		fcalpkt = (fcal_packet_t *)privp->fapktp;
	else
		return;

	(void) ddi_dma_unbind_handle(privp->cmd_handle);
	ddi_dma_free_handle(&privp->cmd_handle);
	ddi_dma_mem_free(&privp->cmd_acchandle);

	if (privp->rsp_handle) {
		(void) ddi_dma_unbind_handle(privp->rsp_handle);
		ddi_dma_free_handle(&privp->rsp_handle);
	}
	if (privp->rsp)
		ddi_dma_mem_free(&privp->rsp_acchandle);

	kmem_free(privp, sizeof (*privp));
	if (fcalpkt != NULL)
		socal_packet_free(fcalpkt);
}

void
socal_lbf_free(socal_priv_cmd_t *privp)
{
	fcal_packet_t		*fcalpkt;

	if (privp)
		fcalpkt = (fcal_packet_t *)privp->fapktp;
	else
		return;

	(void) ddi_dma_unbind_handle(privp->cmd_handle);
	ddi_dma_free_handle(&privp->cmd_handle);
	ddi_dma_mem_free(&privp->cmd_acchandle);

	if (privp->rsp_handle) {
		(void) ddi_dma_unbind_handle(privp->rsp_handle);
		ddi_dma_free_handle(&privp->rsp_handle);
	}

	if (privp->rsp)
		ddi_dma_mem_free(&privp->rsp_acchandle);

	kmem_free(privp, sizeof (*privp));
	if (fcalpkt != NULL)
		socal_packet_free(fcalpkt);
}

static int
socal_getmap(socal_state_t *socalp, uint32_t port, caddr_t arg,
	uint32_t polled, int flags)
{
	ddi_dma_cookie_t	dcookie;
	ddi_dma_handle_t	dhandle = NULL;
	ddi_acc_handle_t	acchandle;
	size_t			real_len, i;
	uint_t			ccount;
	fcal_lilp_map_t		*buf = NULL;
	int			retval, bound = 0;
	socal_port_t		*port_statep;

	port_statep = &socalp->port_state[port];

	if (port_statep->sp_lilpmap_valid) {

		buf = &port_statep->sp_lilpmap; /* give from cache */

		if (arg) {
		if (ddi_copyout(buf, (caddr_t)arg,
		    sizeof (struct lilpmap), flags) == -1)
			return (-1);
		}

		return (buf->lilp_myalpa);
	}

	if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
	    DDI_DMA_DONTWAIT, NULL, &dhandle) != DDI_SUCCESS)
		goto getmap_fail;

	i = sizeof (struct fcal_lilp_map);

	if (ddi_dma_mem_alloc(dhandle, i, &socal_acc_attr,
	    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
	    (caddr_t *)&buf, &real_len, &acchandle) != DDI_SUCCESS)
		goto getmap_fail;

	if (real_len < i)
		goto getmap_fail;

	if (ddi_dma_addr_bind_handle(dhandle, (struct as *)NULL,
	    (caddr_t)buf, i, DDI_DMA_READ | DDI_DMA_CONSISTENT,
	    DDI_DMA_DONTWAIT, NULL, &dcookie, &ccount) != DDI_DMA_MAPPED)
		goto getmap_fail;

	bound = 1;
	if (ccount != 1)
		goto getmap_fail;

	retval = socal_lilp_map((void *)socalp, port,
	    (uint32_t)dcookie.dmac_address, polled);

	(void) ddi_dma_sync(dhandle, 0, 0, DDI_DMA_SYNC_FORKERNEL);

	if (retval == FCAL_SUCCESS) {
		bcopy(buf, &port_statep->sp_lilpmap, sizeof (fcal_lilp_map_t));

		mutex_enter(&port_statep->sp_mtx);
		port_statep->sp_src_id = buf->lilp_myalpa;
		port_statep->sp_lilpmap_valid = 1; /* cached */
		mutex_exit(&port_statep->sp_mtx);

		if (arg) {
		if (ddi_copyout(buf, (caddr_t)arg,
		    sizeof (struct lilpmap), flags) == -1)
			goto getmap_fail;
		}

		retval = buf->lilp_myalpa;
	}
	else
		retval = -1;

	(void) ddi_dma_unbind_handle(dhandle);
	ddi_dma_mem_free(&acchandle);
	ddi_dma_free_handle(&dhandle);
	return (retval);

getmap_fail:
	if (dhandle) {
		if (bound)
			(void) ddi_dma_unbind_handle(dhandle);
		ddi_dma_free_handle(&dhandle);
	}
	if (buf)
		ddi_dma_mem_free(&acchandle);
	return (-1);
}

static	void
socal_wcopy(uint_t *h_src, uint_t *h_dest, int len)
{
	int	i;
	for (i = 0; i < len/4; i++) {
		*h_dest++ = *h_src++;
	}
}

static void
socal_flush_overflowq(socal_state_t *socalp, int port, int q_no)
{
	socal_kcq_t	*kcq;
	fcal_packet_t	*fpkt1, *fpkt2, *head = NULL, *tmp;

	kcq = &socalp->request[q_no];
	mutex_enter(&kcq->skc_mtx);
	fpkt2 = kcq->skc_overflowh;
	fpkt1 = NULL;
	while (fpkt2 != NULL) {
		if ((((soc_request_t *)&fpkt2->fcal_socal_request)
		    ->sr_soc_hdr.sh_flags & SOC_PORT_B) == port) {
			if (fpkt1 == NULL)
				kcq->skc_overflowh = fpkt2->fcal_pkt_next;
			else {
				fpkt1->fcal_pkt_next = fpkt2->fcal_pkt_next;
				if (kcq->skc_overflowt == fpkt2)
					kcq->skc_overflowt = fpkt1;
			}
			tmp = fpkt2->fcal_pkt_next;
			fpkt2->fcal_pkt_next = head;
			head = fpkt2;
			fpkt2 = tmp;
			SOCAL_ID_FREE(head->fcal_socal_request.
			    sr_soc_hdr.sh_request_token);
		} else {
			fpkt1 = fpkt2;
			fpkt2 = fpkt2->fcal_pkt_next;
		}
	}
	mutex_exit(&kcq->skc_mtx);
	fpkt2 = head;
	while (fpkt2 != NULL) {
		fpkt2->fcal_pkt_status = FCAL_STATUS_ERR_OFFLINE;
		fpkt2->fcal_cmd_state |= FCAL_CMD_COMPLETE;
		fpkt2->fcal_pkt_flags |= FCFLAG_COMPLETE;
		tmp = fpkt2->fcal_pkt_next;
		if (fpkt2->fcal_pkt_comp != NULL)
			(*fpkt2->fcal_pkt_comp)(fpkt2);
		fpkt2 = tmp;
	}
}

static void
socal_deferred_intr(void *arg)
{
	socal_kcq_t	*kcq = (socal_kcq_t *)arg;
	socal_state_t	*socalp = kcq->skc_socalp;

	ASSERT((socalp != NULL));

	mutex_enter(&kcq->skc_mtx);

	if ((kcq->skc_out != kcq->skc_saved_out) ||
	    (kcq->skc_seqno != kcq->skc_saved_seqno)) {
		kcq->deferred_intr_timeoutid = 0;
		mutex_exit(&kcq->skc_mtx);
		return;
	}

	if (socalp->socal_on_intr) {
		mutex_exit(&kcq->skc_mtx);
		kcq->deferred_intr_timeoutid = timeout(socal_deferred_intr,
		    (caddr_t)kcq, drv_usectohz(10000));
		return;
	}

	kcq->deferred_intr_timeoutid = 0;
	mutex_exit(&kcq->skc_mtx);
	socal_intr_solicited(socalp, 0);
}

static void
socal_take_core(void *arg)
{
	socal_state_t	*socalp = (socal_state_t *)arg;
	int i, instance;

	socal_disable(socalp);
	for (i = 0; i < SOCAL_N_CQS; i++) {
		mutex_enter(&socalp->request[i].skc_mtx);
		mutex_enter(&socalp->response[i].skc_mtx);
	}
	for (i = 0; i < 4; i++) {
		socalp->socal_rp->socal_cr.w &=
		    ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
		socalp->socal_rp->socal_cr.w |= i<<24;
		(void) bcopy((caddr_t)socalp->socal_xrp,
		    (caddr_t)&socal_xrambuf[i*0x10000], 0x10000);
	}
	for (i = 3; i >= 0; i--) {
		mutex_exit(&socalp->request[i].skc_mtx);
		mutex_exit(&socalp->response[i].skc_mtx);
	}
	instance = ddi_get_instance(socalp->dip);
	cmn_err(CE_PANIC,
	    "socal take core (socal instance %d)", instance);
}

/*
 * Preset AL_PA in hardware, if is told.
 */
static void
socal_fix_harda(socal_state_t *socalp, int port)
{
	socal_port_t	*portp = &socalp->port_state[port];
	uint_t		*xrp = (uint_t *)socalp->socal_xrp;
	uint_t		accum, harda;

	harda = portp->sp_hard_alpa;
	accum = xrp[SOCAL_XRAM_PORTA_HRDA/4];
	if (port == 0) {
		accum &= 0x00FFFFFF;
		accum |= ((harda & 0xFF) << 24);
	} else {
		accum &= 0xFF00FFFF;
		accum |= ((harda & 0xFF) << 16);
	}
	xrp[SOCAL_XRAM_PORTA_HRDA/4] = accum;
}

/*
 * Target-Mode attach function
 */
fcal_transport_t *
socal_sftm_attach(dev_t dev, int loop_id)
{
	int 		instance = getminor(dev) / 2;
	int		port = getminor(dev) % 2;
	int		hard_alpa;
	char		*name;
	socal_state_t	*socalp;

	/*
	 * If the device is not a "socal" device, return
	 */
	if ((name = ddi_major_to_name(getmajor(dev))) == NULL ||
	    strcmp(name, "socal") != 0)
		return (NULL);

	/*
	 * If no soft state structure, return
	 */
	socalp = ddi_get_soft_state(socal_soft_state_p, instance);
	if (socalp == NULL)
		return (NULL);

	/*
	 * If the port is already attached, return
	 */
	if (socalp->port_state[port].sp_status & PORT_CHILD_INIT)
		return (NULL);

	if (loop_id < 0 || loop_id > 126)
		return (NULL);

	/* if this instance is detaching, don't attach */
	mutex_enter(&socalp->board_mtx);
	mutex_enter(&socalp->port_state[port].sp_mtx);
	if (socalp->socal_busy < 0) {
		mutex_exit(&socalp->port_state[port].sp_mtx);
		mutex_exit(&socalp->board_mtx);
		return (NULL);
	}
	socalp->socal_busy++;
	socalp->port_state[port].sp_status |= PORT_CHILD_INIT;
	mutex_exit(&socalp->port_state[port].sp_mtx);
	mutex_exit(&socalp->board_mtx);

	/*
	 * Since we keep the Hard Loop-id in two config files, warn the
	 * user if they don't match.
	 */
	hard_alpa = socal_switch_to_alpa[loop_id];
	if (hard_alpa != socalp->port_state[port].sp_hard_alpa) {
		socalp->port_state[port].sp_hard_alpa = hard_alpa;
		cmn_err(CE_WARN, "socal%d: Hard Loop-id mismatch - "
		    "using Loop-id %d",
		    instance, loop_id);
	}

	return (socalp->port_state[port].sp_transport);
}


/*
 * Target-Mode detach function
 */
int
socal_sftm_detach(socal_state_t *socalp, int port)
{
	mutex_enter(&socalp->board_mtx);
	socalp->socal_busy--;
	socalp->port_state[port].sp_status &= ~PORT_CHILD_INIT;
	mutex_exit(&socalp->board_mtx);

	return (0);
}