changeset 14044:8fa61bfa2342

3797 AHCI: Support for ASMedia ASM106x Reviewed by: Albert Lee <trisk@nexenta.com> Reviewed by: Garrett D'Amore <garrett@damore.org> Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com> Approved by: Robert Mustacchi <rm@joyent.com>
author Marcel Telka <Marcel.Telka@nexenta.com>
date Sat, 08 Jun 2013 23:24:29 +0200
parents 0291cd939b43
children 9475b3fef59d
files usr/src/cmd/mdb/intel/modules/sata/sata.c usr/src/uts/common/io/sata/adapters/ahci/ahci.c usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h
diffstat 4 files changed, 243 insertions(+), 195 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/mdb/intel/modules/sata/sata.c	Mon Jun 10 09:51:40 2013 -0700
+++ b/usr/src/cmd/mdb/intel/modules/sata/sata.c	Sat Jun 08 23:24:29 2013 +0200
@@ -22,7 +22,9 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
-
+/*
+ * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ */
 
 #include <sys/mdb_modapi.h>
 #include <mdb/mdb_ks.h>
@@ -168,7 +170,7 @@
 					(void) mdb_ddi_pathname(
 					    (uintptr_t)dmsg.dip, pathname,
 					    sizeof (pathname));
-					mdb_printf("\n[%s]", pathname);
+					mdb_printf("[%s]", pathname);
 				}
 			}
 		} else {
@@ -181,7 +183,7 @@
 			    dmsg.buf);
 		}
 
-		mdb_printf("%s", merge);
+		mdb_printf("%s\n", merge);
 
 		if (printed != NULL) {
 			(*printed)++;
--- a/usr/src/uts/common/io/sata/adapters/ahci/ahci.c	Mon Jun 10 09:51:40 2013 -0700
+++ b/usr/src/uts/common/io/sata/adapters/ahci/ahci.c	Sat Jun 08 23:24:29 2013 +0200
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
@@ -105,13 +106,14 @@
 /*
  * Local function prototypes
  */
+static	int ahci_setup_port_base_addresses(ahci_ctl_t *, ahci_port_t *);
 static	int ahci_alloc_ports_state(ahci_ctl_t *);
 static	void ahci_dealloc_ports_state(ahci_ctl_t *);
 static	int ahci_alloc_port_state(ahci_ctl_t *, uint8_t);
 static	void ahci_dealloc_port_state(ahci_ctl_t *, uint8_t);
-static	int ahci_alloc_rcvd_fis(ahci_ctl_t *, ahci_port_t *, uint8_t);
+static	int ahci_alloc_rcvd_fis(ahci_ctl_t *, ahci_port_t *);
 static	void ahci_dealloc_rcvd_fis(ahci_port_t *);
-static	int ahci_alloc_cmd_list(ahci_ctl_t *, ahci_port_t *, uint8_t);
+static	int ahci_alloc_cmd_list(ahci_ctl_t *, ahci_port_t *);
 static	void ahci_dealloc_cmd_list(ahci_ctl_t *, ahci_port_t *);
 static  int ahci_alloc_cmd_tables(ahci_ctl_t *, ahci_port_t *);
 static  void ahci_dealloc_cmd_tables(ahci_ctl_t *, ahci_port_t *);
@@ -122,6 +124,7 @@
 static	void ahci_uninitialize_controller(ahci_ctl_t *);
 static	int ahci_initialize_port(ahci_ctl_t *, ahci_port_t *, ahci_addr_t *);
 static	int ahci_config_space_init(ahci_ctl_t *);
+static	void ahci_staggered_spin_up(ahci_ctl_t *, uint8_t);
 
 static	void ahci_drain_ports_taskq(ahci_ctl_t *);
 static	int ahci_rdwr_pmult(ahci_ctl_t *, ahci_addr_t *, uint8_t, uint32_t *,
@@ -454,6 +457,10 @@
 		goto err_out;
 	}
 
+	/* watchdog tick */
+	ahci_watchdog_tick = drv_usectohz(
+	    (clock_t)ahci_watchdog_timeout * 1000000);
+
 	ret = mod_install(&modlinkage);
 	if (ret != 0) {
 		sata_hba_fini(&modlinkage);
@@ -464,9 +471,6 @@
 		goto err_out;
 	}
 
-	/* watchdog tick */
-	ahci_watchdog_tick = drv_usectohz(
-	    (clock_t)ahci_watchdog_timeout * 1000000);
 	return (ret);
 
 err_out:
@@ -517,6 +521,7 @@
 	int status;
 	int attach_state;
 	uint32_t cap_status, ahci_version;
+	uint32_t ghc_control;
 	int intr_types;
 	int i;
 	pci_regspec_t *regs;
@@ -544,6 +549,16 @@
 		ahci_ctlp = ddi_get_soft_state(ahci_statep, instance);
 		mutex_enter(&ahci_ctlp->ahcictl_mutex);
 
+		/*
+		 * GHC.AE must be set to 1 before any other AHCI register
+		 * is accessed
+		 */
+		ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+		    (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp));
+		ghc_control |= AHCI_HBA_GHC_AE;
+		ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+		    (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control);
+
 		/* Restart watch thread */
 		if (ahci_ctlp->ahcictl_timeout_id == 0)
 			ahci_ctlp->ahcictl_timeout_id = timeout(
@@ -655,6 +670,16 @@
 
 	attach_state |= AHCI_ATTACH_STATE_REG_MAP;
 
+	/*
+	 * GHC.AE must be set to 1 before any other AHCI register
+	 * is accessed
+	 */
+	ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp));
+	ghc_control |= AHCI_HBA_GHC_AE;
+	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp), ghc_control);
+
 	/* Get the AHCI version information */
 	ahci_version = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
 	    (uint32_t *)AHCI_GLOBAL_VS(ahci_ctlp));
@@ -678,6 +703,18 @@
 	AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba capabilities = 0x%x",
 	    cap_status);
 
+	/* CAP2 (HBA Capabilities Extended) is available since AHCI spec 1.2 */
+	if (ahci_version >= 0x00010200) {
+		uint32_t cap2_status;
+
+		/* Get the HBA capabilities extended information */
+		cap2_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+		    (uint32_t *)AHCI_GLOBAL_CAP2(ahci_ctlp));
+
+		AHCIDBG(AHCIDBG_INIT, ahci_ctlp,
+		    "hba capabilities extended = 0x%x", cap2_status);
+	}
+
 #if AHCI_DEBUG
 	/* Get the interface speed supported by the HBA */
 	speed = (cap_status & AHCI_HBA_CAP_ISS) >> AHCI_HBA_CAP_ISS_SHIFT;
@@ -709,20 +746,12 @@
 	AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba implementation of ports: 0x%x",
 	    ahci_ctlp->ahcictl_ports_implemented);
 
-	/*
-	 * According to the AHCI spec, CAP.NP should indicate the maximum
-	 * number of ports supported by the HBA silicon, but we found
-	 * this value of ICH8 chipset only indicates the number of ports
-	 * implemented (exposed) by it. Therefore, the driver should calculate
-	 * the potential maximum value by checking PI register, and use
-	 * the maximum of this value and CAP.NP.
-	 */
-	ahci_ctlp->ahcictl_num_ports = max(
-	    (cap_status & AHCI_HBA_CAP_NP) + 1,
-	    ddi_fls(ahci_ctlp->ahcictl_ports_implemented));
+	/* Max port number implemented */
+	ahci_ctlp->ahcictl_num_ports =
+	    ddi_fls(ahci_ctlp->ahcictl_ports_implemented);
 
 	AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "hba number of ports: %d",
-	    ahci_ctlp->ahcictl_num_ports);
+	    (cap_status & AHCI_HBA_CAP_NP) + 1);
 
 	/* Get the number of implemented ports by the HBA */
 	ahci_ctlp->ahcictl_num_implemented_ports =
@@ -3537,6 +3566,7 @@
 	}
 	return (DDI_SUCCESS);
 }
+
 /*
  * Allocate the ports structure, only called by ahci_attach
  */
@@ -3637,28 +3667,11 @@
 {
 	ahci_port_t *ahci_portp;
 	ahci_addr_t addr;
-	uint32_t ghc_control;
 	int port;
 
 	AHCIDBG(AHCIDBG_INIT|AHCIDBG_ENTRY, ahci_ctlp,
 	    "ahci_initialize_controller enter", NULL);
 
-	mutex_enter(&ahci_ctlp->ahcictl_mutex);
-
-	/*
-	 * Indicate that system software is AHCI aware by setting
-	 * GHC.AE to 1
-	 */
-	ghc_control = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
-	    (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp));
-
-	ghc_control |= AHCI_HBA_GHC_AE;
-	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
-	    (uint32_t *)AHCI_GLOBAL_GHC(ahci_ctlp),
-	    ghc_control);
-
-	mutex_exit(&ahci_ctlp->ahcictl_mutex);
-
 	/* Initialize the implemented ports and structures */
 	for (port = 0; port < ahci_ctlp->ahcictl_num_ports; port++) {
 		if (!AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)) {
@@ -3821,6 +3834,41 @@
 }
 
 /*
+ * Staggered Spin-up.
+ *
+ * WARNING!!! ahciport_mutex should be acquired before the function
+ * is called.
+ */
+static void
+ahci_staggered_spin_up(ahci_ctl_t *ahci_ctlp, uint8_t port)
+{
+	uint32_t cap_status;
+	uint32_t port_cmd_status;
+
+	cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp));
+
+	/* Check for staggered spin-up support */
+	if (!(cap_status & AHCI_HBA_CAP_SSS))
+		return;
+
+	port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
+
+	/* If PxCMD.SUD == 1, no staggered spin-up is needed */
+	if (port_cmd_status & AHCI_CMD_STATUS_SUD)
+		return;
+
+	AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "Spin-up at port %d", port);
+
+	/* Set PxCMD.SUD */
+	port_cmd_status |= AHCI_CMD_STATUS_SUD;
+	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
+	    port_cmd_status);
+}
+
+/*
  * The routine is to initialize a port. First put the port in NOTRunning
  * state, then enable port interrupt and clear Serror register. And under
  * AHCI_ATTACH case, find device signature and then try to start the port.
@@ -3874,15 +3922,9 @@
 		    "set PxCLB, PxCLBU, PxFB and PxFBU "
 		    "during resume", port);
 
-		/* Config Port Received FIS Base Address */
-		ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port),
-		    ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress);
-
-		/* Config Port Command List Base Address */
-		ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port),
-		    ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress);
+		if (ahci_setup_port_base_addresses(ahci_ctlp, ahci_portp) !=
+		    AHCI_SUCCESS)
+			return (AHCI_FAILURE);
 	}
 
 	port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
@@ -3904,6 +3946,9 @@
 		    ahci_portp, port);
 	}
 
+	/* Make sure the drive is spun-up */
+	ahci_staggered_spin_up(ahci_ctlp, port);
+
 	/* Disable interrupt */
 	ahci_disable_port_intrs(ahci_ctlp, port);
 
@@ -3939,7 +3984,7 @@
 		if (ret != AHCI_SUCCESS) {
 			AHCIDBG(AHCIDBG_INIT|AHCIDBG_ERRS, ahci_ctlp,
 			    "ahci_initialize_port:"
-			    "port reset faild at port %d", port);
+			    "port reset failed at port %d", port);
 			return (AHCI_FAILURE);
 		}
 
@@ -3952,24 +3997,26 @@
 			return (AHCI_FAILURE);
 		}
 	}
+
 	AHCIPORT_SET_STATE(ahci_portp, addrp, SATA_STATE_READY);
 	AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "port %d is ready now.", port);
 
 	/*
 	 * Try to get the device signature if the port is not empty.
 	 */
-	if (!resuming && ahci_portp->ahciport_device_type != SATA_DTYPE_NONE)
+	if (!resuming && AHCIPORT_DEV_TYPE(ahci_portp, addrp) !=
+	    SATA_DTYPE_NONE)
 		ahci_find_dev_signature(ahci_ctlp, ahci_portp, addrp);
 
 	/* Return directly if no device connected */
-	if (ahci_portp->ahciport_device_type == SATA_DTYPE_NONE) {
+	if (AHCIPORT_DEV_TYPE(ahci_portp, addrp) == SATA_DTYPE_NONE) {
 		AHCIDBG(AHCIDBG_INIT, ahci_ctlp,
 		    "No device connected to port %d", port);
 		goto out;
 	}
 
 	/* If this is a port multiplier, we need do some initialization */
-	if (ahci_portp->ahciport_device_type == SATA_DTYPE_PMULT) {
+	if (AHCIPORT_DEV_TYPE(ahci_portp, addrp) == SATA_DTYPE_PMULT) {
 		AHCIDBG(AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp,
 		    "Port multiplier found at port %d", port);
 		ahci_alloc_pmult(ahci_ctlp, ahci_portp);
@@ -4884,8 +4931,8 @@
  * the port must be idle and PxTFD.STS.BSY and PxTFD.STS.DRQ must be
  * cleared unless command list override (PxCMD.CLO) is supported.
  *
- * WARNING!!! ahciport_mutex should be acquired and PxCMD.FRE should be
- * set before the function is called.
+ * WARNING!!! ahciport_mutex should be acquired before the function
+ * is called.
  */
 static int
 ahci_software_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
@@ -5130,17 +5177,12 @@
  *
  * When an HBA or port reset occurs, Phy communication is going to
  * be re-established with the device through a COMRESET followed by the
- * normal out-of-band communication sequence defined in Serial ATA. AT
+ * normal out-of-band communication sequence defined in Serial ATA. At
  * the end of reset, the device, if working properly, will send a D2H
  * Register FIS, which contains the device signature. When the HBA receives
  * this FIS, it updates PxTFD.STS and PxTFD.ERR register fields, and updates
  * the PxSIG register with the signature.
  *
- * Staggered spin-up is an optional feature in SATA II, and it enables an HBA
- * to individually spin-up attached devices. Please refer to chapter 10.9 of
- * AHCI 1.0 spec.
- */
-/*
  * WARNING!!! ahciport_mutex should be acquired, and PxCMD.ST should be also
  * cleared before the function is called.
  */
@@ -5149,7 +5191,7 @@
     ahci_addr_t *addrp)
 {
 	ahci_addr_t pmult_addr;
-	uint32_t cap_status, port_cmd_status;
+	uint32_t port_cmd_status;
 	uint32_t port_scontrol, port_sstatus, port_serror;
 	uint32_t port_intr_status, port_task_file;
 	uint32_t port_state;
@@ -5169,117 +5211,45 @@
 	    "Port %d port resetting...", port);
 	ahci_portp->ahciport_port_state = 0;
 
-	cap_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
-	    (uint32_t *)AHCI_GLOBAL_CAP(ahci_ctlp));
-
 	port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
 	    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
 
-	if (cap_status & AHCI_HBA_CAP_SSS) {
-		/*
-		 * HBA support staggered spin-up, if the port has
-		 * not spin up yet, then force it to do spin-up
-		 */
-		if (!(port_cmd_status & AHCI_CMD_STATUS_SUD)) {
-			if (!(ahci_portp->ahciport_flags
-			    & AHCI_PORT_FLAG_SPINUP)) {
-				AHCIDBG(AHCIDBG_INIT, ahci_ctlp,
-				    "Port %d PxCMD.SUD is zero, force "
-				    "it to do spin-up", port);
-				ahci_portp->ahciport_flags |=
-				    AHCI_PORT_FLAG_SPINUP;
-			}
-		}
-	} else {
-		/*
-		 * HBA doesn't support stagger spin-up, force it
-		 * to do normal COMRESET
-		 */
-		if (ahci_portp->ahciport_flags &
-		    AHCI_PORT_FLAG_SPINUP) {
-			AHCIDBG(AHCIDBG_INIT, ahci_ctlp,
-			    "HBA does not support staggered spin-up "
-			    "force it to do normal COMRESET", NULL);
-			ahci_portp->ahciport_flags &=
-			    ~AHCI_PORT_FLAG_SPINUP;
-		}
-	}
-
-	if (!(ahci_portp->ahciport_flags & AHCI_PORT_FLAG_SPINUP)) {
-		/* Do normal COMRESET */
-		AHCIDBG(AHCIDBG_INFO, ahci_ctlp,
-		    "ahci_port_reset: do normal COMRESET", port);
-
-		/*
-		 * According to the spec, SUD bit should be set here,
-		 * but JMicron JMB363 doesn't follow it, so remove
-		 * the assertion, and just print a debug message.
-		 */
-#if AHCI_DEBUG
-		if (!(port_cmd_status & AHCI_CMD_STATUS_SUD))
-			AHCIDBG(AHCIDBG_ERRS, ahci_ctlp,
-			    "port %d SUD bit not set", port)
-#endif
-
-		port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port));
-		SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_COMRESET);
-
-		ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port),
-		    port_scontrol);
-
-		/* Enable PxCMD.FRE to read device */
-		ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
-		    port_cmd_status|AHCI_CMD_STATUS_FRE);
-
-		/*
-		 * Give time for COMRESET to percolate, according to the AHCI
-		 * spec, software shall wait at least 1 millisecond before
-		 * clearing PxSCTL.DET
-		 */
-		drv_usecwait(AHCI_1MS_USECS*2);
-
-		/* Fetch the SCONTROL again and rewrite the DET part with 0 */
-		port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port));
-		SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION);
-		ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port),
-		    port_scontrol);
-	} else {
-		/* Do staggered spin-up */
-		port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port));
-		SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION);
-
-		/* PxSCTL.DET must be 0 */
-		ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port),
-		    port_scontrol);
-
-		port_cmd_status &= ~AHCI_CMD_STATUS_SUD;
-		ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
-		    port_cmd_status);
-
-		/* 0 -> 1 edge */
-		drv_usecwait(AHCI_1MS_USECS*2);
-
-		/* Set PxCMD.SUD to 1 */
-		port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
-		port_cmd_status |= AHCI_CMD_STATUS_SUD;
-		ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
-		    port_cmd_status);
-
-		/* Enable PxCMD.FRE to read device */
-		ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
-		    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
-		    port_cmd_status|AHCI_CMD_STATUS_FRE);
-	}
+	/*
+	 * According to the spec, SUD bit should be set here,
+	 * but JMicron JMB363 doesn't follow it, so print
+	 * a debug message.
+	 */
+	if (!(port_cmd_status & AHCI_CMD_STATUS_SUD))
+		AHCIDBG(AHCIDBG_ERRS, ahci_ctlp,
+		    "ahci_port_reset: port %d SUD bit not set", port);
+
+	port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port));
+	SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_COMRESET);
+
+	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port),
+	    port_scontrol);
+
+	/* Enable PxCMD.FRE to read device */
+	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
+	    port_cmd_status|AHCI_CMD_STATUS_FRE);
+
+	/*
+	 * Give time for COMRESET to percolate, according to the AHCI
+	 * spec, software shall wait at least 1 millisecond before
+	 * clearing PxSCTL.DET
+	 */
+	drv_usecwait(AHCI_1MS_USECS * 2);
+
+	/* Fetch the SCONTROL again and rewrite the DET part with 0 */
+	port_scontrol = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port));
+	SCONTROL_SET_DET(port_scontrol, SCONTROL_DET_NOACTION);
+	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port),
+	    port_scontrol);
 
 	/*
 	 * The port enters P:StartComm state, and HBA tells link layer to
@@ -5645,7 +5615,7 @@
  *
  * When an HBA reset occurs, Phy communication will be re-established with
  * the device through a COMRESET followed by the normal out-of-band
- * communication sequence defined in Serial ATA. AT the end of reset, the
+ * communication sequence defined in Serial ATA. At the end of reset, the
  * device, if working properly, will send a D2H Register FIS, which contains
  * the device signature. When the HBA receives this FIS, it updates PxTFD.STS
  * and PxTFD.ERR register fields, and updates the PxSIG register with the
@@ -5657,7 +5627,6 @@
 ahci_hba_reset(ahci_ctl_t *ahci_ctlp)
 {
 	ahci_port_t *ahci_portp;
-	ahci_addr_t addr;
 	uint32_t ghc_control;
 	uint8_t port;
 	int loop_count;
@@ -5728,7 +5697,8 @@
 		ahci_portp = ahci_ctlp->ahcictl_ports[port];
 		mutex_enter(&ahci_portp->ahciport_mutex);
 
-		AHCI_ADDR_SET_PORT(&addr, port);
+		/* Make sure the drive is spun-up */
+		ahci_staggered_spin_up(ahci_ctlp, port);
 
 		if (ahci_restart_port_wait_till_ready(ahci_ctlp, ahci_portp,
 		    port, AHCI_PORT_RESET|AHCI_RESET_NO_EVENTS_UP, NULL) !=
@@ -5854,7 +5824,6 @@
 	signature = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
 	    (uint32_t *)AHCI_PORT_PxSIG(ahci_ctlp, port));
 
-#ifdef AHCI_DEBUG
 	if (AHCI_ADDR_IS_PMPORT(addrp)) {
 		AHCIDBG(AHCIDBG_INIT|AHCIDBG_INFO|AHCIDBG_PMULT, ahci_ctlp,
 		    "ahci_find_dev_signature: signature = 0x%x at port %d:%d",
@@ -5864,7 +5833,6 @@
 		    "ahci_find_dev_signature: signature = 0x%x at port %d",
 		    signature, port);
 	}
-#endif
 
 	/* NOTE: Only support ATAPI device at controller port. */
 	if (signature == AHCI_SIGNATURE_ATAPI && !AHCI_ADDR_IS_PORT(addrp))
@@ -5995,6 +5963,87 @@
 }
 
 /*
+ * Setup PxCLB, PxCLBU, PxFB, and PxFBU for particular port. First, we need
+ * to make sure PxCMD.ST, PxCMD.CR, PxCMD.FRE, and PxCMD.FR are all cleared.
+ * Then set PxCLB, PxCLBU, PxFB, and PxFBU.
+ *
+ * WARNING!!! ahciport_mutex should be acquired before the function is called.
+ */
+static int
+ahci_setup_port_base_addresses(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp)
+{
+	uint8_t port = ahci_portp->ahciport_port_num;
+	uint32_t port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
+
+	/* Step 1: Make sure both PxCMD.ST and PxCMD.CR are cleared. */
+	if (port_cmd_status & (AHCI_CMD_STATUS_ST | AHCI_CMD_STATUS_CR)) {
+		if (ahci_put_port_into_notrunning_state(ahci_ctlp, ahci_portp,
+		    port) != AHCI_SUCCESS)
+			return (AHCI_FAILURE);
+
+		port_cmd_status = ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+		    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
+	}
+
+	/* Step 2: Make sure both PxCMD.FRE and PxCMD.FR are cleared. */
+	if (port_cmd_status & (AHCI_CMD_STATUS_FRE | AHCI_CMD_STATUS_FR)) {
+		int loop_count = 0;
+
+		/* Clear PxCMD.FRE */
+		port_cmd_status &= ~AHCI_CMD_STATUS_FRE;
+		ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+		    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port),
+		    port_cmd_status);
+
+		/* Wait until PxCMD.FR is cleared */
+		for (;;) {
+			port_cmd_status =
+			    ddi_get32(ahci_ctlp->ahcictl_ahci_acc_handle,
+			    (uint32_t *)AHCI_PORT_PxCMD(ahci_ctlp, port));
+
+			if (!(port_cmd_status & AHCI_CMD_STATUS_FR))
+				break;
+
+			if (loop_count++ >= AHCI_POLLRATE_PORT_IDLE_FR) {
+				AHCIDBG(AHCIDBG_INIT | AHCIDBG_ERRS, ahci_ctlp,
+				    "ahci_setup_port_base_addresses: cannot "
+				    "clear PxCMD.FR for port %d.", port);
+
+				/*
+				 * We are effectively timing out after 0.5 sec.
+				 * This value is specified in AHCI spec.
+				 */
+				return (AHCI_FAILURE);
+			}
+
+			/* Wait for 1 millisec */
+			drv_usecwait(AHCI_1MS_USECS);
+		}
+	}
+
+	/* Step 3: Config Port Command List Base Address */
+	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxCLB(ahci_ctlp, port),
+	    ahci_portp->ahciport_cmd_list_dma_cookie.dmac_address);
+
+	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxCLBU(ahci_ctlp, port),
+	    ahci_portp->ahciport_cmd_list_dma_cookie.dmac_notused);
+
+	/* Step 4: Config Port Received FIS Base Address */
+	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxFB(ahci_ctlp, port),
+	    ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_address);
+
+	ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+	    (uint32_t *)AHCI_PORT_PxFBU(ahci_ctlp, port),
+	    ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_notused);
+
+	return (AHCI_SUCCESS);
+}
+
+/*
  * Allocate the ahci_port_t including Received FIS and Command List.
  * The argument - port is the physical port number, and not logical
  * port number seen by the SATA framework.
@@ -6028,14 +6077,20 @@
 	 * Allocate memory for received FIS structure and
 	 * command list for this port
 	 */
-	if (ahci_alloc_rcvd_fis(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) {
+	if (ahci_alloc_rcvd_fis(ahci_ctlp, ahci_portp) != AHCI_SUCCESS) {
 		goto err_case1;
 	}
 
-	if (ahci_alloc_cmd_list(ahci_ctlp, ahci_portp, port) != AHCI_SUCCESS) {
+	if (ahci_alloc_cmd_list(ahci_ctlp, ahci_portp) != AHCI_SUCCESS) {
 		goto err_case2;
 	}
 
+	/* Setup PxCMD.CLB, PxCMD.CLBU, PxCMD.FB, and PxCMD.FBU */
+	if (ahci_setup_port_base_addresses(ahci_ctlp, ahci_portp) !=
+	    AHCI_SUCCESS) {
+		goto err_case3;
+	}
+
 	(void) snprintf(taskq_name + strlen(taskq_name),
 	    sizeof (taskq_name) - strlen(taskq_name),
 	    "_port%d", port);
@@ -6087,7 +6142,7 @@
 }
 
 /*
- * Reverse of ahci_dealloc_port_state().
+ * Reverse of ahci_alloc_port_state().
  *
  * WARNING!!! ahcictl_mutex should be acquired before the function
  * is called.
@@ -6126,8 +6181,7 @@
  * is called.
  */
 static int
-ahci_alloc_rcvd_fis(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
-    uint8_t port)
+ahci_alloc_rcvd_fis(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp)
 {
 	size_t rcvd_fis_size;
 	size_t ret_len;
@@ -6185,11 +6239,6 @@
 
 	bzero((void *)ahci_portp->ahciport_rcvd_fis, rcvd_fis_size);
 
-	/* Config Port Received FIS Base Address */
-	ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle,
-	    (uint64_t *)AHCI_PORT_PxFB(ahci_ctlp, port),
-	    ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress);
-
 	AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx",
 	    ahci_portp->ahciport_rcvd_fis_dma_cookie.dmac_laddress);
 	AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "32-bit, dma address: 0x%x",
@@ -6226,8 +6275,7 @@
  * is called.
  */
 static int
-ahci_alloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
-    uint8_t port)
+ahci_alloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp)
 {
 	size_t cmd_list_size;
 	size_t ret_len;
@@ -6285,11 +6333,6 @@
 
 	bzero((void *)ahci_portp->ahciport_cmd_list, cmd_list_size);
 
-	/* Config Port Command List Base Address */
-	ddi_put64(ahci_ctlp->ahcictl_ahci_acc_handle,
-	    (uint64_t *)AHCI_PORT_PxCLB(ahci_ctlp, port),
-	    ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress);
-
 	AHCIDBG(AHCIDBG_INIT, ahci_ctlp, "64-bit, dma address: 0x%llx",
 	    ahci_portp->ahciport_cmd_list_dma_cookie.dmac_laddress);
 
--- a/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h	Mon Jun 10 09:51:40 2013 -0700
+++ b/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h	Sat Jun 08 23:24:29 2013 +0200
@@ -23,7 +23,9 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
-
+/*
+ * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ */
 
 #ifndef _AHCIREG_H
 #define	_AHCIREG_H
@@ -134,6 +136,10 @@
 #define	AHCI_GLOBAL_EM_LOC(ahci_ctlp)	(AHCI_GLOBAL_OFFSET(ahci_ctlp) + 0x1c)
 	/* Enclosure Management Control */
 #define	AHCI_GLOBAL_EM_CTL(ahci_ctlp)	(AHCI_GLOBAL_OFFSET(ahci_ctlp) + 0x20)
+	/* HBA Capabilities Extended (AHCI spec 1.2) */
+#define	AHCI_GLOBAL_CAP2(ahci_ctlp)	(AHCI_GLOBAL_OFFSET(ahci_ctlp) + 0x24)
+	/* BIOS/OS Handoff Control and Status (AHCI spec 1.2) */
+#define	AHCI_GLOBAL_BOHC(ahci_ctlp)	(AHCI_GLOBAL_OFFSET(ahci_ctlp) + 0x28)
 
 #define	AHCI_PORT_IMPLEMENTED(ahci_ctlp, port)	\
 	((0x1 << port) & ahci_ctlp->ahcictl_ports_implemented)
--- a/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h	Mon Jun 10 09:51:40 2013 -0700
+++ b/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h	Sat Jun 08 23:24:29 2013 +0200
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  */
 
 
@@ -133,9 +134,6 @@
 /*
  * flags for ahciport_flags
  *
- * AHCI_PORT_FLAG_SPINUP: this flag will be set when a HBA which supports
- * staggered spin-up needs to do a spin-up.
- *
  * AHCI_PORT_FLAG_MOPPING: this flag will be set when the HBA is stopped,
  * and all the outstanding commands need to be aborted and sent to upper
  * layers.
@@ -173,7 +171,6 @@
  * will be printed. Note that, for INDENTIFY DEVICE command sent to ATAPI
  * device or ATAPI PACKET command, this flag won't be set.
  */
-#define	AHCI_PORT_FLAG_SPINUP		0x01
 #define	AHCI_PORT_FLAG_MOPPING		0x02
 #define	AHCI_PORT_FLAG_POLLING		0x04
 #define	AHCI_PORT_FLAG_RQSENSE		0x08
@@ -199,7 +196,6 @@
 	ahci_pmult_info_t	*ahciport_pmult_info;
 
 	/*
-	 * AHCI_PORT_FLAG_SPINUP
 	 * AHCI_PORT_FLAG_MOPPING
 	 * AHCI_PORT_FLAG_POLLING
 	 * AHCI_PORT_FLAG_RQSENSE
@@ -552,6 +548,7 @@
 #define	AHCI_POLLRATE_PORT_IDLE		50
 #define	AHCI_POLLRATE_PORT_SOFTRESET	100
 #define	AHCI_POLLRATE_GET_SPKT		100
+#define	AHCI_POLLRATE_PORT_IDLE_FR	500
 
 
 /* Clearing & setting the n'th bit in a given tag */