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