xref: /titanic_52/usr/src/uts/sun/io/socal.c (revision cd21e7c548ae2a3b5e522244bf798f2a6b4ba02d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*
27  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
28  */
29 
30 /*
31  * socal - Serial Optical Channel Arbitrated Loop host adapter driver.
32  */
33 
34 #include <sys/types.h>
35 #include <sys/note.h>
36 #include <sys/devops.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/user.h>
40 #include <sys/buf.h>
41 #include <sys/ioctl.h>
42 #include <sys/uio.h>
43 #include <sys/fcntl.h>
44 
45 #include <sys/cmn_err.h>
46 #include <sys/stropts.h>
47 #include <sys/kmem.h>
48 
49 #include <sys/errno.h>
50 #include <sys/open.h>
51 #include <sys/varargs.h>
52 #include <sys/var.h>
53 #include <sys/thread.h>
54 #include <sys/debug.h>
55 #include <sys/cpu.h>
56 #include <sys/autoconf.h>
57 #include <sys/conf.h>
58 #include <sys/stat.h>
59 
60 #include <sys/file.h>
61 #include <sys/syslog.h>
62 
63 #include <sys/ddi.h>
64 #include <sys/sunddi.h>
65 #include <sys/ddi_impldefs.h>
66 #include <sys/ksynch.h>
67 #include <sys/ddidmareq.h>
68 #include <sys/dditypes.h>
69 #include <sys/ethernet.h>
70 #include <sys/socalreg.h>
71 #include <sys/socalmap.h>
72 #include <sys/fc4/fcal.h>
73 #include <sys/socal_cq_defs.h>
74 #include <sys/fc4/fcal_linkapp.h>
75 #include <sys/fc4/fcal_transport.h>
76 #include <sys/socalio.h>
77 #include <sys/socalvar.h>
78 
79 /*
80  * Local Macros
81  */
82 
83 #ifdef DEBUG
84 #define	SOCAL_DEBUG 1
85 #else
86 #define	SOCAL_DEBUG 0
87 #endif
88 static uchar_t	socal_xrambuf[0x40000];
89 static int 	socal_core = SOCAL_TAKE_CORE;
90 #if SOCAL_DEBUG > 0 && !defined(lint)
91 static	int soc_debug = SOCAL_DEBUG;
92 static  int socal_read_stale_data = 0;
93 #define	DEBUGF(level, args) \
94 	if (soc_debug >= (level)) cmn_err args;
95 #define	SOCALDEBUG(level, args) \
96 	if (soc_debug >= level) args;
97 #else
98 #define	DEBUGF(level, args)	/* Nothing */
99 #define	SOCALDEBUG(level, args)	/* Nothing */
100 #endif
101 
102 
103 /* defines for properties */
104 #define	SOCAL_PORT_NO_PROP		"socal_port"
105 #define	SOCAL_ALT_PORT_NO_PROP		"port#"
106 
107 /* for socal_force_reset() */
108 #define	RESET_PORT			1
109 #define	DONT_RESET_PORT			0
110 
111 /*
112  * Driver Entry points.
113  */
114 static int socal_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
115 static int socal_bus_ctl(dev_info_t *dip, dev_info_t *rip,
116 	ddi_ctl_enum_t op, void *a, void *v);
117 static int socal_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
118 static int socal_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
119 	void *arg, void **result);
120 static unsigned int socal_intr(caddr_t arg);
121 static unsigned int socal_dummy_intr(caddr_t arg);
122 static int socal_open(dev_t *devp, int flag, int otyp,
123 	cred_t *cred_p);
124 static int socal_close(dev_t dev, int flag, int otyp,
125 	cred_t *cred_p);
126 static int socal_ioctl(dev_t dev, int cmd, intptr_t arg,
127 	int mode, cred_t *cred_p, int *rval_p);
128 
129 /*
130  * FC_AL transport functions.
131  */
132 static uint_t socal_transport(fcal_packet_t *, fcal_sleep_t, int);
133 static uint_t socal_transport_poll(fcal_packet_t *, uint_t, int);
134 static uint_t socal_lilp_map(void *, uint_t, uint32_t, uint_t);
135 static uint_t socal_force_lip(void *, uint_t, uint_t, uint_t);
136 static uint_t socal_force_offline(void *, uint_t, uint_t);
137 static uint_t socal_abort_cmd(void *, uint_t, fcal_packet_t *, uint_t);
138 static uint_t socal_doit(fcal_packet_t *, socal_port_t *, int,
139     void (*)(), int, int, uint_t *);
140 static uint_t socal_els(void *, uint_t, uint_t, uint_t,
141 	void (*callback)(), void *, caddr_t, caddr_t *, uint_t);
142 static uint_t socal_bypass_dev(void *, uint_t, uint_t);
143 static void socal_force_reset(void *, uint_t, uint_t);
144 static void socal_add_ulp(void *, uint_t, uchar_t, void (*)(),
145 	void (*)(), void (*)(), void *);
146 static void socal_remove_ulp(void *, uint_t, uchar_t, void *);
147 static void socal_take_core(void *);
148 
149 /*
150  * Driver internal functions.
151  */
152 static void socal_intr_solicited(socal_state_t *, uint32_t srq);
153 static void socal_intr_unsolicited(socal_state_t *, uint32_t urq);
154 static void socal_lilp_map_done(fcal_packet_t *);
155 static void socal_force_lip_done(fcal_packet_t *);
156 static void socal_force_offline_done(fcal_packet_t *);
157 static void socal_abort_done(fcal_packet_t *);
158 static void socal_bypass_dev_done(fcal_packet_t *);
159 static fcal_packet_t *socal_packet_alloc(socal_state_t *, fcal_sleep_t);
160 static void socal_packet_free(fcal_packet_t *);
161 static void socal_disable(socal_state_t *socalp);
162 static void socal_init_transport_interface(socal_state_t *socalp);
163 static int socal_cqalloc_init(socal_state_t *socalp, uint32_t index);
164 static void socal_cqinit(socal_state_t *socalp, uint32_t index);
165 static int socal_start(socal_state_t *socalp);
166 static void socal_doreset(socal_state_t *socalp);
167 static int socal_dodetach(dev_info_t *dip);
168 static int socal_diag_request(socal_state_t *socalp, uint32_t port,
169 	uint_t *diagcode, uint32_t cmd);
170 static void socal_download_ucode(socal_state_t *socalp);
171 static void socal_init_cq_desc(socal_state_t *socalp);
172 static void socal_init_wwn(socal_state_t *socalp);
173 static void socal_enable(socal_state_t *socalp);
174 static int socal_establish_pool(socal_state_t *socalp, uint32_t poolid);
175 static int socal_add_pool_buffer(socal_state_t *socalp, uint32_t poolid);
176 static int socal_issue_adisc(socal_state_t *socalp, uint32_t port, uint32_t
177 	dest, la_els_adisc_t *adisc_pl, uint32_t polled);
178 static int socal_issue_lbf(socal_state_t *socalp, uint32_t port,
179 	uchar_t *flb_pl, size_t length, uint32_t polled);
180 static int socal_issue_rls(socal_state_t *socalp, uint32_t port, uint32_t
181 	dest, la_els_rls_reply_t *rls_pl, uint32_t polled);
182 static void socal_us_els(socal_state_t *, cqe_t *, caddr_t);
183 static fcal_packet_t *socal_els_alloc(socal_state_t *, uint32_t, uint32_t,
184 	uint32_t, uint32_t, caddr_t *, uint32_t);
185 static fcal_packet_t *socal_lbf_alloc(socal_state_t *, uint32_t,
186 	uint32_t, uint32_t, caddr_t *, uint32_t);
187 static void socal_els_free(socal_priv_cmd_t *);
188 static void socal_lbf_free(socal_priv_cmd_t *);
189 static int socal_getmap(socal_state_t *socalp, uint32_t port, caddr_t arg,
190 	uint32_t polled, int);
191 static void socal_flush_overflowq(socal_state_t *, int, int);
192 static void socal_deferred_intr(void *);
193 static void socal_fix_harda(socal_state_t *socalp, int port);
194 
195 /*
196  * SOC+ Circular Queue Management routines.
197  */
198 static int socal_cq_enque(socal_state_t *, socal_port_t *, cqe_t *, int,
199 	fcal_sleep_t, fcal_packet_t *, int);
200 
201 /*
202  * Utility functions
203  */
204 static void socal_disp_err(socal_state_t *, uint_t level, char *mid, char *msg);
205 static void socal_wcopy(uint_t *, uint_t *, int);
206 
207 /*
208  *  Set this bit to enable 64-bit sus mode
209  */
210 static	int socal_64bitsbus = 1;
211 
212 /*
213  * Default soc dma limits
214  */
215 
216 static ddi_dma_lim_t default_socallim = {
217 	(ulong_t)0, (ulong_t)0xffffffff, (uint_t)0xffffffff,
218 	DEFAULT_BURSTSIZE | BURST32 | BURST64, 1, (25*1024)
219 };
220 
221 static struct ddi_dma_attr socal_dma_attr = {
222 	DMA_ATTR_V0,			/* version */
223 	(unsigned long long)0,		/* addr_lo */
224 	(unsigned long long)0xffffffff,	/* addr_hi */
225 	(unsigned long long)0xffffffff,	/* count max */
226 	(unsigned long long)4,		/* align */
227 	DEFAULT_BURSTSIZE | BURST32 | BURST64, 	/* burst size */
228 	1,				/* minxfer */
229 	(unsigned long long)0xffffffff,	/* maxxfer */
230 	(unsigned long long)0xffffffff,	/* seg */
231 	1,				/* sgllen */
232 	4,				/* granularity */
233 	0				/* flags */
234 };
235 
236 static struct ddi_device_acc_attr socal_acc_attr = {
237 	(ushort_t)DDI_DEVICE_ATTR_V0,	/* version */
238 	(uchar_t)DDI_STRUCTURE_BE_ACC,	/* endian flags */
239 	(uchar_t)DDI_STRICTORDER_ACC	/* data order */
240 };
241 
242 static struct fcal_transport_ops socal_transport_ops = {
243 	socal_transport,
244 	socal_transport_poll,
245 	socal_lilp_map,
246 	socal_force_lip,
247 	socal_abort_cmd,
248 	socal_els,
249 	socal_bypass_dev,
250 	socal_force_reset,
251 	socal_add_ulp,
252 	socal_remove_ulp,
253 	socal_take_core
254 };
255 
256 /*
257  * Table used for setting the burst size in the soc+ config register
258  */
259 static int socal_burst32_table[] = {
260 	SOCAL_CR_BURST_4,
261 	SOCAL_CR_BURST_4,
262 	SOCAL_CR_BURST_4,
263 	SOCAL_CR_BURST_4,
264 	SOCAL_CR_BURST_16,
265 	SOCAL_CR_BURST_32,
266 	SOCAL_CR_BURST_64
267 };
268 
269 /*
270  * Table for setting the burst size for 64-bit sbus mode in soc+'s CR
271  */
272 static int socal_burst64_table[] = {
273 	(SOCAL_CR_BURST_8 << 8),
274 	(SOCAL_CR_BURST_8 << 8),
275 	(SOCAL_CR_BURST_8 << 8),
276 	(SOCAL_CR_BURST_8 << 8),
277 	(SOCAL_CR_BURST_8 << 8),
278 	(SOCAL_CR_BURST_32 << 8),
279 	(SOCAL_CR_BURST_64 << 8),
280 	(SOCAL_CR_BURST_128 << 8)
281 };
282 
283 /*
284  * Tables used to define the sizes of the Circular Queues
285  *
286  * To conserve DVMA/IOPB space, we make some of these queues small...
287  */
288 static int socal_req_entries[] = {
289 	SOCAL_SMALL_CQ_ENTRIES,		/* Error (reset, lip) requests */
290 	SOCAL_MAX_CQ_ENTRIES,		/* Most commands */
291 	0,				/* Not currently used */
292 	0				/* Not currently used */
293 };
294 
295 static int socal_rsp_entries[] = {
296 	SOCAL_MAX_CQ_ENTRIES,		/* Solicited  "SOC_OK" responses */
297 	SOCAL_SMALL_CQ_ENTRIES,		/* Solicited error responses */
298 	0,			/* Unsolicited responses */
299 	0				/* Not currently used */
300 };
301 
302 /*
303  * Bus ops vector
304  */
305 
306 static struct bus_ops socal_bus_ops = {
307 	BUSO_REV,		/* rev */
308 	nullbusmap,		/* int (*bus_map)() */
309 	0,			/* ddi_intrspec_t (*bus_get_intrspec)(); */
310 	0,			/* int (*bus_add_intrspec)(); */
311 	0,			/* void	(*bus_remove_intrspec)(); */
312 	i_ddi_map_fault,	/* int (*bus_map_fault)() */
313 	0,			/* int (*bus_dma_map)() */
314 	ddi_dma_allochdl,
315 	ddi_dma_freehdl,
316 	ddi_dma_bindhdl,
317 	ddi_dma_unbindhdl,
318 	ddi_dma_flush,
319 	ddi_dma_win,
320 	ddi_dma_mctl,		/* int (*bus_dma_ctl)() */
321 	socal_bus_ctl,		/* int (*bus_ctl)() */
322 	ddi_bus_prop_op,	/* int (*bus_prop_op*)() */
323 };
324 
325 static struct cb_ops socal_cb_ops = {
326 	socal_open,		/* int (*cb_open)() */
327 	socal_close,		/* int (*cb_close)() */
328 	nodev,			/* int (*cb_strategy)() */
329 	nodev,			/* int (*cb_print)() */
330 	nodev,			/* int (*cb_dump)() */
331 	nodev,			/* int (*cb_read)() */
332 	nodev,			/* int (*cb_write)() */
333 	socal_ioctl,		/* int (*cb_ioctl)() */
334 	nodev,			/* int (*cb_devmap)() */
335 	nodev,			/* int (*cb_mmap)() */
336 	nodev,			/* int (*cb_segmap)() */
337 	nochpoll,		/* int (*cb_chpoll)() */
338 	ddi_prop_op,		/* int (*cb_prop_op)() */
339 	0,			/* struct streamtab *cb_str */
340 	D_MP|D_NEW|D_HOTPLUG,	/* cb_flag */
341 	CB_REV,			/* rev */
342 	nodev,			/* int (*cb_aread)() */
343 	nodev			/* int (*cb_awrite)() */
344 };
345 
346 /*
347  * Soc driver ops structure.
348  */
349 
350 static struct dev_ops socal_ops = {
351 	DEVO_REV,		/* devo_rev, */
352 	0,			/* refcnt */
353 	socal_getinfo,		/* get_dev_info */
354 	nulldev,		/* identify */
355 	nulldev,		/* probe */
356 	socal_attach,		/* attach */
357 	socal_detach,		/* detach */
358 	nodev,			/* reset */
359 	&socal_cb_ops,		/* driver operations */
360 	&socal_bus_ops,		/* bus operations */
361 	NULL,			/* power */
362 	ddi_quiesce_not_supported,	/* quiesce */
363 };
364 
365 /*
366  * Driver private variables.
367  */
368 
369 static void *socal_soft_state_p = NULL;
370 static ddi_dma_lim_t *socallim = NULL;
371 
372 static uchar_t socal_switch_to_alpa[] = {
373 	0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda, 0xd9, 0xd6,
374 	0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce, 0xcd, 0xcc, 0xcb, 0xca,
375 	0xc9, 0xc7, 0xc6, 0xc5, 0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5,
376 	0xb4, 0xb3, 0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9,
377 	0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b, 0x98, 0x97,
378 	0x90, 0x8f, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7c, 0x7a, 0x79,
379 	0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b,
380 	0x6a, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56,
381 	0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a,
382 	0x49, 0x47, 0x46, 0x45, 0x43, 0x3c, 0x3a, 0x39, 0x36, 0x35,
383 	0x34, 0x33, 0x32, 0x31, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
384 	0x27, 0x26, 0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17,
385 	0x10, 0x0f, 0x08, 0x04, 0x02, 0x01, 0x00
386 };
387 
388 /*
389  * Firmware related externs
390  */
391 extern uint32_t socal_ucode[];
392 extern size_t socal_ucode_size;
393 
394 /*
395  * This is the loadable module wrapper: "module configuration section".
396  */
397 
398 #include <sys/modctl.h>
399 extern struct mod_ops mod_driverops;
400 
401 /*
402  * Module linkage information for the kernel.
403  */
404 #define	SOCAL_NAME "SOC+ FC-AL Host Adapter Driver"
405 static	char	socal_version[] = "1.62 08/19/2008";
406 static struct modldrv modldrv = {
407 	&mod_driverops,		/* Type of module.  This one is a driver */
408 	SOCAL_NAME,
409 	&socal_ops,		/* driver ops */
410 };
411 
412 static struct modlinkage modlinkage = {
413 	MODREV_1, (void *)&modldrv, NULL
414 };
415 
416 /*
417  * This is the module initialization/completion routines
418  */
419 
420 #if !defined(lint)
421 static char socal_initmsg[] = "socal _init: socal.c\t1.62\t08/19/2008\n";
422 #endif
423 
424 int
425 _init(void)
426 {
427 	int stat;
428 
429 	DEBUGF(4, (CE_CONT, socal_initmsg));
430 
431 	/* Allocate soft state.  */
432 	stat = ddi_soft_state_init(&socal_soft_state_p,
433 	    sizeof (socal_state_t), SOCAL_INIT_ITEMS);
434 	if (stat != 0)
435 		return (stat);
436 
437 	/* Install the module */
438 	stat = mod_install(&modlinkage);
439 	if (stat != 0)
440 		ddi_soft_state_fini(&socal_soft_state_p);
441 
442 	DEBUGF(4, (CE_CONT, "socal: _init: return=%d\n", stat));
443 	return (stat);
444 }
445 
446 int
447 _fini(void)
448 {
449 	int stat;
450 
451 	if ((stat = mod_remove(&modlinkage)) != 0)
452 		return (stat);
453 
454 	DEBUGF(4, (CE_CONT, "socal: _fini: \n"));
455 
456 	ddi_soft_state_fini(&socal_soft_state_p);
457 
458 	DEBUGF(4, (CE_CONT, "socal: _fini: return=%d\n", stat));
459 	return (stat);
460 }
461 
462 int
463 _info(struct modinfo *modinfop)
464 {
465 	return (mod_info(&modlinkage, modinfop));
466 }
467 
468 
469 int
470 socal_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
471 {
472 	int			instance;
473 	socal_state_t		*socalp;
474 	struct ether_addr	ourmacaddr;
475 	socal_port_t		*porta, *portb;
476 	char			buf[MAXPATHLEN];
477 	char			*cptr, *wwn;
478 	int			y;
479 	int			i, j;
480 	int			burstsize;
481 	short			s;
482 	int			loop_id;
483 
484 	int			rval;
485 
486 
487 	instance = ddi_get_instance(dip);
488 
489 	DEBUGF(4, (CE_CONT, "socal%d entering attach: cmd=%x\n", instance,
490 	    cmd));
491 
492 	if (cmd == DDI_RESUME) {
493 		if ((socalp = ddi_get_driver_private(dip)) == NULL)
494 			return (DDI_FAILURE);
495 
496 		if (!socalp->socal_shutdown) {
497 			/* our work is already done */
498 			return (DDI_SUCCESS);
499 		}
500 		if (socal_start(socalp) != FCAL_SUCCESS) {
501 			return	(DDI_FAILURE);
502 		}
503 		DEBUGF(4, (CE_CONT, "socal%d resumed\n", instance));
504 		return (DDI_SUCCESS);
505 	}
506 
507 	if (cmd != DDI_ATTACH) {
508 		return (DDI_FAILURE);
509 	}
510 
511 	if (ddi_dev_is_sid(dip) != DDI_SUCCESS) {
512 		cmn_err(CE_WARN, "socal%d probe: Not self-identifying",
513 		    instance);
514 		return (DDI_FAILURE);
515 	}
516 
517 	/* If we are in a slave-slot, then we can't be used. */
518 	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
519 		cmn_err(CE_WARN,
520 		    "socal%d attach failed: device in slave-only slot",
521 		    instance);
522 		return (DDI_FAILURE);
523 	}
524 
525 	if (ddi_intr_hilevel(dip, 0)) {
526 		/*
527 		 * Interrupt number '0' is a high-level interrupt.
528 		 * At this point you either add a special interrupt
529 		 * handler that triggers a soft interrupt at a lower level,
530 		 * or - more simply and appropriately here - you just
531 		 * fail the attach.
532 		 */
533 		cmn_err(CE_WARN,
534 		"socal%d attach failed: hilevel interrupt unsupported",
535 		    instance);
536 		return (DDI_FAILURE);
537 	}
538 
539 	/* Allocate soft state. */
540 	if (ddi_soft_state_zalloc(socal_soft_state_p, instance)
541 	    != DDI_SUCCESS) {
542 		cmn_err(CE_WARN, "socal%d attach failed: alloc soft state",
543 		    instance);
544 		return (DDI_FAILURE);
545 	}
546 	DEBUGF(4, (CE_CONT, "socal%d attach: allocated soft state\n",
547 	    instance));
548 
549 	/*
550 	 * Initialize the state structure.
551 	 */
552 	socalp = ddi_get_soft_state(socal_soft_state_p, instance);
553 	if (socalp == (socal_state_t *)NULL) {
554 		cmn_err(CE_WARN, "socal%d attach failed: bad soft state",
555 		    instance);
556 		return (DDI_FAILURE);
557 	}
558 	DEBUGF(4, (CE_CONT, "socal%d: attach: soc soft state ptr=0x%p\n",
559 	    instance, socalp));
560 
561 	socalp->dip = dip;
562 	socallim = &default_socallim;
563 	porta = &socalp->port_state[0];
564 	portb = &socalp->port_state[1];
565 
566 	/* Get the full path name for displaying error messages */
567 	cptr = ddi_pathname(dip, buf);
568 	(void) strcpy(socalp->socal_name, cptr);
569 
570 	porta->sp_unsol_cb = NULL;
571 	portb->sp_unsol_cb = NULL;
572 	porta->sp_port = 0;
573 	portb->sp_port = 1;
574 	porta->sp_board = socalp;
575 	portb->sp_board = socalp;
576 
577 	porta->sp_lilpmap_valid = 0;
578 	portb->sp_lilpmap_valid = 0;
579 
580 	/*
581 	 * If an hard loop-id property is present, then the port is going
582 	 * to be used in target-mode so set the target-mode flag.
583 	 */
584 	loop_id = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
585 	    "port0-loop-id", 127);
586 	if (loop_id >= 0 && loop_id <= 126) {
587 		porta->sp_status |= PORT_TARGET_MODE;
588 		porta->sp_hard_alpa = socal_switch_to_alpa[loop_id];
589 	} else porta->sp_hard_alpa = 0xfe;
590 
591 	loop_id = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
592 	    "port1-loop-id", 127);
593 	if (loop_id >= 0 && loop_id <= 126) {
594 		portb->sp_status |= PORT_TARGET_MODE;
595 		portb->sp_hard_alpa = socal_switch_to_alpa[loop_id];
596 	} else portb->sp_hard_alpa = 0xfe;
597 
598 	/* Get out Node wwn and calculate port wwns */
599 	rval = ddi_prop_op(DDI_DEV_T_ANY, dip,
600 	    PROP_LEN_AND_VAL_ALLOC, DDI_PROP_DONTPASS |
601 	    DDI_PROP_CANSLEEP, "wwn", (caddr_t)&wwn, &i);
602 
603 	if ((rval != DDI_PROP_SUCCESS) || (i < FC_WWN_SIZE) ||
604 	    (bcmp(wwn, "00000000", FC_WWN_SIZE) == 0)) {
605 		(void) localetheraddr((struct ether_addr *)NULL, &ourmacaddr);
606 
607 		bcopy((caddr_t)&ourmacaddr, (caddr_t)&s, sizeof (short));
608 		socalp->socal_n_wwn.w.wwn_hi = s;
609 		bcopy((caddr_t)&ourmacaddr+2,
610 		    (caddr_t)&socalp->socal_n_wwn.w.wwn_lo,
611 		    sizeof (uint_t));
612 		socalp->socal_n_wwn.w.naa_id = NAA_ID_IEEE;
613 		socalp->socal_n_wwn.w.nport_id = 0;
614 	} else {
615 		bcopy((caddr_t)wwn, (caddr_t)&socalp->socal_n_wwn, FC_WWN_SIZE);
616 	}
617 
618 	if (rval == DDI_SUCCESS)
619 		kmem_free((void *)wwn, i);
620 
621 	for (i = 0; i < FC_WWN_SIZE; i++) {
622 		(void) sprintf(&socalp->socal_stats.node_wwn[i << 1],
623 		    "%02x", socalp->socal_n_wwn.raw_wwn[i]);
624 	}
625 	DEBUGF(4, (CE_CONT, "socal%d attach: node wwn: %s\n",
626 	    instance, socalp->socal_stats.node_wwn));
627 
628 	bcopy((caddr_t)&socalp->socal_n_wwn, (caddr_t)&porta->sp_p_wwn,
629 	    sizeof (la_wwn_t));
630 	bcopy((caddr_t)&socalp->socal_n_wwn, (caddr_t)&portb->sp_p_wwn,
631 	    sizeof (la_wwn_t));
632 	porta->sp_p_wwn.w.naa_id = NAA_ID_IEEE_EXTENDED;
633 	portb->sp_p_wwn.w.naa_id = NAA_ID_IEEE_EXTENDED;
634 	porta->sp_p_wwn.w.nport_id = instance*2;
635 	portb->sp_p_wwn.w.nport_id = instance*2+1;
636 
637 	for (i = 0; i < FC_WWN_SIZE; i++) {
638 		(void) sprintf(&socalp->socal_stats.port_wwn[0][i << 1],
639 		    "%02x", porta->sp_p_wwn.raw_wwn[i]);
640 		(void) sprintf(&socalp->socal_stats.port_wwn[1][i << 1],
641 		    "%02x", portb->sp_p_wwn.raw_wwn[i]);
642 	}
643 	DEBUGF(4, (CE_CONT, "socal%d attach: porta wwn: %s\n",
644 	    instance, socalp->socal_stats.port_wwn[0]));
645 	DEBUGF(4, (CE_CONT, "socal%d attach: portb wwn: %s\n",
646 	    instance, socalp->socal_stats.port_wwn[1]));
647 
648 	if ((porta->sp_transport = (fcal_transport_t *)
649 	    kmem_zalloc(sizeof (fcal_transport_t), KM_SLEEP)) == NULL) {
650 		socal_disp_err(socalp, CE_WARN, "attach.4011",
651 		    "attach failed: unable to alloc xport struct");
652 		goto fail;
653 	}
654 
655 	if ((portb->sp_transport = (fcal_transport_t *)
656 	    kmem_zalloc(sizeof (fcal_transport_t), KM_SLEEP)) == NULL) {
657 		socal_disp_err(socalp, CE_WARN, "attach.4012",
658 		    "attach failed: unable to alloc xport struct");
659 		goto fail;
660 	}
661 	DEBUGF(4, (CE_CONT, "socal%d attach: allocated transport structs\n",
662 	    instance));
663 
664 	/*
665 	 * Map the external ram and registers for SOC+.
666 	 * Note: Soc+ sbus host adapter provides 3 register definition
667 	 * but on-board Soc+'s  may have only one register definition.
668 	 */
669 	if ((ddi_dev_nregs(dip, &i) == DDI_SUCCESS) && (i == 1)) {
670 		/* Map XRAM */
671 		if (ddi_map_regs(dip, 0, &socalp->socal_xrp, 0, 0)
672 		    != DDI_SUCCESS) {
673 			socalp->socal_xrp = NULL;
674 			socal_disp_err(socalp, CE_WARN, "attach.4020",
675 			    "attach failed: unable to map XRAM");
676 			goto fail;
677 		}
678 		/* Map registers */
679 		socalp->socal_rp = (socal_reg_t *)(socalp->socal_xrp +
680 		    SOCAL_XRAM_SIZE);
681 	} else {
682 		/* Map EEPROM */
683 		if (ddi_map_regs(dip, 0, &socalp->socal_eeprom, 0, 0) !=
684 		    DDI_SUCCESS) {
685 			socalp->socal_eeprom = NULL;
686 			socal_disp_err(socalp, CE_WARN, "attach.4010",
687 			    "attach failed: unable to map eeprom");
688 			goto fail;
689 		}
690 	DEBUGF(4, (CE_CONT, "socal%d attach: mapped eeprom 0x%p\n",
691 	    instance, socalp->socal_eeprom));
692 		/* Map XRAM */
693 		if (ddi_map_regs(dip, 1, &socalp->socal_xrp, 0, 0) !=
694 		    DDI_SUCCESS) {
695 			socalp->socal_xrp = NULL;
696 			socal_disp_err(socalp, CE_WARN, "attach.4020",
697 			    "attach failed: unable to map XRAM");
698 			goto fail;
699 		}
700 	DEBUGF(4, (CE_CONT, "socal%d attach: mapped xram 0x%p\n",
701 	    instance, socalp->socal_xrp));
702 		/* Map registers */
703 		if (ddi_map_regs(dip, 2, (caddr_t *)&socalp->socal_rp, 0, 0) !=
704 		    DDI_SUCCESS) {
705 			socalp->socal_rp = NULL;
706 			socal_disp_err(socalp, CE_WARN, "attach.4030",
707 			    "attach failed: unable to map registers");
708 			goto fail;
709 		}
710 	DEBUGF(4, (CE_CONT, "socal%d attach: mapped regs 0x%p\n",
711 	    instance, socalp->socal_rp));
712 	}
713 	/*
714 	 * Check to see we really have a SOC+ Host Adapter card installed
715 	 */
716 	if (ddi_peek32(dip, (int32_t *)&socalp->socal_rp->socal_csr.w,
717 	    (int32_t *)NULL) != DDI_SUCCESS) {
718 		socal_disp_err(socalp, CE_WARN, "attach.4040",
719 		    "attach failed: unable to access status register");
720 		goto fail;
721 	}
722 	/* now that we have our registers mapped make sure soc+ reset */
723 	socal_disable(socalp);
724 
725 	/* try defacing a spot in XRAM */
726 	if (ddi_poke32(dip, (int32_t *)(socalp->socal_xrp + SOCAL_XRAM_UCODE),
727 	    0xdefaced) != DDI_SUCCESS) {
728 		socal_disp_err(socalp, CE_WARN, "attach.4050",
729 		    "attach failed: unable to write host adapter XRAM");
730 		goto fail;
731 	}
732 
733 	/* see if it stayed defaced */
734 	if (ddi_peek32(dip, (int32_t *)(socalp->socal_xrp + SOCAL_XRAM_UCODE),
735 	    (int32_t *)&y)
736 	    != DDI_SUCCESS) {
737 		socal_disp_err(socalp, CE_WARN, "attach.4051",
738 		    "attach failed: unable to access host adapter XRAM");
739 		goto fail;
740 	}
741 
742 #ifdef DEBUG
743 	for (i = 0; i < 4; i++) {
744 		socalp->socal_rp->socal_cr.w &=
745 		    ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
746 		socalp->socal_rp->socal_cr.w |= i<<24;
747 		cptr = (char *)(socal_xrambuf + (i*0x10000));
748 		bcopy((caddr_t)socalp->socal_xrp, (caddr_t)cptr, 0x10000);
749 	}
750 	socalp->socal_rp->socal_cr.w &= ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
751 #endif
752 
753 	DEBUGF(4, (CE_CONT, "socal%d attach: read xram\n", instance));
754 
755 	if (y != 0xdefaced) {
756 		socal_disp_err(socalp, CE_WARN, "attach.4052",
757 		    "attach failed: read/write mismatch in XRAM");
758 		goto fail;
759 	}
760 
761 	/* Point to the SOC XRAM CQ Descriptor locations. */
762 	socalp->xram_reqp = (soc_cq_t *)(socalp->socal_xrp +
763 	    SOCAL_XRAM_REQ_DESC);
764 	socalp->xram_rspp = (soc_cq_t *)(socalp->socal_xrp +
765 	    SOCAL_XRAM_RSP_DESC);
766 
767 	if ((socalp->socal_ksp = kstat_create("socal", instance, "statistics",
768 	    "controller", KSTAT_TYPE_RAW, sizeof (struct socal_stats),
769 	    KSTAT_FLAG_VIRTUAL)) == NULL) {
770 		socal_disp_err(socalp, CE_WARN, "attach.4053",
771 		    "unable to create kstats");
772 	} else {
773 		socalp->socal_stats.version = 2;
774 		(void) sprintf(socalp->socal_stats.drvr_name,
775 		    "%s: %s", SOCAL_NAME, socal_version);
776 		socalp->socal_stats.pstats[0].port = 0;
777 		socalp->socal_stats.pstats[1].port = 1;
778 		socalp->socal_ksp->ks_data = (void *)&socalp->socal_stats;
779 		kstat_install(socalp->socal_ksp);
780 	}
781 
782 	/*
783 	 * Install a dummy interrupt routine.
784 	 */
785 	if (ddi_add_intr(dip,
786 	    (uint_t)0,
787 	    &socalp->iblkc,
788 	    &socalp->idevc,
789 	    socal_dummy_intr,
790 	    (caddr_t)socalp) != DDI_SUCCESS) {
791 			socal_disp_err(socalp, CE_WARN, "attach.4060",
792 			"attach failed: unable to install interrupt handler");
793 			goto fail;
794 	}
795 
796 	ddi_set_driver_private(dip, socalp);
797 
798 	/* initialize the interrupt mutex */
799 	mutex_init(&socalp->k_imr_mtx, NULL, MUTEX_DRIVER,
800 	    (void *)socalp->iblkc);
801 
802 	mutex_init(&socalp->board_mtx, NULL, MUTEX_DRIVER,
803 	    (void *)socalp->iblkc);
804 	mutex_init(&socalp->ioctl_mtx, NULL, MUTEX_DRIVER,
805 	    (void *)socalp->iblkc);
806 
807 	/* initialize the abort mutex */
808 	mutex_init(&socalp->abort_mtx, NULL, MUTEX_DRIVER,
809 	    (void *)socalp->iblkc);
810 
811 	cv_init(&socalp->board_cv, NULL, CV_DRIVER, NULL);
812 	DEBUGF(4, (CE_CONT,
813 	    "socal%d: attach: inited imr mutex, board mutex, board cv\n",
814 	    instance));
815 
816 	/* init the port mutexes */
817 	mutex_init(&porta->sp_mtx, NULL, MUTEX_DRIVER, socalp->iblkc);
818 	cv_init(&porta->sp_cv, NULL, CV_DRIVER, NULL);
819 	mutex_init(&portb->sp_mtx, NULL, MUTEX_DRIVER, socalp->iblkc);
820 	cv_init(&portb->sp_cv, NULL, CV_DRIVER, NULL);
821 	DEBUGF(4, (CE_CONT, "socal%d: attach: inited port mutexes and cvs\n",
822 	    instance));
823 
824 	/* get local copy of service params */
825 	socal_wcopy((uint_t *)socalp->socal_xrp + SOCAL_XRAM_SERV_PARAMS,
826 	    (uint_t *)socalp->socal_service_params, SOCAL_SVC_LENGTH);
827 	DEBUGF(4, (CE_CONT, "socal%d: attach: got service params\n", instance));
828 	/*
829 	 * Initailize the FCAL transport interface.
830 	 */
831 	socal_init_transport_interface(socalp);
832 	DEBUGF(4, (CE_CONT, "socal%d: attach: initalized transport interface\n",
833 	    instance));
834 
835 	/*
836 	 * Allocate request and response queues and init their mutexs.
837 	 */
838 	for (i = 0; i < SOCAL_N_CQS; i++) {
839 		if (socal_cqalloc_init(socalp, i) != FCAL_SUCCESS) {
840 			goto fail;
841 		}
842 	}
843 	DEBUGF(4, (CE_CONT, "socal%d: attach: allocated cqs\n", instance));
844 
845 	/*
846 	 * Adjust the burst size we'll use.
847 	 */
848 	burstsize = ddi_dma_burstsizes(socalp->request[0].skc_dhandle);
849 	DEBUGF(4, (CE_CONT, "socal%d: attach: burstsize = 0x%x\n",
850 	    instance, burstsize));
851 	j = burstsize & BURSTSIZE_MASK;
852 	for (i = 0; socal_burst32_table[i] != SOCAL_CR_BURST_64; i++)
853 		if (!(j >>= 1)) break;
854 
855 	socalp->socal_cfg = (socalp->socal_cfg & ~SOCAL_CR_SBUS_BURST_SIZE_MASK)
856 	    | socal_burst32_table[i];
857 
858 	if (socal_64bitsbus) {
859 		if (ddi_dma_set_sbus64(socalp->request[0].skc_dhandle,
860 		    socal_dma_attr.dma_attr_burstsizes | BURST128) ==
861 		    DDI_SUCCESS) {
862 			DEBUGF(4, (CE_CONT, "socal%d: enabled 64 bit sbus\n",
863 			    instance));
864 			socalp->socal_cfg |= SOCAL_CR_SBUS_ENHANCED;
865 			burstsize = ddi_dma_burstsizes(socalp->request[0].
866 			    skc_dhandle);
867 		DEBUGF(4, (CE_CONT, "socal%d: attach: 64bit burstsize = 0x%x\n",
868 		    instance, burstsize));
869 			j = burstsize & BURSTSIZE_MASK;
870 			for (i = 0; socal_burst64_table[i] !=
871 			    (SOCAL_CR_BURST_128 << 8); i++)
872 				if (!(j >>= 1))
873 					break;
874 
875 			socalp->socal_cfg = (socalp->socal_cfg &
876 			    ~SOCAL_CR_SBUS_BURST_SIZE_64BIT_MASK) |
877 			    socal_burst64_table[i];
878 		}
879 	}
880 
881 	ddi_remove_intr(dip, 0, socalp->iblkc);
882 	socalp->iblkc = (void *)NULL;
883 	/*
884 	 * Install the interrupt routine.
885 	 */
886 	if (ddi_add_intr(dip,
887 	    (uint_t)0,
888 	    &socalp->iblkc,
889 	    &socalp->idevc,
890 	    socal_intr,
891 	    (caddr_t)socalp) != DDI_SUCCESS) {
892 			socal_disp_err(socalp, CE_WARN, "attach.4060",
893 			"attach failed: unable to install interrupt handler");
894 			goto fail;
895 	}
896 
897 	DEBUGF(4, (CE_CONT, "socal%d: attach: set config reg %x\n",
898 	    instance, socalp->socal_cfg));
899 
900 	if (ddi_create_minor_node(dip, SOCAL_PORTA_NAME, S_IFCHR,
901 	    instance*N_SOCAL_NPORTS, SOCAL_NT_PORT, 0) != DDI_SUCCESS)
902 		goto fail;
903 	if (ddi_create_minor_node(dip, SOCAL_PORTB_NAME, S_IFCHR,
904 	    instance*N_SOCAL_NPORTS+1, SOCAL_NT_PORT, 0) != DDI_SUCCESS)
905 		goto fail;
906 
907 	if (socal_start(socalp) != FCAL_SUCCESS)
908 		goto fail;
909 	DEBUGF(4, (CE_CONT, "socal%d: attach: soc+ started\n", instance));
910 
911 	ddi_report_dev(dip);
912 
913 	DEBUGF(2, (CE_CONT, "socal%d: attach O.K.\n\n", instance));
914 
915 	return (DDI_SUCCESS);
916 
917 fail:
918 	DEBUGF(4, (CE_CONT, "socal%d: attach: DDI_FAILURE\n", instance));
919 
920 	/* Make sure soc reset */
921 	socal_disable(socalp);
922 
923 	/* let detach do the dirty work */
924 	(void) socal_dodetach(dip);
925 
926 	return (DDI_FAILURE);
927 }
928 
929 static int
930 socal_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
931 {
932 	int		resp;
933 	socal_state_t	*socalp;
934 	int		i;
935 
936 
937 	switch (cmd) {
938 
939 	case DDI_SUSPEND:
940 		DEBUGF(4, (CE_CONT, "socal: suspend called\n"));
941 
942 		if ((socalp = ddi_get_driver_private(dip)) == NULL)
943 			return (DDI_FAILURE);
944 
945 		/*
946 		 * If any of the ports are in target-mode, don't suspend
947 		 */
948 		for (i = 0; i < N_SOCAL_NPORTS; i++) {
949 			if (socalp->port_state[i].sp_status & PORT_TARGET_MODE)
950 				return (DDI_FAILURE);
951 		}
952 
953 		/* do not restart socal after reset */
954 		socal_force_reset((void *)socalp, 0, DONT_RESET_PORT);
955 
956 		return (DDI_SUCCESS);
957 
958 	case DDI_DETACH:
959 		DEBUGF(4, (CE_CONT, "socal: detach called\n"));
960 		resp = socal_dodetach(dip);
961 		if (resp == DDI_SUCCESS)
962 			ddi_set_driver_private(dip, NULL);
963 		return (resp);
964 
965 	default:
966 		return (DDI_FAILURE);
967 	}
968 }
969 
970 static int
971 socal_dodetach(dev_info_t *dip)
972 {
973 
974 	int		instance = ddi_get_instance(dip);
975 	int		i;
976 	socal_state_t	*socalp;
977 	socal_port_t	*portp;
978 	socal_unsol_cb_t	*cb, *cbn = NULL;
979 
980 	/* Get the soft state struct. */
981 	if ((socalp = ddi_get_soft_state(socal_soft_state_p, instance)) == 0) {
982 		return (DDI_FAILURE);
983 	}
984 
985 	/*
986 	 * If somebody is still attached to us from above fail
987 	 * detach.
988 	 */
989 	mutex_enter(&socalp->board_mtx);
990 	if (socalp->socal_busy > 0) {
991 		mutex_exit(&socalp->board_mtx);
992 		return (DDI_FAILURE);
993 	}
994 	/* mark socal_busy = -1 to disallow sftm attach */
995 	socalp->socal_busy = -1;
996 	mutex_exit(&socalp->board_mtx);
997 
998 	/* Make sure soc+ reset */
999 	mutex_enter(&socalp->k_imr_mtx);
1000 	socal_disable(socalp);
1001 	mutex_exit(&socalp->k_imr_mtx);
1002 
1003 	/* remove soc+ interrupt */
1004 	if (socalp->iblkc != (void *)NULL) {
1005 		ddi_remove_intr(dip, (uint_t)0, socalp->iblkc);
1006 		DEBUGF(2, (CE_CONT,
1007 		    "socal%d: detach: Removed SOC+ interrupt from ddi\n",
1008 		    instance));
1009 	}
1010 
1011 	for (i = 0; i < N_SOCAL_NPORTS; i++) {
1012 		portp = &socalp->port_state[i];
1013 		mutex_destroy(&portp->sp_mtx);
1014 		cv_destroy(&portp->sp_cv);
1015 		mutex_destroy(&portp->sp_transport->fcal_mtx);
1016 		cv_destroy(&portp->sp_transport->fcal_cv);
1017 		kmem_free((void *)portp->sp_transport,
1018 		    sizeof (fcal_transport_t));
1019 		for (cb = portp->sp_unsol_cb; cb != (socal_unsol_cb_t *)NULL;
1020 		    cb = cbn) {
1021 			cbn = cb->next;
1022 			kmem_free((void *)cb, sizeof (socal_unsol_cb_t));
1023 		}
1024 		portp->sp_unsol_cb = (socal_unsol_cb_t *)NULL;
1025 	}
1026 
1027 	/*
1028 	 * Free request queues, if allocated
1029 	 */
1030 	for (i = 0; i < SOCAL_N_CQS; i++) {
1031 		/* Free the queues and destroy their mutexes. */
1032 		mutex_destroy(&socalp->request[i].skc_mtx);
1033 		mutex_destroy(&socalp->response[i].skc_mtx);
1034 		cv_destroy(&socalp->request[i].skc_cv);
1035 		cv_destroy(&socalp->response[i].skc_cv);
1036 
1037 		if (socalp->request[i].skc_dhandle) {
1038 			(void) ddi_dma_unbind_handle(socalp->
1039 			    request[i].skc_dhandle);
1040 			ddi_dma_free_handle(&socalp->request[i].skc_dhandle);
1041 		}
1042 		if (socalp->request[i].skc_cq_raw) {
1043 			ddi_dma_mem_free(&socalp->request[i].skc_acchandle);
1044 			socalp->request[i].skc_cq_raw = NULL;
1045 			socalp->request[i].skc_cq = NULL;
1046 		}
1047 		if (socalp->response[i].skc_dhandle) {
1048 			(void) ddi_dma_unbind_handle(socalp->
1049 			    response[i].skc_dhandle);
1050 			ddi_dma_free_handle(&socalp->response[i].skc_dhandle);
1051 		}
1052 		if (socalp->response[i].skc_cq_raw) {
1053 			ddi_dma_mem_free(&socalp->response[i].skc_acchandle);
1054 			socalp->response[i].skc_cq_raw = NULL;
1055 			socalp->response[i].skc_cq = NULL;
1056 		}
1057 		if (socalp->request[i].deferred_intr_timeoutid) {
1058 			(void) untimeout(socalp->
1059 			    request[i].deferred_intr_timeoutid);
1060 		}
1061 		if (socalp->response[i].deferred_intr_timeoutid) {
1062 			(void) untimeout(socalp->
1063 			    response[i].deferred_intr_timeoutid);
1064 		}
1065 	}
1066 
1067 	mutex_destroy(&socalp->abort_mtx);
1068 	mutex_destroy(&socalp->board_mtx);
1069 	mutex_destroy(&socalp->ioctl_mtx);
1070 	cv_destroy(&socalp->board_cv);
1071 
1072 	/*
1073 	 * Free soc data buffer pool
1074 	 */
1075 	if (socalp->pool_dhandle) {
1076 		(void) ddi_dma_unbind_handle(socalp->pool_dhandle);
1077 		ddi_dma_free_handle(&socalp->pool_dhandle);
1078 	}
1079 	if (socalp->pool) {
1080 		ddi_dma_mem_free(&socalp->pool_acchandle);
1081 	}
1082 
1083 	/* release register maps */
1084 	/* Unmap EEPROM */
1085 	if (socalp->socal_eeprom != NULL) {
1086 		ddi_unmap_regs(dip, 0, &socalp->socal_eeprom, 0, 0);
1087 	}
1088 
1089 	/* Unmap XRAM */
1090 	if (socalp->socal_xrp != NULL) {
1091 		ddi_unmap_regs(dip, 1, &socalp->socal_xrp, 0, 0);
1092 	}
1093 
1094 	/* Unmap registers */
1095 	if (socalp->socal_rp != NULL) {
1096 		ddi_unmap_regs(dip, 2, (caddr_t *)&socalp->socal_rp, 0, 0);
1097 	}
1098 
1099 	if (socalp->socal_ksp != NULL)
1100 		kstat_delete(socalp->socal_ksp);
1101 
1102 	mutex_destroy(&socalp->k_imr_mtx);
1103 
1104 	ddi_remove_minor_node(dip, NULL);
1105 
1106 	ddi_soft_state_free(socal_soft_state_p, instance);
1107 
1108 	return (DDI_SUCCESS);
1109 }
1110 
1111 
1112 int
1113 socal_bus_ctl(dev_info_t *dip, dev_info_t *rip, ddi_ctl_enum_t op,
1114 	void *a, void *v)
1115 {
1116 	int		port;
1117 
1118 
1119 	switch (op) {
1120 	case DDI_CTLOPS_REPORTDEV:
1121 		port = ddi_getprop(DDI_DEV_T_ANY, rip, DDI_PROP_DONTPASS,
1122 		    SOCAL_PORT_NO_PROP, -1);
1123 		if ((port < 0) || (port > 1)) {
1124 			port = ddi_getprop(DDI_DEV_T_ANY, rip,
1125 			    DDI_PROP_DONTPASS, SOCAL_ALT_PORT_NO_PROP, -1);
1126 		}
1127 		/* log text identifying this driver (d) & its child (r) */
1128 		cmn_err(CE_CONT, "?%s%d at %s%d: socal_port %d\n",
1129 		    ddi_driver_name(rip), ddi_get_instance(rip),
1130 		    ddi_driver_name(dip), ddi_get_instance(dip),
1131 		    port);
1132 		break;
1133 
1134 	case DDI_CTLOPS_INITCHILD: {
1135 		dev_info_t	*child_dip = (dev_info_t *)a;
1136 		char		name[MAXNAMELEN];
1137 		socal_state_t	*socalp;
1138 
1139 		if ((socalp = ddi_get_driver_private(dip)) == NULL)
1140 			return (DDI_FAILURE);
1141 
1142 		port = ddi_getprop(DDI_DEV_T_ANY, child_dip,
1143 		    DDI_PROP_DONTPASS, SOCAL_PORT_NO_PROP, -1);
1144 
1145 		if ((port < 0) || (port > 1)) {
1146 			port = ddi_getprop(DDI_DEV_T_ANY, child_dip,
1147 			    DDI_PROP_DONTPASS, SOCAL_ALT_PORT_NO_PROP, -1);
1148 			if ((port < 0) || (port > 1)) {
1149 				return (DDI_NOT_WELL_FORMED);
1150 			}
1151 		}
1152 		mutex_enter(&socalp->board_mtx);
1153 		mutex_enter(&socalp->port_state[port].sp_mtx);
1154 		if (socalp->port_state[port].sp_status &
1155 		    (PORT_CHILD_INIT | PORT_TARGET_MODE)) {
1156 			mutex_exit(&socalp->port_state[port].sp_mtx);
1157 			mutex_exit(&socalp->board_mtx);
1158 			return (DDI_FAILURE);
1159 		}
1160 		socalp->socal_busy++;
1161 		socalp->port_state[port].sp_status |= PORT_CHILD_INIT;
1162 		mutex_exit(&socalp->port_state[port].sp_mtx);
1163 		mutex_exit(&socalp->board_mtx);
1164 		ddi_set_parent_data(child_dip,
1165 		    socalp->port_state[port].sp_transport);
1166 		(void) sprintf((char *)name, "%x,0", port);
1167 		ddi_set_name_addr(child_dip, name);
1168 		break;
1169 	}
1170 
1171 	case DDI_CTLOPS_UNINITCHILD: {
1172 		dev_info_t	*child_dip = (dev_info_t *)a;
1173 		socal_state_t	*socalp;
1174 
1175 		socalp = ddi_get_driver_private(dip);
1176 		port = ddi_getprop(DDI_DEV_T_ANY, child_dip,
1177 		    DDI_PROP_DONTPASS, SOCAL_PORT_NO_PROP, -1);
1178 
1179 		if ((port < 0) || (port > 1)) {
1180 			port = ddi_getprop(DDI_DEV_T_ANY, child_dip,
1181 			    DDI_PROP_DONTPASS, SOCAL_ALT_PORT_NO_PROP, -1);
1182 			if ((port < 0) || (port > 1)) {
1183 				return (DDI_NOT_WELL_FORMED);
1184 			}
1185 		}
1186 
1187 		ddi_set_parent_data(child_dip, NULL);
1188 		(void) ddi_set_name_addr(child_dip, NULL);
1189 		mutex_enter(&socalp->board_mtx);
1190 		mutex_enter(&socalp->port_state[port].sp_mtx);
1191 		socalp->socal_busy--;
1192 		socalp->port_state[port].sp_status &= ~PORT_CHILD_INIT;
1193 		mutex_exit(&socalp->port_state[port].sp_mtx);
1194 		mutex_exit(&socalp->board_mtx);
1195 
1196 		break;
1197 	}
1198 
1199 	case DDI_CTLOPS_IOMIN: {
1200 		int val;
1201 
1202 		val = *((int *)v);
1203 		val = maxbit(val, socallim->dlim_minxfer);
1204 		/*
1205 		 * The 'arg' value of nonzero indicates 'streaming' mode.
1206 		 * If in streaming mode, pick the largest of our burstsizes
1207 		 * available and say that that is our minimum value (modulo
1208 		 * what minxfer is).
1209 		 */
1210 		if ((int)(uintptr_t)a) {
1211 			val = maxbit(val,
1212 			    1<<(ddi_fls(socallim->dlim_burstsizes)-1));
1213 		} else {
1214 			val = maxbit(val,
1215 			    1<<(ddi_ffs(socallim->dlim_burstsizes)-1));
1216 		}
1217 
1218 		*((int *)v) = val;
1219 		return (ddi_ctlops(dip, rip, op, a, v));
1220 	}
1221 
1222 	/*
1223 	 * These ops are not available on this nexus.
1224 	 */
1225 
1226 	case DDI_CTLOPS_DMAPMAPC:
1227 	case DDI_CTLOPS_REGSIZE:
1228 	case DDI_CTLOPS_NREGS:
1229 	case DDI_CTLOPS_AFFINITY:
1230 	case DDI_CTLOPS_SIDDEV:
1231 	case DDI_CTLOPS_POKE:
1232 	case DDI_CTLOPS_PEEK:
1233 		return (DDI_FAILURE);
1234 
1235 	case DDI_CTLOPS_SLAVEONLY:
1236 	case DDI_CTLOPS_REPORTINT:
1237 	default:
1238 		/*
1239 		 * Remaining requests get passed up to our parent
1240 		 */
1241 		DEBUGF(2, (CE_CONT, "%s%d: op (%d) from %s%d\n",
1242 		    ddi_get_name(dip), ddi_get_instance(dip),
1243 		    op, ddi_get_name(rip), ddi_get_instance(rip)));
1244 		return (ddi_ctlops(dip, rip, op, a, v));
1245 	}
1246 
1247 	return (DDI_SUCCESS);
1248 }
1249 
1250 
1251 /*ARGSUSED*/
1252 /*
1253  * int
1254  * socal_getinfo() - Given the device number, return the devinfo
1255  * 	pointer or the instance number.  Note: this routine must be
1256  * 	successful on DDI_INFO_DEVT2INSTANCE even before attach.
1257  */
1258 int
1259 socal_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
1260 	void **result)
1261 {
1262 	int instance;
1263 	socal_state_t *socalp;
1264 
1265 	instance = getminor((dev_t)arg) / 2;
1266 
1267 	switch (cmd) {
1268 	case DDI_INFO_DEVT2DEVINFO:
1269 		socalp = ddi_get_soft_state(socal_soft_state_p, instance);
1270 		if (socalp)
1271 			*result = socalp->dip;
1272 		else
1273 			*result = NULL;
1274 		break;
1275 
1276 	case DDI_INFO_DEVT2INSTANCE:
1277 		*result = (void *)(uintptr_t)instance;
1278 		break;
1279 
1280 	default:
1281 		return (DDI_FAILURE);
1282 	}
1283 
1284 	return (DDI_SUCCESS);
1285 }
1286 
1287 /*ARGSUSED*/
1288 int
1289 socal_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
1290 {
1291 	int 	instance = getminor(*devp)/2;
1292 	socal_state_t	*socalp =
1293 	    ddi_get_soft_state(socal_soft_state_p, instance);
1294 	socal_port_t	*port_statep;
1295 	int		port;
1296 
1297 	if (socalp == NULL)
1298 		return (ENXIO);
1299 
1300 	port = getminor(*devp)%2;
1301 	port_statep = &socalp->port_state[port];
1302 
1303 	mutex_enter(&port_statep->sp_mtx);
1304 	port_statep->sp_status |= PORT_OPEN;
1305 	mutex_exit(&port_statep->sp_mtx);
1306 	DEBUGF(2, (CE_CONT,
1307 	    "socal%d: open of port %d\n", instance, port));
1308 	return (0);
1309 }
1310 
1311 /*ARGSUSED*/
1312 int
1313 socal_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
1314 {
1315 	int 	instance = getminor(dev)/2;
1316 	socal_state_t	*socalp =
1317 	    ddi_get_soft_state(socal_soft_state_p, instance);
1318 	socal_port_t	*port_statep;
1319 	int		port;
1320 
1321 	port = getminor(dev)%2;
1322 	port_statep = &socalp->port_state[port];
1323 
1324 	mutex_enter(&port_statep->sp_mtx);
1325 	port_statep->sp_status &= ~PORT_OPEN;
1326 	mutex_exit(&port_statep->sp_mtx);
1327 	DEBUGF(2, (CE_CONT,
1328 	    "socal%d: clsoe of port %d\n", instance, port));
1329 	return (0);
1330 }
1331 
1332 /*ARGSUSED*/
1333 int
1334 socal_ioctl(dev_t dev,
1335     int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
1336 {
1337 	int 	instance = getminor(dev)/2;
1338 	socal_state_t	*socalp =
1339 	    ddi_get_soft_state(socal_soft_state_p, instance);
1340 	int		port;
1341 	socal_port_t	*port_statep;
1342 	int 		i;
1343 	uint_t		r;
1344 	int		offset;
1345 	int 		retval = FCAL_SUCCESS;
1346 	la_els_adisc_t		*adisc_pl;
1347 	la_els_rls_reply_t	*rls_pl;
1348 	dev_info_t	*dip;
1349 	char		*buffer, tmp[10];
1350 	struct socal_fm_version ver;
1351 #ifdef _MULTI_DATAMODEL
1352 	struct socal_fm_version32 {
1353 		uint_t	fcode_ver_len;
1354 		uint_t	mcode_ver_len;
1355 		uint_t	prom_ver_len;
1356 		caddr32_t	fcode_ver;
1357 		caddr32_t	mcode_ver;
1358 		caddr32_t	prom_ver;
1359 	} ver32;
1360 	uint_t		dm32 = 0;
1361 #endif
1362 
1363 	uchar_t		*flb_pl;
1364 	flb_hdr_t	*flb_hdr;
1365 	uint_t		flb_size;
1366 
1367 	if (socalp == NULL)
1368 		return (ENXIO);
1369 
1370 	DEBUGF(4, (CE_CONT, "socal%d ioctl: got command %x\n", instance, cmd));
1371 	port = getminor(dev)%2;
1372 
1373 	switch (cmd) {
1374 	case FCIO_FCODE_MCODE_VERSION:
1375 #ifdef _MULTI_DATAMODEL
1376 		switch (ddi_model_convert_from(mode & FMODELS)) {
1377 			case DDI_MODEL_ILP32:
1378 				dm32 = 1;
1379 				if (ddi_copyin((caddr_t)arg,
1380 				    (caddr_t)&ver32, sizeof (ver32),
1381 				    mode) == -1)
1382 					return (EFAULT);
1383 				ver.fcode_ver_len =
1384 				    ver32.fcode_ver_len;
1385 				ver.mcode_ver_len =
1386 				    ver32.mcode_ver_len;
1387 				ver.prom_ver_len =
1388 				    ver32.prom_ver_len;
1389 				ver.fcode_ver =
1390 				    (caddr_t)(uintptr_t)ver32.fcode_ver;
1391 				ver.mcode_ver =
1392 				    (caddr_t)(uintptr_t)ver32.mcode_ver;
1393 				ver.prom_ver =
1394 				    (caddr_t)(uintptr_t)ver32.prom_ver;
1395 				break;
1396 			case DDI_MODEL_NONE:
1397 				if (ddi_copyin((caddr_t)arg,
1398 				    (caddr_t)&ver, sizeof (ver),
1399 				    mode) == -1)
1400 					return (EFAULT);
1401 		}
1402 #else /* _MULTI_DATAMODEL */
1403 		if (ddi_copyin((caddr_t)arg, (caddr_t)&ver,
1404 		    sizeof (ver), mode) == -1)
1405 			return (EFAULT);
1406 #endif /* _MULTI_DATAMODEL */
1407 		dip = socalp->dip;
1408 		if (ddi_prop_op(DDI_DEV_T_ANY, dip,
1409 		    PROP_LEN_AND_VAL_ALLOC, DDI_PROP_DONTPASS |
1410 		    DDI_PROP_CANSLEEP, "version", (caddr_t)&buffer,
1411 		    &i) != DDI_PROP_SUCCESS)
1412 			return (EIO);
1413 		if (i < ver.fcode_ver_len)
1414 			ver.fcode_ver_len = i;
1415 		if (ddi_copyout((caddr_t)buffer,
1416 		    (caddr_t)ver.fcode_ver, ver.fcode_ver_len,
1417 		    mode) == -1) {
1418 			kmem_free((caddr_t)buffer, i);
1419 			return (EFAULT);
1420 		}
1421 		kmem_free((caddr_t)buffer, i);
1422 		if (socalp->socal_eeprom) {
1423 			for (i = 0; i < SOCAL_N_CQS; i++) {
1424 				mutex_enter(
1425 				    &socalp->request[i].skc_mtx);
1426 				mutex_enter(
1427 				    &socalp->response[i].skc_mtx);
1428 			}
1429 			i = socalp->socal_rp->socal_cr.w;
1430 			socalp->socal_rp->socal_cr.w &=
1431 			    ~SOCAL_CR_EEPROM_BANK_MASK;
1432 			socalp->socal_rp->socal_cr.w |= 3 << 16;
1433 			if (ver.prom_ver_len > 10)
1434 				ver.prom_ver_len = 10;
1435 			bcopy((caddr_t)socalp->socal_eeprom + (unsigned)
1436 			    0xfff6, tmp, 10);
1437 			socalp->socal_rp->socal_cr.w  = i;
1438 			for (i = SOCAL_N_CQS-1; i >= 0; i--) {
1439 				mutex_exit(&socalp->request[i].skc_mtx);
1440 				mutex_exit(
1441 				    &socalp->response[i].skc_mtx);
1442 			}
1443 			if (ddi_copyout((caddr_t)tmp,
1444 			    (caddr_t)ver.prom_ver,
1445 			    ver.prom_ver_len, mode) == -1)
1446 				return (EFAULT);
1447 		} else {
1448 			ver.prom_ver_len = 0;
1449 		}
1450 		ver.mcode_ver_len = 0;
1451 #ifdef _MULTI_DATAMODEL
1452 		if (dm32) {
1453 			ver32.fcode_ver_len = ver.fcode_ver_len;
1454 			ver32.mcode_ver_len = ver.mcode_ver_len;
1455 			ver32.prom_ver_len = ver.prom_ver_len;
1456 			ver32.fcode_ver = (caddr32_t)(uintptr_t)
1457 			    ver.fcode_ver;
1458 			ver32.mcode_ver = (caddr32_t)(uintptr_t)
1459 			    ver.mcode_ver;
1460 			ver32.prom_ver = (caddr32_t)(uintptr_t)
1461 			    ver.prom_ver;
1462 			if (ddi_copyout((caddr_t)&ver32,
1463 			    (caddr_t)arg, sizeof (ver32),
1464 			    mode) == -1)
1465 				return (EFAULT);
1466 		} else
1467 #endif /* _MULTI_DATAMODEL */
1468 		if (ddi_copyout((caddr_t)&ver, (caddr_t)arg,
1469 		    sizeof (struct socal_fm_version), mode) == -1)
1470 			return (EFAULT);
1471 		break;
1472 	case FCIO_LOADUCODE:
1473 		mutex_enter(&socalp->k_imr_mtx);
1474 		socal_disable(socalp);
1475 		mutex_exit(&socalp->k_imr_mtx);
1476 		if (copyin((caddr_t)arg, (caddr_t)socal_ucode, 0x10000)
1477 		    == -1)
1478 			return (EFAULT);
1479 		/* restart socal after resetting */
1480 		(void) socal_force_reset((void *)socalp, 0,
1481 		    RESET_PORT);
1482 		break;
1483 	case FCIO_DUMPXRAM:
1484 		for (i = 0; i < SOCAL_N_CQS; i++) {
1485 			mutex_enter(&socalp->request[i].skc_mtx);
1486 			mutex_enter(&socalp->response[i].skc_mtx);
1487 		}
1488 		for (i = 0; i < 4; i++) {
1489 			offset = arg+(0x10000 * i);
1490 			socalp->socal_rp->socal_cr.w &=
1491 			    ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
1492 			socalp->socal_rp->socal_cr.w |= i<<24;
1493 			(void) copyout((caddr_t)socalp->socal_xrp,
1494 			    (caddr_t)(uintptr_t)offset, 0x10000);
1495 		}
1496 		socalp->socal_rp->socal_cr.w &=
1497 		    ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
1498 		for (i = SOCAL_N_CQS-1; i >= 0; i--) {
1499 			mutex_exit(&socalp->request[i].skc_mtx);
1500 			mutex_exit(&socalp->response[i].skc_mtx);
1501 		}
1502 		break;
1503 #ifdef DEBUG
1504 	case FCIO_DUMPXRAMBUF:
1505 		(void) copyout((caddr_t)socal_xrambuf, (caddr_t)arg,
1506 		    0x40000);
1507 		break;
1508 #endif
1509 	case FCIO_GETMAP:
1510 		mutex_enter(&socalp->ioctl_mtx);
1511 		if (socal_getmap(socalp, port, (caddr_t)arg, 0, 0) ==
1512 		    -1)
1513 			retval = FCAL_ALLOC_FAILED;
1514 		mutex_exit(&socalp->ioctl_mtx);
1515 		break;
1516 	case FCIO_BYPASS_DEV:
1517 		mutex_enter(&socalp->ioctl_mtx);
1518 		retval = socal_bypass_dev((void *)socalp, port, arg);
1519 		mutex_exit(&socalp->ioctl_mtx);
1520 		break;
1521 	case FCIO_FORCE_LIP:
1522 		mutex_enter(&socalp->ioctl_mtx);
1523 		retval = socal_force_lip((void *)socalp, port, 0,
1524 		    FCAL_FORCE_LIP);
1525 		mutex_exit(&socalp->ioctl_mtx);
1526 		break;
1527 	case FCIO_FORCE_OFFLINE:
1528 		mutex_enter(&socalp->ioctl_mtx);
1529 		retval = socal_force_offline((void *)socalp, port, 0);
1530 		mutex_exit(&socalp->ioctl_mtx);
1531 		break;
1532 	case FCIO_ADISC_ELS:
1533 	{
1534 		if ((adisc_pl =
1535 		    (la_els_adisc_t *)kmem_zalloc(
1536 		    sizeof (la_els_adisc_t),
1537 		    KM_NOSLEEP)) == NULL)
1538 			return (ENOMEM);
1539 
1540 		if (copyin((caddr_t)arg, (caddr_t)adisc_pl,
1541 		    sizeof (la_els_adisc_t)) == -1) {
1542 			kmem_free((void *)adisc_pl,
1543 			    sizeof (la_els_adisc_t));
1544 			return (EFAULT);
1545 		}
1546 		mutex_enter(&socalp->ioctl_mtx);
1547 		retval = socal_issue_adisc(socalp, port,
1548 		    adisc_pl->nport_id,
1549 		    adisc_pl, 0);
1550 		mutex_exit(&socalp->ioctl_mtx);
1551 
1552 		if (retval == FCAL_SUCCESS) {
1553 		if (copyout((caddr_t)adisc_pl, (caddr_t)arg,
1554 		    sizeof (la_els_adisc_t)) == -1) {
1555 			kmem_free((void *)adisc_pl,
1556 			    sizeof (la_els_adisc_t));
1557 			return (EFAULT);
1558 		}
1559 		}
1560 
1561 		kmem_free((void *)adisc_pl, sizeof (la_els_adisc_t));
1562 		break;
1563 	}
1564 	case FCIO_LINKSTATUS:
1565 	{
1566 		int dest;
1567 		if ((rls_pl =
1568 		    (la_els_rls_reply_t *)
1569 		    kmem_zalloc(sizeof (la_els_rls_reply_t),
1570 		    KM_NOSLEEP)) == NULL)
1571 			return (ENOMEM);
1572 
1573 		if (copyin((caddr_t)arg, (caddr_t)rls_pl,
1574 		    sizeof (la_els_rls_reply_t)) == -1) {
1575 			kmem_free((void *)rls_pl,
1576 			    sizeof (la_els_rls_reply_t));
1577 		return (EFAULT);
1578 		}
1579 		dest = (rls_pl->mbz[0] << 16) + (rls_pl->mbz[1] << 8) +
1580 		    rls_pl->mbz[2];
1581 		mutex_enter(&socalp->ioctl_mtx);
1582 		retval = socal_issue_rls(socalp, port, dest,
1583 		    rls_pl, 0);
1584 		mutex_exit(&socalp->ioctl_mtx);
1585 
1586 		if (retval == FCAL_SUCCESS) {
1587 		if (copyout((caddr_t)rls_pl, (caddr_t)arg,
1588 		    sizeof (la_els_rls_reply_t)) == -1) {
1589 			kmem_free((void *)rls_pl,
1590 			    sizeof (la_els_rls_reply_t));
1591 			return (EFAULT);
1592 		}
1593 		}
1594 		kmem_free((void *)rls_pl, sizeof (la_els_rls_reply_t));
1595 		break;
1596 	}
1597 	case FCIO_LOOPBACK_INTERNAL:
1598 		/*
1599 		 * If userland doesn't provide a location for a return
1600 		 * value the driver will permanently offline the port,
1601 		 * ignoring any checks for devices on the loop.
1602 		 */
1603 		mutex_enter(&socalp->ioctl_mtx);
1604 		if (arg == 0) {
1605 			port_statep = &socalp->port_state[port];
1606 			mutex_enter(&port_statep->sp_mtx);
1607 			if (port_statep->sp_status & PORT_DISABLED) {
1608 				/* Already disabled */
1609 				mutex_exit(&port_statep->sp_mtx);
1610 				mutex_exit(&socalp->ioctl_mtx);
1611 				return (EALREADY);
1612 			}
1613 			port_statep->sp_status |= PORT_DISABLED;
1614 			mutex_exit(&port_statep->sp_mtx);
1615 		}
1616 		retval = socal_diag_request((void *)socalp, port, &r,
1617 		    SOC_DIAG_INT_LOOP);
1618 		mutex_exit(&socalp->ioctl_mtx);
1619 		if (arg == 0) break;
1620 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1621 		    == -1)
1622 			return (EFAULT);
1623 		break;
1624 	case FCIO_LOOPBACK_MANUAL:
1625 		mutex_enter(&socalp->ioctl_mtx);
1626 		port_statep = &socalp->port_state[port];
1627 		mutex_enter(&port_statep->sp_mtx);
1628 		if (port_statep->sp_status & PORT_DISABLED) {
1629 			mutex_exit(&port_statep->sp_mtx);
1630 			mutex_exit(&socalp->ioctl_mtx);
1631 			return (EBUSY);
1632 		}
1633 		mutex_exit(&port_statep->sp_mtx);
1634 		retval = socal_diag_request((void *)socalp, port, &r,
1635 		    SOC_DIAG_EXT_LOOP);
1636 		mutex_exit(&socalp->ioctl_mtx);
1637 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1638 		    == -1)
1639 			return (EFAULT);
1640 		break;
1641 	case FCIO_NO_LOOPBACK:
1642 		mutex_enter(&socalp->ioctl_mtx);
1643 		port_statep = &socalp->port_state[port];
1644 		mutex_enter(&port_statep->sp_mtx);
1645 		/* Do not allow online if we're disabled */
1646 		if (port_statep->sp_status & PORT_DISABLED) {
1647 			if (arg != 0) {
1648 				mutex_exit(&port_statep->sp_mtx);
1649 				mutex_exit(&socalp->ioctl_mtx);
1650 				/*
1651 				 * It's permanently disabled -- Need to
1652 				 * enable it first
1653 				 */
1654 				return (EBUSY);
1655 			}
1656 			/* This was a request to online. */
1657 			port_statep->sp_status &= ~PORT_DISABLED;
1658 		}
1659 		mutex_exit(&port_statep->sp_mtx);
1660 		retval = socal_diag_request((void *)socalp, port, &r,
1661 		    SOC_DIAG_REM_LOOP);
1662 		mutex_exit(&socalp->ioctl_mtx);
1663 		if (arg == 0) break;
1664 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1665 		    == -1)
1666 			return (EFAULT);
1667 		break;
1668 	case FCIO_DIAG_NOP:
1669 		mutex_enter(&socalp->ioctl_mtx);
1670 		retval = socal_diag_request((void *)socalp, port, &r,
1671 		    SOC_DIAG_NOP);
1672 		mutex_exit(&socalp->ioctl_mtx);
1673 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1674 		    == -1)
1675 			return (EFAULT);
1676 		break;
1677 	case FCIO_DIAG_XRAM:
1678 		mutex_enter(&socalp->ioctl_mtx);
1679 		retval = socal_diag_request((void *)socalp, port, &r,
1680 		    SOC_DIAG_XRAM_TEST);
1681 		mutex_exit(&socalp->ioctl_mtx);
1682 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1683 		    == -1)
1684 			return (EFAULT);
1685 		break;
1686 	case FCIO_DIAG_SOC:
1687 		mutex_enter(&socalp->ioctl_mtx);
1688 		retval = socal_diag_request((void *)socalp, port, &r,
1689 		    SOC_DIAG_SOC_TEST);
1690 		mutex_exit(&socalp->ioctl_mtx);
1691 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1692 		    == -1)
1693 			return (EFAULT);
1694 		break;
1695 	case FCIO_DIAG_HCB:
1696 		mutex_enter(&socalp->ioctl_mtx);
1697 		retval = socal_diag_request((void *)socalp, port, &r,
1698 		    SOC_DIAG_HCB_TEST);
1699 		mutex_exit(&socalp->ioctl_mtx);
1700 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1701 		    == -1)
1702 			return (EFAULT);
1703 		break;
1704 	case FCIO_DIAG_SOCLB:
1705 		mutex_enter(&socalp->ioctl_mtx);
1706 		retval = socal_diag_request((void *)socalp, port, &r,
1707 		    SOC_DIAG_SOCLB_TEST);
1708 		mutex_exit(&socalp->ioctl_mtx);
1709 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1710 		    == -1)
1711 			return (EFAULT);
1712 		break;
1713 	case FCIO_DIAG_SRDSLB:
1714 		mutex_enter(&socalp->ioctl_mtx);
1715 		retval = socal_diag_request((void *)socalp, port, &r,
1716 		    SOC_DIAG_SRDSLB_TEST);
1717 		mutex_exit(&socalp->ioctl_mtx);
1718 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1719 		    == -1)
1720 			return (EFAULT);
1721 		break;
1722 	case FCIO_DIAG_EXTLB:
1723 		mutex_enter(&socalp->ioctl_mtx);
1724 		retval = socal_diag_request((void *)socalp, port, &r,
1725 		    SOC_DIAG_EXTOE_TEST);
1726 		mutex_exit(&socalp->ioctl_mtx);
1727 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1728 		    == -1)
1729 			return (EFAULT);
1730 		break;
1731 	case FCIO_DIAG_RAW:
1732 		if (copyin((caddr_t)arg, (caddr_t)&i, sizeof (uint_t))
1733 		    == -1)
1734 			return (EFAULT);
1735 		mutex_enter(&socalp->ioctl_mtx);
1736 		retval = socal_diag_request((void *)socalp, port, &r,
1737 		    (uint_t)i);
1738 		mutex_exit(&socalp->ioctl_mtx);
1739 		if (copyout((caddr_t)&r, (caddr_t)arg, sizeof (uint_t))
1740 		    == -1)
1741 			return (EFAULT);
1742 		break;
1743 	case FCIO_LOOPBACK_FRAME:
1744 		if ((flb_hdr = (flb_hdr_t *)kmem_zalloc(sizeof (flb_hdr_t),
1745 		    KM_NOSLEEP)) == NULL)
1746 			return (ENOMEM);
1747 
1748 		if (copyin((caddr_t)arg,
1749 		    (caddr_t)flb_hdr, sizeof (flb_hdr_t)) == -1) {
1750 		kmem_free((void *)flb_hdr, sizeof (flb_hdr_t));
1751 		return (EFAULT);
1752 		}
1753 
1754 		flb_size = flb_hdr->length;
1755 
1756 		if ((flb_pl =
1757 		    (uchar_t *)kmem_zalloc(flb_size, KM_NOSLEEP)) == NULL)
1758 			return (ENOMEM);
1759 
1760 		if (copyin((caddr_t)(arg + sizeof (flb_hdr_t)),
1761 		    (caddr_t)flb_pl, flb_size) == -1) {
1762 			kmem_free((void *)flb_pl, flb_size);
1763 			return (EFAULT);
1764 		}
1765 		mutex_enter(&socalp->ioctl_mtx);
1766 		retval = socal_issue_lbf(socalp, port, flb_pl,
1767 		    flb_size, 1);
1768 		mutex_exit(&socalp->ioctl_mtx);
1769 
1770 		if (retval == FCAL_SUCCESS) {
1771 		if (copyout((caddr_t)flb_pl,
1772 		    (caddr_t)(arg + sizeof (flb_hdr_t) +
1773 		    flb_hdr->max_length), flb_size) == -1) {
1774 			kmem_free((void *)flb_pl, flb_size);
1775 			kmem_free((void *)flb_hdr, sizeof (flb_hdr_t));
1776 			return (EFAULT);
1777 		}
1778 		}
1779 
1780 		kmem_free((void *)flb_pl, flb_size);
1781 		kmem_free((void *)flb_hdr, sizeof (flb_hdr_t));
1782 		break;
1783 	default:
1784 		return (ENOTTY);
1785 
1786 	}
1787 	switch (retval) {
1788 		case FCAL_SUCCESS:
1789 			return (0);
1790 		case FCAL_ALLOC_FAILED:
1791 			return (ENOMEM);
1792 		case FCAL_STATUS_DIAG_BUSY:
1793 			return (EALREADY);
1794 		case FCAL_STATUS_DIAG_INVALID:
1795 			return (EINVAL);
1796 		default:
1797 			return (EIO);
1798 	}
1799 
1800 }
1801 
1802 /*
1803  * Function name : socal_disable()
1804  *
1805  * Return Values :  none
1806  *
1807  * Description	 : Reset the soc+
1808  *
1809  * Context	 : Can be called from different kernel process threads.
1810  *		   Can be called by interrupt thread.
1811  *
1812  * Note:  before calling this, the interface should be locked down
1813  * so that it is guaranteed that no other threads are accessing
1814  * the hardware.
1815  */
1816 static	void
1817 socal_disable(socal_state_t *socalp)
1818 {
1819 #if !defined(lint)
1820 	int i;
1821 #endif
1822 	/* Don't touch the hardware if the registers aren't mapped */
1823 	if (!socalp->socal_rp)
1824 		return;
1825 
1826 	socalp->socal_rp->socal_imr = socalp->socal_k_imr = 0;
1827 	socalp->socal_rp->socal_csr.w = SOCAL_CSR_SOFT_RESET;
1828 #if !defined(lint)
1829 	i = socalp->socal_rp->socal_csr.w;
1830 #endif
1831 	DEBUGF(9, (CE_CONT, "csr.w = %x\n", i));
1832 }
1833 
1834 /*
1835  * Function name : socal_init_transport_interface()
1836  *
1837  * Return Values :  none
1838  *
1839  * Description	 : Fill up the fcal_tranpsort struct for ULPs
1840  *
1841  *
1842  * Note:  Only called during attach, so no protection
1843  */
1844 static void
1845 socal_init_transport_interface(socal_state_t *socalp)
1846 {
1847 	int			i;
1848 	fcal_transport_t	*xport;
1849 
1850 	for (i = 0; i < N_SOCAL_NPORTS; i++) {
1851 		xport = socalp->port_state[i].sp_transport;
1852 		mutex_init(&xport->fcal_mtx, NULL, MUTEX_DRIVER,
1853 		    (void *)(socalp->iblkc));
1854 
1855 		cv_init(&xport->fcal_cv, NULL, CV_DRIVER, NULL);
1856 
1857 		xport->fcal_handle = (void *)socalp;
1858 		xport->fcal_dmalimp = socallim;
1859 		xport->fcal_iblock = socalp->iblkc;
1860 		xport->fcal_dmaattr = &socal_dma_attr;
1861 		xport->fcal_accattr = &socal_acc_attr;
1862 		xport->fcal_loginparms = socalp->socal_service_params;
1863 		bcopy((caddr_t)&socalp->socal_n_wwn,
1864 		    (caddr_t)&xport->fcal_n_wwn, sizeof (la_wwn_t));
1865 		bcopy((caddr_t)&socalp->port_state[i].sp_p_wwn,
1866 		    (caddr_t)&xport->fcal_p_wwn, sizeof (la_wwn_t));
1867 		xport->fcal_portno = i;
1868 		xport->fcal_cmdmax = SOCAL_MAX_XCHG;
1869 		xport->fcal_ops = &socal_transport_ops;
1870 	}
1871 }
1872 
1873 /*
1874  * static int
1875  * socal_cqalloc_init() - Inialize the circular queue tables.
1876  *	Also, init the locks that are associated with the tables.
1877  *
1878  *	Returns:	FCAL_SUCCESS, if able to init properly.
1879  *			FCAL_FAILURE, if unable to init properly.
1880  */
1881 
1882 static int
1883 socal_cqalloc_init(socal_state_t *socalp, uint32_t index)
1884 {
1885 	uint32_t cq_size;
1886 	size_t real_len;
1887 	uint_t ccount;
1888 	socal_kcq_t *cqp;
1889 	int	req_bound = 0, rsp_bound = 0;
1890 
1891 	/*
1892 	 * Initialize the Request and Response Queue locks.
1893 	 */
1894 
1895 	mutex_init(&socalp->request[index].skc_mtx, NULL, MUTEX_DRIVER,
1896 	    (void *)socalp->iblkc);
1897 	mutex_init(&socalp->response[index].skc_mtx, NULL, MUTEX_DRIVER,
1898 	    (void *)socalp->iblkc);
1899 	cv_init(&socalp->request[index].skc_cv, NULL, CV_DRIVER, NULL);
1900 	cv_init(&socalp->response[index].skc_cv, NULL, CV_DRIVER, NULL);
1901 
1902 	/* Allocate DVMA resources for the Request Queue. */
1903 	cq_size = socal_req_entries[index] * sizeof (cqe_t);
1904 	if (cq_size) {
1905 		cqp = &socalp->request[index];
1906 
1907 		if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
1908 		    DDI_DMA_DONTWAIT, NULL,
1909 		    &cqp->skc_dhandle) != DDI_SUCCESS) {
1910 			socal_disp_err(socalp, CE_WARN, "driver.4020",
1911 			    "!alloc of dma handle failed");
1912 			goto fail;
1913 		}
1914 
1915 		if (ddi_dma_mem_alloc(cqp->skc_dhandle,
1916 		    cq_size + SOCAL_CQ_ALIGN, &socal_acc_attr,
1917 		    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
1918 		    (caddr_t *)&cqp->skc_cq_raw, &real_len,
1919 		    &cqp->skc_acchandle) != DDI_SUCCESS) {
1920 			socal_disp_err(socalp, CE_WARN, "driver.4030",
1921 			    "!alloc of dma space failed");
1922 				goto fail;
1923 		}
1924 
1925 		if (real_len < (cq_size + SOCAL_CQ_ALIGN)) {
1926 			socal_disp_err(socalp, CE_WARN, "driver.4035",
1927 			    "!alloc of dma space failed");
1928 			goto fail;
1929 		}
1930 		cqp->skc_cq = (cqe_t *)(((uintptr_t)cqp->skc_cq_raw +
1931 		    (uintptr_t)SOCAL_CQ_ALIGN - 1) &
1932 		    ((uintptr_t)(~(SOCAL_CQ_ALIGN-1))));
1933 
1934 		if (ddi_dma_addr_bind_handle(cqp->skc_dhandle,
1935 		    (struct as *)NULL, (caddr_t)cqp->skc_cq, cq_size,
1936 		    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT,
1937 		    NULL, &cqp->skc_dcookie, &ccount) != DDI_DMA_MAPPED) {
1938 			socal_disp_err(socalp, CE_WARN, "driver.4040",
1939 			    "!bind of dma handle failed");
1940 			goto fail;
1941 		}
1942 
1943 		req_bound = 1;
1944 		if (ccount != 1) {
1945 			socal_disp_err(socalp, CE_WARN, "driver.4045",
1946 			    "!bind of dma handle failed");
1947 			goto fail;
1948 		}
1949 
1950 	} else {
1951 		socalp->request[index].skc_cq_raw = NULL;
1952 		socalp->request[index].skc_cq = (cqe_t *)NULL;
1953 		socalp->request[index].skc_dhandle = 0;
1954 	}
1955 
1956 	/* Allocate DVMA resources for the response Queue. */
1957 	cq_size = socal_rsp_entries[index] * sizeof (cqe_t);
1958 	if (cq_size) {
1959 		cqp = &socalp->response[index];
1960 
1961 		if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
1962 		    DDI_DMA_DONTWAIT, NULL,
1963 		    &cqp->skc_dhandle) != DDI_SUCCESS) {
1964 			socal_disp_err(socalp, CE_WARN, "driver.4050",
1965 			    "!alloc of dma handle failed");
1966 			goto fail;
1967 		}
1968 
1969 		if (ddi_dma_mem_alloc(cqp->skc_dhandle,
1970 		    cq_size + SOCAL_CQ_ALIGN, &socal_acc_attr,
1971 		    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
1972 		    (caddr_t *)&cqp->skc_cq_raw, &real_len,
1973 		    &cqp->skc_acchandle) != DDI_SUCCESS) {
1974 			socal_disp_err(socalp, CE_WARN, "driver.4060",
1975 			    "!alloc of dma space failed");
1976 				goto fail;
1977 		}
1978 
1979 		if (real_len < (cq_size + SOCAL_CQ_ALIGN)) {
1980 			socal_disp_err(socalp, CE_WARN, "driver.4065",
1981 			    "!alloc of dma space failed");
1982 			goto fail;
1983 		}
1984 
1985 		cqp->skc_cq = (cqe_t *)(((uintptr_t)cqp->skc_cq_raw +
1986 		    (uintptr_t)SOCAL_CQ_ALIGN - 1) &
1987 		    ((uintptr_t)(~(SOCAL_CQ_ALIGN-1))));
1988 
1989 		if (ddi_dma_addr_bind_handle(cqp->skc_dhandle,
1990 		    (struct as *)NULL, (caddr_t)cqp->skc_cq, cq_size,
1991 		    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT,
1992 		    NULL, &cqp->skc_dcookie, &ccount) != DDI_DMA_MAPPED) {
1993 			socal_disp_err(socalp, CE_WARN, "driver.4070",
1994 			    "!bind of dma handle failed");
1995 			goto fail;
1996 		}
1997 
1998 		rsp_bound = 1;
1999 		if (ccount != 1) {
2000 			socal_disp_err(socalp, CE_WARN, "driver.4075",
2001 			    "!bind of dma handle failed");
2002 			goto fail;
2003 		}
2004 
2005 	} else {
2006 		socalp->response[index].skc_cq_raw = NULL;
2007 		socalp->response[index].skc_cq = (cqe_t *)NULL;
2008 		socalp->response[index].skc_dhandle = 0;
2009 	}
2010 
2011 	/*
2012 	 * Initialize the queue pointers
2013 	 */
2014 	socal_cqinit(socalp, index);
2015 
2016 	return (FCAL_SUCCESS);
2017 fail:
2018 	if (socalp->request[index].skc_dhandle) {
2019 		if (req_bound)
2020 			(void) ddi_dma_unbind_handle(socalp->
2021 			    request[index].skc_dhandle);
2022 		ddi_dma_free_handle(&socalp->request[index].skc_dhandle);
2023 	}
2024 	if (socalp->request[index].skc_cq_raw)
2025 		ddi_dma_mem_free(&socalp->request[index].skc_acchandle);
2026 
2027 	if (socalp->response[index].skc_dhandle) {
2028 		if (rsp_bound)
2029 			(void) ddi_dma_unbind_handle(socalp->
2030 			    response[index].skc_dhandle);
2031 		ddi_dma_free_handle(&socalp->response[index].skc_dhandle);
2032 	}
2033 	if (socalp->response[index].skc_cq_raw)
2034 		ddi_dma_mem_free(&socalp->response[index].skc_acchandle);
2035 
2036 	socalp->request[index].skc_dhandle = NULL;
2037 	socalp->response[index].skc_dhandle = NULL;
2038 	socalp->request[index].skc_cq_raw = NULL;
2039 	socalp->request[index].skc_cq = NULL;
2040 	socalp->response[index].skc_cq_raw = NULL;
2041 	socalp->response[index].skc_cq = NULL;
2042 	mutex_destroy(&socalp->request[index].skc_mtx);
2043 	mutex_destroy(&socalp->response[index].skc_mtx);
2044 	cv_destroy(&socalp->request[index].skc_cv);
2045 	cv_destroy(&socalp->response[index].skc_cv);
2046 	return (FCAL_FAILURE);
2047 
2048 }
2049 
2050 /*
2051  * socal_cqinit() - initializes the driver's circular queue pointers, etc.
2052  */
2053 
2054 static void
2055 socal_cqinit(socal_state_t *socalp, uint32_t index)
2056 {
2057 	socal_kcq_t *kcq_req = &socalp->request[index];
2058 	socal_kcq_t *kcq_rsp = &socalp->response[index];
2059 
2060 	/*
2061 	 * Initialize the Request and Response Queue pointers
2062 	 */
2063 	kcq_req->skc_seqno = 1;
2064 	kcq_rsp->skc_seqno = 1;
2065 	kcq_req->skc_in = 0;
2066 	kcq_rsp->skc_in = 0;
2067 	kcq_req->skc_out = 0;
2068 	kcq_rsp->skc_out = 0;
2069 	kcq_req->skc_last_index = socal_req_entries[index] - 1;
2070 	kcq_rsp->skc_last_index = socal_rsp_entries[index] - 1;
2071 	kcq_req->skc_full = 0;
2072 	kcq_rsp->deferred_intr_timeoutid = 0;
2073 	kcq_req->skc_socalp = socalp;
2074 	kcq_rsp->skc_socalp = socalp;
2075 
2076 	kcq_req->skc_xram_cqdesc =
2077 	    (socalp->xram_reqp + (index * sizeof (struct cq))/8);
2078 	kcq_rsp->skc_xram_cqdesc =
2079 	    (socalp->xram_rspp + (index * sizeof (struct cq))/8);
2080 
2081 	/*  Clear out memory we have allocated */
2082 	if (kcq_req->skc_cq != NULL)
2083 		bzero((caddr_t)kcq_req->skc_cq,
2084 		    socal_req_entries[index] * sizeof (cqe_t));
2085 	if (kcq_rsp->skc_cq != NULL)
2086 		bzero((caddr_t)kcq_rsp->skc_cq,
2087 		    socal_rsp_entries[index] * sizeof (cqe_t));
2088 }
2089 
2090 
2091 static int
2092 socal_start(socal_state_t *socalp)
2093 {
2094 	uint_t r;
2095 
2096 	if (!socalp)
2097 		return (FCAL_FAILURE);
2098 
2099 	socal_download_ucode(socalp);
2100 	socal_init_cq_desc(socalp);
2101 	socal_init_wwn(socalp);
2102 
2103 	mutex_enter(&socalp->port_state[0].sp_mtx);
2104 	socalp->port_state[0].sp_status
2105 	    &= (PORT_OPEN|PORT_CHILD_INIT|PORT_DISABLED|PORT_TARGET_MODE);
2106 	socalp->port_state[0].sp_status |= PORT_OFFLINE;
2107 	mutex_exit(&socalp->port_state[0].sp_mtx);
2108 
2109 	mutex_enter(&socalp->port_state[1].sp_mtx);
2110 	socalp->port_state[1].sp_status
2111 	    &= (PORT_OPEN|PORT_CHILD_INIT|PORT_DISABLED|PORT_TARGET_MODE);
2112 	socalp->port_state[1].sp_status |= PORT_OFFLINE;
2113 	mutex_exit(&socalp->port_state[1].sp_mtx);
2114 
2115 	socal_enable(socalp);
2116 	/* Make sure disabled ports stay disabled. */
2117 	if (socalp->port_state[0].sp_status & PORT_DISABLED)
2118 		(void) socal_diag_request((void *)socalp, 0, &r,
2119 		    SOC_DIAG_INT_LOOP);
2120 	if (socalp->port_state[1].sp_status & PORT_DISABLED)
2121 		(void) socal_diag_request((void *)socalp, 1, &r,
2122 		    SOC_DIAG_INT_LOOP);
2123 
2124 	mutex_enter(&socalp->k_imr_mtx);
2125 	socalp->socal_shutdown = 0;
2126 	mutex_exit(&socalp->k_imr_mtx);
2127 
2128 	mutex_enter(&socalp->board_mtx);
2129 	if (socal_establish_pool(socalp, 1) != FCAL_SUCCESS) {
2130 		mutex_exit(&socalp->board_mtx);
2131 		return (FCAL_FAILURE);
2132 	}
2133 	if (socal_add_pool_buffer(socalp, 1) != FCAL_SUCCESS) {
2134 		mutex_exit(&socalp->board_mtx);
2135 		return (FCAL_FAILURE);
2136 	}
2137 
2138 	mutex_exit(&socalp->board_mtx);
2139 	return (FCAL_SUCCESS);
2140 }
2141 
2142 static void
2143 socal_doreset(socal_state_t *socalp)
2144 {
2145 	int		i;
2146 	socal_port_t	*port_statep;
2147 	socal_unsol_cb_t *scbp;
2148 
2149 	for (i = 0; i < SOCAL_N_CQS; i++) {
2150 		mutex_enter(&socalp->request[i].skc_mtx);
2151 		mutex_enter(&socalp->response[i].skc_mtx);
2152 	}
2153 
2154 	mutex_enter(&socalp->k_imr_mtx);
2155 	socal_disable(socalp);
2156 
2157 	if (socalp->pool_dhandle) {
2158 		(void) ddi_dma_unbind_handle(socalp->pool_dhandle);
2159 		ddi_dma_free_handle(&socalp->pool_dhandle);
2160 	}
2161 
2162 	if (socalp->pool)
2163 		ddi_dma_mem_free(&socalp->pool_acchandle);
2164 
2165 	socalp->pool_dhandle = NULL;
2166 	socalp->pool = NULL;
2167 
2168 	for (i = 0; i < SOCAL_N_CQS; i++)
2169 		socal_cqinit(socalp, i);
2170 
2171 	for (i = 0; i < N_SOCAL_NPORTS; i++) {
2172 		port_statep = &socalp->port_state[i];
2173 
2174 		mutex_enter(&port_statep->sp_mtx);
2175 		port_statep->sp_status &= ~ (PORT_STATUS_MASK |
2176 		    PORT_LILP_PENDING | PORT_LIP_PENDING |
2177 		    PORT_ABORT_PENDING | PORT_BYPASS_PENDING |
2178 		    PORT_ELS_PENDING);
2179 		mutex_exit(&port_statep->sp_mtx);
2180 	}
2181 
2182 	mutex_exit(&socalp->k_imr_mtx);
2183 
2184 	for (i = SOCAL_N_CQS-1; i >= 0; i--) {
2185 		mutex_exit(&socalp->request[i].skc_mtx);
2186 		mutex_exit(&socalp->response[i].skc_mtx);
2187 	}
2188 
2189 	for (i = 0; i < N_SOCAL_NPORTS; i++) {
2190 		for (scbp = socalp->port_state[i].sp_unsol_cb; scbp;
2191 		    scbp = scbp->next)
2192 			(scbp->statec_cb)(scbp->arg, FCAL_STATE_RESET);
2193 	}
2194 
2195 	for (i = 0; i < SOCAL_N_CQS; i++) {
2196 		mutex_enter(&socalp->request[i].skc_mtx);
2197 		mutex_enter(&socalp->response[i].skc_mtx);
2198 	}
2199 
2200 
2201 	for (i = 0; i < SOCAL_N_CQS; i++) {
2202 		socalp->request[i].skc_overflowh = NULL;
2203 		if (socalp->request[i].skc_full & SOCAL_SKC_SLEEP)
2204 			cv_broadcast(&socalp->request[i].skc_cv);
2205 	}
2206 
2207 	for (i = SOCAL_N_CQS-1; i >= 0; i--) {
2208 		mutex_exit(&socalp->request[i].skc_mtx);
2209 		mutex_exit(&socalp->response[i].skc_mtx);
2210 	}
2211 
2212 }
2213 
2214 
2215 /*
2216  * Function name : socal_download_ucode ()
2217  *
2218  * Return Values :
2219  *
2220  * Description	 : Copies firmware from code that has been linked into
2221  *		   the socal module into the soc+'s XRAM.  Prints the date
2222  *		   string
2223  *
2224  */
2225 static void
2226 socal_download_ucode(socal_state_t *socalp)
2227 {
2228 	uint_t	fw_len = 0;
2229 	uint_t	date_str[16];
2230 	auto	char buf[256];
2231 
2232 	fw_len = (uint_t)socal_ucode_size;
2233 
2234 	/* Copy the firmware image */
2235 	socal_wcopy((uint_t *)&socal_ucode,
2236 	    (uint_t *)socalp->socal_xrp, fw_len);
2237 
2238 	socal_fix_harda(socalp, 0);
2239 	socal_fix_harda(socalp, 1);
2240 
2241 	/* Get the date string from the firmware image */
2242 	socal_wcopy((uint_t *)(socalp->socal_xrp+SOCAL_XRAM_FW_DATE_STR),
2243 	    date_str, sizeof (date_str));
2244 	date_str[sizeof (date_str) / sizeof (uint_t) - 1] = 0;
2245 
2246 	if (*(caddr_t)date_str != '\0') {
2247 		(void) sprintf(buf,
2248 		    "!Downloading host adapter, fw date code: %s\n",
2249 		    (caddr_t)date_str);
2250 		socal_disp_err(socalp, CE_CONT, "driver.1010", buf);
2251 		(void) strcpy(socalp->socal_stats.fw_revision,
2252 		    (char *)date_str);
2253 	} else {
2254 		(void) sprintf(buf,
2255 		    "!Downloading host adapter fw, "
2256 		    "date code: <not available>\n");
2257 		socal_disp_err(socalp, CE_CONT, "driver.3010", buf);
2258 		(void) strcpy(socalp->socal_stats.fw_revision,
2259 		    "<Not Available>");
2260 	}
2261 }
2262 
2263 /*
2264  * Function name : socal_disp_err()
2265  *
2266  * Return Values : none
2267  *
2268  * Description   : displays an error message on the system console
2269  *		   with the full device pathname displayed
2270  */
2271 static void
2272 socal_disp_err(
2273 	socal_state_t	*socalp,
2274 	uint_t		level,
2275 	char		*mid,
2276 	char		*msg)
2277 {
2278 	char c;
2279 	int instance;
2280 
2281 	instance = ddi_get_instance(socalp->dip);
2282 
2283 	c = *msg;
2284 
2285 	if (c == '!')		/* log only */
2286 		cmn_err(level,
2287 		"!ID[SUNWssa.socal.%s] socal%d: %s", mid, instance, msg+1);
2288 	else if (c == '?')	/* boot message - log && maybe console */
2289 		cmn_err(level,
2290 		"?ID[SUNWssa.socal.%s] socal%d: %s", mid, instance, msg+1);
2291 	else if (c == '^')	/* console only */
2292 		cmn_err(level, "^socal%d: %s", instance, msg+1);
2293 	else	{		/* log and console */
2294 		cmn_err(level, "^socal%d: %s", instance, msg);
2295 		cmn_err(level, "!ID[SUNWssa.socal.%s] socal%d: %s", mid,
2296 		    instance, msg);
2297 	}
2298 }
2299 
2300 /*
2301  * Function name : socal_init_cq_desc()
2302  *
2303  * Return Values : none
2304  *
2305  * Description	 : Initializes the request and response queue
2306  *		   descriptors in the SOC+'s XRAM
2307  *
2308  * Context	 : Should only be called during initialiation when
2309  *		   the SOC+ is reset.
2310  */
2311 static void
2312 socal_init_cq_desc(socal_state_t *socalp)
2313 {
2314 	soc_cq_t	que_desc[SOCAL_N_CQS];
2315 	uint32_t	i;
2316 
2317 	/*
2318 	 * Finish CQ table initialization and give the descriptor
2319 	 * table to the soc+.  Note that we don't use all of the queues
2320 	 * provided by the hardware, but we make sure we initialize the
2321 	 * quantities in the unused fields in the hardware to zeroes.
2322 	 */
2323 
2324 	/*
2325 	 * Do request queues
2326 	 */
2327 	for (i = 0; i < SOCAL_N_CQS; i++) {
2328 		if (socal_req_entries[i]) {
2329 			que_desc[i].cq_address =
2330 			    (uint32_t)socalp->request[i].
2331 			    skc_dcookie.dmac_address;
2332 			que_desc[i].cq_last_index = socal_req_entries[i] - 1;
2333 		} else {
2334 			que_desc[i].cq_address = (uint32_t)0;
2335 			que_desc[i].cq_last_index = 0;
2336 		}
2337 		que_desc[i].cq_in = 0;
2338 		que_desc[i].cq_out = 0;
2339 		que_desc[i].cq_seqno = 1; /* required by SOC+ microcode */
2340 	}
2341 
2342 	/* copy to XRAM */
2343 	socal_wcopy((uint_t *)que_desc,		/* pointer to kernel copy */
2344 	    (uint_t *)socalp->xram_reqp,	/* pointer to xram location */
2345 	    SOCAL_N_CQS * sizeof (soc_cq_t));
2346 
2347 	/*
2348 	 * Do response queues
2349 	 */
2350 	for (i = 0; i < SOCAL_N_CQS; i++) {
2351 		if (socal_rsp_entries[i]) {
2352 			que_desc[i].cq_last_index = socal_rsp_entries[i] - 1;
2353 			que_desc[i].cq_address =
2354 			    (uint32_t)socalp->response[i].
2355 			    skc_dcookie.dmac_address;
2356 
2357 		} else {
2358 			que_desc[i].cq_address = 0;
2359 			que_desc[i].cq_last_index = 0;
2360 		}
2361 	}
2362 
2363 	/* copy to XRAM */
2364 	socal_wcopy((uint_t *)que_desc,		/* pointer to kernel copy */
2365 	    (uint_t *)socalp->xram_rspp,	/* pointer to xram location */
2366 	    SOCAL_N_CQS * sizeof (soc_cq_t));
2367 }
2368 
2369 static void
2370 socal_init_wwn(socal_state_t *socalp)
2371 {
2372 	/* copy the node wwn to xram */
2373 	socal_wcopy((uint_t *)&socalp->socal_n_wwn,
2374 	    (uint_t *)(socalp->socal_xrp +
2375 	    SOCAL_XRAM_NODE_WWN), sizeof (la_wwn_t));
2376 
2377 	/* copy port a's wwn to xram */
2378 	socal_wcopy((uint_t *)&socalp->port_state[0].sp_p_wwn,
2379 	    (uint_t *)(socalp->socal_xrp + SOCAL_XRAM_PORTA_WWN),
2380 	    sizeof (la_wwn_t));
2381 
2382 	/* copy port b's wwn to xram */
2383 	socal_wcopy((uint_t *)&socalp->port_state[1].sp_p_wwn,
2384 	    (uint_t *)(socalp->socal_xrp + SOCAL_XRAM_PORTB_WWN),
2385 	    sizeof (la_wwn_t));
2386 
2387 	/*
2388 	 * need to avoid deadlock by assuring no other thread grabs both of
2389 	 * these at once
2390 	 */
2391 	mutex_enter(&socalp->port_state[0].sp_transport->fcal_mtx);
2392 	mutex_enter(&socalp->port_state[1].sp_transport->fcal_mtx);
2393 
2394 	socal_wcopy((uint_t *)(socalp->socal_xrp + SOCAL_XRAM_SERV_PARAMS),
2395 	    (uint_t *)&socalp->socal_service_params, SOCAL_SVC_LENGTH);
2396 	mutex_exit(&socalp->port_state[1].sp_transport->fcal_mtx);
2397 	mutex_exit(&socalp->port_state[0].sp_transport->fcal_mtx);
2398 }
2399 
2400 static void
2401 socal_enable(socal_state_t *socalp)
2402 {
2403 	DEBUGF(2, (CE_CONT, "socal%d: enable:\n",
2404 	    ddi_get_instance(socalp->dip)));
2405 
2406 	socalp->socal_rp->socal_cr.w = socalp->socal_cfg;
2407 	socalp->socal_rp->socal_csr.w = SOCAL_CSR_SOCAL_TO_HOST;
2408 
2409 	socalp->socal_k_imr = (uint32_t)SOCAL_CSR_SOCAL_TO_HOST |
2410 	    SOCAL_CSR_SLV_ACC_ERR;
2411 	socalp->socal_rp->socal_imr = (uint32_t)socalp->socal_k_imr;
2412 }
2413 
2414 /*
2415  * static int
2416  * socal_establish_pool() - this routine tells the SOC+ of a buffer pool
2417  *	to place LINK ctl application data as it arrives.
2418  *
2419  *	Returns:
2420  *		FCAL_SUCCESS, upon establishing the pool.
2421  *		FCAL_FAILURE, if unable to establish the pool.
2422  */
2423 
2424 static int
2425 socal_establish_pool(socal_state_t *socalp, uint32_t poolid)
2426 {
2427 	soc_pool_request_t	*prq;
2428 	int			result;
2429 
2430 	if ((prq =
2431 	    (soc_pool_request_t *)kmem_zalloc(sizeof (soc_pool_request_t),
2432 	    KM_NOSLEEP)) == NULL)
2433 			return (FCAL_FAILURE);
2434 	/*
2435 	 * Fill in the request structure.
2436 	 */
2437 	prq->spr_soc_hdr.sh_request_token = 1;
2438 	prq->spr_soc_hdr.sh_flags = SOC_FC_HEADER | SOC_UNSOLICITED |
2439 	    SOC_NO_RESPONSE;
2440 	prq->spr_soc_hdr.sh_class = 0;
2441 	prq->spr_soc_hdr.sh_seg_cnt = 1;
2442 	prq->spr_soc_hdr.sh_byte_cnt = 0;
2443 
2444 	prq->spr_pool_id = poolid;
2445 	prq->spr_header_mask = SOCPR_MASK_RCTL;
2446 	prq->spr_buf_size = SOCAL_POOL_SIZE;
2447 	prq->spr_n_entries = 0;
2448 
2449 	prq->spr_fc_frame_hdr.r_ctl = R_CTL_ELS_REQ;
2450 	prq->spr_fc_frame_hdr.d_id = 0;
2451 	prq->spr_fc_frame_hdr.s_id = 0;
2452 	prq->spr_fc_frame_hdr.type = 0;
2453 	prq->spr_fc_frame_hdr.f_ctl = 0;
2454 	prq->spr_fc_frame_hdr.seq_id = 0;
2455 	prq->spr_fc_frame_hdr.df_ctl = 0;
2456 	prq->spr_fc_frame_hdr.seq_cnt = 0;
2457 	prq->spr_fc_frame_hdr.ox_id = 0;
2458 	prq->spr_fc_frame_hdr.rx_id = 0;
2459 	prq->spr_fc_frame_hdr.ro = 0;
2460 
2461 	prq->spr_cqhdr.cq_hdr_count = 1;
2462 	prq->spr_cqhdr.cq_hdr_type = CQ_TYPE_ADD_POOL;
2463 	prq->spr_cqhdr.cq_hdr_flags = 0;
2464 	prq->spr_cqhdr.cq_hdr_seqno = 0;
2465 
2466 	/* Enque the request. */
2467 	result = socal_cq_enque(socalp, NULL, (cqe_t *)prq, CQ_REQUEST_1,
2468 	    FCAL_NOSLEEP, NULL, 0);
2469 	kmem_free((void *)prq, sizeof (soc_pool_request_t));
2470 	return (result);
2471 
2472 }
2473 
2474 
2475 /*
2476  * static int
2477  * soc_add_pool_buffer() - this routine tells the SOC+ to add one buffer
2478  *	to an established pool of buffers
2479  *
2480  *	Returns:
2481  *		DDI_SUCCESS, upon establishing the pool.
2482  *		DDI_FAILURE, if unable to establish the pool.
2483  */
2484 
2485 static int
2486 socal_add_pool_buffer(socal_state_t *socalp, uint32_t poolid)
2487 {
2488 	soc_data_request_t	*drq;
2489 	int			result;
2490 	size_t			real_len;
2491 	int			bound = 0;
2492 	uint_t			ccount;
2493 
2494 	if ((drq =
2495 	    (soc_data_request_t *)kmem_zalloc(sizeof (soc_data_request_t),
2496 	    KM_NOSLEEP)) == NULL)
2497 			return (FCAL_FAILURE);
2498 
2499 	/* Allocate DVMA resources for the buffer pool */
2500 	if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
2501 	    DDI_DMA_DONTWAIT, NULL, &socalp->pool_dhandle) != DDI_SUCCESS)
2502 		goto fail;
2503 
2504 	if (ddi_dma_mem_alloc(socalp->pool_dhandle, SOCAL_POOL_SIZE,
2505 	    &socal_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
2506 	    (caddr_t *)&socalp->pool, &real_len, &socalp->pool_acchandle)
2507 	    != DDI_SUCCESS)
2508 		goto fail;
2509 
2510 	if (real_len < SOCAL_POOL_SIZE)
2511 		goto fail;
2512 
2513 	if (ddi_dma_addr_bind_handle(socalp->pool_dhandle, (struct as *)NULL,
2514 	    (caddr_t)socalp->pool, SOCAL_POOL_SIZE,
2515 	    DDI_DMA_READ | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT,
2516 	    NULL, &socalp->pool_dcookie, &ccount) != DDI_DMA_MAPPED)
2517 		goto fail;
2518 
2519 	bound = 1;
2520 	if (ccount != 1)
2521 		goto fail;
2522 
2523 	/*
2524 	 * Fill in the request structure.
2525 	 */
2526 	drq->sdr_soc_hdr.sh_request_token = poolid;
2527 	drq->sdr_soc_hdr.sh_flags = SOC_UNSOLICITED | SOC_NO_RESPONSE;
2528 	drq->sdr_soc_hdr.sh_class = 0;
2529 	drq->sdr_soc_hdr.sh_seg_cnt = 1;
2530 	drq->sdr_soc_hdr.sh_byte_cnt = 0;
2531 
2532 	drq->sdr_dataseg[0].fc_base =
2533 	    (uint32_t)socalp->pool_dcookie.dmac_address;
2534 	drq->sdr_dataseg[0].fc_count = SOCAL_POOL_SIZE;
2535 	drq->sdr_dataseg[1].fc_base = 0;
2536 	drq->sdr_dataseg[1].fc_count = 0;
2537 	drq->sdr_dataseg[2].fc_base = 0;
2538 	drq->sdr_dataseg[2].fc_count = 0;
2539 	drq->sdr_dataseg[3].fc_base = 0;
2540 	drq->sdr_dataseg[3].fc_count = 0;
2541 	drq->sdr_dataseg[4].fc_base = 0;
2542 	drq->sdr_dataseg[4].fc_count = 0;
2543 	drq->sdr_dataseg[5].fc_base = 0;
2544 	drq->sdr_dataseg[5].fc_count = 0;
2545 
2546 	drq->sdr_cqhdr.cq_hdr_count = 1;
2547 	drq->sdr_cqhdr.cq_hdr_type = CQ_TYPE_ADD_BUFFER;
2548 	drq->sdr_cqhdr.cq_hdr_flags = 0;
2549 	drq->sdr_cqhdr.cq_hdr_seqno = 0;
2550 
2551 	/* Transport the request. */
2552 	result = socal_cq_enque(socalp, NULL, (cqe_t *)drq, CQ_REQUEST_1,
2553 	    FCAL_NOSLEEP, NULL, 0);
2554 	kmem_free((void *)drq, sizeof (soc_data_request_t));
2555 	return (result);
2556 
2557 fail:
2558 	socal_disp_err(socalp, CE_WARN, "driver.4110",
2559 	    "!Buffer pool DVMA alloc failed");
2560 	if (socalp->pool_dhandle) {
2561 		if (bound)
2562 			(void) ddi_dma_unbind_handle(socalp->pool_dhandle);
2563 		ddi_dma_free_handle(&socalp->pool_dhandle);
2564 	}
2565 	if (socalp->pool)
2566 		ddi_dma_mem_free(&socalp->pool_acchandle);
2567 	socalp->pool_dhandle = NULL;
2568 	return (FCAL_FAILURE);
2569 }
2570 
2571 static uint_t
2572 socal_transport(fcal_packet_t *fcalpkt, fcal_sleep_t sleep, int req_q_no)
2573 {
2574 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
2575 	socal_port_t	*port_statep;
2576 #if defined(DEBUG) && !defined(lint)
2577 	int		instance = ddi_get_instance(socalp->dip);
2578 #endif
2579 	int		port;
2580 	soc_request_t	*sp = (soc_request_t *)&fcalpkt->fcal_socal_request;
2581 
2582 	if (sp->sr_soc_hdr.sh_flags & SOC_PORT_B)
2583 		port = 1;
2584 	else
2585 		port = 0;
2586 	port_statep = &socalp->port_state[port];
2587 
2588 	DEBUGF(4, (CE_CONT, "socal%d: transport: packet, sleep = %p, %d\n",
2589 	    instance, fcalpkt, sleep));
2590 
2591 	fcalpkt->fcal_cmd_state = 0;
2592 	fcalpkt->fcal_pkt_flags &= ~(FCFLAG_COMPLETE | FCFLAG_ABORTING);
2593 
2594 	return (socal_cq_enque(socalp, port_statep, (cqe_t *)sp,
2595 	    req_q_no, sleep, fcalpkt, 0));
2596 }
2597 
2598 /*
2599  * Function name : socal_cq_enque()
2600  *
2601  * Return Values :
2602  *		FCAL_TRANSPORT_SUCCESS, if able to que the entry.
2603  *		FCAL_TRANSPORT_QFULL, if queue full & sleep not set
2604  *		FCAL_TRANSPORT_UNAVAIL if this port down
2605  *
2606  * Description	 : Enqueues an entry into the solicited request
2607  *		   queue
2608  *
2609  * Context	:
2610  */
2611 
2612 /*ARGSUSED*/
2613 static int
2614 socal_cq_enque(socal_state_t *socalp, socal_port_t *port_statep, cqe_t *cqe,
2615 		int rqix, fcal_sleep_t sleep, fcal_packet_t *to_queue,
2616 		int mtxheld)
2617 {
2618 #if defined(DEBUG) && !defined(lint)
2619 	int 		instance = ddi_get_instance(socalp->dip);
2620 #endif
2621 	socal_kcq_t	*kcq;
2622 	cqe_t		*sp;
2623 	uint_t		bitmask, wmask;
2624 	uchar_t		out;
2625 	uchar_t		s_out;
2626 	longlong_t	*p, *q;
2627 
2628 	kcq = &socalp->request[rqix];
2629 
2630 	bitmask = SOCAL_CSR_1ST_H_TO_S << rqix;
2631 	wmask = SOCAL_CSR_SOCAL_TO_HOST | bitmask;
2632 	p = (longlong_t *)cqe;
2633 
2634 	/*
2635 	 * Since we're only reading we don't need a mutex.
2636 	 */
2637 	if (socalp->socal_shutdown) {
2638 		return (FCAL_TRANSPORT_UNAVAIL);
2639 	}
2640 	/*
2641 	 * Get a token early.  That way we won't sleep
2642 	 * in id32_alloc() with a mutex held.
2643 	 */
2644 	if (to_queue) {
2645 		if ((to_queue->fcal_socal_request.sr_soc_hdr.sh_request_token =
2646 		    SOCAL_ID_GET(to_queue, mtxheld ? FCAL_NOSLEEP :
2647 		    sleep)) == NULL) {
2648 			return (FCAL_TRANSPORT_QFULL);
2649 		}
2650 	}
2651 	/*
2652 	 * Grab lock for request queue.
2653 	 */
2654 
2655 	if (!mtxheld)
2656 		mutex_enter(&kcq->skc_mtx);
2657 
2658 	/*
2659 	 * Determine if the queue is full
2660 	 */
2661 
2662 	do {
2663 
2664 		if (kcq->skc_full) {
2665 		/*
2666 		 * If soc's queue full, then we wait for an interrupt
2667 		 * telling us we are not full.
2668 		 */
2669 
2670 			if (to_queue) {
2671 			to_queue->fcal_pkt_next = NULL;
2672 			if (!kcq->skc_overflowh) {
2673 				DEBUGF(2, (CE_CONT,
2674 				    "socal%d: cq_enque: request "
2675 				    "que %d is full\n",
2676 				    instance, rqix));
2677 				kcq->skc_overflowh = to_queue;
2678 				socalp->socal_stats.qfulls++;
2679 			} else
2680 				kcq->skc_overflowt->fcal_pkt_next = to_queue;
2681 			kcq->skc_overflowt = to_queue;
2682 
2683 			mutex_enter(&socalp->k_imr_mtx);
2684 			socalp->socal_rp->socal_imr =
2685 			    (socalp->socal_k_imr |= bitmask);
2686 			mutex_exit(&socalp->k_imr_mtx);
2687 			to_queue->fcal_cmd_state |= FCAL_CMD_IN_TRANSPORT;
2688 			if (!mtxheld)
2689 				mutex_exit(&kcq->skc_mtx);
2690 			return (FCAL_TRANSPORT_SUCCESS);
2691 			}
2692 
2693 			if (!mtxheld)
2694 			mutex_exit(&kcq->skc_mtx);
2695 			return (FCAL_TRANSPORT_QFULL);
2696 		}
2697 
2698 		if (((kcq->skc_in + 1) & kcq->skc_last_index)
2699 		    == (out = kcq->skc_out)) {
2700 		/*
2701 		 * get SOC+'s copy of out to update our copy of out
2702 		 */
2703 		s_out =
2704 		    SOCAL_REQUESTQ_INDEX(rqix, socalp->socal_rp->socal_reqp.w);
2705 		DEBUGF(2, (CE_CONT,
2706 		    "socal%d: cq_enque: &XRAM cq_in: 0x%p s_out.out 0x%x\n",
2707 		    instance, &kcq->skc_xram_cqdesc->cq_in, s_out));
2708 
2709 		kcq->skc_out = out = s_out;
2710 		/* if soc+'s que still full set flag */
2711 		kcq->skc_full = ((((kcq->skc_in + 1) &
2712 		    kcq->skc_last_index) == out)) ? SOCAL_SKC_FULL : 0;
2713 		}
2714 
2715 	} while (kcq->skc_full);
2716 
2717 	/* Now enque the entry. */
2718 	sp = &(kcq->skc_cq[kcq->skc_in]);
2719 	cqe->cqe_hdr.cq_hdr_seqno = kcq->skc_seqno;
2720 
2721 	/* Give the entry to the SOC. */
2722 	q = (longlong_t *)sp;
2723 	*q++ = *p++;
2724 	*q++ = *p++;
2725 	*q++ = *p++;
2726 	*q++ = *p++;
2727 	*q++ = *p++;
2728 	*q++ = *p++;
2729 	*q++ = *p++;
2730 	*q = *p;
2731 	(void) ddi_dma_sync(kcq->skc_dhandle, (int)((caddr_t)sp -
2732 	    (caddr_t)kcq->skc_cq), sizeof (cqe_t), DDI_DMA_SYNC_FORDEV);
2733 	if (to_queue)
2734 		to_queue->fcal_cmd_state |= FCAL_CMD_IN_TRANSPORT;
2735 
2736 	/*
2737 	 * Update circular queue and ring SOC's doorbell.
2738 	 */
2739 	kcq->skc_in++;
2740 	if ((kcq->skc_in & kcq->skc_last_index) == 0) {
2741 		kcq->skc_in = 0;
2742 		kcq->skc_seqno++;
2743 	}
2744 
2745 	socalp->socal_rp->socal_csr.w = wmask | (kcq->skc_in << 24);
2746 	/* Let lock go for request queue. */
2747 	if (!mtxheld)
2748 		mutex_exit(&kcq->skc_mtx);
2749 
2750 	return (FCAL_TRANSPORT_SUCCESS);
2751 }
2752 
2753 static uint_t
2754 socal_transport_poll(fcal_packet_t *fcalpkt, uint_t timeout, int req_q_no)
2755 {
2756 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
2757 	register volatile socal_reg_t *socalreg = socalp->socal_rp;
2758 	uint_t			csr;
2759 	socal_port_t	*port_statep;
2760 	int		port;
2761 	soc_request_t	*sp = (soc_request_t *)&fcalpkt->fcal_socal_request;
2762 	uint32_t	retval;
2763 	clock_t		ticker, t;
2764 
2765 	/* make the timeout meaningful */
2766 	timeout = drv_usectohz(timeout);
2767 	if (sp->sr_soc_hdr.sh_flags & SOC_PORT_B)
2768 		port = 1;
2769 	else
2770 		port = 0;
2771 	port_statep = &socalp->port_state[port];
2772 
2773 	fcalpkt->fcal_cmd_state = 0;
2774 	fcalpkt->fcal_pkt_flags &= ~(FCFLAG_COMPLETE | FCFLAG_ABORTING);
2775 
2776 	ticker = ddi_get_lbolt();
2777 
2778 	if ((retval = socal_cq_enque(socalp, port_statep, (cqe_t *)sp,
2779 	    req_q_no, FCAL_NOSLEEP, fcalpkt, 0)) != FCAL_TRANSPORT_SUCCESS) {
2780 		return (retval);
2781 	} else {
2782 		while (!(fcalpkt->fcal_cmd_state & FCAL_CMD_COMPLETE)) {
2783 			drv_usecwait(SOCAL_NOINTR_POLL_DELAY_TIME);
2784 			t = ddi_get_lbolt();
2785 			if ((ticker + timeout) < t)
2786 				return (FCAL_TRANSPORT_TIMEOUT);
2787 			csr = socalreg->socal_csr.w;
2788 			if ((SOCAL_INTR_CAUSE(socalp, csr)) &
2789 			    SOCAL_CSR_RSP_QUE_0) {
2790 				socal_intr_solicited(socalp, 0);
2791 			}
2792 		}
2793 	}
2794 	return (FCAL_TRANSPORT_SUCCESS);
2795 }
2796 
2797 static uint_t
2798 socal_doit(fcal_packet_t *fcalpkt, socal_port_t *port_statep, int polled,
2799     void (*func)(), int timo, int flag, uint_t *diagcode)
2800 {
2801 	clock_t lb;
2802 	uint32_t retval, status;
2803 	socal_state_t   *socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
2804 
2805 	if (polled) {
2806 		fcalpkt->fcal_pkt_comp = NULL;
2807 		status = socal_transport_poll(fcalpkt, timo, CQ_REQUEST_0);
2808 	} else {
2809 		fcalpkt->fcal_pkt_comp = func;
2810 		mutex_enter(&port_statep->sp_mtx);
2811 		port_statep->sp_status |= flag;
2812 		if ((status = socal_transport(fcalpkt, FCAL_NOSLEEP,
2813 		    CQ_REQUEST_0)) == FCAL_TRANSPORT_SUCCESS) {
2814 			lb = ddi_get_lbolt();
2815 			while (!(fcalpkt->fcal_cmd_state & FCAL_CMD_COMPLETE)) {
2816 			if ((retval = cv_timedwait(&port_statep->sp_cv,
2817 			    &port_statep->sp_mtx,
2818 			    lb+drv_usectohz(timo))) == -1) {
2819 				status = FCAL_TRANSPORT_TIMEOUT;
2820 				break;
2821 			}
2822 			}
2823 		}
2824 		port_statep->sp_status &= ~flag;
2825 		mutex_exit(&port_statep->sp_mtx);
2826 	}
2827 
2828 	switch (status) {
2829 		case FCAL_TRANSPORT_SUCCESS:
2830 			status = fcalpkt->fcal_pkt_status;
2831 			if (diagcode)
2832 				*diagcode = fcalpkt->fcal_diag_status;
2833 			switch (status) {
2834 				case FCAL_STATUS_ABORT_FAILED:
2835 					if (flag == PORT_ABORT_PENDING)
2836 						retval = FCAL_ABORT_FAILED;
2837 					break;
2838 				case FCAL_STATUS_OK:
2839 					if (flag == PORT_ABORT_PENDING)
2840 						retval = FCAL_ABORT_FAILED;
2841 					else
2842 						retval = FCAL_SUCCESS;
2843 					break;
2844 				case FCAL_STATUS_OLD_PORT:
2845 					retval = FCAL_OLD_PORT;
2846 					break;
2847 				case FCAL_STATUS_ERR_OFFLINE:
2848 					retval = FCAL_OFFLINE;
2849 					break;
2850 				case FCAL_STATUS_ABORTED:
2851 					retval = FCAL_ABORTED;
2852 					port_statep->sp_board->
2853 					    socal_stats.pstats[port_statep
2854 					    ->sp_port].abts_ok++;
2855 					break;
2856 				case FCAL_STATUS_BAD_XID:
2857 					retval = FCAL_BAD_ABORT;
2858 					break;
2859 				case FCAL_STATUS_BAD_DID:
2860 					retval = FCAL_BAD_PARAMS;
2861 					break;
2862 				case FCAL_STATUS_DIAG_BUSY:
2863 				case FCAL_STATUS_DIAG_INVALID:
2864 					retval = status;
2865 					break;
2866 				default:
2867 					retval = FCAL_LINK_ERROR;
2868 			}
2869 			break;
2870 		case FCAL_TRANSPORT_TIMEOUT:
2871 			if (flag == PORT_LIP_PENDING ||
2872 			    flag == PORT_LILP_PENDING) {
2873 				if (socal_core &&
2874 				    (socal_core & SOCAL_FAILED_LIP)) {
2875 					socal_core = 0;
2876 					socal_take_core(socalp);
2877 				}
2878 				socal_disp_err(socalp, CE_WARN, "link.6040",
2879 				"SOCAL:Forcing SOC+ reset as LIP timed out\n");
2880 				/* restart socal after resetting */
2881 				(void) socal_force_reset(port_statep->sp_board,
2882 				    polled, RESET_PORT);
2883 			}
2884 			else
2885 				(void) socal_force_lip(port_statep->sp_board,
2886 				    port_statep->sp_port, polled,
2887 				    FCAL_FORCE_LIP);
2888 			retval = FCAL_TIMEOUT;
2889 			break;
2890 		case FCAL_TRANSPORT_FAILURE:
2891 		case FCAL_BAD_PACKET:
2892 		case FCAL_TRANSPORT_UNAVAIL:
2893 		case FCAL_TRANSPORT_QFULL:
2894 			retval = status;
2895 			break;
2896 		default:
2897 			retval = FCAL_LINK_ERROR;
2898 	}
2899 	socal_packet_free(fcalpkt);
2900 	return (retval);
2901 }
2902 
2903 static uint_t
2904 socal_lilp_map(void *ssp, uint_t port, uint32_t bufid, uint_t polled)
2905 {
2906 	fcal_packet_t		*fcalpkt;
2907 	soc_data_request_t	*sdr;
2908 	socal_state_t		*socalp = (socal_state_t *)ssp;
2909 	socal_port_t		*port_statep = &socalp->port_state[port];
2910 
2911 	if ((fcalpkt =
2912 	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
2913 	    == (fcal_packet_t *)NULL)
2914 		return (FCAL_ALLOC_FAILED);
2915 
2916 	sdr = (soc_data_request_t *)&fcalpkt->fcal_socal_request;
2917 	if (port)
2918 		sdr->sdr_soc_hdr.sh_flags = SOC_PORT_B;
2919 	sdr->sdr_soc_hdr.sh_seg_cnt = 1;
2920 	sdr->sdr_soc_hdr.sh_byte_cnt = 132;
2921 	sdr->sdr_dataseg[0].fc_base = bufid;
2922 	sdr->sdr_dataseg[0].fc_count = 132;
2923 	sdr->sdr_cqhdr.cq_hdr_count = 1;
2924 	sdr->sdr_cqhdr.cq_hdr_type = CQ_TYPE_REPORT_MAP;
2925 	fcalpkt->fcal_pkt_cookie = (void *)socalp;
2926 
2927 	return (socal_doit(fcalpkt, port_statep, polled, socal_lilp_map_done,
2928 	    SOCAL_LILP_TIMEOUT, PORT_LILP_PENDING, NULL));
2929 }
2930 
2931 static uint_t
2932 socal_force_lip(void *ssp, uint_t port, uint_t polled, uint_t lip_req)
2933 {
2934 	fcal_packet_t		*fcalpkt;
2935 	soc_cmdonly_request_t	*scr;
2936 	socal_state_t		*socalp = (socal_state_t *)ssp;
2937 	socal_port_t		*port_statep = &socalp->port_state[port];
2938 
2939 
2940 	if (lip_req == FCAL_NO_LIP) {
2941 		mutex_enter(&port_statep->sp_mtx);
2942 		if ((port_statep->sp_status & PORT_ONLINE_LOOP) &&
2943 		    (port_statep->sp_unsol_cb->statec_cb != NULL)) {
2944 				mutex_exit(&port_statep->sp_mtx);
2945 				(*port_statep->sp_unsol_cb->statec_cb)
2946 				    (port_statep->sp_unsol_cb->arg,
2947 				    FCAL_STATUS_LOOP_ONLINE);
2948 			return (FCAL_SUCCESS);
2949 
2950 		} else
2951 			mutex_exit(&port_statep->sp_mtx);
2952 	}
2953 	socalp->socal_stats.pstats[port].lips++;
2954 	if ((fcalpkt =
2955 	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
2956 	    == (fcal_packet_t *)NULL)
2957 		return (FCAL_ALLOC_FAILED);
2958 
2959 	scr = (soc_cmdonly_request_t *)&fcalpkt->fcal_socal_request;
2960 	if (port)
2961 		scr->scr_soc_hdr.sh_flags = SOC_PORT_B;
2962 	scr->scr_cqhdr.cq_hdr_count = 1;
2963 	scr->scr_cqhdr.cq_hdr_type = CQ_TYPE_REQUEST_LIP;
2964 
2965 	fcalpkt->fcal_pkt_cookie = (void *)socalp;
2966 	return (socal_doit(fcalpkt, port_statep, polled, socal_force_lip_done,
2967 	    SOCAL_LIP_TIMEOUT, PORT_LIP_PENDING, NULL));
2968 }
2969 
2970 static uint_t
2971 socal_abort_cmd(void *ssp, uint_t port, fcal_packet_t *fcalpkt, uint_t polled)
2972 {
2973 	fcal_packet_t		*fcalpkt2, *fpkt;
2974 	soc_cmdonly_request_t	*scr, *tscr;
2975 	socal_state_t		*socalp = (socal_state_t *)ssp;
2976 	socal_port_t		*port_statep = &socalp->port_state[port];
2977 	socal_kcq_t		*kcq;
2978 
2979 	socalp->socal_stats.pstats[port].abts++;
2980 	kcq = &socalp->request[CQ_REQUEST_1];
2981 	mutex_enter(&kcq->skc_mtx);
2982 	fcalpkt2 = kcq->skc_overflowh;
2983 	fpkt = NULL;
2984 	while (fcalpkt2 != NULL) {
2985 		if (fcalpkt2 == fcalpkt) {
2986 			if (fpkt == NULL)
2987 				kcq->skc_overflowh = fcalpkt->fcal_pkt_next;
2988 			else {
2989 				fpkt->fcal_pkt_next = fcalpkt->fcal_pkt_next;
2990 				if (kcq->skc_overflowt == fcalpkt)
2991 					kcq->skc_overflowt = fpkt;
2992 			}
2993 			mutex_exit(&kcq->skc_mtx);
2994 			socalp->socal_stats.pstats[port].abts_ok++;
2995 			SOCAL_ID_FREE(fcalpkt->fcal_socal_request.
2996 			    sr_soc_hdr.sh_request_token);
2997 			return (FCAL_ABORTED);
2998 		} else {
2999 			fpkt = fcalpkt2;
3000 			fcalpkt2 = fcalpkt2->fcal_pkt_next;
3001 		}
3002 	}
3003 	mutex_exit(&kcq->skc_mtx);
3004 	if ((fcalpkt2 =
3005 	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
3006 	    == (fcal_packet_t *)NULL)
3007 		return (FCAL_ALLOC_FAILED);
3008 
3009 	mutex_enter(&socalp->abort_mtx);
3010 	/* Too late? */
3011 	if (fcalpkt->fcal_pkt_flags & FCFLAG_COMPLETE) {
3012 		socal_packet_free(fcalpkt2);
3013 		mutex_exit(&socalp->abort_mtx);
3014 		return (FCAL_ABORTED);
3015 		/* I lied.  So shoot me. */
3016 	}
3017 	/* Mark packet as being aborted and put it in the abort pending list. */
3018 	fcalpkt->fcal_pkt_flags |= FCFLAG_ABORTING;
3019 
3020 	scr = (soc_cmdonly_request_t *)&fcalpkt2->fcal_socal_request;
3021 	tscr = (soc_cmdonly_request_t *)&fcalpkt->fcal_socal_request;
3022 	scr->scr_soc_hdr.sh_byte_cnt = tscr->scr_soc_hdr.sh_request_token;
3023 	scr->scr_cqhdr.cq_hdr_count = 1;
3024 	scr->scr_cqhdr.cq_hdr_type = CQ_TYPE_REQUEST_ABORT;
3025 	if (port)
3026 		scr->scr_soc_hdr.sh_flags = SOC_PORT_B;
3027 	fcalpkt2->fcal_pkt_cookie = (void *)socalp;
3028 	mutex_exit(&socalp->abort_mtx);
3029 
3030 	return (socal_doit(fcalpkt2, port_statep, polled, socal_abort_done,
3031 	    SOCAL_ABORT_TIMEOUT, PORT_ABORT_PENDING, NULL));
3032 }
3033 
3034 /*ARGSUSED*/
3035 static uint_t
3036 socal_els(void *ssp, uint_t port, uint_t elscode, uint_t dest,
3037 	void (*callback)(), void *arg, caddr_t reqpl, caddr_t *rsppl,
3038 	uint_t sleep)
3039 {
3040 	return (FCAL_TRANSPORT_FAILURE);
3041 }
3042 
3043 static uint_t
3044 socal_bypass_dev(void *ssp, uint_t port, uint_t dest)
3045 {
3046 	fcal_packet_t		*fcalpkt;
3047 	soc_cmdonly_request_t	*scr;
3048 	socal_state_t		*socalp = (socal_state_t *)ssp;
3049 	socal_port_t		*port_statep = &socalp->port_state[port];
3050 
3051 	if ((fcalpkt =
3052 	    socal_packet_alloc(socalp, FCAL_SLEEP))
3053 	    == (fcal_packet_t *)NULL)
3054 		return (FCAL_ALLOC_FAILED);
3055 
3056 	scr = (soc_cmdonly_request_t *)&fcalpkt->fcal_socal_request;
3057 	if (port)
3058 		scr->scr_soc_hdr.sh_flags = SOC_PORT_B;
3059 	scr->scr_soc_hdr.sh_byte_cnt = dest;
3060 	scr->scr_cqhdr.cq_hdr_count = 1;
3061 	scr->scr_cqhdr.cq_hdr_type = CQ_TYPE_BYPASS_DEV;
3062 	return (socal_doit(fcalpkt, port_statep, 0, socal_bypass_dev_done,
3063 	    SOCAL_BYPASS_TIMEOUT, PORT_BYPASS_PENDING, NULL));
3064 }
3065 
3066 
3067 /*ARGSUSED*/
3068 static void
3069 socal_force_reset(void *ssp, uint_t port, uint_t restart)
3070 {
3071 	socal_state_t 	*socalp = (socal_state_t *)ssp;
3072 
3073 	mutex_enter(&socalp->k_imr_mtx);
3074 	if (socalp->socal_shutdown) {
3075 		mutex_exit(&socalp->k_imr_mtx);
3076 		return;
3077 	} else {
3078 		socalp->socal_shutdown = 1;
3079 		mutex_exit(&socalp->k_imr_mtx);
3080 	}
3081 	socalp->socal_stats.resets++;
3082 	socal_doreset(socalp);
3083 	if (restart) {
3084 		if (socal_start(socalp) != FCAL_SUCCESS) {
3085 			cmn_err(CE_WARN, "socal: start failed.\n");
3086 		}
3087 	}
3088 }
3089 
3090 
3091 static void
3092 socal_add_ulp(void *ssp, uint_t port, uchar_t type,
3093 	void (*ulp_statec_callback)(), void (*ulp_els_callback)(),
3094 	void (*ulp_data_callback)(), void *arg)
3095 {
3096 	socal_state_t	*socalp = (socal_state_t *)ssp;
3097 	socal_port_t	*port_statep = &socalp->port_state[port];
3098 	socal_unsol_cb_t *cbentry;
3099 
3100 	mutex_enter(&port_statep->sp_mtx);
3101 	for (cbentry = port_statep->sp_unsol_cb; cbentry;
3102 	    cbentry = cbentry->next) {
3103 		if (cbentry->type == type) {
3104 			cbentry->statec_cb = ulp_statec_callback;
3105 			cbentry->els_cb = ulp_els_callback;
3106 			cbentry->data_cb = ulp_data_callback;
3107 			cbentry->arg = arg;
3108 			mutex_exit(&port_statep->sp_mtx);
3109 			return;
3110 		}
3111 	}
3112 	mutex_exit(&port_statep->sp_mtx);
3113 	if ((cbentry =
3114 	    (socal_unsol_cb_t *)kmem_zalloc(sizeof (socal_unsol_cb_t),
3115 	    KM_SLEEP)) == (socal_unsol_cb_t *)NULL) {
3116 		return;
3117 	}
3118 	mutex_enter(&port_statep->sp_mtx);
3119 	cbentry->statec_cb = ulp_statec_callback;
3120 	cbentry->els_cb = ulp_els_callback;
3121 	cbentry->data_cb = ulp_data_callback;
3122 	cbentry->arg = arg;
3123 	cbentry->type = type;
3124 
3125 	cbentry->next = port_statep->sp_unsol_cb;
3126 	port_statep->sp_unsol_cb = cbentry;
3127 	mutex_exit(&port_statep->sp_mtx);
3128 }
3129 
3130 
3131 /*
3132  * remove a ULP with matching type and arg
3133  */
3134 static void
3135 socal_remove_ulp(void *ssp, uint_t port, uchar_t type, void *arg)
3136 {
3137 	socal_state_t		*socalp = (socal_state_t *)ssp;
3138 	socal_port_t		*port_statep;
3139 	socal_unsol_cb_t	*cbentry;
3140 	socal_unsol_cb_t	*p_cbentry;
3141 
3142 
3143 	ASSERT(ssp != NULL);
3144 	port_statep = &socalp->port_state[port];
3145 	ASSERT(port_statep != NULL);
3146 
3147 	/* scan the list of unsolicited callback entries */
3148 	mutex_enter(&port_statep->sp_mtx);
3149 	p_cbentry = NULL;
3150 	for (cbentry = port_statep->sp_unsol_cb;
3151 	    cbentry != NULL;
3152 	    p_cbentry = cbentry, cbentry = cbentry->next) {
3153 		if ((cbentry->type != type) || (cbentry->arg != arg)) {
3154 			continue;	/* this entry  doesn't match */
3155 		}
3156 		/* found entry to remove */
3157 		if (port_statep->sp_unsol_cb == cbentry) {
3158 			/* remove first entry in list */
3159 			port_statep->sp_unsol_cb = cbentry->next;
3160 		} else {
3161 			/* remove other entry in list */
3162 			if (p_cbentry)
3163 				p_cbentry->next = cbentry->next;
3164 		}
3165 		kmem_free((void *)cbentry, sizeof (socal_unsol_cb_t));
3166 		DEBUGF(2, (CE_CONT, "socal port %d ULP removed\n", port));
3167 		break;
3168 	}
3169 	mutex_exit(&port_statep->sp_mtx);
3170 }
3171 
3172 
3173 /*
3174  * static unsigned int
3175  * socal_intr() - this is the interrupt routine for the SOC. Process all
3176  *	possible incoming interrupts from the soc device.
3177  */
3178 
3179 static unsigned int
3180 socal_intr(caddr_t arg)
3181 {
3182 	socal_state_t *socalp = (socal_state_t *)arg;
3183 	register volatile socal_reg_t *socalreg = socalp->socal_rp;
3184 	unsigned csr;
3185 	int cause = 0;
3186 #if !defined(lint)
3187 	int instance = ddi_get_instance(socalp->dip);
3188 #endif
3189 	int i, j, request;
3190 	char full;
3191 	struct fcal_packet *fpkt, *nfpkt;
3192 
3193 	csr = socalreg->socal_csr.w;
3194 	cause = (int)SOCAL_INTR_CAUSE(socalp, csr);
3195 
3196 	DEBUGF(2, (CE_CONT,
3197 	    "socal%d: intr: csr: 0x%x cause: 0x%x\n",
3198 	    instance, csr, cause));
3199 
3200 	if (!cause) {
3201 		socalp->socal_on_intr = 0;
3202 		return (DDI_INTR_UNCLAIMED);
3203 	}
3204 
3205 	socalp->socal_on_intr = 1;
3206 
3207 	while (cause) {
3208 
3209 	/*
3210 	 * Process the unsolicited messages first in case there are some
3211 	 * high priority async events that we should act on.
3212 	 *
3213 	 */
3214 
3215 		if (cause & SOCAL_CSR_RSP_QUE_1) {
3216 			socal_intr_unsolicited(socalp, 1);
3217 	DEBUGF(4, (CE_CONT, "socal%d intr: did unsolicited\n", instance));
3218 		}
3219 
3220 		if (cause & SOCAL_CSR_RSP_QUE_0) {
3221 			socal_intr_solicited(socalp, 0);
3222 	DEBUGF(4, (CE_CONT, "socal%d intr: did solicited\n", instance));
3223 		}
3224 
3225 	/*
3226 	 * for use with token-only response queues in the future
3227 	 * if (cause & SOCAL_CSR_RSP_QUE_0) {
3228 	 *	socal_intr_solicited(socalp, 0);
3229 	 * }
3230 	 */
3231 
3232 
3233 	/*
3234 	 * Process any request interrupts
3235 	 * We only allow request interrupts when the request
3236 	 * queue is full and we are waiting so we can enque
3237 	 * another command.
3238 	 */
3239 		if ((request = (cause & SOCAL_CSR_HOST_TO_SOCAL)) != 0) {
3240 		socalp->socal_stats.reqq_intrs++;
3241 		for (i = SOCAL_CSR_1ST_H_TO_S, j = 0; j < SOCAL_N_CQS;
3242 		    j++, i <<= 1) {
3243 			if (request & i) {
3244 			socal_kcq_t *kcq = &socalp->request[j];
3245 
3246 			if (kcq->skc_full) {
3247 				mutex_enter(&kcq->skc_mtx);
3248 				full = kcq->skc_full;
3249 				kcq->skc_full = 0;
3250 				while ((fpkt = kcq->skc_overflowh) != NULL) {
3251 				nfpkt = fpkt->fcal_pkt_next;
3252 				fpkt->fcal_pkt_next = NULL;
3253 				kcq->skc_overflowh = nfpkt;
3254 				if (socal_cq_enque(socalp, (socal_port_t *)
3255 				    fpkt->fcal_pkt_cookie,
3256 				    (cqe_t *)&fpkt->fcal_socal_request,
3257 				    j, FCAL_NOSLEEP, NULL, 1) !=
3258 				    FCAL_TRANSPORT_SUCCESS) {
3259 					break;
3260 				}
3261 				}
3262 				if (!kcq->skc_overflowh) {
3263 				if (full & SOCAL_SKC_SLEEP)
3264 					cv_broadcast(&kcq->skc_cv);
3265 
3266 			    /* Disable this queue's intrs */
3267 				DEBUGF(2, (CE_CONT,
3268 				    "socal%d: req que %d overflow cleared\n",
3269 				    instance, j));
3270 				mutex_enter(&socalp->k_imr_mtx);
3271 				socalp->socal_rp->socal_imr =
3272 				    (socalp->socal_k_imr &= ~i);
3273 				mutex_exit(&socalp->k_imr_mtx);
3274 				}
3275 				mutex_exit(&kcq->skc_mtx);
3276 			}
3277 			}
3278 		}
3279 		}
3280 		csr = socalreg->socal_csr.w;
3281 		cause = (int)SOCAL_INTR_CAUSE(socalp, csr);
3282 	DEBUGF(4, (CE_CONT, "socal%d intr: did request queues\n", instance));
3283 
3284 	}
3285 
3286 	socalp->socal_on_intr = 0;
3287 	return (DDI_INTR_CLAIMED);
3288 }
3289 
3290 static void
3291 socal_intr_solicited(socal_state_t *socalp, uint32_t srq)
3292 {
3293 	socal_kcq_t		*kcq;
3294 	volatile socal_kcq_t	*kcqv;
3295 	soc_response_t		*srp;
3296 	cqe_t			*cqe;
3297 	uint_t			status, i;
3298 	fcal_packet_t		*fcalpkt = NULL;
3299 	soc_header_t		*shp;
3300 	register volatile socal_reg_t *socalreg = socalp->socal_rp;
3301 	caddr_t			src, dst;
3302 	uchar_t			index_in;
3303 	cq_hdr_t		*cq_hdr;
3304 	char			val;
3305 	int			port;
3306 
3307 #if defined(DEBUG) && !defined(lint)
3308 	int instance = ddi_get_instance(socalp->dip);
3309 #endif
3310 	auto char buf[80];
3311 
3312 	kcq = &socalp->response[srq];
3313 	kcqv = (volatile socal_kcq_t *)kcq;
3314 	DEBUGF(4, (CE_CONT, "socal%d intr_sol: entered \n", instance));
3315 
3316 	/*
3317 	 * Grab lock for request queue.
3318 	 */
3319 	mutex_enter(&kcq->skc_mtx);
3320 
3321 	/*
3322 	 * Process as many response queue entries as we can.
3323 	 */
3324 	cqe = &(kcq->skc_cq[kcqv->skc_out]);
3325 
3326 	index_in = SOCAL_RESPONSEQ_INDEX(srq, socalreg->socal_rspp.w);
3327 
3328 	if (index_in == kcqv->skc_out) {
3329 		socalreg->socal_csr.w = ((kcqv->skc_out << 24) |
3330 		    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_0));
3331 
3332 		/* make sure the write completed */
3333 		i = socalreg->socal_csr.w;
3334 
3335 		index_in = SOCAL_RESPONSEQ_INDEX(srq, socalreg->socal_rspp.w);
3336 	}
3337 
3338 	kcqv->skc_in = index_in;
3339 
3340 	while (kcqv->skc_out != index_in) {
3341 		/* Find out where the newest entry lives in the queue */
3342 		(void) ddi_dma_sync(kcq->skc_dhandle, 0, 0,
3343 		    DDI_DMA_SYNC_FORKERNEL);
3344 
3345 		srp = (soc_response_t *)cqe;
3346 		port = srp->sr_soc_hdr.sh_flags & SOC_PORT_B;
3347 		shp = &srp->sr_soc_hdr;
3348 		cq_hdr = &srp->sr_cqhdr;
3349 		/*
3350 		 * It turns out that on faster CPU's we have a problem where
3351 		 * the soc interrupts us before the response has been DMA'ed
3352 		 * in. This should not happen but does !!. So to workaround
3353 		 * the problem for now, check the sequence # of the response.
3354 		 * If it does not match with what we have, we must be
3355 		 * reading stale data
3356 		 */
3357 		if (cq_hdr->cq_hdr_seqno != kcqv->skc_seqno) {
3358 #if defined(DEBUG) && !defined(lint)
3359 			socal_read_stale_data++;
3360 #endif
3361 			if (kcq->deferred_intr_timeoutid) {
3362 				mutex_exit(&kcq->skc_mtx);
3363 				return;
3364 			} else {
3365 				kcq->skc_saved_out = kcqv->skc_out;
3366 				kcq->skc_saved_seqno = kcqv->skc_seqno;
3367 				kcq->deferred_intr_timeoutid = timeout(
3368 				    socal_deferred_intr, (caddr_t)kcq,
3369 				    drv_usectohz(10000));
3370 				mutex_exit(&kcq->skc_mtx);
3371 				return;
3372 			}
3373 		}
3374 
3375 		fcalpkt = (fcal_packet_t *)
3376 		    SOCAL_ID_LOOKUP(shp->sh_request_token);
3377 
3378 		if ((socal_core & SOCAL_TAKE_CORE) && ddi_peek8(socalp->dip,
3379 		    (char *)fcalpkt, &val) != DDI_SUCCESS) {
3380 			cmn_err(CE_WARN, "bad token = %p\n", (void *)fcalpkt);
3381 			mutex_exit(&kcq->skc_mtx);
3382 			socal_take_core(socalp);
3383 		}
3384 
3385 		if ((fcalpkt == (fcal_packet_t *)NULL) ||
3386 		    (fcalpkt->fcal_magic != FCALP_MAGIC)) {
3387 			(void) sprintf(buf, "!invalid FC packet; \n\
3388 			    in, out, seqno = 0x%x, 0x%x, 0x%x\n",
3389 			    kcqv->skc_in, kcqv->skc_out, kcqv->skc_seqno);
3390 			socal_disp_err(socalp, CE_WARN, "link.4060", buf);
3391 			DEBUGF(4, (CE_CONT,
3392 			    "\tsoc CR: 0x%x SAE: 0x%x CSR: 0x%x IMR: 0x%x\n",
3393 			    socalreg->socal_cr.w,
3394 			    socalreg->socal_sae.w,
3395 			    socalreg->socal_csr.w,
3396 			    socalreg->socal_imr));
3397 		/*
3398 		 * Update response queue ptrs and soc registers.
3399 		 */
3400 			kcqv->skc_out++;
3401 			if ((kcqv->skc_out & kcq->skc_last_index) == 0) {
3402 				kcqv->skc_out = 0;
3403 				kcqv->skc_seqno++;
3404 			}
3405 
3406 		} else {
3407 
3408 			DEBUGF(2, (CE_CONT, "packet 0x%p complete\n",
3409 			    fcalpkt));
3410 			status = srp->sr_soc_status;
3411 			fcalpkt->fcal_pkt_status = status;
3412 			DEBUGF(2, (CE_CONT, "SOC status: 0x%x\n", status));
3413 			/*
3414 			 * map soc status codes to
3415 			 * transport status codes
3416 			 */
3417 
3418 			ASSERT((fcalpkt->fcal_cmd_state & FCAL_CMD_COMPLETE)
3419 			    == 0);
3420 			mutex_enter(&socalp->abort_mtx);
3421 			fcalpkt->fcal_pkt_flags |= FCFLAG_COMPLETE;
3422 			mutex_exit(&socalp->abort_mtx);
3423 
3424 			/*
3425 			 * Copy the response frame header (if there is one)
3426 			 * so that the upper levels can use it.  Note that,
3427 			 * for now, we'll copy the header only if there was
3428 			 * some sort of non-OK status, to save the PIO reads
3429 			 * required to get the header from the host adapter's
3430 			 * xRAM.
3431 			 */
3432 			if (((status != FCAL_STATUS_OK) ||
3433 			    (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags
3434 			    & SOC_RESP_HEADER)) &&
3435 			    (srp->sr_soc_hdr.sh_flags & SOC_FC_HEADER)) {
3436 				src = (caddr_t)&srp->sr_fc_frame_hdr;
3437 				dst = (caddr_t)&fcalpkt->fcal_resp_hdr;
3438 				bcopy(src, dst, sizeof (fc_frame_header_t));
3439 				fcalpkt->fcal_pkt_flags |= FCFLAG_RESP_HEADER;
3440 				i = srp->sr_soc_hdr.sh_flags & SOC_PORT_B ?
3441 				    1 : 0;
3442 				if ((status != FCAL_STATUS_OK) &&
3443 				    (status <= FCAL_STATUS_MAX_STATUS)) {
3444 					socalp->socal_stats.pstats[i].
3445 					    resp_status[status]++;
3446 				} else {
3447 					socalp->socal_stats.pstats[i].
3448 					    resp_status[FCAL_STATUS_ERROR]++;
3449 				}
3450 			} else if (status == FCAL_STATUS_OK) {
3451 				fcalpkt->fcal_socal_request.
3452 				    sr_soc_hdr.sh_byte_cnt =
3453 				    shp->sh_byte_cnt;
3454 			}
3455 			fcalpkt->fcal_diag_status =
3456 			    (uint32_t)srp->sr_dataseg.fc_base;
3457 			fcalpkt->fcal_ncmds = srp->sr_ncmds;
3458 
3459 			/*
3460 			 * Update response queue ptrs and soc registers.
3461 			 */
3462 			kcqv->skc_out++;
3463 			if ((kcqv->skc_out & kcq->skc_last_index) == 0) {
3464 				kcqv->skc_out = 0;
3465 				kcqv->skc_seqno++;
3466 			}
3467 
3468 			/* For incmplt DMA offline loop by loopback */
3469 			if (fcalpkt->fcal_pkt_status ==
3470 			    FCAL_STATUS_INCOMPLETE_DMA_ERR) {
3471 				socal_port_t	*port_statep;
3472 				uint_t		r;
3473 
3474 				/*
3475 				 * Give up the mutex to avoid a deadlock
3476 				 * with the loopback routine.
3477 				 */
3478 				mutex_exit(&kcq->skc_mtx);
3479 
3480 				port_statep = &socalp->port_state[port];
3481 				mutex_enter(&port_statep->sp_mtx);
3482 				if (port_statep->sp_status &
3483 				    PORT_DISABLED) {
3484 					/* Already disabled */
3485 					mutex_exit(&port_statep->sp_mtx);
3486 				} else {
3487 					port_statep->sp_status |=
3488 					    PORT_DISABLED;
3489 					mutex_exit(&port_statep->sp_mtx);
3490 					(void) socal_diag_request(
3491 					    (void *)socalp, port,
3492 					    &r, SOC_DIAG_INT_LOOP);
3493 				}
3494 				/* reacquire mutex */
3495 				mutex_enter(&kcq->skc_mtx);
3496 			}
3497 
3498 			/*
3499 			 * Complete the packet *ONLY* if it not being aborted
3500 			 * or the abort has already completed.  Otherwise it is
3501 			 * not safe to free the ID.
3502 			 */
3503 			mutex_enter(&socalp->abort_mtx);
3504 			if (!(fcalpkt->fcal_pkt_flags & FCFLAG_ABORTING)) {
3505 				/*
3506 				 * Call the completion routine
3507 				 */
3508 				SOCAL_ID_FREE(shp->sh_request_token);
3509 				if (fcalpkt->fcal_pkt_comp != NULL) {
3510 					fcalpkt->fcal_cmd_state |=
3511 					    FCAL_CMD_COMPLETE;
3512 
3513 					/*
3514 					 * Give up the mutex to avoid a
3515 					 * deadlock with the callback routine.
3516 					 */
3517 					mutex_exit(&socalp->abort_mtx);
3518 					mutex_exit(&kcq->skc_mtx);
3519 
3520 					/* callback */
3521 					(*fcalpkt->fcal_pkt_comp)(fcalpkt);
3522 
3523 					/* reacquire mutex */
3524 					mutex_enter(&kcq->skc_mtx);
3525 				} else {
3526 					fcalpkt->fcal_cmd_state |=
3527 					    FCAL_CMD_COMPLETE;
3528 					mutex_exit(&socalp->abort_mtx);
3529 				}
3530 			} else {
3531 				mutex_exit(&socalp->abort_mtx);
3532 			}
3533 		}
3534 
3535 
3536 		if (kcq->skc_cq == NULL)
3537 			/*
3538 			 * This action averts a potential PANIC scenario
3539 			 * where the SUSPEND code flow grabbed the kcq->skc_mtx
3540 			 * when we let it go, to call our completion routine,
3541 			 * and "initialized" the response queue.  We exit our
3542 			 * processing loop here, thereby averting a PANIC due
3543 			 * to a NULL de-reference from the response queue.
3544 			 *
3545 			 * Note that this is an interim measure that needs
3546 			 * to be revisited when this driver is next revised
3547 			 * for enhanced performance.
3548 			 */
3549 			break;
3550 
3551 		/*
3552 		 * We need to re-read the input and output pointers in
3553 		 * case a polling routine should process some entries
3554 		 * from the response queue while we're doing a callback
3555 		 * routine with the response queue mutex dropped.
3556 		 */
3557 		cqe = &(kcq->skc_cq[kcqv->skc_out]);
3558 		index_in = SOCAL_RESPONSEQ_INDEX(srq, socalreg->socal_rspp.w);
3559 
3560 		/*
3561 		 * Mess around with the hardware if we think we've run out
3562 		 * of entries in the queue, just to make sure we've read
3563 		 * all entries that are available.
3564 		 */
3565 
3566 		socalreg->socal_csr.w = ((kcqv->skc_out << 24) |
3567 		    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_0));
3568 
3569 		/* Make sure the csr write has completed */
3570 		i = socalreg->socal_csr.w;
3571 		DEBUGF(9, (CE_CONT, "csr.w = %x\n", i));
3572 
3573 		/*
3574 		 * Update our idea of where the host adapter has placed
3575 		 * the most recent entry in the response queue and resync
3576 		 * the response queue
3577 		 */
3578 		index_in = SOCAL_RESPONSEQ_INDEX(srq, socalreg->socal_rspp.w);
3579 
3580 		kcqv->skc_in = index_in;
3581 	}
3582 
3583 	/* Drop lock for request queue. */
3584 	mutex_exit(&kcq->skc_mtx);
3585 }
3586 
3587 /*
3588  * Function name : socal_intr_unsolicited()
3589  *
3590  * Return Values : none
3591  *
3592  * Description	 : Processes entries in the unsolicited response
3593  *		   queue
3594  *
3595  *	The SOC+ will give us an unsolicited response
3596  *	whenever its status changes: OFFLINE, ONLINE,
3597  *	or in response to a packet arriving from an originator.
3598  *
3599  *	When message requests come in they will be placed in our
3600  *	buffer queue or in the next "inline" packet by the SOC hardware.
3601  *
3602  * Context	: Unsolicited interrupts must be masked
3603  */
3604 
3605 static void
3606 socal_intr_unsolicited(socal_state_t *socalp, uint32_t urq)
3607 {
3608 	socal_kcq_t		*kcq;
3609 	volatile socal_kcq_t	*kcqv;
3610 	soc_response_t		*srp;
3611 	volatile cqe_t		*cqe;
3612 	int			port;
3613 	register uchar_t		t_index, t_seqno;
3614 	register volatile socal_reg_t *socalreg = socalp->socal_rp;
3615 	volatile cqe_t		*cqe_cont = NULL;
3616 	uint_t			i;
3617 	int			hdr_count;
3618 	int			status;
3619 	ushort_t		flags;
3620 	auto char		buf[256];
3621 	socal_port_t		*port_statep;
3622 #if defined(DEBUG) && !defined(lint)
3623 	int			instance = ddi_get_instance(socalp->dip);
3624 #endif
3625 	uchar_t			index_in;
3626 	socal_unsol_cb_t	*cblist;
3627 
3628 	kcq = &socalp->response[urq];
3629 	kcqv = (volatile socal_kcq_t *)kcq;
3630 
3631 	/*
3632 	 * Grab lock for response queue.
3633 	 */
3634 	mutex_enter(&kcq->skc_mtx);
3635 
3636 	cqe = (volatile cqe_t *)&(kcq->skc_cq[kcqv->skc_out]);
3637 
3638 	index_in = SOCAL_RESPONSEQ_INDEX(urq, socalreg->socal_rspp.w);
3639 
3640 	kcqv->skc_in = index_in;
3641 
3642 	while (kcqv->skc_out != index_in) {
3643 		(void) ddi_dma_sync(kcq->skc_dhandle, 0, 0,
3644 		    DDI_DMA_SYNC_FORKERNEL);
3645 
3646 		/* Check for continuation entries */
3647 		if ((hdr_count = cqe->cqe_hdr.cq_hdr_count) != 1) {
3648 
3649 			t_seqno = kcqv->skc_seqno;
3650 			t_index = kcqv->skc_out + hdr_count;
3651 
3652 			i = index_in;
3653 			if (kcqv->skc_out > index_in)
3654 			i += kcq->skc_last_index + 1;
3655 
3656 		/*
3657 		 * If we think the continuation entries haven't yet
3658 		 * arrived, try once more before giving up
3659 		 */
3660 			if (i < t_index) {
3661 
3662 			socalreg->socal_csr.w =
3663 			    ((kcqv->skc_out << 24) |
3664 			    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_1));
3665 
3666 			/* Make sure the csr write has completed */
3667 			i = socalreg->socal_csr.w;
3668 
3669 			/*
3670 			 * Update our idea of where the host adapter has placed
3671 			 * the most recent entry in the response queue
3672 			 */
3673 			i = index_in = SOCAL_RESPONSEQ_INDEX(urq,
3674 			    socalreg->socal_rspp.w);
3675 			if (kcqv->skc_out > index_in)
3676 				i += kcq->skc_last_index + 1;
3677 
3678 			/*
3679 			 * Exit if the continuation entries haven't yet
3680 			 * arrived
3681 			 */
3682 			if (i < t_index)
3683 				break;
3684 			}
3685 
3686 			if (t_index > kcq->skc_last_index) {
3687 			t_seqno++;
3688 			t_index &= kcq->skc_last_index;
3689 			}
3690 
3691 			cqe_cont = (volatile cqe_t *)
3692 			    &(kcq->skc_cq[t_index ? t_index - 1 :
3693 			    kcq->skc_last_index]);
3694 
3695 
3696 		    /* A cq_hdr_count > 2 is illegal; throw away the response */
3697 
3698 		/*
3699 		 * XXX - should probably throw out as many entries as the
3700 		 * hdr_cout tells us there are
3701 		 */
3702 			if (hdr_count != 2) {
3703 			socal_disp_err(socalp, CE_WARN, "driver.4030",
3704 			    "!too many continuation entries");
3705 			DEBUGF(4, (CE_CONT,
3706 			    "socal%d: soc+ unsolicited entry count = %d\n",
3707 			    instance, cqe->cqe_hdr.cq_hdr_count));
3708 
3709 			if ((++t_index & kcq->skc_last_index) == 0) {
3710 				t_index = 0;
3711 				t_seqno++;
3712 			}
3713 			kcqv->skc_out = t_index;
3714 			kcqv->skc_seqno = t_seqno;
3715 
3716 			cqe = &(kcq->skc_cq[kcqv->skc_out]);
3717 			cqe_cont = NULL;
3718 			continue;
3719 			}
3720 		}
3721 
3722 		/*
3723 		 * Update unsolicited response queue ptrs
3724 		 */
3725 		kcqv->skc_out++;
3726 		if ((kcqv->skc_out & kcq->skc_last_index) == 0) {
3727 			kcqv->skc_out = 0;
3728 			kcqv->skc_seqno++;
3729 		}
3730 
3731 		if (cqe_cont != NULL) {
3732 			kcqv->skc_out++;
3733 			if ((kcqv->skc_out & kcq->skc_last_index) == 0) {
3734 				kcqv->skc_out = 0;
3735 				kcqv->skc_seqno++;
3736 			}
3737 		}
3738 
3739 		if (index_in == kcqv->skc_out) {
3740 			socalreg->socal_csr.w = ((kcqv->skc_out << 24) |
3741 			    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_1));
3742 
3743 		/* Make sure the csr write has completed */
3744 			i = socalreg->socal_csr.w;
3745 		}
3746 
3747 		srp = (soc_response_t *)cqe;
3748 		flags = srp->sr_soc_hdr.sh_flags;
3749 		port = flags & SOC_PORT_B;
3750 		port_statep = &socalp->port_state[port];
3751 
3752 		/*
3753 		 * XXX need to deal buffer pool entries here
3754 		 */
3755 		switch (flags & ~SOC_PORT_B) {
3756 		case SOC_UNSOLICITED | SOC_FC_HEADER:
3757 
3758 			srp = (soc_response_t *)cqe;
3759 
3760 			switch (srp->sr_fc_frame_hdr.r_ctl & R_CTL_ROUTING) {
3761 			case R_CTL_EXTENDED_SVC:
3762 			/*
3763 			 * Extended Link Services frame received
3764 			 */
3765 			socalp->socal_stats.pstats[port].els_rcvd++;
3766 			socal_us_els(socalp, (cqe_t *)cqe, (caddr_t)cqe_cont);
3767 
3768 			/* do callbacks to any interested ULPs */
3769 			mutex_enter(&port_statep->sp_mtx);
3770 			for (cblist = port_statep->sp_unsol_cb; cblist;
3771 			    cblist = cblist->next) {
3772 				if (cblist->els_cb) {
3773 					mutex_exit(&port_statep->sp_mtx);
3774 					mutex_exit(&kcq->skc_mtx);
3775 					cblist->els_cb(cblist->arg,
3776 					    (cqe_t *)cqe,
3777 					    (caddr_t)cqe_cont);
3778 					mutex_enter(&kcq->skc_mtx);
3779 					mutex_enter(&port_statep->sp_mtx);
3780 				}
3781 			}
3782 			mutex_exit(&port_statep->sp_mtx);
3783 			break;
3784 			case R_CTL_BASIC_SVC:
3785 			(void) sprintf(buf,
3786 			    "!unsupported Link Service command: 0x%x",
3787 			    srp->sr_fc_frame_hdr.type);
3788 			socal_disp_err(socalp, CE_WARN, "link.4020", buf);
3789 			break;
3790 			case R_CTL_DEVICE_DATA:
3791 			switch (srp->sr_fc_frame_hdr.type) {
3792 			default:
3793 				mutex_enter(&port_statep->sp_mtx);
3794 				status = 1;
3795 				for (cblist = port_statep->sp_unsol_cb; cblist;
3796 				    cblist = cblist->next) {
3797 				if (cblist->data_cb &&
3798 				    (cblist->type ==
3799 				    srp->sr_fc_frame_hdr.type)) {
3800 					mutex_exit(&port_statep->sp_mtx);
3801 					mutex_exit(&kcq->skc_mtx);
3802 					cblist->data_cb(cblist->arg,
3803 					    (cqe_t *)cqe, (caddr_t)cqe_cont);
3804 					mutex_enter(&kcq->skc_mtx);
3805 					mutex_enter(&port_statep->sp_mtx);
3806 					status = 0;
3807 				}
3808 				}
3809 				mutex_exit(&port_statep->sp_mtx);
3810 
3811 				if (status == 0)
3812 				break;
3813 
3814 				(void) sprintf(buf,
3815 				    "!unknown FC-4 command: 0x%x",
3816 				    srp->sr_fc_frame_hdr.type);
3817 				socal_disp_err(socalp, CE_WARN,
3818 				    "link.4030", buf);
3819 				break;
3820 			}
3821 			break;
3822 			default:
3823 			(void) sprintf(buf, "!unsupported FC frame R_CTL: 0x%x",
3824 			    srp->sr_fc_frame_hdr.r_ctl);
3825 			socal_disp_err(socalp, CE_WARN, "link.4040", buf);
3826 			break;
3827 			}
3828 			break;
3829 
3830 		case SOC_STATUS: {
3831 
3832 			/*
3833 			 * Note that only the lsbyte of the status has
3834 			 * interesting information...
3835 			 */
3836 			status = srp->sr_soc_status;
3837 
3838 			switch (status) {
3839 
3840 			case FCAL_STATUS_ONLINE:
3841 				(void) sprintf(buf,
3842 				"!port %d: Fibre Channel is ONLINE\n", port);
3843 				socal_disp_err(socalp, CE_CONT, "link.6010",
3844 				    buf);
3845 				mutex_enter(&port_statep->sp_mtx);
3846 				port_statep->sp_status &= ~PORT_STATUS_MASK;
3847 				port_statep->sp_status |= PORT_ONLINE;
3848 				mutex_exit(&port_statep->sp_mtx);
3849 				socalp->socal_stats.pstats[port].onlines++;
3850 				DEBUGF(4, (CE_CONT,
3851 				    "socal%d intr_unsol: ONLINE intr\n",
3852 				    instance));
3853 				break;
3854 
3855 			case FCAL_STATUS_LOOP_ONLINE:
3856 				(void) sprintf(buf,
3857 				"!port %d: Fibre Channel Loop is ONLINE\n",
3858 				    port);
3859 				socal_disp_err(socalp, CE_CONT, "link.6010",
3860 				    buf);
3861 				mutex_enter(&port_statep->sp_mtx);
3862 				port_statep->sp_status &= ~PORT_STATUS_MASK;
3863 				port_statep->sp_status |= PORT_ONLINE_LOOP;
3864 				mutex_exit(&port_statep->sp_mtx);
3865 				socalp->socal_stats.pstats[port].online_loops++;
3866 				DEBUGF(4, (CE_CONT,
3867 				    "socal%d intr_unsol: ONLINE-LOOP intr\n",
3868 				    instance));
3869 				break;
3870 
3871 			case FCAL_STATUS_ERR_OFFLINE:
3872 				/*
3873 				 * SOC and Responder will both flush
3874 				 * all active commands.
3875 				 * So I don't have to do anything
3876 				 * until it comes back online.
3877 				 */
3878 				(void) sprintf(buf,
3879 				"!port %d: Fibre Channel is OFFLINE\n", port);
3880 				socal_disp_err(socalp, CE_CONT, "link.5010",
3881 				    buf);
3882 
3883 				mutex_enter(&port_statep->sp_mtx);
3884 				port_statep->sp_status &= ~PORT_STATUS_MASK;
3885 				port_statep->sp_status |= PORT_OFFLINE;
3886 				port_statep->sp_lilpmap_valid = 0;
3887 				mutex_exit(&port_statep->sp_mtx);
3888 				socalp->socal_stats.pstats[port].offlines++;
3889 				DEBUGF(4, (CE_CONT,
3890 				    "socal%d intr_unsol: OFFLINE intr\n",
3891 				    instance));
3892 
3893 				break;
3894 			default:
3895 				(void) sprintf(buf, "!unknown status: 0x%x\n",
3896 				    status);
3897 				socal_disp_err(socalp, CE_WARN, "link.3020",
3898 				    buf);
3899 			}
3900 			mutex_exit(&kcq->skc_mtx);
3901 			mutex_enter(&port_statep->sp_mtx);
3902 			for (cblist = port_statep->sp_unsol_cb; cblist;
3903 			    cblist = cblist->next) {
3904 				if (cblist->statec_cb) {
3905 					mutex_exit(&port_statep->sp_mtx);
3906 					(*cblist->statec_cb)(cblist->arg,
3907 					    status);
3908 					mutex_enter(&port_statep->sp_mtx);
3909 				}
3910 			}
3911 			mutex_exit(&port_statep->sp_mtx);
3912 			if (status == FCAL_STATUS_ERR_OFFLINE) {
3913 				socal_flush_overflowq(socalp, port,
3914 				    CQ_REQUEST_0);
3915 				socal_flush_overflowq(socalp, port,
3916 				    CQ_REQUEST_1);
3917 			}
3918 			mutex_enter(&kcq->skc_mtx);
3919 			break;
3920 		}
3921 		default:
3922 			(void) sprintf(buf, "!unexpected state: flags: 0x%x\n",
3923 			    flags);
3924 			socal_disp_err(socalp, CE_WARN, "link.4050", buf);
3925 			DEBUGF(4, (CE_CONT,
3926 			    "\tsoc CR: 0x%x SAE: 0x%x CSR: 0x%x IMR: 0x%x\n",
3927 			    socalp->socal_rp->socal_cr.w,
3928 			    socalp->socal_rp->socal_sae.w,
3929 			    socalp->socal_rp->socal_csr.w,
3930 			    socalp->socal_rp->socal_imr));
3931 		}
3932 
3933 
3934 		if (kcq->skc_cq == NULL)
3935 			/*
3936 			 * This action averts a potential PANIC scenario
3937 			 * where the SUSPEND code flow grabbed the kcq->skc_mtx
3938 			 * when we let it go, to call our completion routine,
3939 			 * and "initialized" the response queue.  We exit our
3940 			 * processing loop here, thereby averting a PANIC due
3941 			 * to a NULL de-reference from the response queue.
3942 			 *
3943 			 * Note that this is an interim measure that needs
3944 			 * to be revisited when this driver is next revised
3945 			 * for enhanced performance.
3946 			 */
3947 			break;
3948 
3949 		/*
3950 		 * We need to re-read the input and output pointers in
3951 		 * case a polling routine should process some entries
3952 		 * from the response queue while we're doing a callback
3953 		 * routine with the response queue mutex dropped.
3954 		 */
3955 		cqe = &(kcq->skc_cq[kcqv->skc_out]);
3956 		index_in = SOCAL_RESPONSEQ_INDEX(urq, socalreg->socal_rspp.w);
3957 		cqe_cont = NULL;
3958 
3959 		/*
3960 		 * Mess around with the hardware if we think we've run out
3961 		 * of entries in the queue, just to make sure we've read
3962 		 * all entries that are available.
3963 		 */
3964 		if (index_in == kcqv->skc_out) {
3965 
3966 			socalreg->socal_csr.w =
3967 			    ((kcqv->skc_out << 24) |
3968 			    (SOCAL_CSR_SOCAL_TO_HOST & ~SOCAL_CSR_RSP_QUE_1));
3969 
3970 		/* Make sure the csr write has completed */
3971 			i = socalreg->socal_csr.w;
3972 
3973 		/*
3974 		 * Update our idea of where the host adapter has placed
3975 		 * the most recent entry in the response queue
3976 		 */
3977 			index_in =
3978 			    SOCAL_RESPONSEQ_INDEX(urq, socalreg->socal_rspp.w);
3979 		}
3980 
3981 		socalp->socal_stats.pstats[port].unsol_resps++;
3982 
3983 		kcqv->skc_in = index_in;
3984 
3985 	}
3986 
3987 	/* Release lock for response queue. */
3988 	mutex_exit(&kcq->skc_mtx);
3989 }
3990 
3991 /*
3992  * socal_us_els() - This function handles unsolicited extended link
3993  *	service responses received from the soc.
3994  */
3995 static void
3996 socal_us_els(socal_state_t *socalp, cqe_t *cqe, caddr_t payload)
3997 {
3998 	soc_response_t	*srp = (soc_response_t *)cqe;
3999 	els_payload_t	*els = (els_payload_t *)payload;
4000 	int	i;
4001 	char   *bp;
4002 	auto	char buf[256];
4003 
4004 	/*
4005 	 * There should be a CQE continuation entry for all
4006 	 * extended link services
4007 	 */
4008 	if ((els == NULL) || ((i = srp->sr_soc_hdr.sh_byte_cnt) == 0)) {
4009 		socal_disp_err(socalp, CE_WARN, "link.4010",
4010 		"!incomplete continuation entry");
4011 		return;
4012 	}
4013 
4014 	/* Quietly impose a maximum byte count */
4015 	if (i > SOC_CQE_PAYLOAD)
4016 		i = SOC_CQE_PAYLOAD;
4017 	i -= sizeof (union els_cmd_u);
4018 
4019 	/*
4020 	 * Decode the LS_Command code
4021 	 */
4022 	switch (els->els_cmd.c.ls_command) {
4023 		case LA_ELS_DISPLAY:
4024 		els->els_data[i] = '\0';	/* terminate the string */
4025 		for (bp = (char *)&(els->els_data[0]); *bp; bp++) {
4026 			/* squash newlines */
4027 			if (*bp == '\n') *bp = ' ';
4028 		}
4029 		(void) sprintf(buf, "!message: %s\n", els->els_data);
4030 		socal_disp_err(socalp, CE_CONT, "link.1010", buf);
4031 		break;
4032 
4033 		default:
4034 		DEBUGF(3, (CE_CONT, "!unknown LS_Command, %x\n",
4035 		    els->els_cmd.i));
4036 		break;
4037 	}
4038 
4039 }
4040 
4041 /*ARGSUSED*/
4042 static fcal_packet_t *
4043 socal_packet_alloc(socal_state_t *socalp, fcal_sleep_t sleep)
4044 {
4045 	int flag;
4046 	fcal_packet_t *pkt;
4047 
4048 	if (sleep == FCAL_SLEEP)
4049 		flag = KM_SLEEP;
4050 	else
4051 		flag = KM_NOSLEEP;
4052 
4053 	pkt = (fcal_packet_t *)kmem_zalloc(sizeof (fcal_packet_t), flag);
4054 
4055 	if (pkt != (fcal_packet_t *)NULL)
4056 		pkt->fcal_magic = FCALP_MAGIC;
4057 
4058 	return (pkt);
4059 }
4060 
4061 static void
4062 socal_packet_free(fcal_packet_t *fcalpkt)
4063 {
4064 	kmem_free((void *)fcalpkt, sizeof (fcal_packet_t));
4065 }
4066 
4067 static void
4068 socal_lilp_map_done(fcal_packet_t *fcalpkt)
4069 {
4070 	uint32_t	port;
4071 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
4072 
4073 	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
4074 		port = 1;
4075 	else
4076 		port = 0;
4077 	mutex_enter(&socalp->port_state[port].sp_mtx);
4078 	socalp->port_state[port].sp_status &= ~PORT_LILP_PENDING;
4079 	cv_broadcast(&socalp->port_state[port].sp_cv);
4080 	mutex_exit(&socalp->port_state[port].sp_mtx);
4081 }
4082 
4083 static void
4084 socal_force_lip_done(fcal_packet_t *fcalpkt)
4085 {
4086 	uint32_t	port;
4087 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
4088 
4089 	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
4090 		port = 1;
4091 	else
4092 		port = 0;
4093 	mutex_enter(&socalp->port_state[port].sp_mtx);
4094 	socalp->port_state[port].sp_status &= ~PORT_LIP_PENDING;
4095 	cv_broadcast(&socalp->port_state[port].sp_cv);
4096 	mutex_exit(&socalp->port_state[port].sp_mtx);
4097 }
4098 
4099 static void
4100 socal_adisc_done(fcal_packet_t *fcalpkt)
4101 {
4102 	uint32_t	port;
4103 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
4104 
4105 	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
4106 		port = 1;
4107 	else
4108 		port = 0;
4109 	mutex_enter(&socalp->port_state[port].sp_mtx);
4110 	socalp->port_state[port].sp_status &= ~PORT_ADISC_PENDING;
4111 	cv_broadcast(&socalp->port_state[port].sp_cv);
4112 	mutex_exit(&socalp->port_state[port].sp_mtx);
4113 }
4114 
4115 static void
4116 socal_lbf_done(fcal_packet_t *fcalpkt)
4117 {
4118 	uint32_t	port;
4119 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
4120 
4121 	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
4122 		port = 1;
4123 	else
4124 		port = 0;
4125 	mutex_enter(&socalp->port_state[port].sp_mtx);
4126 	socalp->port_state[port].sp_status &= ~PORT_LBF_PENDING;
4127 	cv_broadcast(&socalp->port_state[port].sp_cv);
4128 	mutex_exit(&socalp->port_state[port].sp_mtx);
4129 }
4130 
4131 static void
4132 socal_rls_done(fcal_packet_t *fcalpkt)
4133 {
4134 	uint32_t	port;
4135 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
4136 
4137 	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
4138 		port = 1;
4139 	else
4140 		port = 0;
4141 	mutex_enter(&socalp->port_state[port].sp_mtx);
4142 	socalp->port_state[port].sp_status &= ~PORT_RLS_PENDING;
4143 	cv_broadcast(&socalp->port_state[port].sp_cv);
4144 	mutex_exit(&socalp->port_state[port].sp_mtx);
4145 }
4146 
4147 static void
4148 socal_force_offline_done(fcal_packet_t *fcalpkt)
4149 {
4150 	uint32_t	port;
4151 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
4152 
4153 	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
4154 		port = 1;
4155 	else
4156 		port = 0;
4157 	mutex_enter(&socalp->port_state[port].sp_mtx);
4158 	socalp->port_state[port].sp_status &= ~PORT_OFFLINE_PENDING;
4159 	cv_broadcast(&socalp->port_state[port].sp_cv);
4160 	mutex_exit(&socalp->port_state[port].sp_mtx);
4161 }
4162 
4163 static void
4164 socal_abort_done(fcal_packet_t *fcalpkt)
4165 {
4166 	uint32_t	port;
4167 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
4168 	soc_header_t	*shp =
4169 	    (soc_header_t *)&fcalpkt->fcal_socal_request.sr_soc_hdr;
4170 	fcal_packet_t	*target = (fcal_packet_t *)
4171 	    SOCAL_ID_LOOKUP(shp->sh_request_token);
4172 
4173 	mutex_enter(&socalp->abort_mtx);
4174 	ASSERT(target->fcal_pkt_flags & FCFLAG_ABORTING);
4175 	if (!(target->fcal_pkt_flags & FCFLAG_COMPLETE)) {
4176 		SOCAL_ID_FREE(shp->sh_request_token);
4177 	}
4178 	mutex_exit(&socalp->abort_mtx);
4179 	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
4180 		port = 1;
4181 	else
4182 		port = 0;
4183 	mutex_enter(&socalp->port_state[port].sp_mtx);
4184 	socalp->port_state[port].sp_status &= ~PORT_ABORT_PENDING;
4185 	cv_broadcast(&socalp->port_state[port].sp_cv);
4186 	mutex_exit(&socalp->port_state[port].sp_mtx);
4187 }
4188 
4189 static void
4190 socal_bypass_dev_done(fcal_packet_t *fcalpkt)
4191 {
4192 	uint32_t	port;
4193 	socal_state_t	*socalp = (socal_state_t *)fcalpkt->fcal_pkt_cookie;
4194 	if (fcalpkt->fcal_socal_request.sr_soc_hdr.sh_flags & SOC_PORT_B)
4195 		port = 1;
4196 	else
4197 		port = 0;
4198 	mutex_enter(&socalp->port_state[port].sp_mtx);
4199 	socalp->port_state[port].sp_status &= ~PORT_BYPASS_PENDING;
4200 	cv_broadcast(&socalp->port_state[port].sp_cv);
4201 	mutex_exit(&socalp->port_state[port].sp_mtx);
4202 }
4203 
4204 /*ARGSUSED*/
4205 static unsigned int
4206 socal_dummy_intr(caddr_t arg)
4207 {
4208 	return (DDI_INTR_UNCLAIMED);
4209 }
4210 
4211 static int
4212 socal_diag_request(socal_state_t *socalp, uint32_t port, uint_t *diagcode,
4213     uint32_t cmd)
4214 {
4215 	fcal_packet_t		*fcalpkt;
4216 	soc_diag_request_t	*sdr;
4217 	socal_port_t		*port_statep = &socalp->port_state[port];
4218 	struct fcal_lilp_map	map;
4219 
4220 	/* Grabbing the state mutex is totally unnecessary.... */
4221 	if (!(port_statep->sp_status & PORT_DISABLED)) {
4222 		if (socal_getmap(socalp, port, (caddr_t)&map, 0, FKIOCTL)
4223 		    != -1) {
4224 			if (map.lilp_length != 1 && ((port_statep->sp_status &
4225 			    PORT_ONLINE_LOOP) && cmd != SOC_DIAG_REM_LOOP))
4226 				return (FCAL_TRANSPORT_UNAVAIL);
4227 		}
4228 	}
4229 	if ((fcalpkt = socal_packet_alloc(socalp, FCAL_SLEEP))
4230 	    == (fcal_packet_t *)NULL)
4231 		return (FCAL_ALLOC_FAILED);
4232 	sdr = (soc_diag_request_t *)&fcalpkt->fcal_socal_request;
4233 	if (port)
4234 		sdr->sdr_soc_hdr.sh_flags = SOC_PORT_B;
4235 	sdr->sdr_diag_cmd = cmd;
4236 	sdr->sdr_cqhdr.cq_hdr_count = 1;
4237 	sdr->sdr_cqhdr.cq_hdr_type = CQ_TYPE_DIAGNOSTIC;
4238 	fcalpkt->fcal_pkt_cookie = (void *)socalp;
4239 	return (socal_doit(fcalpkt, port_statep, 1, NULL,
4240 	    SOCAL_DIAG_TIMEOUT, 0, diagcode));
4241 }
4242 
4243 static uint_t
4244 socal_force_offline(void *ssp, uint_t port, uint_t polled)
4245 {
4246 	fcal_packet_t		*fcalpkt;
4247 	soc_cmdonly_request_t	*scr;
4248 	socal_state_t		*socalp = (socal_state_t *)ssp;
4249 	socal_port_t		*port_statep = &socalp->port_state[port];
4250 
4251 	if ((fcalpkt =
4252 	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
4253 	    == (fcal_packet_t *)NULL)
4254 		return (FCAL_ALLOC_FAILED);
4255 
4256 	scr = (soc_cmdonly_request_t *)&fcalpkt->fcal_socal_request;
4257 	if (port)
4258 		scr->scr_soc_hdr.sh_flags = SOC_PORT_B;
4259 	scr->scr_cqhdr.cq_hdr_count = 1;
4260 	scr->scr_cqhdr.cq_hdr_type = CQ_TYPE_OFFLINE;
4261 	fcalpkt->fcal_pkt_cookie = (void *)socalp;
4262 	return (socal_doit(fcalpkt, port_statep, 0, socal_force_offline_done,
4263 	    SOCAL_OFFLINE_TIMEOUT, PORT_OFFLINE_PENDING, NULL));
4264 }
4265 
4266 static int
4267 socal_issue_adisc(socal_state_t *socalp, uint32_t port, uint32_t dest,
4268 	la_els_adisc_t *payload, uint32_t polled)
4269 {
4270 	int			retval;
4271 	la_els_adisc_t		*buf;
4272 	fcal_packet_t		*fcalpkt;
4273 	socal_port_t		*port_statep;
4274 	socal_priv_cmd_t 	*privp;
4275 
4276 	port_statep = &socalp->port_state[port];
4277 
4278 	if ((fcalpkt =
4279 	    socal_els_alloc(socalp, port, dest, sizeof (la_els_adisc_t),
4280 	    sizeof (la_els_adisc_t), (caddr_t *)&privp, polled))
4281 	    == (fcal_packet_t *)NULL)
4282 		return (FCAL_ALLOC_FAILED);
4283 
4284 	privp = (socal_priv_cmd_t *)fcalpkt->fcal_pkt_private;
4285 	buf = (la_els_adisc_t *)privp->cmd;
4286 	buf->ls_code = LA_ELS_ADISC;
4287 	buf->mbz[0] = 0;
4288 	buf->mbz[1] = 0;
4289 	buf->mbz[2] = 0;
4290 	buf->hard_address = 0;
4291 	bcopy((caddr_t)&port_statep->sp_p_wwn,
4292 	    (caddr_t)&buf->port_wwn, sizeof (buf->port_wwn));
4293 	bcopy((caddr_t)&socalp->socal_n_wwn,
4294 	    (caddr_t)&buf->node_wwn, sizeof (buf->node_wwn));
4295 	buf->nport_id = fcalpkt->fcal_socal_request.sr_fc_frame_hdr.s_id;
4296 	(void) ddi_dma_sync(privp->cmd_handle, 0, 0, DDI_DMA_SYNC_FORDEV);
4297 
4298 	retval = socal_doit(fcalpkt, port_statep, 0, socal_adisc_done,
4299 	    SOCAL_ADISC_TIMEOUT, PORT_ADISC_PENDING, NULL);
4300 	if (retval == FCAL_SUCCESS) {
4301 		(void) ddi_dma_sync(privp->rsp_handle, 0, 0,
4302 		    DDI_DMA_SYNC_FORKERNEL);
4303 		bcopy(privp->rsp, (caddr_t)payload, sizeof (la_els_adisc_t));
4304 	}
4305 	privp->fapktp = NULL;
4306 	socal_els_free(privp);
4307 	return (retval);
4308 }
4309 
4310 static int
4311 socal_issue_lbf(socal_state_t *socalp, uint32_t port,
4312     uchar_t *payload, size_t length, uint32_t polled)
4313 {
4314 	int			retval;
4315 	fcal_packet_t		*fcalpkt;
4316 	socal_port_t		*port_statep;
4317 	socal_priv_cmd_t 	*privp;
4318 
4319 	port_statep = &socalp->port_state[port];
4320 
4321 	if ((fcalpkt = socal_lbf_alloc(socalp, port, length, length,
4322 	    (caddr_t *)&privp, polled)) == (fcal_packet_t *)NULL)
4323 		return (FCAL_ALLOC_FAILED);
4324 
4325 	privp = (socal_priv_cmd_t *)fcalpkt->fcal_pkt_private;
4326 	bcopy((caddr_t)payload, privp->cmd, length);
4327 	(void) ddi_dma_sync(privp->cmd_handle, 0, 0, DDI_DMA_SYNC_FORDEV);
4328 
4329 	retval = socal_doit(fcalpkt, port_statep, polled, socal_lbf_done,
4330 	    SOCAL_LBF_TIMEOUT, PORT_LBF_PENDING, NULL);
4331 
4332 	if (retval == FCAL_SUCCESS) {
4333 		(void) ddi_dma_sync(privp->rsp_handle, 0, 0,
4334 		    DDI_DMA_SYNC_FORKERNEL);
4335 		bcopy(privp->rsp, (caddr_t)payload, length);
4336 	}
4337 	privp->fapktp = NULL;
4338 	socal_lbf_free(privp);
4339 	return (retval);
4340 }
4341 
4342 static int
4343 socal_issue_rls(socal_state_t *socalp, uint32_t port, uint32_t dest,
4344 	la_els_rls_reply_t *payload, uint32_t polled)
4345 {
4346 	int	retval;
4347 	la_els_rls_t		*buf;
4348 	fcal_packet_t		*fcalpkt;
4349 	socal_port_t		*port_statep;
4350 	socal_priv_cmd_t 	*privp;
4351 	uint32_t		arg;
4352 
4353 	port_statep = &socalp->port_state[port];
4354 
4355 	if (dest == socal_getmap(socalp, port, NULL, 0, 0)) {
4356 		/* load up the the struct with the local lesb */
4357 		struct la_els_rjt *rsp = (struct la_els_rjt *)payload;
4358 
4359 		rsp->ls_code = LA_ELS_RJT;
4360 		rsp->mbz[0] = 0;
4361 		rsp->mbz[1] = 0;
4362 		rsp->mbz[2] = 0;
4363 		rsp->reason_code = RJT_UNSUPPORTED;
4364 		rsp->reserved = 0;
4365 		rsp->explanation = 0;
4366 		rsp->vendor = 0;
4367 		return (FCAL_SUCCESS);
4368 	}
4369 
4370 	if ((fcalpkt =
4371 	    socal_els_alloc(socalp, port, dest, sizeof (la_els_rls_t),
4372 	    sizeof (la_els_rls_reply_t), (caddr_t *)&privp, polled))
4373 	    == (fcal_packet_t *)NULL)
4374 		return (FCAL_ALLOC_FAILED);
4375 
4376 	privp = (socal_priv_cmd_t *)fcalpkt->fcal_pkt_private;
4377 
4378 	if (payload->link_failure & 0xff000000)
4379 		arg = payload->link_failure;
4380 	else
4381 		arg = dest;
4382 
4383 	buf = (la_els_rls_t *)privp->cmd;
4384 	buf->ls_code = LA_ELS_RLS;
4385 	buf->mbz[0] = 0;
4386 	buf->mbz[1] = 0;
4387 	buf->mbz[2] = 0;
4388 	buf->reserved = 0;
4389 	buf->nport_id[0] = (arg >> 16) & 0xff;
4390 	buf->nport_id[1] = (arg >> 8) & 0xff;
4391 	buf->nport_id[2] = arg & 0xff;
4392 	(void) ddi_dma_sync(privp->cmd_handle, 0, 0, DDI_DMA_SYNC_FORDEV);
4393 
4394 	retval = socal_doit(fcalpkt, port_statep, 0, socal_rls_done,
4395 	    SOCAL_RLS_TIMEOUT, PORT_RLS_PENDING, NULL);
4396 	if (retval == FCAL_SUCCESS) {
4397 		(void) ddi_dma_sync(privp->rsp_handle, 0, 0,
4398 		    DDI_DMA_SYNC_FORKERNEL);
4399 		bcopy(privp->rsp, (caddr_t)payload,
4400 		    sizeof (la_els_rls_reply_t));
4401 	}
4402 	privp->fapktp = NULL;
4403 	socal_els_free(privp);
4404 	return (retval);
4405 }
4406 
4407 fcal_packet_t *
4408 socal_els_alloc(socal_state_t *socalp, uint32_t port, uint32_t dest,
4409 	uint32_t cmd_size, uint32_t rsp_size, caddr_t *rprivp, uint32_t polled)
4410 {
4411 	struct fcal_packet	*fcalpkt;
4412 	ddi_dma_cookie_t	ccookie;
4413 	ddi_dma_cookie_t	rcookie;
4414 	socal_priv_cmd_t	*privp;
4415 	ddi_dma_handle_t	chandle = NULL;
4416 	ddi_dma_handle_t	rhandle = NULL;
4417 	ddi_acc_handle_t	cacchandle;
4418 	ddi_acc_handle_t	racchandle;
4419 	soc_request_t		*srp;
4420 	fc_frame_header_t	*fhp;
4421 	uint_t			ccount, cmd_bound = 0, rsp_bound = 0;
4422 	size_t			real_len;
4423 	caddr_t			cmd;
4424 	caddr_t			rsp;
4425 	uint32_t		ouralpa;
4426 
4427 	if ((fcalpkt =
4428 	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
4429 	    == (fcal_packet_t *)NULL)
4430 		return (NULL);
4431 
4432 	if ((privp =
4433 	    (socal_priv_cmd_t *)kmem_zalloc(sizeof (socal_priv_cmd_t),
4434 	    polled ? KM_NOSLEEP : KM_SLEEP)) == (socal_priv_cmd_t *)NULL) {
4435 		goto fail;
4436 	}
4437 
4438 	rprivp = (caddr_t *)&privp;
4439 
4440 	fcalpkt->fcal_pkt_private = (caddr_t)privp;
4441 	privp->fapktp = (void *)fcalpkt;
4442 
4443 	if ((ouralpa = socal_getmap(socalp, port, NULL, 0, 0)) == -1)
4444 		goto fail;
4445 
4446 	if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
4447 	    DDI_DMA_DONTWAIT, NULL, &chandle) != DDI_SUCCESS)
4448 		goto fail;
4449 	privp->cmd_handle = chandle;
4450 
4451 	if (ddi_dma_mem_alloc(chandle, cmd_size, &socal_acc_attr,
4452 	    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
4453 	    (caddr_t *)&cmd, &real_len, &cacchandle) != DDI_SUCCESS)
4454 		goto fail;
4455 	privp->cmd = cmd;
4456 	privp->cmd_acchandle = cacchandle;
4457 
4458 	if (real_len < cmd_size)
4459 		goto fail;
4460 
4461 	if (ddi_dma_addr_bind_handle(chandle, (struct as *)NULL,
4462 	    (caddr_t)cmd, cmd_size,
4463 	    DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
4464 	    DDI_DMA_DONTWAIT, NULL, &ccookie, &ccount)
4465 	    != DDI_DMA_MAPPED)
4466 		goto fail;
4467 	cmd_bound = 1;
4468 	if (ccount != 1)
4469 		goto fail;
4470 
4471 	if (rsp_size) {
4472 		if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
4473 		    DDI_DMA_DONTWAIT, NULL, &rhandle) != DDI_SUCCESS)
4474 		goto fail;
4475 
4476 		privp->rsp_handle = rhandle;
4477 		if (ddi_dma_mem_alloc(rhandle, rsp_size, &socal_acc_attr,
4478 		    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
4479 		    &rsp, &real_len, &racchandle) != DDI_SUCCESS)
4480 			goto fail;
4481 		privp->rsp = rsp;
4482 		privp->rsp_acchandle = racchandle;
4483 		if (real_len < rsp_size)
4484 		goto fail;
4485 
4486 		if (ddi_dma_addr_bind_handle(rhandle, (struct as *)NULL,
4487 		    rsp, rsp_size,
4488 		    DDI_DMA_READ | DDI_DMA_CONSISTENT,
4489 		    DDI_DMA_DONTWAIT, NULL, &rcookie, &ccount)
4490 		    != DDI_DMA_MAPPED)
4491 		goto fail;
4492 
4493 		rsp_bound = 1;
4494 		if (ccount != 1)
4495 		goto fail;
4496 	}
4497 
4498 	srp = (soc_request_t *)&fcalpkt->fcal_socal_request;
4499 	srp->sr_soc_hdr.sh_flags = SOC_FC_HEADER;
4500 	if (port)
4501 		srp->sr_soc_hdr.sh_flags |= SOC_PORT_B;
4502 	srp->sr_soc_hdr.sh_class = 3;
4503 	srp->sr_soc_hdr.sh_byte_cnt = cmd_size;
4504 	srp->sr_dataseg[0].fc_base = (uint32_t)ccookie.dmac_address;
4505 	srp->sr_dataseg[0].fc_count = cmd_size;
4506 	if (rsp_size == 0) {
4507 		srp->sr_soc_hdr.sh_seg_cnt = 1;
4508 	} else {
4509 		srp->sr_soc_hdr.sh_seg_cnt = 2;
4510 		srp->sr_dataseg[1].fc_base = (uint32_t)rcookie.dmac_address;
4511 		srp->sr_dataseg[1].fc_count = rsp_size;
4512 	}
4513 	srp->sr_cqhdr.cq_hdr_count = 1;
4514 	/* this will potentially be overwritten by the calling function */
4515 	srp->sr_cqhdr.cq_hdr_type = CQ_TYPE_SIMPLE;
4516 
4517 	fcalpkt->fcal_pkt_cookie = (void *)socalp;
4518 
4519 	/* Fill in the Fabric Channel Header */
4520 	fhp = &srp->sr_fc_frame_hdr;
4521 	fhp->r_ctl = R_CTL_ELS_REQ;
4522 	fhp->d_id = dest;
4523 	fhp->s_id = ouralpa;
4524 	fhp->type = TYPE_EXTENDED_LS;
4525 	fhp->f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ;
4526 	fhp->seq_id = 0;
4527 	fhp->df_ctl  = 0;
4528 	fhp->seq_cnt = 0;
4529 	fhp->ox_id = 0xffff;
4530 	fhp->rx_id = 0xffff;
4531 	fhp->ro = 0;
4532 	return (fcalpkt);
4533 fail:
4534 	socal_packet_free(fcalpkt);
4535 	if (privp) {
4536 		if (privp->cmd_handle) {
4537 			if (cmd_bound)
4538 				(void) ddi_dma_unbind_handle(privp->cmd_handle);
4539 			ddi_dma_free_handle(&privp->cmd_handle);
4540 		}
4541 		if (privp->cmd)
4542 			ddi_dma_mem_free(&privp->cmd_acchandle);
4543 		if (privp->rsp_handle) {
4544 			if (rsp_bound)
4545 				(void) ddi_dma_unbind_handle(privp->rsp_handle);
4546 			ddi_dma_free_handle(&privp->rsp_handle);
4547 		}
4548 		if (privp->rsp)
4549 			ddi_dma_mem_free(&privp->rsp_acchandle);
4550 
4551 		kmem_free(privp, sizeof (*privp));
4552 	}
4553 	return (NULL);
4554 }
4555 
4556 fcal_packet_t *
4557 socal_lbf_alloc(socal_state_t *socalp, uint32_t port,
4558 	uint32_t cmd_size, uint32_t rsp_size, caddr_t *rprivp,
4559 	uint32_t polled)
4560 {
4561 	struct fcal_packet	*fcalpkt;
4562 	ddi_dma_cookie_t	ccookie;
4563 	ddi_dma_cookie_t	rcookie;
4564 	socal_priv_cmd_t	*privp;
4565 	ddi_dma_handle_t	chandle = NULL;
4566 	ddi_dma_handle_t	rhandle = NULL;
4567 	ddi_acc_handle_t	cacchandle;
4568 	ddi_acc_handle_t	racchandle;
4569 	soc_request_t		*srp;
4570 	fc_frame_header_t	*fhp;
4571 	uint_t			ccount, cmd_bound = 0, rsp_bound = 0;
4572 	size_t			real_len;
4573 	caddr_t			cmd;
4574 	caddr_t			rsp;
4575 
4576 	if ((fcalpkt =
4577 	    socal_packet_alloc(socalp, polled ? FCAL_NOSLEEP : FCAL_SLEEP))
4578 	    == (fcal_packet_t *)NULL)
4579 		return (NULL);
4580 
4581 	if ((privp =
4582 	    (socal_priv_cmd_t *)kmem_zalloc(sizeof (socal_priv_cmd_t),
4583 	    polled ? KM_NOSLEEP : KM_SLEEP)) == (socal_priv_cmd_t *)NULL) {
4584 		goto fail;
4585 	}
4586 
4587 	rprivp = (caddr_t *)&privp;
4588 
4589 	fcalpkt->fcal_pkt_private = (caddr_t)privp;
4590 	privp->fapktp = (void *)fcalpkt;
4591 
4592 	if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
4593 	    DDI_DMA_DONTWAIT, NULL, &chandle) != DDI_SUCCESS)
4594 		goto fail;
4595 	privp->cmd_handle = chandle;
4596 
4597 	if (ddi_dma_mem_alloc(chandle, cmd_size, &socal_acc_attr,
4598 	    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
4599 	    (caddr_t *)&cmd, &real_len, &cacchandle) != DDI_SUCCESS)
4600 		goto fail;
4601 	privp->cmd = cmd;
4602 	privp->cmd_acchandle = cacchandle;
4603 
4604 	if (real_len < cmd_size)
4605 		goto fail;
4606 
4607 	if (ddi_dma_addr_bind_handle(chandle, (struct as *)NULL,
4608 	    (caddr_t)cmd, cmd_size,
4609 	    DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
4610 	    DDI_DMA_DONTWAIT, NULL, &ccookie, &ccount)
4611 	    != DDI_DMA_MAPPED)
4612 		goto fail;
4613 	cmd_bound = 1;
4614 	if (ccount != 1)
4615 		goto fail;
4616 
4617 	if (rsp_size) {
4618 		if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
4619 		    DDI_DMA_DONTWAIT, NULL, &rhandle) != DDI_SUCCESS)
4620 		goto fail;
4621 
4622 		privp->rsp_handle = rhandle;
4623 		if (ddi_dma_mem_alloc(rhandle, rsp_size, &socal_acc_attr,
4624 		    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
4625 		    &rsp, &real_len, &racchandle) != DDI_SUCCESS)
4626 			goto fail;
4627 
4628 		privp->rsp = rsp;
4629 		privp->rsp_acchandle = racchandle;
4630 		if (real_len < rsp_size)
4631 		goto fail;
4632 
4633 		if (ddi_dma_addr_bind_handle(rhandle, (struct as *)NULL,
4634 		    rsp, rsp_size,
4635 		    DDI_DMA_READ | DDI_DMA_CONSISTENT,
4636 		    DDI_DMA_DONTWAIT, NULL, &rcookie, &ccount)
4637 		    != DDI_DMA_MAPPED)
4638 			goto fail;
4639 
4640 		rsp_bound = 1;
4641 		if (ccount != 1)
4642 		goto fail;
4643 	}
4644 
4645 	srp = (soc_request_t *)&fcalpkt->fcal_socal_request;
4646 	srp->sr_soc_hdr.sh_flags = SOC_FC_HEADER;
4647 	if (port)
4648 		srp->sr_soc_hdr.sh_flags |= SOC_PORT_B;
4649 	srp->sr_soc_hdr.sh_class = 3;
4650 	srp->sr_soc_hdr.sh_byte_cnt = cmd_size;
4651 	srp->sr_dataseg[0].fc_base = (uint32_t)ccookie.dmac_address;
4652 	srp->sr_dataseg[0].fc_count = cmd_size;
4653 	if (rsp_size == 0) {
4654 		srp->sr_soc_hdr.sh_seg_cnt = 1;
4655 	} else {
4656 		srp->sr_soc_hdr.sh_seg_cnt = 2;
4657 		srp->sr_dataseg[1].fc_base = (uint32_t)rcookie.dmac_address;
4658 		srp->sr_dataseg[1].fc_count = rsp_size;
4659 	}
4660 	srp->sr_cqhdr.cq_hdr_count = 1;
4661 	/* this will potentially be overwritten by the calling function */
4662 	srp->sr_cqhdr.cq_hdr_type = CQ_TYPE_SIMPLE;
4663 
4664 	fcalpkt->fcal_pkt_cookie = (void *)socalp;
4665 
4666 	/* Fill in the Fabric Channel Header */
4667 	fhp = &srp->sr_fc_frame_hdr;
4668 	fhp->r_ctl = R_CTL_SOLICITED_DATA;
4669 	fhp->d_id = socalp->port_state[port].sp_src_id;
4670 	fhp->s_id = socalp->port_state[port].sp_src_id;
4671 	fhp->type = TYPE_SCSI_FCP;
4672 	fhp->f_ctl = F_CTL_SEQ_INITIATIVE | F_CTL_FIRST_SEQ | F_CTL_LAST_SEQ;
4673 	fhp->seq_id = 0;
4674 	fhp->df_ctl  = 0;
4675 	fhp->seq_cnt = 0;
4676 	fhp->ox_id = 0xffff;
4677 	fhp->rx_id = 0xffff;
4678 	fhp->ro = 0;
4679 	return (fcalpkt);
4680 fail:
4681 	socal_packet_free(fcalpkt);
4682 	if (privp) {
4683 		if (privp->cmd_handle) {
4684 			if (cmd_bound)
4685 				(void) ddi_dma_unbind_handle(privp->cmd_handle);
4686 			ddi_dma_free_handle(&privp->cmd_handle);
4687 		}
4688 		if (privp->cmd)
4689 			ddi_dma_mem_free(&privp->cmd_acchandle);
4690 		if (privp->rsp_handle) {
4691 			if (rsp_bound)
4692 				(void) ddi_dma_unbind_handle(privp->rsp_handle);
4693 			ddi_dma_free_handle(&privp->rsp_handle);
4694 		}
4695 		if (privp->rsp)
4696 			ddi_dma_mem_free(&privp->rsp_acchandle);
4697 
4698 		kmem_free(privp, sizeof (*privp));
4699 	}
4700 	return (NULL);
4701 }
4702 
4703 void
4704 socal_els_free(socal_priv_cmd_t *privp)
4705 {
4706 	fcal_packet_t		*fcalpkt;
4707 
4708 	if (privp)
4709 		fcalpkt = (fcal_packet_t *)privp->fapktp;
4710 	else
4711 		return;
4712 
4713 	(void) ddi_dma_unbind_handle(privp->cmd_handle);
4714 	ddi_dma_free_handle(&privp->cmd_handle);
4715 	ddi_dma_mem_free(&privp->cmd_acchandle);
4716 
4717 	if (privp->rsp_handle) {
4718 		(void) ddi_dma_unbind_handle(privp->rsp_handle);
4719 		ddi_dma_free_handle(&privp->rsp_handle);
4720 	}
4721 	if (privp->rsp)
4722 		ddi_dma_mem_free(&privp->rsp_acchandle);
4723 
4724 	kmem_free(privp, sizeof (*privp));
4725 	if (fcalpkt != NULL)
4726 		socal_packet_free(fcalpkt);
4727 }
4728 
4729 void
4730 socal_lbf_free(socal_priv_cmd_t *privp)
4731 {
4732 	fcal_packet_t		*fcalpkt;
4733 
4734 	if (privp)
4735 		fcalpkt = (fcal_packet_t *)privp->fapktp;
4736 	else
4737 		return;
4738 
4739 	(void) ddi_dma_unbind_handle(privp->cmd_handle);
4740 	ddi_dma_free_handle(&privp->cmd_handle);
4741 	ddi_dma_mem_free(&privp->cmd_acchandle);
4742 
4743 	if (privp->rsp_handle) {
4744 		(void) ddi_dma_unbind_handle(privp->rsp_handle);
4745 		ddi_dma_free_handle(&privp->rsp_handle);
4746 	}
4747 
4748 	if (privp->rsp)
4749 		ddi_dma_mem_free(&privp->rsp_acchandle);
4750 
4751 	kmem_free(privp, sizeof (*privp));
4752 	if (fcalpkt != NULL)
4753 		socal_packet_free(fcalpkt);
4754 }
4755 
4756 static int
4757 socal_getmap(socal_state_t *socalp, uint32_t port, caddr_t arg,
4758 	uint32_t polled, int flags)
4759 {
4760 	ddi_dma_cookie_t	dcookie;
4761 	ddi_dma_handle_t	dhandle = NULL;
4762 	ddi_acc_handle_t	acchandle;
4763 	size_t			real_len, i;
4764 	uint_t			ccount;
4765 	fcal_lilp_map_t		*buf = NULL;
4766 	int			retval, bound = 0;
4767 	socal_port_t		*port_statep;
4768 
4769 	port_statep = &socalp->port_state[port];
4770 
4771 	if (port_statep->sp_lilpmap_valid) {
4772 
4773 		buf = &port_statep->sp_lilpmap; /* give from cache */
4774 
4775 		if (arg) {
4776 		if (ddi_copyout(buf, (caddr_t)arg,
4777 		    sizeof (struct lilpmap), flags) == -1)
4778 			return (-1);
4779 		}
4780 
4781 		return (buf->lilp_myalpa);
4782 	}
4783 
4784 	if (ddi_dma_alloc_handle(socalp->dip, &socal_dma_attr,
4785 	    DDI_DMA_DONTWAIT, NULL, &dhandle) != DDI_SUCCESS)
4786 		goto getmap_fail;
4787 
4788 	i = sizeof (struct fcal_lilp_map);
4789 
4790 	if (ddi_dma_mem_alloc(dhandle, i, &socal_acc_attr,
4791 	    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
4792 	    (caddr_t *)&buf, &real_len, &acchandle) != DDI_SUCCESS)
4793 		goto getmap_fail;
4794 
4795 	if (real_len < i)
4796 		goto getmap_fail;
4797 
4798 	if (ddi_dma_addr_bind_handle(dhandle, (struct as *)NULL,
4799 	    (caddr_t)buf, i, DDI_DMA_READ | DDI_DMA_CONSISTENT,
4800 	    DDI_DMA_DONTWAIT, NULL, &dcookie, &ccount) != DDI_DMA_MAPPED)
4801 		goto getmap_fail;
4802 
4803 	bound = 1;
4804 	if (ccount != 1)
4805 		goto getmap_fail;
4806 
4807 	retval = socal_lilp_map((void *)socalp, port,
4808 	    (uint32_t)dcookie.dmac_address, polled);
4809 
4810 	(void) ddi_dma_sync(dhandle, 0, 0, DDI_DMA_SYNC_FORKERNEL);
4811 
4812 	if (retval == FCAL_SUCCESS) {
4813 		bcopy(buf, &port_statep->sp_lilpmap, sizeof (fcal_lilp_map_t));
4814 
4815 		mutex_enter(&port_statep->sp_mtx);
4816 		port_statep->sp_src_id = buf->lilp_myalpa;
4817 		port_statep->sp_lilpmap_valid = 1; /* cached */
4818 		mutex_exit(&port_statep->sp_mtx);
4819 
4820 		if (arg) {
4821 		if (ddi_copyout(buf, (caddr_t)arg,
4822 		    sizeof (struct lilpmap), flags) == -1)
4823 			goto getmap_fail;
4824 		}
4825 
4826 		retval = buf->lilp_myalpa;
4827 	}
4828 	else
4829 		retval = -1;
4830 
4831 	(void) ddi_dma_unbind_handle(dhandle);
4832 	ddi_dma_mem_free(&acchandle);
4833 	ddi_dma_free_handle(&dhandle);
4834 	return (retval);
4835 
4836 getmap_fail:
4837 	if (dhandle) {
4838 		if (bound)
4839 			(void) ddi_dma_unbind_handle(dhandle);
4840 		ddi_dma_free_handle(&dhandle);
4841 	}
4842 	if (buf)
4843 		ddi_dma_mem_free(&acchandle);
4844 	return (-1);
4845 }
4846 
4847 static	void
4848 socal_wcopy(uint_t *h_src, uint_t *h_dest, int len)
4849 {
4850 	int	i;
4851 	for (i = 0; i < len/4; i++) {
4852 		*h_dest++ = *h_src++;
4853 	}
4854 }
4855 
4856 static void
4857 socal_flush_overflowq(socal_state_t *socalp, int port, int q_no)
4858 {
4859 	socal_kcq_t	*kcq;
4860 	fcal_packet_t	*fpkt1, *fpkt2, *head = NULL, *tmp;
4861 
4862 	kcq = &socalp->request[q_no];
4863 	mutex_enter(&kcq->skc_mtx);
4864 	fpkt2 = kcq->skc_overflowh;
4865 	fpkt1 = NULL;
4866 	while (fpkt2 != NULL) {
4867 		if ((((soc_request_t *)&fpkt2->fcal_socal_request)
4868 		    ->sr_soc_hdr.sh_flags & SOC_PORT_B) == port) {
4869 			if (fpkt1 == NULL)
4870 				kcq->skc_overflowh = fpkt2->fcal_pkt_next;
4871 			else {
4872 				fpkt1->fcal_pkt_next = fpkt2->fcal_pkt_next;
4873 				if (kcq->skc_overflowt == fpkt2)
4874 					kcq->skc_overflowt = fpkt1;
4875 			}
4876 			tmp = fpkt2->fcal_pkt_next;
4877 			fpkt2->fcal_pkt_next = head;
4878 			head = fpkt2;
4879 			fpkt2 = tmp;
4880 			SOCAL_ID_FREE(head->fcal_socal_request.
4881 			    sr_soc_hdr.sh_request_token);
4882 		} else {
4883 			fpkt1 = fpkt2;
4884 			fpkt2 = fpkt2->fcal_pkt_next;
4885 		}
4886 	}
4887 	mutex_exit(&kcq->skc_mtx);
4888 	fpkt2 = head;
4889 	while (fpkt2 != NULL) {
4890 		fpkt2->fcal_pkt_status = FCAL_STATUS_ERR_OFFLINE;
4891 		fpkt2->fcal_cmd_state |= FCAL_CMD_COMPLETE;
4892 		fpkt2->fcal_pkt_flags |= FCFLAG_COMPLETE;
4893 		tmp = fpkt2->fcal_pkt_next;
4894 		if (fpkt2->fcal_pkt_comp != NULL)
4895 			(*fpkt2->fcal_pkt_comp)(fpkt2);
4896 		fpkt2 = tmp;
4897 	}
4898 }
4899 
4900 static void
4901 socal_deferred_intr(void *arg)
4902 {
4903 	socal_kcq_t	*kcq = (socal_kcq_t *)arg;
4904 	socal_state_t	*socalp = kcq->skc_socalp;
4905 
4906 	ASSERT((socalp != NULL));
4907 
4908 	mutex_enter(&kcq->skc_mtx);
4909 
4910 	if ((kcq->skc_out != kcq->skc_saved_out) ||
4911 	    (kcq->skc_seqno != kcq->skc_saved_seqno)) {
4912 		kcq->deferred_intr_timeoutid = 0;
4913 		mutex_exit(&kcq->skc_mtx);
4914 		return;
4915 	}
4916 
4917 	if (socalp->socal_on_intr) {
4918 		mutex_exit(&kcq->skc_mtx);
4919 		kcq->deferred_intr_timeoutid = timeout(socal_deferred_intr,
4920 		    (caddr_t)kcq, drv_usectohz(10000));
4921 		return;
4922 	}
4923 
4924 	kcq->deferred_intr_timeoutid = 0;
4925 	mutex_exit(&kcq->skc_mtx);
4926 	socal_intr_solicited(socalp, 0);
4927 }
4928 
4929 static void
4930 socal_take_core(void *arg)
4931 {
4932 	socal_state_t	*socalp = (socal_state_t *)arg;
4933 	int i, instance;
4934 
4935 	socal_disable(socalp);
4936 	for (i = 0; i < SOCAL_N_CQS; i++) {
4937 		mutex_enter(&socalp->request[i].skc_mtx);
4938 		mutex_enter(&socalp->response[i].skc_mtx);
4939 	}
4940 	for (i = 0; i < 4; i++) {
4941 		socalp->socal_rp->socal_cr.w &=
4942 		    ~SOCAL_CR_EXTERNAL_RAM_BANK_MASK;
4943 		socalp->socal_rp->socal_cr.w |= i<<24;
4944 		(void) bcopy((caddr_t)socalp->socal_xrp,
4945 		    (caddr_t)&socal_xrambuf[i*0x10000], 0x10000);
4946 	}
4947 	for (i = 3; i >= 0; i--) {
4948 		mutex_exit(&socalp->request[i].skc_mtx);
4949 		mutex_exit(&socalp->response[i].skc_mtx);
4950 	}
4951 	instance = ddi_get_instance(socalp->dip);
4952 	cmn_err(CE_PANIC,
4953 	    "socal take core (socal instance %d)", instance);
4954 }
4955 
4956 /*
4957  * Preset AL_PA in hardware, if is told.
4958  */
4959 static void
4960 socal_fix_harda(socal_state_t *socalp, int port)
4961 {
4962 	socal_port_t	*portp = &socalp->port_state[port];
4963 	uint_t		*xrp = (uint_t *)socalp->socal_xrp;
4964 	uint_t		accum, harda;
4965 
4966 	harda = portp->sp_hard_alpa;
4967 	accum = xrp[SOCAL_XRAM_PORTA_HRDA/4];
4968 	if (port == 0) {
4969 		accum &= 0x00FFFFFF;
4970 		accum |= ((harda & 0xFF) << 24);
4971 	} else {
4972 		accum &= 0xFF00FFFF;
4973 		accum |= ((harda & 0xFF) << 16);
4974 	}
4975 	xrp[SOCAL_XRAM_PORTA_HRDA/4] = accum;
4976 }
4977 
4978 /*
4979  * Target-Mode attach function
4980  */
4981 fcal_transport_t *
4982 socal_sftm_attach(dev_t dev, int loop_id)
4983 {
4984 	int 		instance = getminor(dev) / 2;
4985 	int		port = getminor(dev) % 2;
4986 	int		hard_alpa;
4987 	char		*name;
4988 	socal_state_t	*socalp;
4989 
4990 	/*
4991 	 * If the device is not a "socal" device, return
4992 	 */
4993 	if ((name = ddi_major_to_name(getmajor(dev))) == NULL ||
4994 	    strcmp(name, "socal") != 0)
4995 		return (NULL);
4996 
4997 	/*
4998 	 * If no soft state structure, return
4999 	 */
5000 	socalp = ddi_get_soft_state(socal_soft_state_p, instance);
5001 	if (socalp == NULL)
5002 		return (NULL);
5003 
5004 	/*
5005 	 * If the port is already attached, return
5006 	 */
5007 	if (socalp->port_state[port].sp_status & PORT_CHILD_INIT)
5008 		return (NULL);
5009 
5010 	if (loop_id < 0 || loop_id > 126)
5011 		return (NULL);
5012 
5013 	/* if this instance is detaching, don't attach */
5014 	mutex_enter(&socalp->board_mtx);
5015 	mutex_enter(&socalp->port_state[port].sp_mtx);
5016 	if (socalp->socal_busy < 0) {
5017 		mutex_exit(&socalp->port_state[port].sp_mtx);
5018 		mutex_exit(&socalp->board_mtx);
5019 		return (NULL);
5020 	}
5021 	socalp->socal_busy++;
5022 	socalp->port_state[port].sp_status |= PORT_CHILD_INIT;
5023 	mutex_exit(&socalp->port_state[port].sp_mtx);
5024 	mutex_exit(&socalp->board_mtx);
5025 
5026 	/*
5027 	 * Since we keep the Hard Loop-id in two config files, warn the
5028 	 * user if they don't match.
5029 	 */
5030 	hard_alpa = socal_switch_to_alpa[loop_id];
5031 	if (hard_alpa != socalp->port_state[port].sp_hard_alpa) {
5032 		socalp->port_state[port].sp_hard_alpa = hard_alpa;
5033 		cmn_err(CE_WARN, "socal%d: Hard Loop-id mismatch - "
5034 		    "using Loop-id %d",
5035 		    instance, loop_id);
5036 	}
5037 
5038 	return (socalp->port_state[port].sp_transport);
5039 }
5040 
5041 
5042 /*
5043  * Target-Mode detach function
5044  */
5045 int
5046 socal_sftm_detach(socal_state_t *socalp, int port)
5047 {
5048 	mutex_enter(&socalp->board_mtx);
5049 	socalp->socal_busy--;
5050 	socalp->port_state[port].sp_status &= ~PORT_CHILD_INIT;
5051 	mutex_exit(&socalp->board_mtx);
5052 
5053 	return (0);
5054 }
5055