PSARC/2006/501 Nvidia ck804/mcp55 SATA HBA driver
authormlf
Thu, 16 Aug 2007 14:46:34 -0700
changeset 4876 ecd69ba0713a
parent 4875 4ae54dfe2667
child 4877 5744980c78cf
PSARC/2006/501 Nvidia ck804/mcp55 SATA HBA driver PSARC/2007/100 Device Id Extensions PSARC/2007/172 Reserved Devnames 6296435 native sata driver needed for nVIDIA mcp04 and mcp55 controllers 6418034 DMA resources need to be released for scsi commands emulated in sata framework 6487018 driver binding for sata storage devices should be more flexible 6487838 common definitions can be defined in sata_defs.h to be shared with other sata HBA's 6549855 PSARC/2007/100 Device Id Extensions 6549915 devfsadm enhancements for PSARC/2007/172 Reserved Devnames
usr/src/Targetdirs
usr/src/cmd/devfsadm/Makefile
usr/src/cmd/devfsadm/devfsadm.c
usr/src/cmd/devfsadm/devfsadm.h
usr/src/cmd/devfsadm/devfsadm_impl.h
usr/src/cmd/devfsadm/disk_link.c
usr/src/cmd/devfsadm/reserved_devnames
usr/src/pkgdefs/SUNWcsr/prototype_com
usr/src/pkgdefs/SUNWnvsata/Makefile
usr/src/pkgdefs/SUNWnvsata/postinstall
usr/src/pkgdefs/SUNWnvsata/postremove
usr/src/pkgdefs/SUNWnvsata/prototype_i386
usr/src/tools/findunref/exception_list
usr/src/tools/scripts/bfu.sh
usr/src/uts/common/Makefile.files
usr/src/uts/common/Makefile.rules
usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c
usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.conf
usr/src/uts/common/io/sata/impl/sata.c
usr/src/uts/common/io/scsi/targets/sd.c
usr/src/uts/common/io/warlock/nv_sata.wlcmd
usr/src/uts/common/os/sunddi.c
usr/src/uts/common/sys/sata/adapters/nv_sata/nv_sata.h
usr/src/uts/common/sys/sata/adapters/si3124/si3124reg.h
usr/src/uts/common/sys/sata/sata_defs.h
usr/src/uts/common/sys/sunddi.h
usr/src/uts/intel/Makefile.intel.shared
usr/src/uts/intel/io/pci/pci_boot.c
usr/src/uts/intel/nv_sata/Makefile
usr/src/uts/intel/warlock/Makefile
usr/src/uts/sparc/nv_sata/Makefile
usr/src/uts/sparc/warlock/Makefile
--- a/usr/src/Targetdirs	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/Targetdirs	Thu Aug 16 14:46:34 2007 -0700
@@ -80,6 +80,7 @@
 	/etc/crypto/crls \
 	/etc/default \
 	/etc/devices  \
+	/etc/dev  \
 	/etc/dfs  \
 	/etc/fs  \
 	/etc/fs/nfs  \
--- a/usr/src/cmd/devfsadm/Makefile	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/cmd/devfsadm/Makefile	Thu Aug 16 14:46:34 2007 -0700
@@ -2,9 +2,8 @@
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 # or http://www.opensolaris.org/os/licensing.
@@ -22,7 +21,7 @@
 #
 #ident	"%Z%%M%	%I%	%E% SMI"
 #
-# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # cmd/devfsadm/Makefile
@@ -30,6 +29,8 @@
 
 DEFAULTFILES = devfsadm.dfl
 
+ETCDEVFILES=reserved_devnames
+
 include ../Makefile.cmd
 
 #
@@ -44,11 +45,31 @@
 _msg	:=	TARGET= _msg
 lint	:=	TARGET= lint
 
+ROOTETCDEV=	$(ROOTETC)/dev
+ROOTETCDEVFILES=$(ETCDEVFILES:%=$(ROOTETCDEV)/%)
+$(ROOTETCDEV) :=	DIRMODE=	755
+$(ROOTETCDEV) :=	OWNER=          root
+$(ROOTETCDEV) :=	GROUP=          sys
+$(ROOTETCDEVFILES) :=	OWNER = root
+$(ROOTETCDEVFILES) :=	GROUP = sys
+$(ROOTETCDEVFILES) :=	FILEMODE = 0644
+
 .KEEP_STATE:
 
-all clean clobber lint _msg:	$(SUBDIRS)
+all:	$(SUBDIRS) $(ETCDEVFILES)
+
+clean lint _msg:	$(SUBDIRS)
+
+clobber: $(SUBDIRS)
+	$(RM) $(ROOTETCDEVFILES)
 
-install: $(SUBDIRS) $(ROOTETCDEFAULTFILES)
+install: $(SUBDIRS) $(ROOTETCDEFAULTFILES) $(ROOTETCDEVFILES)
+
+$(ROOTETCDEV):
+	$(INS.dir)
+
+$(ROOTETCDEV)/% : % $(ROOTETCDEV)
+	$(INS.file)
 
 $(SUBDIRS):	FRC
 	@cd $@; pwd; $(MAKE) $(TARGET)
--- a/usr/src/cmd/devfsadm/devfsadm.c	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/cmd/devfsadm/devfsadm.c	Thu Aug 16 14:46:34 2007 -0700
@@ -165,6 +165,10 @@
 /* /etc/devlink.tab unless devlinks -t used */
 static char *devlinktab_file = NULL;
 
+/* File and data structure to reserve enumerate IDs */
+static char *enumerate_file = ENUMERATE_RESERVED;
+static enumerate_file_t *enumerate_reserved = NULL;
+
 /* set if /dev link is new. speeds up rm_stale_links */
 static int linknew = TRUE;
 
@@ -1363,6 +1367,7 @@
 		read_driver_aliases_file();
 		read_devlinktab_file();
 		read_logindevperm_file();
+		read_enumerate_file();
 	}
 
 	if (module_head != NULL)
@@ -4821,6 +4826,9 @@
 
 	/* if matching entry already cached, return it */
 	if (matchcount == 1) {
+		/* should never create a link with a reserved ID */
+		vprint(ENUM_MID, "%s: 1 match w/ ID: %s\n", fcn, matchnp->id);
+		assert(matchnp->flags == 0);
 		*buf = s_strdup(matchnp->id);
 		free(cmp_str);
 		return (DEVFSADM_SUCCESS);
@@ -4836,6 +4844,10 @@
 	numeral->rule_index = index;
 	numeral->cmp_str = cmp_str;
 	cmp_str = NULL;
+	numeral->flags = 0;
+	vprint(RSRV_MID, "%s: alloc new_id: %s numeral flags = %d\n",
+	    fcn, numeral->id, numeral->flags);
+
 
 	/* insert to head of list for fast lookups */
 	numeral->next = set->headnumeral;
@@ -4882,6 +4894,23 @@
 	 * Check and see if a matching entry is already cached.
 	 */
 	for (np = set->headnumeral; np != NULL; np = np->next) {
+
+		/*
+		 * Skip reserved IDs
+		 */
+		if (np->flags & NUMERAL_RESERVED) {
+			vprint(RSRV_MID, "lookup_enum_cache: "
+			    "Cannot Match with reserved ID (%s), "
+			    "skipping\n", np->id);
+			assert(np->flags == NUMERAL_RESERVED);
+			continue;
+		} else {
+			vprint(RSRV_MID, "lookup_enum_cache: "
+			    "Attempting match with numeral ID: %s"
+			    " numeral flags = %d\n", np->id, np->flags);
+			assert(np->flags == 0);
+		}
+
 		if (np->cmp_str == NULL) {
 			vprint(ENUM_MID, "%s: invalid entry in enumerate"
 			    " cache. path: %s\n", fcn, np->full_path);
@@ -4930,6 +4959,7 @@
 		vprint(ENUM_MID, "%s: full_path: %s\n", fcn, np->full_path);
 		vprint(ENUM_MID, "%s: rule_index: %d\n", fcn, np->rule_index);
 		vprint(ENUM_MID, "%s: cmp_str: %s\n", fcn, np->cmp_str);
+		vprint(ENUM_MID, "%s: flags: %d\n", fcn, np->flags);
 	}
 }
 #endif
@@ -4959,7 +4989,9 @@
 	/* linked list of numeral sets */
 	numeral_set_t *setp;
 	int i;
+	int ret;
 	char *path_left;
+	enumerate_file_t *entry;
 	char *fcn = "get_enum_cache";
 
 	/*
@@ -5016,6 +5048,33 @@
 	head_numeral_set = setp;
 
 	/*
+	 * For each RE, search the "reserved" list to create numeral IDs that
+	 * are reserved.
+	 */
+	for (entry = enumerate_reserved; entry; entry = entry->er_next) {
+
+		vprint(RSRV_MID, "parsing rstring: %s\n", entry->er_file);
+
+		for (i = 0; i < nrules; i++) {
+			path_left = s_strdup(setp->re[i]);
+			vprint(RSRV_MID, "parsing rule RE: %s\n", path_left);
+			ret = enumerate_parse(entry->er_file, path_left,
+			    setp, rules, i);
+			free(path_left);
+			if (ret == 1) {
+				/*
+				 * We found the reserved ID for this entry.
+				 * We still keep the entry since it is needed
+				 * by the new link bypass code in disks
+				 */
+				vprint(RSRV_MID, "found rsv ID: rstring: %s "
+				    "rule RE: %s\n", entry->er_file, path_left);
+				break;
+			}
+		}
+	}
+
+	/*
 	 * For each RE, search disk and cache any matches on the
 	 * numeral list.
 	 */
@@ -5124,6 +5183,8 @@
 		}
 
 		for (np = numeral; np != NULL; np = np->next) {
+			assert(np->flags == 0 ||
+			    np->flags == NUMERAL_RESERVED);
 			letter[*np->id - 'a']++;
 		}
 
@@ -5151,6 +5212,8 @@
 
 		/* sort list */
 		for (np = numeral; np != NULL; np = np->next) {
+			assert(np->flags == 0 ||
+			    np->flags == NUMERAL_RESERVED);
 			temp = s_malloc(sizeof (temp_t));
 			temp->integer = atoi(np->id);
 			temp->next = NULL;
@@ -5195,6 +5258,114 @@
 	return (s_strdup(""));
 }
 
+static int
+enumerate_parse(char *rsvstr, char *path_left, numeral_set_t *setp,
+	    devfsadm_enumerate_t rules[], int index)
+{
+	char	*slash1 = NULL;
+	char	*slash2 = NULL;
+	char	*numeral_id;
+	char	*path_left_save;
+	char	*rsvstr_save;
+	int	ret = 0;
+	static int warned = 0;
+
+	rsvstr_save = rsvstr;
+	path_left_save = path_left;
+
+	if (rsvstr == NULL || rsvstr[0] == '\0' || rsvstr[0] == '/') {
+		if (!warned) {
+			err_print("invalid reserved filepath: %s\n",
+			    rsvstr ? rsvstr : "<NULL>");
+			warned = 1;
+		}
+		return (0);
+	}
+
+	vprint(RSRV_MID, "processing rule: %s, rstring: %s\n",
+	    path_left, rsvstr);
+
+
+	for (;;) {
+		/* get rid of any extra '/' in the reserve string */
+		while (*rsvstr == '/') {
+			rsvstr++;
+		}
+
+		/* get rid of any extra '/' in the RE */
+		while (*path_left == '/') {
+			path_left++;
+		}
+
+		if (slash1 = strchr(path_left, '/')) {
+			*slash1 = '\0';
+		}
+		if (slash2 = strchr(rsvstr, '/')) {
+			*slash2 = '\0';
+		}
+
+		if ((slash1 != NULL) ^ (slash2 != NULL)) {
+			ret = 0;
+			vprint(RSRV_MID, "mismatch in # of path components\n");
+			goto out;
+		}
+
+		/*
+		 *  Returns true if path_left matches the list entry.
+		 *  If it is the last path component, pass subexp
+		 *  so that it will return the corresponding ID in
+		 *  numeral_id.
+		 */
+		numeral_id = NULL;
+		if (match_path_component(path_left, rsvstr, &numeral_id,
+				    slash1 ? 0 : rules[index].subexp)) {
+
+			/* We have a match. */
+			if (slash1 == NULL) {
+				/* Is last path component */
+				vprint(RSRV_MID, "match and last component\n");
+				create_reserved_numeral(setp, numeral_id);
+				if (numeral_id != NULL) {
+					free(numeral_id);
+				}
+				ret = 1;
+				goto out;
+			} else {
+				/* Not last path component. Continue parsing */
+				*slash1 = '/';
+				*slash2 = '/';
+				path_left = slash1 + 1;
+				rsvstr = slash2 + 1;
+				vprint(RSRV_MID,
+				    "match and NOT last component\n");
+				continue;
+			}
+		} else {
+			/* No match */
+			ret = 0;
+			vprint(RSRV_MID, "No match: rule RE = %s, "
+			    "rstring = %s\n", path_left, rsvstr);
+			goto out;
+		}
+	}
+
+out:
+	if (slash1)
+		*slash1 = '/';
+	if (slash2)
+		*slash2 = '/';
+
+	if (ret == 1) {
+		vprint(RSRV_MID, "match: rule RE: %s, rstring: %s\n",
+		    path_left_save, rsvstr_save);
+	} else {
+		vprint(RSRV_MID, "NO match: rule RE: %s, rstring: %s\n",
+		    path_left_save, rsvstr_save);
+	}
+
+	return (ret);
+}
+
 /*
  * Search current_dir for all files which match the first path component
  * of path_left, which is an RE.  If a match is found, but there are more
@@ -5314,6 +5485,45 @@
 	return (match);
 }
 
+static void
+create_reserved_numeral(numeral_set_t *setp, char *numeral_id)
+{
+	numeral_t *np;
+
+	vprint(RSRV_MID, "Attempting to create reserved numeral: %s\n",
+	    numeral_id);
+
+	/*
+	 * We found a numeral_id from an entry in the enumerate_reserved file
+	 * which matched the re passed in from devfsadm_enumerate.  We only
+	 * need to make sure ONE copy of numeral_id exists on the numeral list.
+	 * We only need to store /dev/dsk/cNtod0s0 and no other entries
+	 * hanging off of controller N.
+	 */
+	for (np = setp->headnumeral; np != NULL; np = np->next) {
+		if (strcmp(numeral_id, np->id) == 0) {
+			vprint(RSRV_MID, "ID: %s, already reserved\n", np->id);
+			assert(np->flags == NUMERAL_RESERVED);
+			return;
+		} else {
+			assert(np->flags == 0 ||
+			    np->flags == NUMERAL_RESERVED);
+		}
+	}
+
+	/* NOT on list, so add it */
+	np = s_malloc(sizeof (numeral_t));
+	np->id = s_strdup(numeral_id);
+	np->full_path = NULL;
+	np->rule_index = 0;
+	np->cmp_str = NULL;
+	np->flags = NUMERAL_RESERVED;
+	np->next = setp->headnumeral;
+	setp->headnumeral = np;
+
+	vprint(RSRV_MID, "Reserved numeral ID: %s\n", np->id);
+}
+
 /*
  * This function is called for every file which matched the leaf
  * component of the RE.  If the "numeral_id" is not already on the
@@ -5342,7 +5552,20 @@
 	 *  of controller N.
 	 */
 	for (np = setp->headnumeral; np != NULL; np = np->next) {
+		assert(np->flags == 0 || np->flags == NUMERAL_RESERVED);
 		if (strcmp(numeral_id, np->id) == 0) {
+			/*
+			 * Note that we can't assert that the flags field
+			 * of the numeral is 0, since both reserved and
+			 * unreserved links in /dev come here
+			 */
+			if (np->flags == NUMERAL_RESERVED) {
+				vprint(RSRV_MID, "ID derived from /dev link is"
+				    " reserved: %s\n", np->id);
+			} else {
+				vprint(RSRV_MID, "ID derived from /dev link is"
+				    " NOT reserved: %s\n", np->id);
+			}
 			return;
 		}
 	}
@@ -5386,6 +5609,7 @@
 	np->full_path = s_strdup(linkptr);
 	np->rule_index = index;
 	np->cmp_str = cmp_str;
+	np->flags = 0;
 
 	np->next = setp->headnumeral;
 	setp->headnumeral = np;
@@ -5659,6 +5883,114 @@
 }
 
 /*
+ * Called at devfsadm startup to read the file /etc/dev/enumerate_reserved
+ * Creates a linked list of devlinks from which reserved IDs can be derived
+ */
+static void
+read_enumerate_file(void)
+{
+	FILE *fp;
+	int linenum;
+	char line[PATH_MAX+1];
+	enumerate_file_t *entry;
+	struct stat current_sb;
+	static struct stat cached_sb;
+	static int cached = FALSE;
+
+	assert(enumerate_file);
+
+	if (stat(enumerate_file, &current_sb) == -1) {
+		vprint(RSRV_MID, "No reserved file: %s\n", enumerate_file);
+		cached = FALSE;
+		if (enumerate_reserved != NULL) {
+			vprint(RSRV_MID, "invalidating %s cache\n",
+			    enumerate_file);
+		}
+		while (enumerate_reserved != NULL) {
+			entry = enumerate_reserved;
+			enumerate_reserved = entry->er_next;
+			free(entry->er_file);
+			free(entry->er_id);
+			free(entry);
+		}
+		return;
+	}
+
+	/* if already cached, check to see if it is still valid */
+	if (cached == TRUE) {
+
+		if (current_sb.st_mtime == cached_sb.st_mtime) {
+			vprint(RSRV_MID, "%s cache valid\n", enumerate_file);
+			vprint(FILES_MID, "%s cache valid\n", enumerate_file);
+			return;
+		}
+
+		vprint(RSRV_MID, "invalidating %s cache\n", enumerate_file);
+		vprint(FILES_MID, "invalidating %s cache\n", enumerate_file);
+
+		while (enumerate_reserved != NULL) {
+			entry = enumerate_reserved;
+			enumerate_reserved = entry->er_next;
+			free(entry->er_file);
+			free(entry->er_id);
+			free(entry);
+		}
+		vprint(RSRV_MID, "Recaching file: %s\n", enumerate_file);
+	} else {
+		vprint(RSRV_MID, "Caching file (first time): %s\n",
+		    enumerate_file);
+		cached = TRUE;
+	}
+
+	(void) stat(enumerate_file, &cached_sb);
+
+	if ((fp = fopen(enumerate_file, "r")) == NULL) {
+		err_print(FOPEN_FAILED, enumerate_file, strerror(errno));
+		return;
+	}
+
+	vprint(RSRV_MID, "Reading reserve file: %s\n", enumerate_file);
+	linenum = 0;
+	while (fgets(line, sizeof (line), fp) != NULL) {
+		char	*cp, *ncp;
+
+		linenum++;
+
+		/* remove newline */
+		cp = strchr(line, '\n');
+		if (cp)
+			*cp = '\0';
+
+		vprint(RSRV_MID, "Reserve file: line %d: %s\n",
+			linenum, line);
+
+		/* skip over space and tab */
+		for (cp = line; *cp == ' ' || *cp == '\t'; cp++);
+
+		if (*cp == '\0' || *cp == '#') {
+			vprint(RSRV_MID, "Skipping line: '%s'\n", line);
+			continue; /* blank line or comment line */
+		}
+
+		ncp = cp;
+
+		/* delete trailing blanks */
+		for (; *cp != ' ' && *cp != '\t' && *cp != '\0'; cp++);
+		*cp = '\0';
+
+		entry = s_zalloc(sizeof (enumerate_file_t));
+		entry->er_file = s_strdup(ncp);
+		entry->er_id = NULL;
+		entry->er_next = enumerate_reserved;
+		enumerate_reserved = entry;
+	}
+
+	if (fclose(fp) == EOF) {
+		err_print(FCLOSE_FAILED, enumerate_file, strerror(errno));
+	}
+}
+
+/*
  * Called at devfsadm startup to read in the devlink.tab file.	Creates
  * a linked list of devlinktab_list structures which will be
  * searched for every minor node.
@@ -8512,3 +8844,149 @@
 	(void) door_return((char *)&res, sizeof (struct sdev_door_res),
 	    NULL, 0);
 }
+
+
+di_devlink_handle_t
+devfsadm_devlink_cache(void)
+{
+	return (devlink_cache);
+}
+
+int
+devfsadm_reserve_id_cache(devlink_re_t re_array[], enumerate_file_t *head)
+{
+	enumerate_file_t *entry;
+	int nelem;
+	int i;
+	int subex;
+	char *re;
+	size_t size;
+	regmatch_t *pmch;
+
+	/*
+	 * Check the <RE, subexp> array passed in and compile it.
+	 */
+	for (i = 0; re_array[i].d_re; i++) {
+		if (re_array[i].d_subexp == 0) {
+			err_print("bad subexp value in RE: %s\n",
+			    re_array[i].d_re);
+			goto bad_re;
+		}
+
+		re = re_array[i].d_re;
+		if (regcomp(&re_array[i].d_rcomp, re, REG_EXTENDED) != 0) {
+			err_print("reg. exp. failed to compile: %s\n", re);
+			goto bad_re;
+		}
+		subex = re_array[i].d_subexp;
+		nelem = subex + 1;
+		re_array[i].d_pmatch = s_malloc(sizeof (regmatch_t) * nelem);
+	}
+
+	entry = head ? head : enumerate_reserved;
+	for (; entry; entry = entry->er_next) {
+		if (entry->er_id) {
+			vprint(RSBY_MID, "entry %s already has ID %s\n",
+			    entry->er_file, entry->er_id);
+			continue;
+		}
+		for (i = 0; re_array[i].d_re; i++) {
+			subex = re_array[i].d_subexp;
+			pmch = re_array[i].d_pmatch;
+			if (regexec(&re_array[i].d_rcomp, entry->er_file,
+			    subex + 1, pmch, 0) != 0) {
+				/* No match */
+				continue;
+			}
+			size = pmch[subex].rm_eo - pmch[subex].rm_so;
+			entry->er_id = s_malloc(size + 1);
+			(void) strncpy(entry->er_id,
+			    &entry->er_file[pmch[subex].rm_so], size);
+			entry->er_id[size] = '\0';
+			if (head) {
+				vprint(RSBY_MID, "devlink(%s) matches RE(%s). "
+				    "ID is %s\n", entry->er_file,
+				    re_array[i].d_re, entry->er_id);
+			} else {
+				vprint(RSBY_MID, "rsrv entry(%s) matches "
+				    "RE(%s) ID is %s\n", entry->er_file,
+				    re_array[i].d_re, entry->er_id);
+			}
+			break;
+		}
+	}
+
+	for (i = 0; re_array[i].d_re; i++) {
+		regfree(&re_array[i].d_rcomp);
+		assert(re_array[i].d_pmatch);
+		free(re_array[i].d_pmatch);
+	}
+
+	entry = head ? head : enumerate_reserved;
+	for (; entry; entry = entry->er_next) {
+		if (entry->er_id == NULL)
+			continue;
+		if (head) {
+			vprint(RSBY_MID, "devlink: %s\n", entry->er_file);
+			vprint(RSBY_MID, "ID: %s\n", entry->er_id);
+		} else {
+			vprint(RSBY_MID, "reserve file entry: %s\n",
+			    entry->er_file);
+			vprint(RSBY_MID, "reserve file id: %s\n",
+			    entry->er_id);
+		}
+	}
+
+	return (DEVFSADM_SUCCESS);
+
+bad_re:
+	for (i = i-1; i >= 0; i--) {
+		regfree(&re_array[i].d_rcomp);
+		assert(re_array[i].d_pmatch);
+		free(re_array[i].d_pmatch);
+	}
+	return (DEVFSADM_FAILURE);
+}
+
+/*
+ * This functions errs on the side of caution. If there is any error
+ * we assume that the devlink is  *not* reserved
+ */
+int
+devfsadm_is_reserved(devlink_re_t re_array[], char *devlink)
+{
+	int match;
+	enumerate_file_t estruct = {NULL};
+	enumerate_file_t *entry;
+
+	match = 0;
+	estruct.er_file = devlink;
+	estruct.er_id = NULL;
+	estruct.er_next = NULL;
+
+	if (devfsadm_reserve_id_cache(re_array, &estruct) != DEVFSADM_SUCCESS) {
+		err_print("devfsadm_is_reserved: devlink (%s) does not "
+		    "match RE\n", devlink);
+		return (0);
+	}
+	if (estruct.er_id == NULL) {
+		err_print("devfsadm_is_reserved: ID derived from devlink %s "
+		    "is NULL\n", devlink);
+		return (0);
+	}
+
+	entry = enumerate_reserved;
+	for (; entry; entry = entry->er_next) {
+		if (entry->er_id == NULL)
+			continue;
+		if (strcmp(entry->er_id, estruct.er_id) != 0)
+			continue;
+		match = 1;
+		vprint(RSBY_MID, "reserve file entry (%s) and devlink (%s) "
+		    "match\n", entry->er_file, devlink);
+		break;
+	}
+
+	free(estruct.er_id);
+	return (match);
+}
--- a/usr/src/cmd/devfsadm/devfsadm.h	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/cmd/devfsadm/devfsadm.h	Thu Aug 16 14:46:34 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -33,6 +33,15 @@
 #include <sys/devinfo_impl.h>
 #include <regex.h>
 
+#undef	DEBUG
+#ifndef DEBUG
+#define	NDEBUG 1
+#else
+#undef	NDEBUG
+#endif
+
+#include <assert.h>
+
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -90,6 +99,9 @@
 /* /dev device name binding rule locations */
 #define	DEVNAME_MASTER_MAP	"/etc/dev/devname_master"
 
+/* File of reserved devnames */
+#define	ENUMERATE_RESERVED "/etc/dev/reserved_devnames"
+
 /* flags for devfsadm_mklink */
 #define	DEV_SYNC 0x02	/* synchronous mklink */
 
@@ -189,6 +201,20 @@
 	(sizeof (tbl) / sizeof (devfsadm_remove_V1_t)), \
 	((devfsadm_remove_V1_t *)(tbl)) }
 
+/* reserved devname support */
+typedef struct devlink_re {
+	char *d_re;
+	int d_subexp;
+	regex_t d_rcomp;
+	regmatch_t *d_pmatch;
+} devlink_re_t;
+
+typedef struct enumerate_file {
+	char *er_file;
+	char *er_id;
+	struct enumerate_file *er_next;
+} enumerate_file_t;
+
 int devfsadm_noupdate(void);
 const char *devfsadm_root_path(void);
 int devfsadm_link_valid(char *link);
@@ -207,6 +233,9 @@
 char **devfsadm_lookup_dev_names(char *phys_path, char *re, int *lenp);
 void devfsadm_free_dev_names(char **dev_names, int len);
 
+/* devlink cache related */
+di_devlink_handle_t devfsadm_devlink_cache(void);
+
 /*
  * Private enumerate interface for disks and sgen modules
  */
@@ -220,6 +249,10 @@
 int devfsadm_read_link(char *link, char **devfs_path);
 char *s_strdup(const char *ptr);
 
+/* Private interface between reserve subsystm and disks link generator */
+int devfsadm_is_reserved(devlink_re_t re_array[], char *devlink);
+int devfsadm_reserve_id_cache(devlink_re_t re_array[], enumerate_file_t *head);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/cmd/devfsadm/devfsadm_impl.h	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/cmd/devfsadm/devfsadm_impl.h	Thu Aug 16 14:46:34 2007 -0700
@@ -79,16 +79,6 @@
 #include <rpcsvc/ypclnt.h>
 #include <sys/sysevent/eventdefs.h>
 
-#undef	DEBUG
-#ifndef DEBUG
-#define	NDEBUG 1
-#else
-#undef	NDEBUG
-#endif
-
-#include <assert.h>
-
-
 #define	DEV_LOCK_FILE ".devfsadm_dev.lock"
 #define	DAEMON_LOCK_FILE ".devfsadm_daemon.lock"
 
@@ -163,6 +153,9 @@
 
 #define	MODULE_ACTIVE 0x01
 
+/* Possible flag values for flag member of numeral_t */
+#define	NUMERAL_RESERVED 0x01
+
 #define	MAX_SLEEP 120
 
 #define	DEVLINKTAB_FILE "/etc/devlink.tab"
@@ -193,6 +186,8 @@
 #define	INSTSYNC_MID		"devfsadm:instsync"
 #define	FILES_MID		"devfsadm:files"
 #define	ENUM_MID		"devfsadm:enum"
+#define	RSRV_MID		"devfsadm:rsrv"	/* enum interface reserve  */
+#define	RSBY_MID		"devfsadm:rsby"	/* enum reserve bypass */
 #define	LINKCACHE_MID		"devfsadm:linkcache"
 #define	ADDREMCACHE_MID		"devfsadm:addremcache"
 #define	MALLOC_MID		"devfsadm:malloc"
@@ -305,6 +300,7 @@
 	int rule_index;
 	char *cmp_str;
 	struct numeral *next;
+	int flags;
 } numeral_t;
 
 typedef struct numeral_set {
@@ -514,12 +510,19 @@
 static void devname_lookup_handler(void *, char *, size_t,
     door_desc_t *, uint_t);		/* /dev name lookup server */
 static int devname_kcall(int, void *);	/* syscall into the devname fs */
+
 static void nfphash_create(void);
 static int nfphash_fcn(char *key);
 static item_t *nfphash_lookup(char *key);
 static void nfphash_insert(char *key);
 static void nfphash_destroy(void);
 
+/* Enumerate reserve related */
+static void read_enumerate_file(void);
+static int enumerate_parse(char *rsvstr, char *path_left, numeral_set_t *setp,
+    devfsadm_enumerate_t rules[], int index);
+static void create_reserved_numeral(numeral_set_t *setp, char *numeral_id);
+
 /* convenient short hands */
 #define	vprint		devfsadm_print
 #define	err_print	devfsadm_errprint
--- a/usr/src/cmd/devfsadm/disk_link.c	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/cmd/devfsadm/disk_link.c	Thu Aug 16 14:46:34 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -55,7 +55,7 @@
 static void disk_common(di_minor_t minor, di_node_t node, char *disk,
 				int flags);
 static char *diskctrl(di_node_t node, di_minor_t minor);
-extern void rm_link_from_cache(char *devlink, char *physpath);
+static int reserved_links_exist(di_node_t node, di_minor_t minor, int nflags);
 
 
 static devfsadm_create_t disk_cbt[] = {
@@ -92,6 +92,25 @@
 
 DEVFSADM_REMOVE_INIT_V0(disk_remove_cbt);
 
+static devlink_re_t disks_re_array[] = {
+	{"^r?dsk/c([0-9]+)", 1},
+	{"^cfg/c([0-9]+)$", 1},
+	{"^scsi/.+/c([0-9]+)", 1},
+	{NULL}
+};
+
+static char *disk_mid = "disk_mid";
+static char *modname = "disk_link";
+
+int
+minor_init()
+{
+	devfsadm_print(disk_mid,
+	    "%s: minor_init(): Creating disks reserved ID cache\n",
+	    modname);
+	return (devfsadm_reserve_id_cache(disks_re_array, NULL));
+}
+
 static int
 disk_callback_chan(di_minor_t minor, di_node_t node)
 {
@@ -231,6 +250,23 @@
 		(void) sprintf(slice, SLICE_EFI);
 	}
 
+	nflags = 0;
+	if (system_labeled) {
+		nt = di_minor_nodetype(minor);
+		if ((nt != NULL) &&
+		    ((strcmp(nt, DDI_NT_CD) == 0) ||
+		    (strcmp(nt, DDI_NT_CD_CHAN) == 0) ||
+		    (strcmp(nt, DDI_NT_BLOCK_CHAN) == 0))) {
+			nflags = DA_ADD|DA_CD;
+		}
+	}
+
+	if (reserved_links_exist(node, minor, nflags) == DEVFSADM_SUCCESS) {
+		devfsadm_print(disk_mid, "Reserved link exists. Not "
+		    "creating links for slice %s\n", slice);
+		return;
+	}
+
 	if (NULL == (ctrl = diskctrl(node, minor)))
 		return;
 
@@ -261,16 +297,6 @@
 	}
 	(void) strcat(l_path, slice);
 
-	if (system_labeled) {
-		nt = di_minor_nodetype(minor);
-		if ((nt != NULL) &&
-		    ((strcmp(nt, DDI_NT_CD) == 0) ||
-		    (strcmp(nt, DDI_NT_CD_CHAN) == 0) ||
-		    (strcmp(nt, DDI_NT_BLOCK_CHAN) == 0))) {
-			nflags = DA_ADD|DA_CD;
-		}
-	}
-
 	(void) devfsadm_mklink(l_path, node, minor, nflags);
 
 	/* secondary links for removable and hotpluggable devices */
@@ -358,3 +384,209 @@
 
 	return (buf);
 }
+
+typedef struct dvlist {
+	char *dv_link;
+	struct dvlist *dv_next;
+} dvlist_t;
+
+static void
+free_dvlist(dvlist_t **pp)
+{
+	dvlist_t *entry;
+
+	while (*pp) {
+		entry = *pp;
+		*pp = entry->dv_next;
+		assert(entry->dv_link);
+		free(entry->dv_link);
+		free(entry);
+	}
+}
+static int
+dvlink_cb(di_devlink_t devlink, void *arg)
+{
+	char *path;
+	char *can_path;
+	dvlist_t **pp = (dvlist_t **)arg;
+	dvlist_t *entry = NULL;
+
+	entry = calloc(1, sizeof (dvlist_t));
+	if (entry == NULL) {
+		devfsadm_errprint("%s: calloc failed\n", modname);
+		goto error;
+	}
+
+	path = (char *)di_devlink_path(devlink);
+	assert(path);
+	if (path == NULL) {
+		devfsadm_errprint("%s: di_devlink_path() returned NULL\n",
+		    modname);
+		goto error;
+	}
+
+	devfsadm_print(disk_mid, "%s: found link %s in reverse link cache\n",
+	    modname, path);
+
+	/*
+	 * Return linkname in canonical form i.e. without the
+	 * "/dev/" prefix
+	 */
+	can_path = strstr(path, "/dev/");
+	if (can_path == NULL) {
+		devfsadm_errprint("%s: devlink path %s has no /dev/\n",
+		    modname, path);
+		goto error;
+	}
+
+	entry->dv_link = s_strdup(can_path + strlen("/dev/"));
+	entry->dv_next = *pp;
+	*pp = entry;
+
+	return (DI_WALK_CONTINUE);
+
+error:
+	free(entry);
+	free_dvlist(pp);
+	*pp = NULL;
+	return (DI_WALK_TERMINATE);
+}
+
+/*
+ * Returns success only if all goes well. If there is no matching reserved link
+ * or if there is an error, we assume no match. It is better to err on the side
+ * of caution by creating extra links than to miss out creating a required link.
+ */
+static int
+reserved_links_exist(di_node_t node, di_minor_t minor, int nflags)
+{
+	di_devlink_handle_t dvlink_cache = devfsadm_devlink_cache();
+	char phys_path[PATH_MAX];
+	char *minor_path;
+	dvlist_t *head;
+	dvlist_t *entry;
+	char *s;
+	char l[PATH_MAX];
+	int switch_link = 0;
+	struct stat sb;
+	char *mn = di_minor_name(minor);
+
+	if (dvlink_cache == NULL || mn == NULL) {
+		devfsadm_errprint("%s: No minor or devlink cache\n", modname);
+		return (DEVFSADM_FAILURE);
+	}
+
+	if (stat(ENUMERATE_RESERVED, &sb) == -1) {
+		devfsadm_print(disk_mid, "%s: No reserved file: %s. Will "
+		    "not bypass new link creation\n",
+		    modname, ENUMERATE_RESERVED);
+		return (DEVFSADM_FAILURE);
+	}
+
+	minor_path = di_devfs_minor_path(minor);
+	if (minor_path == NULL) {
+		devfsadm_errprint("%s: di_devfs_minor_path failed\n", modname);
+		return (DEVFSADM_FAILURE);
+	}
+
+	(void) strlcpy(phys_path, minor_path, sizeof (phys_path));
+
+	di_devfs_path_free(minor_path);
+
+	head = NULL;
+	(void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE, phys_path,
+	    DI_PRIMARY_LINK, &head, dvlink_cb);
+
+	/*
+	 * We may be switching between EFI label and SMI label in which case
+	 * we only have minors of the other type.
+	 */
+	if (head == NULL && (*mn == *(MN_SMI) ||
+	    (strncmp(mn, MN_EFI, 2) == 0))) {
+		devfsadm_print(disk_mid, "%s: No links for minor %s in /dev. "
+		    "Trying another label\n", modname, mn);
+		s = strrchr(phys_path, ':');
+		if (s == NULL) {
+			devfsadm_errprint("%s: invalid minor path: %s\n",
+			    modname, phys_path);
+			return (DEVFSADM_FAILURE);
+		}
+		(void) snprintf(s+1, sizeof (phys_path) - (s + 1 - phys_path),
+			"%s%s", *mn == *(MN_SMI) ? MN_EFI : MN_SMI,
+			strstr(s, ",raw") ? ",raw" : "");
+		(void) di_devlink_cache_walk(dvlink_cache, DISK_LINK_RE,
+		    phys_path, DI_PRIMARY_LINK, &head, dvlink_cb);
+	}
+
+	if (head == NULL) {
+		devfsadm_print(disk_mid, "%s: minor %s has no links in /dev\n",
+		    modname, phys_path);
+		/* no links on disk */
+		return (DEVFSADM_FAILURE);
+	}
+
+	/*
+	 * It suffices to use 1 link to this minor, since
+	 * we are matching with reserved IDs on the basis of
+	 * the controller number which will be the same for
+	 * all links to this minor.
+	 */
+	if (!devfsadm_is_reserved(disks_re_array, head->dv_link)) {
+		/* not reserved links */
+		devfsadm_print(disk_mid, "%s: devlink %s and its minor "
+		    "are NOT reserved\n", modname, head->dv_link);
+		free_dvlist(&head);
+		return (DEVFSADM_FAILURE);
+	}
+
+	devfsadm_print(disk_mid, "%s: devlink %s and its minor are on "
+	    "reserved list\n", modname, head->dv_link);
+
+	/*
+	 * Switch between SMI and EFI labels if required
+	 */
+	switch_link = 0;
+	if (*mn == *(MN_SMI) || (strncmp(mn, MN_EFI, 2) == 0)) {
+		for (entry = head; entry; entry = entry->dv_next) {
+			s = strrchr(entry->dv_link, '/');
+			assert(s);
+			if (s == NULL) {
+				devfsadm_errprint("%s: disk link %s has no "
+				    "directory\n", modname, entry->dv_link);
+				continue;
+			}
+			if (*mn == *(MN_SMI) && strchr(s, 's') == NULL) {
+				(void) snprintf(l, sizeof (l), "%s%s",
+				    entry->dv_link, SLICE_SMI);
+				switch_link = 1;
+				devfsadm_print(disk_mid, "%s: switching "
+				    "reserved link from EFI to SMI label. "
+				    "New link is %s\n", modname, l);
+			} else if (strncmp(mn, MN_EFI, 2) == 0 &&
+			    (s = strchr(s, 's'))) {
+				*s = '\0';
+				(void) snprintf(l, sizeof (l), "%s",
+				    entry->dv_link);
+				*s = 's';
+				switch_link = 1;
+				devfsadm_print(disk_mid, "%s: switching "
+				    "reserved link from SMI to EFI label. "
+				    "New link is %s\n", modname, l);
+			}
+			if (switch_link) {
+				devfsadm_print(disk_mid, "%s: switching "
+				    "link: deleting %s and creating %s\n",
+				    modname, entry->dv_link, l);
+				devfsadm_rm_link(entry->dv_link);
+				(void) devfsadm_mklink(l, node, minor, nflags);
+			}
+		}
+	}
+	free_dvlist(&head);
+
+	/*
+	 * return SUCCESS to indicate that new links to this minor should not
+	 * be created so that only compatibility links to this minor remain.
+	 */
+	return (DEVFSADM_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/devfsadm/reserved_devnames	Thu Aug 16 14:46:34 2007 -0700
@@ -0,0 +1,26 @@
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# reserved controller IDs
+# do not hand edit
--- a/usr/src/pkgdefs/SUNWcsr/prototype_com	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/pkgdefs/SUNWcsr/prototype_com	Thu Aug 16 14:46:34 2007 -0700
@@ -133,6 +133,8 @@
 e preserve etc/default/utmpd 644 root sys
 e preserve etc/device.tab 444 root root
 d none etc/devices 755 root sys
+d none etc/dev 755 root sys
+e preserve etc/dev/reserved_devnames 644 root sys
 d none etc/dfs 755 root sys
 e preserve etc/dfs/dfstab 644 root sys
 v preserve etc/dfs/sharetab 444 root root
--- a/usr/src/pkgdefs/SUNWnvsata/Makefile	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/pkgdefs/SUNWnvsata/Makefile	Thu Aug 16 14:46:34 2007 -0700
@@ -30,7 +30,7 @@
 
 .KEEP_STATE:
 
-all: $(FILES)
+all: $(FILES) postinstall postremove
 install: all pkg
 
 include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWnvsata/postinstall	Thu Aug 16 14:46:34 2007 -0700
@@ -0,0 +1,1374 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+# ident	"%Z%%M%	%I%	%E% SMI"
+
+ROOT=${PKG_INSTALL_ROOT:-/}
+SYSTEM=${ROOT}/etc/system
+BOOTENVRC=${ROOT}/boot/solaris/bootenv.rc
+PATH_TO_INST=${ROOT}/etc/path_to_inst
+POWER_CONF=${ROOT}/etc/power.conf
+BAK=bak
+TMP=/tmp
+NV_SATA=nv_sata
+NVSATA_TMP=$TMP/${NV_SATA}
+SYSTEM_BAK=${NVSATA_TMP}/system.${BAK}
+BOOTENVRC_BAK=${NVSATA_TMP}/bootenv.rc.${BAK}
+POWER_CONF_BAK=${NVSATA_TMP}/power.conf.${BAK}
+UPGRADE_LOG=${ROOT}/var/sadm/system/logs/upgrade_log
+LIVEUPGRADE_TMP_LOG=$TMP/.luupgrade.pfinstall.log.*
+DEVMAP_TABLE=${TMP}/devmap_table
+DRIVER_ALIASES='"pci10de,37f" "pci10de,55" "pci10de,54"'
+ETC_DEV_DIR=${ROOT}/etc/dev
+ENUMERATE_RESERVED=${ETC_DEV_DIR}/reserved_devnames
+DEVLINK_DB=${ETC_DEV_DIR}/.devlink_db
+
+CP=/usr/bin/cp
+MV=/usr/bin/mv
+LN=/usr/bin/ln
+LS=/usr/bin/ls
+GREP=/usr/bin/grep
+SED=/usr/bin/sed
+CUT=/usr/bin/cut
+CAT=/usr/bin/cat
+NAWK=/usr/bin/nawk
+RM=/usr/bin/rm
+MKDIR=/usr/bin/mkdir
+MOUNT=/usr/sbin/mount
+MODINFO=/usr/sbin/modinfo
+PRTCONF=/etc/prtconf
+PRTCONF_OUT=${NVSATA_TMP}/prtconf_vp.out
+DEV_PROPS=${NVSATA_TMP}/dev_props.out
+
+#
+# get_devinfo_prop(file, prop)
+#
+#   <prtconf.out> file that has output from 'prtconf -vp' command
+#   <prop>  name of the property OR "ALL" for all properties
+#
+# parse the output of prtconf command and print the value of
+# of the specified property or all if <prop> is "ALL" in
+# the format "<name>=<value>".
+#
+get_devinfo_prop()
+{
+    /bin/cat $1 | ${NAWK} -v prop=$2 '
+	BEGIN {
+		if (match(prop,"ALL"))
+			check_all=1;
+		else
+			check_all=0;
+	}
+	{
+		if (!match($1, "name="))
+			next;
+		if (!check_all) {
+			pname = sprintf("name=\047%s\047", prop);
+			if (!match($1, pname))
+				next;
+		}
+		gsub("\047", "", $1);
+		name = substr($1, index($1,"=")+1);
+		if (match($2, "type=boolean")) {
+			value=1;
+		} else if (match($2, "type=int") || match($2, "type=byte")) {
+			getline;
+			value = ""
+			cnt = split(substr($0,index($0, "=")+1), words, ".");
+			for (i = 1; i <= cnt; i++) {
+				w = "0x" skipzeros(words[i]);
+				if (i > 1)
+					value = value " ";
+				value = value w;
+			}
+		} else {
+			getline;
+			value = substr($0,index($0, "=")+1);
+		} 
+		if (!check_all) {
+			print value;
+			exit (0);
+		}
+		print name "=" value;
+	}
+	# skp the leading '0' characters in the string
+	function skipzeros (str, out, n, i)
+	{
+		n = length(str);
+		for (i = 1; i <= n; i++) {
+			if (!match(substr(str, i, 1), "0")) {
+				break;
+			}
+		}
+		if (i > n)
+			out=substr(str,n);
+		else
+			out=substr(str,i);
+		return (out);
+	}'
+}
+
+#
+# Function get_properties() gets the following properties for the
+# device (i.e /devices path without '/devices' prefix) passed as
+# an argument:
+#
+#	vendor-id
+#	device-id
+#	subsystem-vendor-id
+#	subsystem-id
+#
+# Note: it saves the pathname and the properties in ${DEV_PROPS} file 
+# to optimize subsequent mapping calls for the same path name.
+#
+# e.g: get_properties "/pci@0,0/pci-ide@1f,2"
+#
+get_properties()
+{
+    if [ -f ${DEV_PROPS} ]
+    then
+	mapped_path=`${GREP} $1 ${DEV_PROPS}`
+	if [ $? -eq 0 ]
+	then
+		vendor_id=`echo ${mapped_path} | ${CUT} -d ' ' -f2` 
+		device_id=`echo ${mapped_path} | ${CUT} -d ' ' -f3` 
+		subsystem_vendor_id=`echo ${mapped_path} | ${CUT} -d ' ' -f4` 
+		subsystem_id=`echo ${mapped_path} | ${CUT} -d ' ' -f5` 
+		return;
+	fi
+    fi
+
+    path=`echo $1|${GREP} -v storage`
+    ${PRTCONF} -vp "/devices"$path >${PRTCONF_OUT}
+    if [ $? -eq 0 ]
+    then
+	vendor_id=`get_devinfo_prop ${PRTCONF_OUT} "vendor-id" | ${SED} "s/^0x//"`
+	device_id=`get_devinfo_prop ${PRTCONF_OUT} "device-id" | ${SED} "s/^0x//"`
+	subsystem_vendor_id=`get_devinfo_prop ${PRTCONF_OUT} "subsystem-vendor-id" | ${SED} "s/^0x//"`
+	subsystem_id=`get_devinfo_prop ${PRTCONF_OUT} "subsystem-id" | ${SED} "s/^0x//"`
+	rm -f ${PRTCONF_OUT}
+	echo $1 "$vendor_id" "$device_id" "$subsystem_vendor_id" "$subsystem_id" >>${DEV_PROPS}
+    else
+	vendor_id=""
+	device_id=""
+	subsystem_vendor_id=""
+	subsystem_id=""
+	echo $1 "$vendor_id" >>${DEV_PROPS}
+    fi
+}
+
+#
+# Function process_bootenvrc_for_install() converts bootpath in ata style
+# to nv_sata style in the file /boot/solaris/bootenv.rc for normal upgrade.
+#
+process_bootenvrc_for_install() {
+
+	# Check to see if bootenv.rc file has ata disk entries.
+	${GREP} "cmdk@" ${BOOTENVRC} > /dev/null
+	if [ $? -eq 1 ]
+	then
+		# There is no ata disk entry. No mapping is needed.
+		return 0
+	fi
+
+	# Backup the bootenv.rc file.
+	$CP ${BOOTENVRC} ${BOOTENVRC_BAK}
+	if [ $? -ne 0 ]
+	then
+		# Can't backup bootenv.rc file.
+		rem_drv -b ${ROOT} nv_sata
+		${RM} -rf ${NVSATA_TMP}
+		exit 1
+	fi
+ 
+	${GREP} "cmdk@" ${BOOTENVRC} | while read bootpath_line
+	do
+		old_path=`echo ${bootpath_line} | ${CUT} -d' ' -f3`
+		devname=`echo ${old_path} | $CUT -d/ -f4- | $SED "{
+			s/ide@/disk@/
+			s/\/cmdk@.//
+			}"`
+		$LS -l /dev/dsk | $GREP ${devname} | while read line
+		do
+			new_path=`echo $line | $NAWK '{print $11}'`
+			arg="/`echo ${new_path} | ${CUT} -d/ -f4,5`"
+			get_properties ${arg}
+	
+			for pair in ${DRIVER_ALIASES}
+			do
+				VENDOR_ID=`echo ${pair} | $SED "s/\"//g" \
+					| $CUT -d, -f1`
+				DEVICE_ID=`echo ${pair} | $SED "s/\"//g" \
+					| $CUT -d, -f2`
+				# Check to see if it is a nv_sata device.
+				if [ "pci${vendor_id}" = ${VENDOR_ID} -a \
+					${device_id} = ${DEVICE_ID} ]
+				then	
+					if [ "X${subsystem_vendor_id}" = "X" ]
+					then
+                       				ids="pci${vendor_id},${device_id}"
+					else
+                       				ids="pci${subsystem_vendor_id},${subsystem_id}"
+					fi
+					mapped_path=`echo ${old_path} | ${SED} "{
+						s/pci-ide/${ids}/
+						s/ide/disk/
+						s/\/cmdk@.//
+						}"`
+					new_path="/`echo ${new_path} | $CUT -d/ -f4-`"
+					if [ ${new_path} = ${mapped_path} ]
+					then
+						if [ ! -f ${NVSATA_TMP}/bootenv.rc ]
+						then
+							$GREP -v "cmdk@" ${BOOTENVRC} > \
+								${NVSATA_TMP}/bootenv.rc	
+						fi
+						echo "setprop bootpath ${new_path}" \
+							>> ${NVSATA_TMP}/bootenv.rc
+						break	
+					fi	
+				fi	
+			done
+		done
+	done
+	# Commit the changes
+	if [ -f ${NVSATA_TMP}/bootenv.rc ]
+	then
+		${CP} ${NVSATA_TMP}/bootenv.rc ${BOOTENVRC}
+		if [ $? -ne 0 ]
+		then
+			# Failed to modify bootenv.rc file.
+			rem_drv -b ${ROOT} nv_sata
+			${RM} -rf ${NVSATA_TMP}
+			exit 1
+		fi
+	fi
+}
+
+#
+# Function process_bootenvrc_for_lu() converts bootpath in ata style
+# to nv_sata style in the file /boot/solaris/bootenv.rc for live upgrade.
+#
+process_bootenvrc_for_lu() {
+
+        # Check to see if bootenv.rc has ata disk entries.
+
+        ${GREP} "cmdk@" ${BOOTENVRC} > /dev/null
+        if [ $? -eq 1 ]
+        then
+                # There is no ata disk. No mapping is needed.
+                return 0
+        fi
+
+	# Backup the bootenv.rc file.
+	$CP ${BOOTENVRC} ${BOOTENVRC_BAK}
+	if [ $? -ne 0 ]
+	then
+		# Can't backup bootenv.rc file.
+		rem_drv -b ${ROOT} nv_sata
+		${RM} -rf ${NVSATA_TMP}
+		exit 1
+	fi
+
+	${GREP} "cmdk@" ${BOOTENVRC} | while read bootpath_line
+	do
+	    bootpath=`echo ${bootpath_line} | ${CUT} -d' ' -f3`
+	    if [ "X${no_nvsata_in_CBE}" != "X0" ]
+	    then 
+        	arg=`echo ${bootpath} | ${CUT} -d/ -f1-3`
+        	get_properties ${arg}
+
+        	# Check if boot device is a supported nv_sata device.
+        	for pair in ${DRIVER_ALIASES}
+        	do
+                	dr_vendor_id=`echo $pair | $SED "s/\"//g" | $CUT -d, -f1`
+                	dr_device_id=`echo $pair | $SED "s/\"//g" | $CUT -d, -f2`
+                	if [ "pci${vendor_id}" = ${dr_vendor_id} -a ${device_id} = \
+				${dr_device_id} ]
+                	then
+				if [ "X${subsystem_vendor_id}" = "X" ]
+				then
+                       			ids="pci${vendor_id},${device_id}"
+				else
+                       			ids="pci${subsystem_vendor_id},${subsystem_id}"
+				fi
+				if [ ! -f ${NVSATA_TMP}/bootenv.rc ]
+				then
+					$GREP -v "cmdk@" ${BOOTENVRC} > \
+						${NVSATA_TMP}/bootenv.rc
+				fi
+				mapped_bootpath_line=`echo ${bootpath_line} | ${SED} "{
+                                	s/pci-ide/${ids}/
+                                	s/ide/disk/
+                                	s/\/cmdk@.//
+                                	}"`
+
+				echo ${mapped_bootpath_line} >> ${NVSATA_TMP}/bootenv.rc
+				break
+                	fi
+        	done
+	    else
+		is_mapped="no"
+		devname=`echo ${bootpath} | $CUT -d/ -f4- | $SED "{
+			s/ide@/disk@/
+			s/\/cmdk@.//
+			}"`
+		for new_path in `$LS -l "/dev/dsk" | $GREP ${devname} |$NAWK '{print $11}'`
+		do
+			arg2="/`echo ${new_path} | ${CUT} -d/ -f4,5`"
+			get_properties ${arg2}
+			for pair in ${DRIVER_ALIASES}
+			do
+				VENDOR_ID=`echo ${pair} | $SED "s/\"//g" \
+					| $CUT -d, -f1`
+				DEVICE_ID=`echo ${pair} | $SED "s/\"//g" \
+					| $CUT -d, -f2`
+
+				# Check to see if it is a nv_sata device.
+				if [ "pci${vendor_id}" = ${VENDOR_ID} -a \
+					${device_id} = ${DEVICE_ID} ]
+				then
+					if [ "X${subsystem_vendor_id}" = "X" ]
+					then
+						ids3="pci${vendor_id},${device_id}"
+					else
+						ids3="pci${subsystem_vendor_id},${subsystem_id}"
+					fi
+					mapped_bootpath_line=`echo ${bootpath_line} | ${SED} "{
+						s/pci-ide/${ids3}/
+						s/ide/disk/
+						s/\/cmdk@.//
+						 }"`
+					mapped_bootpath=`echo ${mapped_bootpath_line} \
+						| ${CUT} -d' ' -f3`
+					$LS -l "/dev/dsk" | $GREP ${mapped_bootpath} >/dev/null
+					if [ $? -eq 0 ]
+					then
+						if [ ! -f ${NVSATA_TMP}/bootenv.rc ]
+						then
+							$GREP -v "cmdk@" ${BOOTENVRC} > \
+								${NVSATA_TMP}/bootenv.rc
+						fi
+						echo ${mapped_bootpath_line} >> \
+							${NVSATA_TMP}/bootenv.rc	
+						is_mapped="yes"
+						break
+					fi
+				fi
+			done
+			if [ "${is_mapped}" = "yes" ]
+			then 
+				break
+			fi
+		done
+	    fi
+	done
+	# Commit the changes
+	if [ -f ${NVSATA_TMP}/bootenv.rc ]
+	then
+		${CP} ${NVSATA_TMP}/bootenv.rc ${BOOTENVRC}
+		if [ $? -ne 0 ]
+		then
+			# Failed to modify bootenv.rc file.
+			rem_drv -b ${ROOT} nv_sata
+			${RM} -rf ${NVSATA_TMP}
+			exit 1
+		fi
+	fi
+}
+
+#
+# Function process_system_for_lu() converts rootdev in /etc/system from
+# old ata fromat to new nv_sata format during live upgrade.
+#
+process_system_for_lu() {
+
+        # Check to see if system has ata disk as a root device.
+        rootdev_line=`${GREP} -v "*" $SYSTEM | ${GREP} "rootdev:/"| ${GREP} "cmdk@"`
+        if [ $? -eq 1 ]
+        then
+                # roodev entry does not exist, or root disk is not ata.
+                return 0
+        fi
+
+	${CP} ${SYSTEM} ${SYSTEM_BAK}
+	if [ $? -ne 0 ]
+	then
+		# Can't backup system file."
+		$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+		rem_drv -b ${ROOT} nv_sata
+		${RM} -rf ${NVSATA_TMP}
+		exit 1
+	fi
+
+        rootdev=`echo ${rootdev_line} | ${CUT} -d: -f2-`
+	if [ "X${no_nvsata_in_CBE}" != "X0" ]
+	then
+        	ar=`echo ${rootdev} | ${CUT} -d/ -f1-3`
+        	get_properties ${ar}
+
+        	# Check if boot device is a supported nv_sata device.
+        	for pair in ${DRIVER_ALIASES}
+        	do
+			dr_vendor_id=`echo $pair | $SED "s/\"//g" | $CUT -d, -f1`
+                	dr_device_id=`echo $pair | $SED "s/\"//g" | $CUT -d, -f2`
+                	if [ "pci${vendor_id}" = ${dr_vendor_id} -a \
+				${device_id} = ${dr_device_id} ]
+                	then
+
+				if [ "X${subsystem_vendor_id}" = "X" ]
+				then
+                       			ids="pci${vendor_id},${device_id}"
+				else
+                       			ids="pci${subsystem_vendor_id},${subsystem_id}"
+				fi
+                        	$SED "{
+                                	s/pci-ide/$ids/
+                                	s/ide/disk/
+                                	s/\/cmdk@.//
+                                	}" ${SYSTEM} > "${NVSATA_TMP}/system"
+
+                        	break
+			fi
+        	done
+	else
+		devname=`echo ${rootdev} | $CUT -d/ -f4- | $SED "{
+			s/ide@/disk@/
+			s/\/cmdk@.//
+			}"`
+		is_mapped="no"
+		for dev_path in `$LS -l "/dev/dsk" | $GREP ${devname} |$NAWK '{print $11}'`
+		do
+			ar="/`echo ${dev_path} | ${CUT} -d/ -f4,5`"
+			get_properties ${ar}
+			for pair in ${DRIVER_ALIASES}
+			do
+				VENDOR_ID=`echo ${pair} | $SED "s/\"//g" \
+					| $CUT -d, -f1`
+				DEVICE_ID=`echo ${pair} | $SED "s/\"//g" \
+					| $CUT -d, -f2`
+
+				# Check to see if it is a nv_sata device.
+				if [ "pci${vendor_id}" = ${VENDOR_ID} -a \
+					${device_id} = ${DEVICE_ID} ]
+				then
+					if [ "X${subsystem_vendor_id}" = "X" ]
+					then
+						ids="pci${vendor_id},${device_id}"
+					else
+						ids="pci${subsystem_vendor_id},${subsystem_id}"
+					fi
+					
+					mapped_rootdev=`echo ${rootdev} | ${SED} "{
+						s/pci-ide/${ids}/
+						s/ide/disk/
+						s/\/cmdk@.//
+						}"`		
+					$LS -l "/dev/dsk" | $GREP ${mapped_rootdev} >/dev/null
+					if [ $? -eq 0 ]
+					then
+						$SED "{
+							s/pci-ide/$ids/
+							s/ide/disk/
+							s/\/cmdk@.//
+							}" ${SYSTEM} > "${NVSATA_TMP}/system"
+						is_mapped="yes"	
+						break
+					fi
+				fi
+			done
+			if [ "${is_mapped}" = "yes" ]
+			then
+				break
+			fi
+		done
+	fi
+	
+	if [ -f ${NVSATA_TMP}/system ]
+	then
+		${CP} ${NVSATA_TMP}/system ${SYSTEM}
+		if [ $? -ne 0 ]
+		then
+			# Failed to modify system file.
+			$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+			rem_drv -b ${ROOT} nv_sata
+			${RM} -rf ${NVSATA_TMP}
+			exit 1
+		fi
+	fi
+}
+
+#
+# Function process_system_for_install() converts rootdev in /etc/system from 
+# old ata format to new nv_sata format during the upgrade installation.
+#
+process_system_for_install() {
+
+        # Check to see if system has ata disk as a root device.
+	rootdev_line=`${GREP} -v "*" $SYSTEM | ${GREP} "rootdev:/"| ${GREP} "cmdk@"`
+        if [ $? -eq 1 ]
+        then
+		# roodev entry does not exist, or root disk is not ata.
+                return 0
+        fi
+
+	${CP} ${SYSTEM} ${SYSTEM_BAK}
+	if [ $? -ne 0 ]
+	then
+		# Can't backup system file."
+		$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+		rem_drv -b ${ROOT} nv_sata
+		${RM} -rf ${NVSATA_TMP}
+		exit 1
+	fi
+
+	rootdev=`echo ${rootdev_line} | ${CUT} -d: -f2-`
+	devname=`echo ${rootdev} | $CUT -d/ -f4- | $SED "{
+		s/ide@/disk@/
+		s/\/cmdk@.//
+		}"`
+	is_mapped="no"
+	for dev_path in `$LS -l "/dev/dsk" | $GREP ${devname} |$NAWK '{print $11}'`
+	do
+		arg="/`echo ${dev_path} | ${CUT} -d/ -f4,5`"
+		get_properties ${arg}
+		for pair in ${DRIVER_ALIASES}
+		do
+			VENDOR_ID=`echo ${pair} | $SED "s/\"//g" \
+				| $CUT -d, -f1`
+			DEVICE_ID=`echo ${pair} | $SED "s/\"//g" \
+				| $CUT -d, -f2`
+
+			# Check to see if it is a nv_sata device.
+			if [ "pci${vendor_id}" = ${VENDOR_ID} -a \
+				${device_id} = ${DEVICE_ID} ]
+			then
+				if [ "X${subsystem_vendor_id}" = "X" ]
+				then
+					ids="pci${vendor_id},${device_id}"
+				else
+					ids="pci${subsystem_vendor_id},${subsystem_id}"
+				fi
+
+				mapped_rootdev=`echo ${rootdev} | ${SED} "{
+					s/pci-ide/${ids}/
+					s/ide/disk/
+					s/\/cmdk@.//
+					}"`
+				$LS -l "/dev/dsk" | $GREP ${mapped_rootdev} >/dev/null
+				if [ $? -eq 0 ]
+				then
+					$SED "{
+						s/pci-ide/$ids/
+						s/ide/disk/
+						s/\/cmdk@.//
+						}" ${SYSTEM} > "${NVSATA_TMP}/system"
+						is_mapped="yes"
+						break
+				fi
+			fi
+		done
+		if [ "${is_mapped}" = "yes" ]
+		then
+			break
+		fi
+	done
+
+        if [ -f ${NVSATA_TMP}/system ]
+        then
+                ${CP} ${NVSATA_TMP}/system ${SYSTEM}
+                if [ $? -ne 0 ]
+                then
+                        # Failed to modify system file.
+                        $CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+                        rem_drv -b ${ROOT} nv_sata
+                        ${RM} -rf ${NVSATA_TMP}
+                        exit 1
+                fi
+        fi			
+}
+
+#
+# Function process_power_conf_for_lu converts ata device path to nv_sata 
+# device path if any for live upgrade.
+#
+process_power_conf_for_lu() {
+
+	$GREP -v "#" ${POWER_CONF} | $GREP "cmdk@">/dev/null
+	if [ $? -ne 0 ]
+	then
+		# No ata device entry in power.conf. No conversion is needed.
+		return 0
+	fi
+
+	$CP ${POWER_CONF} ${POWER_CONF_BAK}
+	if [ $? -ne 0 ]
+	then
+		# Failed to back up the power.conf file
+		$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+		$CP ${SYSTEM_BAK} ${SYSTEM}
+		rem_drv -b ${ROOT} nv_sata
+		$RM -rf ${NVSATA_TMP}
+		exit 1
+	fi
+
+	$GREP "#" ${POWER_CONF} > ${NVSATA_TMP}/power.conf
+	$GREP -v "#" ${POWER_CONF} | $GREP -v "cmdk@" \
+		>> ${NVSATA_TMP}/power.conf
+
+	if [ "X${no_nvsata_in_CBE}" != "X0" ]
+	then
+		$GREP -v "#" ${POWER_CONF} | $GREP "cmdk@" \
+			${POWER_CONF} | while read l
+		do
+			is_mapped="no"
+			new_entry=""
+			for i in $l
+			do
+				tp=$i
+				echo $i | $GREP "cmdk@" >/dev/dull
+				if [ $? -eq 0 ]
+				then
+			    		a="/`echo $i | ${CUT} -d/ -f2,3`"
+			    		get_properties $a
+			   
+			    		for pair in ${DRIVER_ALIASES}
+			    		do
+						dr_vendor_id=`echo $pair | $SED "s/\"//g" \
+				    			| $CUT -d, -f1`	
+						dr_device_id=`echo $pair | $SED "s/\"//g" \
+				    			| $CUT -d, -f2`
+						if [ "pci${vendor_id}" = ${dr_vendor_id} -a \
+				    			${device_id} = ${dr_device_id} ]
+						then
+				    			if [ "X${subsystem_vendor_id}" = "X" ]
+				    			then
+                       						is="pci${vendor_id},${device_id}"
+				    			else
+                       						is="pci${subsystem_vendor_id},${subsystem_id}"
+				    			fi
+				    			tp=`echo $i | $SED "{
+								s/pci-ide/$is/
+								s/ide/disk/
+								s/\/cmdk@.//
+								}"`
+							if [ "${is_mapped}" = "no" ]
+							then
+								is_mapped="yes"
+							fi
+				    			break
+						fi
+			    		done
+				fi
+				new_entry="${new_entry}$tp "
+			done		
+			if [ "${is_mapped}" = "yes" ]
+			then
+				echo ${new_entry} >> ${NVSATA_TMP}/mapped_power.conf
+			else
+				echo ${new_entry} >> ${NVSATA_TMP}/power.conf
+			fi
+		done 
+	else
+        	$GREP -v "#" ${POWER_CONF} | $GREP "cmdk@" ${POWER_CONF} \
+			| while read l
+        	do
+			is_mapped="no"
+                	new_entry=""
+                	for i in $l
+                	do
+                        	tp=$i
+                        	echo $i | $GREP "cmdk@" >/dev/dull
+                        	if [ $? -eq 0 ]
+                        	then
+                                	dn=`echo $i | $CUT -d/ -f4- | $SED "{
+                                        	s/ide@/disk@/
+                                        	s/\/cmdk@.//
+                                        	}"`
+                                	for x in `$LS -l /dev/dsk | $GREP $dn \
+                                        	| $NAWK '{print $11}'`
+                                	do
+                                        	np=""
+                                        	a="/`echo ${x} | ${CUT} -d/ -f4,5`"
+                                        	get_properties $a
+                                        	for pair in ${DRIVER_ALIASES}
+                                        	do
+                                            		VENDOR_ID=`echo ${pair} | $SED "s/\"//g" \
+                                                		| $CUT -d, -f1`
+                                            		DEVICE_ID=`echo ${pair} | $SED "s/\"//g" \
+                                                		| $CUT -d, -f2`
+                                            		if [ "pci${vendor_id}" = ${VENDOR_ID} -a \
+                                                		${device_id} = ${DEVICE_ID} ]
+                                            		then
+                                                		if [ "X${subsystem_vendor_id}" = "X" ]
+                                                		then
+                                                    			is="pci${vendor_id},${device_id}"
+                                                		else
+                                                    			is="pci${subsystem_vendor_id},${subsystem_id}"
+                                                		fi
+                                                		mapped_path=`echo $i | ${SED} "{
+                                                        		s/pci-ide/${is}/
+                                                        		s/ide/disk/
+                                                        		s/\/cmdk@.//
+                                                        		}"`
+                                                		np="/`echo ${x} | $CUT -d/ -f4- \
+                                                        		| $CUT -d: -f1`"
+                                                		if [ ${np} = ${mapped_path} ]
+                                                		then
+                                                        		tp=${mapped_path}
+									if [ "${is_mapped}" = "no" ]
+									then
+										is_mapped="yes"
+									fi	
+                                                        		break
+								fi
+                                            		fi
+                                        	done
+
+                                        	if [ "${is_mapped}" = "yes" ]
+                                        	then
+                                                	break
+                                        	fi
+                                	done
+                        	fi
+                        	new_entry="${new_entry}$tp "
+                	done
+			if [ "${is_mapped}" = "yes" ]
+			then
+                		echo ${new_entry} >> ${NVSATA_TMP}/mapped_power.conf
+			else
+				echo ${new_entry} >> ${NVSATA_TMP}/power.conf
+			fi	
+        	done
+	fi
+
+	if [ -f ${NVSATA_TMP}/mapped_power.conf ]
+	then
+		$CAT ${NVSATA_TMP}/mapped_power.conf >> ${NVSATA_TMP}/power.conf
+		$CP ${NVSATA_TMP}/power.conf ${POWER_CONF}
+		if [ $? -ne 0 ]
+		then
+			$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+			$CP ${SYSTEM_BAK} ${SYSTEM}
+			rem_drv -b ${ROOT} nv_sata
+			$RM -rf ${NVSATA_TMP}
+			exit 1
+		fi
+	fi
+}
+
+#
+# Function process_power_conf_for_install converts ata device path to nv_sata 
+# device path if any for normal upgrade installation.
+#
+process_power_conf_for_install() {
+
+	$GREP -v "#" ${POWER_CONF} | $GREP "cmdk@" >/dev/null
+        if [ $? -ne 0 ]
+        then
+                # No ata device entry in power.conf. No conversion is needed.
+                return 0
+        fi
+
+        $CP ${POWER_CONF} ${POWER_CONF_BAK}
+        if [ $? -ne 0 ]
+        then
+		# Failed to back up the power.conf file.
+		$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+		$CP ${SYSTEM_BAK} ${SYSTEM}
+		rem_drv -b ${ROOT} nv_sata
+        	$RM -rf ${NVSATA_TMP}
+                exit 1
+        fi
+
+	$GREP "#" ${POWER_CONF} > ${NVSATA_TMP}/power.conf
+	$GREP -v "#" ${POWER_CONF} | $GREP -v "cmdk@" \
+		>> ${NVSATA_TMP}/power.conf
+
+	$GREP -v "#" ${POWER_CONF} | $GREP "cmdk@" ${POWER_CONF} | while read l
+        do
+		is_mapped="no"
+		new_entry=""
+		for i in $l
+		do
+			tp=$i
+			echo $i | $GREP "cmdk@" >/dev/dull
+			if [ $? -eq 0 ]
+			then
+				dn=`echo $i | $CUT -d/ -f4- | $SED "{
+					s/ide@/disk@/
+					s/\/cmdk@.//
+					}"`
+				for x in `$LS -l /dev/dsk | $GREP $dn \
+					| $NAWK '{print $11}'`
+				do
+					np=""
+					a="/`echo ${x} | ${CUT} -d/ -f4,5`"
+					get_properties $a
+
+					for pair in ${DRIVER_ALIASES}
+					do
+					    VENDOR_ID=`echo ${pair} | $SED "s/\"//g" \
+						| $CUT -d, -f1`
+					    DEVICE_ID=`echo ${pair} | $SED "s/\"//g" \
+						| $CUT -d, -f2`
+					    if [ "pci${vendor_id}" = ${VENDOR_ID} -a \
+						${device_id} = ${DEVICE_ID} ]
+					    then
+				    		if [ "X${subsystem_vendor_id}" = "X" ]
+				    		then
+                       				    is="pci${vendor_id},${device_id}"
+				    		else
+                       				    is="pci${subsystem_vendor_id},${subsystem_id}"
+				    		fi
+						mapped_path=`echo $i | ${SED} "{
+							s/pci-ide/${is}/
+							s/ide/disk/
+							s/\/cmdk@.//
+							}"`
+						np="/`echo ${x} | $CUT -d/ -f4- \
+							| $CUT -d: -f1`"
+						if [ ${np} = ${mapped_path} ]
+						then
+							tp=${mapped_path}	
+							if [ "${is_mapped}" = "no" ]
+							then
+								is_mapped="yes"
+							fi
+							break
+						fi
+					    fi
+					done
+					
+					if [ "${is_mapped}" = "yes" ]
+					then
+						break
+					fi
+				done
+			fi
+			new_entry="${new_entry}$tp "
+                done
+		if [ "${is_mapped}" = "yes" ]
+		then
+			echo ${new_entry} >> ${NVSATA_TMP}/mapped_power.conf
+		else
+			echo ${new_entry} >> ${NVSATA_TMP}/power.conf
+		fi
+        done
+
+	if [ -f ${NVSATA_TMP}/mapped_power.conf ]
+	then
+		$CAT ${NVSATA_TMP}/mapped_power.conf >> ${NVSATA_TMP}/power.conf
+        	$CP ${NVSATA_TMP}/power.conf ${POWER_CONF}
+        	if [ $? -ne 0 ]
+        	then
+			$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+			$CP ${SYSTEM_BAK} ${SYSTEM}
+			rem_drv -b ${ROOT} nv_sata
+                	$RM -rf ${NVSATA_TMP}
+                	exit 1
+        	fi
+	fi
+}
+
+#
+# Function process_devlink_for_lu() links all old ata logical device names
+# to new physical device paths on upgraded BE.
+#
+process_devlink_for_lu() {
+
+        $LS -l "${ROOT}/dev/dsk" | $GREP "cmdk@" | while read line1
+        do
+                logical_disk=`echo $line1 | $NAWK '{print $9}'`
+                old_path=`echo $line1 | $NAWK '{print $11}'`
+		mapped_new_path=""
+		if [ "X${no_nvsata_in_CBE}" != "X0" ]
+		then
+                	arg2="/`echo ${old_path} | ${CUT} -d/ -f4,5`"
+                	get_properties ${arg2}
+
+                	for pair in ${DRIVER_ALIASES}
+                	do
+                        	VENDOR_ID=`echo ${pair} | $SED "s/\"//g" \
+                                	| $CUT -d, -f1`
+                        	DEVICE_ID=`echo ${pair} | $SED "s/\"//g" \
+                                	| $CUT -d, -f2`
+                        	# Check to see if it is a nv_sata device.
+                        	if [ "pci${vendor_id}" = ${VENDOR_ID} -a \
+					${device_id} = ${DEVICE_ID} ]
+                        	then
+					if [ "X${subsystem_vendor_id}" = "X" ]
+					then
+                        	    	    ids3="pci${vendor_id},${device_id}"
+					else
+                        	    	    ids3="pci${subsystem_vendor_id},${subsystem_id}"
+					fi
+					mapped_new_path=`echo ${old_path} | ${SED} "{
+						s/pci-ide/${ids3}/
+						s/ide/disk/
+						s/\/cmdk@.//
+						 }"`
+					break
+				fi
+			done
+		else
+			devname=`echo ${old_path} | $CUT -d/ -f6- | $SED "{
+				s/ide@/disk@/
+				s/\/cmdk@.//
+				}"`
+			is_mapped="no"
+			for new_path in `$LS -l "/dev/dsk" | $GREP ${devname} | $NAWK '{print $11}'`
+			do
+				arg2="/`echo ${new_path} | ${CUT} -d/ -f4,5`"
+				get_properties ${arg2}
+				for pair in ${DRIVER_ALIASES}
+				do
+					VENDOR_ID=`echo ${pair} | $SED "s/\"//g" \
+						| $CUT -d, -f1`
+					DEVICE_ID=`echo ${pair} | $SED "s/\"//g" \
+						| $CUT -d, -f2`
+					
+					# Check to see if it is a nv_sata device.
+					if [ "pci${vendor_id}" = ${VENDOR_ID} -a \
+						${device_id} = ${DEVICE_ID} ]
+					then
+						if [ "X${subsystem_vendor_id}" = "X" ]
+						then
+						    ids3="pci${vendor_id},${device_id}"
+						else
+						    ids3="pci${subsystem_vendor_id},${subsystem_id}"
+						fi
+						mapped_new_path=`echo ${old_path} | ${SED} "{
+							s/pci-ide/${ids3}/
+							s/ide/disk/
+							s/\/cmdk@.//
+							}"`
+						if [ ${new_path} = ${mapped_new_path} ]
+						then
+							is_mapped="yes"
+							break
+						fi
+					fi
+				done
+				if [ "${is_mapped}" = "yes" ]
+				then
+					break
+				fi
+			done
+		fi
+						
+		if [ "X${mapped_new_path}" != "X" ]
+		then
+			cd ${ROOT}/dev/dsk
+			${MV} ${logical_disk} ${logical_disk}.${BAK}
+			if [ $? -ne 0 ]
+			then
+				# Can't back up old link.
+				$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+				$CP ${SYSTEM_BAK} ${SYSTEM}
+				$CP ${POWER_CONF_BAK} ${POWER_CONF}
+				rem_drv -b ${ROOT} nv_sata
+                       		${RM} -rf ${NVSATA_TMP}
+                       		exit 1
+                        fi
+
+			${LN} -s ${mapped_new_path} ${logical_disk}
+			if [ $? -ne 0 ]
+			then
+				# Can't create symbolic link
+				${MV} ${logical_disk}.${BAK} ${logical_disk}
+                               	$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+				$CP ${SYSTEM_BAK} ${SYSTEM}
+				$CP ${POWER_CONF_BAK} ${POWER_CONF}
+				rem_drv -b ${ROOT} nv_sata
+                                ${RM} -rf ${NVSATA_TMP}
+                               	exit 1
+			fi
+				
+			c_number="dsk/`echo ${logical_disk} | $CUT -d'd' -f1`"
+			$GREP ${c_number} ${ENUMERATE_RESERVED} >/dev/null
+			if [ $? -ne 0 ]
+			then
+				echo ${c_number} >> ${ENUMERATE_RESERVED}
+			fi
+
+			cd ../rdsk
+			mapped_new_path="${mapped_new_path},raw"
+			${MV} ${logical_disk} ${logical_disk}.${BAK}
+			if [ $? -ne 0 ]
+			then
+                              	# Can't back up old link.
+                               	${RM} ../dsk/${logical_disk}
+                               	${MV} ../dsk/${logical_disk}.${BAK} \
+					../dsk/${logical_disk}
+                               	$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+				$CP ${SYSTEM_BAK} ${SYSTEM}
+				$CP ${POWER_CONF_BAK} ${POWER_CONF}
+				rem_drv -b ${ROOT} nv_sata
+                               	${RM} -rf ${NVSATA_TMP}
+                               	exit 1
+			fi
+
+			${LN} -s ${mapped_new_path} ${logical_disk}
+			if [ $? -ne 0 ]
+			then
+				# Can't create symbolic link.
+                               	${MV} ${logical_disk}.${BAK} ${logical_disk}
+                               	${RM} ../dsk/${logical_disk}
+                               	${MV} ../dsk/${logical_disk}.${BAK} \
+                                       	../dsk/${logical_disk}
+                               	$CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+				$CP ${SYSTEM_BAK} ${SYSTEM}
+				$CP ${POWER_CONF_BAK} ${POWER_CONF}
+				rem_drv -b ${ROOT} nv_sata
+                               	${RM} -rf ${NVSATA_TMP}
+                               	exit 1
+			fi
+               	fi
+        done
+
+	$GREP "^dsk" ${ENUMERATE_RESERVED} >/dev/null
+	if [ $? -eq 0 ]
+        then
+                $RM ${DEVLINK_DB}       
+        fi
+}
+
+#
+# Function process_devlink_for_install() links all old ata logical device names
+# to new physical device paths for normal upgrade installation.
+#
+process_devlink_for_install() {
+
+        $LS -l "${ROOT}/dev/dsk" | $GREP "cmdk@" | while read line1
+        do
+                logical_disk=`echo $line1 | $NAWK '{print $9}'`
+                old_path=`echo $line1 | $NAWK '{print $11}'`
+		devname=`echo ${old_path} | $CUT -d/ -f6- | $SED "{
+			s/ide@/disk@/
+			s/\/cmdk@.//
+			 }"`
+                $LS -l "/dev/dsk" | $GREP ${devname} | while read line2
+                do
+                        new_path=`echo $line2 | $NAWK '{print $11}'`
+                        arg2="/`echo ${new_path} | ${CUT} -d/ -f4,5`"
+                        get_properties ${arg2}
+
+			for pair in ${DRIVER_ALIASES}
+                        do
+                                VENDOR_ID=`echo ${pair} | $SED "s/\"//g"\
+                                        | $CUT -d, -f1`
+                                DEVICE_ID=`echo ${pair} | $SED "s/\"//g" \
+                                        | $CUT -d, -f2`
+
+                                # Check to see if it is a nv_sata device.
+                                if [ "pci${vendor_id}" = ${VENDOR_ID} -a \
+                                        ${device_id} = ${DEVICE_ID} ]
+                                then
+					if [ "X${subsystem_vendor_id}" = "X" ]
+					then
+                        		    ids3="pci${vendor_id},${device_id}"
+					else
+                        		    ids3="pci${subsystem_vendor_id},${subsystem_id}"
+					fi
+                                        mapped_new_path=`echo ${old_path} | ${SED} "{
+                                                s/pci-ide/${ids3}/
+                                                s/ide/disk/
+                                                s/\/cmdk@.//
+                                                }"`
+                                        if [ ${new_path} = ${mapped_new_path} ]
+                                        then
+                                                # Found the match.
+                                                cd ${ROOT}/dev/dsk
+                                                ${MV} ${logical_disk} ${logical_disk}.${BAK}
+                                                if [ $? -ne 0 ]
+                                                then
+                                                        # Can't back up old link.
+                                                        $CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+							$CP ${SYSTEM_BAK} ${SYSTEM}
+							$CP ${POWER_CONF_BAK} ${POWER_CONF}
+							rem_drv -b ${ROOT} nv_sata
+                                                        ${RM} -rf ${NVSATA_TMP}
+                                                        exit 1
+                                                fi
+
+                                                ${LN} -s ${mapped_new_path} ${logical_disk}
+                                                if [ $? -ne 0 ]
+                                                then
+                                                        # Can't create symbolic link
+                                                        ${MV} ${logical_disk}.${BAK} ${logical_disk}
+                                                        $CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+							$CP ${SYSTEM_BAK} ${SYSTEM}
+							$CP ${POWER_CONF_BAK} ${POWER_CONF}
+							rem_drv -b ${ROOT} nv_sata
+                                                        ${RM} -rf ${NVSATA_TMP}
+                                                        exit 1
+                                                fi
+						
+						c_number="dsk/`echo ${logical_disk} \
+							| $CUT -d'd' -f1`"
+						$GREP ${c_number} ${ENUMERATE_RESERVED} >/dev/null
+						if [ $? -ne 0 ]
+						then
+							echo ${c_number} >> ${ENUMERATE_RESERVED}
+						fi
+
+                                                cd ../rdsk
+                                                mapped_new_path="${mapped_new_path},raw"
+                                                ${MV} ${logical_disk} ${logical_disk}.${BAK}
+                                                if [ $? -ne 0 ]
+                                                then
+                                                        # Can't back up old link.
+                                                        ${RM} ../dsk/${logical_disk}
+                                                        ${MV} ../dsk/${logical_disk}.${BAK} \
+                                                                ../dsk/${logical_disk}
+                                                        $CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+							$CP ${SYSTEM_BAK} ${SYSTEM}
+							$CP ${POWER_CONF_BAK} ${POWER_CONF}
+							rem_drv -b ${ROOT} nv_sata
+                                                        ${RM} -rf ${NVSATA_TMP}
+                                                        exit 1
+                                                fi
+
+                                                ${LN} -s ${mapped_new_path} ${logical_disk}
+                                                if [ $? -ne 0 ]
+                                                then
+                                                        # Can't create symbolic link.
+                                                        ${MV} ${logical_disk}.${BAK} ${logical_disk}
+                                                        ${RM} ../dsk/${logical_disk}
+                                                        ${MV} ../dsk/${logical_disk}.${BAK} \
+                                                                ../dsk/${logical_disk}
+                                                        $CP ${BOOTENVRC_BAK} ${BOOTENVRC}
+							$CP ${SYSTEM_BAK} ${SYSTEM}
+							$CP ${POWER_CONF_BAK} ${POWER_CONF}
+							rem_drv -b ${ROOT} nv_sata
+                                                        ${RM} -rf ${NVSATA_TMP}
+                                                        exit 1
+                                                fi
+                                        fi
+                                fi
+                        done
+                done
+        done
+	
+	$GREP "^dsk" ${ENUMERATE_RESERVED} >/dev/null
+	if [ $? -eq 0 ]
+	then
+		$RM ${DEVLINK_DB}	
+	fi
+}
+
+clean_up() {
+
+        ${RM} -rf ${NVSATA_TMP}
+        $LS -l "${ROOT}/dev/dsk" | $GREP "disk@" | while read line
+        do
+                logical_disk=`echo $line | $NAWK '{print $9}'`
+                cd ${ROOT}/dev/dsk
+                ${RM} -f ${logical_disk}.${BAK}
+
+                cd ../rdsk
+                ${RM} -f ${logical_disk}.${BAK}
+        done
+}
+
+# Function: check_add_drv()
+#
+# This function will check if the module has an entry in etc/name_to_major
+# If not simply calls add_drv with the arguments given. If there is
+# such an entry in name_to_major file, it adds entries in driver_aliases
+# driver_classes and minor_perm if necessary.
+# The syntax of this function is the same as add_drv. 
+
+check_add_drv()
+{
+	if [ "$BASEDIR" = "" ]
+	then
+		BASEDIR=/  
+	fi
+	alias=""
+	class=""
+	ADD_ALIAS=0
+	ADD_CLASS=0
+	ADD_MINOR=0
+	OPTIND=1
+	IS_NET_DRIVER=0
+
+	cmd="add_drv"
+
+	NO_CMD=
+	while getopts i:b:m:c:N  opt
+	do
+		case $opt in
+			N )	NO_CMD=1;;
+			i )	ADD_ALIAS=1	
+				alias=$OPTARG
+				cmd=$cmd" -i '$alias'"
+				;;
+			m )	ADD_MINOR=1
+				minor=$OPTARG
+				cmd=$cmd" -m '$minor'"
+				;;
+			c)	ADD_CLASS=1
+				class=$OPTARG
+				cmd=$cmd" -c $class"
+				;;
+			b)	BASEDIR=$OPTARG
+				cmd=$cmd" -b $BASEDIR"
+				;;
+			\?) 	echo "check_add_drv can not handle this option"
+				return
+				;;
+			esac
+	done 
+	shift `/usr/bin/expr $OPTIND - 1`
+	
+	drvname=$1
+
+	cmd=$cmd" "$drvname
+
+	drvname=`echo $drvname | /usr/bin/sed 's;.*/;;g'`
+
+	/usr/bin/grep "^$drvname[ 	]" ${ROOT}/etc/name_to_major >  /dev/null 2>&1
+
+	if [ "$NO_CMD" = "" -a $? -ne 0 ] 
+	then
+		eval $cmd
+	else	
+		# entry already in name_to_major, add alias, class, minorperm
+		# if necessary
+		if [ $ADD_ALIAS = 1 ]	
+		then
+			for i in $alias
+			do
+				/usr/bin/egrep "^$drvname[ 	]+$i" ${ROOT}/etc/driver_aliases>/dev/null 2>&1
+				if [ $? -ne 0 ]
+				then
+					echo "$drvname $i" >> ${ROOT}/etc/driver_aliases	
+				fi
+			done
+		fi
+
+		if [ $ADD_CLASS = 1 ]
+		then
+			/usr/bin/egrep "^$drvname[ 	]+$class( |	|$)" ${ROOT}/etc/driver_classes > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				echo "$drvname\t$class" >> ${ROOT}/etc/driver_classes
+			fi
+		fi
+
+		if [ $ADD_MINOR = 1 ]
+		then
+			/usr/bin/grep "^$drvname:" ${ROOT}/etc/minor_perm > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				minorentry="$drvname:$minor"
+				echo $minorentry >> ${ROOT}/etc/minor_perm
+			fi
+		fi
+
+	fi
+}
+
+#
+# Main program starts here.
+#
+check_add_drv -b ${ROOT} -i '"pci10de,37f" "pci10de,55" "pci10de,54"' -m '* 0644 root sys' nv_sata
+
+#
+# Create temporary directory for node migration 
+#
+if [ ! -d ${NVSATA_TMP} ]
+then
+        ${MKDIR} ${NVSATA_TMP}
+        if [ $? -ne 0 ]
+        then
+                # Failed to create ${NVSATA_TMP} directory.
+                exit 1
+        fi
+fi
+
+#
+# If it is an install, do nothing. If it is an upgrade, do the conversion.
+#
+line=`$MOUNT | $GREP "/ "`
+first_field=`echo $line | $NAWK '{print $1}'`
+third_field=`echo $line | $NAWK '{print $3}'`
+
+if [ ${first_field} = "/" -a ${third_field} = "/ramdisk:a" ]
+then
+	# It is either an install/upgrade.
+	if [ -f ${UPGRADE_LOG} ]
+	then
+		# It is an upgrade.
+		$GREP ${NV_SATA} ${PATH_TO_INST} > /dev/null
+		if [ $? -eq 0 ]
+		then
+			# The system was running nv_sata before the upgrade.
+			$RM -rf ${NVSATA_TMP}
+			exit 0
+		fi
+
+		process_bootenvrc_for_install
+		process_system_for_install
+		process_power_conf_for_install
+		process_devlink_for_install
+		clean_up
+		exit 0
+	fi
+	$RM -rf ${NVSATA_TMP}
+	exit 0
+fi
+
+#
+# Liveupgrade. 
+#
+if [ -f ${LIVEUPGRADE_TMP_LOG} ]
+then
+	$GREP ${NV_SATA} /etc/driver_aliases >/dev/null
+	no_nvsata_in_CBE=$?
+	$GREP ${NV_SATA} ${PATH_TO_INST} > /dev/null
+	if [ $? -eq 0 ]
+	then
+		# Target BE was running nv_sata driver before the upgrade.
+		$RM -rf ${NVSATA_TMP}
+		exit 0
+	fi	
+	process_bootenvrc_for_lu
+	process_system_for_lu
+	process_power_conf_for_lu
+	process_devlink_for_lu
+	clean_up
+	touch ${ROOT}/reconfigure
+	exit 0
+fi
+
+#
+# Pkgadd.
+#
+$RM -rf ${NVSATA_TMP}
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWnvsata/postremove	Thu Aug 16 14:46:34 2007 -0700
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+BD=${BASEDIR:-/}
+if grep "\<nv_sata\>" $BD/etc/name_to_major > /dev/null 2>&1
+then
+	rem_drv -b ${BD} nv_sata
+fi
+exit 0
--- a/usr/src/pkgdefs/SUNWnvsata/prototype_i386	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/pkgdefs/SUNWnvsata/prototype_i386	Thu Aug 16 14:46:34 2007 -0700
@@ -45,8 +45,13 @@
 i pkginfo
 i copyright
 i depend
+i postinstall
+i postremove
 
 # nv_sata sata driver
 d none kernel	0755	root	sys
 d none kernel/drv	0755	root	sys
 d none kernel/drv/amd64	0755	root	sys
+f none kernel/drv/nv_sata 0755	root	sys
+f none kernel/drv/nv_sata.conf 0644	root	sys
+f none kernel/drv/amd64/nv_sata 0755	root	sys
--- a/usr/src/tools/findunref/exception_list	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/tools/findunref/exception_list	Thu Aug 16 14:46:34 2007 -0700
@@ -212,12 +212,13 @@
 ./src/common/bignum/mont_mulf.c
 
 #
-# Ignore the sparc Makefiles for sata, si3124, marvell88sx, ahci and audioixp.
-# These are used for warlock purposes only.
+# Ignore the sparc Makefiles for sata, si3124, marvell88sx, nv_sata, ahci
+# and audioixp.  These are used for warlock purposes only.
 #
 ./src/uts/sparc/sata/Makefile
 ./src/uts/sparc/si3124/Makefile
 ./closed/uts/sparc/marvell88sx/Makefile
+./src/uts/sparc/nv_sata/Makefile
 ./src/uts/sparc/ahci/Makefile
 ./src/uts/sparc/audioixp/Makefile
 
--- a/usr/src/tools/scripts/bfu.sh	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/tools/scripts/bfu.sh	Thu Aug 16 14:46:34 2007 -0700
@@ -88,6 +88,7 @@
 	etc/cron.d/cron.deny
 	etc/crypto/pkcs11.conf
 	etc/default/*
+	etc/dev/reserved_devnames
 	etc/dfs/dfstab
 	etc/dumpdates
 	etc/ftpd/*
--- a/usr/src/uts/common/Makefile.files	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/common/Makefile.files	Thu Aug 16 14:46:34 2007 -0700
@@ -776,6 +776,8 @@
 
 ZCONS_OBJS += zcons.o
 
+NV_SATA_OBJS += nv_sata.o
+
 SI3124_OBJS += si3124.o
 
 NSCONFIG_DEVNAME_OBJS += sdev_nsconfig_mod.o
--- a/usr/src/uts/common/Makefile.rules	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/common/Makefile.rules	Thu Aug 16 14:46:34 2007 -0700
@@ -667,6 +667,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/sata/adapters/nv_sata/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/sata/adapters/si3124/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1442,6 +1446,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/sata/adapters/ahci/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/sata/adapters/nv_sata/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/sata/adapters/si3124/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.c	Thu Aug 16 14:46:34 2007 -0700
@@ -0,0 +1,4766 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ *
+ * nv_sata is a combo SATA HBA driver for ck804/mcp55 based chipsets.
+ *
+ * NCQ
+ * ---
+ *
+ * A portion of the NCQ is in place, but is incomplete.  NCQ is disabled
+ * and is likely to be revisited in the future.
+ *
+ *
+ * Power Management
+ * ----------------
+ *
+ * Normally power management would be responsible for ensuring the device
+ * is quiescent and then changing power states to the device, such as
+ * powering down parts or all of the device.  mcp55/ck804 is unique in
+ * that it is only available as part of a larger southbridge chipset, so
+ * removing power to the device isn't possible.  Switches to control
+ * power management states D0/D3 in the PCI configuration space appear to
+ * be supported but changes to these states are apparently are ignored.
+ * The only further PM that the driver _could_ do is shut down the PHY,
+ * but in order to deliver the first rev of the driver sooner than later,
+ * that will be deferred until some future phase.
+ *
+ * Since the driver currently will not directly change any power state to
+ * the device, no power() entry point will be required.  However, it is
+ * possible that in ACPI power state S3, aka suspend to RAM, that power
+ * can be removed to the device, and the driver cannot rely on BIOS to
+ * have reset any state.  For the time being, there is no known
+ * non-default configurations that need to be programmed.  This judgement
+ * is based on the port of the legacy ata driver not having any such
+ * functionality and based on conversations with the PM team.  If such a
+ * restoration is later deemed necessary it can be incorporated into the
+ * DDI_RESUME processing.
+ *
+ */
+
+#include <sys/scsi/scsi.h>
+#include <sys/pci.h>
+#include <sys/byteorder.h>
+#include <sys/sata/sata_hba.h>
+#include <sys/sata/adapters/nv_sata/nv_sata.h>
+#include <sys/disp.h>
+#include <sys/note.h>
+#include <sys/promif.h>
+
+
+/*
+ * Function prototypes for driver entry points
+ */
+static int nv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int nv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static int nv_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
+    void *arg, void **result);
+
+/*
+ * Function prototypes for entry points from sata service module
+ * These functions are distinguished from other local functions
+ * by the prefix "nv_sata_"
+ */
+static int nv_sata_start(dev_info_t *dip, sata_pkt_t *spkt);
+static int nv_sata_abort(dev_info_t *dip, sata_pkt_t *spkt, int);
+static int nv_sata_reset(dev_info_t *dip, sata_device_t *sd);
+static int nv_sata_activate(dev_info_t *dip, sata_device_t *sd);
+static int nv_sata_deactivate(dev_info_t *dip, sata_device_t *sd);
+
+/*
+ * Local function prototypes
+ */
+static uint_t mcp55_intr(caddr_t arg1, caddr_t arg2);
+static uint_t mcp04_intr(caddr_t arg1, caddr_t arg2);
+static int nv_add_legacy_intrs(nv_ctl_t *nvc);
+#ifdef NV_MSI_SUPPORTED
+static int nv_add_msi_intrs(nv_ctl_t *nvc);
+#endif
+static void nv_rem_intrs(nv_ctl_t *nvc);
+static int nv_start_common(nv_port_t *nvp, sata_pkt_t *spkt);
+static int nv_start_nodata(nv_port_t *nvp, int slot);
+static void nv_intr_nodata(nv_port_t *nvp, nv_slot_t *spkt);
+static int nv_start_pio_in(nv_port_t *nvp, int slot);
+static int nv_start_pio_out(nv_port_t *nvp, int slot);
+static void nv_intr_pio_in(nv_port_t *nvp, nv_slot_t *spkt);
+static void nv_intr_pio_out(nv_port_t *nvp, nv_slot_t *spkt);
+static int nv_start_dma(nv_port_t *nvp, int slot);
+static void nv_intr_dma(nv_port_t *nvp, struct nv_slot *spkt);
+static void nv_log(uint_t flag, nv_ctl_t *nvc, nv_port_t *nvp, char *fmt, ...);
+static void nv_uninit_ctl(nv_ctl_t *nvc);
+static void mcp55_reg_init(nv_ctl_t *nvc, ddi_acc_handle_t pci_conf_handle);
+static void mcp04_reg_init(nv_ctl_t *nvc, ddi_acc_handle_t pci_conf_handle);
+static void nv_uninit_port(nv_port_t *nvp);
+static int nv_init_port(nv_port_t *nvp);
+static int nv_init_ctl(nv_ctl_t *nvc, ddi_acc_handle_t pci_conf_handle);
+static int mcp55_packet_complete_intr(nv_ctl_t *nvc, nv_port_t *nvp);
+#ifdef NCQ
+static int mcp55_dma_setup_intr(nv_ctl_t *nvc, nv_port_t *nvp);
+#endif
+static void nv_start_dma_engine(nv_port_t *nvp, int slot);
+static void nv_port_state_change(nv_port_t *nvp, int event, uint8_t addr_type,
+    int state);
+static boolean_t nv_check_link(uint32_t sstatus);
+static void nv_common_reg_init(nv_ctl_t *nvc);
+static void mcp04_intr_process(nv_ctl_t *nvc, uint8_t intr_status);
+static void nv_reset(nv_port_t *nvp);
+static void nv_complete_io(nv_port_t *nvp,  sata_pkt_t *spkt, int slot);
+static void nv_timeout(void *);
+static int nv_poll_wait(nv_port_t *nvp, sata_pkt_t *spkt);
+static void nv_cmn_err(int ce, nv_ctl_t *nvc, nv_port_t *nvp, char *fmt, ...);
+static void nv_read_signature(nv_port_t *nvp);
+static void mcp55_set_intr(nv_port_t *nvp, int flag);
+static void mcp04_set_intr(nv_port_t *nvp, int flag);
+static void nv_resume(nv_port_t *nvp);
+static int nv_start_sync(nv_port_t *nvp, sata_pkt_t *spkt);
+static int nv_abort_active(nv_port_t *nvp, sata_pkt_t *spkt, int abort_reason);
+static void nv_copy_registers(nv_port_t *nvp, sata_device_t *sd,
+    sata_pkt_t *spkt);
+static void nv_report_add_remove(nv_port_t *nvp, int flags);
+static int nv_start_async(nv_port_t *nvp, sata_pkt_t *spkt);
+static int nv_wait3(nv_port_t *nvp, uchar_t onbits1, uchar_t offbits1,
+    uchar_t failure_onbits2, uchar_t failure_offbits2,
+    uchar_t failure_onbits3, uchar_t failure_offbits3,
+    uint_t timeout_usec, int type_wait);
+static int nv_wait(nv_port_t *nvp, uchar_t onbits, uchar_t offbits,
+    uint_t timeout_usec, int type_wait);
+
+
+/*
+ * DMA attributes for the data buffer for x86.  dma_attr_burstsizes is unused.
+ * Verify if needed if ported to other ISA.
+ */
+static ddi_dma_attr_t buffer_dma_attr = {
+	DMA_ATTR_V0,		/* dma_attr_version */
+	0,			/* dma_attr_addr_lo: lowest bus address */
+	0xffffffffull,		/* dma_attr_addr_hi: */
+	NV_BM_64K_BOUNDARY - 1,	/* dma_attr_count_max i.e for one cookie */
+	4,			/* dma_attr_align */
+	1,			/* dma_attr_burstsizes. */
+	1,			/* dma_attr_minxfer */
+	0xffffffffull,		/* dma_attr_max xfer including all cookies */
+	0xffffffffull,		/* dma_attr_seg */
+	NV_DMA_NSEGS,		/* dma_attr_sgllen */
+	512,			/* dma_attr_granular */
+	0,			/* dma_attr_flags */
+};
+
+
+/*
+ * DMA attributes for PRD tables
+ */
+ddi_dma_attr_t nv_prd_dma_attr = {
+	DMA_ATTR_V0,		/* dma_attr_version */
+	0,			/* dma_attr_addr_lo */
+	0xffffffffull,		/* dma_attr_addr_hi */
+	NV_BM_64K_BOUNDARY - 1,	/* dma_attr_count_max */
+	4,			/* dma_attr_align */
+	1,			/* dma_attr_burstsizes */
+	1,			/* dma_attr_minxfer */
+	NV_BM_64K_BOUNDARY,	/* dma_attr_maxxfer */
+	NV_BM_64K_BOUNDARY - 1,	/* dma_attr_seg */
+	1,			/* dma_attr_sgllen */
+	1,			/* dma_attr_granular */
+	0			/* dma_attr_flags */
+};
+
+/*
+ * Device access attributes
+ */
+static ddi_device_acc_attr_t accattr = {
+    DDI_DEVICE_ATTR_V0,
+    DDI_STRUCTURE_LE_ACC,
+    DDI_STRICTORDER_ACC
+};
+
+
+static struct dev_ops nv_dev_ops = {
+	DEVO_REV,		/* devo_rev */
+	0,			/* refcnt  */
+	nv_getinfo,		/* info */
+	nulldev,		/* identify */
+	nulldev,		/* probe */
+	nv_attach,		/* attach */
+	nv_detach,		/* detach */
+	nodev,			/* no reset */
+	(struct cb_ops *)0,	/* driver operations */
+	NULL,			/* bus operations */
+	NULL			/* power */
+};
+
+static sata_tran_hotplug_ops_t nv_hotplug_ops;
+
+extern struct mod_ops mod_driverops;
+
+static  struct modldrv modldrv = {
+	&mod_driverops,	/* driverops */
+	"Nvidia ck804/mcp55 HBA v%I%",
+	&nv_dev_ops,	/* driver ops */
+};
+
+static  struct modlinkage modlinkage = {
+	MODREV_1,
+	&modldrv,
+	NULL
+};
+
+
+/*
+ * wait between checks of reg status
+ */
+int nv_usec_delay = NV_WAIT_REG_CHECK;
+
+/*
+ * The following is needed for nv_vcmn_err()
+ */
+static kmutex_t nv_log_mutex; /* protects nv_log_buf */
+static char nv_log_buf[NV_STRING_512];
+int nv_debug_flags = NVDBG_ALWAYS;
+int nv_log_to_console = B_FALSE;
+
+int nv_log_delay = 0;
+int nv_prom_print = B_FALSE;
+
+/*
+ * for debugging
+ */
+#ifdef DEBUG
+int ncq_commands = 0;
+int non_ncq_commands = 0;
+#endif
+
+/*
+ * Opaque state pointer to be initialized by ddi_soft_state_init()
+ */
+static void *nv_statep	= NULL;
+
+
+static sata_tran_hotplug_ops_t nv_hotplug_ops = {
+	SATA_TRAN_HOTPLUG_OPS_REV_1,	/* structure version */
+	nv_sata_activate,	/* activate port. cfgadm -c connect */
+	nv_sata_deactivate	/* deactivate port. cfgadm -c disconnect */
+};
+
+
+/*
+ *  nv module initialization
+ */
+int
+_init(void)
+{
+	int	error;
+
+	error = ddi_soft_state_init(&nv_statep, sizeof (nv_ctl_t), 0);
+
+	if (error != 0) {
+
+		return (error);
+	}
+
+	mutex_init(&nv_log_mutex, NULL, MUTEX_DRIVER, NULL);
+
+	if ((error = sata_hba_init(&modlinkage)) != 0) {
+		ddi_soft_state_fini(&nv_statep);
+		mutex_destroy(&nv_log_mutex);
+
+		return (error);
+	}
+
+	error = mod_install(&modlinkage);
+	if (error != 0) {
+		sata_hba_fini(&modlinkage);
+		ddi_soft_state_fini(&nv_statep);
+		mutex_destroy(&nv_log_mutex);
+
+		return (error);
+	}
+
+	return (error);
+}
+
+
+/*
+ * nv module uninitialize
+ */
+int
+_fini(void)
+{
+	int	error;
+
+	error = mod_remove(&modlinkage);
+
+	if (error != 0) {
+		return (error);
+	}
+
+	/*
+	 * remove the resources allocated in _init()
+	 */
+	mutex_destroy(&nv_log_mutex);
+	sata_hba_fini(&modlinkage);
+	ddi_soft_state_fini(&nv_statep);
+
+	return (error);
+}
+
+
+/*
+ * nv _info entry point
+ */
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+
+/*
+ * these wrappers for ddi_{get,put}8 are for observability
+ * with dtrace
+ */
+#ifdef DEBUG
+
+static void
+nv_put8(ddi_acc_handle_t handle, uint8_t *dev_addr, uint8_t value)
+{
+	ddi_put8(handle, dev_addr, value);
+}
+
+static void
+nv_put32(ddi_acc_handle_t handle, uint32_t *dev_addr, uint32_t value)
+{
+	ddi_put32(handle, dev_addr, value);
+}
+
+static uint32_t
+nv_get32(ddi_acc_handle_t handle, uint32_t *dev_addr)
+{
+	return (ddi_get32(handle, dev_addr));
+}
+
+static void
+nv_put16(ddi_acc_handle_t handle, uint16_t *dev_addr, uint16_t value)
+{
+	ddi_put16(handle, dev_addr, value);
+}
+
+static uint16_t
+nv_get16(ddi_acc_handle_t handle, uint16_t *dev_addr)
+{
+	return (ddi_get16(handle, dev_addr));
+}
+
+static uint8_t
+nv_get8(ddi_acc_handle_t handle, uint8_t *dev_addr)
+{
+	return (ddi_get8(handle, dev_addr));
+}
+
+#else
+
+#define	nv_put8 ddi_put8
+#define	nv_put32 ddi_put32
+#define	nv_get32 ddi_get32
+#define	nv_put16 ddi_put16
+#define	nv_get16 ddi_get16
+#define	nv_get8 ddi_get8
+
+#endif
+
+
+/*
+ * Driver attach
+ */
+static int
+nv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	int status, attach_state, intr_types, bar, i;
+	int inst = ddi_get_instance(dip);
+	ddi_acc_handle_t pci_conf_handle;
+	nv_ctl_t *nvc;
+	uint8_t subclass;
+
+	switch (cmd) {
+
+	case DDI_ATTACH:
+
+		NVLOG((NVDBG_INIT, NULL, NULL,
+		    "nv_attach(): DDI_ATTACH inst %d", inst));
+
+		attach_state = ATTACH_PROGRESS_NONE;
+
+		status = ddi_soft_state_zalloc(nv_statep, inst);
+
+		if (status != DDI_SUCCESS) {
+			break;
+		}
+
+		nvc = ddi_get_soft_state(nv_statep, inst);
+
+		nvc->nvc_dip = dip;
+
+		attach_state |= ATTACH_PROGRESS_STATEP_ALLOC;
+
+		if (pci_config_setup(dip, &pci_conf_handle) == DDI_SUCCESS) {
+			nvc->nvc_revid = pci_config_get8(pci_conf_handle,
+			    PCI_CONF_REVID);
+			NVLOG((NVDBG_INIT, NULL, NULL,
+			    "inst %d: silicon revid is %x nv_debug_flags=%x",
+			    inst, nvc->nvc_revid, nv_debug_flags));
+		} else {
+			break;
+		}
+
+		attach_state |= ATTACH_PROGRESS_CONF_HANDLE;
+
+		subclass = pci_config_get8(pci_conf_handle, PCI_CONF_SUBCLASS);
+
+		if (subclass & PCI_MASS_RAID) {
+			cmn_err(CE_WARN,
+			    "attach failed: RAID mode not supported");
+			break;
+		}
+
+		/*
+		 * the 6 bars of the controller are:
+		 * 0: port 0 task file
+		 * 1: port 0 status
+		 * 2: port 1 task file
+		 * 3: port 1 status
+		 * 4: bus master for both ports
+		 * 5: extended registers for SATA features
+		 */
+		for (bar = 0; bar < 6; bar++) {
+			status = ddi_regs_map_setup(dip, bar + 1,
+			    (caddr_t *)&nvc->nvc_bar_addr[bar], 0, 0, &accattr,
+			    &nvc->nvc_bar_hdl[bar]);
+
+			if (status != DDI_SUCCESS) {
+				NVLOG((NVDBG_INIT, nvc, NULL,
+				    "ddi_regs_map_setup failure for bar"
+				    " %d status = %d", bar, status));
+				break;
+			}
+		}
+
+		attach_state |= ATTACH_PROGRESS_BARS;
+
+		/*
+		 * initialize controller and driver core
+		 */
+		status = nv_init_ctl(nvc, pci_conf_handle);
+
+		if (status == NV_FAILURE) {
+			NVLOG((NVDBG_INIT, nvc, NULL, "nv_init_ctl failed"));
+
+			break;
+		}
+
+		attach_state |= ATTACH_PROGRESS_CTL_SETUP;
+
+		/*
+		 * initialize mutexes
+		 */
+		mutex_init(&nvc->nvc_mutex, NULL, MUTEX_DRIVER,
+		    DDI_INTR_PRI(nvc->nvc_intr_pri));
+
+		attach_state |= ATTACH_PROGRESS_MUTEX_INIT;
+
+		/*
+		 * get supported interrupt types
+		 */
+		if (ddi_intr_get_supported_types(dip, &intr_types) !=
+		    DDI_SUCCESS) {
+			nv_cmn_err(CE_WARN, nvc, NULL,
+			    "!ddi_intr_get_supported_types failed");
+			NVLOG((NVDBG_INIT, nvc, NULL,
+			    "interrupt supported types failed"));
+
+			break;
+		}
+
+		NVLOG((NVDBG_INIT, nvc, NULL,
+		    "ddi_intr_get_supported_types() returned: 0x%x",
+		    intr_types));
+
+#ifdef NV_MSI_SUPPORTED
+		if (intr_types & DDI_INTR_TYPE_MSI) {
+			NVLOG((NVDBG_INIT, nvc, NULL,
+			    "using MSI interrupt type"));
+
+			/*
+			 * Try MSI first, but fall back to legacy if MSI
+			 * attach fails
+			 */
+			if (nv_add_msi_intrs(nvc) == DDI_SUCCESS) {
+				nvc->nvc_intr_type = DDI_INTR_TYPE_MSI;
+				attach_state |= ATTACH_PROGRESS_INTR_ADDED;
+				NVLOG((NVDBG_INIT, nvc, NULL,
+				    "MSI interrupt setup done"));
+			} else {
+				nv_cmn_err(CE_CONT, nvc, NULL,
+				    "!MSI registration failed "
+				    "will try Legacy interrupts");
+			}
+		}
+#endif
+
+		/*
+		 * Either the MSI interrupt setup has failed or only
+		 * the fixed interrupts are available on the system.
+		 */
+		if (!(attach_state & ATTACH_PROGRESS_INTR_ADDED) &&
+		    (intr_types & DDI_INTR_TYPE_FIXED)) {
+
+			NVLOG((NVDBG_INIT, nvc, NULL,
+			    "using Legacy interrupt type"));
+
+			if (nv_add_legacy_intrs(nvc) == DDI_SUCCESS) {
+				nvc->nvc_intr_type = DDI_INTR_TYPE_FIXED;
+				attach_state |= ATTACH_PROGRESS_INTR_ADDED;
+				NVLOG((NVDBG_INIT, nvc, NULL,
+				    "Legacy interrupt setup done"));
+			} else {
+				nv_cmn_err(CE_WARN, nvc, NULL,
+				    "!legacy interrupt setup failed");
+				NVLOG((NVDBG_INIT, nvc, NULL,
+				    "legacy interrupt setup failed"));
+				break;
+			}
+		}
+
+		if (!(attach_state & ATTACH_PROGRESS_INTR_ADDED)) {
+			NVLOG((NVDBG_INIT, nvc, NULL,
+			    "no interrupts registered"));
+			break;
+		}
+
+		/*
+		 * attach to sata module
+		 */
+		if (sata_hba_attach(nvc->nvc_dip,
+		    &nvc->nvc_sata_hba_tran,
+		    DDI_ATTACH) != DDI_SUCCESS) {
+			attach_state |= ATTACH_PROGRESS_SATA_MODULE;
+
+			break;
+		}
+
+		pci_config_teardown(&pci_conf_handle);
+
+		NVLOG((NVDBG_INIT, nvc, NULL, "nv_attach DDI_SUCCESS"));
+
+		return (DDI_SUCCESS);
+
+	case DDI_RESUME:
+
+		nvc = ddi_get_soft_state(nv_statep, inst);
+
+		NVLOG((NVDBG_INIT, nvc, NULL,
+		    "nv_attach(): DDI_RESUME inst %d", inst));
+
+
+		nvc->nvc_state &= ~NV_CTRL_SUSPEND;
+
+		for (i = 0; i < NV_MAX_PORTS(nvc); i++) {
+			nv_resume(&(nvc->nvc_port[i]));
+		}
+
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+
+
+	/*
+	 * DDI_ATTACH failure path starts here
+	 */
+
+	if (attach_state & ATTACH_PROGRESS_INTR_ADDED) {
+		nv_rem_intrs(nvc);
+	}
+
+	if (attach_state & ATTACH_PROGRESS_SATA_MODULE) {
+		/*
+		 * Remove timers
+		 */
+		int port = 0;
+		nv_port_t *nvp;
+
+		for (; port < NV_MAX_PORTS(nvc); port++) {
+			nvp = &(nvc->nvc_port[port]);
+			if (nvp->nvp_timeout_id != 0) {
+				(void) untimeout(nvp->nvp_timeout_id);
+			}
+		}
+	}
+
+	if (attach_state & ATTACH_PROGRESS_MUTEX_INIT) {
+		mutex_destroy(&nvc->nvc_mutex);
+	}
+
+	if (attach_state & ATTACH_PROGRESS_CTL_SETUP) {
+		nv_uninit_ctl(nvc);
+	}
+
+	if (attach_state & ATTACH_PROGRESS_BARS) {
+		while (--bar >= 0) {
+			ddi_regs_map_free(&nvc->nvc_bar_hdl[bar]);
+		}
+	}
+
+	if (attach_state & ATTACH_PROGRESS_STATEP_ALLOC) {
+		ddi_soft_state_free(nv_statep, inst);
+	}
+
+	if (attach_state & ATTACH_PROGRESS_CONF_HANDLE) {
+		pci_config_teardown(&pci_conf_handle);
+	}
+
+	cmn_err(CE_WARN, "nv_sata%d attach failed", inst);
+
+	return (DDI_FAILURE);
+}
+
+
+static int
+nv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	int i, port, inst = ddi_get_instance(dip);
+	nv_ctl_t *nvc;
+	nv_port_t *nvp;
+
+	nvc = ddi_get_soft_state(nv_statep, inst);
+
+	switch (cmd) {
+
+	case DDI_DETACH:
+
+		NVLOG((NVDBG_INIT, nvc, NULL, "nv_detach: DDI_DETACH"));
+
+		/*
+		 * Remove interrupts
+		 */
+		nv_rem_intrs(nvc);
+
+		/*
+		 * Remove timers
+		 */
+		for (port = 0; port < NV_MAX_PORTS(nvc); port++) {
+			nvp = &(nvc->nvc_port[port]);
+			if (nvp->nvp_timeout_id != 0) {
+				(void) untimeout(nvp->nvp_timeout_id);
+			}
+		}
+
+		/*
+		 * Remove maps
+		 */
+		for (i = 0; i < 6; i++) {
+			ddi_regs_map_free(&nvc->nvc_bar_hdl[i]);
+		}
+
+		/*
+		 * Destroy mutexes
+		 */
+		mutex_destroy(&nvc->nvc_mutex);
+
+		/*
+		 * Uninitialize the controller
+		 */
+		nv_uninit_ctl(nvc);
+
+		/*
+		 * unregister from the sata module
+		 */
+		(void) sata_hba_detach(nvc->nvc_dip, DDI_DETACH);
+
+		/*
+		 * Free soft state
+		 */
+		ddi_soft_state_free(nv_statep, inst);
+
+		return (DDI_SUCCESS);
+
+	case DDI_SUSPEND:
+		/*
+		 * The PM functions for suspend and resume are incomplete
+		 * and need additional work.  It may or may not work in
+		 * the current state.
+		 */
+		NVLOG((NVDBG_INIT, nvc, NULL, "nv_detach: DDI_SUSPEND"));
+		nvc->nvc_state |= NV_CTRL_SUSPEND;
+
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+
+/*ARGSUSED*/
+static int
+nv_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+	nv_ctl_t *nvc;
+	int instance;
+	dev_t dev;
+
+	dev = (dev_t)arg;
+	instance = getminor(dev);
+
+	switch (infocmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		nvc = ddi_get_soft_state(nv_statep,  instance);
+		if (nvc != NULL) {
+			*result = nvc->nvc_dip;
+			return (DDI_SUCCESS);
+		} else {
+			*result = NULL;
+			return (DDI_FAILURE);
+		}
+	case DDI_INFO_DEVT2INSTANCE:
+		*(int *)result = instance;
+		break;
+	default:
+		break;
+	}
+	return (DDI_SUCCESS);
+}
+
+
+/*
+ * Called by sata module to probe a port.  Port and device state
+ * are not changed here... only reported back to the sata module.
+ *
+ * If probe confirms a device is present for the first time, it will
+ * initiate a device reset, then probe will be called again and the
+ * signature will be check.  If the signature is valid, data structures
+ * will be initialized.
+ */
+static int
+nv_sata_probe(dev_info_t *dip, sata_device_t *sd)
+{
+	nv_ctl_t *nvc = ddi_get_soft_state(nv_statep, ddi_get_instance(dip));
+	uint8_t cport = sd->satadev_addr.cport;
+	uint8_t pmport = sd->satadev_addr.pmport;
+	uint8_t qual = sd->satadev_addr.qual;
+	clock_t nv_lbolt = ddi_get_lbolt();
+	nv_port_t *nvp;
+
+	if (cport >= NV_MAX_PORTS(nvc)) {
+		sd->satadev_type = SATA_DTYPE_NONE;
+		sd->satadev_state = SATA_STATE_PROBED;
+
+		return (SATA_FAILURE);
+	}
+
+	ASSERT(nvc->nvc_port != NULL);
+	nvp = &(nvc->nvc_port[cport]);
+	ASSERT(nvp != NULL);
+
+	NVLOG((NVDBG_PROBE, nvc, nvp,
+	    "nv_sata_probe: enter cport: 0x%x, pmport: 0x%x, "
+	    "qual: 0x%x", cport, pmport, qual));
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	/*
+	 * This check seems to be done in the SATA module.
+	 * It may not be required here
+	 */
+	if (nvp->nvp_state & NV_PORT_INACTIVE) {
+		nv_cmn_err(CE_WARN, nvc, nvp,
+		    "port inactive.  Use cfgadm to activate");
+		sd->satadev_type = SATA_DTYPE_UNKNOWN;
+		sd->satadev_state = SATA_PSTATE_SHUTDOWN;
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_FAILURE);
+	}
+
+	if (qual == SATA_ADDR_PMPORT) {
+		sd->satadev_type = SATA_DTYPE_NONE;
+		sd->satadev_state = SATA_STATE_PROBED;
+		mutex_exit(&nvp->nvp_mutex);
+		nv_cmn_err(CE_WARN, nvc, nvp,
+		    "controller does not support port multiplier");
+
+		return (SATA_FAILURE);
+	}
+
+	sd->satadev_state = SATA_PSTATE_PWRON;
+
+	nv_copy_registers(nvp, sd, NULL);
+
+	/*
+	 * determine link status
+	 */
+	if (nv_check_link(sd->satadev_scr.sstatus) == B_FALSE) {
+		uint8_t det;
+
+		/*
+		 * Reset will cause the link to go down for a short period of
+		 * time.  If link is lost for less than 2 seconds ignore it
+		 * so that the reset can progress.
+		 */
+		if (nvp->nvp_state & NV_PORT_RESET_PROBE) {
+
+			if (nvp->nvp_link_lost_time == 0) {
+				nvp->nvp_link_lost_time = nv_lbolt;
+			}
+
+			if (TICK_TO_SEC(nv_lbolt -
+			    nvp->nvp_link_lost_time) < NV_LINK_LOST_OK) {
+				NVLOG((NVDBG_ALWAYS, nvp->nvp_ctlp, nvp,
+				    "probe: intermittent link lost while"
+				    " resetting"));
+				/*
+				 * fake status of link so that probe continues
+				 */
+				SSTATUS_SET_IPM(sd->satadev_scr.sstatus,
+				    SSTATUS_IPM_ACTIVE);
+				SSTATUS_SET_DET(sd->satadev_scr.sstatus,
+				    SSTATUS_DET_DEVPRE_PHYCOM);
+				sd->satadev_type = SATA_DTYPE_UNKNOWN;
+				mutex_exit(&nvp->nvp_mutex);
+
+				return (SATA_SUCCESS);
+			} else {
+				nvp->nvp_state &=
+				    ~(NV_PORT_RESET_PROBE|NV_PORT_RESET);
+			}
+		}
+
+		/*
+		 * no link, so tear down port and abort all active packets
+		 */
+
+		det = (sd->satadev_scr.sstatus & SSTATUS_DET) >>
+		    SSTATUS_DET_SHIFT;
+
+		switch (det) {
+		case SSTATUS_DET_NODEV:
+		case SSTATUS_DET_PHYOFFLINE:
+			sd->satadev_type = SATA_DTYPE_NONE;
+			break;
+		default:
+			sd->satadev_type = SATA_DTYPE_UNKNOWN;
+			break;
+		}
+
+		NVLOG((NVDBG_PROBE, nvp->nvp_ctlp, nvp,
+		    "probe: link lost invoking nv_abort_active"));
+
+		(void) nv_abort_active(nvp, NULL, SATA_PKT_TIMEOUT);
+		nv_uninit_port(nvp);
+
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_SUCCESS);
+	} else {
+		nvp->nvp_link_lost_time = 0;
+	}
+
+	/*
+	 * A device is present so clear hotremoved flag
+	 */
+	nvp->nvp_state &= ~NV_PORT_HOTREMOVED;
+
+	/*
+	 * If the signature was acquired previously there is no need to
+	 * do it again.
+	 */
+	if (nvp->nvp_signature != 0) {
+		NVLOG((NVDBG_PROBE, nvp->nvp_ctlp, nvp,
+		    "probe: signature acquired previously"));
+		sd->satadev_type = nvp->nvp_type;
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_SUCCESS);
+	}
+
+	/*
+	 * If NV_PORT_RESET is not set, this is the first time through
+	 * so perform reset and return.
+	 */
+	if ((nvp->nvp_state & NV_PORT_RESET) == 0) {
+		NVLOG((NVDBG_PROBE, nvp->nvp_ctlp, nvp,
+		    "probe: first reset to get sig"));
+		nvp->nvp_state |= NV_PORT_RESET_PROBE;
+		nv_reset(nvp);
+		sd->satadev_type = nvp->nvp_type = SATA_DTYPE_UNKNOWN;
+		nvp->nvp_probe_time = nv_lbolt;
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_SUCCESS);
+	}
+
+	/*
+	 * Reset was done previously.  see if the signature is
+	 * available.
+	 */
+	nv_read_signature(nvp);
+	sd->satadev_type = nvp->nvp_type;
+
+	/*
+	 * Some drives may require additional resets to get a
+	 * valid signature.  If a drive was not just powered up, the signature
+	 * should arrive within half a second of reset.  Therefore if more
+	 * than 5 seconds has elapsed while waiting for a signature, reset
+	 * again.  These extra resets do not appear to create problems when
+	 * the drive is spinning up for more than this reset period.
+	 */
+	if (nvp->nvp_signature == 0) {
+		if (TICK_TO_SEC(nv_lbolt - nvp->nvp_reset_time) > 5) {
+			NVLOG((NVDBG_PROBE, nvc, nvp, "additional reset"
+			    " during signature acquisition"));
+			nv_reset(nvp);
+		}
+
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_SUCCESS);
+	}
+
+	NVLOG((NVDBG_PROBE, nvc, nvp, "signature acquired after %d ms",
+	    TICK_TO_MSEC(nv_lbolt - nvp->nvp_probe_time)));
+
+	/*
+	 * nv_sata only deals with ATA disks so far.  If it is
+	 * not an ATA disk, then just return.
+	 */
+	if (nvp->nvp_type != SATA_DTYPE_ATADISK) {
+		nv_cmn_err(CE_WARN, nvc, nvp, "Driver currently handles only"
+		    " disks.  Signature acquired was %X", nvp->nvp_signature);
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_SUCCESS);
+	}
+
+	/*
+	 * make sure structures are initialized
+	 */
+	if (nv_init_port(nvp) == NV_SUCCESS) {
+		NVLOG((NVDBG_PROBE, nvc, nvp,
+		    "device detected and set up at port %d", cport));
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_SUCCESS);
+	} else {
+		nv_cmn_err(CE_WARN, nvc, nvp, "failed to set up data "
+		    "structures for port %d", cport);
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_FAILURE);
+	}
+	/*NOTREACHED*/
+}
+
+
+/*
+ * Called by sata module to start a new command.
+ */
+static int
+nv_sata_start(dev_info_t *dip, sata_pkt_t *spkt)
+{
+	int cport = spkt->satapkt_device.satadev_addr.cport;
+	nv_ctl_t *nvc = ddi_get_soft_state(nv_statep, ddi_get_instance(dip));
+	nv_port_t *nvp = &(nvc->nvc_port[cport]);
+	int ret;
+
+	NVLOG((NVDBG_ENTRY, nvc, nvp, "nv_sata_start: opmode: 0x%x cmd=%x",
+	    spkt->satapkt_op_mode, spkt->satapkt_cmd.satacmd_cmd_reg));
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	/*
+	 * hotremoved is an intermediate state where the link was lost,
+	 * but the hotplug event has not yet been processed by the sata
+	 * module.  Fail the request.
+	 */
+	if (nvp->nvp_state & NV_PORT_HOTREMOVED) {
+		spkt->satapkt_reason = SATA_PKT_PORT_ERROR;
+		spkt->satapkt_device.satadev_state = SATA_STATE_UNKNOWN;
+		NVLOG((NVDBG_ERRS, nvc, nvp,
+		    "nv_sata_start: NV_PORT_HOTREMOVED"));
+		nv_copy_registers(nvp, &spkt->satapkt_device, NULL);
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_TRAN_PORT_ERROR);
+	}
+
+	if (nvp->nvp_state & NV_PORT_RESET) {
+		NVLOG((NVDBG_ERRS, nvc, nvp,
+		    "still waiting for reset completion"));
+		spkt->satapkt_reason = SATA_PKT_BUSY;
+		mutex_exit(&nvp->nvp_mutex);
+
+		/*
+		 * If in panic, timeouts do not occur, so fake one
+		 * so that the signature can be acquired to complete
+		 * the reset handling.
+		 */
+		if (ddi_in_panic()) {
+			nv_timeout(nvp);
+		}
+
+		return (SATA_TRAN_BUSY);
+	}
+
+	if (nvp->nvp_type == SATA_DTYPE_NONE) {
+		spkt->satapkt_reason = SATA_PKT_PORT_ERROR;
+		NVLOG((NVDBG_ERRS, nvc, nvp,
+		    "nv_sata_start: SATA_DTYPE_NONE"));
+		nv_copy_registers(nvp, &spkt->satapkt_device, NULL);
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_TRAN_PORT_ERROR);
+	}
+
+	if (spkt->satapkt_device.satadev_type == SATA_DTYPE_ATAPICD) {
+		ASSERT(nvp->nvp_type == SATA_DTYPE_ATAPICD);
+		nv_cmn_err(CE_WARN, nvc, nvp,
+		    "optical devices not supported");
+		spkt->satapkt_reason = SATA_PKT_CMD_UNSUPPORTED;
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_TRAN_CMD_UNSUPPORTED);
+	}
+
+	if (spkt->satapkt_device.satadev_type == SATA_DTYPE_PMULT) {
+		ASSERT(nvp->nvp_type == SATA_DTYPE_PMULT);
+		nv_cmn_err(CE_WARN, nvc, nvp,
+		    "port multipliers not supported by controller");
+		spkt->satapkt_reason = SATA_PKT_CMD_UNSUPPORTED;
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_TRAN_CMD_UNSUPPORTED);
+	}
+
+	if ((nvp->nvp_state & NV_PORT_INIT) == 0) {
+		spkt->satapkt_reason = SATA_PKT_PORT_ERROR;
+		NVLOG((NVDBG_ERRS, nvc, nvp,
+		    "nv_sata_start: port not yet initialized"));
+		nv_copy_registers(nvp, &spkt->satapkt_device, NULL);
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_TRAN_PORT_ERROR);
+	}
+
+	if (nvp->nvp_state & NV_PORT_INACTIVE) {
+		spkt->satapkt_reason = SATA_PKT_PORT_ERROR;
+		NVLOG((NVDBG_ERRS, nvc, nvp,
+		    "nv_sata_start: NV_PORT_INACTIVE"));
+		nv_copy_registers(nvp, &spkt->satapkt_device, NULL);
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_TRAN_PORT_ERROR);
+	}
+
+	if (nvp->nvp_state & NV_PORT_FAILED) {
+		spkt->satapkt_reason = SATA_PKT_PORT_ERROR;
+		NVLOG((NVDBG_ERRS, nvc, nvp,
+		    "nv_sata_start: NV_PORT_FAILED state"));
+		nv_copy_registers(nvp, &spkt->satapkt_device, NULL);
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_TRAN_PORT_ERROR);
+	}
+
+	/*
+	 * after a device reset, and then when sata module restore processing
+	 * is complete, the sata module will set sata_clear_dev_reset which
+	 * indicates that restore processing has completed and normal
+	 * non-restore related commands should be processed.
+	 */
+	if (spkt->satapkt_cmd.satacmd_flags.sata_clear_dev_reset) {
+		nvp->nvp_state &= ~NV_PORT_RESTORE;
+		NVLOG((NVDBG_ENTRY, nvc, nvp,
+		    "nv_sata_start: clearing NV_PORT_RESTORE"));
+	}
+
+	/*
+	 * if the device was recently reset as indicated by NV_PORT_RESTORE,
+	 * only allow commands which restore device state.  The sata module
+	 * marks such commands with with sata_ignore_dev_reset.
+	 *
+	 * during coredump, nv_reset is called and but then the restore
+	 * doesn't happen.  For now, workaround by ignoring the wait for
+	 * restore if the system is panicing.
+	 */
+	if ((nvp->nvp_state & NV_PORT_RESTORE) &&
+	    !(spkt->satapkt_cmd.satacmd_flags.sata_ignore_dev_reset) &&
+	    (ddi_in_panic() == 0)) {
+		spkt->satapkt_reason = SATA_PKT_BUSY;
+		NVLOG((NVDBG_ENTRY, nvc, nvp,
+		    "nv_sata_start: waiting for restore "));
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_TRAN_BUSY);
+	}
+
+	if (nvp->nvp_state & NV_PORT_ABORTING) {
+		spkt->satapkt_reason = SATA_PKT_BUSY;
+		NVLOG((NVDBG_ERRS, nvc, nvp,
+		    "nv_sata_start: NV_PORT_ABORTING"));
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (SATA_TRAN_BUSY);
+	}
+
+	if (spkt->satapkt_op_mode &
+	    (SATA_OPMODE_POLLING|SATA_OPMODE_SYNCH)) {
+
+		ret = nv_start_sync(nvp, spkt);
+
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (ret);
+	}
+
+	/*
+	 * start command asynchronous command
+	 */
+	ret = nv_start_async(nvp, spkt);
+
+	mutex_exit(&nvp->nvp_mutex);
+
+	return (ret);
+}
+
+
+/*
+ * SATA_OPMODE_POLLING implies the driver is in a
+ * synchronous mode, and SATA_OPMODE_SYNCH is also set.
+ * If only SATA_OPMODE_SYNCH is set, the driver can use
+ * interrupts and sleep wait on a cv.
+ *
+ * If SATA_OPMODE_POLLING is set, the driver can't use
+ * interrupts and must busy wait and simulate the
+ * interrupts by waiting for BSY to be cleared.
+ *
+ * Synchronous mode has to return BUSY if there are
+ * any other commands already on the drive.
+ */
+static int
+nv_start_sync(nv_port_t *nvp, sata_pkt_t *spkt)
+{
+	nv_ctl_t *nvc = nvp->nvp_ctlp;
+	int ret;
+
+	NVLOG((NVDBG_SYNC, nvp->nvp_ctlp, nvp, "nv_sata_satapkt_sync: entry"));
+
+	if (nvp->nvp_ncq_run != 0 || nvp->nvp_non_ncq_run != 0) {
+		spkt->satapkt_reason = SATA_PKT_BUSY;
+		NVLOG((NVDBG_SYNC, nvp->nvp_ctlp, nvp,
+		    "nv_sata_satapkt_sync: device is busy, sync cmd rejected"
+		    "ncq_run: %d non_ncq_run: %d  spkt: %p",
+		    nvp->nvp_ncq_run, nvp->nvp_non_ncq_run,
+		    (&(nvp->nvp_slot[0]))->nvslot_spkt));
+
+		return (SATA_TRAN_BUSY);
+	}
+
+	/*
+	 * if SYNC but not POLL, verify that this is not on interrupt thread.
+	 */
+	if (!(spkt->satapkt_op_mode & SATA_OPMODE_POLLING) &&
+	    servicing_interrupt()) {
+		spkt->satapkt_reason = SATA_PKT_BUSY;
+		nv_cmn_err(CE_WARN, nvp->nvp_ctlp, nvp,
+		    "SYNC mode not allowed during interrupt");
+
+		return (SATA_TRAN_BUSY);
+
+	}
+
+	/*
+	 * disable interrupt generation if in polled mode
+	 */
+	if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) {
+		(*(nvc->nvc_set_intr))(nvp, NV_INTR_DISABLE);
+	}
+
+	if ((ret = nv_start_common(nvp, spkt)) != SATA_TRAN_ACCEPTED) {
+		if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) {
+			(*(nvc->nvc_set_intr))(nvp, NV_INTR_ENABLE);
+		}
+
+		return (ret);
+	}
+
+	if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) {
+		mutex_exit(&nvp->nvp_mutex);
+		ret = nv_poll_wait(nvp, spkt);
+		mutex_enter(&nvp->nvp_mutex);
+
+		(*(nvc->nvc_set_intr))(nvp, NV_INTR_ENABLE);
+
+		NVLOG((NVDBG_SYNC, nvp->nvp_ctlp, nvp, "nv_sata_satapkt_sync:"
+			" done % reason %d", ret));
+
+		return (ret);
+	}
+
+	/*
+	 * non-polling synchronous mode handling.  The interrupt will signal
+	 * when the IO is completed.
+	 */
+	cv_wait(&nvp->nvp_poll_cv, &nvp->nvp_mutex);
+
+	if (spkt->satapkt_reason != SATA_PKT_COMPLETED) {
+
+		spkt->satapkt_reason = SATA_PKT_TIMEOUT;
+	}
+
+	NVLOG((NVDBG_SYNC, nvp->nvp_ctlp, nvp, "nv_sata_satapkt_sync:"
+	    " done % reason %d", spkt->satapkt_reason));
+
+	return (SATA_TRAN_ACCEPTED);
+}
+
+
+static int
+nv_poll_wait(nv_port_t *nvp, sata_pkt_t *spkt)
+{
+	int ret;
+	nv_ctl_t *nvc = nvp->nvp_ctlp;
+#if ! defined(__lock_lint)
+	nv_slot_t *nv_slotp = &(nvp->nvp_slot[0]); /* not NCQ aware */
+#endif
+
+	NVLOG((NVDBG_SYNC, nvc, nvp, "nv_poll_wait: enter"));
+
+	for (;;) {
+
+		NV_DELAY_NSEC(400);
+
+		NVLOG((NVDBG_SYNC, nvc, nvp, "nv_poll_wait: before nv_wait"));
+		if (nv_wait(nvp, 0, SATA_STATUS_BSY,
+		    NV_SEC2USEC(spkt->satapkt_time), NV_NOSLEEP) == B_FALSE) {
+			mutex_enter(&nvp->nvp_mutex);
+			spkt->satapkt_reason = SATA_PKT_TIMEOUT;
+			nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+			nv_reset(nvp);
+			nv_complete_io(nvp, spkt, 0);
+			mutex_exit(&nvp->nvp_mutex);
+			NVLOG((NVDBG_SYNC, nvc, nvp, "nv_poll_wait: "
+			    "SATA_STATUS_BSY"));
+
+			return (SATA_TRAN_ACCEPTED);
+		}
+
+		NVLOG((NVDBG_SYNC, nvc, nvp, "nv_poll_wait: before nvc_intr"));
+
+		/*
+		 * Simulate interrupt.
+		 */
+		ret = (*(nvc->nvc_interrupt))((caddr_t)nvc, NULL);
+		NVLOG((NVDBG_SYNC, nvc, nvp, "nv_poll_wait: after nvc_intr"));
+
+		if (ret != DDI_INTR_CLAIMED) {
+			NVLOG((NVDBG_SYNC, nvc, nvp, "nv_poll_wait:"
+			    " unclaimed -- resetting"));
+			mutex_enter(&nvp->nvp_mutex);
+			nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+			nv_reset(nvp);
+			spkt->satapkt_reason = SATA_PKT_TIMEOUT;
+			nv_complete_io(nvp, spkt, 0);
+			mutex_exit(&nvp->nvp_mutex);
+
+			return (SATA_TRAN_ACCEPTED);
+		}
+
+#if ! defined(__lock_lint)
+		if (nv_slotp->nvslot_flags == NVSLOT_COMPLETE) {
+			/*
+			 * packet is complete
+			 */
+			return (SATA_TRAN_ACCEPTED);
+		}
+#endif
+	}
+	/*NOTREACHED*/
+}
+
+
+/*
+ * Called by sata module to abort outstanding packets.
+ */
+/*ARGSUSED*/
+static int
+nv_sata_abort(dev_info_t *dip, sata_pkt_t *spkt, int flag)
+{
+	int cport = spkt->satapkt_device.satadev_addr.cport;
+	nv_ctl_t *nvc = ddi_get_soft_state(nv_statep, ddi_get_instance(dip));
+	nv_port_t *nvp = &(nvc->nvc_port[cport]);
+	int c_a, ret;
+
+	ASSERT(cport < NV_MAX_PORTS(nvc));
+	NVLOG((NVDBG_ENTRY, nvc, nvp, "nv_sata_abort %d %p", flag, spkt));
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	if (nvp->nvp_state & NV_PORT_INACTIVE) {
+		mutex_exit(&nvp->nvp_mutex);
+		nv_cmn_err(CE_WARN, nvc, nvp,
+		    "abort request failed: port inactive");
+
+		return (SATA_FAILURE);
+	}
+
+	/*
+	 * spkt == NULL then abort all commands
+	 */
+	c_a = nv_abort_active(nvp, spkt, SATA_PKT_ABORTED);
+
+	if (c_a) {
+		NVLOG((NVDBG_ENTRY, nvc, nvp,
+		    "packets aborted running=%d", c_a));
+		ret = SATA_SUCCESS;
+	} else {
+		if (spkt == NULL) {
+			NVLOG((NVDBG_ENTRY, nvc, nvp, "no spkts to abort"));
+		} else {
+			NVLOG((NVDBG_ENTRY, nvc, nvp,
+			    "can't find spkt to abort"));
+		}
+		ret = SATA_FAILURE;
+	}
+
+	mutex_exit(&nvp->nvp_mutex);
+
+	return (ret);
+}
+
+
+/*
+ * if spkt == NULL abort all pkts running, otherwise
+ * abort the requested packet.  must be called with nv_mutex
+ * held and returns with it held.  Not NCQ aware.
+ */
+static int
+nv_abort_active(nv_port_t *nvp, sata_pkt_t *spkt, int abort_reason)
+{
+	int aborted = 0, i, reset_once = B_FALSE;
+	struct nv_slot *nv_slotp;
+	sata_pkt_t *spkt_slot;
+
+	ASSERT(MUTEX_HELD(&nvp->nvp_mutex));
+
+	/*
+	 * return if the port is not configured
+	 */
+	if (nvp->nvp_slot == NULL) {
+		NVLOG((NVDBG_ENTRY, nvp->nvp_ctlp, nvp,
+		    "nv_abort_active: not configured so returning"));
+
+		return (0);
+	}
+
+	NVLOG((NVDBG_ENTRY, nvp->nvp_ctlp, nvp, "nv_abort_active"));
+
+	nvp->nvp_state |= NV_PORT_ABORTING;
+
+	for (i = 0; i < nvp->nvp_queue_depth; i++) {
+
+		nv_slotp = &(nvp->nvp_slot[i]);
+		spkt_slot = nv_slotp->nvslot_spkt;
+
+		/*
+		 * skip if not active command in slot
+		 */
+		if (spkt_slot == NULL) {
+			continue;
+		}
+
+		/*
+		 * if a specific packet was requested, skip if
+		 * this is not a match
+		 */
+		if ((spkt != NULL) && (spkt != spkt_slot)) {
+			continue;
+		}
+
+		/*
+		 * stop the hardware.  This could need reworking
+		 * when NCQ is enabled in the driver.
+		 */
+		if (reset_once == B_FALSE) {
+			ddi_acc_handle_t bmhdl = nvp->nvp_bm_hdl;
+
+			/*
+			 * stop DMA engine
+			 */
+			nv_put8(bmhdl, nvp->nvp_bmicx,  0);
+
+			nv_reset(nvp);
+			reset_once = B_TRUE;
+		}
+
+		spkt_slot->satapkt_reason = abort_reason;
+		nv_complete_io(nvp, spkt_slot, i);
+		aborted++;
+	}
+
+	nvp->nvp_state &= ~NV_PORT_ABORTING;
+
+	return (aborted);
+}
+
+
+/*
+ * Called by sata module to reset a port, device, or the controller.
+ */
+static int
+nv_sata_reset(dev_info_t *dip, sata_device_t *sd)
+{
+	int cport = sd->satadev_addr.cport;
+	nv_ctl_t *nvc = ddi_get_soft_state(nv_statep, ddi_get_instance(dip));
+	nv_port_t *nvp = &(nvc->nvc_port[cport]);
+	int ret = SATA_SUCCESS;
+
+	ASSERT(cport < NV_MAX_PORTS(nvc));
+
+	NVLOG((NVDBG_ENTRY, nvc, nvp, "nv_sata_reset"));
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	switch (sd->satadev_addr.qual) {
+
+	case SATA_ADDR_CPORT:
+		/*FALLTHROUGH*/
+	case SATA_ADDR_DCPORT:
+		nv_reset(nvp);
+		(void) nv_abort_active(nvp, NULL, SATA_PKT_RESET);
+
+		break;
+	case SATA_ADDR_CNTRL:
+		NVLOG((NVDBG_ENTRY, nvc, nvp,
+		    "nv_sata_reset: constroller reset not supported"));
+
+		break;
+	case SATA_ADDR_PMPORT:
+	case SATA_ADDR_DPMPORT:
+		NVLOG((NVDBG_ENTRY, nvc, nvp,
+		    "nv_sata_reset: port multipliers not supported"));
+		/*FALLTHROUGH*/
+	default:
+		/*
+		 * unsupported case
+		 */
+		ret = SATA_FAILURE;
+		break;
+	}
+
+	if (ret == SATA_SUCCESS) {
+		/*
+		 * If the port is inactive, do a quiet reset and don't attempt
+		 * to wait for reset completion or do any post reset processing
+		 */
+		if (nvp->nvp_state & NV_PORT_INACTIVE) {
+			nvp->nvp_state &= ~NV_PORT_RESET;
+			nvp->nvp_reset_time = 0;
+		}
+
+		/*
+		 * clear the port failed flag
+		 */
+		nvp->nvp_state &= ~NV_PORT_FAILED;
+	}
+
+	mutex_exit(&nvp->nvp_mutex);
+
+	return (ret);
+}
+
+
+/*
+ * Sata entry point to handle port activation.  cfgadm -c connect
+ */
+static int
+nv_sata_activate(dev_info_t *dip, sata_device_t *sd)
+{
+	int cport = sd->satadev_addr.cport;
+	nv_ctl_t *nvc = ddi_get_soft_state(nv_statep, ddi_get_instance(dip));
+	nv_port_t *nvp = &(nvc->nvc_port[cport]);
+
+	ASSERT(cport < NV_MAX_PORTS(nvc));
+	NVLOG((NVDBG_ENTRY, nvc, nvp, "nv_sata_activate"));
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	sd->satadev_state = SATA_STATE_READY;
+
+	nv_copy_registers(nvp, sd, NULL);
+
+	(*(nvc->nvc_set_intr))(nvp, NV_INTR_ENABLE);
+
+	nvp->nvp_state = 0;
+
+	mutex_exit(&nvp->nvp_mutex);
+
+	return (SATA_SUCCESS);
+}
+
+
+/*
+ * Sata entry point to handle port deactivation.  cfgadm -c disconnect
+ */
+static int
+nv_sata_deactivate(dev_info_t *dip, sata_device_t *sd)
+{
+	int cport = sd->satadev_addr.cport;
+	nv_ctl_t *nvc = ddi_get_soft_state(nv_statep, ddi_get_instance(dip));
+	nv_port_t *nvp = &(nvc->nvc_port[cport]);
+
+	ASSERT(cport < NV_MAX_PORTS(nvc));
+	NVLOG((NVDBG_ENTRY, nvc, nvp, "nv_sata_deactivate"));
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	(void) nv_abort_active(nvp, NULL, SATA_PKT_RESET);
+
+	/*
+	 * mark the device as inaccessible
+	 */
+	nvp->nvp_state &= ~NV_PORT_INACTIVE;
+
+	/*
+	 * disable the interrupts on port
+	 */
+	(*(nvc->nvc_set_intr))(nvp, NV_INTR_DISABLE);
+
+	nv_uninit_port(nvp);
+
+	sd->satadev_state = SATA_PSTATE_SHUTDOWN;
+	nv_copy_registers(nvp, sd, NULL);
+
+	mutex_exit(&nvp->nvp_mutex);
+
+	return (SATA_SUCCESS);
+}
+
+
+/*
+ * find an empty slot in the driver's queue, increment counters,
+ * and then invoke the appropriate PIO or DMA start routine.
+ */
+static int
+nv_start_common(nv_port_t *nvp, sata_pkt_t *spkt)
+{
+	sata_cmd_t *sata_cmdp = &spkt->satapkt_cmd;
+	int on_bit = 0x01, slot, sactive, ret, ncq = 0;
+	uint8_t cmd = spkt->satapkt_cmd.satacmd_cmd_reg;
+	int direction = sata_cmdp->satacmd_flags.sata_data_direction;
+	nv_ctl_t *nvc = nvp->nvp_ctlp;
+	nv_slot_t *nv_slotp;
+	boolean_t dma_cmd;
+
+	NVLOG((NVDBG_DELIVER, nvc, nvp, "nv_start_common  entered: cmd: 0x%x",
+	    sata_cmdp->satacmd_cmd_reg));
+
+	if ((cmd == SATAC_WRITE_FPDMA_QUEUED) ||
+	    (cmd == SATAC_READ_FPDMA_QUEUED)) {
+		nvp->nvp_ncq_run++;
+		/*
+		 * search for an empty NCQ slot.  by the time, it's already
+		 * been determined by the caller that there is room on the
+		 * queue.
+		 */
+		for (slot = 0; slot < nvp->nvp_queue_depth; slot++,
+		    on_bit <<= 1) {
+			if ((nvp->nvp_sactive_cache & on_bit) == 0) {
+				break;
+			}
+		}
+
+		/*
+		 * the first empty slot found, should not exceed the queue
+		 * depth of the drive.  if it does it's an error.
+		 */
+		ASSERT(slot != nvp->nvp_queue_depth);
+
+		sactive = nv_get32(nvc->nvc_bar_hdl[5],
+		    nvp->nvp_sactive);
+		ASSERT((sactive & on_bit) == 0);
+		nv_put32(nvc->nvc_bar_hdl[5], nvp->nvp_sactive, on_bit);
+		NVLOG((NVDBG_INIT, nvc, nvp, "setting SACTIVE onbit: %X",
+		    on_bit));
+		nvp->nvp_sactive_cache |= on_bit;
+
+		ncq = NVSLOT_NCQ;
+
+	} else {
+		nvp->nvp_non_ncq_run++;
+		slot = 0;
+	}
+
+	nv_slotp = (nv_slot_t *)&nvp->nvp_slot[slot];
+
+	ASSERT(nv_slotp->nvslot_spkt == NULL);
+
+	nv_slotp->nvslot_spkt = spkt;
+	nv_slotp->nvslot_flags = ncq;
+
+	/*
+	 * the sata module doesn't indicate which commands utilize the
+	 * DMA engine, so find out using this switch table.
+	 */
+	switch (spkt->satapkt_cmd.satacmd_cmd_reg) {
+	case SATAC_READ_DMA_EXT:
+	case SATAC_WRITE_DMA_EXT:
+	case SATAC_WRITE_DMA:
+	case SATAC_READ_DMA:
+	case SATAC_READ_DMA_QUEUED:
+	case SATAC_READ_DMA_QUEUED_EXT:
+	case SATAC_WRITE_DMA_QUEUED:
+	case SATAC_WRITE_DMA_QUEUED_EXT:
+	case SATAC_READ_FPDMA_QUEUED:
+	case SATAC_WRITE_FPDMA_QUEUED:
+		dma_cmd = B_TRUE;
+		break;
+	default:
+		dma_cmd = B_FALSE;
+	}
+
+	if (sata_cmdp->satacmd_num_dma_cookies != 0 && dma_cmd == B_TRUE) {
+		NVLOG((NVDBG_DELIVER, nvc,  nvp, "DMA command"));
+		nv_slotp->nvslot_start = nv_start_dma;
+		nv_slotp->nvslot_intr = nv_intr_dma;
+	} else if (direction == SATA_DIR_NODATA_XFER) {
+		NVLOG((NVDBG_DELIVER, nvc, nvp, "non-data command"));
+		nv_slotp->nvslot_start = nv_start_nodata;
+		nv_slotp->nvslot_intr = nv_intr_nodata;
+	} else if (direction == SATA_DIR_READ) {
+		NVLOG((NVDBG_DELIVER, nvc, nvp, "pio in command"));
+		nv_slotp->nvslot_start = nv_start_pio_in;
+		nv_slotp->nvslot_intr = nv_intr_pio_in;
+		nv_slotp->nvslot_byte_count =
+		    spkt->satapkt_cmd.satacmd_bp->b_bcount;
+		nv_slotp->nvslot_v_addr =
+		    spkt->satapkt_cmd.satacmd_bp->b_un.b_addr;
+	} else if (direction == SATA_DIR_WRITE) {
+		NVLOG((NVDBG_DELIVER, nvc, nvp, "pio out command"));
+		nv_slotp->nvslot_start = nv_start_pio_out;
+		nv_slotp->nvslot_intr = nv_intr_pio_out;
+		nv_slotp->nvslot_byte_count =
+		    spkt->satapkt_cmd.satacmd_bp->b_bcount;
+		nv_slotp->nvslot_v_addr =
+		    spkt->satapkt_cmd.satacmd_bp->b_un.b_addr;
+	} else {
+		nv_cmn_err(CE_WARN, nvc, nvp, "malformed command: direction"
+		    " %d cookies %d cmd %x",
+		    sata_cmdp->satacmd_flags.sata_data_direction,
+		    sata_cmdp->satacmd_num_dma_cookies,  cmd);
+		spkt->satapkt_reason = SATA_PKT_CMD_UNSUPPORTED;
+		ret = SATA_TRAN_CMD_UNSUPPORTED;
+
+		goto fail;
+	}
+
+	if ((ret = (*nv_slotp->nvslot_start)(nvp, slot)) ==
+	    SATA_TRAN_ACCEPTED) {
+		nv_slotp->nvslot_stime = ddi_get_lbolt();
+
+		/*
+		 * start timer if it's not already running and this packet
+		 * is not requesting polled mode.
+		 */
+		if ((nvp->nvp_timeout_id == 0) &&
+		    ((spkt->satapkt_op_mode & SATA_OPMODE_POLLING) == 0)) {
+			nvp->nvp_timeout_id = timeout(nv_timeout, (void *)nvp,
+			    drv_usectohz(NV_ONE_SEC));
+		}
+
+		return (SATA_TRAN_ACCEPTED);
+	}
+
+	fail:
+
+	spkt->satapkt_reason = SATA_TRAN_PORT_ERROR;
+
+	if (ncq == NVSLOT_NCQ) {
+		nvp->nvp_ncq_run--;
+		nvp->nvp_sactive_cache &= ~on_bit;
+	} else {
+		nvp->nvp_non_ncq_run--;
+	}
+	nv_slotp->nvslot_spkt = NULL;
+	nv_slotp->nvslot_flags = 0;
+
+	return (ret);
+}
+
+
+/*
+ * Check if the signature is ready and if non-zero translate
+ * it into a solaris sata defined type.
+ */
+static void
+nv_read_signature(nv_port_t *nvp)
+{
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+
+	nvp->nvp_signature = nv_get8(cmdhdl, nvp->nvp_count);
+	nvp->nvp_signature |= (nv_get8(cmdhdl, nvp->nvp_sect) << 8);
+	nvp->nvp_signature |= (nv_get8(cmdhdl, nvp->nvp_lcyl) << 16);
+	nvp->nvp_signature |= (nv_get8(cmdhdl, nvp->nvp_hcyl) << 24);
+
+	switch (nvp->nvp_signature) {
+
+	case NV_SIG_DISK:
+		NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp, "drive is a disk"));
+		nvp->nvp_type = SATA_DTYPE_ATADISK;
+		break;
+	case NV_SIG_ATAPI:
+		NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp,
+		    "drive is an optical device"));
+		nvp->nvp_type = SATA_DTYPE_ATAPICD;
+		break;
+	case NV_SIG_PM:
+		NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp,
+		    "device is a port multiplier"));
+		nvp->nvp_type = SATA_DTYPE_PMULT;
+		break;
+	case NV_SIG_NOTREADY:
+		NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp,
+		    "signature not ready"));
+		nvp->nvp_type = SATA_DTYPE_UNKNOWN;
+		break;
+	default:
+		nv_cmn_err(CE_WARN, nvp->nvp_ctlp, nvp, "signature %X not"
+		    " recognized", nvp->nvp_signature);
+		nvp->nvp_type = SATA_DTYPE_UNKNOWN;
+		break;
+	}
+
+	if (nvp->nvp_signature) {
+		nvp->nvp_state &= ~(NV_PORT_RESET_PROBE|NV_PORT_RESET);
+	}
+}
+
+
+/*
+ * Reset the port
+ */
+static void
+nv_reset(nv_port_t *nvp)
+{
+	ddi_acc_handle_t bar5_hdl = nvp->nvp_ctlp->nvc_bar_hdl[5];
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	nv_ctl_t *nvc = nvp->nvp_ctlp;
+	uint32_t sctrl;
+
+	NVLOG((NVDBG_ENTRY, nvc, nvp, "nv_reset()"));
+
+	ASSERT(mutex_owned(&nvp->nvp_mutex));
+
+	/*
+	 * clear signature registers
+	 */
+	nv_put8(cmdhdl, nvp->nvp_sect, 0);
+	nv_put8(cmdhdl, nvp->nvp_lcyl, 0);
+	nv_put8(cmdhdl, nvp->nvp_hcyl, 0);
+	nv_put8(cmdhdl, nvp->nvp_count, 0);
+
+	nvp->nvp_signature = 0;
+	nvp->nvp_type = 0;
+	nvp->nvp_state |= NV_PORT_RESET;
+	nvp->nvp_reset_time = ddi_get_lbolt();
+	nvp->nvp_link_lost_time = 0;
+
+	/*
+	 * assert reset in PHY by writing a 1 to bit 0 scontrol
+	 */
+	sctrl = nv_get32(bar5_hdl, nvp->nvp_sctrl);
+
+	nv_put32(bar5_hdl, nvp->nvp_sctrl, sctrl | SCONTROL_DET_COMRESET);
+
+	/*
+	 * wait 1ms
+	 */
+	drv_usecwait(1000);
+
+	/*
+	 * de-assert reset in PHY
+	 */
+	nv_put32(bar5_hdl, nvp->nvp_sctrl, sctrl);
+
+	/*
+	 * make sure timer is running
+	 */
+	if (nvp->nvp_timeout_id == 0) {
+		nvp->nvp_timeout_id = timeout(nv_timeout, (void *)nvp,
+		    drv_usectohz(NV_ONE_SEC));
+	}
+}
+
+
+/*
+ * Initialize register handling specific to mcp55
+ */
+/* ARGSUSED */
+static void
+mcp55_reg_init(nv_ctl_t *nvc, ddi_acc_handle_t pci_conf_handle)
+{
+	nv_port_t *nvp;
+	uchar_t *bar5  = nvc->nvc_bar_addr[5];
+	uint8_t off, port;
+
+	nvc->nvc_mcp55_ctl = (uint32_t *)(bar5 + MCP55_CTL);
+	nvc->nvc_mcp55_ncq = (uint32_t *)(bar5 + MCP55_NCQ);
+
+	for (port = 0, off = 0; port < NV_MAX_PORTS(nvc); port++, off += 2) {
+		nvp = &(nvc->nvc_port[port]);
+		nvp->nvp_mcp55_int_status =
+		    (uint16_t *)(bar5 + MCP55_INT_STATUS + off);
+		nvp->nvp_mcp55_int_ctl =
+		    (uint16_t *)(bar5 + MCP55_INT_CTL + off);
+
+		/*
+		 * clear any previous interrupts asserted
+		 */
+		nv_put16(nvc->nvc_bar_hdl[5], nvp->nvp_mcp55_int_status,
+		    MCP55_INT_CLEAR);
+
+		/*
+		 * These are the interrupts to accept for now.  The spec
+		 * says these are enable bits, but nvidia has indicated
+		 * these are masking bits.  Even though they may be masked
+		 * out to prevent asserting the main interrupt, they can
+		 * still be asserted while reading the interrupt status
+		 * register, so that needs to be considered in the interrupt
+		 * handler.
+		 */
+		nv_put16(nvc->nvc_bar_hdl[5], nvp->nvp_mcp55_int_ctl,
+		    ~(MCP55_INT_IGNORE));
+	}
+
+	/*
+	 * Allow the driver to program the BM on the first command instead
+	 * of waiting for an interrupt.
+	 */
+#ifdef NCQ
+	flags = MCP_SATA_AE_NCQ_PDEV_FIRST_CMD | MCP_SATA_AE_NCQ_SDEV_FIRST_CMD;
+	nv_put32(nvc->nvc_bar_hdl[5], nvc->nvc_mcp55_ncq, flags);
+	flags = MCP_SATA_AE_CTL_PRI_SWNCQ | MCP_SATA_AE_CTL_SEC_SWNCQ;
+	nv_put32(nvc->nvc_bar_hdl[5], nvc->nvc_mcp55_ctl, flags);
+#endif
+
+
+#if 0
+	/*
+	 * This caused problems on some but not all mcp55 based systems.
+	 * DMA writes would never complete.  This happens even on small
+	 * mem systems, and only setting NV_40BIT_PRD below and not
+	 * buffer_dma_attr.dma_attr_addr_hi, so it seems to be a hardware
+	 * issue that needs further investigation.
+	 */
+
+	/*
+	 * mcp55 rev A03 and above supports 40-bit physical addressing.
+	 * Enable DMA to take advantage of that.
+	 *
+	 */
+	if (nvc->nvc_revid >= 0xa3) {
+		uint32_t reg32;
+		NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp, "rev id is %X and"
+		    " is capable of 40-bit addressing", nvc->nvc_revid));
+		buffer_dma_attr.dma_attr_addr_hi = 0xffffffffffull;
+		reg32 = pci_config_get32(pci_conf_handle, NV_SATA_CFG_20);
+		pci_config_put32(pci_conf_handle, NV_SATA_CFG_20,
+		    reg32 |NV_40BIT_PRD);
+	} else {
+		NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp, "rev is %X and is "
+		    "not capable of 40-bit addressing", nvc->nvc_revid));
+	}
+#endif
+
+}
+
+
+/*
+ * Initialize register handling specific to mcp04
+ */
+static void
+mcp04_reg_init(nv_ctl_t *nvc, ddi_acc_handle_t pci_conf_handle)
+{
+	uchar_t *bar5  = nvc->nvc_bar_addr[5];
+	uint32_t reg32;
+	uint16_t reg16;
+	nv_port_t *nvp;
+	int j;
+
+	/*
+	 * delay hotplug interrupts until PHYRDY.
+	 */
+	reg32 = pci_config_get32(pci_conf_handle, NV_SATA_CFG_42);
+	pci_config_put32(pci_conf_handle, NV_SATA_CFG_42,
+	    reg32 | MCP04_CFG_DELAY_HOTPLUG_INTR);
+
+	/*
+	 * enable hot plug interrupts for channel x and y
+	 */
+	reg16 = nv_get16(nvc->nvc_bar_hdl[5],
+	    (uint16_t *)(bar5 + NV_ADMACTL_X));
+	nv_put16(nvc->nvc_bar_hdl[5], (uint16_t *)(bar5 + NV_ADMACTL_X),
+	    NV_HIRQ_EN | reg16);
+
+
+	reg16 = nv_get16(nvc->nvc_bar_hdl[5],
+	    (uint16_t *)(bar5 + NV_ADMACTL_Y));
+	nv_put16(nvc->nvc_bar_hdl[5], (uint16_t *)(bar5 + NV_ADMACTL_Y),
+	    NV_HIRQ_EN | reg16);
+
+	nvc->nvc_mcp04_int_status = (uint8_t *)(bar5 + MCP04_SATA_INT_STATUS);
+
+	/*
+	 * clear any existing interrupt pending then enable
+	 */
+	for (j = 0; j < NV_MAX_PORTS(nvc); j++) {
+		nvp = &(nvc->nvc_port[j]);
+		mutex_enter(&nvp->nvp_mutex);
+		(*(nvp->nvp_ctlp->nvc_set_intr))(nvp,
+		    NV_INTR_CLEAR_ALL|NV_INTR_ENABLE);
+		mutex_exit(&nvp->nvp_mutex);
+	}
+}
+
+
+/*
+ * Initialize the controller and set up driver data structures.
+ * determine if ck804 or mcp55 class.
+ */
+static int
+nv_init_ctl(nv_ctl_t *nvc, ddi_acc_handle_t pci_conf_handle)
+{
+	struct sata_hba_tran stran;
+	nv_port_t *nvp;
+	int j, ck804 = B_TRUE;
+	uchar_t *cmd_addr, *ctl_addr, *bm_addr;
+	ddi_acc_handle_t bar5_hdl = nvc->nvc_bar_hdl[5];
+	uchar_t *bar5  = nvc->nvc_bar_addr[5];
+	uint32_t reg32;
+	uint8_t reg8, reg8_save;
+
+	NVLOG((NVDBG_INIT, nvc, NULL, "nv_init_ctl entered"));
+
+	/*
+	 * Need to set bit 2 to 1 at config offset 0x50
+	 * to enable access to the bar5 registers.
+	 */
+	reg32 = pci_config_get32(pci_conf_handle, NV_SATA_CFG_20);
+	pci_config_put32(pci_conf_handle, NV_SATA_CFG_20,
+	    reg32 | NV_BAR5_SPACE_EN);
+
+	/*
+	 * Determine if this is ck804 or mcp55.  ck804 will map in the
+	 * task file registers into bar5 while mcp55 won't.  The offset of
+	 * the task file registers in mcp55's space is unused, so it will
+	 * return zero.  So check one of the task file registers to see if it is
+	 * writable and reads back what was written.  If it's mcp55 it will
+	 * return back 0xff whereas ck804 will return the value written.
+	 */
+	reg8_save = nv_get8(bar5_hdl,
+	    (uint8_t *)(bar5 + NV_BAR5_TRAN_LEN_CH_X));
+
+
+	for (j = 1; j < 3; j++) {
+
+		nv_put8(bar5_hdl, (uint8_t *)(bar5 + NV_BAR5_TRAN_LEN_CH_X), j);
+		reg8 = nv_get8(bar5_hdl,
+		    (uint8_t *)(bar5 + NV_BAR5_TRAN_LEN_CH_X));
+
+		if (reg8 != j) {
+			ck804 = B_FALSE;
+			break;
+		}
+	}
+
+	nv_put8(bar5_hdl, (uint8_t *)(bar5 + NV_BAR5_TRAN_LEN_CH_X), reg8_save);
+
+	if (ck804 == B_TRUE) {
+		NVLOG((NVDBG_INIT, nvc, NULL, "controller is CK804"));
+		nvc->nvc_interrupt = mcp04_intr;
+		nvc->nvc_reg_init = mcp04_reg_init;
+		nvc->nvc_set_intr = mcp04_set_intr;
+	} else {
+		NVLOG((NVDBG_INIT, nvc, NULL, "controller is MCP55"));
+		nvc->nvc_interrupt = mcp55_intr;
+		nvc->nvc_reg_init = mcp55_reg_init;
+		nvc->nvc_set_intr = mcp55_set_intr;
+	}
+
+
+	stran.sata_tran_hba_rev = SATA_TRAN_HBA_REV;
+	stran.sata_tran_hba_dip = nvc->nvc_dip;
+	stran.sata_tran_hba_dma_attr = &buffer_dma_attr;
+	stran.sata_tran_hba_num_cports = NV_NUM_CPORTS;
+	stran.sata_tran_hba_features_support =
+	    SATA_CTLF_HOTPLUG | SATA_CTLF_ASN;
+	stran.sata_tran_hba_qdepth = NV_QUEUE_SLOTS;
+	stran.sata_tran_probe_port = nv_sata_probe;
+	stran.sata_tran_start = nv_sata_start;
+	stran.sata_tran_abort = nv_sata_abort;
+	stran.sata_tran_reset_dport = nv_sata_reset;
+	stran.sata_tran_selftest = NULL;
+	stran.sata_tran_hotplug_ops = &nv_hotplug_ops;
+	stran.sata_tran_pwrmgt_ops = NULL;
+	stran.sata_tran_ioctl = NULL;
+	nvc->nvc_sata_hba_tran = stran;
+
+	nvc->nvc_port = kmem_zalloc(sizeof (nv_port_t) * NV_MAX_PORTS(nvc),
+	    KM_SLEEP);
+
+	/*
+	 * initialize registers common to all chipsets
+	 */
+	nv_common_reg_init(nvc);
+
+	for (j = 0; j < NV_MAX_PORTS(nvc); j++) {
+		nvp = &(nvc->nvc_port[j]);
+
+		cmd_addr = nvp->nvp_cmd_addr;
+		ctl_addr = nvp->nvp_ctl_addr;
+		bm_addr = nvp->nvp_bm_addr;
+
+		mutex_init(&nvp->nvp_mutex, NULL, MUTEX_DRIVER,
+		    DDI_INTR_PRI(nvc->nvc_intr_pri));
+
+		cv_init(&nvp->nvp_poll_cv, NULL, CV_DRIVER, NULL);
+
+		nvp->nvp_data	= cmd_addr + NV_DATA;
+		nvp->nvp_error	= cmd_addr + NV_ERROR;
+		nvp->nvp_feature = cmd_addr + NV_FEATURE;
+		nvp->nvp_count	= cmd_addr + NV_COUNT;
+		nvp->nvp_sect	= cmd_addr + NV_SECT;
+		nvp->nvp_lcyl	= cmd_addr + NV_LCYL;
+		nvp->nvp_hcyl	= cmd_addr + NV_HCYL;
+		nvp->nvp_drvhd	= cmd_addr + NV_DRVHD;
+		nvp->nvp_status	= cmd_addr + NV_STATUS;
+		nvp->nvp_cmd	= cmd_addr + NV_CMD;
+		nvp->nvp_altstatus = ctl_addr + NV_ALTSTATUS;
+		nvp->nvp_devctl	= ctl_addr + NV_DEVCTL;
+
+		nvp->nvp_bmicx	= bm_addr + BMICX_REG;
+		nvp->nvp_bmisx	= bm_addr + BMISX_REG;
+		nvp->nvp_bmidtpx = (uint32_t *)(bm_addr + BMIDTPX_REG);
+
+		nvp->nvp_state = 0;
+	}
+
+	/*
+	 * initialize register by calling chip specific reg initialization
+	 */
+	(*(nvc->nvc_reg_init))(nvc, pci_conf_handle);
+
+	return (NV_SUCCESS);
+}
+
+
+/*
+ * Initialize data structures with enough slots to handle queuing, if
+ * enabled.  NV_QUEUE_SLOTS will be set to 1 or 32, depending on whether
+ * NCQ support is built into the driver and enabled.  It might have been
+ * better to derive the true size from the drive itself, but the sata
+ * module only sends down that information on the first NCQ command,
+ * which means possibly re-sizing the structures on an interrupt stack,
+ * making error handling more messy.  The easy way is to just allocate
+ * all 32 slots, which is what most drives support anyway.
+ */
+static int
+nv_init_port(nv_port_t *nvp)
+{
+	nv_ctl_t *nvc = nvp->nvp_ctlp;
+	size_t	prd_size = sizeof (prde_t) * NV_DMA_NSEGS;
+	dev_info_t *dip = nvc->nvc_dip;
+	ddi_device_acc_attr_t dev_attr;
+	size_t buf_size;
+	ddi_dma_cookie_t cookie;
+	uint_t count;
+	int rc, i;
+
+	dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+	dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
+	dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+
+	if (nvp->nvp_state & NV_PORT_INIT) {
+		NVLOG((NVDBG_INIT, nvc, nvp,
+		    "nv_init_port previously initialized"));
+
+		return (NV_SUCCESS);
+	} else {
+		NVLOG((NVDBG_INIT, nvc, nvp, "nv_init_port initializing"));
+	}
+
+	nvp->nvp_sg_dma_hdl = kmem_zalloc(sizeof (ddi_dma_handle_t) *
+	    NV_QUEUE_SLOTS, KM_SLEEP);
+
+	nvp->nvp_sg_acc_hdl = kmem_zalloc(sizeof (ddi_acc_handle_t) *
+	    NV_QUEUE_SLOTS, KM_SLEEP);
+
+	nvp->nvp_sg_addr = kmem_zalloc(sizeof (caddr_t) *
+	    NV_QUEUE_SLOTS, KM_SLEEP);
+
+	nvp->nvp_sg_paddr = kmem_zalloc(sizeof (uint32_t) *
+	    NV_QUEUE_SLOTS, KM_SLEEP);
+
+	nvp->nvp_slot = kmem_zalloc(sizeof (nv_slot_t) * NV_QUEUE_SLOTS,
+	    KM_SLEEP);
+
+	for (i = 0; i < NV_QUEUE_SLOTS; i++) {
+
+		rc = ddi_dma_alloc_handle(dip, &nv_prd_dma_attr,
+		    DDI_DMA_SLEEP, NULL, &(nvp->nvp_sg_dma_hdl[i]));
+
+		if (rc != DDI_SUCCESS) {
+			nv_uninit_port(nvp);
+
+			return (NV_FAILURE);
+		}
+
+		rc = ddi_dma_mem_alloc(nvp->nvp_sg_dma_hdl[i], prd_size,
+		    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
+		    NULL, &(nvp->nvp_sg_addr[i]), &buf_size,
+		    &(nvp->nvp_sg_acc_hdl[i]));
+
+		if (rc != DDI_SUCCESS) {
+			nv_uninit_port(nvp);
+
+			return (NV_FAILURE);
+		}
+
+		rc = ddi_dma_addr_bind_handle(nvp->nvp_sg_dma_hdl[i], NULL,
+		    nvp->nvp_sg_addr[i], buf_size,
+		    DDI_DMA_WRITE | DDI_DMA_CONSISTENT,
+		    DDI_DMA_SLEEP, NULL, &cookie, &count);
+
+		if (rc != DDI_DMA_MAPPED) {
+			nv_uninit_port(nvp);
+
+			return (NV_FAILURE);
+		}
+
+		ASSERT(count == 1);
+		ASSERT((cookie.dmac_address & (sizeof (int) - 1)) == 0);
+
+		ASSERT(cookie.dmac_laddress <= UINT32_MAX);
+
+		nvp->nvp_sg_paddr[i] = cookie.dmac_address;
+	}
+
+	/*
+	 * nvp_queue_depth represents the actual drive queue depth, not the
+	 * number of slots allocated in the structures (which may be more).
+	 * Actual queue depth is only learned after the first NCQ command, so
+	 * initialize it to 1 for now.
+	 */
+	nvp->nvp_queue_depth = 1;
+
+	nvp->nvp_state |= NV_PORT_INIT;
+
+	return (NV_SUCCESS);
+}
+
+
+/*
+ * Free dynamically allocated structures for port.
+ */
+static void
+nv_uninit_port(nv_port_t *nvp)
+{
+	int i;
+
+	/*
+	 * It is possible to reach here before a port has been initialized or
+	 * after it has already been uninitialized.  Just return in that case.
+	 */
+	if (nvp->nvp_slot == NULL) {
+
+		return;
+	}
+
+	NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp,
+	    "nv_uninit_port uninitializing"));
+
+	nvp->nvp_type = SATA_DTYPE_NONE;
+
+	for (i = 0; i < NV_QUEUE_SLOTS; i++) {
+		if (nvp->nvp_sg_paddr[i]) {
+			(void) ddi_dma_unbind_handle(nvp->nvp_sg_dma_hdl[i]);
+		}
+
+		if (nvp->nvp_sg_acc_hdl[i] != NULL) {
+			ddi_dma_mem_free(&(nvp->nvp_sg_acc_hdl[i]));
+		}
+
+		if (nvp->nvp_sg_dma_hdl[i] != NULL) {
+			ddi_dma_free_handle(&(nvp->nvp_sg_dma_hdl[i]));
+		}
+	}
+
+	kmem_free(nvp->nvp_slot, sizeof (nv_slot_t) * NV_QUEUE_SLOTS);
+	nvp->nvp_slot = NULL;
+
+	kmem_free(nvp->nvp_sg_dma_hdl,
+	    sizeof (ddi_dma_handle_t) * NV_QUEUE_SLOTS);
+	nvp->nvp_sg_dma_hdl = NULL;
+
+	kmem_free(nvp->nvp_sg_acc_hdl,
+	    sizeof (ddi_acc_handle_t) * NV_QUEUE_SLOTS);
+	nvp->nvp_sg_acc_hdl = NULL;
+
+	kmem_free(nvp->nvp_sg_addr, sizeof (caddr_t) * NV_QUEUE_SLOTS);
+	nvp->nvp_sg_addr = NULL;
+
+	kmem_free(nvp->nvp_sg_paddr, sizeof (uint32_t) * NV_QUEUE_SLOTS);
+	nvp->nvp_sg_paddr = NULL;
+
+	nvp->nvp_state &= ~NV_PORT_INIT;
+	nvp->nvp_signature = 0;
+}
+
+
+/*
+ * Cache register offsets and access handles to frequently accessed registers
+ * which are common to either chipset.
+ */
+static void
+nv_common_reg_init(nv_ctl_t *nvc)
+{
+	uchar_t *bar5_addr = nvc->nvc_bar_addr[5];
+	uchar_t *bm_addr_offset, *sreg_offset;
+	uint8_t bar, port;
+	nv_port_t *nvp;
+
+	for (port = 0; port < NV_MAX_PORTS(nvc); port++) {
+		if (port == 0) {
+			bar = NV_BAR_0;
+			bm_addr_offset = 0;
+			sreg_offset = (uchar_t *)(CH0_SREG_OFFSET + bar5_addr);
+		} else {
+			bar = NV_BAR_2;
+			bm_addr_offset = (uchar_t *)8;
+			sreg_offset = (uchar_t *)(CH1_SREG_OFFSET + bar5_addr);
+		}
+
+		nvp = &(nvc->nvc_port[port]);
+		nvp->nvp_ctlp = nvc;
+		nvp->nvp_port_num = port;
+		NVLOG((NVDBG_INIT, nvc, nvp, "setting up port mappings"));
+
+		nvp->nvp_cmd_hdl = nvc->nvc_bar_hdl[bar];
+		nvp->nvp_cmd_addr = nvc->nvc_bar_addr[bar];
+		nvp->nvp_ctl_hdl = nvc->nvc_bar_hdl[bar + 1];
+		nvp->nvp_ctl_addr = nvc->nvc_bar_addr[bar + 1];
+		nvp->nvp_bm_hdl = nvc->nvc_bar_hdl[NV_BAR_4];
+		nvp->nvp_bm_addr = nvc->nvc_bar_addr[NV_BAR_4] +
+		    (long)bm_addr_offset;
+
+		nvp->nvp_sstatus = (uint32_t *)(sreg_offset + NV_SSTATUS);
+		nvp->nvp_serror = (uint32_t *)(sreg_offset + NV_SERROR);
+		nvp->nvp_sactive = (uint32_t *)(sreg_offset + NV_SACTIVE);
+		nvp->nvp_sctrl = (uint32_t *)(sreg_offset + NV_SCTRL);
+	}
+}
+
+
+static void
+nv_uninit_ctl(nv_ctl_t *nvc)
+{
+	int port;
+	nv_port_t *nvp;
+
+	NVLOG((NVDBG_INIT, nvc, NULL, "nv_uninit_ctl entered"));
+
+	for (port = 0; port < NV_MAX_PORTS(nvc); port++) {
+		nvp = &(nvc->nvc_port[port]);
+		mutex_enter(&nvp->nvp_mutex);
+		NVLOG((NVDBG_INIT, nvc, nvp, "uninitializing port"));
+		nv_uninit_port(nvp);
+		mutex_exit(&nvp->nvp_mutex);
+		mutex_destroy(&nvp->nvp_mutex);
+		cv_destroy(&nvp->nvp_poll_cv);
+	}
+
+	kmem_free(nvc->nvc_port, NV_MAX_PORTS(nvc) * sizeof (nv_port_t));
+	nvc->nvc_port = NULL;
+}
+
+
+/*
+ * mcp04 interrupt.  This is a wrapper around mcp04_intr_process so
+ * that interrupts from other devices can be disregarded while dtracing.
+ */
+/* ARGSUSED */
+static uint_t
+mcp04_intr(caddr_t arg1, caddr_t arg2)
+{
+	nv_ctl_t *nvc = (nv_ctl_t *)arg1;
+	uint8_t intr_status;
+	ddi_acc_handle_t bar5_hdl = nvc->nvc_bar_hdl[5];
+
+	intr_status = ddi_get8(bar5_hdl, nvc->nvc_mcp04_int_status);
+
+	if (intr_status == 0) {
+
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	mcp04_intr_process(nvc, intr_status);
+
+	return (DDI_INTR_CLAIMED);
+}
+
+
+/*
+ * Main interrupt handler for ck804.  handles normal device
+ * interrupts as well as port hot plug and remove interrupts.
+ *
+ */
+static void
+mcp04_intr_process(nv_ctl_t *nvc, uint8_t intr_status)
+{
+
+	int port, i;
+	nv_port_t *nvp;
+	nv_slot_t *nv_slotp;
+	uchar_t	status;
+	sata_pkt_t *spkt;
+	uint8_t bmstatus, clear_bits;
+	ddi_acc_handle_t bmhdl;
+	int nvcleared = 0;
+	ddi_acc_handle_t bar5_hdl = nvc->nvc_bar_hdl[5];
+	uint32_t sstatus;
+	int port_mask_hot[] = {
+		MCP04_INT_PDEV_HOT, MCP04_INT_SDEV_HOT,
+	};
+	int port_mask_pm[] = {
+		MCP04_INT_PDEV_PM, MCP04_INT_SDEV_PM,
+	};
+
+	NVLOG((NVDBG_INTR, nvc, NULL,
+	    "mcp04_intr_process entered intr_status=%x", intr_status));
+
+	/*
+	 * For command completion interrupt, explicit clear is not required.
+	 * however, for the error cases explicit clear is performed.
+	 */
+	for (port = 0; port < NV_MAX_PORTS(nvc); port++) {
+
+		int port_mask[] = {MCP04_INT_PDEV_INT, MCP04_INT_SDEV_INT};
+
+		if ((port_mask[port] & intr_status) == 0) {
+			continue;
+		}
+
+		NVLOG((NVDBG_INTR, nvc, NULL,
+		    "mcp04_intr_process interrupt on port %d", port));
+
+		nvp = &(nvc->nvc_port[port]);
+
+		mutex_enter(&nvp->nvp_mutex);
+
+		/*
+		 * there was a corner case found where an interrupt
+		 * arrived before nvp_slot was set.  Should
+		 * probably should track down why that happens and try
+		 * to eliminate that source and then get rid of this
+		 * check.
+		 */
+		if (nvp->nvp_slot == NULL) {
+			status = nv_get8(nvp->nvp_ctl_hdl, nvp->nvp_status);
+			NVLOG((NVDBG_ALWAYS, nvc, nvp, "spurious interrupt "
+			    "received before initialization "
+			    "completed status=%x", status));
+			mutex_exit(&nvp->nvp_mutex);
+
+			/*
+			 * clear interrupt bits
+			 */
+			nv_put8(bar5_hdl, nvc->nvc_mcp04_int_status,
+			    port_mask[port]);
+
+			continue;
+		}
+
+		if ((&(nvp->nvp_slot[0]))->nvslot_spkt == NULL)  {
+			status = nv_get8(nvp->nvp_ctl_hdl, nvp->nvp_status);
+			NVLOG((NVDBG_ALWAYS, nvc, nvp, "spurious interrupt "
+			    " no command in progress status=%x", status));
+			mutex_exit(&nvp->nvp_mutex);
+
+			/*
+			 * clear interrupt bits
+			 */
+			nv_put8(bar5_hdl, nvc->nvc_mcp04_int_status,
+			    port_mask[port]);
+
+			continue;
+		}
+
+		bmhdl = nvp->nvp_bm_hdl;
+		bmstatus = nv_get8(bmhdl, nvp->nvp_bmisx);
+
+		if (!(bmstatus & BMISX_IDEINTS)) {
+			mutex_exit(&nvp->nvp_mutex);
+
+			continue;
+		}
+
+		status = nv_get8(nvp->nvp_ctl_hdl, nvp->nvp_altstatus);
+
+		if (status & SATA_STATUS_BSY) {
+			mutex_exit(&nvp->nvp_mutex);
+
+			continue;
+		}
+
+		nv_slotp = &(nvp->nvp_slot[0]);
+
+		ASSERT(nv_slotp);
+
+		spkt = nv_slotp->nvslot_spkt;
+
+		if (spkt == NULL) {
+			mutex_exit(&nvp->nvp_mutex);
+
+			continue;
+		}
+
+		(*nv_slotp->nvslot_intr)(nvp, nv_slotp);
+
+		nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+
+		/*
+		 * If there is no link cannot be certain about the completion
+		 * of the packet, so abort it.
+		 */
+		if (nv_check_link((&spkt->satapkt_device)->
+		    satadev_scr.sstatus) == B_FALSE) {
+
+			(void) nv_abort_active(nvp, NULL, SATA_PKT_PORT_ERROR);
+
+		} else if (nv_slotp->nvslot_flags == NVSLOT_COMPLETE) {
+
+			nv_complete_io(nvp, spkt, 0);
+		}
+
+		mutex_exit(&nvp->nvp_mutex);
+	}
+
+	/*
+	 * mcp04 often doesn't correctly distinguish hot add/remove
+	 * interrupts.  Frequently both the ADD and the REMOVE bits
+	 * are asserted, whether it was a remove or add.  Use sstatus
+	 * to distinguish hot add from hot remove.
+	 */
+
+	for (port = 0; port < NV_MAX_PORTS(nvc); port++) {
+		clear_bits = 0;
+
+		nvp = &(nvc->nvc_port[port]);
+		mutex_enter(&nvp->nvp_mutex);
+
+		if ((port_mask_pm[port] & intr_status) != 0) {
+			clear_bits = port_mask_pm[port];
+			NVLOG((NVDBG_HOT, nvc, nvp,
+			    "clearing PM interrupt bit: %x",
+			    intr_status & port_mask_pm[port]));
+		}
+
+		if ((port_mask_hot[port] & intr_status) == 0) {
+			if (clear_bits != 0) {
+				goto clear;
+			} else {
+				mutex_exit(&nvp->nvp_mutex);
+				continue;
+			}
+		}
+
+		/*
+		 * reaching here means there was a hot add or remove.
+		 */
+		clear_bits |= port_mask_hot[port];
+
+		ASSERT(nvc->nvc_port[port].nvp_sstatus);
+
+		sstatus = nv_get32(bar5_hdl,
+		    nvc->nvc_port[port].nvp_sstatus);
+
+		if ((sstatus & SSTATUS_DET_DEVPRE_PHYCOM) ==
+		    SSTATUS_DET_DEVPRE_PHYCOM) {
+			nv_report_add_remove(nvp, 0);
+		} else {
+			nv_report_add_remove(nvp, NV_PORT_HOTREMOVED);
+		}
+	clear:
+		/*
+		 * clear interrupt bits.  explicit interrupt clear is
+		 * required for hotplug interrupts.
+		 */
+		nv_put8(bar5_hdl, nvc->nvc_mcp04_int_status, clear_bits);
+
+		/*
+		 * make sure it's flushed and cleared.  If not try
+		 * again.  Sometimes it has been observed to not clear
+		 * on the first try.
+		 */
+		intr_status = nv_get8(bar5_hdl, nvc->nvc_mcp04_int_status);
+
+		/*
+		 * make 10 additional attempts to clear the interrupt
+		 */
+		for (i = 0; (intr_status & clear_bits) && (i < 10); i++) {
+			NVLOG((NVDBG_ALWAYS, nvc, nvp, "inst_status=%x "
+			    "still not clear try=%d", intr_status,
+			    ++nvcleared));
+			nv_put8(bar5_hdl, nvc->nvc_mcp04_int_status,
+			    clear_bits);
+			intr_status = nv_get8(bar5_hdl,
+			    nvc->nvc_mcp04_int_status);
+		}
+
+		/*
+		 * if still not clear, log a message and disable the
+		 * port. highly unlikely that this path is taken, but it
+		 * gives protection against a wedged interrupt.
+		 */
+		if (intr_status & clear_bits) {
+			(*(nvc->nvc_set_intr))(nvp, NV_INTR_DISABLE);
+			nv_port_state_change(nvp, SATA_EVNT_PORT_FAILED,
+			    SATA_ADDR_CPORT, SATA_PSTATE_FAILED);
+			nvp->nvp_state |= NV_PORT_FAILED;
+			(void) nv_abort_active(nvp, NULL, SATA_PKT_DEV_ERROR);
+			nv_cmn_err(CE_WARN, nvc, nvp, "unable to clear "
+			    "interrupt.  disabling port intr_status=%X",
+			    intr_status);
+		}
+
+		mutex_exit(&nvp->nvp_mutex);
+	}
+}
+
+
+/*
+ * Interrupt handler for mcp55.  It is invoked by the wrapper for each port
+ * on the controller, to handle completion and hot plug and remove events.
+ *
+ */
+static uint_t
+mcp55_intr_port(nv_port_t *nvp)
+{
+	nv_ctl_t *nvc = nvp->nvp_ctlp;
+	ddi_acc_handle_t bar5_hdl = nvc->nvc_bar_hdl[5];
+	uint8_t clear = 0, intr_cycles = 0;
+	int ret = DDI_INTR_UNCLAIMED;
+	uint16_t int_status;
+
+	NVLOG((NVDBG_INTR, nvc, nvp, "mcp55_intr_port entered"));
+
+	for (;;) {
+		/*
+		 * read current interrupt status
+		 */
+		int_status = nv_get16(bar5_hdl, nvp->nvp_mcp55_int_status);
+
+		NVLOG((NVDBG_INTR, nvc, nvp, "int_status = %x", int_status));
+
+		/*
+		 * MCP55_INT_IGNORE interrupts will show up in the status,
+		 * but are masked out from causing an interrupt to be generated
+		 * to the processor.  Ignore them here by masking them out.
+		 */
+		int_status &= ~(MCP55_INT_IGNORE);
+
+		/*
+		 * exit the loop when no more interrupts to process
+		 */
+		if (int_status == 0) {
+
+			break;
+		}
+
+		if (int_status & MCP55_INT_COMPLETE) {
+			NVLOG((NVDBG_INTR, nvc, nvp,
+			    "mcp55_packet_complete_intr"));
+			/*
+			 * since int_status was set, return DDI_INTR_CLAIMED
+			 * from the DDI's perspective even though the packet
+			 * completion may not have succeeded.  If it fails,
+			 * need to manually clear the interrupt, otherwise
+			 * clearing is implicit.
+			 */
+			ret = DDI_INTR_CLAIMED;
+			if (mcp55_packet_complete_intr(nvc, nvp) ==
+			    NV_FAILURE) {
+				clear = MCP55_INT_COMPLETE;
+			}
+		}
+
+		if (int_status & MCP55_INT_DMA_SETUP) {
+			NVLOG((NVDBG_INTR, nvc, nvp, "mcp55_dma_setup_intr"));
+
+			/*
+			 * Needs to be cleared before starting the BM, so do it
+			 * now.  make sure this is still working.
+			 */
+			nv_put16(bar5_hdl, nvp->nvp_mcp55_int_status,
+			    MCP55_INT_DMA_SETUP);
+#ifdef NCQ
+			ret = mcp55_dma_setup_intr(nvc, nvp);
+#endif
+		}
+
+		if (int_status & MCP55_INT_REM) {
+			NVLOG((NVDBG_INTR, nvc, nvp, "mcp55 device removed"));
+			clear = MCP55_INT_REM;
+			ret = DDI_INTR_CLAIMED;
+
+			mutex_enter(&nvp->nvp_mutex);
+			nv_report_add_remove(nvp, NV_PORT_HOTREMOVED);
+			mutex_exit(&nvp->nvp_mutex);
+
+		} else if (int_status & MCP55_INT_ADD) {
+			NVLOG((NVDBG_HOT, nvc, nvp, "mcp55 device added"));
+			clear = MCP55_INT_ADD;
+			ret = DDI_INTR_CLAIMED;
+
+			mutex_enter(&nvp->nvp_mutex);
+			nv_report_add_remove(nvp, 0);
+			mutex_exit(&nvp->nvp_mutex);
+		}
+
+		if (clear) {
+			nv_put16(bar5_hdl, nvp->nvp_mcp55_int_status, clear);
+			clear = 0;
+		}
+
+		if (intr_cycles++ == NV_MAX_INTR_LOOP) {
+			nv_cmn_err(CE_WARN, nvc, nvp, "excessive interrupt "
+			    "processing.  Disabling port int_status=%X"
+			    " clear=%X", int_status, clear);
+			mutex_enter(&nvp->nvp_mutex);
+			(*(nvc->nvc_set_intr))(nvp, NV_INTR_DISABLE);
+			nv_port_state_change(nvp, SATA_EVNT_PORT_FAILED,
+			    SATA_ADDR_CPORT, SATA_PSTATE_FAILED);
+			nvp->nvp_state |= NV_PORT_FAILED;
+			(void) nv_abort_active(nvp, NULL, SATA_PKT_DEV_ERROR);
+			mutex_exit(&nvp->nvp_mutex);
+		}
+	}
+
+	NVLOG((NVDBG_INTR, nvc, nvp, "mcp55_intr_port: finished ret=%d", ret));
+
+	return (ret);
+}
+
+
+/* ARGSUSED */
+static uint_t
+mcp55_intr(caddr_t arg1, caddr_t arg2)
+{
+	nv_ctl_t *nvc = (nv_ctl_t *)arg1;
+	int ret;
+
+	ret = mcp55_intr_port(&(nvc->nvc_port[0]));
+	ret |= mcp55_intr_port(&(nvc->nvc_port[1]));
+
+	return (ret);
+}
+
+
+#ifdef NCQ
+/*
+ * with software driven NCQ on mcp55, an interrupt occurs right
+ * before the drive is ready to do a DMA transfer.  At this point,
+ * the PRD table needs to be programmed and the DMA engine enabled
+ * and ready to go.
+ *
+ * -- MCP_SATA_AE_INT_STATUS_SDEV_DMA_SETUP indicates the interrupt
+ * -- MCP_SATA_AE_NCQ_PDEV_DMA_SETUP_TAG shows which command is ready
+ * -- clear bit 0 of master command reg
+ * -- program PRD
+ * -- clear the interrupt status bit for the DMA Setup FIS
+ * -- set bit 0 of the bus master command register
+ */
+static int
+mcp55_dma_setup_intr(nv_ctl_t *nvc, nv_port_t *nvp)
+{
+	int slot;
+	ddi_acc_handle_t bmhdl = nvp->nvp_bm_hdl;
+	uint8_t bmicx;
+	int port = nvp->nvp_port_num;
+	uint8_t tag_shift[] = {MCP_SATA_AE_NCQ_PDEV_DMA_SETUP_TAG_SHIFT,
+	    MCP_SATA_AE_NCQ_SDEV_DMA_SETUP_TAG_SHIFT};
+
+	nv_cmn_err(CE_PANIC, nvc, nvp,
+		"this is should not be executed at all until NCQ");
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	slot = nv_get32(nvc->nvc_bar_hdl[5], nvc->nvc_mcp55_ncq);
+
+	slot = (slot >> tag_shift[port]) & MCP_SATA_AE_NCQ_DMA_SETUP_TAG_MASK;
+
+	NVLOG((NVDBG_INTR, nvc, nvp, "mcp55_dma_setup_intr slot %d"
+	    " nvp_slot_sactive %X", slot, nvp->nvp_sactive_cache));
+
+	/*
+	 * halt the DMA engine.  This step is necessary according to
+	 * the mcp55 spec, probably since there may have been a "first" packet
+	 * that already programmed the DMA engine, but may not turn out to
+	 * be the first one processed.
+	 */
+	bmicx = nv_get8(bmhdl, nvp->nvp_bmicx);
+
+#if 0
+	if (bmicx & BMICX_SSBM) {
+		NVLOG((NVDBG_INTR, nvc, nvp, "BM was already enabled for "
+		    "another packet.  Cancelling and reprogramming"));
+		nv_put8(bmhdl, nvp->nvp_bmicx,  bmicx & ~BMICX_SSBM);
+	}
+#endif
+	nv_put8(bmhdl, nvp->nvp_bmicx,  bmicx & ~BMICX_SSBM);
+
+	nv_start_dma_engine(nvp, slot);
+
+	mutex_exit(&nvp->nvp_mutex);
+
+	return (DDI_INTR_CLAIMED);
+}
+#endif /* NCQ */
+
+
+/*
+ * packet completion interrupt.  If the packet is complete, invoke
+ * the packet completion callback.
+ */
+static int
+mcp55_packet_complete_intr(nv_ctl_t *nvc, nv_port_t *nvp)
+{
+	uint8_t status, bmstatus;
+	ddi_acc_handle_t bmhdl = nvp->nvp_bm_hdl;
+	int sactive;
+	int active_pkt_bit = 0, active_pkt = 0, ncq_command = B_FALSE;
+	sata_pkt_t *spkt;
+	nv_slot_t *nv_slotp;
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	bmstatus = nv_get8(bmhdl, nvp->nvp_bmisx);
+
+	if (!(bmstatus & BMISX_IDEINTS)) {
+		NVLOG((NVDBG_INTR, nvc, nvp, "BMISX_IDEINTS not set"));
+		mutex_exit(&nvp->nvp_mutex);
+
+		return (NV_FAILURE);
+	}
+
+	/*
+	 * If the just completed item is a non-ncq command, the busy
+	 * bit should not be set
+	 */
+	if (nvp->nvp_non_ncq_run) {
+		status = nv_get8(nvp->nvp_ctl_hdl, nvp->nvp_altstatus);
+		if (status & SATA_STATUS_BSY) {
+			nv_cmn_err(CE_WARN, nvc, nvp,
+			    "unexpected SATA_STATUS_BSY set");
+			mutex_exit(&nvp->nvp_mutex);
+			/*
+			 * calling function will clear interrupt.  then
+			 * the real interrupt will either arrive or the
+			 * packet timeout handling will take over and
+			 * reset.
+			 */
+			return (NV_FAILURE);
+		}
+
+	} else {
+		/*
+		 * NCQ check for BSY here and wait if still bsy before
+		 * continuing. Rather than wait for it to be cleared
+		 * when starting a packet and wasting CPU time, the starting
+		 * thread can exit immediate, but might have to spin here
+		 * for a bit possibly.  Needs more work and experimentation.
+		 */
+		ASSERT(nvp->nvp_ncq_run);
+	}
+
+
+	if (nvp->nvp_ncq_run) {
+		ncq_command = B_TRUE;
+		ASSERT(nvp->nvp_non_ncq_run == 0);
+	} else {
+		ASSERT(nvp->nvp_non_ncq_run != 0);
+	}
+
+	/*
+	 * active_pkt_bit will represent the bitmap of the single completed
+	 * packet.  Because of the nature of sw assisted NCQ, only one
+	 * command will complete per interrupt.
+	 */
+
+	if (ncq_command == B_FALSE) {
+		active_pkt = 0;
+	} else {
+		/*
+		 * NCQ: determine which command just completed, by examining
+		 * which bit cleared in the register since last written.
+		 */
+		sactive = nv_get32(nvc->nvc_bar_hdl[5], nvp->nvp_sactive);
+
+		active_pkt_bit = ~sactive & nvp->nvp_sactive_cache;
+
+		ASSERT(active_pkt_bit);
+
+
+		/*
+		 * this failure path needs more work to handle the
+		 * error condition and recovery.
+		 */
+		if (active_pkt_bit == 0) {
+			ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+
+			nv_cmn_err(CE_CONT, nvc, nvp, "ERROR sactive = %X  "
+			    "nvp->nvp_sactive %X", sactive,
+			    nvp->nvp_sactive_cache);
+
+			(void) nv_get8(cmdhdl, nvp->nvp_status);
+
+			mutex_exit(&nvp->nvp_mutex);
+
+			return (NV_FAILURE);
+		}
+
+		for (active_pkt = 0; (active_pkt_bit & 0x1) != 0x1;
+		    active_pkt++, active_pkt_bit >>= 1) {
+		}
+
+		/*
+		 * make sure only one bit is ever turned on
+		 */
+		ASSERT(active_pkt_bit == 1);
+
+		nvp->nvp_sactive_cache &= ~(0x01 << active_pkt);
+	}
+
+	nv_slotp = &(nvp->nvp_slot[active_pkt]);
+
+	spkt = nv_slotp->nvslot_spkt;
+
+	ASSERT(spkt != NULL);
+
+	(*nv_slotp->nvslot_intr)(nvp, nv_slotp);
+
+	nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+
+	/*
+	 * If there is no link cannot be certain about the completion
+	 * of the packet, so abort it.
+	 */
+	if (nv_check_link((&spkt->satapkt_device)->
+	    satadev_scr.sstatus) == B_FALSE) {
+		(void) nv_abort_active(nvp, NULL, SATA_PKT_PORT_ERROR);
+
+	} else if (nv_slotp->nvslot_flags == NVSLOT_COMPLETE) {
+
+		nv_complete_io(nvp, spkt, active_pkt);
+	}
+
+	mutex_exit(&nvp->nvp_mutex);
+
+	return (NV_SUCCESS);
+}
+
+
+static void
+nv_complete_io(nv_port_t *nvp, sata_pkt_t *spkt, int slot)
+{
+
+	ASSERT(MUTEX_HELD(&nvp->nvp_mutex));
+
+	if ((&(nvp->nvp_slot[slot]))->nvslot_flags & NVSLOT_NCQ) {
+		nvp->nvp_ncq_run--;
+	} else {
+		nvp->nvp_non_ncq_run--;
+	}
+
+	/*
+	 * mark the packet slot idle so it can be reused.  Do this before
+	 * calling satapkt_comp so the slot can be reused.
+	 */
+	(&(nvp->nvp_slot[slot]))->nvslot_spkt = NULL;
+
+	if (spkt->satapkt_op_mode & SATA_OPMODE_SYNCH) {
+		/*
+		 * If this is not timed polled mode cmd, which has an
+		 * active thread monitoring for completion, then need
+		 * to signal the sleeping thread that the cmd is complete.
+		 */
+		if ((spkt->satapkt_op_mode & SATA_OPMODE_POLLING) == 0) {
+			cv_signal(&nvp->nvp_poll_cv);
+		}
+
+		return;
+	}
+
+	if (spkt->satapkt_comp != NULL) {
+		mutex_exit(&nvp->nvp_mutex);
+		(*spkt->satapkt_comp)(spkt);
+		mutex_enter(&nvp->nvp_mutex);
+	}
+}
+
+
+/*
+ * check whether packet is ncq command or not.  for ncq command,
+ * start it if there is still room on queue.  for non-ncq command only
+ * start if no other command is running.
+ */
+static int
+nv_start_async(nv_port_t *nvp, sata_pkt_t *spkt)
+{
+	uint8_t cmd, ncq;
+
+	NVLOG((NVDBG_ENTRY, nvp->nvp_ctlp, nvp, "nv_start_async: entry"));
+
+	cmd = spkt->satapkt_cmd.satacmd_cmd_reg;
+
+	ncq = ((cmd == SATAC_WRITE_FPDMA_QUEUED) ||
+	    (cmd == SATAC_READ_FPDMA_QUEUED));
+
+	if (ncq == B_FALSE) {
+
+		if ((nvp->nvp_non_ncq_run == 1) ||
+		    (nvp->nvp_ncq_run > 0)) {
+			/*
+			 * next command is non-ncq which can't run
+			 * concurrently.  exit and return queue full.
+			 */
+			spkt->satapkt_reason = SATA_PKT_QUEUE_FULL;
+
+			return (SATA_TRAN_QUEUE_FULL);
+		}
+
+		return (nv_start_common(nvp, spkt));
+	}
+
+	/*
+	 * ncq == B_TRUE
+	 */
+	if (nvp->nvp_non_ncq_run == 1) {
+		/*
+		 * cannot start any NCQ commands when there
+		 * is a non-NCQ command running.
+		 */
+		spkt->satapkt_reason = SATA_PKT_QUEUE_FULL;
+
+		return (SATA_TRAN_QUEUE_FULL);
+	}
+
+#ifdef NCQ
+	/*
+	 * this is not compiled for now as satapkt_device.satadev_qdepth
+	 * is being pulled out until NCQ support is later addressed
+	 *
+	 * nvp_queue_depth is initialized by the first NCQ command
+	 * received.
+	 */
+	if (nvp->nvp_queue_depth == 1) {
+		nvp->nvp_queue_depth =
+		    spkt->satapkt_device.satadev_qdepth;
+
+		ASSERT(nvp->nvp_queue_depth > 1);
+
+		NVLOG((NVDBG_ENTRY, nvp->nvp_ctlp, nvp,
+		    "nv_process_queue: nvp_queue_depth set to %d",
+		    nvp->nvp_queue_depth));
+	}
+#endif
+
+	if (nvp->nvp_ncq_run >= nvp->nvp_queue_depth) {
+		/*
+		 * max number of NCQ commands already active
+		 */
+		spkt->satapkt_reason = SATA_PKT_QUEUE_FULL;
+
+		return (SATA_TRAN_QUEUE_FULL);
+	}
+
+	return (nv_start_common(nvp, spkt));
+}
+
+
+/*
+ * configure INTx and legacy interrupts
+ */
+static int
+nv_add_legacy_intrs(nv_ctl_t *nvc)
+{
+	dev_info_t	*devinfo = nvc->nvc_dip;
+	int		actual, count = 0;
+	int		x, y, rc, inum = 0;
+
+	NVLOG((NVDBG_ENTRY, nvc, NULL, "nv_add_legacy_intrs"));
+
+	/*
+	 * get number of interrupts
+	 */
+	rc = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_FIXED, &count);
+	if ((rc != DDI_SUCCESS) || (count == 0)) {
+		NVLOG((NVDBG_INTR, nvc, NULL,
+		    "ddi_intr_get_nintrs() failed, "
+		    "rc %d count %d", rc, count));
+
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * allocate an array of interrupt handles
+	 */
+	nvc->nvc_intr_size = count * sizeof (ddi_intr_handle_t);
+	nvc->nvc_htable = kmem_zalloc(nvc->nvc_intr_size, KM_SLEEP);
+
+	/*
+	 * call ddi_intr_alloc()
+	 */
+	rc = ddi_intr_alloc(devinfo, nvc->nvc_htable, DDI_INTR_TYPE_FIXED,
+	    inum, count, &actual, DDI_INTR_ALLOC_STRICT);
+
+	if ((rc != DDI_SUCCESS) || (actual == 0)) {
+		nv_cmn_err(CE_WARN, nvc, NULL,
+		    "ddi_intr_alloc() failed, rc %d", rc);
+		kmem_free(nvc->nvc_htable, nvc->nvc_intr_size);
+
+		return (DDI_FAILURE);
+	}
+
+	if (actual < count) {
+		nv_cmn_err(CE_WARN, nvc, NULL,
+		    "ddi_intr_alloc: requested: %d, received: %d",
+		    count, actual);
+
+		goto failure;
+	}
+
+	nvc->nvc_intr_cnt = actual;
+
+	/*
+	 * get intr priority
+	 */
+	if (ddi_intr_get_pri(nvc->nvc_htable[0], &nvc->nvc_intr_pri) !=
+	    DDI_SUCCESS) {
+		nv_cmn_err(CE_WARN, nvc, NULL, "ddi_intr_get_pri() failed");
+
+		goto failure;
+	}
+
+	/*
+	 * Test for high level mutex
+	 */
+	if (nvc->nvc_intr_pri >= ddi_intr_get_hilevel_pri()) {
+		nv_cmn_err(CE_WARN, nvc, NULL,
+		    "nv_add_legacy_intrs: high level intr not supported");
+
+		goto failure;
+	}
+
+	for (x = 0; x < actual; x++) {
+		if (ddi_intr_add_handler(nvc->nvc_htable[x],
+		    nvc->nvc_interrupt, (caddr_t)nvc, NULL) != DDI_SUCCESS) {
+			nv_cmn_err(CE_WARN, nvc, NULL,
+			    "ddi_intr_add_handler() failed");
+
+			goto failure;
+		}
+	}
+
+	/*
+	 * call ddi_intr_enable() for legacy interrupts
+	 */
+	for (x = 0; x < nvc->nvc_intr_cnt; x++) {
+		(void) ddi_intr_enable(nvc->nvc_htable[x]);
+	}
+
+	return (DDI_SUCCESS);
+
+	failure:
+	/*
+	 * free allocated intr and nvc_htable
+	 */
+	for (y = 0; y < actual; y++) {
+		(void) ddi_intr_free(nvc->nvc_htable[y]);
+	}
+
+	kmem_free(nvc->nvc_htable, nvc->nvc_intr_size);
+
+	return (DDI_FAILURE);
+}
+
+#ifdef	NV_MSI_SUPPORTED
+/*
+ * configure MSI interrupts
+ */
+static int
+nv_add_msi_intrs(nv_ctl_t *nvc)
+{
+	dev_info_t	*devinfo = nvc->nvc_dip;
+	int		count, avail, actual;
+	int		x, y, rc, inum = 0;
+
+	NVLOG((NVDBG_ENTRY, nvc, NULL, "nv_add_msi_intrs"));
+
+	/*
+	 * get number of interrupts
+	 */
+	rc = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_MSI, &count);
+	if ((rc != DDI_SUCCESS) || (count == 0)) {
+		nv_cmn_err(CE_WARN, nvc, NULL,
+		    "ddi_intr_get_nintrs() failed, "
+		    "rc %d count %d", rc, count);
+
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * get number of available interrupts
+	 */
+	rc = ddi_intr_get_navail(devinfo, DDI_INTR_TYPE_MSI, &avail);
+	if ((rc != DDI_SUCCESS) || (avail == 0)) {
+		nv_cmn_err(CE_WARN, nvc, NULL,
+		    "ddi_intr_get_navail() failed, "
+		    "rc %d avail %d", rc, avail);
+
+		return (DDI_FAILURE);
+	}
+
+	if (avail < count) {
+		nv_cmn_err(CE_WARN, nvc, NULL,
+		    "ddi_intr_get_nvail returned %d ddi_intr_get_nintrs: %d",
+		    avail, count);
+	}
+
+	/*
+	 * allocate an array of interrupt handles
+	 */
+	nvc->nvc_intr_size = count * sizeof (ddi_intr_handle_t);
+	nvc->nvc_htable = kmem_alloc(nvc->nvc_intr_size, KM_SLEEP);
+
+	rc = ddi_intr_alloc(devinfo, nvc->nvc_htable, DDI_INTR_TYPE_MSI,
+	    inum, count, &actual, DDI_INTR_ALLOC_NORMAL);
+
+	if ((rc != DDI_SUCCESS) || (actual == 0)) {
+		nv_cmn_err(CE_WARN, nvc, NULL,
+		    "ddi_intr_alloc() failed, rc %d", rc);
+		kmem_free(nvc->nvc_htable, nvc->nvc_intr_size);
+
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Use interrupt count returned or abort?
+	 */
+	if (actual < count) {
+		NVLOG((NVDBG_INIT, nvc, NULL,
+		    "Requested: %d, Received: %d", count, actual));
+	}
+
+	nvc->nvc_intr_cnt = actual;
+
+	/*
+	 * get priority for first msi, assume remaining are all the same
+	 */
+	if (ddi_intr_get_pri(nvc->nvc_htable[0], &nvc->nvc_intr_pri) !=
+	    DDI_SUCCESS) {
+		nv_cmn_err(CE_WARN, nvc, NULL, "ddi_intr_get_pri() failed");
+
+		goto failure;
+	}
+
+	/*
+	 * test for high level mutex
+	 */
+	if (nvc->nvc_intr_pri >= ddi_intr_get_hilevel_pri()) {
+		nv_cmn_err(CE_WARN, nvc, NULL,
+		    "nv_add_msi_intrs: high level intr not supported");
+
+		goto failure;
+	}
+
+	/*
+	 * Call ddi_intr_add_handler()
+	 */
+	for (x = 0; x < actual; x++) {
+		if (ddi_intr_add_handler(nvc->nvc_htable[x],
+		    nvc->nvc_interrupt, (caddr_t)nvc, NULL) != DDI_SUCCESS) {
+			nv_cmn_err(CE_WARN, nvc, NULL,
+			    "ddi_intr_add_handler() failed");
+
+			goto failure;
+		}
+	}
+
+	(void) ddi_intr_get_cap(nvc->nvc_htable[0], &nvc->nvc_intr_cap);
+
+	if (nvc->nvc_intr_cap & DDI_INTR_FLAG_BLOCK) {
+		(void) ddi_intr_block_enable(nvc->nvc_htable,
+		    nvc->nvc_intr_cnt);
+	} else {
+		/*
+		 * Call ddi_intr_enable() for MSI non block enable
+		 */
+		for (x = 0; x < nvc->nvc_intr_cnt; x++) {
+			(void) ddi_intr_enable(nvc->nvc_htable[x]);
+		}
+	}
+
+	return (DDI_SUCCESS);
+
+	failure:
+	/*
+	 * free allocated intr and nvc_htable
+	 */
+	for (y = 0; y < actual; y++) {
+		(void) ddi_intr_free(nvc->nvc_htable[y]);
+	}
+
+	kmem_free(nvc->nvc_htable, nvc->nvc_intr_size);
+
+	return (DDI_FAILURE);
+}
+#endif
+
+
+static void
+nv_rem_intrs(nv_ctl_t *nvc)
+{
+	int x, i;
+	nv_port_t *nvp;
+
+	NVLOG((NVDBG_ENTRY, nvc, NULL, "nv_rem_intrs"));
+
+	/*
+	 * prevent controller from generating interrupts by
+	 * masking them out.  This is an extra precaution.
+	 */
+	for (i = 0; i < NV_MAX_PORTS(nvc); i++) {
+		nvp = (&nvc->nvc_port[i]);
+		mutex_enter(&nvp->nvp_mutex);
+		(*(nvc->nvc_set_intr))(nvp, NV_INTR_DISABLE);
+		mutex_exit(&nvp->nvp_mutex);
+	}
+
+	/*
+	 * disable all interrupts
+	 */
+	if ((nvc->nvc_intr_type == DDI_INTR_TYPE_MSI) &&
+	    (nvc->nvc_intr_cap & DDI_INTR_FLAG_BLOCK)) {
+		(void) ddi_intr_block_disable(nvc->nvc_htable,
+		    nvc->nvc_intr_cnt);
+	} else {
+		for (x = 0; x < nvc->nvc_intr_cnt; x++) {
+			(void) ddi_intr_disable(nvc->nvc_htable[x]);
+		}
+	}
+
+	for (x = 0; x < nvc->nvc_intr_cnt; x++) {
+		(void) ddi_intr_remove_handler(nvc->nvc_htable[x]);
+		(void) ddi_intr_free(nvc->nvc_htable[x]);
+	}
+
+	kmem_free(nvc->nvc_htable, nvc->nvc_intr_size);
+}
+
+
+/*
+ * variable argument wrapper for cmn_err.  prefixes the instance and port
+ * number if possible
+ */
+static void
+nv_vcmn_err(int ce, nv_ctl_t *nvc, nv_port_t *nvp, char *fmt, va_list ap)
+{
+	char port[NV_STRING_10];
+	char inst[NV_STRING_10];
+
+	mutex_enter(&nv_log_mutex);
+
+	if (nvc) {
+		(void) snprintf(inst, NV_STRING_10, "inst %d",
+		    ddi_get_instance(nvc->nvc_dip));
+	} else {
+		inst[0] = '\0';
+	}
+
+	if (nvp) {
+		(void) sprintf(port, " port %d", nvp->nvp_port_num);
+	} else {
+		port[0] = '\0';
+	}
+
+	(void) sprintf(nv_log_buf, "nv_sata %s%s%s", inst, port,
+	    (inst[0]|port[0] ? ": " :""));
+
+	(void) vsnprintf(&nv_log_buf[strlen(nv_log_buf)],
+	    NV_STRING_512 - strlen(nv_log_buf), fmt, ap);
+
+	/*
+	 * normally set to log to console but in some debug situations it
+	 * may be useful to log only to a file.
+	 */
+	if (nv_log_to_console) {
+		if (nv_prom_print) {
+			prom_printf("%s\n", nv_log_buf);
+		} else {
+			cmn_err(ce, "%s", nv_log_buf);
+		}
+
+
+	} else {
+		cmn_err(ce, "!%s", nv_log_buf);
+	}
+
+	mutex_exit(&nv_log_mutex);
+}
+
+
+/*
+ * wrapper for cmn_err
+ */
+static void
+nv_cmn_err(int ce, nv_ctl_t *nvc, nv_port_t *nvp, char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	nv_vcmn_err(ce, nvc, nvp, fmt, ap);
+	va_end(ap);
+}
+
+
+#if defined(DEBUG)
+/*
+ * prefixes the instance and port number if possible to the debug message
+ */
+static void
+nv_log(uint_t flag, nv_ctl_t *nvc, nv_port_t *nvp, char *fmt, ...)
+{
+	va_list ap;
+
+	if ((nv_debug_flags & flag) == 0) {
+		return;
+	}
+
+	va_start(ap, fmt);
+	nv_vcmn_err(CE_NOTE, nvc, nvp, fmt, ap);
+	va_end(ap);
+
+	/*
+	 * useful for some debugging situations
+	 */
+	if (nv_log_delay) {
+		drv_usecwait(nv_log_delay);
+	}
+
+}
+#endif /* DEBUG */
+
+
+/*
+ * program registers which are common to all commands
+ */
+static void
+nv_program_taskfile_regs(nv_port_t *nvp, int slot)
+{
+	nv_slot_t *nv_slotp = &(nvp->nvp_slot[slot]);
+	sata_pkt_t *spkt;
+	sata_cmd_t *satacmd;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	uint8_t cmd, ncq = B_FALSE;
+
+	spkt = nv_slotp->nvslot_spkt;
+	satacmd = &spkt->satapkt_cmd;
+	cmd = satacmd->satacmd_cmd_reg;
+
+	ASSERT(nvp->nvp_slot);
+
+	if ((cmd == SATAC_WRITE_FPDMA_QUEUED) ||
+	    (cmd == SATAC_READ_FPDMA_QUEUED)) {
+		ncq = B_TRUE;
+	}
+
+	/*
+	 * select the drive
+	 */
+	nv_put8(cmdhdl, nvp->nvp_drvhd, satacmd->satacmd_device_reg);
+
+	/*
+	 * make certain the drive selected
+	 */
+	if (nv_wait(nvp, SATA_STATUS_DRDY, SATA_STATUS_BSY,
+	    NV_SEC2USEC(5), 0) == B_FALSE) {
+
+		return;
+	}
+
+	switch (spkt->satapkt_cmd.satacmd_addr_type) {
+
+	case ATA_ADDR_LBA:
+		NVLOG((NVDBG_DELIVER, nvp->nvp_ctlp, nvp, "ATA_ADDR_LBA mode"));
+
+		nv_put8(cmdhdl, nvp->nvp_count, satacmd->satacmd_sec_count_lsb);
+		nv_put8(cmdhdl, nvp->nvp_hcyl, satacmd->satacmd_lba_high_lsb);
+		nv_put8(cmdhdl, nvp->nvp_lcyl, satacmd->satacmd_lba_mid_lsb);
+		nv_put8(cmdhdl, nvp->nvp_sect, satacmd->satacmd_lba_low_lsb);
+
+		break;
+
+	case ATA_ADDR_LBA28:
+		NVLOG((NVDBG_DELIVER, nvp->nvp_ctlp, nvp,
+		    "ATA_ADDR_LBA28 mode"));
+		/*
+		 * NCQ only uses 48-bit addressing
+		 */
+		ASSERT(ncq != B_TRUE);
+
+		nv_put8(cmdhdl, nvp->nvp_count, satacmd->satacmd_sec_count_lsb);
+		nv_put8(cmdhdl, nvp->nvp_hcyl, satacmd->satacmd_lba_high_lsb);
+		nv_put8(cmdhdl, nvp->nvp_lcyl, satacmd->satacmd_lba_mid_lsb);
+		nv_put8(cmdhdl, nvp->nvp_sect, satacmd->satacmd_lba_low_lsb);
+
+		break;
+
+	case ATA_ADDR_LBA48:
+		NVLOG((NVDBG_DELIVER, nvp->nvp_ctlp, nvp,
+		    "ATA_ADDR_LBA48 mode"));
+
+		/*
+		 * for NCQ, tag goes into count register and real sector count
+		 * into features register.  The sata module does the translation
+		 * in the satacmd.
+		 */
+		if (ncq == B_TRUE) {
+			nv_put8(cmdhdl, nvp->nvp_count, slot << 3);
+			nv_put8(cmdhdl, nvp->nvp_feature,
+			    satacmd->satacmd_features_reg_ext);
+			nv_put8(cmdhdl, nvp->nvp_feature,
+			    satacmd->satacmd_features_reg);
+		} else {
+			nv_put8(cmdhdl, nvp->nvp_count,
+			    satacmd->satacmd_sec_count_msb);
+			nv_put8(cmdhdl, nvp->nvp_count,
+			    satacmd->satacmd_sec_count_lsb);
+		}
+
+		/*
+		 * send the high-order half first
+		 */
+		nv_put8(cmdhdl, nvp->nvp_hcyl, satacmd->satacmd_lba_high_msb);
+		nv_put8(cmdhdl, nvp->nvp_lcyl, satacmd->satacmd_lba_mid_msb);
+		nv_put8(cmdhdl, nvp->nvp_sect, satacmd->satacmd_lba_low_msb);
+		/*
+		 * Send the low-order half
+		 */
+		nv_put8(cmdhdl, nvp->nvp_hcyl, satacmd->satacmd_lba_high_lsb);
+		nv_put8(cmdhdl, nvp->nvp_lcyl, satacmd->satacmd_lba_mid_lsb);
+		nv_put8(cmdhdl, nvp->nvp_sect, satacmd->satacmd_lba_low_lsb);
+
+		break;
+
+	case 0:
+		/*
+		 * non-media access commands such as identify and features
+		 * take this path.
+		 */
+		nv_put8(cmdhdl, nvp->nvp_count, satacmd->satacmd_sec_count_lsb);
+		nv_put8(cmdhdl, nvp->nvp_feature,
+		    satacmd->satacmd_features_reg);
+		nv_put8(cmdhdl, nvp->nvp_hcyl, satacmd->satacmd_lba_high_lsb);
+		nv_put8(cmdhdl, nvp->nvp_lcyl, satacmd->satacmd_lba_mid_lsb);
+		nv_put8(cmdhdl, nvp->nvp_sect, satacmd->satacmd_lba_low_lsb);
+
+		break;
+
+	default:
+		break;
+	}
+
+	ASSERT(nvp->nvp_slot);
+}
+
+
+/*
+ * start a command that involves no media access
+ */
+static int
+nv_start_nodata(nv_port_t *nvp, int slot)
+{
+	nv_slot_t *nv_slotp = &(nvp->nvp_slot[slot]);
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	sata_cmd_t *sata_cmdp = &spkt->satapkt_cmd;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+
+	nv_program_taskfile_regs(nvp, slot);
+
+	/*
+	 * This next one sets the controller in motion
+	 */
+	nv_put8(cmdhdl, nvp->nvp_cmd, sata_cmdp->satacmd_cmd_reg);
+
+	return (SATA_TRAN_ACCEPTED);
+}
+
+
+int
+nv_bm_status_clear(nv_port_t *nvp)
+{
+	ddi_acc_handle_t bmhdl = nvp->nvp_bm_hdl;
+	uchar_t	status, ret;
+
+	/*
+	 * Get the current BM status
+	 */
+	ret = status = nv_get8(bmhdl, nvp->nvp_bmisx);
+
+	status = (status & BMISX_MASK) | BMISX_IDERR | BMISX_IDEINTS;
+
+	/*
+	 * Clear the latches (and preserve the other bits)
+	 */
+	nv_put8(bmhdl, nvp->nvp_bmisx, status);
+
+	return (ret);
+}
+
+
+/*
+ * program the bus master DMA engine with the PRD address for
+ * the active slot command, and start the DMA engine.
+ */
+static void
+nv_start_dma_engine(nv_port_t *nvp, int slot)
+{
+	nv_slot_t *nv_slotp = &(nvp->nvp_slot[slot]);
+	ddi_acc_handle_t bmhdl = nvp->nvp_bm_hdl;
+	uchar_t direction;
+
+	ASSERT(nv_slotp->nvslot_spkt != NULL);
+
+	if (nv_slotp->nvslot_spkt->satapkt_cmd.satacmd_flags.sata_data_direction
+	    == SATA_DIR_READ) {
+		direction = BMICX_RWCON_WRITE_TO_MEMORY;
+	} else {
+		direction = BMICX_RWCON_READ_FROM_MEMORY;
+	}
+
+	NVLOG((NVDBG_DELIVER, nvp->nvp_ctlp, nvp,
+	    "nv_start_dma_engine entered"));
+
+	/*
+	 * reset the controller's interrupt and error status bits
+	 */
+	(void) nv_bm_status_clear(nvp);
+
+	/*
+	 * program the PRD table physical start address
+	 */
+	nv_put32(bmhdl, nvp->nvp_bmidtpx, nvp->nvp_sg_paddr[slot]);
+
+	/*
+	 * set the direction control and start the DMA controller
+	 */
+	nv_put8(bmhdl, nvp->nvp_bmicx, direction | BMICX_SSBM);
+}
+
+/*
+ * start dma command, either in or out
+ */
+static int
+nv_start_dma(nv_port_t *nvp, int slot)
+{
+	nv_slot_t *nv_slotp = &(nvp->nvp_slot[slot]);
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	sata_cmd_t *sata_cmdp = &spkt->satapkt_cmd;
+	uint8_t cmd = sata_cmdp->satacmd_cmd_reg;
+#ifdef NCQ
+	uint8_t ncq = B_FALSE;
+#endif
+	ddi_acc_handle_t sghdl = nvp->nvp_sg_acc_hdl[slot];
+	uint_t *dstp = (uint_t *)nvp->nvp_sg_addr[slot];
+	int sg_count = sata_cmdp->satacmd_num_dma_cookies, idx;
+	ddi_dma_cookie_t  *srcp = sata_cmdp->satacmd_dma_cookie_list;
+
+	ASSERT(sg_count != 0);
+
+	if (sata_cmdp->satacmd_num_dma_cookies > NV_DMA_NSEGS) {
+		nv_cmn_err(CE_WARN, nvp->nvp_ctlp, nvp, "NV_DMA_NSEGS=%d <"
+		    " satacmd_num_dma_cookies=%d", NV_DMA_NSEGS,
+		    sata_cmdp->satacmd_num_dma_cookies);
+
+		return (NV_FAILURE);
+	}
+
+	nv_program_taskfile_regs(nvp, slot);
+
+	/*
+	 * start the drive in motion
+	 */
+	nv_put8(cmdhdl, nvp->nvp_cmd, cmd);
+
+	/*
+	 * the drive starts processing the transaction when the cmd register
+	 * is written.  This is done here before programming the DMA engine to
+	 * parallelize and save some time.  In the event that the drive is ready
+	 * before DMA, it will wait.
+	 */
+#ifdef NCQ
+	if ((cmd == SATAC_WRITE_FPDMA_QUEUED) ||
+	    (cmd == SATAC_READ_FPDMA_QUEUED)) {
+		ncq = B_TRUE;
+	}
+#endif
+
+	/*
+	 * copy the PRD list to PRD table in DMA accessible memory
+	 * so that the controller can access it.
+	 */
+	for (idx = 0; idx < sg_count; idx++, srcp++) {
+		uint32_t size;
+
+		ASSERT(srcp->dmac_size <= UINT16_MAX);
+
+		nv_put32(sghdl, dstp++, srcp->dmac_address);
+
+		size = srcp->dmac_size;
+
+		/*
+		 * If this is a 40-bit address, copy bits 32-40 of the
+		 * physical address to bits 16-24 of the PRD count.
+		 */
+		if (srcp->dmac_laddress > UINT32_MAX) {
+			size |= ((srcp->dmac_laddress & 0xff00000000) >> 16);
+		}
+
+		/*
+		 * set the end of table flag for the last entry
+		 */
+		if (idx == (sg_count - 1)) {
+			size |= PRDE_EOT;
+		}
+
+		nv_put32(sghdl, dstp++, size);
+	}
+
+	(void) ddi_dma_sync(nvp->nvp_sg_dma_hdl[slot], 0,
+	    sizeof (prde_t) * NV_DMA_NSEGS, DDI_DMA_SYNC_FORDEV);
+
+	nv_start_dma_engine(nvp, slot);
+
+#ifdef NCQ
+	/*
+	 * optimization:  for SWNCQ, start DMA engine if this is the only
+	 * command running.  Preliminary NCQ efforts indicated this needs
+	 * more debugging.
+	 *
+	 * if (nvp->nvp_ncq_run <= 1)
+	 */
+
+	if (ncq == B_FALSE) {
+		NVLOG((NVDBG_DELIVER, nvp->nvp_ctlp, nvp,
+		    "NOT NCQ so starting DMA NOW non_ncq_commands=%d"
+		    " cmd = %X", non_ncq_commands++, cmd));
+		nv_start_dma_engine(nvp, slot);
+	} else {
+		NVLOG((NVDBG_DELIVER, nvp->nvp_ctlp, nvp, "?NCQ, so program "
+		    "DMA later ncq_commands=%d cmd = %X", ncq_commands++, cmd));
+	}
+#endif /* NCQ */
+
+	return (SATA_TRAN_ACCEPTED);
+}
+
+
+/*
+ * start a PIO data-in ATA command
+ */
+static int
+nv_start_pio_in(nv_port_t *nvp, int slot)
+{
+
+	nv_slot_t *nv_slotp = &(nvp->nvp_slot[slot]);
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+
+	nv_program_taskfile_regs(nvp, slot);
+
+	/*
+	 * This next one sets the drive in motion
+	 */
+	nv_put8(cmdhdl, nvp->nvp_cmd, spkt->satapkt_cmd.satacmd_cmd_reg);
+
+	return (SATA_TRAN_ACCEPTED);
+}
+
+
+/*
+ * start a PIO data-out ATA command
+ */
+static int
+nv_start_pio_out(nv_port_t *nvp, int slot)
+{
+	nv_slot_t *nv_slotp = &(nvp->nvp_slot[slot]);
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+
+	nv_program_taskfile_regs(nvp, slot);
+
+	/*
+	 * this next one sets the drive in motion
+	 */
+	nv_put8(cmdhdl, nvp->nvp_cmd, spkt->satapkt_cmd.satacmd_cmd_reg);
+
+	/*
+	 * wait for the busy bit to settle
+	 */
+	NV_DELAY_NSEC(400);
+
+	/*
+	 * wait for the drive to assert DRQ to send the first chunk
+	 * of data. Have to busy wait because there's no interrupt for
+	 * the first chunk. This is bad... uses a lot of cycles if the
+	 * drive responds too slowly or if the wait loop granularity
+	 * is too large. It's even worse if the drive is defective and
+	 * the loop times out.
+	 */
+	if (nv_wait3(nvp, SATA_STATUS_DRQ, SATA_STATUS_BSY, /* okay */
+	    SATA_STATUS_ERR, SATA_STATUS_BSY, /* cmd failed */
+	    SATA_STATUS_DF, SATA_STATUS_BSY, /* drive failed */
+	    4000000, 0) == B_FALSE) {
+		spkt->satapkt_reason = SATA_PKT_TIMEOUT;
+
+		goto error;
+	}
+
+	/*
+	 * send the first block.
+	 */
+	nv_intr_pio_out(nvp, nv_slotp);
+
+	/*
+	 * If nvslot_flags is not set to COMPLETE yet, then processing
+	 * is OK so far, so return.  Otherwise, fall into error handling
+	 * below.
+	 */
+	if (nv_slotp->nvslot_flags != NVSLOT_COMPLETE) {
+
+		return (SATA_TRAN_ACCEPTED);
+	}
+
+	error:
+	/*
+	 * there was an error so reset the device and complete the packet.
+	 */
+	nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+	nv_complete_io(nvp, spkt, 0);
+	nv_reset(nvp);
+
+	return (SATA_TRAN_PORT_ERROR);
+}
+
+
+/*
+ * Interrupt processing for a non-data ATA command.
+ */
+static void
+nv_intr_nodata(nv_port_t *nvp, nv_slot_t *nv_slotp)
+{
+	uchar_t status;
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	sata_cmd_t *sata_cmdp = &spkt->satapkt_cmd;
+	ddi_acc_handle_t ctlhdl = nvp->nvp_ctl_hdl;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+
+	NVLOG((NVDBG_INTR, nvp->nvp_ctlp, nvp, "nv_intr_nodata entered"));
+
+	status = nv_get8(cmdhdl, nvp->nvp_status);
+
+	/*
+	 * check for errors
+	 */
+	if (status & (SATA_STATUS_DF | SATA_STATUS_ERR)) {
+		spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+		sata_cmdp->satacmd_status_reg = nv_get8(ctlhdl,
+		    nvp->nvp_altstatus);
+		sata_cmdp->satacmd_error_reg = nv_get8(cmdhdl, nvp->nvp_error);
+	} else {
+		spkt->satapkt_reason = SATA_PKT_COMPLETED;
+	}
+
+	nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+}
+
+
+/*
+ * ATA command, PIO data in
+ */
+static void
+nv_intr_pio_in(nv_port_t *nvp, nv_slot_t *nv_slotp)
+{
+	uchar_t	status;
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	sata_cmd_t *sata_cmdp = &spkt->satapkt_cmd;
+	ddi_acc_handle_t ctlhdl = nvp->nvp_ctl_hdl;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	int count;
+
+	status = nv_get8(cmdhdl, nvp->nvp_status);
+
+	if (status & SATA_STATUS_BSY) {
+		spkt->satapkt_reason = SATA_PKT_TIMEOUT;
+		nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+		sata_cmdp->satacmd_status_reg = nv_get8(ctlhdl,
+		    nvp->nvp_altstatus);
+		sata_cmdp->satacmd_error_reg = nv_get8(cmdhdl, nvp->nvp_error);
+		nv_reset(nvp);
+
+		return;
+	}
+
+	/*
+	 * check for errors
+	 */
+	if ((status & (SATA_STATUS_DRQ | SATA_STATUS_DF |
+	    SATA_STATUS_ERR)) != SATA_STATUS_DRQ) {
+		nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+		nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+		spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+
+		return;
+	}
+
+	/*
+	 * read the next chunk of data (if any)
+	 */
+	count = min(nv_slotp->nvslot_byte_count, NV_BYTES_PER_SEC);
+
+	/*
+	 * read count bytes
+	 */
+	ASSERT(count != 0);
+
+	ddi_rep_get16(cmdhdl, (ushort_t *)nv_slotp->nvslot_v_addr,
+	    (ushort_t *)nvp->nvp_data, (count >> 1), DDI_DEV_NO_AUTOINCR);
+
+	nv_slotp->nvslot_v_addr += count;
+	nv_slotp->nvslot_byte_count -= count;
+
+
+	if (nv_slotp->nvslot_byte_count != 0) {
+		/*
+		 * more to transfer.  Wait for next interrupt.
+		 */
+		return;
+	}
+
+	/*
+	 * transfer is complete. wait for the busy bit to settle.
+	 */
+	NV_DELAY_NSEC(400);
+
+	spkt->satapkt_reason = SATA_PKT_COMPLETED;
+	nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+}
+
+
+/*
+ * ATA command PIO data out
+ */
+static void
+nv_intr_pio_out(nv_port_t *nvp, nv_slot_t *nv_slotp)
+{
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	sata_cmd_t *sata_cmdp = &spkt->satapkt_cmd;
+	uchar_t status;
+	ddi_acc_handle_t ctlhdl = nvp->nvp_ctl_hdl;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	int count;
+
+	/*
+	 * clear the IRQ
+	 */
+	status = nv_get8(cmdhdl, nvp->nvp_status);
+
+	if (status & SATA_STATUS_BSY) {
+		/*
+		 * this should not happen
+		 */
+		spkt->satapkt_reason = SATA_PKT_TIMEOUT;
+		nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+		sata_cmdp->satacmd_status_reg = nv_get8(ctlhdl,
+		    nvp->nvp_altstatus);
+		sata_cmdp->satacmd_error_reg = nv_get8(cmdhdl, nvp->nvp_error);
+
+		return;
+	}
+
+	/*
+	 * check for errors
+	 */
+	if (status & (SATA_STATUS_DF | SATA_STATUS_ERR)) {
+		nv_copy_registers(nvp,  &spkt->satapkt_device, spkt);
+		nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+		spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+
+		return;
+	}
+
+	/*
+	 * this is the condition which signals the drive is
+	 * no longer ready to transfer.  Likely that the transfer
+	 * completed successfully, but check that byte_count is
+	 * zero.
+	 */
+	if ((status & SATA_STATUS_DRQ) == 0) {
+
+		if (nv_slotp->nvslot_byte_count == 0) {
+			/*
+			 * complete; successful transfer
+			 */
+			spkt->satapkt_reason = SATA_PKT_COMPLETED;
+		} else {
+			/*
+			 * error condition, incomplete transfer
+			 */
+			nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+			spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+		}
+		nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+
+		return;
+	}
+
+	/*
+	 * write the next chunk of data
+	 */
+	count = min(nv_slotp->nvslot_byte_count, NV_BYTES_PER_SEC);
+
+	/*
+	 * read or write count bytes
+	 */
+
+	ASSERT(count != 0);
+
+	ddi_rep_put16(cmdhdl, (ushort_t *)nv_slotp->nvslot_v_addr,
+	    (ushort_t *)nvp->nvp_data, (count >> 1), DDI_DEV_NO_AUTOINCR);
+
+	nv_slotp->nvslot_v_addr += count;
+	nv_slotp->nvslot_byte_count -= count;
+}
+
+
+/*
+ * ATA command, DMA data in/out
+ */
+static void
+nv_intr_dma(nv_port_t *nvp, struct nv_slot *nv_slotp)
+{
+	uchar_t status;
+	sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+	sata_cmd_t *sata_cmdp = &spkt->satapkt_cmd;
+	ddi_acc_handle_t ctlhdl = nvp->nvp_ctl_hdl;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	ddi_acc_handle_t bmhdl = nvp->nvp_bm_hdl;
+	uchar_t	bmicx;
+	uchar_t bm_status;
+
+	nv_slotp->nvslot_flags = NVSLOT_COMPLETE;
+
+	/*
+	 * stop DMA engine.
+	 */
+	bmicx = nv_get8(bmhdl, nvp->nvp_bmicx);
+	nv_put8(bmhdl, nvp->nvp_bmicx,  bmicx & ~BMICX_SSBM);
+
+	/*
+	 * get the status and clear the IRQ, and check for DMA error
+	 */
+	status = nv_get8(cmdhdl, nvp->nvp_status);
+
+	/*
+	 * check for drive errors
+	 */
+	if (status & (SATA_STATUS_DF | SATA_STATUS_ERR)) {
+		nv_copy_registers(nvp, &spkt->satapkt_device, spkt);
+		spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
+		(void) nv_bm_status_clear(nvp);
+
+		return;
+	}
+
+	bm_status = nv_bm_status_clear(nvp);
+
+	/*
+	 * check for bus master errors
+	 */
+	if (bm_status & BMISX_IDERR) {
+		spkt->satapkt_reason = SATA_PKT_RESET;
+		sata_cmdp->satacmd_status_reg = nv_get8(ctlhdl,
+		    nvp->nvp_altstatus);
+		sata_cmdp->satacmd_error_reg = nv_get8(cmdhdl, nvp->nvp_error);
+		nv_reset(nvp);
+
+		return;
+	}
+
+	spkt->satapkt_reason = SATA_PKT_COMPLETED;
+}
+
+
+/*
+ * Wait for a register of a controller to achieve a specific state.
+ * To return normally, all the bits in the first sub-mask must be ON,
+ * all the bits in the second sub-mask must be OFF.
+ * If timeout_usec microseconds pass without the controller achieving
+ * the desired bit configuration, return TRUE, else FALSE.
+ *
+ * hybrid waiting algorithm: if not in interrupt context, busy looping will
+ * occur for the first 250 us, then switch over to a sleeping wait.
+ *
+ */
+int
+nv_wait(nv_port_t *nvp, uchar_t onbits, uchar_t offbits, uint_t timeout_usec,
+    int type_wait)
+{
+	ddi_acc_handle_t ctlhdl = nvp->nvp_ctl_hdl;
+	hrtime_t end, cur, start_sleep, start;
+	int first_time = B_TRUE;
+	ushort_t val;
+
+	for (;;) {
+		val = nv_get8(ctlhdl, nvp->nvp_altstatus);
+
+		if ((val & onbits) == onbits && (val & offbits) == 0) {
+
+			return (B_TRUE);
+		}
+
+		cur = gethrtime();
+
+		/*
+		 * store the start time and calculate the end
+		 * time.  also calculate "start_sleep" which is
+		 * the point after which the driver will stop busy
+		 * waiting and change to sleep waiting.
+		 */
+		if (first_time) {
+			first_time = B_FALSE;
+			/*
+			 * start and end are in nanoseconds
+			 */
+			start = cur;
+			end = start + timeout_usec * 1000;
+			/*
+			 * add 1 ms to start
+			 */
+			start_sleep =  start + 250000;
+
+			if (servicing_interrupt()) {
+				type_wait = NV_NOSLEEP;
+			}
+		}
+
+		if (cur > end) {
+
+			break;
+		}
+
+		if ((type_wait != NV_NOSLEEP) && (cur > start_sleep)) {
+#if ! defined(__lock_lint)
+			delay(1);
+#endif
+		} else {
+			drv_usecwait(nv_usec_delay);
+		}
+	}
+
+	return (B_FALSE);
+}
+
+
+/*
+ * This is a slightly more complicated version that checks
+ * for error conditions and bails-out rather than looping
+ * until the timeout is exceeded.
+ *
+ * hybrid waiting algorithm: if not in interrupt context, busy looping will
+ * occur for the first 250 us, then switch over to a sleeping wait.
+ */
+int
+nv_wait3(
+	nv_port_t	*nvp,
+	uchar_t		onbits1,
+	uchar_t		offbits1,
+	uchar_t		failure_onbits2,
+	uchar_t		failure_offbits2,
+	uchar_t		failure_onbits3,
+	uchar_t		failure_offbits3,
+	uint_t		timeout_usec,
+	int		type_wait)
+{
+	ddi_acc_handle_t ctlhdl = nvp->nvp_ctl_hdl;
+	hrtime_t end, cur, start_sleep, start;
+	int first_time = B_TRUE;
+	ushort_t val;
+
+	for (;;) {
+		val = nv_get8(ctlhdl, nvp->nvp_altstatus);
+
+		/*
+		 * check for expected condition
+		 */
+		if ((val & onbits1) == onbits1 && (val & offbits1) == 0) {
+
+			return (B_TRUE);
+		}
+
+		/*
+		 * check for error conditions
+		 */
+		if ((val & failure_onbits2) == failure_onbits2 &&
+		    (val & failure_offbits2) == 0) {
+
+			return (B_FALSE);
+		}
+
+		if ((val & failure_onbits3) == failure_onbits3 &&
+		    (val & failure_offbits3) == 0) {
+
+			return (B_FALSE);
+		}
+
+		/*
+		 * store the start time and calculate the end
+		 * time.  also calculate "start_sleep" which is
+		 * the point after which the driver will stop busy
+		 * waiting and change to sleep waiting.
+		 */
+		if (first_time) {
+			first_time = B_FALSE;
+			/*
+			 * start and end are in nanoseconds
+			 */
+			cur = start = gethrtime();
+			end = start + timeout_usec * 1000;
+			/*
+			 * add 1 ms to start
+			 */
+			start_sleep =  start + 250000;
+
+			if (servicing_interrupt()) {
+				type_wait = NV_NOSLEEP;
+			}
+		} else {
+			cur = gethrtime();
+		}
+
+		if (cur > end) {
+
+			break;
+		}
+
+		if ((type_wait != NV_NOSLEEP) && (cur > start_sleep)) {
+#if ! defined(__lock_lint)
+			delay(1);
+#endif
+		} else {
+			drv_usecwait(nv_usec_delay);
+		}
+	}
+
+	return (B_FALSE);
+}
+
+
+/*
+ * nv_check_link() checks if a specified link is active device present
+ * and communicating.
+ */
+static boolean_t
+nv_check_link(uint32_t sstatus)
+{
+	uint8_t det;
+
+	det = (sstatus & SSTATUS_DET) >> SSTATUS_DET_SHIFT;
+
+	return (det == SSTATUS_DET_DEVPRE_PHYCOM);
+}
+
+
+/*
+ * nv_port_state_change() reports the state of the port to the
+ * sata module by calling sata_hba_event_notify().  This
+ * function is called any time the state of the port is changed
+ */
+static void
+nv_port_state_change(nv_port_t *nvp, int event, uint8_t addr_type, int state)
+{
+	sata_device_t sd;
+
+	bzero((void *)&sd, sizeof (sata_device_t));
+	sd.satadev_rev = SATA_DEVICE_REV;
+	nv_copy_registers(nvp, &sd, NULL);
+
+	/*
+	 * When NCQ is implemented sactive and snotific field need to be
+	 * updated.
+	 */
+	sd.satadev_addr.cport = nvp->nvp_port_num;
+	sd.satadev_addr.qual = addr_type;
+	sd.satadev_state = state;
+
+	sata_hba_event_notify(nvp->nvp_ctlp->nvc_dip, &sd, event);
+}
+
+
+/*
+ * timeout processing:
+ *
+ * Check if any packets have crossed a timeout threshold.  If so, then
+ * abort the packet.  This function is not NCQ aware.
+ *
+ * If reset was invoked in any other place than nv_sata_probe(), then
+ * monitor for reset completion here.
+ *
+ */
+static void
+nv_timeout(void *arg)
+{
+	nv_port_t *nvp = arg;
+	nv_slot_t *nv_slotp;
+	int restart_timeout = B_FALSE;
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	/*
+	 * If the probe entry point is driving the reset and signature
+	 * acquisition, just return.
+	 */
+	if (nvp->nvp_state & NV_PORT_RESET_PROBE) {
+		goto finished;
+	}
+
+	/*
+	 * If the port is not in the init state, it likely
+	 * means the link was lost while a timeout was active.
+	 */
+	if ((nvp->nvp_state & NV_PORT_INIT) == 0) {
+		NVLOG((NVDBG_TIMEOUT, nvp->nvp_ctlp, nvp,
+		    "nv_timeout: port uninitialized"));
+
+		goto finished;
+	}
+
+	if (nvp->nvp_state & NV_PORT_RESET) {
+		ddi_acc_handle_t bar5_hdl = nvp->nvp_ctlp->nvc_bar_hdl[5];
+		uint32_t sstatus;
+
+		NVLOG((NVDBG_TIMEOUT, nvp->nvp_ctlp, nvp,
+		    "nv_timeout(): port waiting for signature"));
+
+		sstatus = nv_get32(bar5_hdl, nvp->nvp_sstatus);
+
+		/*
+		 * check for link presence.  If the link remains
+		 * missing for more than 2 seconds, send a remove
+		 * event and abort signature acquisition.
+		 */
+		if (nv_check_link(sstatus) == B_FALSE) {
+			clock_t e_link_lost = ddi_get_lbolt();
+
+			if (nvp->nvp_link_lost_time == 0) {
+				nvp->nvp_link_lost_time = e_link_lost;
+			}
+			if (TICK_TO_SEC(e_link_lost -
+			    nvp->nvp_link_lost_time) < NV_LINK_LOST_OK) {
+				NVLOG((NVDBG_TIMEOUT, nvp->nvp_ctlp, nvp,
+				    "probe: intermittent link lost while"
+				    " resetting"));
+				restart_timeout = B_TRUE;
+			} else {
+				NVLOG((NVDBG_TIMEOUT, nvp->nvp_ctlp, nvp,
+				    "link lost during signature acquisition."
+				    "  Giving up"));
+				nv_port_state_change(nvp,
+				    SATA_EVNT_DEVICE_DETACHED|
+				    SATA_EVNT_LINK_LOST,
+				    SATA_ADDR_CPORT, 0);
+				nvp->nvp_state |= NV_PORT_HOTREMOVED;
+				nvp->nvp_state &= ~NV_PORT_RESET;
+			}
+
+			goto finished;
+		} else {
+
+			nvp->nvp_link_lost_time = 0;
+		}
+
+		nv_read_signature(nvp);
+
+		if (nvp->nvp_signature != 0) {
+			if (nvp->nvp_type == SATA_DTYPE_ATADISK) {
+				nvp->nvp_state |= NV_PORT_RESTORE;
+				nv_port_state_change(nvp,
+				    SATA_EVNT_DEVICE_RESET,
+				    SATA_ADDR_DCPORT,
+				    SATA_DSTATE_RESET|SATA_DSTATE_PWR_ACTIVE);
+			}
+
+			goto finished;
+		}
+
+		/*
+		 * Reset if more than 5 seconds has passed without
+		 * acquiring a signature.
+		 */
+		if (TICK_TO_SEC(ddi_get_lbolt() - nvp->nvp_reset_time) > 5) {
+			nv_reset(nvp);
+		}
+
+		restart_timeout = B_TRUE;
+		goto finished;
+	}
+
+
+	/*
+	 * not yet NCQ aware
+	 */
+	nv_slotp = &(nvp->nvp_slot[0]);
+
+	/*
+	 * this happens early on before nv_slotp is set
+	 * up OR when a device was unexpectedly removed and
+	 * there was an active packet.
+	 */
+	if (nv_slotp == NULL) {
+		NVLOG((NVDBG_TIMEOUT, nvp->nvp_ctlp, nvp,
+		    "nv_timeout: nv_slotp == NULL"));
+
+		goto finished;
+	}
+
+	/*
+	 * perform timeout checking and processing only if there is an
+	 * active packet on the port
+	 */
+	if (nv_slotp->nvslot_spkt != NULL)  {
+		sata_pkt_t *spkt = nv_slotp->nvslot_spkt;
+		sata_cmd_t *satacmd = &spkt->satapkt_cmd;
+		uint8_t cmd = satacmd->satacmd_cmd_reg;
+		uint64_t lba;
+
+#if ! defined(__lock_lint) && defined(DEBUG)
+
+		lba = (uint64_t)satacmd->satacmd_lba_low_lsb |
+		    ((uint64_t)satacmd->satacmd_lba_mid_lsb << 8) |
+		    ((uint64_t)satacmd->satacmd_lba_high_lsb << 16) |
+		    ((uint64_t)satacmd->satacmd_lba_low_msb << 24) |
+		    ((uint64_t)satacmd->satacmd_lba_mid_msb << 32) |
+		    ((uint64_t)satacmd->satacmd_lba_high_msb << 40);
+#endif
+
+		/*
+		 * timeout not needed if there is a polling thread
+		 */
+		if (spkt->satapkt_op_mode & SATA_OPMODE_POLLING) {
+
+			goto finished;
+		}
+
+		if (TICK_TO_SEC(ddi_get_lbolt() - nv_slotp->nvslot_stime) >
+		    spkt->satapkt_time) {
+			NVLOG((NVDBG_TIMEOUT, nvp->nvp_ctlp, nvp,
+			    "abort timeout: "
+			    "nvslot_stime: %ld max ticks till timeout: "
+			    "%ld cur_time: %ld cmd=%x lba=%d",
+			    nv_slotp->nvslot_stime, drv_usectohz(MICROSEC *
+			    spkt->satapkt_time), ddi_get_lbolt(), cmd, lba));
+
+			(void) nv_abort_active(nvp, spkt, SATA_PKT_TIMEOUT);
+
+		} else {
+			NVLOG((NVDBG_TIMEOUT, nvp->nvp_ctlp, nvp, "nv_timeout:"
+			    " still in use so restarting timeout"));
+		}
+		restart_timeout = B_TRUE;
+
+	} else {
+		/*
+		 * there was no active packet, so do not re-enable timeout
+		 */
+		NVLOG((NVDBG_TIMEOUT, nvp->nvp_ctlp, nvp,
+		    "nv_timeout: no active packet so not re-arming timeout"));
+	}
+
+	finished:
+
+	if (restart_timeout == B_TRUE) {
+		nvp->nvp_timeout_id = timeout(nv_timeout, (void *)nvp,
+		    drv_usectohz(NV_ONE_SEC));
+	} else {
+		nvp->nvp_timeout_id = 0;
+	}
+	mutex_exit(&nvp->nvp_mutex);
+}
+
+
+/*
+ * enable or disable the 3 interrupt types the driver is
+ * interested in: completion, add and remove.
+ */
+static void
+mcp04_set_intr(nv_port_t *nvp, int flag)
+{
+	nv_ctl_t *nvc = nvp->nvp_ctlp;
+	ddi_acc_handle_t bar5_hdl = nvc->nvc_bar_hdl[5];
+	uchar_t *bar5  = nvc->nvc_bar_addr[5];
+	uint8_t intr_bits[] = { MCP04_INT_PDEV_HOT|MCP04_INT_PDEV_INT,
+	    MCP04_INT_SDEV_HOT|MCP04_INT_SDEV_INT };
+	uint8_t clear_all_bits[] = { MCP04_INT_PDEV_ALL, MCP04_INT_SDEV_ALL };
+	uint8_t int_en, port = nvp->nvp_port_num, intr_status;
+
+	ASSERT(mutex_owned(&nvp->nvp_mutex));
+
+	/*
+	 * controller level lock also required since access to an 8-bit
+	 * interrupt register is shared between both channels.
+	 */
+	mutex_enter(&nvc->nvc_mutex);
+
+	if (flag & NV_INTR_CLEAR_ALL) {
+		NVLOG((NVDBG_INTR, nvc, nvp,
+		    "mcp04_set_intr: NV_INTR_CLEAR_ALL"));
+
+		intr_status = nv_get8(nvc->nvc_bar_hdl[5],
+		    (uint8_t *)(nvc->nvc_mcp04_int_status));
+
+		if (intr_status & clear_all_bits[port]) {
+
+			nv_put8(nvc->nvc_bar_hdl[5],
+			    (uint8_t *)(nvc->nvc_mcp04_int_status),
+			    clear_all_bits[port]);
+
+			NVLOG((NVDBG_INTR, nvc, nvp,
+			    "interrupt bits cleared %x",
+			    intr_status & clear_all_bits[port]));
+		}
+	}
+
+	if (flag & NV_INTR_DISABLE) {
+		NVLOG((NVDBG_INTR, nvc, nvp,
+		    "mcp04_set_intr: NV_INTR_DISABLE"));
+		int_en = nv_get8(bar5_hdl,
+		    (uint8_t *)(bar5 + MCP04_SATA_INT_EN));
+		int_en &= ~intr_bits[port];
+		nv_put8(bar5_hdl, (uint8_t *)(bar5 + MCP04_SATA_INT_EN),
+		    int_en);
+	}
+
+	if (flag & NV_INTR_ENABLE) {
+		NVLOG((NVDBG_INTR, nvc, nvp, "mcp04_set_intr: NV_INTR_ENABLE"));
+		int_en = nv_get8(bar5_hdl,
+		    (uint8_t *)(bar5 + MCP04_SATA_INT_EN));
+		int_en |= intr_bits[port];
+		nv_put8(bar5_hdl, (uint8_t *)(bar5 + MCP04_SATA_INT_EN),
+		    int_en);
+	}
+
+	mutex_exit(&nvc->nvc_mutex);
+}
+
+
+/*
+ * enable or disable the 3 interrupts the driver is interested in:
+ * completion interrupt, hot add, and hot remove interrupt.
+ */
+static void
+mcp55_set_intr(nv_port_t *nvp, int flag)
+{
+	nv_ctl_t *nvc = nvp->nvp_ctlp;
+	ddi_acc_handle_t bar5_hdl = nvc->nvc_bar_hdl[5];
+	uint16_t intr_bits =
+	    MCP55_INT_ADD|MCP55_INT_REM|MCP55_INT_COMPLETE;
+	uint16_t int_en;
+
+	ASSERT(mutex_owned(&nvp->nvp_mutex));
+
+	NVLOG((NVDBG_HOT, nvc, nvp, "mcp055_set_intr: enter flag: %d", flag));
+
+	if (flag & NV_INTR_CLEAR_ALL) {
+		NVLOG((NVDBG_INTR, nvc, nvp,
+		    "mcp55_set_intr: NV_INTR_CLEAR_ALL"));
+		nv_put16(bar5_hdl, nvp->nvp_mcp55_int_status, MCP55_INT_CLEAR);
+	}
+
+	if (flag & NV_INTR_ENABLE) {
+		NVLOG((NVDBG_INTR, nvc, nvp, "mcp55_set_intr: NV_INTR_ENABLE"));
+		int_en = nv_get16(bar5_hdl, nvp->nvp_mcp55_int_ctl);
+		int_en |= intr_bits;
+		nv_put16(bar5_hdl, nvp->nvp_mcp55_int_ctl, int_en);
+	}
+
+	if (flag & NV_INTR_DISABLE) {
+		NVLOG((NVDBG_INTR, nvc, nvp,
+		    "mcp55_set_intr: NV_INTR_DISABLE"));
+		int_en = nv_get16(bar5_hdl, nvp->nvp_mcp55_int_ctl);
+		int_en &= ~intr_bits;
+		nv_put16(bar5_hdl, nvp->nvp_mcp55_int_ctl, int_en);
+	}
+}
+
+
+/*
+ * The PM functions for suspend and resume are incomplete and need additional
+ * work.  It may or may not work in the current state.
+ */
+static void
+nv_resume(nv_port_t *nvp)
+{
+	NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp, "nv_resume()"));
+
+	mutex_enter(&nvp->nvp_mutex);
+
+	if (nvp->nvp_state & NV_PORT_INACTIVE) {
+		mutex_exit(&nvp->nvp_mutex);
+
+		return;
+	}
+
+	(*(nvp->nvp_ctlp->nvc_set_intr))(nvp, NV_INTR_CLEAR_ALL|NV_INTR_ENABLE);
+
+	/*
+	 * power may have been removed to the port and the
+	 * drive, and/or a drive may have been added or removed.
+	 * Force a reset which will cause a probe and re-establish
+	 * any state needed on the drive.
+	 * nv_reset(nvp);
+	 */
+
+	mutex_exit(&nvp->nvp_mutex);
+}
+
+
+static void
+nv_copy_registers(nv_port_t *nvp, sata_device_t *sd, sata_pkt_t *spkt)
+{
+	ddi_acc_handle_t bar5_hdl = nvp->nvp_ctlp->nvc_bar_hdl[5];
+	sata_cmd_t *scmd = &spkt->satapkt_cmd;
+	ddi_acc_handle_t ctlhdl = nvp->nvp_ctl_hdl;
+	ddi_acc_handle_t cmdhdl = nvp->nvp_cmd_hdl;
+	uchar_t status;
+	struct sata_cmd_flags flags;
+
+	NVLOG((NVDBG_INIT, nvp->nvp_ctlp, nvp, "nv_copy_registers()"));
+
+	sd->satadev_scr.sstatus = nv_get32(bar5_hdl, nvp->nvp_sstatus);
+	sd->satadev_scr.serror = nv_get32(bar5_hdl, nvp->nvp_serror);
+	sd->satadev_scr.scontrol = nv_get32(bar5_hdl, nvp->nvp_sctrl);
+
+	if (spkt == NULL) {
+
+		return;
+	}
+
+	/*
+	 * in the error case, implicitly set the return of regs needed
+	 * for error handling.
+	 */
+	status = scmd->satacmd_status_reg = nv_get8(ctlhdl,
+	    nvp->nvp_altstatus);
+
+	flags = scmd->satacmd_flags;
+
+	if (status & SATA_STATUS_ERR) {
+		flags.sata_copy_out_lba_low_msb = B_TRUE;
+		flags.sata_copy_out_lba_mid_msb = B_TRUE;
+		flags.sata_copy_out_lba_high_msb = B_TRUE;
+		flags.sata_copy_out_lba_low_lsb = B_TRUE;
+		flags.sata_copy_out_lba_mid_lsb = B_TRUE;
+		flags.sata_copy_out_lba_high_lsb = B_TRUE;
+		flags.sata_copy_out_error_reg = B_TRUE;
+		flags.sata_copy_out_sec_count_msb = B_TRUE;
+		flags.sata_copy_out_sec_count_lsb = B_TRUE;
+		scmd->satacmd_status_reg = status;
+	}
+
+	if (scmd->satacmd_addr_type & ATA_ADDR_LBA48) {
+
+		/*
+		 * set HOB so that high byte will be read
+		 */
+		nv_put8(ctlhdl, nvp->nvp_devctl, ATDC_HOB|ATDC_D3);
+
+		/*
+		 * get the requested high bytes
+		 */
+		if (flags.sata_copy_out_sec_count_msb) {
+			scmd->satacmd_sec_count_msb =
+			    nv_get8(cmdhdl, nvp->nvp_count);
+		}
+
+		if (flags.sata_copy_out_lba_low_msb) {
+			scmd->satacmd_lba_low_msb =
+			    nv_get8(cmdhdl, nvp->nvp_sect);
+		}
+
+		if (flags.sata_copy_out_lba_mid_msb) {
+			scmd->satacmd_lba_mid_msb =
+			    nv_get8(cmdhdl, nvp->nvp_lcyl);
+		}
+
+		if (flags.sata_copy_out_lba_high_msb) {
+			scmd->satacmd_lba_high_msb =
+			    nv_get8(cmdhdl, nvp->nvp_hcyl);
+		}
+	}
+
+	/*
+	 * disable HOB so that low byte is read
+	 */
+	nv_put8(ctlhdl, nvp->nvp_devctl, ATDC_D3);
+
+	/*
+	 * get the requested low bytes
+	 */
+	if (flags.sata_copy_out_sec_count_lsb) {
+		scmd->satacmd_sec_count_lsb = nv_get8(cmdhdl, nvp->nvp_count);
+	}
+
+	if (flags.sata_copy_out_lba_low_lsb) {
+		scmd->satacmd_lba_low_lsb = nv_get8(cmdhdl, nvp->nvp_sect);
+	}
+
+	if (flags.sata_copy_out_lba_mid_lsb) {
+		scmd->satacmd_lba_mid_lsb = nv_get8(cmdhdl, nvp->nvp_lcyl);
+	}
+
+	if (flags.sata_copy_out_lba_high_lsb) {
+		scmd->satacmd_lba_high_lsb = nv_get8(cmdhdl, nvp->nvp_hcyl);
+	}
+
+	/*
+	 * get the device register if requested
+	 */
+	if (flags.sata_copy_out_device_reg) {
+		scmd->satacmd_device_reg =  nv_get8(cmdhdl, nvp->nvp_drvhd);
+	}
+
+	/*
+	 * get the error register if requested
+	 */
+	if (flags.sata_copy_out_error_reg) {
+		scmd->satacmd_error_reg = nv_get8(cmdhdl, nvp->nvp_error);
+	}
+}
+
+
+/*
+ * Hot plug and remove interrupts can occur when the device is reset.  Just
+ * masking the interrupt doesn't always work well because if a
+ * different interrupt arrives on the other port, the driver can still
+ * end up checking the state of the other port and discover the hot
+ * interrupt flag is set even though it was masked.  Checking for recent
+ * reset activity and then ignoring turns out to be the easiest way.
+ */
+static void
+nv_report_add_remove(nv_port_t *nvp, int flags)
+{
+	ddi_acc_handle_t bar5_hdl = nvp->nvp_ctlp->nvc_bar_hdl[5];
+	clock_t time_diff = ddi_get_lbolt() - nvp->nvp_reset_time;
+	uint32_t sstatus;
+	int i;
+
+	/*
+	 * If reset within last 1 second ignore.  This should be
+	 * reworked and improved instead of having this somewhat
+	 * heavy handed clamping job.
+	 */
+	if (time_diff < drv_usectohz(NV_ONE_SEC)) {
+		NVLOG((NVDBG_HOT, nvp->nvp_ctlp, nvp, "nv_report_add_remove()"
+		    "ignoring plug interrupt was %dms ago",
+		    TICK_TO_MSEC(time_diff)));
+
+		return;
+	}
+
+	/*
+	 * wait up to 1ms for sstatus to settle and reflect the true
+	 * status of the port.  Failure to do so can create confusion
+	 * in probe, where the incorrect sstatus value can still
+	 * persist.
+	 */
+	for (i = 0; i < 1000; i++) {
+		sstatus = nv_get32(bar5_hdl, nvp->nvp_sstatus);
+
+		if ((flags == NV_PORT_HOTREMOVED) &&
+		    ((sstatus & SSTATUS_DET_DEVPRE_PHYCOM) !=
+		    SSTATUS_DET_DEVPRE_PHYCOM)) {
+			break;
+		}
+
+		if ((flags != NV_PORT_HOTREMOVED) &&
+		    ((sstatus & SSTATUS_DET_DEVPRE_PHYCOM) ==
+		    SSTATUS_DET_DEVPRE_PHYCOM)) {
+			break;
+		}
+		drv_usecwait(1);
+	}
+
+	NVLOG((NVDBG_HOT, nvp->nvp_ctlp, nvp,
+	    "sstatus took %i us for DEVPRE_PHYCOM to settle", i));
+
+	if (flags == NV_PORT_HOTREMOVED) {
+		NVLOG((NVDBG_HOT, nvp->nvp_ctlp, nvp,
+		    "nv_report_add_remove() hot removed"));
+		nv_port_state_change(nvp,
+		    SATA_EVNT_DEVICE_DETACHED,
+		    SATA_ADDR_CPORT, 0);
+
+		nvp->nvp_state |= NV_PORT_HOTREMOVED;
+	} else {
+		NVLOG((NVDBG_HOT, nvp->nvp_ctlp, nvp,
+		    "nv_report_add_remove() hot plugged"));
+		nv_port_state_change(nvp, SATA_EVNT_DEVICE_ATTACHED,
+		    SATA_ADDR_CPORT, 0);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/sata/adapters/nv_sata/nv_sata.conf	Thu Aug 16 14:46:34 2007 -0700
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+use-cmdk-devid-format=1;
--- a/usr/src/uts/common/io/sata/impl/sata.c	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/common/io/sata/impl/sata.c	Thu Aug 16 14:46:34 2007 -0700
@@ -85,6 +85,8 @@
 static void
 sata_test_atapi_packet_command(sata_hba_inst_t *, int);
 #endif
+#define	LEGACY_HWID_LEN	64	/* Model (40) + Serial (20) + pad */
+
 
 /*
  * SATA cb_ops functions
@@ -264,6 +266,9 @@
     sata_drive_info_t *);
 static	void sata_atapi_packet_cmd_setup(sata_cmd_t *, sata_drive_info_t *);
 static	void sata_fixed_sense_data_preset(struct scsi_extended_sense *);
+static  void sata_target_devid_register(dev_info_t *, sata_drive_info_t *);
+static  int sata_check_modser(char *, int);
+
 
 
 /*
@@ -2695,6 +2700,24 @@
 	    sata_device.satadev_addr.cport)));
 
 	/*
+	 * Check if we need to create a legacy devid (i.e cmdk style) for
+	 * the target disks.
+	 *
+	 * HBA devinfo node will have the property "use-cmdk-devid-format"
+	 * if we need to create cmdk-style devid for all the disk devices
+	 * attached to this controller. This property may have been set
+	 * from HBA driver's .conf file or by the HBA driver in its
+	 * attach(9F) function.
+	 */
+	if ((sdinfo->satadrv_type == SATA_DTYPE_ATADISK) &&
+	    (ddi_getprop(DDI_DEV_T_ANY, hba_dip, DDI_PROP_DONTPASS,
+	    "use-cmdk-devid-format", 0) == 1)) {
+		/* register a legacy devid for this target node */
+		sata_target_devid_register(tgt_dip, sdinfo);
+	}
+
+
+	/*
 	 * 'Identify Device Data' does not always fit in standard SCSI
 	 * INQUIRY data, so establish INQUIRY_* properties with full-form
 	 * of information.
@@ -2780,6 +2803,7 @@
 	sata_device_t		sata_device;
 	sata_drive_info_t	*sdinfo;
 	sata_hba_inst_t		*sata_hba_inst;
+	ddi_devid_t		devid;
 
 	sata_hba_inst = (sata_hba_inst_t *)(hba_tran->tran_hba_private);
 
@@ -2810,6 +2834,18 @@
 		SATA_LOG_D((sata_hba_inst, CE_WARN,
 		    "sata_scsi_tgt_free: pm-capable "
 		    "property could not be removed"));
+
+	/*
+	 * If devid was previously created but not freed up from
+	 * sd(7D) driver (i.e during detach(9F)) then do it here.
+	 */
+	if ((sdinfo->satadrv_type == SATA_DTYPE_ATADISK) &&
+	    (ddi_getprop(DDI_DEV_T_ANY, hba_dip, DDI_PROP_DONTPASS,
+	    "use-cmdk-devid-format", 0) == 1) &&
+	    (ddi_devid_get(tgt_dip, &devid) == DDI_SUCCESS)) {
+		ddi_devid_unregister(tgt_dip);
+		ddi_devid_free(devid);
+	}
 }
 
 /*
@@ -3561,12 +3597,14 @@
 		/*
 		 * Free DMA resources - cookies and handles
 		 */
-		ASSERT(spx->txlt_dma_cookie_list != NULL);
-		if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) {
-			(void) kmem_free(spx->txlt_dma_cookie_list,
-			    spx->txlt_dma_cookie_list_len *
-			    sizeof (ddi_dma_cookie_t));
-			spx->txlt_dma_cookie_list = NULL;
+		if (spx->txlt_dma_cookie_list != NULL) {
+			if (spx->txlt_dma_cookie_list !=
+			    &spx->txlt_dma_cookie) {
+				(void) kmem_free(spx->txlt_dma_cookie_list,
+				    spx->txlt_dma_cookie_list_len *
+				    sizeof (ddi_dma_cookie_t));
+				spx->txlt_dma_cookie_list = NULL;
+			}
 		}
 		(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
 		(void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
@@ -3594,18 +3632,30 @@
 	spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private;
 
 	if (spx->txlt_buf_dma_handle != NULL) {
+		if (spx->txlt_tmp_buf != NULL)  {
+			/*
+			 * Intermediate DMA buffer was allocated.
+			 * Free allocated buffer and associated access handle.
+			 */
+			ddi_dma_mem_free(&spx->txlt_tmp_buf_handle);
+			spx->txlt_tmp_buf = NULL;
+		}
 		/*
 		 * Free DMA resources - cookies and handles
 		 */
-		ASSERT(spx->txlt_dma_cookie_list != NULL);
-		if (spx->txlt_dma_cookie_list != &spx->txlt_dma_cookie) {
-			(void) kmem_free(spx->txlt_dma_cookie_list,
-			    spx->txlt_dma_cookie_list_len *
-			    sizeof (ddi_dma_cookie_t));
-			spx->txlt_dma_cookie_list = NULL;
+		/* ASSERT(spx->txlt_dma_cookie_list != NULL); */
+		if (spx->txlt_dma_cookie_list != NULL) {
+			if (spx->txlt_dma_cookie_list !=
+			    &spx->txlt_dma_cookie) {
+				(void) kmem_free(spx->txlt_dma_cookie_list,
+				    spx->txlt_dma_cookie_list_len *
+				    sizeof (ddi_dma_cookie_t));
+				spx->txlt_dma_cookie_list = NULL;
+			}
 		}
 		(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
 		(void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
+		spx->txlt_buf_dma_handle = NULL;
 	}
 }
 
@@ -4033,8 +4083,16 @@
 
 	if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
 
+		/*
+		 * Because it is fully emulated command storing data
+		 * programatically in the specified buffer, release
+		 * preallocated DMA resources before storing data in the buffer,
+		 * so no unwanted DMA sync would take place.
+		 */
+		sata_scsi_dmafree(NULL, scsipkt);
+
 		if (!(scsipkt->pkt_cdbp[1] & EVPD)) {
-		/* Standard Inquiry Data request */
+			/* Standard Inquiry Data request */
 			struct scsi_inquiry inq;
 			unsigned int bufsize;
 
@@ -4190,8 +4248,15 @@
 	*scsipkt->pkt_scbp = STATUS_GOOD;
 
 	if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
+		/*
+		 * Because it is fully emulated command storing data
+		 * programatically in the specified buffer, release
+		 * preallocated DMA resources before storing data in the buffer,
+		 * so no unwanted DMA sync would take place.
+		 */
 		int count = MIN(bp->b_bcount,
 		    sizeof (struct scsi_extended_sense));
+		sata_scsi_dmafree(NULL, scsipkt);
 		bzero(&sense, sizeof (struct scsi_extended_sense));
 		sense.es_valid = 0;	/* Valid LBA */
 		sense.es_class = 7;	/* Response code 0x70 - current err */
@@ -4425,6 +4490,14 @@
 	    STATE_SENT_CMD | STATE_GOT_STATUS;
 	*scsipkt->pkt_scbp = STATUS_GOOD;
 	if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
+		/*
+		 * Because it is fully emulated command storing data
+		 * programatically in the specified buffer, release
+		 * preallocated DMA resources before storing data in the buffer,
+		 * so no unwanted DMA sync would take place.
+		 */
+		sata_scsi_dmafree(NULL, scsipkt);
+
 		sdinfo = sata_get_device_info(
 		    spx->txlt_sata_hba_inst,
 		    &spx->txlt_sata_pkt->satapkt_device);
@@ -4512,6 +4585,14 @@
 	pc = scsipkt->pkt_cdbp[2] >> 6;
 
 	if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
+		/*
+		 * Because it is fully emulated command storing data
+		 * programatically in the specified buffer, release
+		 * preallocated DMA resources before storing data in the buffer,
+		 * so no unwanted DMA sync would take place.
+		 */
+		sata_scsi_dmafree(NULL, scsipkt);
+
 		len = 0;
 		bdlen = 0;
 		if (!(scsipkt->pkt_cdbp[1] & 8)) {
@@ -5081,7 +5162,17 @@
 	}
 
 	if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
+		/*
+		 * Because log sense uses local buffers for data retrieval from
+		 * the devices and sets the data programatically in the
+		 * original specified buffer, release preallocated DMA
+		 * resources before storing data in the original buffer,
+		 * so no unwanted DMA sync would take place.
+		 */
 		sata_id_t *sata_id;
+
+		sata_scsi_dmafree(NULL, scsipkt);
+
 		len = 0;
 
 		/* Build log parameter header */
@@ -11664,6 +11755,86 @@
 	sense->es_qual_code = 0;
 }
 
+/*
+ * Register a legacy cmdk-style devid for the target (disk) device.
+ *
+ * Note: This function is called only when the HBA devinfo node has the
+ * property "use-cmdk-devid-format" set. This property indicates that
+ * devid compatible with old cmdk (target) driver is to be generated
+ * for any target device attached to this controller. This will take
+ * precedence over the devid generated by sd (target) driver.
+ * This function is derived from cmdk_devid_setup() function in cmdk.c.
+ */
+static void
+sata_target_devid_register(dev_info_t *dip, sata_drive_info_t *sdinfo)
+{
+	char	*hwid;
+	int	modlen;
+	int	serlen;
+	int	rval;
+	ddi_devid_t	devid;
+
+	/*
+	 * device ID is a concatanation of model number, "=", serial number.
+	 */
+	hwid = kmem_zalloc(LEGACY_HWID_LEN, KM_SLEEP);
+	bcopy(&sdinfo->satadrv_id.ai_model, hwid,
+	    sizeof (sdinfo->satadrv_id.ai_model));
+	swab(hwid, hwid, sizeof (sdinfo->satadrv_id.ai_model));
+	modlen = sata_check_modser(hwid, sizeof (sdinfo->satadrv_id.ai_model));
+	if (modlen == 0)
+		goto err;
+	hwid[modlen++] = '=';
+	bcopy(&sdinfo->satadrv_id.ai_drvser, &hwid[modlen],
+	    sizeof (sdinfo->satadrv_id.ai_drvser));
+	swab(&hwid[modlen], &hwid[modlen],
+		sizeof (sdinfo->satadrv_id.ai_drvser));
+	serlen = sata_check_modser(&hwid[modlen],
+		sizeof (sdinfo->satadrv_id.ai_drvser));
+	if (serlen == 0)
+		goto err;
+	hwid[modlen + serlen] = 0; /* terminate the hwid string */
+
+	/* initialize/register devid */
+	if ((rval = ddi_devid_init(dip, DEVID_ATA_SERIAL,
+		(ushort_t)(modlen + serlen), hwid, &devid)) == DDI_SUCCESS)
+		rval = ddi_devid_register(dip, devid);
+
+	if (rval != DDI_SUCCESS)
+		cmn_err(CE_WARN, "sata: failed to create devid for the disk"
+			" on port %d", sdinfo->satadrv_addr.cport);
+err:
+	kmem_free(hwid, LEGACY_HWID_LEN);
+}
+
+/*
+ * valid model/serial string must contain a non-zero non-space characters.
+ * trim trailing spaces/NULLs.
+ */
+static int
+sata_check_modser(char *buf, int buf_len)
+{
+	boolean_t ret;
+	char *s;
+	int i;
+	int tb;
+	char ch;
+
+	ret = B_FALSE;
+	s = buf;
+	for (i = 0; i < buf_len; i++) {
+		ch = *s++;
+		if (ch != ' ' && ch != '\0')
+			tb = i + 1;
+		if (ch != ' ' && ch != '\0' && ch != '0')
+			ret = B_TRUE;
+	}
+
+	if (ret == B_FALSE)
+		return (0); /* invalid string */
+
+	return (tb); /* return length */
+}
 
 /*
  * sata_set_drive_features function compares current device features setting
--- a/usr/src/uts/common/io/scsi/targets/sd.c	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/common/io/scsi/targets/sd.c	Thu Aug 16 14:46:34 2007 -0700
@@ -4637,6 +4637,16 @@
 	ASSERT((SD_DEVINFO(un)) == devi);
 
 	/*
+	 * If transport has already registered a devid for this target
+	 * then that takes precedence over the driver's determination
+	 * of the devid.
+	 */
+	if (ddi_devid_get(SD_DEVINFO(un), &un->un_devid) == DDI_SUCCESS) {
+		ASSERT(un->un_devid);
+		return; /* use devid registered by the transport */
+	}
+
+	/*
 	 * This is the case of antiquated Sun disk drives that have the
 	 * FAB_DEVID property set in the disk_table.  These drives
 	 * manage the devid's by storing them in last 2 available sectors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/warlock/nv_sata.wlcmd	Thu Aug 16 14:46:34 2007 -0700
@@ -0,0 +1,120 @@
+# CDDL HEADER START
+# 
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# usr/src/uts/common/io/warlock/nv_sata.wlcmd
+
+one 	nv_ctl
+one 	nv_port
+
+one     scsi_device
+one     __ddi_xbuf_attr
+one     sd_lun
+one     sd_resv_reclaim_request
+
+root    sata_hba_ioctl
+root    sata_hba_open
+root    sata_hba_close
+root    sata_scsi_reset
+root    sata_scsi_init_pkt
+root    sata_scsi_start
+root    sata_scsi_destroy_pkt
+root    sata_scsi_sync_pkt
+root	sata_scsi_tgt_init
+root	sata_scsi_tgt_free
+root	sata_scsi_tgt_probe
+root    sata_scsi_dmafree
+root    sata_scsi_abort
+root    sata_scsi_getcap
+root    sata_scsi_setcap
+
+add     sd.c:sd_start_cmds/funcp target sd_initpkt_for_buf sd_initpkt_for_uscsi
+root    sd.c:sd_handle_mchange sd_media_change_task sd_start_stop_unit_task
+root    sd.c:sd_wm_cache_constructor sd_wm_cache_destructor
+root	sd.c:sd_read_modify_write_task sd_reenable_dsense_task
+root	sd.c:sd_failfast_flushq_callback sd_start_direct_priority_command
+root	sd.c:sdstrategy sdioctl
+
+root	scsi_hba.c:scsi_hba_bus_power
+
+ignore  sd.c:sd_scsi_probe_cache_fini
+ignore  sd.c:sd_scsi_probe_cache_init
+ignore  sd.c:sd_scsi_target_lun_fini
+ignore  sd.c:sd_scsi_target_lun_init
+root    sd.c:sd_taskq_create
+root    sd.c:sd_taskq_delete
+
+add     bus_ops::bus_add_eventcall		targets warlock_dummy
+add     bus_ops::bus_config			targets warlock_dummy
+add     bus_ops::bus_get_eventcookie		targets warlock_dummy
+add     bus_ops::bus_intr_ctl			targets warlock_dummy
+add     bus_ops::bus_post_event			targets warlock_dummy
+add     bus_ops::bus_remove_eventcall		targets warlock_dummy
+add     bus_ops::bus_unconfig			targets warlock_dummy
+
+
+add     scsi_hba_tran::tran_tgt_init    targets sata_scsi_tgt_init
+add     scsi_hba_tran::tran_tgt_probe   targets sata_scsi_tgt_probe
+add     scsi_hba_tran::tran_tgt_free    targets sata_scsi_tgt_free
+add     scsi_hba_tran::tran_start       targets sata_scsi_start
+add     scsi_hba_tran::tran_abort       targets sata_scsi_abort
+add     scsi_hba_tran::tran_reset       targets sata_scsi_reset
+add     scsi_hba_tran::tran_getcap      targets sata_scsi_getcap
+add     scsi_hba_tran::tran_setcap      targets sata_scsi_setcap
+add     scsi_hba_tran::tran_init_pkt    targets sata_scsi_init_pkt
+add     scsi_hba_tran::tran_destroy_pkt targets sata_scsi_destroy_pkt
+
+add     scsi_hba_tran::tran_add_eventcall       targets warlock_dummy
+add     scsi_hba_tran::tran_bus_config          targets warlock_dummy
+add     scsi_hba_tran::tran_bus_power           targets warlock_dummy
+add     scsi_hba_tran::tran_bus_unconfig        targets warlock_dummy
+add     scsi_hba_tran::tran_get_eventcookie     targets warlock_dummy
+add     scsi_hba_tran::tran_get_name            targets warlock_dummy
+add     scsi_hba_tran::tran_post_event          targets warlock_dummy
+add     scsi_hba_tran::tran_remove_eventcall    targets warlock_dummy
+
+root    scsi_hba.c:scsi_hba_bus_power
+
+
+add	dk_callback::dkc_callback		targets warlock_dummy
+add	sd_uscsi_info::ui_dkc.dkc_callback	targets warlock_dummy
+ 
+add scsi_watch_request::swr_callback targets \
+        sd.c:sd_mhd_watch_cb \
+        sd.c:sd_media_watch_cb
+
+add scsi_pkt::pkt_comp targets \
+	scsi_watch.c:scsi_watch_request_intr \
+	sd.c:sdintr \
+        sata_scsi_destroy_pkt \
+        sata_scsi_init_pkt \
+        sata_scsi_start \
+        sata_scsi_abort \
+        sata_scsi_reset \
+	sata_scsi_start
+
+add     __ddi_xbuf_attr::xa_strategy targets sd_xbuf_strategy
+
+ignore	sd.c:sd_mhd_reset_notify_cb
--- a/usr/src/uts/common/os/sunddi.c	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/common/os/sunddi.c	Thu Aug 16 14:46:34 2007 -0700
@@ -7623,6 +7623,12 @@
 }
 
 int
+ddi_devid_get(dev_info_t *dip, ddi_devid_t *ret_devid)
+{
+	return (i_ddi_devi_get_devid(DDI_DEV_T_ANY, dip, ret_devid));
+}
+
+int
 i_ddi_devi_get_devid(dev_t dev, dev_info_t *dip, ddi_devid_t *ret_devid)
 {
 	char		*devidstr;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/sata/adapters/nv_sata/nv_sata.h	Thu Aug 16 14:46:34 2007 -0700
@@ -0,0 +1,642 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NV_SATA_H
+#define	_NV_SATA_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	NV_MAX_PORTS(nvc) nvc->nvc_sata_hba_tran.sata_tran_hba_num_cports
+
+typedef struct nv_port nv_port_t;
+
+typedef struct nv_ctl {
+	/*
+	 * Each of these are specific to the chipset in use.
+	 */
+	uint_t		(*nvc_interrupt)(caddr_t arg1, caddr_t arg2);
+	void		(*nvc_reg_init)(struct nv_ctl *nvc,
+			    ddi_acc_handle_t pci_conf_handle);
+
+	dev_info_t	*nvc_dip; /* devinfo pointer of controller */
+
+	struct nv_port	*nvc_port; /* array of pointers to port struct */
+
+	/*
+	 * handle and base address to register space.
+	 *
+	 * 0: port 0 task file
+	 * 1: port 0 status
+	 * 2: port 1 task file
+	 * 3: port 1 status
+	 * 4: bus master for both ports
+	 * 5: extended registers for SATA features
+	 */
+	ddi_acc_handle_t nvc_bar_hdl[6];
+	uchar_t		*nvc_bar_addr[6];
+
+	/*
+	 * sata registers in bar 5 which are shared on all devices
+	 * on the channel.
+	 */
+	uint32_t	*nvc_mcp55_ctl;
+	uint32_t	*nvc_mcp55_ncq; /* NCQ status control bits */
+
+	kmutex_t	nvc_mutex; /* ctrl level lock */
+
+	ddi_intr_handle_t *nvc_htable;	/* For array of interrupts */
+	int		 nvc_intr_type;	/* What type of interrupt */
+	int		nvc_intr_cnt;	/* # of intrs count returned */
+	size_t		nvc_intr_size;	/* Size of intr array to */
+	uint_t		nvc_intr_pri;   /* Interrupt priority */
+	int		nvc_intr_cap;	/* Interrupt capabilities */
+	uint8_t		*nvc_mcp04_int_status; /* interrupt status mcp04 */
+
+	sata_hba_tran_t	nvc_sata_hba_tran; /* sata_hba_tran for ctrl */
+
+	/*
+	 * enable/disable interrupts, controller specific
+	 */
+	void		(*nvc_set_intr)(nv_port_t *nvp, int flag);
+	int		nvc_state;	/* state flags of ctrl see below */
+	uint8_t		nvc_revid;	/* PCI revid of device */
+} nv_ctl_t;
+
+
+struct nv_port {
+
+	struct nv_ctl	*nvp_ctlp; /* back pointer to controller */
+
+	uint8_t		nvp_port_num; /* port number, ie 1 or 2 */
+
+	uint8_t		nvp_type;	/* SATA_DTYPE_{NONE,ATADISK,UNKNOWN} */
+	uint32_t	nvp_signature;	/* sig acquired from task file regs */
+	uchar_t		*nvp_cmd_addr;	/* base addr for cmd regs for port */
+	uchar_t		*nvp_bm_addr;	/* base addr for bus master for port */
+	uchar_t		*nvp_ctl_addr;	/* base addr for ctrl regs for port */
+
+	ddi_acc_handle_t nvp_cmd_hdl;
+	uchar_t		*nvp_data;	/* data register */
+	uchar_t		*nvp_error;	/* error register (read) */
+	uchar_t		*nvp_feature;	/* features (write) */
+	uchar_t		*nvp_count;	/* sector count */
+	uchar_t		*nvp_sect;	/* sector number */
+	uchar_t		*nvp_lcyl;	/* cylinder low byte */
+	uchar_t		*nvp_hcyl;	/* cylinder high byte */
+	uchar_t		*nvp_drvhd;	/* drive/head register */
+	uchar_t		*nvp_status;	/* status/command register */
+	uchar_t		*nvp_cmd;	/* status/command register */
+
+	ddi_acc_handle_t nvp_ctl_hdl;
+	uchar_t		*nvp_altstatus; /* alternate status (read) */
+	uchar_t		*nvp_devctl;	/* device control (write) */
+
+	ddi_acc_handle_t nvp_bm_hdl;
+	uchar_t		*nvp_bmisx;
+	uint32_t	*nvp_bmidtpx;
+	uchar_t		*nvp_bmicx;
+
+	ddi_dma_handle_t *nvp_sg_dma_hdl; /* dma handle to prd table */
+	caddr_t		 *nvp_sg_addr;	  /* virtual addr of prd table */
+	uint32_t	 *nvp_sg_paddr;   /* physical address of prd table */
+	ddi_acc_handle_t *nvp_sg_acc_hdl; /* mem acc handle to the prd table */
+
+	uint32_t	*nvp_sstatus;
+	uint32_t	*nvp_serror;
+	uint32_t	*nvp_sctrl;
+	uint32_t	*nvp_sactive;
+
+	kmutex_t	nvp_mutex;	/* main per port mutex */
+	kcondvar_t	nvp_poll_cv;	/* handshake cv between poll & isr */
+
+	/*
+	 * nvp_slot is a pointer to an array of nv_slot
+	 */
+	struct nv_slot	*nvp_slot;
+	uint32_t	nvp_sactive_cache; /* cache of SACTIVE */
+	uint8_t		nvp_queue_depth;
+
+	/*
+	 * NCQ flow control.  During NCQ operation, no other commands
+	 * allowed.  The following are used to enforce this.
+	 */
+	int		nvp_ncq_run;
+	int		nvp_non_ncq_run;
+
+	timeout_id_t	nvp_timeout_id;
+
+	clock_t		nvp_reset_time;	/* time of last reset */
+	clock_t		nvp_probe_time;	/* time when probe began */
+	clock_t		nvp_link_lost_time; /* time link lost was noticed */
+
+	int		nvp_state; /* state of port. flags defined below */
+
+	uint16_t	*nvp_mcp55_int_status;
+	uint16_t	*nvp_mcp55_int_ctl;
+};
+
+
+typedef struct nv_device_table {
+	ushort_t vendor_id;	/* vendor id */
+	ushort_t device_id;	/* device id */
+	ushort_t type;		/* chipset type, mcp04 or mcp55 */
+} nv_device_table_t;
+
+
+typedef struct nv_slot {
+	caddr_t		nvslot_v_addr;	/* I/O buffer address */
+	size_t		nvslot_byte_count; /* # bytes left to read/write */
+	sata_pkt_t	*nvslot_spkt;
+	clock_t		nvslot_stime;
+	int		(*nvslot_start)(nv_port_t *nvp, int queue);
+	void		(*nvslot_intr)(nv_port_t *nvp,
+			    struct nv_slot *nv_slotp);
+	uint32_t	nvslot_flags;
+} nv_slot_t;
+
+
+/*
+ * nvslot_flags
+ */
+#define	NVSLOT_COMPLETE 0x01
+#define	NVSLOT_NCQ	0x02 /* NCQ is active */
+
+/*
+ * state values for nv_attach
+ */
+#define	ATTACH_PROGRESS_NONE			(1 << 0)
+#define	ATTACH_PROGRESS_STATEP_ALLOC		(1 << 1)
+#define	ATTACH_PROGRESS_PCI_HANDLE		(1 << 2)
+#define	ATTACH_PROGRESS_BARS			(1 << 3)
+#define	ATTACH_PROGRESS_INTR_ADDED		(1 << 4)
+#define	ATTACH_PROGRESS_MUTEX_INIT		(1 << 5)
+#define	ATTACH_PROGRESS_CTL_SETUP		(1 << 6)
+#define	ATTACH_PROGRESS_TRAN_SETUP		(1 << 7)
+#define	ATTACH_PROGRESS_COUNT			(1 << 8)
+#define	ATTACH_PROGRESS_CONF_HANDLE		(1 << 9)
+#define	ATTACH_PROGRESS_SATA_MODULE		(1 << 10)
+
+#ifdef DEBUG
+
+#define	NV_DEBUG		1
+
+#endif /* DEBUG */
+
+
+/*
+ * nv_debug_flags
+ */
+#define	NVDBG_ALWAYS	0x0001
+#define	NVDBG_INIT	0x0002
+#define	NVDBG_ENTRY	0x0004
+#define	NVDBG_DELIVER	0x0008
+#define	NVDBG_EVENT	0x0010
+#define	NVDBG_SYNC	0x0020
+#define	NVDBG_PKTCOMP	0x0040
+#define	NVDBG_TIMEOUT	0x0080
+#define	NVDBG_INFO	0x0100
+#define	NVDBG_VERBOSE	0x0200
+#define	NVDBG_INTR	0x0400
+#define	NVDBG_ERRS	0x0800
+#define	NVDBG_COOKIES	0x1000
+#define	NVDBG_HOT	0x2000
+#define	NVDBG_PROBE	0x4000
+
+#ifdef DEBUG
+#define	NVLOG(a) nv_log a
+#else
+#define	NVLOG(a)
+#endif
+
+#define	NV_SUCCESS	0
+#define	NV_FAILURE	-1
+
+/*
+ * indicates whether nv_wait functions can sleep or not.
+ */
+#define	NV_SLEEP	1
+#define	NV_NOSLEEP	2
+
+/*
+ * port offsets from base address ioaddr1
+ */
+#define	NV_DATA		0x00	/* data register 			*/
+#define	NV_ERROR	0x01	/* error register (read)		*/
+#define	NV_FEATURE	0x01	/* features (write)			*/
+#define	NV_COUNT	0x02    /* sector count 			*/
+#define	NV_SECT		0x03	/* sector number 			*/
+#define	NV_LCYL		0x04	/* cylinder low byte 			*/
+#define	NV_HCYL		0x05	/* cylinder high byte 			*/
+#define	NV_DRVHD	0x06    /* drive/head register 			*/
+#define	NV_STATUS	0x07	/* status/command register 		*/
+#define	NV_CMD		0x07	/* status/command register 		*/
+
+/*
+ * port offsets from base address ioaddr2
+ */
+#define	NV_ALTSTATUS	0x02	/* alternate status (read)		*/
+#define	NV_DEVCTL	0x02	/* device control (write)		*/
+
+/*
+ * device control register
+ */
+#define	ATDC_NIEN    	0x02    /* disable interrupts */
+#define	ATDC_SRST	0x04	/* controller reset */
+#define	ATDC_D3		0x08	/* mysterious bit */
+#define	ATDC_HOB	0x80	/* high order byte to read 48-bit values */
+
+
+#define	MCP55_CTL		0x400 /* queuing control */
+#define	MCP55_INT_STATUS	0x440 /* status bits for interrupt */
+#define	MCP55_INT_CTL		0x444 /* enable bits for interrupt */
+#define	MCP55_NCQ		0x448 /* NCQ status and ctrl bits */
+
+/*
+ * if either of these bits are set, when using NCQ, if no other commands are
+ * active while a new command is started, DMA engine can be programmed ahead
+ * of time to save extra interrupt.  Presumably pre-programming is discarded
+ * if a subsequent command ends up finishing first.
+ */
+#define	MCP_SATA_AE_NCQ_PDEV_FIRST_CMD	(1 << 7)
+#define	MCP_SATA_AE_NCQ_SDEV_FIRST_CMD	(1 << 23)
+
+/*
+ * bit definitions to indicate which NCQ command requires
+ * DMA setup.
+ */
+#define	MCP_SATA_AE_NCQ_PDEV_DMA_SETUP_TAG_SHIFT	2
+#define	MCP_SATA_AE_NCQ_SDEV_DMA_SETUP_TAG_SHIFT	18
+#define	MCP_SATA_AE_NCQ_DMA_SETUP_TAG_MASK		0x1f
+
+
+/*
+ * Bits for NV_MCP55_INT_CTL and NV_MCP55_INT_STATUS
+ */
+#define	MCP55_INT_SNOTIFY	0x200	/* snotification set */
+#define	MCP55_INT_SERROR	0x100	/* serror set */
+#define	MCP55_INT_DMA_SETUP	0x80	/* DMA to be programmed */
+#define	MCP55_INT_DH_REGFIS	0x40	/* REGFIS received */
+#define	MCP55_INT_SDB_FIS	0x20	/* SDB FIS */
+#define	MCP55_INT_TX_BACKOUT	0x10	/* TX backout */
+#define	MCP55_INT_REM		0x08	/* device removed */
+#define	MCP55_INT_ADD		0x04	/* device added */
+#define	MCP55_INT_PM		0x02	/* power changed */
+#define	MCP55_INT_COMPLETE	0x01	/* device interrupt */
+
+/*
+ * Bits above that are not used for now.
+ */
+#define	MCP55_INT_IGNORE (MCP55_INT_DMA_SETUP|MCP55_INT_DH_REGFIS|\
+	MCP55_INT_SDB_FIS|MCP55_INT_TX_BACKOUT|MCP55_INT_PM|\
+	MCP55_INT_SNOTIFY|MCP55_INT_SERROR)
+
+/*
+ * Bits for MCP_SATA_AE_CTL
+ */
+#define	MCP_SATA_AE_CTL_PRI_SWNCQ	(1 << 1) /* software NCQ chan 0 */
+#define	MCP_SATA_AE_CTL_SEC_SWNCQ	(1 << 2) /* software NCQ chan 1 */
+
+#define	NV_DELAY_NSEC(wait_ns) \
+{ \
+	hrtime_t start, end; \
+	start = end =  gethrtime(); \
+	while ((end - start) < wait_ns) \
+		end = gethrtime(); \
+}
+
+/*
+ * signatures in task file registers after device reset
+ */
+#define	NV_SIG_DISK	0x00000101
+#define	NV_SIG_ATAPI	0xeb140101
+#define	NV_SIG_PM	0x96690101
+#define	NV_SIG_NOTREADY	0x00000000
+
+/*
+ * These bar5 offsets are common to mcp55/mcp04 and thus
+ * prefixed with NV.
+ */
+#define	NV_SSTATUS	0x00
+#define	NV_SERROR	0x04
+#define	NV_SCTRL	0x08
+#define	NV_SACTIVE	0x0c
+#define	NV_SNOTIFICATION 0x10
+
+#define	CH0_SREG_OFFSET	0x0
+#define	CH1_SREG_OFFSET	0x40
+
+
+/*
+ * The following config space offsets are needed to enable
+ * bar 5 register access in mcp04/mcp55
+ */
+#define	NV_SATA_CFG_20		0x50
+#define	NV_BAR5_SPACE_EN	0x04
+#define	NV_40BIT_PRD		0x20
+
+/*
+ * mcp04 interrupt status register
+ */
+
+/*
+ * offsets to bar 5 registers
+ */
+#define	MCP04_SATA_INT_STATUS	0x440
+#define	MCP04_SATA_INT_EN	0x441
+
+
+/*
+ * bit fields for int status and int enable
+ * registers
+ */
+#define	MCP04_INT_PDEV_INT	0x01 /* completion interrupt */
+#define	MCP04_INT_PDEV_PM	0x02 /* power change */
+#define	MCP04_INT_PDEV_ADD	0x04 /* hot plug */
+#define	MCP04_INT_PDEV_REM	0x08 /* hot remove */
+#define	MCP04_INT_PDEV_HOT	MCP04_INT_PDEV_ADD|MCP04_INT_PDEV_REM
+
+#define	MCP04_INT_SDEV_INT	0x10 /* completion interrupt */
+#define	MCP04_INT_SDEV_PM	0x20 /* power change */
+#define	MCP04_INT_SDEV_ADD	0x40 /* hot plug */
+#define	MCP04_INT_SDEV_REM	0x80 /* hot remove */
+#define	MCP04_INT_SDEV_HOT	MCP04_INT_SDEV_ADD|MCP04_INT_SDEV_REM
+
+#define	MCP04_INT_PDEV_ALL	MCP04_INT_PDEV_INT|MCP04_INT_PDEV_HOT|\
+				MCP04_INT_PDEV_PM
+#define	MCP04_INT_SDEV_ALL	MCP04_INT_SDEV_INT|MCP04_INT_SDEV_HOT|\
+				MCP04_INT_SDEV_PM
+
+/*
+ * config space offset 42
+ */
+#define	NV_SATA_CFG_42			0xac
+
+/*
+ * bit in CFG_42 which delays hotplug interrupt until
+ * PHY ready
+ */
+#define	MCP04_CFG_DELAY_HOTPLUG_INTR	(0x1 << 12)
+
+
+/*
+ * bar 5 offsets for SATA registers in ck804
+ */
+#define	MCP04_CH1_SSTATUS	0x00
+#define	MCP04_CH1_SERROR	0x04
+#define	MCP04_CH1_SCTRL		0x08
+#define	MCP04_CH1_SACTIVE	0x0c
+#define	MCP04_CH1_SNOTIFICATION	0x10
+
+#define	MCP04_CH2_SSTATUS	0x40
+#define	MCP04_CH2_SERROR	0x44
+#define	MCP04_CH2_SCTRL		0x48
+#define	MCP04_CH2_SACTIVE	0x4c
+#define	MCP04_CH2_SNOTIFICATION	0x50
+
+
+/*
+ * bar 5 offsets for ADMACTL settings for both mcp04/mcp55
+ */
+#define	NV_ADMACTL_X	0x4C0
+#define	NV_ADMACTL_Y	0x5C0
+
+/*
+ * Bits for NV_ADMACTL_X and NV_ADMACTL_Y
+ */
+#define	NV_HIRQ_EN	0x01 /* hot plug/unplug interrupt enable */
+#define	NV_CH_RST	0x04 /* reset channel */
+
+
+/*
+ * bar 5 offset for ADMASTAT regs for mcp04
+ */
+#define	MCP04_ADMASTAT_X	0x4C4
+#define	MCP04_ADMASTAT_Y	0x5C4
+
+/*
+ * Bits for MCP04_ADMASTAT_X and MCP04_ADMASTAT_Y
+ */
+#define	MCP04_HPIRQ	0x4
+#define	MCP05_HUIRQ	0x2
+
+
+/*
+ * bar 4 offset to bus master command registers
+ */
+#define	BMICX_REG	0
+
+/*
+ * bit definitions for BMICX_REG
+ */
+#define	BMICX_SSBM	0x01	/* Start/Stop Bus Master */
+				/* 1=Start (Enable) */
+				/* 0=Start (Disable) */
+
+/*
+ * NOTE: "read" and "write" are the actions of the DMA engine
+ * on the PCI bus, not the SATA bus.  Therefore for a ATA READ
+ * command, program the DMA engine to "write to memory" mode
+ * (and vice versa).
+ */
+#define	BMICX_RWCON			0x08 /* Read/Write Control */
+#define	BMICX_RWCON_WRITE_TO_MEMORY	0x08 /* 1=Write (dev to host) */
+#define	BMICX_RWCON_READ_FROM_MEMORY	0x00 /* 0=Read  (host to dev) */
+
+/*
+ * BMICX bits to preserve during updates
+ */
+#define	BMICX_MASK	(~(BMICX_SSBM | BMICX_RWCON))
+
+/*
+ * bar 4 offset to bus master status register
+ */
+#define	BMISX_REG	2
+
+/*
+ * bit fields for bus master status register
+ */
+#define	BMISX_BMIDEA	0x01	/* Bus Master IDE Active */
+#define	BMISX_IDERR	0x02	/* IDE DMA Error */
+#define	BMISX_IDEINTS	0x04	/* IDE Interrupt Status */
+
+/*
+ * bus master status register bits to preserve
+ */
+#define	BMISX_MASK	0xf8
+
+/*
+ * bar4 offset to bus master PRD descriptor table
+ */
+#define	BMIDTPX_REG	4
+
+
+/*
+ * structure for a single entry in the PRD table
+ * (physical region descriptor table)
+ */
+typedef struct prde {
+	uint32_t p_address; /* physical address */
+	uint32_t p_count;   /* byte count, EOT in high order bit */
+} prde_t;
+
+
+#define	PRDE_EOT	((uint_t)0x80000000)
+
+#define	NV_DMA_NSEGS	256  /* XXX DEBUG TEST change back to 257 */
+
+/*
+ * ck804 and mcp55 both have 2 ports per controller
+ */
+#define	NV_NUM_CPORTS	2
+
+/*
+ * Number of slots to allocate in data nv_sata structures to handle
+ * multiple commands at once.  This does not reflect the capability of
+ * the drive or the hardware, and in many cases will not match.
+ * 1 or 32 slots are allocated, so in cases where the driver has NCQ
+ * enabled but the drive doesn't support it, or supports fewer than
+ * 32 slots, here may be an over allocation of memory.
+ */
+#ifdef NCQ
+#define	NV_QUEUE_SLOTS	32
+#else
+#define	NV_QUEUE_SLOTS	1
+#endif
+
+/*
+ * wait 30 seconds for signature
+ */
+#define	NV_SIG_TIMEOUT		45
+
+#define	NV_BM_64K_BOUNDARY	0x10000ull
+
+/*
+ * every 1 second
+ */
+#define	NV_ONE_SEC	1000000
+
+
+/*
+ * the amount of time link can be down during
+ * reset without taking action.
+ */
+#define	NV_LINK_LOST_OK	2
+
+/*
+ * nv_reset() flags
+ */
+#define	NV_RESET_SEND_EVENT	0x1 /* send reset event to sata module */
+#define	NV_RESET_WAIT		0x2 /* OK to block waiting for reset */
+
+
+
+#define	NV_RESET_ATTEMPTS 3
+
+/*
+ * nvp_state flags
+ */
+#define	NV_PORT_INACTIVE	0x001
+#define	NV_PORT_ABORTING	0x002
+#define	NV_PORT_HOTREMOVED	0x004
+#define	NV_PORT_INIT		0x008
+#define	NV_PORT_FAILED		0x010
+#define	NV_PORT_RESET		0x020
+#define	NV_PORT_RESET_PROBE	0x040
+#define	NV_PORT_RESTORE		0x080
+
+/*
+ * nvc_state flags
+ */
+#define	NV_CTRL_SUSPEND		0x1
+
+
+/*
+ * flags for mcp04_set_intr/mcp55_set_intr
+ */
+#define	NV_INTR_DISABLE		0x1
+#define	NV_INTR_ENABLE		0x2
+#define	NV_INTR_CLEAR_ALL	0x4
+
+/*
+ * sizes of strings to allocate
+ */
+#define	NV_STRING_10	10
+#define	NV_STRING_512	512
+
+#define	NV_BYTES_PER_SEC 512
+
+#define	NV_WAIT_REG_CHECK	10	/* 10 microseconds */
+#define	NV_ATA_NUM_CMDS		256	/* max num ATA cmds possible, 8 bits */
+#define	NV_PRINT_INTERVAL	40	/* throttle debug msg from flooding */
+#define	MCP55_INT_CLEAR		0xffff	/* clear all interrupts */
+
+/*
+ * definition labels for the BAR registers
+ */
+#define	NV_BAR_0 0 /* chan 0 task file regs */
+#define	NV_BAR_1 1 /* chan 0 status reg */
+#define	NV_BAR_2 2 /* chan 1 task file regs */
+#define	NV_BAR_3 3 /* chan 1 status reg */
+#define	NV_BAR_4 4 /* bus master regs */
+#define	NV_BAR_5 5 /* extra regs mostly SATA related */
+
+/*
+ * transform seconds to microseconds
+ */
+#define	NV_SEC2USEC(x) x * MICROSEC
+
+
+/*
+ * ck804 maps in task file regs into bar 5.  These are
+ * only used to identify ck804, therefore only this reg is
+ * listed here.
+ */
+#define	NV_BAR5_TRAN_LEN_CH_X	0x518
+
+/*
+ * if after this many iterations through the interrupt
+ * processing loop, declare the interrupt wedged and
+ * disable.
+ */
+#define	NV_MAX_INTR_LOOP 10
+
+/*
+ * flag values for nv_copy_regs_out
+ */
+#define	NV_COPY_COMPLETE 0x01	/* normal command completion */
+#define	NV_COPY_ERROR    0x02	/* error, did not complete ok */
+#define	NV_COPY_SSREGS   0x04	/* SS port registers */
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _NV_SATA_H */
--- a/usr/src/uts/common/sys/sata/adapters/si3124/si3124reg.h	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/common/sys/sata/adapters/si3124/si3124reg.h	Thu Aug 16 14:46:34 2007 -0700
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -338,23 +338,10 @@
 #define	SSTATUS_IPM_MASK	0x00000f00
 #define	SSTATUS_IPM_SHIFT	8
 
-#define	SSTATUS_GET_DET(x)		\
-	(x & SSTATUS_DET_MASK)
-
-#define	SSTATUS_SET_DET(x, new_val)	\
-	(x = (x & ~SSTATUS_DET_MASK) | (new_val & SSTATUS_DET_MASK))
 
 #define	SSTATUS_DET_NODEV_NOPHY		 0x0 /* No device, no PHY */
 #define	SSTATUS_DET_DEVPRESENT_NOPHY	 0x1 /* Dev present, no PHY */
 #define	SSTATUS_DET_DEVPRESENT_PHYONLINE 0x3 /* Dev present, PHY online */
-#define	SSTATUS_DET_PHYOFFLINE		 0x4 /* PHY offline */
-
-#define	SSTATUS_GET_IPM(x)	\
-	((x & SSTATUS_IPM_MASK) >> SSTATUS_IPM_SHIFT)
-
-#define	SSTATUS_SET_IPM(x, new_val)					\
-	(x = (x & ~SSTATUS_IPM_MASK) | 					\
-		((new_val << SSTATUS_IPM_SHIFT) & SSTATUS_IPM_MASK))
 
 #define	SSTATUS_IPM_NODEV_NOPHY			0x0 /* No dev, no PHY */
 #define	SSTATUS_IPM_INTERFACE_ACTIVE		0x1 /* Interface active */
@@ -364,14 +351,8 @@
 /* SControl bit fields */
 #define	SCONTROL_DET_MASK	0x0000000f
 
-#define	SCONTROL_GET_DET(x)		\
-	(x & SCONTROL_DET_MASK)
 
-#define	SCONTROL_SET_DET(x, new_val)	\
-	(x = (x & ~SCONTROL_DET_MASK) | (new_val & SCONTROL_DET_MASK))
 
-#define	SCONTROL_DET_NOACTION		0x0 /* No action requested */
-#define	SCONTROL_DET_COMRESET		0x1 /* Send COMRESET */
 
 /* Command Error codes */
 #define	CMD_ERR_DEVICEERRROR		1
--- a/usr/src/uts/common/sys/sata/sata_defs.h	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/common/sys/sata/sata_defs.h	Thu Aug 16 14:46:34 2007 -0700
@@ -622,6 +622,109 @@
 
 #define	MODEPAGE_ACOUSTIC_MANAG 0x30
 
+/*
+ * sstatus field definitions
+ */
+#define	SSTATUS_DET_SHIFT	0
+#define	SSTATUS_SPD_SHIFT	4
+#define	SSTATUS_IPM_SHIFT	8
+
+#define	SSTATUS_DET	(0xf << SSTATUS_DET_SHIFT)
+#define	SSTATUS_SPD	(0xf << SSTATUS_SPD_SHIFT)
+#define	SSTATUS_IPM	(0xf << SSTATUS_IPM_SHIFT)
+
+/*
+ * sstatus DET values
+ */
+#define	SSTATUS_DET_NODEV		0	/* No dev detected */
+#define	SSTATUS_DET_DEVPRE_NOPHYCOM	1	/* dev detected */
+#define	SSTATUS_DET_DEVPRE_PHYCOM	3	/* dev detected */
+#define	SSTATUS_DET_PHYOFFLINE		4	/* PHY is in offline */
+
+#define	SSTATUS_GET_DET(x) \
+	(x & SSTATUS_DET)
+
+#define	SSTATUS_SET_DET(x, new_val) \
+	(x = (x & ~SSTATUS_DET) | (new_val & SSTATUS_DET))
+
+#define	SSTATUS_SPD_NOLIMIT	0 /* No speed limit */
+#define	SSTATUS_SPD_GEN1	1 /* Limit Gen 1 rate */
+#define	SSTATUS_SPD_GEN2	2 /* Limit Gen 2 rate */
+
+/*
+ * sstatus IPM values
+ */
+#define	SSTATUS_IPM_NODEV_NOPHYCOM	0x0 /* No dev, no PHY */
+#define	SSTATUS_IPM_ACTIVE		0x1 /* Interface active */
+#define	SSTATUS_IPM_POWERPARTIAL	0x2 /* partial power mgmnt */
+#define	SSTATUS_IPM_POWERSLUMBER	0x6 /* slumber power mgmt */
+
+#define	SSTATUS_GET_IPM(x) \
+	((x & SSTATUS_IPM) >> SSTATUS_IPM_SHIFT)
+
+#define	SSTATUS_SET_IPM(x, new_val) \
+	(x = (x & ~SSTATUS_IPM) | \
+	((new_val << SSTATUS_IPM_SHIFT) & SSTATUS_IPM))
+
+
+/*
+ * serror register fields
+ */
+#define	SERROR_DATA_ERR_FIXED	(1 << 0) /* D integrity err */
+#define	SERROR_COMM_ERR_FIXED	(1 << 1) /* comm err recov */
+#define	SERROR_DATA_ERR		(1 << 8) /* D integrity err */
+#define	SERROR_PERSISTENT_ERR	(1 << 9)  /* norecov com err */
+#define	SERROR_PROTOCOL_ERR	(1 << 10) /* protocol err */
+#define	SERROR_INT_ERR		(1 << 11) /* internal err */
+#define	SERROR_PHY_RDY_CHG	(1 << 16) /* PHY state change */
+#define	SERROR_PHY_INT_ERR	(1 << 17) /* PHY internal err */
+#define	SERROR_COMM_WAKE	(1 << 18) /* COM wake */
+#define	SERROR_10B_TO_8B_ERR	(1 << 19) /* 10B-to-8B decode */
+#define	SERROR_DISPARITY_ERR	(1 << 20) /* disparity err */
+#define	SERROR_CRC_ERR		(1 << 21) /* CRC err */
+#define	SERROR_HANDSHAKE_ERR	(1 << 22) /* Handshake err */
+#define	SERROR_LINK_SEQ_ERR	(1 << 23) /* Link seq err */
+#define	SERROR_TRANS_ERR	(1 << 24) /* Tran state err */
+#define	SERROR_FIS_TYPE		(1 << 25) /* FIS type err */
+#define	SERROR_EXCHANGED_ERR	(1 << 26) /* Device exchanged */
+
+/*
+ * S-Control Bridge port x register fields
+ */
+#define	SCONTROL_DET_SHIFT	0
+#define	SCONTROL_SPD_SHIFT	4
+#define	SCONTROL_IPM_SHIFT	8
+#define	SCONTROL_SPM_SHIFT	12
+
+#define	SCONTROL_DET		(0xf << SSTATUS_DET_SHIFT)
+#define	SCONTROL_SPD		(0xf << SSTATUS_SPD_SHIFT)
+#define	SCONTROL_IPM		(0xf << SSTATUS_IPM_SHIFT)
+#define	SCONTROL_SPM		(0xf << SSTATUS_SPM_SHIFT)
+
+#define	SCONTROL_GET_DET(x)	\
+	(x & SCONTROL_DET)
+
+#define	SCONTROL_SET_DET(x, new_val)    \
+	(x = (x & ~SCONTROL_DET) | (new_val & SCONTROL_DET))
+
+#define	SCONTROL_DET_NOACTION	0 /* Do nothing to port */
+#define	SCONTROL_DET_COMRESET	1 /* Re-initialize port */
+#define	SCONTROL_DET_DISABLE	4 /* Disable port */
+
+#define	SCONTROL_SPD_NOLIMIT	0 /* No speed limit */
+#define	SCONTROL_SPD_GEN1	1 /* Limit Gen 1 rate */
+#define	SCONTROL_SPD_GEN2	2 /* Limit Gen 2 rate */
+
+#define	SCONTROL_IPM_NORESTRICT		0 /* No PM limit */
+#define	SCONTROL_IPM_DISABLE_PARTIAL	1 /* Disable partial */
+#define	SCONTROL_IPM_DISABLE_SLUMBER	2 /* Disable slumber */
+#define	SCONTROL_IPM_DISABLE_BOTH	3 /* Disable both */
+
+#define	SCONTROL_SPM_NORESTRICT		0 /* No PM limits */
+#define	SCONTROL_SPM_DO_PARTIAL		1 /* Go to partial */
+#define	SCONTROL_SPM_DO_SLUMBER		2 /* Go to slumber */
+#define	SCONTROL_SPM_DO_ACTIVE		4 /* Go to active */
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/common/sys/sunddi.h	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/common/sys/sunddi.h	Thu Aug 16 14:46:34 2007 -0700
@@ -1997,6 +1997,9 @@
 ddi_devid_init(dev_info_t *dip, ushort_t devid_type, ushort_t nbytes,
     void *id, ddi_devid_t *ret_devid);
 
+int
+ddi_devid_get(dev_info_t *dip, ddi_devid_t *ret_devid);
+
 size_t
 ddi_devid_sizeof(ddi_devid_t devid);
 
--- a/usr/src/uts/intel/Makefile.intel.shared	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/intel/Makefile.intel.shared	Thu Aug 16 14:46:34 2007 -0700
@@ -266,6 +266,7 @@
 DRV_KMODS_32	+= mscsi
 DRV_KMODS_32	+= msm
 DRV_KMODS	+= nca
+DRV_KMODS	+= nv_sata
 DRV_KMODS	+= openeepr
 DRV_KMODS	+= pci_pci
 DRV_KMODS	+= pcic
--- a/usr/src/uts/intel/io/pci/pci_boot.c	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/intel/io/pci/pci_boot.c	Thu Aug 16 14:46:34 2007 -0700
@@ -914,7 +914,7 @@
 	ushort_t subvenid, subdevid, status;
 	ushort_t slot_num;
 	uint_t classcode, revclass;
-	int reprogram = 0, pciide;
+	int reprogram = 0, pciide = 0;
 	int power[2] = {1, 1};
 	int pciex = 0;
 	ushort_t is_pci_bridge = 0;
@@ -952,12 +952,9 @@
 	basecl = classcode >> 16;
 	subcl = (classcode >> 8) & 0xff;
 	progcl = classcode & 0xff;
-	pciide = is_pciide(basecl, subcl, revid, vendorid, deviceid,
-	    subvenid, subdevid);
+
 
-	if (pciide)
-		(void) snprintf(nodename, sizeof (nodename), "pci-ide");
-	else if (is_display(classcode))
+	if (is_display(classcode))
 		(void) snprintf(nodename, sizeof (nodename), "display");
 	else if (subvenid != 0)
 		(void) snprintf(nodename, sizeof (nodename),
@@ -1066,6 +1063,29 @@
 
 	add_compatible(dip, subvenid, subdevid, vendorid, deviceid,
 	    revid, classcode, pciex);
+
+	/*
+	 * See if this device is a controller that advertises
+	 * itself to be a standard ATA task file controller, or one that
+	 * has been hard coded.
+	 *
+	 * If it is, check if any other higher precedence driver listed in
+	 * driver_aliases will claim the node by calling
+	 * ddi_compatibile_driver_major.  If so, clear pciide and do not
+	 * create a pci-ide node or any other special handling.
+	 *
+	 * If another driver does not bind, set the node name to pci-ide
+	 * and then let the special pci-ide handling for registers and
+	 * child pci-ide nodes proceed below.
+	 */
+	if (is_pciide(basecl, subcl, revid, vendorid, deviceid,
+	    subvenid, subdevid) == 1) {
+		if (ddi_compatible_driver_major(dip, NULL) == (major_t)-1) {
+			(void) ndi_devi_set_nodename(dip, "pci-ide", 0);
+			pciide = 1;
+		}
+	}
+
 	reprogram = add_reg_props(dip, bus, dev, func, config_op, pciide);
 	(void) ndi_devi_bind_driver(dip, 0);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/nv_sata/Makefile	Thu Aug 16 14:46:34 2007 -0700
@@ -0,0 +1,152 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= nv_sata
+OBJECTS		= $(NV_SATA_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(NV_SATA_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR     = $(UTSBASE)/common/io/sata/adapters/nv_sata
+WARLOCK_OUT     = $(NV_SATA_OBJS:%.o=%.ll)
+WARLOCK_OK      = $(MODULE).ok
+WLCMD_DIR	= $(UTSBASE)/common/io/warlock
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+# lint pass one enforcement
+#  
+CFLAGS += $(CCVERBOSE)
+
+#
+# dependency on sata module
+#
+LDFLAGS += -dy -N misc/sata
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS        += -erroff=E_BAD_PTR_CAST_ALIGN 
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+clobber:	$(CLOBBER_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+
+#
+#	Defines for local commands.
+#
+WARLOCK		= warlock
+WLCC		= wlcc
+TOUCH		= touch
+TEST		= test
+
+NV_SATA_FILES   = $(MODULE).ll
+SD_FILES = $(SD_OBJS:%.o=../sd/%.ll)
+SATA_FILES = $(SATA_OBJS:%.o=-l ../sata/%.ll)
+SCSI_FILES = $(SCSI_OBJS:%.o=-l ../scsi/%.ll)
+CMLB_FILES = $(CMLB_OBJS:%.o=-l ../cmlb/%.ll)
+ 
+warlock: $(WARLOCK_OK)
+
+
+$(WARLOCK_OK): $(WLCMD_DIR)/nv_sata.wlcmd $(WARLOCK_OUT)  warlock_ddi.files \
+	sata.files scsi.files sd.files cmlb.files
+	$(WARLOCK) -c $(WLCMD_DIR)/nv_sata.wlcmd $(WARLOCK_OUT) \
+	$(SD_FILES) \
+	$(SCSI_FILES) \
+	$(CMLB_FILES) \
+        $(SATA_FILES) \
+	-l ../warlock/ddi_dki_impl.ll
+	$(TOUCH) $@
+
+%.ll: $(UTSBASE)/common/io/sata/adapters/nv_sata/%.c
+	$(WLCC) $(CPPFLAGS) -D DEBUG -o $@ $<
+
+sata.files:
+	@cd ../sata; pwd; $(MAKE) warlock
+ 
+scsi.files:
+	@cd ../scsi; pwd; $(MAKE) warlock
+
+sd.files: 
+	@cd ../sd; pwd; $(MAKE) warlock_alone
+
+cmlb.files:
+	@cd ../cmlb; pwd; $(MAKE) warlock
+
+warlock_ddi.files:
+	@cd ../warlock; pwd; $(MAKE) warlock
--- a/usr/src/uts/intel/warlock/Makefile	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/intel/warlock/Makefile	Thu Aug 16 14:46:34 2007 -0700
@@ -129,6 +129,7 @@
 warlock.sata:
 	@cd ../sata; $(MAKE) clean; $(MAKE) warlock
 	@cd ../si3124; $(MAKE) clean; $(MAKE) warlock
+	@cd ../nv_sata; $(MAKE) clean; $(MAKE) warlock
 	@cd ../ahci; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/intel/marvell88sx; \
 		$(MAKE) clean; $(MAKE) warlock
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/nv_sata/Makefile	Thu Aug 16 14:46:34 2007 -0700
@@ -0,0 +1,138 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= nv_sata
+OBJECTS		= $(NV_SATA_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(NV_SATA_OBJS:%.o=$(LINTS_DIR)/%.ln)
+WARLOCK_OUT     = $(NV_SATA_OBJS:%.o=%.ll)
+WARLOCK_OK      = $(MODULE).ok
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR     = $(UTSBASE)/common/io/sata/adapters/nv_sata
+WLCMD_DIR       = $(UTSBASE)/common/io/warlock
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+# lint pass one enforcement
+#  
+CFLAGS += $(CCVERBOSE)
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+clobber:	$(CLOBBER_DEPS)
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+
+
+#
+#	Defines for local commands.
+#
+WARLOCK		= warlock
+WLCC		= wlcc
+TOUCH		= touch
+TEST		= test
+
+SD_FILES = $(SD_OBJS:%.o=../sd/%.ll)
+SATA_FILES = $(SATA_OBJS:%.o=-l ../sata/%.ll)
+SCSI_FILES = $(SCSI_OBJS:%.o=-l ../scsi/%.ll)
+CMLB_FILES = $(CMLB_OBJS:%.o=-l ../cmlb/%.ll)
+ 
+warlock: $(WARLOCK_OK)
+
+$(WARLOCK_OK): $(WLCMD_DIR)/nv_sata.wlcmd $(WARLOCK_OUT)  warlock_ddi.files \
+	sata.files scsi.files sd.files cmlb.files
+	$(WARLOCK) -c $(WLCMD_DIR)/nv_sata.wlcmd $(WARLOCK_OUT) \
+	$(SD_FILES) \
+	$(SCSI_FILES) \
+	$(CMLB_FILES) \
+        $(SATA_FILES) \
+	-l ../warlock/ddi_dki_impl.ll
+	$(TOUCH) $@
+
+%.ll: $(UTSBASE)/common/io/sata/adapters/nv_sata/%.c
+	$(WLCC) $(CPPFLAGS) -D DEBUG -D __sparcv9 -o $@ $<
+
+sata.files:
+	@cd ../sata; pwd; $(MAKE) warlock
+ 
+scsi.files:
+	@cd ../scsi; pwd; $(MAKE) warlock
+
+sd.files: 
+	@cd ../sd; pwd; $(MAKE) warlock_alone
+
+cmlb.files:
+	@cd ../cmlb; pwd; $(MAKE) warlock
+
+
+warlock_ddi.files:
+	@cd ../warlock; pwd; $(MAKE) warlock
--- a/usr/src/uts/sparc/warlock/Makefile	Thu Aug 16 12:02:04 2007 -0700
+++ b/usr/src/uts/sparc/warlock/Makefile	Thu Aug 16 14:46:34 2007 -0700
@@ -144,5 +144,6 @@
 	@cd ../sata; $(MAKE) clean; $(MAKE) warlock
 	@cd ../si3124; $(MAKE) clean; $(MAKE) warlock
 	@cd ../ahci; $(MAKE) clean; $(MAKE) warlock
+	@cd ../nv_sata; $(MAKE) clean; $(MAKE) warlock
 $(CLOSED_BUILD)	@cd $(CLOSED)/uts/sparc/marvell88sx; \
 		$(MAKE) clean; $(MAKE) warlock