From owner-svn-src-head@FreeBSD.ORG Fri Apr 15 16:40:32 2011 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 31E221065673; Fri, 15 Apr 2011 16:40:32 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 205D18FC0A; Fri, 15 Apr 2011 16:40:32 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id p3FGeWFe075400; Fri, 15 Apr 2011 16:40:32 GMT (envelope-from mav@svn.freebsd.org) Received: (from mav@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id p3FGeWYh075397; Fri, 15 Apr 2011 16:40:32 GMT (envelope-from mav@svn.freebsd.org) Message-Id: <201104151640.p3FGeWYh075397@svn.freebsd.org> From: Alexander Motin Date: Fri, 15 Apr 2011 16:40:31 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r220657 - head/sys/dev/ahci X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 15 Apr 2011 16:40:32 -0000 Author: mav Date: Fri Apr 15 16:40:31 2011 New Revision: 220657 URL: http://svn.freebsd.org/changeset/base/220657 Log: Some changes around hot-plug and interface power-management: - use ATA_SE_EXCHANGED (SError.DIAG.X) bit to detect hot-plug events when power-management enabled and ATA_SE_PHY_CHANGED (SError.DIAG.N) can't be trusted; - on controllers supporting staggered spin-up (SS) put unused channels into Listen state instead of Off. It should still save some power, but allow plug-in events to be detected; - on controllers supporting cold presence detection (CPD), when power management enabled, use CPD events to detect hot-plug in addition to PHY events. Modified: head/sys/dev/ahci/ahci.c head/sys/dev/ahci/ahci.h Modified: head/sys/dev/ahci/ahci.c ============================================================================== --- head/sys/dev/ahci/ahci.c Fri Apr 15 15:33:24 2011 (r220656) +++ head/sys/dev/ahci/ahci.c Fri Apr 15 16:40:31 2011 (r220657) @@ -1272,34 +1272,66 @@ ahci_slotsfree(device_t dev) } } -static void +static int ahci_phy_check_events(device_t dev, u_int32_t serr) { struct ahci_channel *ch = device_get_softc(dev); - if ((serr & ATA_SE_PHY_CHANGED) && (ch->pm_level == 0)) { + if (((ch->pm_level == 0) && (serr & ATA_SE_PHY_CHANGED)) || + ((ch->pm_level != 0 || ch->listening) && (serr & ATA_SE_EXCHANGED))) { u_int32_t status = ATA_INL(ch->r_mem, AHCI_P_SSTS); union ccb *ccb; if (bootverbose) { - if (((status & ATA_SS_DET_MASK) == ATA_SS_DET_PHY_ONLINE) && - ((status & ATA_SS_SPD_MASK) != ATA_SS_SPD_NO_SPEED) && - ((status & ATA_SS_IPM_MASK) == ATA_SS_IPM_ACTIVE)) { + if ((status & ATA_SS_DET_MASK) != ATA_SS_DET_NO_DEVICE) device_printf(dev, "CONNECT requested\n"); - } else + else device_printf(dev, "DISCONNECT requested\n"); } ahci_reset(dev); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) - return; + return (0); if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); - return; + return (0); } xpt_rescan(ccb); + return (1); } + return (0); +} + +static void +ahci_cpd_check_events(device_t dev) +{ + struct ahci_channel *ch = device_get_softc(dev); + u_int32_t status; + union ccb *ccb; + + if (ch->pm_level == 0) + return; + + status = ATA_INL(ch->r_mem, AHCI_P_CMD); + if ((status & AHCI_P_CMD_CPD) == 0) + return; + + if (bootverbose) { + if (status & AHCI_P_CMD_CPS) { + device_printf(dev, "COLD CONNECT requested\n"); + } else + device_printf(dev, "COLD DISCONNECT requested\n"); + } + ahci_reset(dev); + if ((ccb = xpt_alloc_ccb_nowait()) == NULL) + return; + if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { + xpt_free_ccb(ccb); + return; + } + xpt_rescan(ccb); } static void @@ -1359,7 +1391,7 @@ ahci_ch_intr(void *data) struct ahci_channel *ch = device_get_softc(dev); uint32_t istatus, sstatus, cstatus, serr = 0, sntf = 0, ok, err; enum ahci_err_type et; - int i, ccs, port; + int i, ccs, port, reset = 0; /* Read and clear interrupt statuses. */ istatus = ATA_INL(ch->r_mem, AHCI_P_IS); @@ -1395,9 +1427,12 @@ ahci_ch_intr(void *data) serr = ATA_INL(ch->r_mem, AHCI_P_SERR); if (serr) { ATA_OUTL(ch->r_mem, AHCI_P_SERR, serr); - ahci_phy_check_events(dev, serr); + reset = ahci_phy_check_events(dev, serr); } } + /* Process cold presence detection events */ + if ((istatus & AHCI_P_IX_CPD) && !reset) + ahci_cpd_check_events(dev); /* Process command errors */ if (istatus & (AHCI_P_IX_OF | AHCI_P_IX_IF | AHCI_P_IX_HBD | AHCI_P_IX_HBF | AHCI_P_IX_TFE)) { @@ -2446,7 +2481,8 @@ ahci_reset(device_t dev) ch->devices = 0; /* Enable wanted port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, - (AHCI_P_IX_CPD | AHCI_P_IX_PRC | AHCI_P_IX_PC)); + (((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) | + AHCI_P_IX_PRC | AHCI_P_IX_PC)); xpt_release_simq(ch->sim, TRUE); return; } @@ -2463,9 +2499,10 @@ ahci_reset(device_t dev) ch->devices = 1; /* Enable wanted port interrupts */ ATA_OUTL(ch->r_mem, AHCI_P_IE, - (AHCI_P_IX_CPD | AHCI_P_IX_TFE | AHCI_P_IX_HBF | + (((ch->pm_level != 0) ? AHCI_P_IX_CPD | AHCI_P_IX_MP : 0) | + AHCI_P_IX_TFE | AHCI_P_IX_HBF | AHCI_P_IX_HBD | AHCI_P_IX_IF | AHCI_P_IX_OF | - ((ch->pm_level == 0) ? AHCI_P_IX_PRC | AHCI_P_IX_PC : 0) | + ((ch->pm_level == 0) ? AHCI_P_IX_PRC : 0) | AHCI_P_IX_PC | AHCI_P_IX_DP | AHCI_P_IX_UF | (ctlr->ccc ? 0 : AHCI_P_IX_SDB) | AHCI_P_IX_DS | AHCI_P_IX_PS | (ctlr->ccc ? 0 : AHCI_P_IX_DHR))); if (ch->resetting) @@ -2570,6 +2607,12 @@ ahci_sata_phy_reset(device_t dev) int sata_rev; uint32_t val; + if (ch->listening) { + val = ATA_INL(ch->r_mem, AHCI_P_CMD); + val |= AHCI_P_CMD_SUD; + ATA_OUTL(ch->r_mem, AHCI_P_CMD, val); + ch->listening = 0; + } sata_rev = ch->user[ch->pm_present ? 15 : 0].revision; if (sata_rev == 1) val = ATA_SC_SPD_SPEED_GEN1; @@ -2588,7 +2631,12 @@ ahci_sata_phy_reset(device_t dev) (ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER))); DELAY(5000); if (!ahci_sata_connect(ch)) { - if (ch->pm_level > 0) + if (ch->caps & AHCI_CAP_SSS) { + val = ATA_INL(ch->r_mem, AHCI_P_CMD); + val &= ~AHCI_P_CMD_SUD; + ATA_OUTL(ch->r_mem, AHCI_P_CMD, val); + ch->listening = 1; + } else if (ch->pm_level > 0) ATA_OUTL(ch->r_mem, AHCI_P_SCTL, ATA_SC_DET_DISABLE); return (0); } Modified: head/sys/dev/ahci/ahci.h ============================================================================== --- head/sys/dev/ahci/ahci.h Fri Apr 15 15:33:24 2011 (r220656) +++ head/sys/dev/ahci/ahci.h Fri Apr 15 16:40:31 2011 (r220657) @@ -118,6 +118,7 @@ #define ATA_SE_LINKSEQ_ERR 0x00800000 #define ATA_SE_TRANSPORT_ERR 0x01000000 #define ATA_SE_UNKNOWN_FIS 0x02000000 +#define ATA_SE_EXCHANGED 0x04000000 #define ATA_SCONTROL 15 #define ATA_SC_DET_MASK 0x0000000f @@ -221,7 +222,7 @@ #define AHCI_P_IX_UF 0x00000010 #define AHCI_P_IX_DP 0x00000020 #define AHCI_P_IX_PC 0x00000040 -#define AHCI_P_IX_DI 0x00000080 +#define AHCI_P_IX_MP 0x00000080 #define AHCI_P_IX_PRC 0x00400000 #define AHCI_P_IX_IPM 0x00800000 @@ -413,6 +414,7 @@ struct ahci_channel { int lastslot; /* Last used slot */ int taggedtarget; /* Last tagged target */ int resetting; /* Hard-reset in progress. */ + int listening; /* SUD bit is cleared. */ union ccb *frozen; /* Frozen command */ struct callout pm_timer; /* Power management events */ struct callout reset_timer; /* Hard-reset timeout */