rpms/kernel/devel linux-2.6-sata-ahci-suspend.patch, NONE, 1.1 kernel-2.6.spec, 1.2145, 1.2146

fedora-cvs-commits at redhat.com fedora-cvs-commits at redhat.com
Fri Apr 21 03:21:16 UTC 2006


Author: davej

Update of /cvs/dist/rpms/kernel/devel
In directory cvs.devel.redhat.com:/tmp/cvs-serv4119

Modified Files:
	kernel-2.6.spec 
Added Files:
	linux-2.6-sata-ahci-suspend.patch 
Log Message:
fix ahci suspend


linux-2.6-sata-ahci-suspend.patch:
 ahci.c |  194 +++++++++++++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 141 insertions(+), 53 deletions(-)

--- NEW FILE linux-2.6-sata-ahci-suspend.patch ---
diff -ur linux-2.6.16.noarch/drivers/scsi/ahci.c linux-ahci.suspend/drivers/scsi/ahci.c
--- linux-2.6.16.noarch/drivers/scsi/ahci.c	2006-04-20 14:39:10.000000000 -0400
+++ linux-ahci.suspend/drivers/scsi/ahci.c	2006-04-20 22:57:54.000000000 -0400
@@ -44,6 +44,7 @@
 #include <linux/device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
 #include <linux/libata.h>
 #include <asm/io.h>
 
@@ -194,8 +195,14 @@
 static int ahci_probe_reset(struct ata_port *ap, unsigned int *classes);
 static void ahci_irq_clear(struct ata_port *ap);
 static void ahci_eng_timeout(struct ata_port *ap);
+static int ahci_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state);
+static int ahci_scsi_device_resume(struct scsi_device *sdev);
 static int ahci_port_start(struct ata_port *ap);
+static void ahci_port_suspend(struct ata_port *ap);
+static void ahci_port_resume(struct ata_port *ap);
 static void ahci_port_stop(struct ata_port *ap);
+static void ahci_start_engine(struct ata_port *ap);
+static int ahci_stop_engine(struct ata_port *ap);
 static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
 static void ahci_qc_prep(struct ata_queued_cmd *qc);
 static u8 ahci_check_status(struct ata_port *ap);
@@ -217,6 +224,8 @@
 	.dma_boundary		= AHCI_DMA_BOUNDARY,
 	.slave_configure	= ata_scsi_slave_config,
 	.bios_param		= ata_std_bios_param,
+	.resume			= ahci_scsi_device_resume,
+	.suspend		= ahci_scsi_device_suspend,
 };
 
 static const struct ata_port_operations ahci_ops = {
@@ -305,6 +314,8 @@
 	.id_table		= ahci_pci_tbl,
 	.probe			= ahci_init_one,
 	.remove			= ahci_remove_one,
+	.suspend		= ata_pci_device_suspend,
+	.resume			= ata_pci_device_resume,
 };
 
 
@@ -321,10 +332,7 @@
 static int ahci_port_start(struct ata_port *ap)
 {
 	struct device *dev = ap->host_set->dev;
-	struct ahci_host_priv *hpriv = ap->host_set->private_data;
 	struct ahci_port_priv *pp;
-	void __iomem *mmio = ap->host_set->mmio_base;
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
 	void *mem;
 	dma_addr_t mem_dma;
 	int rc;
@@ -378,6 +386,22 @@
 
 	ap->private_data = pp;
 
+	/*
+	 * Internal structures are initialized,
+	 * we can now do a simple resume()
+	 */
+	ahci_port_resume(ap);
+
+	return 0;
+}
+
+static void ahci_port_resume(struct ata_port *ap)
+{
+	void *mmio = ap->host_set->mmio_base;
+	void *port_mmio = ahci_port_base(mmio, ap->port_no);
+	struct ahci_host_priv *hpriv = ap->host_set->private_data;
+	struct ahci_port_priv *pp = ap->private_data;
+
 	if (hpriv->cap & HOST_CAP_64)
 		writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
 	writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
@@ -389,31 +413,49 @@
 	readl(port_mmio + PORT_FIS_ADDR); /* flush */
 
 	writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
-	       PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
-	       PORT_CMD_START, port_mmio + PORT_CMD);
+	       PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP,
+	       port_mmio + PORT_CMD);
 	readl(port_mmio + PORT_CMD); /* flush */
 
-	return 0;
+	ahci_start_engine(ap);
 }
 
-
-static void ahci_port_stop(struct ata_port *ap)
+static void ahci_port_suspend(struct ata_port *ap)
 {
-	struct device *dev = ap->host_set->dev;
-	struct ahci_port_priv *pp = ap->private_data;
-	void __iomem *mmio = ap->host_set->mmio_base;
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	void *mmio = ap->host_set->mmio_base;
+	void *port_mmio = ahci_port_base(mmio, ap->port_no);
 	u32 tmp;
+	int work;
 
+	ahci_stop_engine(ap);
+
+	/*
+	 * Disable FIS reception
+	 */
 	tmp = readl(port_mmio + PORT_CMD);
-	tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX);
+	tmp &= ~(PORT_CMD_FIS_RX);
 	writel(tmp, port_mmio + PORT_CMD);
 	readl(port_mmio + PORT_CMD); /* flush */
 
-	/* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
-	 * this is slightly incorrect.
+	/*
+	 * Wait for HBA to acknowledge.
+	 * This could be as long as 500 msec
 	 */
-	msleep(500);
+	work = 1000;
+	while (work-- > 0) {
+		tmp = readl(port_mmio + PORT_CMD);
+		if ((tmp & PORT_CMD_FIS_ON) == 0)
+			break;
+		udelay(10);
+	}
+}
+
+static void ahci_port_stop(struct ata_port *ap)
+{
+	struct device *dev = ap->host_set->dev;
+	struct ahci_port_priv *pp = ap->private_data;
+
+	ahci_port_suspend(ap);
 
 	ap->private_data = NULL;
 	dma_free_coherent(dev, AHCI_PORT_PRIV_DMA_SZ,
@@ -456,43 +498,6 @@
 	writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4));
 }
 
-static int ahci_stop_engine(struct ata_port *ap)
-{
-	void __iomem *mmio = ap->host_set->mmio_base;
-	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
-	int work;
-	u32 tmp;
-
-	tmp = readl(port_mmio + PORT_CMD);
-	tmp &= ~PORT_CMD_START;
-	writel(tmp, port_mmio + PORT_CMD);
-
-	/* wait for engine to stop.  TODO: this could be
-	 * as long as 500 msec
-	 */
-	work = 1000;
-	while (work-- > 0) {
-		tmp = readl(port_mmio + PORT_CMD);
-		if ((tmp & PORT_CMD_LIST_ON) == 0)
-			return 0;
-		udelay(10);
-	}
-
-	return -EIO;
-}
-
-static void ahci_start_engine(struct ata_port *ap)
-{
-	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);
-	tmp |= PORT_CMD_START;
-	writel(tmp, port_mmio + PORT_CMD);
-	readl(port_mmio + PORT_CMD); /* flush */
-}
-
 static unsigned int ahci_dev_classify(struct ata_port *ap)
 {
 	void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
@@ -772,6 +777,65 @@
 	ahci_fill_cmd_slot(pp, opts);
 }
 
+static void ahci_start_engine(struct ata_port *ap)
+{
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	u32 tmp;
+	int work;
+
+	tmp = readl(port_mmio + PORT_CMD);
+	/*
+	 * AHCI rev 1.1 section 10.3.1:
+	 * Software shall not set PxCMD.ST to ‘1’ until it verifies
+	 * that PxCMD.CR is ‘0’ and has set PxCMD.FRE to ‘1’.
+	 */
+	if ((tmp & PORT_CMD_FIS_RX) == 0)
+		printk(KERN_WARNING "ata%d: dma not running\n",ap->id);
+	/* 
+	 * wait for engine to become idle.
+	 */
+	work = 1000;
+	while (work-- > 0) {
+		tmp = readl(port_mmio + PORT_CMD);
+		if ((tmp & PORT_CMD_LIST_ON) == 0)
+			break;
+		udelay(10);
+	}
+	
+	/*
+	 * Start DMA
+	 */
+	tmp |= PORT_CMD_START;
+	writel(tmp, port_mmio + PORT_CMD);
+	readl(port_mmio + PORT_CMD); /* flush */
+}
+
+static int ahci_stop_engine(struct ata_port *ap)
+{
+	void __iomem *mmio = ap->host_set->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	int work;
+	u32 tmp;
+
+	tmp = readl(port_mmio + PORT_CMD);
+	tmp &= ~PORT_CMD_START;
+	writel(tmp, port_mmio + PORT_CMD);
+
+	/* 
+	 * wait for engine to become idle
+	 */
+	work = 1000;
+	while (work-- > 0) {
+		tmp = readl(port_mmio + PORT_CMD);
+		if ((tmp & PORT_CMD_LIST_ON) == 0)
+			return 0;
+		udelay(10);
+	}
+
+	return -EIO;
+}
+
 static void ahci_restart_port(struct ata_port *ap, u32 irq_stat)
 {
 	void __iomem *mmio = ap->host_set->mmio_base;
@@ -953,6 +1017,30 @@
 	return 0;
 }
 
+static int ahci_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state)
+{
+	struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+	struct ata_device *dev = &ap->device[sdev->id];
+	int rc;
+
+	rc = ata_device_suspend(ap, dev, state);
+
+	if (!rc)
+		ahci_port_suspend(ap);
+
+	return rc;
+}
+
+static int ahci_scsi_device_resume(struct scsi_device *sdev)
+{
+	struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0];
+	struct ata_device *dev = &ap->device[sdev->id];
+
+	ahci_port_resume(ap);
+
+	return ata_device_resume(ap, dev);
+}
+
 static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
 			    unsigned int port_idx)
 {


Index: kernel-2.6.spec
===================================================================
RCS file: /cvs/dist/rpms/kernel/devel/kernel-2.6.spec,v
retrieving revision 1.2145
retrieving revision 1.2146
diff -u -r1.2145 -r1.2146
--- kernel-2.6.spec	21 Apr 2006 01:48:20 -0000	1.2145
+++ kernel-2.6.spec	21 Apr 2006 03:21:12 -0000	1.2146
@@ -364,6 +364,7 @@
 # SATA Bits
 Patch2200: linux-2.6-sata-promise-pata-ports.patch
 Patch2201: linux-2.6-sata-silence-dumb-msg.patch
+Patch2202: linux-2.6-sata-ahci-suspend.patch
 
 # ACPI bits
 Patch2300: linux-2.6-acpi_os_acquire_object-gfp_kernel-called-with-irqs.patch
@@ -988,6 +989,8 @@
 #%patch2200 -p1
 # Silence silly SATA printk.
 %patch2201 -p1
+# Fix AHCI Suspend.
+%patch2202 -p1
 
 # ACPI patches
 # Silence more ACPI debug spew from suspend.
@@ -1662,6 +1665,7 @@
 %changelog
 * Thu Apr 20 2006 Dave Jones <davej at redhat.com>
 - 2.6.17rc2-git3
+- Make AHCI suspend/resume work.
 
 * Wed Apr 19 2006 Dave Jones <davej at redhat.com>
 - 2.6.17rc2-git1




More information about the fedora-cvs-commits mailing list