diff options
-rw-r--r-- | drivers/ata/libahci.c | 48 |
1 files changed, 47 insertions, 1 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 8cdf0afd551d..2d21b46db714 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c | |||
@@ -225,6 +225,31 @@ static void ahci_enable_ahci(void __iomem *mmio) | |||
225 | WARN_ON(1); | 225 | WARN_ON(1); |
226 | } | 226 | } |
227 | 227 | ||
228 | /** | ||
229 | * ahci_rpm_get_port - Make sure the port is powered on | ||
230 | * @ap: Port to power on | ||
231 | * | ||
232 | * Whenever there is need to access the AHCI host registers outside of | ||
233 | * normal execution paths, call this function to make sure the host is | ||
234 | * actually powered on. | ||
235 | */ | ||
236 | static int ahci_rpm_get_port(struct ata_port *ap) | ||
237 | { | ||
238 | return pm_runtime_get_sync(ap->dev); | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * ahci_rpm_put_port - Undoes ahci_rpm_get_port() | ||
243 | * @ap: Port to power down | ||
244 | * | ||
245 | * Undoes ahci_rpm_get_port() and possibly powers down the AHCI host | ||
246 | * if it has no more active users. | ||
247 | */ | ||
248 | static void ahci_rpm_put_port(struct ata_port *ap) | ||
249 | { | ||
250 | pm_runtime_put(ap->dev); | ||
251 | } | ||
252 | |||
228 | static ssize_t ahci_show_host_caps(struct device *dev, | 253 | static ssize_t ahci_show_host_caps(struct device *dev, |
229 | struct device_attribute *attr, char *buf) | 254 | struct device_attribute *attr, char *buf) |
230 | { | 255 | { |
@@ -261,8 +286,13 @@ static ssize_t ahci_show_port_cmd(struct device *dev, | |||
261 | struct Scsi_Host *shost = class_to_shost(dev); | 286 | struct Scsi_Host *shost = class_to_shost(dev); |
262 | struct ata_port *ap = ata_shost_to_port(shost); | 287 | struct ata_port *ap = ata_shost_to_port(shost); |
263 | void __iomem *port_mmio = ahci_port_base(ap); | 288 | void __iomem *port_mmio = ahci_port_base(ap); |
289 | ssize_t ret; | ||
264 | 290 | ||
265 | return sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD)); | 291 | ahci_rpm_get_port(ap); |
292 | ret = sprintf(buf, "%x\n", readl(port_mmio + PORT_CMD)); | ||
293 | ahci_rpm_put_port(ap); | ||
294 | |||
295 | return ret; | ||
266 | } | 296 | } |
267 | 297 | ||
268 | static ssize_t ahci_read_em_buffer(struct device *dev, | 298 | static ssize_t ahci_read_em_buffer(struct device *dev, |
@@ -278,17 +308,20 @@ static ssize_t ahci_read_em_buffer(struct device *dev, | |||
278 | size_t count; | 308 | size_t count; |
279 | int i; | 309 | int i; |
280 | 310 | ||
311 | ahci_rpm_get_port(ap); | ||
281 | spin_lock_irqsave(ap->lock, flags); | 312 | spin_lock_irqsave(ap->lock, flags); |
282 | 313 | ||
283 | em_ctl = readl(mmio + HOST_EM_CTL); | 314 | em_ctl = readl(mmio + HOST_EM_CTL); |
284 | if (!(ap->flags & ATA_FLAG_EM) || em_ctl & EM_CTL_XMT || | 315 | if (!(ap->flags & ATA_FLAG_EM) || em_ctl & EM_CTL_XMT || |
285 | !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO)) { | 316 | !(hpriv->em_msg_type & EM_MSG_TYPE_SGPIO)) { |
286 | spin_unlock_irqrestore(ap->lock, flags); | 317 | spin_unlock_irqrestore(ap->lock, flags); |
318 | ahci_rpm_put_port(ap); | ||
287 | return -EINVAL; | 319 | return -EINVAL; |
288 | } | 320 | } |
289 | 321 | ||
290 | if (!(em_ctl & EM_CTL_MR)) { | 322 | if (!(em_ctl & EM_CTL_MR)) { |
291 | spin_unlock_irqrestore(ap->lock, flags); | 323 | spin_unlock_irqrestore(ap->lock, flags); |
324 | ahci_rpm_put_port(ap); | ||
292 | return -EAGAIN; | 325 | return -EAGAIN; |
293 | } | 326 | } |
294 | 327 | ||
@@ -316,6 +349,7 @@ static ssize_t ahci_read_em_buffer(struct device *dev, | |||
316 | } | 349 | } |
317 | 350 | ||
318 | spin_unlock_irqrestore(ap->lock, flags); | 351 | spin_unlock_irqrestore(ap->lock, flags); |
352 | ahci_rpm_put_port(ap); | ||
319 | 353 | ||
320 | return i; | 354 | return i; |
321 | } | 355 | } |
@@ -340,11 +374,13 @@ static ssize_t ahci_store_em_buffer(struct device *dev, | |||
340 | size % 4 || size > hpriv->em_buf_sz) | 374 | size % 4 || size > hpriv->em_buf_sz) |
341 | return -EINVAL; | 375 | return -EINVAL; |
342 | 376 | ||
377 | ahci_rpm_get_port(ap); | ||
343 | spin_lock_irqsave(ap->lock, flags); | 378 | spin_lock_irqsave(ap->lock, flags); |
344 | 379 | ||
345 | em_ctl = readl(mmio + HOST_EM_CTL); | 380 | em_ctl = readl(mmio + HOST_EM_CTL); |
346 | if (em_ctl & EM_CTL_TM) { | 381 | if (em_ctl & EM_CTL_TM) { |
347 | spin_unlock_irqrestore(ap->lock, flags); | 382 | spin_unlock_irqrestore(ap->lock, flags); |
383 | ahci_rpm_put_port(ap); | ||
348 | return -EBUSY; | 384 | return -EBUSY; |
349 | } | 385 | } |
350 | 386 | ||
@@ -357,6 +393,7 @@ static ssize_t ahci_store_em_buffer(struct device *dev, | |||
357 | writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL); | 393 | writel(em_ctl | EM_CTL_TM, mmio + HOST_EM_CTL); |
358 | 394 | ||
359 | spin_unlock_irqrestore(ap->lock, flags); | 395 | spin_unlock_irqrestore(ap->lock, flags); |
396 | ahci_rpm_put_port(ap); | ||
360 | 397 | ||
361 | return size; | 398 | return size; |
362 | } | 399 | } |
@@ -370,7 +407,9 @@ static ssize_t ahci_show_em_supported(struct device *dev, | |||
370 | void __iomem *mmio = hpriv->mmio; | 407 | void __iomem *mmio = hpriv->mmio; |
371 | u32 em_ctl; | 408 | u32 em_ctl; |
372 | 409 | ||
410 | ahci_rpm_get_port(ap); | ||
373 | em_ctl = readl(mmio + HOST_EM_CTL); | 411 | em_ctl = readl(mmio + HOST_EM_CTL); |
412 | ahci_rpm_put_port(ap); | ||
374 | 413 | ||
375 | return sprintf(buf, "%s%s%s%s\n", | 414 | return sprintf(buf, "%s%s%s%s\n", |
376 | em_ctl & EM_CTL_LED ? "led " : "", | 415 | em_ctl & EM_CTL_LED ? "led " : "", |
@@ -1014,6 +1053,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, | |||
1014 | else | 1053 | else |
1015 | return -EINVAL; | 1054 | return -EINVAL; |
1016 | 1055 | ||
1056 | ahci_rpm_get_port(ap); | ||
1017 | spin_lock_irqsave(ap->lock, flags); | 1057 | spin_lock_irqsave(ap->lock, flags); |
1018 | 1058 | ||
1019 | /* | 1059 | /* |
@@ -1023,6 +1063,7 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, | |||
1023 | em_ctl = readl(mmio + HOST_EM_CTL); | 1063 | em_ctl = readl(mmio + HOST_EM_CTL); |
1024 | if (em_ctl & EM_CTL_TM) { | 1064 | if (em_ctl & EM_CTL_TM) { |
1025 | spin_unlock_irqrestore(ap->lock, flags); | 1065 | spin_unlock_irqrestore(ap->lock, flags); |
1066 | ahci_rpm_put_port(ap); | ||
1026 | return -EBUSY; | 1067 | return -EBUSY; |
1027 | } | 1068 | } |
1028 | 1069 | ||
@@ -1050,6 +1091,8 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, | |||
1050 | emp->led_state = state; | 1091 | emp->led_state = state; |
1051 | 1092 | ||
1052 | spin_unlock_irqrestore(ap->lock, flags); | 1093 | spin_unlock_irqrestore(ap->lock, flags); |
1094 | ahci_rpm_put_port(ap); | ||
1095 | |||
1053 | return size; | 1096 | return size; |
1054 | } | 1097 | } |
1055 | 1098 | ||
@@ -2216,6 +2259,8 @@ static void ahci_pmp_detach(struct ata_port *ap) | |||
2216 | 2259 | ||
2217 | int ahci_port_resume(struct ata_port *ap) | 2260 | int ahci_port_resume(struct ata_port *ap) |
2218 | { | 2261 | { |
2262 | ahci_rpm_get_port(ap); | ||
2263 | |||
2219 | ahci_power_up(ap); | 2264 | ahci_power_up(ap); |
2220 | ahci_start_port(ap); | 2265 | ahci_start_port(ap); |
2221 | 2266 | ||
@@ -2242,6 +2287,7 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) | |||
2242 | ata_port_freeze(ap); | 2287 | ata_port_freeze(ap); |
2243 | } | 2288 | } |
2244 | 2289 | ||
2290 | ahci_rpm_put_port(ap); | ||
2245 | return rc; | 2291 | return rc; |
2246 | } | 2292 | } |
2247 | #endif | 2293 | #endif |