linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3] ahci: add "em_buffer" attribute for AHCI hosts
@ 2010-04-19  3:17 Harry Zhang
  2010-04-19  4:57 ` Tejun Heo
  0 siblings, 1 reply; 5+ messages in thread
From: Harry Zhang @ 2010-04-19  3:17 UTC (permalink / raw)
  To: jgarzik; +Cc: tj, linux-ide, Shane.Huang, Zhang, Harry

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.

Signed-off-by: Harry Zhang <harry.zhang@amd.com>
---
v2: rebase this patch on the new upstream branch of linux-next.git

v3: do not use "ahci_em_messages" module parameter as EM message type
    control, instead by detecting supported types at initialization.
    Remove EM message structure shared with userland, insteady by
    simply writing the raw message into the buffer.
    Move write function for this attribute from libata-scsi.c to
    libachi.c.
    Add read function.
    Add mecessary definitions for EM in ahci.h.

 drivers/ata/ahci.c    |    2 +-
 drivers/ata/ahci.h    |   16 ++++---
 drivers/ata/libahci.c |  114 +++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 112 insertions(+), 20 deletions(-)

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index c44d112..8ca16f5 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1185,7 +1185,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 */
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 733def2..6605b03 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -220,13 +220,16 @@ enum {
 	ICH_MAP				= 0x90, /* ICH MAP register */
 
 	/* em constants */
-	EM_MAX_SLOTS			= 8,
-	EM_MAX_RETRY			= 5,
+	EM_MAX_SLOTS		= 8,
+	EM_MAX_RETRY		= 5,
 
 	/* 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 */
 };
 
 struct ahci_cmd_hdr {
@@ -282,9 +285,10 @@ struct ahci_host_priv {
 	u32			saved_cap2;	/* saved initial cap2 */
 	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 */
 };
 
-extern int ahci_em_messages;
 extern int ahci_ignore_sss;
 
 extern struct scsi_host_template ahci_sht;
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 34fc57d..48cf631 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -108,11 +108,18 @@ static ssize_t ahci_show_host_version(struct device *dev,
 				      struct device_attribute *attr, char *buf);
 static ssize_t ahci_show_port_cmd(struct device *dev,
 				  struct device_attribute *attr, char *buf);
+static ssize_t ahci_read_em_buffer(struct device *dev,
+				   struct device_attribute *attr, char *buf);
+static ssize_t ahci_store_em_buffer(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size);
 
 static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
 static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
 static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL);
 static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL);
+static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
+		   ahci_read_em_buffer, ahci_store_em_buffer);
 
 static struct device_attribute *ahci_shost_attrs[] = {
 	&dev_attr_link_power_management_policy,
@@ -122,6 +129,7 @@ static struct device_attribute *ahci_shost_attrs[] = {
 	&dev_attr_ahci_host_cap2,
 	&dev_attr_ahci_host_version,
 	&dev_attr_ahci_port_cmd,
+	&dev_attr_em_buffer,
 	NULL
 };
 
@@ -184,7 +192,7 @@ EXPORT_SYMBOL_GPL(ahci_em_messages);
 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)");
 
 static void ahci_enable_ahci(void __iomem *mmio)
 {
@@ -252,6 +260,83 @@ static ssize_t ahci_show_port_cmd(struct device *dev,
 	return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD));
 }
 
+static ssize_t ahci_read_em_buffer(struct device *dev,
+				   struct device_attribute *attr, 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 = hpriv->mmio;
+	void __iomem *em_mmio = mmio + hpriv->em_loc;
+	u32 em_ctl, msg;
+	unsigned long flags;
+	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 ||
+					!(em_ctl & EM_CTL_MR)) {
+		spin_unlock_irqrestore(ap->lock, flags);
+		return -EINVAL;
+	}
+
+	if (!(em_ctl & EM_CTL_SMB))
+		em_mmio += hpriv->em_buf_sz;
+
+	for (i = 0; i < hpriv->em_buf_sz; 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 device *dev,
+				    struct device_attribute *attr,
+				    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 = hpriv->mmio;
+	void __iomem *em_mmio = mmio + hpriv->em_loc;
+	u32 em_ctl, msg;
+	unsigned long flags;
+	int i;
+
+	/* check size validity */
+	if (!(ap->flags & ATA_FLAG_EM) || 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 = buf[i] | buf[i + 1] << 8 | buf[i + 2] << 16 |
+						 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;
+}
+
 /**
  *	ahci_save_initial_config - Save and fixup initial config values
  *	@dev: target AHCI device
@@ -931,18 +1016,20 @@ 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 & 0x01) {
+		/*
+		 * 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);
+	}
 
 	/* save off new led state for port/slot */
 	emp->led_state = state;
@@ -2094,10 +2181,11 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
 
 	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;
-- 
1.6.4.2




^ permalink raw reply related	[flat|nested] 5+ messages in thread
* [PATCH v3] ahci: add "em_buffer" attribute for AHCI hosts
@ 2010-04-16  4:46 Harry Zhang
  2010-04-18 23:18 ` Tejun Heo
  0 siblings, 1 reply; 5+ messages in thread
From: Harry Zhang @ 2010-04-16  4:46 UTC (permalink / raw)
  To: jgarzik; +Cc: tj, linux-ide, Shane.Huang, Harry.Zhang

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.

Signed-off-by: Harry Zhang <harry.zhang@amd.com>
---
v2: rebase this patch on the new upstream branch of linux-next.git

v3: do not use "ahci_em_messages" module parameter as EM message type
    control, instead by detecting supported types at initialization.
    Remove EM message structure shared with userland, insteady by
    simply writing the raw message into the buffer.
    Move write function for this attribute from libata-scsi.c to
    libachi.c.
    Add read function.
    Add mecessary definitions for EM in ahci.h.

 drivers/ata/ahci.c    |    2 +-
 drivers/ata/ahci.h    |   16 ++++---
 drivers/ata/libahci.c |  115 +++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 112 insertions(+), 21 deletions(-)

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index c44d112..8ca16f5 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1185,7 +1185,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 */
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 733def2..6605b03 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -220,13 +220,16 @@ enum {
 	ICH_MAP				= 0x90, /* ICH MAP register */
 
 	/* em constants */
-	EM_MAX_SLOTS			= 8,
-	EM_MAX_RETRY			= 5,
+	EM_MAX_SLOTS		= 8,
+	EM_MAX_RETRY		= 5,
 
 	/* 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 */
 };
 
 struct ahci_cmd_hdr {
@@ -282,9 +285,10 @@ struct ahci_host_priv {
 	u32			saved_cap2;	/* saved initial cap2 */
 	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 */
 };
 
-extern int ahci_em_messages;
 extern int ahci_ignore_sss;
 
 extern struct scsi_host_template ahci_sht;
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 34fc57d..5002254 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -108,11 +108,18 @@ static ssize_t ahci_show_host_version(struct device *dev,
 				      struct device_attribute *attr, char *buf);
 static ssize_t ahci_show_port_cmd(struct device *dev,
 				  struct device_attribute *attr, char *buf);
+static ssize_t ahci_read_em_buffer(struct device *dev,
+				   struct device_attribute *attr, char *buf);
+static ssize_t ahci_store_em_buffer(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size);
 
 static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
 static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
 static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL);
 static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL);
+static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
+		   ahci_read_em_buffer, ahci_store_em_buffer);
 
 static struct device_attribute *ahci_shost_attrs[] = {
 	&dev_attr_link_power_management_policy,
@@ -122,6 +129,7 @@ static struct device_attribute *ahci_shost_attrs[] = {
 	&dev_attr_ahci_host_cap2,
 	&dev_attr_ahci_host_version,
 	&dev_attr_ahci_port_cmd,
+	&dev_attr_em_buffer,
 	NULL
 };
 
@@ -184,7 +192,7 @@ EXPORT_SYMBOL_GPL(ahci_em_messages);
 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)");
 
 static void ahci_enable_ahci(void __iomem *mmio)
 {
@@ -252,6 +260,83 @@ static ssize_t ahci_show_port_cmd(struct device *dev,
 	return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD));
 }
 
+static ssize_t ahci_read_em_buffer(struct device *dev,
+				   struct device_attribute *attr, 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 = hpriv->mmio;
+	void __iomem *em_mmio = mmio + hpriv->em_loc;
+	u32 em_ctl, msg;
+	unsigned long flags;
+	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 ||
+					!(em_ctl & EM_CTL_MR)) {
+		spin_unlock_irqrestore(ap->lock, flags);
+		return -EINVAL;
+	}
+
+	if (!(em_ctl & EM_CTL_SMB))
+		em_mmio += hpriv->em_buf_sz;
+
+	for (i = 0; i < hpriv->em_buf_sz; 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 device *dev,
+				    struct device_attribute *attr,
+				    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 = hpriv->mmio;
+	void __iomem *em_mmio = mmio + hpriv->em_loc;
+	u32 em_ctl, msg;
+	unsigned long flags;
+	int i;
+
+	/* check size validity */
+	if (!(ap->flags & ATA_FLAG_EM) || 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 = buf[i] | buf[i + 1] << 8 | buf[i + 2] << 16 |
+						 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;
+}
+
 /**
  *	ahci_save_initial_config - Save and fixup initial config values
  *	@dev: target AHCI device
@@ -931,18 +1016,20 @@ 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 & 0x01) {
+		/*
+		 * 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);
+	}
 
 	/* save off new led state for port/slot */
 	emp->led_state = state;
@@ -2088,16 +2175,16 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
 	void __iomem *mmio = hpriv->mmio;
 	u32 em_loc = readl(mmio + HOST_EM_LOC);
 	u32 em_ctl = readl(mmio + HOST_EM_CTL);
-[PATCH v3] ahci: add "em_buffer" attribute for AHCI hosts
 	if (!ahci_em_messages || !(hpriv->cap & HOST_CAP_EMS))
 		return;
 
 	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;
-- 
1.6.4.2




^ permalink raw reply related	[flat|nested] 5+ messages in thread
* [PATCH] ahci: add "em_buffer" attribute for AHCI hosts
@ 2010-03-09 10:37 Harry Zhang
  2010-04-06  7:43 ` [PATCH UPDATED] " Harry Zhang
  0 siblings, 1 reply; 5+ messages in thread
From: Harry Zhang @ 2010-03-09 10:37 UTC (permalink / raw)
  To: jgarzik; +Cc: linux-ide, tj, Huang, Shane, Zhang, Harry

Add "em_buffer" attribute for SATA AHCI hosts to enable user access AHCI EM
(enclosure management) buffer in user space if the host support EM.

AHCI driver should support SGPIO EM message. However the SATA/AHCI spec does no
define the SGPIO message format filled in EM buffer. Different HW vendors may 
have different definitions. The mainly purpose of "em_buffer" attribute is to
solve this issue by allowing HW vendors to provide user space SGPIO drivers and
tools.

Signed-off-by: Harry Zhang <harry.zhang@amd.com>
---
 drivers/ata/ahci.c        |   73 +++++++++++++++++++++++++++++++++++++++++---
 drivers/ata/libata-scsi.c |   14 ++++++++
 include/linux/libata.h    |    3 ++
 3 files changed, 85 insertions(+), 5 deletions(-)

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 6bd930b..a6b9059 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -268,6 +268,10 @@ enum {
 	EM_CTL_RST			= (1 << 9), /* Reset */
 	EM_CTL_TM			= (1 << 8), /* Transmit Message */
 	EM_CTL_ALHD			= (1 << 26), /* Activity LED */
+
+	/* EM buffer message flags */
+	EM_BUF_MSG_RST			= 1,	    /* reset */
+	EM_BUF_MSG_TM			= (1 << 1), /* transmit message */
 };
 
 struct ahci_cmd_hdr {
@@ -293,6 +297,13 @@ struct ahci_em_priv {
 	unsigned long led_state;
 };
 
+struct ahci_em_buf_msg {
+	u32			flags;	/* message flags */
+	u16			offset;	/* DWORD offset in EM buffer to r/w */
+	u16			len;	/* data length in DWORD */
+	u32			buf[0];	/* message data buffer */
+};
+
 struct ahci_host_priv {
 	unsigned int		flags;		/* AHCI_HFLAG_* */
 	u32			cap;		/* cap to use */
@@ -302,6 +313,7 @@ struct ahci_host_priv {
 	u32			saved_cap2;	/* saved initial cap2 */
 	u32			saved_port_map;	/* saved initial port_map */
 	u32 			em_loc; /* enclosure management location */
+	u32			em_buf_sz;	/* EM buffer size*/
 };
 
 struct ahci_port_priv {
@@ -365,6 +377,8 @@ 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_em_buffer_store(struct ata_port *ap,
+				    const char *buf, size_t size);
 
 static ssize_t ahci_show_host_caps(struct device *dev,
 				   struct device_attribute *attr, char *buf);
@@ -384,6 +398,7 @@ static struct device_attribute *ahci_shost_attrs[] = {
 	&dev_attr_link_power_management_policy,
 	&dev_attr_em_message_type,
 	&dev_attr_em_message,
+	&dev_attr_em_buffer,
 	&dev_attr_ahci_host_caps,
 	&dev_attr_ahci_host_cap2,
 	&dev_attr_ahci_host_version,
@@ -435,6 +450,7 @@ static struct ata_port_operations ahci_ops = {
 	.em_store		= ahci_led_store,
 	.sw_activity_show	= ahci_activity_show,
 	.sw_activity_store	= ahci_activity_store,
+	.em_buffer_store	= ahci_em_buffer_store,
 #ifdef CONFIG_PM
 	.port_suspend		= ahci_port_suspend,
 	.port_resume		= ahci_port_resume,
@@ -721,11 +737,13 @@ static struct pci_driver ahci_pci_driver = {
 #endif
 };
 
-static int ahci_em_messages = 1;
+static int ahci_em_messages = 0x09;
 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 type on-off bit mask. \
+	bit 0 to 3 control LED, SAF-TE, SES-2, and SGPIO respectively \
+	(0 to disable, 1 to enable)");
 
 #if defined(CONFIG_PATA_MARVELL) || defined(CONFIG_PATA_MARVELL_MODULE)
 static int marvell_enable;
@@ -1608,6 +1626,48 @@ static ssize_t ahci_activity_show(struct ata_device *dev, char *buf)
 	return sprintf(buf, "%d\n", emp->blink_policy);
 }
 
+static ssize_t ahci_em_buffer_store(struct ata_port *ap,
+				    const char *buf, size_t size)
+{
+	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;
+	struct ahci_em_buf_msg *msg = (struct ahci_em_buf_msg *)buf;
+	u32 em_ctl;
+	unsigned long flags;
+	int i, tmp;
+
+	/* check message validity */
+	tmp = sizeof(struct ahci_em_buf_msg);
+	if (size % 4 || size < tmp || size > tmp + msg->len * 4 ||
+			msg->offset + msg->len > hpriv->em_buf_sz / 4) {
+		return -EINVAL;
+	}
+
+	if (msg->flags & EM_BUF_MSG_RST)
+		ahci_reset_em(ap->host);
+
+	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 < msg->len; ++i) {
+		tmp = (msg->offset + i) * 4;
+		writel(msg->buf[i], em_mmio + tmp);
+	}
+
+	if (msg->flags & EM_BUF_MSG_TM)
+		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)
@@ -3295,12 +3355,15 @@ 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)) {
+		/* only LED and SGPIO EM messages are enabled as default */
+		ahci_em_messages &= messages;
+		if (ahci_em_messages) {
 			/* store em_loc */
 			hpriv->em_loc = ((em_loc >> 16) * 4);
+			hpriv->em_buf_sz = ((em_loc & 0xff) * 4);
 			pi.flags |= ATA_FLAG_EM;
-			if (!(em_ctl & EM_CTL_ALHD))
+			if (!(em_ctl & EM_CTL_ALHD) &&
+			     (ahci_em_messages == 1))
 				pi.flags |= ATA_FLAG_SW_ACTIVITY;
 		}
 	}
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index bea003a..b1a2379 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -370,6 +370,20 @@ DEVICE_ATTR(sw_activity, S_IWUSR | S_IRUGO, ata_scsi_activity_show,
 			ata_scsi_activity_store);
 EXPORT_SYMBOL_GPL(dev_attr_sw_activity);
 
+static ssize_t
+ata_scsi_em_buffer_store(struct device *dev, struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct Scsi_Host *shost = class_to_shost(dev);
+	struct ata_port *ap = ata_shost_to_port(shost);
+
+	if (ap->ops->em_buffer_store && (ap->flags & ATA_FLAG_EM))
+		return ap->ops->em_buffer_store(ap, buf, count);
+	return -EINVAL;
+}
+DEVICE_ATTR(em_buffer, S_IWUSR, NULL, ata_scsi_em_buffer_store);
+EXPORT_SYMBOL_GPL(dev_attr_em_buffer);
+
 struct device_attribute *ata_common_sdev_attrs[] = {
 	&dev_attr_unload_heads,
 	NULL
diff --git a/include/linux/libata.h b/include/linux/libata.h
index f8ea71e..a26fd0e 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -495,6 +495,7 @@ extern struct device_attribute dev_attr_unload_heads;
 extern struct device_attribute dev_attr_em_message_type;
 extern struct device_attribute dev_attr_em_message;
 extern struct device_attribute dev_attr_sw_activity;
+extern struct device_attribute dev_attr_em_buffer;
 
 enum sw_activity {
 	OFF,
@@ -874,6 +875,8 @@ struct ata_port_operations {
 	ssize_t (*sw_activity_show)(struct ata_device *dev, char *buf);
 	ssize_t (*sw_activity_store)(struct ata_device *dev,
 				     enum sw_activity val);
+	ssize_t (*em_buffer_store)(struct ata_port *ap,
+				  const char *buf, size_t size);
 	/*
 	 * Obsolete
 	 */





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

end of thread, other threads:[~2010-04-19  4:53 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-04-19  3:17 [PATCH v3] ahci: add "em_buffer" attribute for AHCI hosts Harry Zhang
2010-04-19  4:57 ` Tejun Heo
  -- strict thread matches above, loose matches on Subject: below --
2010-04-16  4:46 Harry Zhang
2010-04-18 23:18 ` Tejun Heo
2010-03-09 10:37 [PATCH] " Harry Zhang
2010-04-06  7:43 ` [PATCH UPDATED] " Harry Zhang
2010-04-09  8:00   ` Harry Zhang
2010-04-09 10:14     ` Tejun Heo
     [not found]       ` <1793EC4BDC456040AA0FC17136E1732B207127@sshaexmb1.amd.com>
2010-04-10  1:50         ` Tejun Heo
2010-04-16  4:38           ` [PATCH v3] " Harry Zhang

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).