xref: /illumos-gate/usr/src/uts/common/io/cpqary3/cpqary3.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
14  */
15 
16 #include "cpqary3.h"
17 
18 /*
19  * Local Autoconfiguration Function Prototype Declations
20  */
21 
22 int cpqary3_attach(dev_info_t *, ddi_attach_cmd_t);
23 int cpqary3_detach(dev_info_t *, ddi_detach_cmd_t);
24 int cpqary3_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
25 
26 /*
27  * Local Functions Definitions
28  */
29 
30 static void cpqary3_cleanup(cpqary3_t *, uint32_t);
31 static uint8_t cpqary3_update_ctlrdetails(cpqary3_t *, uint32_t *);
32 int8_t cpqary3_detect_target_geometry(cpqary3_t *);
33 
34 /*
35  * External Variable Definitions
36  */
37 
38 extern cpqary3_driver_info_t gdriver_info;
39 
40 /*
41  * Global Variables Definitions
42  */
43 
44 static char cpqary3_brief[]    =	"HP Smart Array Driver";
45 void *cpqary3_state;
46 
47 /* HPQaculi Changes */
48 
49 /*
50  * HBA minor number schema
51  *
52  * The minor numbers for any minor device nodes that we create are
53  * governed by the SCSA framework.  We use the macros below to
54  * fabricate minor numbers for nodes that we own.
55  *
56  * See sys/impl/transport.h for more info.
57  */
58 
59 /* Macro to extract interface from minor number */
60 #define	CPQARY3_MINOR2INTERFACE(_x)  ((_x) & (TRAN_MINOR_MASK))
61 
62 /* Base of range assigned to HBAs: */
63 #define	SCSA_MINOR_HBABASE  (32)
64 
65 /* Our minor nodes: */
66 #define	CPQARY3_MINOR  (0 + SCSA_MINOR_HBABASE)
67 
68 /* Convenience macros to convert device instances to minor numbers */
69 #define	CPQARY3_INST2x(_i, _x)    (((_i) << INST_MINOR_SHIFT) | (_x))
70 #define	CPQARY3_INST2CPQARY3(_i)  CPQARY3_INST2x(_i, CPQARY3_MINOR)
71 
72 /* HPQacucli Changes */
73 
74 /*
75  * The Driver DMA Limit structure.
76  * Data used for SMART Integrated Array Controller shall be used.
77  */
78 
79 ddi_dma_attr_t cpqary3_dma_attr = {
80 	DMA_ATTR_V0,		/* ddi_dma_attr version */
81 	0,			/* Low Address */
82 	0xFFFFFFFFFFFFFFFF,	/* High Address */
83 	0x00FFFFFF,		/* Max DMA Counter register */
84 	0x20,			/* Byte Alignment */
85 	0x20,			/* Burst Sizes : 32 Byte */
86 	DMA_UNIT_8,		/* Minimum DMA xfer Size */
87 	0xFFFFFFFF,		/* Maximum DMA xfer Size */
88 	/*
89 	 * Segment boundary restrictions
90 	 * The addr should not cross 4GB boundry.
91 	 * This is required to address an issue
92 	 * in the Surge ASIC, with earlier FW versions.
93 	 */
94 	0xFFFFFFFF,
95 	CPQARY3_SG_CNT,		/* Scatter/Gather List Length */
96 	512,			/* Device Granularity */
97 	0			/* DMA flags */
98 };
99 
100 /*
101  * The Device Access Attribute Structure.
102  */
103 
104 ddi_device_acc_attr_t cpqary3_dev_attributes = {
105 	DDI_DEVICE_ATTR_V0,
106 	DDI_STRUCTURE_LE_ACC,
107 	DDI_STRICTORDER_ACC
108 };
109 
110 /*
111  * Character-Block Operations Structure
112  */
113 
114 static struct cb_ops cpqary3_cb_ops = {
115 	/* HPQacucli Changes */
116 	scsi_hba_open,
117 	scsi_hba_close,
118 	/* HPQacucli Changes */
119 	nodev,			/* cb_strategy */
120 	nodev,			/* cb_print */
121 	nodev,			/* cb_dump */
122 	nodev,			/* cb_read */
123 	nodev,			/* cb_write */
124 	cpqary3_ioctl,		/* cb_ioctl */
125 	nodev,			/* cb_devmap */
126 	nodev,			/* cb_mmap */
127 	nodev,			/* cb_segmap */
128 	nochpoll,		/* cb_chpoll */
129 	ddi_prop_op,		/* cb_prop_op */
130 	NULL,			/* cb_stream */
131 	(int)(D_NEW|D_MP),	/* cb_flag */
132 	CB_REV,
133 	nodev,
134 	nodev
135 };
136 
137 /*
138  * Device Operations Structure
139  */
140 
141 static struct dev_ops cpqary3_dev_ops = {
142 	DEVO_REV,		/* Driver Build Version */
143 	0,			/* Driver reference count */
144 	nodev,			/* Get Info */
145 	nulldev,		/* Identify not required */
146 	nulldev,		/* Probe, obselete for s2.6 and up */
147 	cpqary3_attach,		/* Attach routine */
148 	cpqary3_detach,		/* Detach routine */
149 	nodev,			/* Reset */
150 	&cpqary3_cb_ops,	/* Entry Points for C&B drivers */
151 	NULL,			/* Bus ops */
152 	nodev			/* cpqary3_power */
153 };
154 
155 /*
156  * Linkage structures
157  */
158 
159 static struct modldrv cpqary3_modldrv = {
160 	&mod_driverops,		/* Module Type - driver */
161 	cpqary3_brief,		/* Driver Desc */
162 	&cpqary3_dev_ops	/* Driver Ops */
163 };
164 
165 static struct modlinkage cpqary3_modlinkage = {
166 	MODREV_1,		/* Loadable module rev. no. */
167 	&cpqary3_modldrv, 	/* Loadable module */
168 	NULL 			/* end */
169 };
170 
171 
172 /*
173  * Function	:	_init
174  * Description	:	This routine allocates soft state resources for the
175  *			driver, registers the HBA with the system and
176  *			adds the driver(loadable module).
177  * Called By	:	Kernel
178  * Parameters	:	None
179  * Return Values:	0 / Non-Zero
180  *			[as returned by the mod_install OS function]
181  */
182 int
183 _init()
184 {
185 	int  retvalue;
186 
187 	/*
188 	 * Allocate Soft State Resources; if failure, return.
189 	 */
190 	retvalue = ddi_soft_state_init(&cpqary3_state,
191 	    sizeof (cpqary3_t), MAX_CTLRS);
192 	VERIFY(retvalue == 0);
193 
194 	/*
195 	 * Initialise the HBA Interface.
196 	 */
197 	if (!(retvalue = scsi_hba_init(&cpqary3_modlinkage))) {
198 		/* Load the driver */
199 		if ((retvalue = mod_install(&cpqary3_modlinkage))) {
200 			/*
201 			 * Failed to load the driver, undo HBA interface
202 			 * and soft state allocation.
203 			 */
204 			scsi_hba_fini(&cpqary3_modlinkage);
205 			ddi_soft_state_fini(&cpqary3_state);
206 		}
207 	} else {
208 		/*
209 		 * Failed to register HBA interface, undo all soft state
210 		 * allocation
211 		 */
212 		ddi_soft_state_fini(&cpqary3_state);
213 	}
214 
215 	return (retvalue);
216 }
217 
218 /*
219  * Function	: 	_fini
220  * Description	: 	This routine removes the loadable module, cancels the
221  *			HBA registration and deallocates soft state resources.
222  * Called By	: 	Kernel
223  * Parameters	: 	None
224  * Return Values: 	0 - Success / Non-Zero - Failure
225  *			[as returned by the mod_remove(OS provided) function]
226  */
227 int
228 _fini()
229 {
230 	int  retvalue;
231 
232 	/* Unload the Driver(loadable module) */
233 
234 	if ((retvalue = mod_remove(&cpqary3_modlinkage)) == 0) {
235 
236 		/* Cancel the registeration for the HBA Interface */
237 		scsi_hba_fini(&cpqary3_modlinkage);
238 
239 		/* dealloacte soft state resources of the driver */
240 		ddi_soft_state_fini(&cpqary3_state);
241 	}
242 
243 	return (retvalue);
244 }
245 
246 /*
247  * Function	: 	_info
248  * Description	: 	This routine returns information about the driver.
249  * Called By	: 	Kernel
250  * Parameters	: 	None
251  * Return Values: 	0 / Non-Zero
252  *			[as returned by mod_info(OS provided) function]
253  */
254 int
255 _info(struct modinfo *modinfop)
256 {
257 	/*
258 	 * Get the module information.
259 	 */
260 	return (mod_info(&cpqary3_modlinkage, modinfop));
261 }
262 
263 
264 /*
265  * Function	: 	cpqary3_attach
266  * Description	: 	This routine initializes the driver specific soft state
267  *			structure, initializes the HBA, interrupt handlers,
268  *			memory pool, timeout handler, various mutex, creates the
269  *			minor node.
270  * Called By	: 	kernel
271  * Parameters	: 	dip, command for attach
272  * Return Values: 	DDI_SUCCESS / DDI_FAILURE
273  *			[Success on overall initialization & configuration
274  *			being successful. Failure if any of the initialization
275  *			or any driver-specific mandatory configuration fails]
276  */
277 int
278 cpqary3_attach(dev_info_t *dip, ddi_attach_cmd_t attach_cmd)
279 {
280 	int8_t		minor_node_name[14];
281 	uint32_t	instance;
282 	uint32_t	retvalue;
283 	uint32_t	cleanstatus = 0;
284 	cpqary3_t	*cpqary3p;		/* per-controller */
285 	ddi_dma_attr_t	tmp_dma_attr;
286 
287 	/* Return Failure, If the Command is other than - DDI_ATTACH. */
288 
289 	if (attach_cmd != DDI_ATTACH)
290 		return (DDI_FAILURE);
291 
292 	/* Get the Instance of the Device */
293 
294 	instance = ddi_get_instance(dip);
295 
296 	/* Allocate the per-device-instance soft state structure */
297 
298 	retvalue = ddi_soft_state_zalloc(cpqary3_state, instance);
299 	VERIFY(retvalue == 0);
300 
301 	cleanstatus |= CPQARY3_SOFTSTATE_ALLOC_DONE;
302 
303 	/* Per Controller Pointer */
304 	cpqary3p = ddi_get_soft_state(cpqary3_state, instance);
305 	if (!cpqary3p) {
306 		cmn_err(CE_WARN, "CPQary3: Soft State Retrieval Failed");
307 		cpqary3_cleanup(cpqary3p, cleanstatus);
308 		return (DDI_FAILURE);
309 	}
310 
311 	/* Maintain a record in per-ctlr structure */
312 	cpqary3p->dip = dip;
313 	cpqary3p->instance = instance;
314 
315 	/* Get the User Configuration information from Driver's conf File */
316 	cpqary3_read_conf_file(dip, cpqary3p);
317 
318 	/* Get and Map the HW Configuration */
319 	retvalue = cpqary3_update_ctlrdetails(cpqary3p, &cleanstatus);
320 	if (retvalue == CPQARY3_FAILURE) {
321 		cpqary3_cleanup(cpqary3p, cleanstatus);
322 		return (DDI_FAILURE);
323 	}
324 
325 	/* Get the Cookie for hardware Interrupt Handler */
326 	if (ddi_get_iblock_cookie(dip, 0, &cpqary3p->hw_iblock_cookie) !=
327 	    DDI_SUCCESS) {
328 		cpqary3_cleanup(cpqary3p, cleanstatus);
329 		return (DDI_FAILURE);
330 	}
331 
332 	/* Initialize Per Controller Mutex */
333 	mutex_init(&cpqary3p->hw_mutex, NULL, MUTEX_DRIVER,
334 	    (void *)cpqary3p->hw_iblock_cookie);
335 
336 	cleanstatus |= CPQARY3_MUTEX_INIT_DONE;
337 
338 	/* Get the Cookie for Soft(low level) Interrupt Handler */
339 	if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_HIGH,
340 	    &cpqary3p->sw_iblock_cookie) != DDI_SUCCESS) {
341 		cpqary3_cleanup(cpqary3p, cleanstatus);
342 		return (DDI_FAILURE);
343 	}
344 
345 	/* Initialize the s/w Mutex */
346 	mutex_init(&cpqary3p->sw_mutex, NULL, MUTEX_DRIVER,
347 	    (void *)cpqary3p->sw_iblock_cookie);
348 	cleanstatus |= CPQARY3_SW_MUTEX_INIT_DONE;
349 
350 	/* Initialize per Controller private details */
351 	retvalue = cpqary3_init_ctlr_resource(cpqary3p);
352 	if (retvalue != CPQARY3_SUCCESS) {
353 		cpqary3_cleanup(cpqary3p, cleanstatus);
354 		return (DDI_FAILURE);
355 	}
356 	cleanstatus |= CPQARY3_CTLR_CONFIG_DONE;
357 
358 	/*
359 	 * Allocate HBA transport structure
360 	 */
361 	cpqary3p->hba_tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
362 	if (!cpqary3p->hba_tran) {
363 		cpqary3_cleanup(cpqary3p, cleanstatus);
364 		return (DDI_FAILURE);
365 	}
366 	cleanstatus |= CPQARY3_HBA_TRAN_ALLOC_DONE;
367 
368 	/*
369 	 * Set private field for the HBA tran structure.
370 	 * Initialise the HBA tran entry points.
371 	 * Attach the controller to HBA.
372 	 */
373 	cpqary3_init_hbatran(cpqary3p);
374 
375 	/* PERF */
376 	/* SG */
377 	tmp_dma_attr = cpqary3_dma_attr;
378 	tmp_dma_attr.dma_attr_sgllen = cpqary3p->sg_cnt;
379 	/* SG */
380 	/* PERF */
381 	/*
382 	 * Register the DMA attributes and the transport vectors
383 	 * of each instance of the  HBA device.
384 	 */
385 	if (scsi_hba_attach_setup(dip, &tmp_dma_attr, cpqary3p->hba_tran,
386 	    SCSI_HBA_TRAN_CLONE) == DDI_FAILURE) {
387 		cpqary3_cleanup(cpqary3p, cleanstatus);
388 		return (DDI_FAILURE);
389 	}
390 	cleanstatus |= CPQARY3_HBA_TRAN_ATTACH_DONE;
391 
392 	/*
393 	 * Create a minor node for Ioctl interface.
394 	 * The nomenclature used will be "cpqary3" immediately followed by
395 	 * the current driver instance in the system.
396 	 * for e.g.: 	for 0th instance : cpqary3,0
397 	 * 				for 1st instance : cpqary3,1
398 	 */
399 
400 	(void) sprintf(minor_node_name, "cpqary3,%d", instance);
401 
402 	/* HPQacucli Changes */
403 	if (ddi_create_minor_node(dip, minor_node_name, S_IFCHR,
404 	    CPQARY3_INST2CPQARY3(instance), DDI_NT_SCSI_NEXUS, 0) ==
405 	    DDI_SUCCESS) {
406 		/* HPQacucli Changes */
407 		cleanstatus |= CPQARY3_CREATE_MINOR_NODE;
408 	} else {
409 		cmn_err(CE_NOTE, "CPQary3 : Failed to create minor node");
410 		cpqary3_cleanup(cpqary3p, cleanstatus);
411 		return (DDI_FAILURE);
412 	}
413 
414 
415 	/* Register a timeout driver-routine to be called every 2 secs */
416 	cpqary3p->tick_tmout_id = timeout(cpqary3_tick_hdlr,
417 	    (caddr_t)cpqary3p, drv_usectohz(CPQARY3_TICKTMOUT_VALUE));
418 	cleanstatus |= CPQARY3_TICK_TMOUT_REGD;
419 
420 	/* Register Software Interrupt Handler */
421 	if (ddi_add_softintr(dip,  DDI_SOFTINT_HIGH,
422 	    &cpqary3p->cpqary3_softintr_id, &cpqary3p->sw_iblock_cookie, NULL,
423 	    cpqary3_sw_isr, (caddr_t)cpqary3p) != DDI_SUCCESS) {
424 		cpqary3_cleanup(cpqary3p, cleanstatus);
425 		return (DDI_FAILURE);
426 	}
427 	cleanstatus |= CPQARY3_SW_INTR_HDLR_SET;
428 
429 	/* Register Interrupt Handler */
430 	if (ddi_add_intr(dip, 0, &cpqary3p->hw_iblock_cookie, NULL,
431 	    cpqary3_hw_isr, (caddr_t)cpqary3p) != DDI_SUCCESS) {
432 		cpqary3_cleanup(cpqary3p, cleanstatus);
433 		return (DDI_FAILURE);
434 	}
435 	cleanstatus |= CPQARY3_INTR_HDLR_SET;
436 
437 	/* Enable the Controller Interrupt */
438 	cpqary3_intr_onoff(cpqary3p, CPQARY3_INTR_ENABLE);
439 	if (cpqary3p->host_support & 0x4)
440 		cpqary3_lockup_intr_onoff(cpqary3p, CPQARY3_LOCKUP_INTR_ENABLE);
441 
442 	/*
443 	 * We have come with hmaeventd - which logs the storage events on
444 	 * console as well as in IML. So we are commenting the NOE support in
445 	 * the driver
446 	 */
447 
448 	/* NOE */
449 	if (cpqary3p->noe_support == 1) {
450 		/* Enable the Notification on Event in this controller */
451 		if (CPQARY3_SUCCESS ==
452 		    cpqary3_send_NOE_command(cpqary3p,
453 		    NULL, CPQARY3_NOE_INIT)) {
454 			cleanstatus |= CPQARY3_NOE_INIT_DONE;
455 		} else {
456 			cmn_err(CE_CONT, "CPQary3 : Failed to initialize "
457 			    "NOTIFICATION ON EVENT \n");
458 		}
459 	}
460 	/* NOE */
461 
462 	/* Report that an Instance of the Driver is Attached Successfully */
463 	ddi_report_dev(dip);
464 
465 	/*
466 	 * Now update the num_ctlr
467 	 * This is required for the agents
468 	 */
469 
470 	gdriver_info.num_ctlr++;
471 
472 	return (DDI_SUCCESS);
473 
474 }
475 
476 /*
477  * Function	: 	cpqary3_detach
478  * Description	: 	This routine removes the state associated with a
479  * 			given instance of a device node prior to the
480  * 			removal of that instance from the system
481  * Called By	: 	kernel
482  * Parameters	: 	dip, command for detach
483  * Return Values: 	DDI_SUCCESS / DDI_FAILURE
484  *			[failure ONLY if the command sent with this function
485  *			as a paramter is not DETACH]
486  */
487 int
488 cpqary3_detach(dev_info_t *dip, ddi_detach_cmd_t detach_cmd)
489 {
490 	cpqary3_t	*cpqary3p;
491 	scsi_hba_tran_t	*hba_tran;
492 
493 	/* Return failure, If Command is not DDI_DETACH */
494 
495 	if (DDI_DETACH != detach_cmd)
496 		return (DDI_FAILURE);
497 
498 	/*
499 	 *  Get scsi_hba_tran structure.
500 	 *  Get per controller structure.
501 	 */
502 
503 	hba_tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip);
504 	cpqary3p = (cpqary3_t *)hba_tran->tran_hba_private;
505 
506 	/* Flush the cache */
507 
508 	cpqary3_flush_cache(cpqary3p);
509 
510 	/* Undo cpqary3_attach */
511 
512 	cpqary3_cleanup(cpqary3p, CPQARY3_CLEAN_ALL);
513 
514 	return (DDI_SUCCESS);
515 
516 }
517 
518 /*
519  *	Function	: 	cpary3_ioctl
520  *	Description	: 	This routine services ioctl requests.
521  *	Called By	: 	kernel
522  *	Parameters	: 	Too many to list. Please look below !!!
523  *	Return Values:  	0 / EINVAL / EFAULT /
524  *				[0 on normal successful completion of the ioctl
525  *				request]
526  */
527 int
528 cpqary3_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
529     int *retvaluep)
530 {
531 	minor_t		cpqary3_minor_num;
532 	cpqary3_t	*cpqary3p;
533 	int		instance;
534 
535 	/*
536 	 * Get the soft state structure for this instance
537 	 * Return ENODEV if the structure does not exist.
538 	 */
539 
540 	/*
541 	 * minor() call used in cpqary3_ioctl() returns minor number of the
542 	 * device which are in the
543 	 * range 0-255. if the minor number of the device is greater than 255,
544 	 * data will get truncated. so we are now using getminor(),
545 	 * instead of minor()
546 	 */
547 
548 	if (EINVAL == (cpqary3_minor_num = getminor(dev))) {
549 		*retvaluep = ENODEV;
550 		return (*retvaluep);
551 	}
552 
553 	/* HPQacucli Changes */
554 
555 	/* get instance */
556 	instance = MINOR2INST(cpqary3_minor_num);
557 
558 	cpqary3p = (cpqary3_t *)ddi_get_soft_state(cpqary3_state, instance);
559 
560 	/* HPQacucli Changes */
561 
562 	if (!cpqary3p) {
563 		*retvaluep = ENODEV;
564 		return (*retvaluep);
565 	}
566 
567 	/* HPQacucli Changes */
568 
569 	/* check which interface is being requested */
570 	if (CPQARY3_MINOR2INTERFACE(cpqary3_minor_num) != CPQARY3_MINOR) {
571 		/* defer to SCSA */
572 		return (scsi_hba_ioctl(dev, cmd, arg, mode, credp, retvaluep));
573 	}
574 
575 	/* HPQacucli Changes */
576 
577 	switch (cmd) {
578 		case CPQARY3_IOCTL_DRIVER_INFO:
579 			*retvaluep =
580 			    cpqary3_ioctl_driver_info(arg, mode);
581 			break;
582 
583 		case CPQARY3_IOCTL_CTLR_INFO:
584 			*retvaluep =
585 			    cpqary3_ioctl_ctlr_info(arg, cpqary3p, mode);
586 			break;
587 
588 		case CPQARY3_IOCTL_BMIC_PASS:
589 			*retvaluep =
590 			    cpqary3_ioctl_bmic_pass(arg, cpqary3p, mode);
591 			break;
592 
593 		case CPQARY3_IOCTL_SCSI_PASS:
594 			*retvaluep =
595 			    cpqary3_ioctl_scsi_pass(arg, cpqary3p, mode);
596 			break;
597 
598 		default:
599 			*retvaluep = EINVAL;
600 			break;
601 	}
602 		return (*retvaluep);
603 
604 
605 }
606 
607 
608 /*
609  * Function	: 	cqpary3_cleanup
610  * Description	: 	This routine frees all allocated resources.
611  * Called By	: 	kernel
612  * Parameters	: 	per-controller, bit-map(stating what all to clean)
613  * Return Values: 	None
614  */
615 static void
616 cpqary3_cleanup(cpqary3_t *cpqary3p, uint32_t status)
617 {
618 	int8_t		node_name[10];
619 	clock_t		cpqary3_lbolt;
620 	uint32_t	targ;
621 
622 	ASSERT(cpqary3p != NULL);
623 
624 	/*
625 	 * Disable the NOE command
626 	 * Free the Command Memory Pool
627 	 * destroy all conditional variables
628 	 */
629 
630 	/*
631 	 * We have removed NOE functionality from the
632 	 * driver. So commenting the below piece of code
633 	 */
634 
635 	if (status & CPQARY3_NOE_INIT_DONE) {
636 		if (CPQARY3_SUCCESS == cpqary3_disable_NOE_command(cpqary3p)) {
637 			mutex_enter(&cpqary3p->hw_mutex);
638 			cpqary3_lbolt = ddi_get_lbolt();
639 			if (DDI_FAILURE ==
640 			    cv_timedwait_sig(&cpqary3p->cv_noe_wait,
641 			    &cpqary3p->hw_mutex,
642 			    cpqary3_lbolt + drv_usectohz(3000000))) {
643 				cmn_err(CE_NOTE,
644 				    "CPQary3: Resume signal for disable NOE "
645 				    "command not received \n");
646 			}
647 			mutex_exit(&cpqary3p->hw_mutex);
648 		}
649 	}
650 
651 	/*
652 	 * Detach the device
653 	 * Free / Release / Destroy the following entities/resources:
654 	 * transport layer
655 	 * h/w & s/w interrupt handlers
656 	 * all mutex
657 	 * timeout handler
658 	 * target structure
659 	 * minor node
660 	 * soft state
661 	 * any register/memory mapping
662 	 */
663 
664 	if (status & CPQARY3_INTR_HDLR_SET)
665 		ddi_remove_intr(cpqary3p->dip, 0, cpqary3p->hw_iblock_cookie);
666 
667 	if (status & CPQARY3_SW_INTR_HDLR_SET)
668 		ddi_remove_softintr(cpqary3p->cpqary3_softintr_id);
669 
670 	if ((status & CPQARY3_TICK_TMOUT_REGD) && cpqary3p->tick_tmout_id) {
671 		VERIFY(untimeout(cpqary3p->tick_tmout_id) >= 0);
672 		cpqary3p->tick_tmout_id = NULL;
673 	}
674 
675 	if (status & CPQARY3_CREATE_MINOR_NODE) {
676 		(void) sprintf(node_name, "cpqary3%d", cpqary3p->instance);
677 		ddi_remove_minor_node(cpqary3p->dip, node_name);
678 	}
679 
680 	if (status & CPQARY3_HBA_TRAN_ATTACH_DONE)
681 		(void) scsi_hba_detach(cpqary3p->dip);
682 
683 	if (status & CPQARY3_HBA_TRAN_ALLOC_DONE)
684 		scsi_hba_tran_free(cpqary3p->hba_tran);
685 
686 	if (status & CPQARY3_CTLR_CONFIG_DONE) {
687 		mutex_enter(&cpqary3p->hw_mutex);
688 
689 		cv_destroy(&cpqary3p->cv_abort_wait);
690 		cv_destroy(&cpqary3p->cv_flushcache_wait);
691 		cv_destroy(&cpqary3p->cv_noe_wait);
692 		cv_destroy(&cpqary3p->cv_immediate_wait);
693 		cv_destroy(&cpqary3p->cv_ioctl_wait);
694 
695 		for (targ = 0; targ < CPQARY3_MAX_TGT;  targ++) {
696 			if (cpqary3p->cpqary3_tgtp[targ] == NULL)
697 				continue;
698 			MEM_SFREE(cpqary3p->cpqary3_tgtp[targ],
699 			    sizeof (cpqary3_tgt_t));
700 		}
701 
702 		mutex_exit(&cpqary3p->hw_mutex);
703 
704 		cpqary3_memfini(cpqary3p, CPQARY3_MEMLIST_DONE |
705 		    CPQARY3_PHYCTGS_DONE | CPQARY3_CMDMEM_DONE);
706 	}
707 
708 	if (status & CPQARY3_SW_MUTEX_INIT_DONE)
709 		mutex_destroy(&cpqary3p->sw_mutex);
710 
711 	if (status & CPQARY3_MUTEX_INIT_DONE)
712 		mutex_destroy(&cpqary3p->hw_mutex);
713 
714 	/*
715 	 * If this flag is set, free all mapped registers
716 	 */
717 	if (status & CPQARY3_MEM_MAPPED) {
718 		if (cpqary3p->idr_handle)
719 			ddi_regs_map_free(&cpqary3p->idr_handle);
720 		if (cpqary3p->isr_handle)
721 			ddi_regs_map_free(&cpqary3p->isr_handle);
722 		if (cpqary3p->imr_handle)
723 			ddi_regs_map_free(&cpqary3p->imr_handle);
724 		if (cpqary3p->ipq_handle)
725 			ddi_regs_map_free(&cpqary3p->ipq_handle);
726 		if (cpqary3p->opq_handle)
727 			ddi_regs_map_free(&cpqary3p->opq_handle);
728 		if (cpqary3p->ct_handle)
729 			ddi_regs_map_free(&cpqary3p->ct_handle);
730 	}
731 
732 	if (status & CPQARY3_SOFTSTATE_ALLOC_DONE) {
733 		ddi_soft_state_free(cpqary3_state,
734 		    ddi_get_instance(cpqary3p->dip));
735 	}
736 }
737 
738 /*
739  * Function	: 	cpqary3_update_ctlrdetails
740  * Description	: 	Performs Sanity check of the hw, Updates PCI Config
741  *			Information, Verifies the supported board-id and
742  *			Sets up a mapping for the Primary I2O Memory BAR and
743  *			the Primary DRAM 1 BAR to access Host Interface
744  *			registers and the Transport Configuration table.
745  * Called By	: 	cpqary3_attach()
746  * Parameters	: 	per-controller, bitmap (used for cleaning operations)
747  * Return Values: 	SUCCESS / FAILURE
748  *			[Success / failure depending upon the outcome of all
749  *			checks and mapping. If any of them fail, a failure is
750  *			sent back]
751  */
752 static uint8_t
753 cpqary3_update_ctlrdetails(cpqary3_t *cpqary3p, uint32_t *cleanstatus)
754 {
755 	int8_t			retvalue;
756 	uint8_t			mem_bar0_set = 0;
757 	uint8_t			mem_64_bar0_set = 0;
758 	uint8_t			mem_bar1_set = 0;
759 	uint8_t			mem_64_bar1_set = 0;
760 	int32_t			reglen;
761 	uint32_t		*regp;
762 	uint32_t		mem_bar0 = 0;
763 	uint32_t		mem_64_bar0;
764 	uint32_t		mem_bar1 = 0;
765 	uint32_t		mem_64_bar1 = 0;
766 	uint32_t		ct_mem_bar = 0;
767 	uint32_t		ct_cfgmem_val = 0;
768 	uint32_t		ct_memoff_val = 0;
769 	uint32_t		ct_cfg_bar = 0;
770 	uint32_t		ct_mem_len = 0;
771 	offset_t		map_len = 0;
772 	uint32_t		regset_index;
773 	ddi_acc_handle_t 	pci_handle;
774 	uint32_t		*ct_cfg_offset;
775 	ddi_acc_handle_t	ct_cfgoff_handle;
776 	uint32_t		*ct_mem_offset;
777 	ddi_acc_handle_t	ct_memoff_handle;
778 
779 	RETURN_FAILURE_IF_NULL(cpqary3p);
780 
781 	/*
782 	 * Check if the bus, or part of the bus  that  the  device  is installed
783 	 * on, permits the device to become a DMA master.
784 	 * If our device is not permitted to become master, return
785 	 */
786 	if (ddi_slaveonly(cpqary3p->dip) == DDI_SUCCESS)
787 		return (CPQARY3_FAILURE);
788 
789 	/*
790 	 * Get the HW Configuration
791 	 * Get Bus #, Dev # and Func # for this device
792 	 * Free the memory that regp points towards after the
793 	 * ddi_getlongprop() call
794 	 */
795 	if (ddi_getlongprop(DDI_DEV_T_NONE, cpqary3p->dip, DDI_PROP_DONTPASS,
796 	    "reg", (caddr_t)&regp, &reglen) != DDI_PROP_SUCCESS)
797 		return (CPQARY3_FAILURE);
798 
799 	cpqary3p->bus = PCI_REG_BUS_G(*regp);
800 	cpqary3p->dev = PCI_REG_DEV_G(*regp);
801 	cpqary3p->fun = PCI_REG_FUNC_G(*regp);
802 
803 	for (regset_index = 0; regset_index < reglen / 20; regset_index ++) {
804 		if (PCI_REG_ADDR_G(*(regp + regset_index * 5)) == 0x2) {
805 			if (!mem_bar0_set) {
806 				mem_bar0 = regset_index;
807 				mem_bar0_set = 1;
808 			} else if (!mem_bar1_set) {
809 				mem_bar1 = regset_index;
810 				mem_bar1_set = 1;
811 			}
812 		}
813 	}
814 
815 	mem_64_bar0 = mem_bar0;
816 	mem_64_bar1 = mem_bar1;
817 
818 	for (regset_index = 0; regset_index < reglen / 20; regset_index ++) {
819 		if (PCI_REG_ADDR_G(*(regp + regset_index * 5)) == 0x3) {
820 			if (!mem_64_bar0_set) {
821 				mem_64_bar0 = regset_index;
822 				mem_64_bar0_set = 1;
823 			} else if (!mem_64_bar1_set) {
824 				mem_64_bar1 = regset_index;
825 				mem_64_bar1_set = 1;
826 			}
827 		}
828 	}
829 
830 	mem_bar0 = mem_64_bar0;
831 	mem_bar1 = mem_64_bar1;
832 
833 	MEM_SFREE(regp, reglen);
834 
835 	/*
836 	 * Setup resources to access the Local PCI Bus
837 	 * If unsuccessful, return.
838 	 * Else, read the following from the PCI space:
839 	 * 	Sub-System Vendor ID
840 	 * 	Sub-System Device ID
841 	 * 	Interrupt Line
842 	 * 	Command Register
843 	 * Free the just allocated resources.
844 	 */
845 	if (pci_config_setup(cpqary3p->dip, &pci_handle) != DDI_SUCCESS)
846 		return (CPQARY3_FAILURE);
847 
848 	cpqary3p->irq = pci_config_get8(pci_handle, PCI_CONF_ILINE);
849 	cpqary3p->board_id =
850 	    (pci_config_get16(pci_handle, PCI_CONF_SUBVENID) << 16)
851 	    | pci_config_get16(pci_handle, PCI_CONF_SUBSYSID);
852 
853 	pci_config_teardown(&pci_handle);
854 
855 	/*
856 	 * Verify Board Id
857 	 * If unsupported boards are detected, return.
858 	 * Update name for controller for driver use.
859 	 */
860 	cpqary3p->bddef = cpqary3_bd_getbybid(cpqary3p->board_id);
861 	if (cpqary3p->bddef == NULL) {
862 		cmn_err(CE_WARN,
863 		    "CPQary3: <Bid 0x%X> Controller NOT Supported",
864 		    cpqary3p->board_id);
865 		return (CPQARY3_FAILURE);
866 	}
867 	map_len = cpqary3p->bddef->bd_maplen;
868 	(void) strcpy(cpqary3p->hba_name, cpqary3p->bddef->bd_dispname);
869 
870 	/*
871 	 * Set up a mapping for the following registers:
872 	 * 	Inbound Doorbell
873 	 * 	Outbound List Status
874 	 * 	Outbound Interrupt Mask
875 	 * 	Host Inbound Queue
876 	 * 	Host Outbound Queue
877 	 * 	Host Transport Configuration Table
878 	 * Mapping of the above has been done in that order.
879 	 */
880 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
881 	    mem_bar0, /* INDEX_PCI_BASE0, */
882 	    (caddr_t *)&cpqary3p->idr, (offset_t)I2O_IBDB_SET, map_len,
883 	    &cpqary3_dev_attributes, &cpqary3p->idr_handle);
884 
885 	if (retvalue != DDI_SUCCESS) {
886 		if (DDI_REGS_ACC_CONFLICT == retvalue) {
887 			cmn_err(CE_WARN,
888 			    "CPQary3 : Registers Mapping Conflict");
889 		}
890 		cmn_err(CE_WARN, "CPQary3 : Inbound Doorbell "
891 		    "Register Mapping Failed");
892 		return (CPQARY3_FAILURE);
893 	}
894 
895 	/* PERF */
896 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
897 	    mem_bar0, /* INDEX_PCI_BASE0, */
898 	    (caddr_t *)&cpqary3p->odr, (offset_t)I2O_OBDB_STATUS, map_len,
899 	    &cpqary3_dev_attributes, &cpqary3p->odr_handle);
900 
901 	if (retvalue != DDI_SUCCESS) {
902 		if (DDI_REGS_ACC_CONFLICT == retvalue) {
903 			cmn_err(CE_WARN,
904 			    "CPQary3 : Registers Mapping Conflict");
905 		}
906 		cmn_err(CE_WARN,
907 		    "CPQary3 : Outbound Doorbell Register Mapping Failed");
908 		return (CPQARY3_FAILURE);
909 	}
910 
911 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
912 	    mem_bar0, /* INDEX_PCI_BASE0, */
913 	    (caddr_t *)&cpqary3p->odr_cl, (offset_t)I2O_OBDB_CLEAR, map_len,
914 	    &cpqary3_dev_attributes, &cpqary3p->odr_cl_handle);
915 
916 	if (retvalue != DDI_SUCCESS) {
917 		if (DDI_REGS_ACC_CONFLICT == retvalue) {
918 			cmn_err(CE_WARN,
919 			    "CPQary3 : Registers Mapping Conflict");
920 		}
921 		cmn_err(CE_WARN, "CPQary3 : Outbound Doorbell "
922 		    "Register Clear Mapping Failed");
923 		return (CPQARY3_FAILURE);
924 	}
925 
926 	/* LOCKUP CODE */
927 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
928 	    mem_bar0, /* INDEX_PCI_BASE0, */
929 	    (caddr_t *)&cpqary3p->spr0, (offset_t)I2O_CTLR_INIT, map_len,
930 	    &cpqary3_dev_attributes, &cpqary3p->spr0_handle);
931 
932 	if (retvalue != DDI_SUCCESS) {
933 		if (DDI_REGS_ACC_CONFLICT == retvalue) {
934 			cmn_err(CE_WARN,
935 			    "CPQary3 : Registers Mapping Conflict");
936 		}
937 		cmn_err(CE_WARN,
938 		    "CPQary3 : Scratch Pad register zero Mapping Failed");
939 		return (CPQARY3_FAILURE);
940 	}
941 	/* LOCKUP CODE */
942 	/* PERF */
943 
944 	*cleanstatus |= CPQARY3_MEM_MAPPED;
945 
946 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
947 	    mem_bar0, /* INDEX_PCI_BASE0, */
948 	    (caddr_t *)&cpqary3p->isr, (offset_t)I2O_INT_STATUS, map_len,
949 	    &cpqary3_dev_attributes, &cpqary3p->isr_handle);
950 
951 	if (retvalue != DDI_SUCCESS) {
952 		if (retvalue == DDI_REGS_ACC_CONFLICT) {
953 			cmn_err(CE_WARN,
954 			    "CPQary3 : Registers Mapping Conflict");
955 		}
956 		cmn_err(CE_WARN,
957 		    "CPQary3 : Interrupt Status Register Mapping Failed");
958 		return (CPQARY3_FAILURE);
959 	}
960 
961 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
962 	    mem_bar0, /* INDEX_PCI_BASE0, */
963 	    (caddr_t *)&cpqary3p->imr, (offset_t)I2O_INT_MASK, map_len,
964 	    &cpqary3_dev_attributes, &cpqary3p->imr_handle);
965 
966 	if (retvalue != DDI_SUCCESS) {
967 		if (retvalue == DDI_REGS_ACC_CONFLICT) {
968 			cmn_err(CE_WARN,
969 			    "CPQary3 : Registers Mapping Conflict");
970 		}
971 		cmn_err(CE_WARN,
972 		    "CPQary3 : Interrupt Mask Register Mapping Failed");
973 		return (CPQARY3_FAILURE);
974 	}
975 
976 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
977 	    mem_bar0, /* INDEX_PCI_BASE0, */
978 	    (caddr_t *)&cpqary3p->ipq, (offset_t)I2O_IBPOST_Q, map_len,
979 	    &cpqary3_dev_attributes, &cpqary3p->ipq_handle);
980 
981 	if (retvalue != DDI_SUCCESS) {
982 		if (retvalue == DDI_REGS_ACC_CONFLICT) {
983 			cmn_err(CE_WARN,
984 			    "CPQary3 : Registers Mapping Conflict");
985 		}
986 		cmn_err(CE_WARN,
987 		    "CPQary3 : Inbound Queue Register Mapping Failed");
988 		return (CPQARY3_FAILURE);
989 	}
990 
991 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
992 	    mem_bar0, /* INDEX_PCI_BASE0, */ (caddr_t *)&cpqary3p->opq,
993 	    (offset_t)I2O_OBPOST_Q, map_len, &cpqary3_dev_attributes,
994 	    &cpqary3p->opq_handle);
995 
996 	if (retvalue != DDI_SUCCESS) {
997 		if (retvalue == DDI_REGS_ACC_CONFLICT) {
998 			cmn_err(CE_WARN,
999 			    "CPQary3 : Registers Mapping Conflict");
1000 		}
1001 		cmn_err(CE_WARN, "CPQary3 : Outbound Post Queue "
1002 		    "Register Mapping Failed");
1003 		return (CPQARY3_FAILURE);
1004 	}
1005 
1006 
1007 	/*
1008 	 * The config offset and memory offset have to be obtained in order to
1009 	 * locate the config table.
1010 	 */
1011 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
1012 	    mem_bar0, /* INDEX_PCI_BASE0, */ (caddr_t *)&ct_cfg_offset,
1013 	    (offset_t)CT_CFG_OFFSET, map_len, &cpqary3_dev_attributes,
1014 	    &ct_cfgoff_handle);
1015 
1016 	if (retvalue != DDI_SUCCESS) {
1017 		if (retvalue == DDI_REGS_ACC_CONFLICT) {
1018 			cmn_err(CE_WARN,
1019 			    "CPQary3 : Registers Mapping Conflict");
1020 		}
1021 		cmn_err(CE_WARN, "CPQary3 : Configuration Table "
1022 		    "Register Mapping Failed");
1023 		return (CPQARY3_FAILURE);
1024 	}
1025 
1026 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
1027 	    mem_bar0, /* INDEX_PCI_BASE0, */
1028 	    (caddr_t *)&ct_mem_offset, (offset_t)CT_MEM_OFFSET, map_len,
1029 	    &cpqary3_dev_attributes, &ct_memoff_handle);
1030 
1031 	if (retvalue != DDI_SUCCESS) {
1032 		if (retvalue == DDI_REGS_ACC_CONFLICT) {
1033 			cmn_err(CE_WARN,
1034 			    "CPQary3 : Registers Mapping Conflict");
1035 		}
1036 		cmn_err(CE_WARN, "CPQary3 : Configuration Table "
1037 		    "Register Mapping Failed");
1038 		return (CPQARY3_FAILURE);
1039 	}
1040 
1041 	ct_cfgmem_val = (uint32_t)ddi_get32(ct_cfgoff_handle, ct_cfg_offset);
1042 	ct_memoff_val = (uint32_t)ddi_get32(ct_memoff_handle, ct_mem_offset);
1043 
1044 	ddi_regs_map_free(&ct_cfgoff_handle);
1045 	ddi_regs_map_free(&ct_memoff_handle);
1046 
1047 	ct_cfg_bar = (ct_cfgmem_val & 0x0000ffff);
1048 	ct_mem_len = (ct_cfgmem_val & 0xffff0000);
1049 	ct_mem_len = (ct_mem_len >> 16);
1050 
1051 	if (ct_cfg_bar == 0x10) {
1052 		if (ct_mem_len) {
1053 			ct_mem_bar = mem_64_bar0;
1054 		} else {
1055 			ct_mem_bar = mem_bar0;
1056 		}
1057 
1058 	} else if (ct_cfg_bar == 0x14) {
1059 		if (ct_mem_len) {
1060 			ct_mem_bar = mem_64_bar1;
1061 		} else {
1062 			ct_mem_bar = mem_bar1;
1063 		}
1064 	} else {
1065 		return (CPQARY3_FAILURE);
1066 	}
1067 
1068 
1069 	/*
1070 	 * The Configuration Table(CT) shall be mapped in the form of a
1071 	 * structure since several members in the CT need to be accessed
1072 	 * to read and write.
1073 	 */
1074 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
1075 	    ct_mem_bar, /* INDEX_PCI_BASE0/1, */
1076 	    (caddr_t *)&cpqary3p->ct, (offset_t)ct_memoff_val,
1077 	    sizeof (CfgTable_t), &cpqary3_dev_attributes, &cpqary3p->ct_handle);
1078 
1079 	if (retvalue != DDI_SUCCESS) {
1080 		if (retvalue == DDI_REGS_ACC_CONFLICT) {
1081 			cmn_err(CE_WARN,
1082 			    "CPQary3 : Registers Mapping Conflict");
1083 		}
1084 		cmn_err(CE_WARN, "CPQary3 : Configuration Table "
1085 		    "Register Mapping Failed");
1086 		return (CPQARY3_FAILURE);
1087 	}
1088 
1089 	/* PERF */
1090 
1091 	retvalue = ddi_regs_map_setup(cpqary3p->dip,
1092 	    ct_mem_bar, /* INDEX_PCI_BASE0/1, */
1093 	    (caddr_t *)&cpqary3p->cp,
1094 	    (offset_t)(ct_memoff_val + cpqary3p->ct->TransportMethodOffset),
1095 	    sizeof (CfgTrans_Perf_t), &cpqary3_dev_attributes,
1096 	    &cpqary3p->cp_handle);
1097 
1098 	if (retvalue != DDI_SUCCESS) {
1099 		if (retvalue == DDI_REGS_ACC_CONFLICT)
1100 			cmn_err(CE_WARN,
1101 			    "CPQary3 : Registers Mapping Conflict");
1102 		cmn_err(CE_WARN, "CPQary3 : Performant Transport Method Table "
1103 		    "Mapping Failed");
1104 		return (CPQARY3_FAILURE);
1105 	}
1106 
1107 	/* PERF */
1108 
1109 	return (CPQARY3_SUCCESS);
1110 }
1111