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