aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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))