Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 156

kernel-2.6.18-238.el5.src.rpm

From: David Milburn <dmilburn@redhat.com>
Date: Tue, 20 Jul 2010 20:53:35 -0400
Subject: [ata] ahci: add em_buffer attribute for AHCI hosts
Message-id: <20100720205335.GB10683@localhost.localdomain>
Patchwork-id: 26979
O-Subject: [RHEL5.6 PATCH] ahci: add "em_buffer" attribute for AHCI hosts
Bugzilla: 568364

This ahci patch will add support for the SGPIO message type and
allow messages to be transmitted and received through the Enclosure
Management message buffer. This patch is from Harry Zhang (AMD)
and has been tested by AMD.

The following upstream commits were backported to RHEL5:

commit c06231661e4fb5f2f50c73ff33702937a11764cf
Author: Harry Zhang <harry.zhang@amd.com>
Date:   Fri Apr 23 17:28:38 2010 +0800

    ahci: add "em_buffer" attribute for AHCI hosts

    Add "em_buffer" attribute for SATA AHCI hosts to provide a way for
    userland to access AHCI EM (enclosure management) buffer directly if the
    host supports EM.

    AHCI driver should support SGPIO EM messages. However the SATA/AHCI
    specs did not define the SGPIO message format filled in EM buffer.
    Different HW vendors may have different definitions. The mainly purpose
    of this attribute is to solve this issue by allowing HW vendors to
    provide userland drivers and tools for their SGPIO initiators.

commit 008dbd61ebee3e647f63bbe8315192e1331cd75f
Author: Harry Zhang <harry.zhang@amd.com>
Date:   Fri Apr 23 17:27:19 2010 +0800

    ahci: EM message type auto detect

commit f9ce889b8f8384ee29e1be4b34091a932e6e40f3
Author: Harry Zhang <harry.zhang@amd.com>
Date:   Thu Jun 24 11:34:23 2010 +0800

    libahci: Fix bug in storing EM messages

This resolves BZ 568364, please review and ACK.

Thanks,
David

 drivers/ata/ahci.c |  158 ++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 136 insertions(+), 22 deletions(-)

Signed-off-by: Jarod Wilson <jarod@redhat.com>

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 544e8ad..5d3bb5b 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -236,9 +236,18 @@ enum {
 	ICH_MAP				= 0x90, /* ICH MAP register */
 
 	/* em_ctl bits */
-	EM_CTL_RST			= (1 << 9), /* Reset */
-	EM_CTL_TM			= (1 << 8), /* Transmit Message */
-	EM_CTL_ALHD			= (1 << 26), /* Activity LED */
+	EM_CTL_RST		= (1 << 9), /* Reset */
+	EM_CTL_TM		= (1 << 8), /* Transmit Message */
+	EM_CTL_MR		= (1 << 0), /* Message Recieved */
+	EM_CTL_ALHD		= (1 << 26), /* Activity LED */
+	EM_CTL_XMT		= (1 << 25), /* Transmit Only */
+	EM_CTL_SMB		= (1 << 24), /* Single Message Buffer */
+
+	/* em message type */
+	EM_MSG_TYPE_LED		= (1 << 0), /* LED */
+	EM_MSG_TYPE_SAFTE	= (1 << 1), /* SAF-TE */
+	EM_MSG_TYPE_SES2	= (1 << 2), /* SES-2 */
+	EM_MSG_TYPE_SGPIO	= (1 << 3), /* SGPIO */
 };
 
 struct ahci_cmd_hdr {
@@ -271,6 +280,8 @@ struct ahci_host_priv {
 	u32			saved_cap;	/* saved initial cap */
 	u32			saved_port_map;	/* saved initial port_map */
 	u32 			em_loc; /* enclosure management location */
+	u32			em_buf_sz;	/* EM buffer size in byte */
+	u32			em_msg_type;	/* EM message type */
 };
 
 struct ahci_port_priv {
@@ -335,11 +346,17 @@ static ssize_t ahci_activity_show(struct ata_device *dev, char *buf);
 static ssize_t ahci_activity_store(struct ata_device *dev,
 				   enum sw_activity val);
 static void ahci_init_sw_activity(struct ata_link *link);
+static ssize_t ahci_read_em_buffer(struct class_device *dev, char *buf);
+static ssize_t ahci_store_em_buffer(struct class_device *dev,
+				    const char *buf, size_t size);
+CLASS_DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
+		   ahci_read_em_buffer, ahci_store_em_buffer);
 
 static struct class_device_attribute *ahci_shost_attrs[] = {
 	&class_device_attr_link_power_management_policy,
 	&class_device_attr_em_message_type,
 	&class_device_attr_em_message,
+	&class_device_attr_em_buffer,
 	NULL
 };
 
@@ -640,7 +657,7 @@ static int ahci_em_messages = 1;
 module_param(ahci_em_messages, int, 0444);
 /* add other LED protocol types when they become supported */
 MODULE_PARM_DESC(ahci_em_messages,
-	"Set AHCI Enclosure Management Message type (0 = disabled, 1 = LED");
+	"AHCI Enclosure Management Message control (0 = off, 1 = on)");
 
 #if defined(CONFIG_PATA_MARVELL) || defined(CONFIG_PATA_MARVELL_MODULE)
 static int marvell_enable;
@@ -1350,27 +1367,29 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
 		return -EBUSY;
 	}
 
-	/*
-	 * create message header - this is all zero except for
-	 * the message size, which is 4 bytes.
-	 */
-	message[0] |= (4 << 8);
+	if (hpriv->em_msg_type & EM_MSG_TYPE_LED) {
+		/*
+		 * create message header - this is all zero except for
+		 * the message size, which is 4 bytes.
+		 */
+		message[0] |= (4 << 8);
 
-	/* ignore 0:4 of byte zero, fill in port info yourself */
-	message[1] = ((state & ~EM_MSG_LED_HBA_PORT) | ap->port_no);
+		/* ignore 0:4 of byte zero, fill in port info yourself */
+		message[1] = ((state & ~EM_MSG_LED_HBA_PORT) | ap->port_no);
 
-	/* write message to EM_LOC */
-	writel(message[0], mmio + hpriv->em_loc);
-	writel(message[1], mmio + hpriv->em_loc+4);
+		/* write message to EM_LOC */
+		writel(message[0], mmio + hpriv->em_loc);
+		writel(message[1], mmio + hpriv->em_loc+4);
+
+		/*
+		 * tell hardware to transmit the message
+		 */
+		writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
+	}
 
 	/* save off new led state for port/slot */
 	emp->led_state = state;
 
-	/*
-	 * tell hardware to transmit the message
-	 */
-	writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
-
 	spin_unlock_irqrestore(ap->lock, flags);
 	return size;
 }
@@ -1460,6 +1479,100 @@ static ssize_t ahci_activity_show(struct ata_device *dev, char *buf)
 	return sprintf(buf, "%d\n", emp->blink_policy);
 }
 
+static ssize_t ahci_read_em_buffer(struct class_device *dev, char *buf)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ata_port *ap = ata_shost_to_port(shost);
+	struct ahci_host_priv *hpriv = ap->host->private_data;
+	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
+	void __iomem *em_mmio = mmio + hpriv->em_loc;
+	u32 em_ctl, msg;
+	unsigned long flags;
+	size_t count;
+	int i;
+
+	spin_lock_irqsave(ap->lock, flags);
+
+	em_ctl = readl(mmio + HOST_EM_CTL);
+	if (!(ap->flags & ATA_FLAG_EM) || em_ctl & EM_CTL_XMT ||
+	    !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO)) {
+		spin_unlock_irqrestore(ap->lock, flags);
+		return -EINVAL;
+	}
+
+	if (!(em_ctl & EM_CTL_MR)) {
+		spin_unlock_irqrestore(ap->lock, flags);
+		return -EAGAIN;
+	}
+
+	if (!(em_ctl & EM_CTL_SMB))
+		em_mmio += hpriv->em_buf_sz;
+
+	count = hpriv->em_buf_sz;
+
+	/* the count should not be larger than PAGE_SIZE */
+	if (count > PAGE_SIZE) {
+		if (printk_ratelimit())
+			ata_port_printk(ap, KERN_WARNING,
+					"EM read buffer size too large: "
+					"buffer size %u, page size %lu\n",
+					hpriv->em_buf_sz, PAGE_SIZE);
+		count = PAGE_SIZE;
+	}
+
+	for (i = 0; i < count; i += 4) {
+		msg = readl(em_mmio + i);
+		buf[i] = msg & 0xff;
+		buf[i + 1] = (msg >> 8) & 0xff;
+		buf[i + 2] = (msg >> 16) & 0xff;
+		buf[i + 3] = (msg >> 24) & 0xff;
+	}
+
+	spin_unlock_irqrestore(ap->lock, flags);
+
+	return i;
+}
+
+static ssize_t ahci_store_em_buffer(struct class_device *dev,
+				    const char *buf, size_t size)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ata_port *ap = ata_shost_to_port(shost);
+	struct ahci_host_priv *hpriv = ap->host->private_data;
+	void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR];
+	void __iomem *em_mmio = mmio + hpriv->em_loc;
+	const unsigned char *msg_buf = buf;
+	u32 em_ctl, msg;
+	unsigned long flags;
+	int i;
+
+	/* check size validity */
+	if (!(ap->flags & ATA_FLAG_EM) ||
+	    !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO) ||
+	    size % 4 || size > hpriv->em_buf_sz)
+		return -EINVAL;
+
+	spin_lock_irqsave(ap->lock, flags);
+
+	em_ctl = readl(mmio + HOST_EM_CTL);
+	if (em_ctl & EM_CTL_TM) {
+		spin_unlock_irqrestore(ap->lock, flags);
+		return -EBUSY;
+	}
+
+	for (i = 0; i < size; i += 4) {
+		msg = msg_buf[i] | msg_buf[i + 1] << 8 |
+			msg_buf[i + 2] << 16 | msg_buf[i + 3] << 24;
+		writel(msg, em_mmio + i);
+	}
+
+	writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
+
+	spin_unlock_irqrestore(ap->lock, flags);
+
+	return size;
+}
+
 static void ahci_port_init(struct pci_dev *pdev, struct ata_port *ap,
 			   int port_no, void __iomem *mmio,
 			   void __iomem *port_mmio)
@@ -2844,10 +2957,11 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 		messages = (em_ctl & EM_CTRL_MSG_TYPE) >> 16;
 
-		/* we only support LED message type right now */
-		if ((messages & 0x01) && (ahci_em_messages == 1)) {
+		if (messages) {
 			/* store em_loc */
 			hpriv->em_loc = ((em_loc >> 16) * 4);
+			hpriv->em_buf_sz = ((em_loc & 0xff) * 4);
+			hpriv->em_msg_type = messages;
 			pi.flags |= ATA_FLAG_EM;
 			if (!(em_ctl & EM_CTL_ALHD))
 				pi.flags |= ATA_FLAG_SW_ACTIVITY;
@@ -2882,7 +2996,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 		/* set enclosure management message type */
 		if (ap->flags & ATA_FLAG_EM)
-			ap->em_message_type = ahci_em_messages;
+			ap->em_message_type = hpriv->em_msg_type;
 
 		/* disabled/not-implemented port */
 		if (!(hpriv->port_map & (1 << i)))