aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata
diff options
context:
space:
mode:
authorHarry Zhang <harry.zhang@amd.com>2010-04-23 05:28:38 -0400
committerJeff Garzik <jgarzik@redhat.com>2010-05-14 17:35:51 -0400
commitc06231661e4fb5f2f50c73ff33702937a11764cf (patch)
treee82300639ad09429fb3a6881bb6985363dd9ddbe /drivers/ata
parent008dbd61ebee3e647f63bbe8315192e1331cd75f (diff)
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. Signed-off-by: Harry Zhang <harry.zhang@amd.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/ahci.h10
-rw-r--r--drivers/ata/libahci.c104
2 files changed, 111 insertions, 3 deletions
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 5edce4447d8..7113c572447 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -224,9 +224,12 @@ enum {
224 EM_MAX_RETRY = 5, 224 EM_MAX_RETRY = 5,
225 225
226 /* em_ctl bits */ 226 /* em_ctl bits */
227 EM_CTL_RST = (1 << 9), /* Reset */ 227 EM_CTL_RST = (1 << 9), /* Reset */
228 EM_CTL_TM = (1 << 8), /* Transmit Message */ 228 EM_CTL_TM = (1 << 8), /* Transmit Message */
229 EM_CTL_ALHD = (1 << 26), /* Activity LED */ 229 EM_CTL_MR = (1 << 0), /* Message Recieved */
230 EM_CTL_ALHD = (1 << 26), /* Activity LED */
231 EM_CTL_XMT = (1 << 25), /* Transmit Only */
232 EM_CTL_SMB = (1 << 24), /* Single Message Buffer */
230 233
231 /* em message type */ 234 /* em message type */
232 EM_MSG_TYPE_LED = (1 << 0), /* LED */ 235 EM_MSG_TYPE_LED = (1 << 0), /* LED */
@@ -288,6 +291,7 @@ struct ahci_host_priv {
288 u32 saved_cap2; /* saved initial cap2 */ 291 u32 saved_cap2; /* saved initial cap2 */
289 u32 saved_port_map; /* saved initial port_map */ 292 u32 saved_port_map; /* saved initial port_map */
290 u32 em_loc; /* enclosure management location */ 293 u32 em_loc; /* enclosure management location */
294 u32 em_buf_sz; /* EM buffer size in byte */
291 u32 em_msg_type; /* EM message type */ 295 u32 em_msg_type; /* EM message type */
292}; 296};
293 297
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 817bcd023cc..1984a6e89e8 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,
108 struct device_attribute *attr, char *buf); 108 struct device_attribute *attr, char *buf);
109static ssize_t ahci_show_port_cmd(struct device *dev, 109static ssize_t ahci_show_port_cmd(struct device *dev,
110 struct device_attribute *attr, char *buf); 110 struct device_attribute *attr, char *buf);
111static ssize_t ahci_read_em_buffer(struct device *dev,
112 struct device_attribute *attr, char *buf);
113static ssize_t ahci_store_em_buffer(struct device *dev,
114 struct device_attribute *attr,
115 const char *buf, size_t size);
111 116
112static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL); 117static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL);
113static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL); 118static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL);
114static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL); 119static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL);
115static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL); 120static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL);
121static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO,
122 ahci_read_em_buffer, ahci_store_em_buffer);
116 123
117static struct device_attribute *ahci_shost_attrs[] = { 124static struct device_attribute *ahci_shost_attrs[] = {
118 &dev_attr_link_power_management_policy, 125 &dev_attr_link_power_management_policy,
@@ -122,6 +129,7 @@ static struct device_attribute *ahci_shost_attrs[] = {
122 &dev_attr_ahci_host_cap2, 129 &dev_attr_ahci_host_cap2,
123 &dev_attr_ahci_host_version, 130 &dev_attr_ahci_host_version,
124 &dev_attr_ahci_port_cmd, 131 &dev_attr_ahci_port_cmd,
132 &dev_attr_em_buffer,
125 NULL 133 NULL
126}; 134};
127 135
@@ -252,6 +260,101 @@ static ssize_t ahci_show_port_cmd(struct device *dev,
252 return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD)); 260 return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD));
253} 261}
254 262
263static ssize_t ahci_read_em_buffer(struct device *dev,
264 struct device_attribute *attr, char *buf)
265{
266 struct Scsi_Host *shost = class_to_shost(dev);
267 struct ata_port *ap = ata_shost_to_port(shost);
268 struct ahci_host_priv *hpriv = ap->host->private_data;
269 void __iomem *mmio = hpriv->mmio;
270 void __iomem *em_mmio = mmio + hpriv->em_loc;
271 u32 em_ctl, msg;
272 unsigned long flags;
273 size_t count;
274 int i;
275
276 spin_lock_irqsave(ap->lock, flags);
277
278 em_ctl = readl(mmio + HOST_EM_CTL);
279 if (!(ap->flags & ATA_FLAG_EM) || em_ctl & EM_CTL_XMT ||
280 !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO)) {
281 spin_unlock_irqrestore(ap->lock, flags);
282 return -EINVAL;
283 }
284
285 if (!(em_ctl & EM_CTL_MR)) {
286 spin_unlock_irqrestore(ap->lock, flags);
287 return -EAGAIN;
288 }
289
290 if (!(em_ctl & EM_CTL_SMB))
291 em_mmio += hpriv->em_buf_sz;
292
293 count = hpriv->em_buf_sz;
294
295 /* the count should not be larger than PAGE_SIZE */
296 if (count > PAGE_SIZE) {
297 if (printk_ratelimit())
298 ata_port_printk(ap, KERN_WARNING,
299 "EM read buffer size too large: "
300 "buffer size %u, page size %lu\n",
301 hpriv->em_buf_sz, PAGE_SIZE);
302 count = PAGE_SIZE;
303 }
304
305 for (i = 0; i < count; i += 4) {
306 msg = readl(em_mmio + i);
307 buf[i] = msg & 0xff;
308 buf[i + 1] = (msg >> 8) & 0xff;
309 buf[i + 2] = (msg >> 16) & 0xff;
310 buf[i + 3] = (msg >> 24) & 0xff;
311 }
312
313 spin_unlock_irqrestore(ap->lock, flags);
314
315 return i;
316}
317
318static ssize_t ahci_store_em_buffer(struct device *dev,
319 struct device_attribute *attr,
320 const char *buf, size_t size)
321{
322 struct Scsi_Host *shost = class_to_shost(dev);
323 struct ata_port *ap = ata_shost_to_port(shost);
324 struct ahci_host_priv *hpriv = ap->host->private_data;
325 void __iomem *mmio = hpriv->mmio;
326 void __iomem *em_mmio = mmio + hpriv->em_loc;
327 u32 em_ctl, msg;
328 unsigned long flags;
329 int i;
330
331 /* check size validity */
332 if (!(ap->flags & ATA_FLAG_EM) ||
333 !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO) ||
334 size % 4 || size > hpriv->em_buf_sz)
335 return -EINVAL;
336
337 spin_lock_irqsave(ap->lock, flags);
338
339 em_ctl = readl(mmio + HOST_EM_CTL);
340 if (em_ctl & EM_CTL_TM) {
341 spin_unlock_irqrestore(ap->lock, flags);
342 return -EBUSY;
343 }
344
345 for (i = 0; i < size; i += 4) {
346 msg = buf[i] | buf[i + 1] << 8 |
347 buf[i + 2] << 16 | buf[i + 3] << 24;
348 writel(msg, em_mmio + i);
349 }
350
351 writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL);
352
353 spin_unlock_irqrestore(ap->lock, flags);
354
355 return size;
356}
357
255/** 358/**
256 * ahci_save_initial_config - Save and fixup initial config values 359 * ahci_save_initial_config - Save and fixup initial config values
257 * @dev: target AHCI device 360 * @dev: target AHCI device
@@ -2099,6 +2202,7 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
2099 if (messages) { 2202 if (messages) {
2100 /* store em_loc */ 2203 /* store em_loc */
2101 hpriv->em_loc = ((em_loc >> 16) * 4); 2204 hpriv->em_loc = ((em_loc >> 16) * 4);
2205 hpriv->em_buf_sz = ((em_loc & 0xff) * 4);
2102 hpriv->em_msg_type = messages; 2206 hpriv->em_msg_type = messages;
2103 pi->flags |= ATA_FLAG_EM; 2207 pi->flags |= ATA_FLAG_EM;
2104 if (!(em_ctl & EM_CTL_ALHD)) 2208 if (!(em_ctl & EM_CTL_ALHD))