diff options
author | Marcel Telka <Marcel.Telka@nexenta.com> | 2013-06-08 23:24:29 +0200 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2013-06-11 08:20:10 -0700 |
commit | 2ac302890e472bf0c11db192dd18f12ded6043f6 (patch) | |
tree | 615625df2bd69c24c097816db4bebc618fb3ab21 | |
parent | 79ec9da85c2648e2e165ce68612ad0cb6e185618 (diff) | |
download | illumos-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.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/io/sata/adapters/ahci/ahci.c | 405 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sata/adapters/ahci/ahcireg.h | 8 | ||||
-rw-r--r-- | usr/src/uts/common/sys/sata/adapters/ahci/ahcivar.h | 7 |
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 */ |