diff options
-rw-r--r-- | drivers/ata/ahci.h | 10 | ||||
-rw-r--r-- | drivers/ata/libahci.c | 104 |
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); |
109 | static ssize_t ahci_show_port_cmd(struct device *dev, | 109 | static ssize_t ahci_show_port_cmd(struct device *dev, |
110 | struct device_attribute *attr, char *buf); | 110 | struct device_attribute *attr, char *buf); |
111 | static ssize_t ahci_read_em_buffer(struct device *dev, | ||
112 | struct device_attribute *attr, char *buf); | ||
113 | static ssize_t ahci_store_em_buffer(struct device *dev, | ||
114 | struct device_attribute *attr, | ||
115 | const char *buf, size_t size); | ||
111 | 116 | ||
112 | static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL); | 117 | static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL); |
113 | static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL); | 118 | static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL); |
114 | static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL); | 119 | static DEVICE_ATTR(ahci_host_version, S_IRUGO, ahci_show_host_version, NULL); |
115 | static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL); | 120 | static DEVICE_ATTR(ahci_port_cmd, S_IRUGO, ahci_show_port_cmd, NULL); |
121 | static DEVICE_ATTR(em_buffer, S_IWUSR | S_IRUGO, | ||
122 | ahci_read_em_buffer, ahci_store_em_buffer); | ||
116 | 123 | ||
117 | static struct device_attribute *ahci_shost_attrs[] = { | 124 | static 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 | ||
263 | static 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 | |||
318 | static 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)) |