summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Telka <Marcel.Telka@nexenta.com>2013-06-08 23:24:29 +0200
committerRobert Mustacchi <rm@joyent.com>2013-06-11 08:20:10 -0700
commit2ac302890e472bf0c11db192dd18f12ded6043f6 (patch)
tree615625df2bd69c24c097816db4bebc618fb3ab21
parent79ec9da85c2648e2e165ce68612ad0cb6e185618 (diff)
downloadillumos-joyent-2ac302890e472bf0c11db192dd18f12ded6043f6.tar.gz
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>
-rw-r--r--usr/src/cmd/mdb/intel/modules/sata/sata.c8
-rw-r--r--usr/src/uts/common/io/sata/adapters/ahci/ahci.c405
-rw-r--r--usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h8
-rw-r--r--usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h7
4 files changed, 238 insertions, 190 deletions
diff --git a/usr/src/cmd/mdb/intel/modules/sata/sata.c b/usr/src/cmd/mdb/intel/modules/sata/sata.c
index 5a43653d30..b1618cd8d8 100644
--- a/usr/src/cmd/mdb/intel/modules/sata/sata.c
+++ b/usr/src/cmd/mdb/intel/modules/sata/sata.c
@@ -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 @@ sata_dmsg_dump(sata_trace_dmsg_t *addr, int print_pathname, uint_t *printed)
(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 @@ sata_dmsg_dump(sata_trace_dmsg_t *addr, int print_pathname, uint_t *printed)
dmsg.buf);
}
- mdb_printf("%s", merge);
+ mdb_printf("%s\n", merge);
if (printed != NULL) {
(*printed)++;
diff --git a/usr/src/uts/common/io/sata/adapters/ahci/ahci.c b/usr/src/uts/common/io/sata/adapters/ahci/ahci.c
index 73cef1199b..bea112d166 100644
--- a/usr/src/uts/common/io/sata/adapters/ahci/ahci.c
+++ b/usr/src/uts/common/io/sata/adapters/ahci/ahci.c
@@ -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 @@ static int ahci_check_slot_handle(ahci_port_t *, int);
/*
* 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 int ahci_initialize_controller(ahci_ctl_t *);
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 @@ _init(void)
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 @@ _init(void)
goto err_out;
}
- /* watchdog tick */
- ahci_watchdog_tick = drv_usectohz(
- (clock_t)ahci_watchdog_timeout * 1000000);
return (ret);
err_out:
@@ -517,6 +521,7 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
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_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
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 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
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 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
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 @@ ahci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
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 @@ ahci_check_slot_handle(ahci_port_t *ahci_portp, int slot)
}
return (DDI_SUCCESS);
}
+
/*
* Allocate the ports structure, only called by ahci_attach
*/
@@ -3637,28 +3667,11 @@ ahci_initialize_controller(ahci_ctl_t *ahci_ctlp)
{
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 @@ ahci_dealloc_pmult(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp)
}
/*
+ * 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 @@ ahci_initialize_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
"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_initialize_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
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 @@ ahci_initialize_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
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 @@ ahci_initialize_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
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 @@ ahci_probe_pmport(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
* 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 @@ out:
*
* 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_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
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 @@ ahci_port_reset(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
"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);
+ /*
+ * 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);
- /* PxSCTL.DET must be 0 */
- ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
- (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port),
- port_scontrol);
+ 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);
- 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);
+ ddi_put32(ahci_ctlp->ahcictl_ahci_acc_handle,
+ (uint32_t *)AHCI_PORT_PxSCTL(ahci_ctlp, port),
+ port_scontrol);
- /* 0 -> 1 edge */
- drv_usecwait(AHCI_1MS_USECS*2);
+ /* 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);
- /* 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);
+ /*
+ * 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);
- /* 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);
- }
+ /* 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 @@ err: /* R/W PMULT error */
*
* 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 @@ static int
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_hba_reset(ahci_ctl_t *ahci_ctlp)
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 @@ ahci_find_dev_signature(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
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(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
"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 @@ ahci_start_port(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp, uint8_t port)
}
/*
+ * 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 @@ ahci_alloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port)
* 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 @@ err_case1:
}
/*
- * 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 @@ ahci_dealloc_port_state(ahci_ctl_t *ahci_ctlp, uint8_t port)
* 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 @@ ahci_alloc_rcvd_fis(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
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 @@ ahci_dealloc_rcvd_fis(ahci_port_t *ahci_portp)
* 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 @@ ahci_alloc_cmd_list(ahci_ctl_t *ahci_ctlp, ahci_port_t *ahci_portp,
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);
diff --git a/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h b/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h
index 5614c929d5..e738783dfe 100644
--- a/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h
+++ b/usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h
@@ -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 @@ extern "C" {
#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)
diff --git a/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h b/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h
index bf8671425c..b28d0b6464 100644
--- a/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h
+++ b/usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h
@@ -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 @@ typedef struct ahci_pmult_info ahci_pmult_info_t;
/*
* 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 @@ typedef struct ahci_pmult_info ahci_pmult_info_t;
* 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 @@ typedef struct ahci_port {
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 @@ _NOTE(MUTEX_PROTECTS_DATA(ahci_ctl_t::ahcictl_mutex,
#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 */