linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Tejun Heo <htejun@gmail.com>
To: jgarzik@pobox.com, alan@lxorguk.ukuu.org.uk, lkml@rtr.ca,
	axboe@suse.de, forrest.zhao@intel.com, linux-ide@vger.kernel.org
Cc: Tejun Heo <htejun@gmail.com>
Subject: [PATCH 11/12] ahci: implement link powersave
Date: Mon, 17 Jul 2006 15:52:32 +0900	[thread overview]
Message-ID: <11531191523232-git-send-email-htejun@gmail.com> (raw)
In-Reply-To: <11531191512028-git-send-email-htejun@gmail.com>

Implement link powersave.  AHCI can initiate transition to powersave
mode but doesn't use standard SControl SPM field.  It uses ICC field
in PORT_CMD register.  As PHY RDY changed interrupt can trigger during
dynamic link powersave, it needs to be turned off while dynamic
powersave is active.  All other can be taken care of by standard
helpers.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/ahci.c |   66 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 66 insertions(+), 0 deletions(-)

14143d016b88395ffcec7be07ffa99d652a3353d
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 77e7202..b5fe19a 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -92,6 +92,8 @@ enum {
 	HOST_AHCI_EN		= (1 << 31), /* AHCI enabled */
 
 	/* HOST_CAP bits */
+	HOST_CAP_PARTIAL	= (1 << 13), /* Partial state support */
+	HOST_CAP_SLUMBER	= (1 << 14), /* Slumber state support */
 	HOST_CAP_CLO		= (1 << 24), /* Command List Override support */
 	HOST_CAP_NCQ		= (1 << 30), /* Native Command Queueing */
 	HOST_CAP_64		= (1 << 31), /* PCI DAC (64-bit DMA) support */
@@ -158,6 +160,7 @@ enum {
 	PORT_CMD_ICC_ACTIVE	= (0x1 << 28), /* Put i/f in active state */
 	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_CMD_ICC_MASK	= (0xf << 28),
 
 	/* hpriv->flags bits */
 	AHCI_FLAG_MSI		= (1 << 0),
@@ -212,6 +215,7 @@ static void ahci_freeze(struct ata_port 
 static void ahci_thaw(struct ata_port *ap);
 static void ahci_error_handler(struct ata_port *ap);
 static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
+static void ahci_set_powersave(struct ata_port *ap, int ps_state);
 static void ahci_remove_one (struct pci_dev *pdev);
 
 static struct scsi_host_template ahci_sht = {
@@ -257,6 +261,8 @@ static const struct ata_port_operations 
 	.error_handler		= ahci_error_handler,
 	.post_internal_cmd	= ahci_post_internal_cmd,
 
+	.set_powersave		= ahci_set_powersave,
+
 	.port_start		= ahci_port_start,
 	.port_stop		= ahci_port_stop,
 };
@@ -913,6 +919,9 @@ static void ahci_host_intr(struct ata_po
 	status = readl(port_mmio + PORT_IRQ_STAT);
 	writel(status, port_mmio + PORT_IRQ_STAT);
 
+	if (ata_ps_dynamic(ap->ps_state))
+		status &= ~PORT_IRQ_PHYRDY;
+
 	if (unlikely(status & PORT_IRQ_ERROR)) {
 		ahci_error_intr(ap, status);
 		return;
@@ -1077,6 +1086,60 @@ static void ahci_post_internal_cmd(struc
 	}
 }
 
+static void ahci_update_sctl_spm(struct ata_port *ap, u8 sctl_spm,
+				 int may_push_sctl)
+{
+	static const u32 icc_map[] = {
+		[0x1]	= PORT_CMD_ICC_PARTIAL,
+		[0x2]	= PORT_CMD_ICC_SLUMBER,
+		[0x4]	= PORT_CMD_ICC_ACTIVE,
+	};
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	u32 tmp;
+
+	tmp = readl(port_mmio + PORT_CMD);
+
+	/* proceed only if idle */
+	if (!(tmp & PORT_CMD_ICC_MASK))
+		writel(tmp | icc_map[sctl_spm], port_mmio + PORT_CMD);
+}
+
+static unsigned long ahci_hips_timer_fn(struct ata_port *ap, int seq)
+{
+	u8 sctl_ipm = ata_scontrol_field(ap->scontrol, ATA_SCTL_IPM);
+
+	return sata_do_hips_timer_fn(ap, seq, sctl_ipm, ahci_update_sctl_spm);
+}
+
+static void ahci_set_powersave(struct ata_port *ap, int ps_state)
+{
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	struct ahci_host_priv *hpriv = ap->host_set->private_data;
+	u8 sctl_ipm;
+
+	/* determine IPM */
+	sctl_ipm = 0x3;
+	if (hpriv->cap & HOST_CAP_PARTIAL)
+		sctl_ipm &= ~0x1;
+	if (hpriv->cap & HOST_CAP_SLUMBER)
+		sctl_ipm &= ~0x2;
+
+	/* turn off SError.N IRQ if entering dynamic powersave mode */
+	if (ata_ps_dynamic(ps_state))
+		writel(DEF_PORT_IRQ & ~PORT_IRQ_PHYRDY,
+		       port_mmio + PORT_IRQ_MASK);
+
+	/* do standard SATA set_powersave */
+	if (ata_ps_dynamic(ps_state) == ATA_PS_HIPS) {
+		ap->ps_timer_fn = ahci_hips_timer_fn;
+		sata_determine_hips_params(ap, &sctl_ipm);
+	}
+
+	sata_do_set_powersave(ap, ps_state, sctl_ipm, ahci_update_sctl_spm);
+}
+
 static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
 			    unsigned int port_idx)
 {
@@ -1398,6 +1461,9 @@ static int ahci_init_one (struct pci_dev
 	    (hpriv->cap & HOST_CAP_NCQ))
 		probe_ent->host_flags |= ATA_FLAG_NCQ;
 
+	if (hpriv->cap & (HOST_CAP_PARTIAL | HOST_CAP_SLUMBER))
+		probe_ent->host_flags |= ATA_FLAG_HIPS | ATA_FLAG_DIPS;
+
 	ahci_print_info(probe_ent);
 
 	/* FIXME: check ata_device_add return value */
-- 
1.3.2



  parent reply	other threads:[~2006-07-17  6:51 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-07-17  6:52 [PATCHSET] libata: implement runtime link powersave Tejun Heo
2006-07-17  6:52 ` [PATCH 01/12] libata: add msec_to_jiffies() Tejun Heo
2006-07-17  6:52 ` [PATCH 02/12] libata: add ata_id_has_sata() and use it in ata_id_has_ncq() Tejun Heo
2006-07-17  6:52 ` [PATCH 04/12] libata: implement ata_all_ports list Tejun Heo
2006-07-19 19:34   ` Jeff Garzik
2006-07-17  6:52 ` [PATCH 03/12] libata: add more SATA specific constants and macros to ata.h Tejun Heo
2006-07-19 19:32   ` Jeff Garzik
2006-07-17  6:52 ` [PATCH 05/12] libata: make counting functions global Tejun Heo
2006-07-17  6:52 ` [PATCH 09/12] libata: implement powersave timer Tejun Heo
2006-07-19 19:48   ` Jeff Garzik
2006-07-19 20:22     ` Jens Axboe
2006-07-24  7:27       ` Tejun Heo
2006-07-25  8:01         ` Jens Axboe
2006-07-17  6:52 ` [PATCH 07/12] libata: implement sata_update_scontrol() Tejun Heo
2006-07-19 19:35   ` Jeff Garzik
2006-07-17  6:52 ` [PATCH 08/12] libata: implement interface power management infrastructure Tejun Heo
2006-07-19 19:45   ` Jeff Garzik
2006-07-24  8:02     ` Tejun Heo
2006-07-17  6:52 ` Tejun Heo [this message]
2006-07-19 19:51   ` [PATCH 11/12] ahci: implement link powersave Jeff Garzik
2006-07-17  6:52 ` [PATCH 10/12] libata: implement standard powersave methods Tejun Heo
2006-07-19 19:50   ` Jeff Garzik
2006-07-17  6:52 ` [PATCH 06/12] libata: add ata_port_nr_ready() Tejun Heo
2006-07-17  6:52 ` [PATCH 12/12] sata_sil24: implement link powersave Tejun Heo
2006-07-19 19:38 ` [PATCHSET] libata: implement runtime " Jeff Garzik
2006-07-24  7:33   ` Tejun Heo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=11531191523232-git-send-email-htejun@gmail.com \
    --to=htejun@gmail.com \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=axboe@suse.de \
    --cc=forrest.zhao@intel.com \
    --cc=jgarzik@pobox.com \
    --cc=linux-ide@vger.kernel.org \
    --cc=lkml@rtr.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).