[RFT PATCH] ahci PMP support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

This patch is originally from Forrest Zhao.  I updated it to the newly
posted PMP patchset[1], rewrote qc_defer and simplified here and
there.  I don't have access to PMP capable ahci at the moment (vt8251
reports pmp but it doesn't seem to work.  I flagged it w/ NO_PMP), so
couldn't test it.  If you have access to known-good ahci w/ PMP
support, please test it.

Thanks.

Index: work/drivers/ata/ahci.c
===================================================================
--- work.orig/drivers/ata/ahci.c	2006-10-17 13:57:39.000000000 +0900
+++ work/drivers/ata/ahci.c	2006-10-17 13:57:39.000000000 +0900
@@ -48,7 +48,7 @@
 #include <asm/io.h>
 
 #define DRV_NAME	"ahci"
-#define DRV_VERSION	"2.0"
+#define DRV_VERSION	"3.0"
 
 
 enum {
@@ -93,6 +93,7 @@ enum {
 
 	/* HOST_CAP bits */
 	HOST_CAP_SSC		= (1 << 14), /* Slumber capable */
+	HOST_CAP_PMP		= (1 << 17), /* Port Multiplier support */
 	HOST_CAP_CLO		= (1 << 24), /* Command List Override support */
 	HOST_CAP_SSS		= (1 << 27), /* Staggered Spin-up */
 	HOST_CAP_NCQ		= (1 << 30), /* Native Command Queueing */
@@ -167,6 +168,7 @@ enum {
 
 	/* ap->flags bits */
 	AHCI_FLAG_NO_NCQ		= (1 << 24),
+	AHCI_FLAG_NO_PMP		= (1 << 25),
 };
 
 struct ahci_cmd_hdr {
@@ -212,6 +214,8 @@ static void ahci_qc_prep(struct ata_queu
 static u8 ahci_check_status(struct ata_port *ap);
 static void ahci_freeze(struct ata_port *ap);
 static void ahci_thaw(struct ata_port *ap);
+static int ahci_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val);
+static int ahci_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val);
 static void ahci_error_handler(struct ata_port *ap);
 static void ahci_vt8251_error_handler(struct ata_port *ap);
 static void ahci_post_internal_cmd(struct ata_queued_cmd *qc);
@@ -251,7 +255,7 @@ static const struct ata_port_operations 
 
 	.tf_read		= ahci_tf_read,
 
-	.qc_defer		= ata_std_qc_defer,
+	.qc_defer		= sata_pmp_qc_defer_cmd_switch,
 	.qc_prep		= ahci_qc_prep,
 	.qc_issue		= ahci_qc_issue,
 
@@ -266,6 +270,9 @@ static const struct ata_port_operations 
 	.error_handler		= ahci_error_handler,
 	.post_internal_cmd	= ahci_post_internal_cmd,
 
+	.pmp_read		= ahci_pmp_read,
+	.pmp_write		= ahci_pmp_write,
+
 	.hp_poll_activate	= sata_std_hp_poll_activate,
 	.hp_poll		= sata_std_hp_poll,
 
@@ -328,7 +335,8 @@ static const struct ata_port_info ahci_p
 		.flags		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
 				  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
 				  ATA_FLAG_SKIP_D2H_BSY |
-				  ATA_FLAG_HRST_TO_RESUME | AHCI_FLAG_NO_NCQ,
+				  ATA_FLAG_HRST_TO_RESUME |
+				  AHCI_FLAG_NO_NCQ | AHCI_FLAG_NO_PMP,
 		.pio_mask	= 0x1f, /* pio0-4 */
 		.udma_mask	= 0x7f, /* udma0-6 ; FIXME */
 		.port_ops	= &ahci_vt8251_ops,
@@ -735,7 +743,8 @@ static int ahci_clo(struct ata_port *ap)
 	return 0;
 }
 
-static int ahci_softreset(struct ata_link *link, unsigned int *class)
+static int ahci_do_softreset(struct ata_link *link, unsigned int *class,
+			     int pmp)
 {
 	struct ata_port *ap = link->ap;
 	struct ahci_port_priv *pp = ap->private_data;
@@ -787,7 +796,7 @@ static int ahci_softreset(struct ata_lin
 			   cmd_fis_len | AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY);
 
 	tf.ctl |= ATA_SRST;
-	ata_tf_to_fis(&tf, 0, 0, fis);
+	ata_tf_to_fis(&tf, pmp, 0, fis);
 
 	writel(1, port_mmio + PORT_CMD_ISSUE);
 
@@ -805,7 +814,7 @@ static int ahci_softreset(struct ata_lin
 	ahci_fill_cmd_slot(pp, 0, cmd_fis_len);
 
 	tf.ctl &= ~ATA_SRST;
-	ata_tf_to_fis(&tf, 0, 0, fis);
+	ata_tf_to_fis(&tf, pmp, 0, fis);
 
 	writel(1, port_mmio + PORT_CMD_ISSUE);
 	readl(port_mmio + PORT_CMD_ISSUE);	/* flush */
@@ -840,6 +849,16 @@ static int ahci_softreset(struct ata_lin
 	return rc;
 }
 
+static int ahci_softreset(struct ata_link *link, unsigned int *class)
+{
+	int pmp = 0;
+
+	if (link->ap->flags & ATA_FLAG_PMP)
+		pmp = SATA_PMP_CTRL_PORT;
+
+	return ahci_do_softreset(link, class, pmp);
+}
+
 static int ahci_hardreset(struct ata_link *link, unsigned int *class)
 {
 	struct ata_port *ap = link->ap;
@@ -918,6 +937,11 @@ static void ahci_postreset(struct ata_li
 	}
 }
 
+static int ahci_pmp_softreset(struct ata_link *link, unsigned int *class)
+{
+	return ahci_do_softreset(link, class, link->pmp);
+}
+
 static u8 ahci_check_status(struct ata_port *ap)
 {
 	void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr;
@@ -1214,8 +1238,10 @@ static void ahci_error_handler(struct at
 	}
 
 	/* perform recovery */
-	ata_do_eh(ap, ata_std_prereset, ahci_softreset, ahci_hardreset,
-		  ahci_postreset);
+	sata_pmp_do_eh(ap, ata_std_prereset, ahci_softreset,
+		       ahci_hardreset, ahci_postreset,
+		       sata_pmp_std_prereset, ahci_pmp_softreset,
+		       sata_pmp_std_hardreset, sata_pmp_std_postreset);
 }
 
 static void ahci_vt8251_error_handler(struct ata_port *ap)
@@ -1409,6 +1435,54 @@ static void ahci_port_stop(struct ata_po
 	kfree(pp);
 }
 
+static int ahci_rw_pmp_reg(struct ata_device *dev, int rw, int pmp, int reg,
+			   u32 *val)
+{
+	const u32 cmd_fis_len = 5; /* five dwords */
+	struct ata_port *ap = dev->link->ap;
+	struct ahci_port_priv *pp = ap->private_data;
+	void __iomem *mmio = ap->host->mmio_base;
+	void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no);
+	u8 *fis = pp->cmd_tbl;
+	struct ata_taskfile tf;
+	u32 tmp;
+
+	if (rw == READ)
+		sata_pmp_read_init_tf(&tf, dev, pmp, reg);
+	else
+		sata_pmp_write_init_tf(&tf, dev, pmp, reg, *val);
+
+	ahci_fill_cmd_slot(pp, 0, cmd_fis_len);
+	ata_tf_to_fis(&tf, SATA_PMP_CTRL_PORT, 1, fis);
+
+	writel(1, port_mmio + PORT_CMD_ISSUE);
+
+	tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, 1, 500);
+	if (tmp & 0x1) {
+		ata_link_printk(dev->link, KERN_ERR, "failed to %s SCR "
+				"register, pmp is %d, reg is %d\n",
+				rw == READ ? "read" : "write", pmp, reg);
+		return -EIO;
+	}
+
+	if (rw == READ) {
+		ahci_tf_read(ap, &tf);
+		*val = sata_pmp_read_val(&tf);
+	}
+
+	return 0;
+}
+
+static int ahci_pmp_read(struct ata_device *dev, int pmp, int reg, u32 *r_val)
+{
+	return ahci_rw_pmp_reg(dev, READ, pmp, reg, r_val);
+}
+
+static int ahci_pmp_write(struct ata_device *dev, int pmp, int reg, u32 val)
+{
+	return ahci_rw_pmp_reg(dev, WRITE, pmp, reg, &val);
+}
+
 static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
 			    unsigned int port_idx)
 {
@@ -1527,25 +1601,27 @@ static void ahci_print_info(struct ata_p
 		scc_s);
 
 	dev_printk(KERN_INFO, &pdev->dev,
-		"flags: "
-	       	"%s%s%s%s%s%s"
-	       	"%s%s%s%s%s%s%s\n"
+		"flags:"
+	       	"%s%s%s%s%s%s%s"
+	       	"%s%s%s%s%s%s%s%s\n"
 	       	,
 
-		cap & (1 << 31) ? "64bit " : "",
-		cap & (1 << 30) ? "ncq " : "",
-		cap & (1 << 28) ? "ilck " : "",
-		cap & (1 << 27) ? "stag " : "",
-		cap & (1 << 26) ? "pm " : "",
-		cap & (1 << 25) ? "led " : "",
-
-		cap & (1 << 24) ? "clo " : "",
-		cap & (1 << 19) ? "nz " : "",
-		cap & (1 << 18) ? "only " : "",
-		cap & (1 << 17) ? "pmp " : "",
-		cap & (1 << 15) ? "pio " : "",
-		cap & (1 << 14) ? "slum " : "",
-		cap & (1 << 13) ? "part " : ""
+		cap & (1 << 31) ? " 64bit" : "",
+		cap & (1 << 30) ? " ncq" : "",
+		probe_ent->port_flags & AHCI_FLAG_NO_NCQ ? "(X)" : "",
+		cap & (1 << 28) ? " ilck" : "",
+		cap & (1 << 27) ? " stag" : "",
+		cap & (1 << 26) ? " pm" : "",
+		cap & (1 << 25) ? " led" : "",
+
+		cap & (1 << 24) ? " clo" : "",
+		cap & (1 << 19) ? " nz" : "",
+		cap & (1 << 18) ? " only" : "",
+		cap & (1 << 17) ? " pmp" : "",
+		probe_ent->port_flags & AHCI_FLAG_NO_PMP ? "(X)" : "",
+		cap & (1 << 15) ? " pio" : "",
+		cap & (1 << 14) ? " slum" : "",
+		cap & (1 << 13) ? " part" : ""
 		);
 }
 
@@ -1642,6 +1718,10 @@ static int ahci_init_one (struct pci_dev
 	    (hpriv->cap & HOST_CAP_NCQ))
 		probe_ent->port_flags |= ATA_FLAG_NCQ;
 
+	if (!(probe_ent->port_flags & AHCI_FLAG_NO_PMP) &&
+	    (hpriv->cap & HOST_CAP_PMP))
+		probe_ent->port_flags |= ATA_FLAG_PMP;
+
 	ahci_print_info(probe_ent);
 
 	/* FIXME: check ata_device_add return value */
Index: work/drivers/ata/libata-core.c
===================================================================
--- work.orig/drivers/ata/libata-core.c	2006-10-17 13:57:39.000000000 +0900
+++ work/drivers/ata/libata-core.c	2006-10-17 13:57:39.000000000 +0900
@@ -6463,6 +6463,7 @@ EXPORT_SYMBOL_GPL(ata_pci_clear_simplex)
 EXPORT_SYMBOL_GPL(ata_scsi_device_suspend);
 EXPORT_SYMBOL_GPL(ata_scsi_device_resume);
 
+EXPORT_SYMBOL_GPL(sata_pmp_qc_defer_cmd_switch);
 EXPORT_SYMBOL_GPL(sata_pmp_read_init_tf);
 EXPORT_SYMBOL_GPL(sata_pmp_read_val);
 EXPORT_SYMBOL_GPL(sata_pmp_write_init_tf);
Index: work/drivers/ata/libata-pmp.c
===================================================================
--- work.orig/drivers/ata/libata-pmp.c	2006-10-17 13:57:39.000000000 +0900
+++ work/drivers/ata/libata-pmp.c	2006-10-17 13:57:39.000000000 +0900
@@ -37,6 +37,36 @@
 #include "libata.h"
 
 /**
+ *	sata_pmp_qc_defer_cmd_switch - qc_defer for command switching PMP
+ *	@qc: ATA command in question
+ *
+ *	A host which has command switching PMP support cannot issue
+ *	commands to multiple links simultaneously.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host lock)
+ *
+ *	RETURNS:
+ *	ATA_DEFER_* if deferring is needed, 0 otherwise.
+ */
+int sata_pmp_qc_defer_cmd_switch(struct ata_queued_cmd *qc)
+{
+	struct ata_link *link = qc->dev->link;
+	struct ata_port *ap = link->ap;
+
+	if (ap->excl_link == NULL || ap->excl_link == link) {
+		if (ap->nr_active_links == 0 || ata_link_active(link)) {
+			qc->flags |= ATA_QCFLAG_CLEAR_EXCL;
+			return ata_std_qc_defer(qc);
+		}
+
+		ap->excl_link = link;
+	}
+
+	return ATA_DEFER_PORT;
+}
+
+/**
  *	sata_pmp_read_init_tf - initialize TF for PMP read
  *	@tf: taskfile to initialize
  *	@dev: PMP dev
Index: work/include/linux/libata.h
===================================================================
--- work.orig/include/linux/libata.h	2006-10-17 13:57:39.000000000 +0900
+++ work/include/linux/libata.h	2006-10-17 13:57:39.000000000 +0900
@@ -929,6 +929,7 @@ extern unsigned long ata_pci_default_fil
 /*
  * PMP
  */
+extern int sata_pmp_qc_defer_cmd_switch(struct ata_queued_cmd *qc);
 extern void sata_pmp_read_init_tf(struct ata_taskfile *tf,
 				  struct ata_device *dev, int pmp, int reg);
 extern u32 sata_pmp_read_val(const struct ata_taskfile *tf);
-
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux