svn commit: r191674 - in head/sys/dev/ata: . chipsets

Alexander Motin mav at FreeBSD.org
Wed Apr 29 21:17:20 UTC 2009


Author: mav
Date: Wed Apr 29 21:17:18 2009
New Revision: 191674
URL: http://svn.freebsd.org/changeset/base/191674

Log:
  Add experimental support for SATA interface power management.
  Feature is controlled by hint.ata.X.pm_level tunable:
   0 - PM disabled, old behaviour, default.
   1 - device is allowed to initiate PM state change, host is passive.
   2 - host initiates PARTIAL state transition every time port is idle.
   3 - host initiates SLUMBER state transition every time port is idle.
  
  PARTIAL state has up to 100us (50us for me) wakeup latency, but for my
  ICH8M saves 0.5W of power per drive. SLUMBER state has up to 10ms (3.5ms
  for me) wakeup latency, but saves 0.8W of power.
  
  Modes 2 and 3 are implemented only for AHCI driver now.
  
  Interface power management is incompatible with device presence detection
  (host receives no signal from drive, so unable to monitor it), so later is
  disabled when PM is used.

Modified:
  head/sys/dev/ata/ata-all.h
  head/sys/dev/ata/ata-pci.c
  head/sys/dev/ata/ata-sata.c
  head/sys/dev/ata/chipsets/ata-ahci.c

Modified: head/sys/dev/ata/ata-all.h
==============================================================================
--- head/sys/dev/ata/ata-all.h	Wed Apr 29 21:14:15 2009	(r191673)
+++ head/sys/dev/ata/ata-all.h	Wed Apr 29 21:17:18 2009	(r191674)
@@ -148,9 +148,12 @@
 
 /* SATA AHCI v1.0 register defines */
 #define ATA_AHCI_CAP                    0x00
-#define         ATA_AHCI_NPMASK         0x1f
+#define		ATA_AHCI_CAP_NPMASK	0x0000001f
+#define		ATA_AHCI_CAP_PSC	0x00002000
+#define		ATA_AHCI_CAP_SSC	0x00004000
 #define		ATA_AHCI_CAP_SPM	0x00020000
 #define		ATA_AHCI_CAP_CLO	0x01000000
+#define		ATA_AHCI_CAP_SALP	0x04000000
 #define		ATA_AHCI_CAP_64BIT	0x80000000
 
 #define ATA_AHCI_GHC                    0x04
@@ -513,6 +516,7 @@ struct ata_channel {
 #define         ATA_NO_48BIT_DMA        0x08
 #define         ATA_ALWAYS_DMASTAT      0x10
 
+    int				pm_level;	/* power management level */
     int                         devices;        /* what is present */
 #define         ATA_ATA_MASTER          0x00000001
 #define         ATA_ATA_SLAVE           0x00000002

Modified: head/sys/dev/ata/ata-pci.c
==============================================================================
--- head/sys/dev/ata/ata-pci.c	Wed Apr 29 21:14:15 2009	(r191673)
+++ head/sys/dev/ata/ata-pci.c	Wed Apr 29 21:17:18 2009	(r191674)
@@ -556,6 +556,9 @@ ata_pcichannel_attach(device_t dev)
 
     ch->unit = (intptr_t)device_get_ivars(dev);
 
+    resource_int_value(device_get_name(dev),
+	device_get_unit(dev), "pm_level", &ch->pm_level);
+
     if ((error = ctlr->ch_attach(dev)))
 	return error;
 

Modified: head/sys/dev/ata/ata-sata.c
==============================================================================
--- head/sys/dev/ata/ata-sata.c	Wed Apr 29 21:14:15 2009	(r191673)
+++ head/sys/dev/ata/ata-sata.c	Wed Apr 29 21:17:18 2009	(r191674)
@@ -60,7 +60,7 @@ ata_sata_phy_check_events(device_t dev)
     ATA_IDX_OUTL(ch, ATA_SERROR, error);
 
     /* if we have a connection event deal with it */
-    if (error & ATA_SE_PHY_CHANGED) {
+    if ((error & ATA_SE_PHY_CHANGED) && (ch->pm_level == 0)) {
 	if (bootverbose) {
 	    u_int32_t status = ATA_IDX_INL(ch, ATA_SSTATUS);
 	    if (((status & ATA_SS_CONWELL_MASK) == ATA_SS_CONWELL_GEN1) ||
@@ -199,9 +199,8 @@ ata_sata_phy_reset(device_t dev, int por
 	ata_udelay(5000);
 	for (loop = 0; loop < 10; loop++) {
 	    if (ata_sata_scr_write(ch, port, ATA_SCONTROL,
-					ATA_SC_DET_IDLE |
-					ATA_SC_IPM_DIS_PARTIAL |
-					ATA_SC_IPM_DIS_SLUMBER))
+		    ATA_SC_DET_IDLE | ((ch->pm_level > 0) ? 0 :
+		    ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER)))
 		return (0);
 	    ata_udelay(100);
 	    if (ata_sata_scr_read(ch, port, ATA_SCONTROL, &val))

Modified: head/sys/dev/ata/chipsets/ata-ahci.c
==============================================================================
--- head/sys/dev/ata/chipsets/ata-ahci.c	Wed Apr 29 21:14:15 2009	(r191673)
+++ head/sys/dev/ata/chipsets/ata-ahci.c	Wed Apr 29 21:17:18 2009	(r191674)
@@ -131,7 +131,7 @@ ata_ahci_chipinit(device_t dev)
     ctlr->ichannels = ATA_INL(ctlr->r_res2, ATA_AHCI_PI);
     ctlr->channels =
 	MAX(flsl(ctlr->ichannels),
-	    (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_NPMASK) + 1);
+	    (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_NPMASK) + 1);
 
     ctlr->reset = ata_ahci_reset;
     ctlr->ch_attach = ata_ahci_ch_attach;
@@ -148,7 +148,7 @@ ata_ahci_chipinit(device_t dev)
 		  "AHCI Version %x%x.%x%x controller with %d ports PM %s\n",
 		  (version >> 24) & 0xff, (version >> 16) & 0xff,
 		  (version >> 8) & 0xff, version & 0xff,
-		  (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_NPMASK) + 1,
+		  (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_NPMASK) + 1,
 		  (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_SPM) ?
 		  "supported" : "not supported");
     return 0;
@@ -286,7 +286,9 @@ ata_ahci_ch_resume(device_t dev)
 
     /* activate the channel and power/spin up device */
     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_CMD + offset,
-	     (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD));
+	     (ATA_AHCI_P_CMD_ACTIVE | ATA_AHCI_P_CMD_POD | ATA_AHCI_P_CMD_SUD |
+	     ((ch->pm_level > 1) ? ATA_AHCI_P_CMD_ALPE : 0) |
+	     ((ch->pm_level > 2) ? ATA_AHCI_P_CMD_ASP : 0 )));
     ata_ahci_start_fr(dev);
     ata_ahci_start(dev);
 
@@ -818,9 +820,9 @@ ata_ahci_reset(device_t dev)
     ATA_OUTL(ctlr->r_res2, ATA_AHCI_P_IE + offset,
 	     (ATA_AHCI_P_IX_CPD | ATA_AHCI_P_IX_TFE | ATA_AHCI_P_IX_HBF |
 	      ATA_AHCI_P_IX_HBD | ATA_AHCI_P_IX_IF | ATA_AHCI_P_IX_OF |
-	      ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC | ATA_AHCI_P_IX_DP |
-	      ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB | ATA_AHCI_P_IX_DS |
-	      ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
+	      ((ch->pm_level == 0) ? ATA_AHCI_P_IX_PRC | ATA_AHCI_P_IX_PC : 0) |
+	      ATA_AHCI_P_IX_DP | ATA_AHCI_P_IX_UF | ATA_AHCI_P_IX_SDB |
+	      ATA_AHCI_P_IX_DS | ATA_AHCI_P_IX_PS | ATA_AHCI_P_IX_DHR));
 
     /* only probe for PortMultiplier if HW has support */
     if (ATA_INL(ctlr->r_res2, ATA_AHCI_CAP) & ATA_AHCI_CAP_SPM) {


More information about the svn-src-head mailing list