All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RESEND] ahci: implement aggressive SATA device sleep support
@ 2012-08-07 17:44 Shane Huang
  2012-08-17 17:53 ` Jeff Garzik
  2012-08-22 10:23 ` David Woodhouse
  0 siblings, 2 replies; 5+ messages in thread
From: Shane Huang @ 2012-08-07 17:44 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: linux-ide, Tejun Heo, Shane Huang

Device Sleep is a feature as described in AHCI 1.3.1 Technical Proposal.
This feature enables an HBA and SATA storage device to enter the DevSleep
interface state, enabling lower power SATA-based systems.

Signed-off-by: Shane Huang <shane.huang@amd.com>
---
 drivers/ata/ahci.h        |   14 +++++++++
 drivers/ata/libahci.c     |   71 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/ata/libata-core.c |    9 ++++--
 include/linux/ata.h       |    2 ++
 include/linux/libata.h    |    1 +
 5 files changed, 93 insertions(+), 4 deletions(-)

diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index c2594dd..7b6fcf7 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -115,6 +115,9 @@ enum {
 	HOST_CAP2_BOH		= (1 << 0),  /* BIOS/OS handoff supported */
 	HOST_CAP2_NVMHCI	= (1 << 1),  /* NVMHCI supported */
 	HOST_CAP2_APST		= (1 << 2),  /* Automatic partial to slumber */
+	HOST_CAP2_SDS		= (1 << 3),  /* Support device sleep */
+	HOST_CAP2_SADM		= (1 << 4),  /* Support aggressive DevSlp */
+	HOST_CAP2_DESO		= (1 << 5),  /* DevSlp from slumber only */
 
 	/* registers for each SATA port */
 	PORT_LST_ADDR		= 0x00, /* command list DMA addr */
@@ -133,6 +136,7 @@ enum {
 	PORT_SCR_ACT		= 0x34, /* SATA phy register: SActive */
 	PORT_SCR_NTF		= 0x3c, /* SATA phy register: SNotification */
 	PORT_FBS		= 0x40, /* FIS-based Switching */
+	PORT_DEVSLP		= 0x44, /* device sleep */
 
 	/* PORT_IRQ_{STAT,MASK} bits */
 	PORT_IRQ_COLD_PRES	= (1 << 31), /* cold presence detect */
@@ -186,6 +190,7 @@ enum {
 	PORT_CMD_ICC_PARTIAL	= (0x2 << 28), /* Put i/f in partial state */
 	PORT_CMD_ICC_SLUMBER	= (0x6 << 28), /* Put i/f in slumber state */
 
+	/* PORT_FBS bits */
 	PORT_FBS_DWE_OFFSET	= 16, /* FBS device with error offset */
 	PORT_FBS_ADO_OFFSET	= 12, /* FBS active dev optimization offset */
 	PORT_FBS_DEV_OFFSET	= 8,  /* FBS device to issue offset */
@@ -194,6 +199,15 @@ enum {
 	PORT_FBS_DEC		= (1 << 1), /* FBS device error clear */
 	PORT_FBS_EN		= (1 << 0), /* Enable FBS */
 
+	/* PORT_DEVSLP bits */
+	PORT_DEVSLP_DM_OFFSET	= 25,             /* DITO multiplier offset */
+	PORT_DEVSLP_DM_MASK	= (0xf << 25),    /* DITO multiplier mask */
+	PORT_DEVSLP_DITO_OFFSET	= 15,             /* DITO offset */
+	PORT_DEVSLP_MDAT_OFFSET	= 10,             /* Minimum assertion time */
+	PORT_DEVSLP_DETO_OFFSET	= 2,              /* DevSlp exit timeout */
+	PORT_DEVSLP_DSP		= (1 << 1),       /* DevSlp present */
+	PORT_DEVSLP_ADSE	= (1 << 0),       /* Aggressive DevSlp enable */
+
 	/* hpriv->flags bits */
 
 #define AHCI_HFLAGS(flags)		.private_data	= (void *)(flags)
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index f9eaa82..69b6617 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -45,6 +45,7 @@
 #include <scsi/scsi_cmnd.h>
 #include <linux/libata.h>
 #include "ahci.h"
+#include "libata.h"
 
 static int ahci_skip_host_reset;
 int ahci_ignore_sss;
@@ -76,6 +77,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc);
 static int ahci_pmp_qc_defer(struct ata_queued_cmd *qc);
 static void ahci_freeze(struct ata_port *ap);
 static void ahci_thaw(struct ata_port *ap);
+static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep);
 static void ahci_enable_fbs(struct ata_port *ap);
 static void ahci_disable_fbs(struct ata_port *ap);
 static void ahci_pmp_attach(struct ata_port *ap);
@@ -193,6 +195,10 @@ module_param(ahci_em_messages, int, 0444);
 MODULE_PARM_DESC(ahci_em_messages,
 	"AHCI Enclosure Management Message control (0 = off, 1 = on)");
 
+int devslp_idle_timeout = 1000;	/* device sleep idle timeout in ms */
+module_param(devslp_idle_timeout, int, 0644);
+MODULE_PARM_DESC(devslp_idle_timeout, "device sleep idle timeout");
+
 static void ahci_enable_ahci(void __iomem *mmio)
 {
 	int i;
@@ -702,6 +708,16 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
 		}
 	}
 
+	/* set aggressive device sleep */
+	if ((hpriv->cap2 & HOST_CAP2_SDS) &&
+	    (hpriv->cap2 & HOST_CAP2_SADM) &&
+	    (link->device->flags & ATA_DFLAG_DEVSLP)) {
+		if (policy == ATA_LPM_MIN_POWER)
+			ahci_set_aggressive_devslp(ap, true);
+		else
+			ahci_set_aggressive_devslp(ap, false);
+	}
+
 	if (policy == ATA_LPM_MAX_POWER) {
 		sata_link_scr_lpm(link, policy, false);
 
@@ -1889,6 +1905,55 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
 		ahci_kick_engine(ap);
 }
 
+static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
+{
+	void __iomem *port_mmio = ahci_port_base(ap);
+	u32 devslp, dm, dito;
+	int rc;
+	unsigned int err_mask;
+
+	devslp = readl(port_mmio + PORT_DEVSLP);
+	if (!(devslp & PORT_DEVSLP_DSP)) {
+		dev_err(ap->host->dev, "port does not support device sleep\n");
+		return;
+	}
+
+	/* disable device sleep */
+	if (!sleep) {
+		writel(devslp & ~PORT_DEVSLP_ADSE, port_mmio + PORT_DEVSLP);
+		return;
+	}
+
+	/* device sleep was already enabled */
+	if (devslp & PORT_DEVSLP_ADSE)
+		return;
+
+	/* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */
+	rc = ahci_stop_engine(ap);
+	if (rc)
+		return;
+
+	dm = (devslp & PORT_DEVSLP_DM_MASK) >> PORT_DEVSLP_DM_OFFSET;
+	dito = devslp_idle_timeout / (dm + 1);
+	if (dito > 0x3ff)
+		dito = 0x3ff;
+	devslp |= ((dito << PORT_DEVSLP_DITO_OFFSET) |
+		   (10 << PORT_DEVSLP_MDAT_OFFSET) |
+		   (20 << PORT_DEVSLP_DETO_OFFSET) |
+		   PORT_DEVSLP_ADSE);
+	writel(devslp, port_mmio + PORT_DEVSLP);
+
+	ahci_start_engine(ap);
+
+	/* enable device sleep feature for the drive */
+	err_mask = ata_dev_set_feature(ap->link.device,
+				       SETFEATURES_SATA_ENABLE,
+				       SATA_DEVSLP);
+	if (err_mask && err_mask != AC_ERR_DEV)
+		ata_dev_warn(ap->link.device,
+			     "failed to enable DEVSLP, Emask 0x%x\n", err_mask);
+}
+
 static void ahci_enable_fbs(struct ata_port *ap)
 {
 	struct ahci_port_priv *pp = ap->private_data;
@@ -2163,7 +2228,8 @@ void ahci_print_info(struct ata_host *host, const char *scc_s)
 		"flags: "
 		"%s%s%s%s%s%s%s"
 		"%s%s%s%s%s%s%s"
-		"%s%s%s%s%s%s\n"
+		"%s%s%s%s%s%s%s"
+		"%s%s\n"
 		,
 
 		cap & HOST_CAP_64 ? "64bit " : "",
@@ -2183,6 +2249,9 @@ void ahci_print_info(struct ata_host *host, const char *scc_s)
 		cap & HOST_CAP_CCC ? "ccc " : "",
 		cap & HOST_CAP_EMS ? "ems " : "",
 		cap & HOST_CAP_SXS ? "sxs " : "",
+		cap2 & HOST_CAP2_DESO ? "deso " : "",
+		cap2 & HOST_CAP2_SADM ? "sadm " : "",
+		cap2 & HOST_CAP2_SDS ? "sds " : "",
 		cap2 & HOST_CAP2_APST ? "apst " : "",
 		cap2 & HOST_CAP2_NVMHCI ? "nvmp " : "",
 		cap2 & HOST_CAP2_BOH ? "boh " : ""
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index fadd586..9d5de59 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2323,6 +2323,9 @@ int ata_dev_configure(struct ata_device *dev)
 			}
 		}
 
+		if (ata_id_has_devslp(dev->id))
+			dev->flags |= ATA_DFLAG_DEVSLP;
+
 		dev->cdb_len = 16;
 	}
 
@@ -3598,7 +3601,7 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
 	switch (policy) {
 	case ATA_LPM_MAX_POWER:
 		/* disable all LPM transitions */
-		scontrol |= (0x3 << 8);
+		scontrol |= (0x7 << 8);
 		/* initiate transition to active state */
 		if (spm_wakeup) {
 			scontrol |= (0x4 << 12);
@@ -3608,12 +3611,12 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy,
 	case ATA_LPM_MED_POWER:
 		/* allow LPM to PARTIAL */
 		scontrol &= ~(0x1 << 8);
-		scontrol |= (0x2 << 8);
+		scontrol |= (0x6 << 8);
 		break;
 	case ATA_LPM_MIN_POWER:
 		if (ata_link_nr_enabled(link) > 0)
 			/* no restrictions on LPM transitions */
-			scontrol &= ~(0x3 << 8);
+			scontrol &= ~(0x7 << 8);
 		else {
 			/* empty port, power off */
 			scontrol &= ~0xf;
diff --git a/include/linux/ata.h b/include/linux/ata.h
index 5713d3a..68e8c95 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -345,6 +345,7 @@ enum {
 	SATA_FPDMA_IN_ORDER	= 0x04,	/* FPDMA in-order data delivery */
 	SATA_AN			= 0x05,	/* Asynchronous Notification */
 	SATA_SSP		= 0x06,	/* Software Settings Preservation */
+	SATA_DEVSLP		= 0x09,	/* Device Sleep */
 
 	/* feature values for SET_MAX */
 	ATA_SET_MAX_ADDR	= 0x00,
@@ -579,6 +580,7 @@ static inline int ata_is_data(u8 prot)
 
 #define ata_id_cdb_intr(id)	(((id)[ATA_ID_CONFIG] & 0x60) == 0x20)
 #define ata_id_has_da(id)	((id)[77] & (1 << 4))
+#define ata_id_has_devslp(id)	((id)[78] & (1 << 8))
 
 static inline bool ata_id_has_hipm(const u16 *id)
 {
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 64f90e1..174c3ea 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -162,6 +162,7 @@ enum {
 	ATA_DFLAG_DETACHED	= (1 << 25),
 
 	ATA_DFLAG_DA		= (1 << 26), /* device supports Device Attention */
+	ATA_DFLAG_DEVSLP	= (1 << 27), /* device supports Device Sleep */
 
 	ATA_DEV_UNKNOWN		= 0,	/* unknown device */
 	ATA_DEV_ATA		= 1,	/* ATA device */
-- 
1.7.9.5



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH RESEND] ahci: implement aggressive SATA device sleep support
  2012-08-07 17:44 [PATCH RESEND] ahci: implement aggressive SATA device sleep support Shane Huang
@ 2012-08-17 17:53 ` Jeff Garzik
  2012-08-19 15:48   ` Huang, Shane
  2012-08-22 10:23 ` David Woodhouse
  1 sibling, 1 reply; 5+ messages in thread
From: Jeff Garzik @ 2012-08-17 17:53 UTC (permalink / raw)
  To: Shane Huang; +Cc: linux-ide, Tejun Heo

On 08/07/2012 01:44 PM, Shane Huang wrote:
> @@ -702,6 +708,16 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
>   		}
>   	}
>
> +	/* set aggressive device sleep */
> +	if ((hpriv->cap2 & HOST_CAP2_SDS) &&
> +	    (hpriv->cap2 & HOST_CAP2_SADM) &&
> +	    (link->device->flags & ATA_DFLAG_DEVSLP)) {
> +		if (policy == ATA_LPM_MIN_POWER)
> +			ahci_set_aggressive_devslp(ap, true);
> +		else
> +			ahci_set_aggressive_devslp(ap, false);
> +	}
> +
>   	if (policy == ATA_LPM_MAX_POWER) {
>   		sata_link_scr_lpm(link, policy, false);
>
> @@ -1889,6 +1905,55 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
>   		ahci_kick_engine(ap);
>   }
>
> +static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep)
> +{
> +	void __iomem *port_mmio = ahci_port_base(ap);
> +	u32 devslp, dm, dito;
> +	int rc;
> +	unsigned int err_mask;
> +
> +	devslp = readl(port_mmio + PORT_DEVSLP);
> +	if (!(devslp & PORT_DEVSLP_DSP)) {
> +		dev_err(ap->host->dev, "port does not support device sleep\n");
> +		return;
> +	}
> +
> +	/* disable device sleep */
> +	if (!sleep) {
> +		writel(devslp & ~PORT_DEVSLP_ADSE, port_mmio + PORT_DEVSLP);
> +		return;
> +	}
> +
> +	/* device sleep was already enabled */
> +	if (devslp & PORT_DEVSLP_ADSE)
> +		return;

Mostly OK.  A question and a comment.

* for the !sleep case, don't writel() if the devslp value is unchanged

* if we are disabling sleep -- a valid case where host & device both 
support it, but policy denies it -- do we need to stop the ahci engine 
as is done in the enabling case?




^ permalink raw reply	[flat|nested] 5+ messages in thread

* RE: [PATCH RESEND] ahci: implement aggressive SATA device sleep support
  2012-08-17 17:53 ` Jeff Garzik
@ 2012-08-19 15:48   ` Huang, Shane
  0 siblings, 0 replies; 5+ messages in thread
From: Huang, Shane @ 2012-08-19 15:48 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: linux-ide, Tejun Heo, Aaron Lu, Huang, Shane

Hi Jeff,

Thanks for your feedback.

> * for the !sleep case, don't writel() if the devslp value is unchanged

You are right, I will fix it in v2 together with some other improvements.

> * if we are disabling sleep -- a valid case where host & device both 
> support it, but policy denies it -- do we need to stop the ahci engine 
> as is done in the enabling case?

The spec said that software shall only set DITO, MDAT, DETO when
PxCMD.ST is cleared to 0. While disabling case doesn't need stop
engine because the above requirement is unnecessary for ADSE.
Please tell me if you have concern.


Regards
Shane


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH RESEND] ahci: implement aggressive SATA device sleep support
  2012-08-07 17:44 [PATCH RESEND] ahci: implement aggressive SATA device sleep support Shane Huang
  2012-08-17 17:53 ` Jeff Garzik
@ 2012-08-22 10:23 ` David Woodhouse
  2012-08-22 10:49   ` Huang, Shane
  1 sibling, 1 reply; 5+ messages in thread
From: David Woodhouse @ 2012-08-22 10:23 UTC (permalink / raw)
  To: Shane Huang; +Cc: Jeff Garzik, linux-ide, Tejun Heo

[-- Attachment #1: Type: text/plain, Size: 1211 bytes --]

On Wed, 2012-08-08 at 01:44 +0800, Shane Huang wrote:
> Device Sleep is a feature as described in AHCI 1.3.1 Technical Proposal.
> This feature enables an HBA and SATA storage device to enter the DevSleep
> interface state, enabling lower power SATA-based systems.
> 
> Signed-off-by: Shane Huang <shane.huang@amd.com> 

Am I missing the part where we actually send the command to the drive to
*enable* the DevSleep feature? And shouldn't we also read the MDAT/DETO
timings from the drive if it provides them (which it optionally can)?

Also, don't we need to revalidate the drive after a sleep/wake cycle?
How do we do that in the automatic/aggressive sleep case? After my
initial reading of the specs, I came to the conclusion that the *sleep*
is automatic, but the wakeup still requires careful handling (and we
need to ensure that we don't submit commands while it's asleep, etc.)?

Since the DevSleep line could just be a GPIO pin, I suspect we are going
to want to support platforms where it's not automatic, and we
assert/deassert it under direct software control. My test platform does
it with an ACPI call, in fact. Have you looked at that mode of operation
at all?

-- 
dwmw2

[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 6171 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* RE: [PATCH RESEND] ahci: implement aggressive SATA device sleep support
  2012-08-22 10:23 ` David Woodhouse
@ 2012-08-22 10:49   ` Huang, Shane
  0 siblings, 0 replies; 5+ messages in thread
From: Huang, Shane @ 2012-08-22 10:49 UTC (permalink / raw)
  To: David Woodhouse
  Cc: Jeff Garzik, linux-ide, Tejun Heo, aaron.lwe, Huang, Shane

Hi David,

> Am I missing the part where we actually send the command to the drive to
> *enable* the DevSleep feature? And shouldn't we also read the MDAT/DETO
> timings from the drive if it provides them (which it optionally can)?

Thanks for your feedback. You are right, I received the same suggestion
from copied Aaron, my v2 is already in progress with fixes to both
setting feature and MDAT/DETO.


> Also, don't we need to revalidate the drive after a sleep/wake cycle?
> How do we do that in the automatic/aggressive sleep case? After my
> initial reading of the specs, I came to the conclusion that the *sleep*
> is automatic, but the wakeup still requires careful handling (and we
> need to ensure that we don't submit commands while it's asleep, etc.)?

The patch only implements the _aggressive_ SATA device sleep(to be
stated in my v2). I don't see the special handling at wakeup stage,
please tell me if you see some specific problems, if we submit commands
while it's asleep, the DevSlp will exit automatically.


> Since the DevSleep line could just be a GPIO pin, I suspect we are going
> to want to support platforms where it's not automatic, and we
> assert/deassert it under direct software control. My test platform does
> it with an ACPI call, in fact. Have you looked at that mode of operation
> at all?

This patch doesn't care about the software control with PxCMD.ICC,
it only implements the _aggressive_ one.


Regards,
Shane


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2012-08-22 10:49 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-07 17:44 [PATCH RESEND] ahci: implement aggressive SATA device sleep support Shane Huang
2012-08-17 17:53 ` Jeff Garzik
2012-08-19 15:48   ` Huang, Shane
2012-08-22 10:23 ` David Woodhouse
2012-08-22 10:49   ` Huang, Shane

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.