aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanesh Petigara <dpetigara@broadcom.com>2016-01-11 16:22:26 -0500
committerTejun Heo <tj@kernel.org>2016-01-25 15:20:44 -0500
commitfb3296335500aaff61333df8eabbccf28761c79d (patch)
tree0dae24ccd8a24d7b20c95503e9793eae3d7d7e4d
parent92e963f50fc74041b5e9e744c330dca48e04f08d (diff)
drivers: ata: wake port before DMA stop for ALPM
The AHCI driver code stops and starts port DMA engines at will without considering the power state of the particular port. The AHCI specification isn't very clear on how to handle this scenario, leaving implementation open to interpretation. Broadcom's STB SATA host controller is unable to handle port DMA controller restarts when the port in question is in low power mode. When a port enters partial or slumber mode, its PHY is powered down. When a controller restart is requested, the controller's internal state machine expects the PHY to be brought back up by software which never happens in this case, resulting in failures. To avoid this situation, logic is added to manually wake up the port just before its DMA engine is stopped, if the port happens to be in a low power state. HBA initiated power management ensures that the port eventually returns to its configured low power state, when the link is idle (as per the conditions listed in the spec). A new host flag is also added to ensure this logic is only exercised for hosts with the above limitation. tj: Formatting changes. Signed-off-by: Danesh Petigara <dpetigara@broadcom.com> Reviewed-by: Markus Mayer <mmayer@broadcom.com> Signed-off-by: Tejun Heo <tj@kernel.org>
-rw-r--r--drivers/ata/ahci.h1
-rw-r--r--drivers/ata/ahci_brcmstb.c1
-rw-r--r--drivers/ata/libahci.c23
-rw-r--r--include/linux/libata.h1
4 files changed, 25 insertions, 1 deletions
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index a4faa438889c..a44c75d4c284 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -250,6 +250,7 @@ enum {
250 AHCI_HFLAG_MULTI_MSI = 0, 250 AHCI_HFLAG_MULTI_MSI = 0,
251 AHCI_HFLAG_MULTI_MSIX = 0, 251 AHCI_HFLAG_MULTI_MSIX = 0,
252#endif 252#endif
253 AHCI_HFLAG_WAKE_BEFORE_STOP = (1 << 22), /* wake before DMA stop */
253 254
254 /* ap->flags bits */ 255 /* ap->flags bits */
255 256
diff --git a/drivers/ata/ahci_brcmstb.c b/drivers/ata/ahci_brcmstb.c
index b36cae2fd04b..e87bcec0fd7c 100644
--- a/drivers/ata/ahci_brcmstb.c
+++ b/drivers/ata/ahci_brcmstb.c
@@ -317,6 +317,7 @@ static int brcm_ahci_probe(struct platform_device *pdev)
317 if (IS_ERR(hpriv)) 317 if (IS_ERR(hpriv))
318 return PTR_ERR(hpriv); 318 return PTR_ERR(hpriv);
319 hpriv->plat_data = priv; 319 hpriv->plat_data = priv;
320 hpriv->flags = AHCI_HFLAG_WAKE_BEFORE_STOP;
320 321
321 brcm_sata_alpm_init(hpriv); 322 brcm_sata_alpm_init(hpriv);
322 323
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index d61740e78d6d..284a176076f5 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -593,8 +593,22 @@ EXPORT_SYMBOL_GPL(ahci_start_engine);
593int ahci_stop_engine(struct ata_port *ap) 593int ahci_stop_engine(struct ata_port *ap)
594{ 594{
595 void __iomem *port_mmio = ahci_port_base(ap); 595 void __iomem *port_mmio = ahci_port_base(ap);
596 struct ahci_host_priv *hpriv = ap->host->private_data;
596 u32 tmp; 597 u32 tmp;
597 598
599 /*
600 * On some controllers, stopping a port's DMA engine while the port
601 * is in ALPM state (partial or slumber) results in failures on
602 * subsequent DMA engine starts. For those controllers, put the
603 * port back in active state before stopping its DMA engine.
604 */
605 if ((hpriv->flags & AHCI_HFLAG_WAKE_BEFORE_STOP) &&
606 (ap->link.lpm_policy > ATA_LPM_MAX_POWER) &&
607 ahci_set_lpm(&ap->link, ATA_LPM_MAX_POWER, ATA_LPM_WAKE_ONLY)) {
608 dev_err(ap->host->dev, "Failed to wake up port before engine stop\n");
609 return -EIO;
610 }
611
598 tmp = readl(port_mmio + PORT_CMD); 612 tmp = readl(port_mmio + PORT_CMD);
599 613
600 /* check if the HBA is idle */ 614 /* check if the HBA is idle */
@@ -689,6 +703,9 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
689 void __iomem *port_mmio = ahci_port_base(ap); 703 void __iomem *port_mmio = ahci_port_base(ap);
690 704
691 if (policy != ATA_LPM_MAX_POWER) { 705 if (policy != ATA_LPM_MAX_POWER) {
706 /* wakeup flag only applies to the max power policy */
707 hints &= ~ATA_LPM_WAKE_ONLY;
708
692 /* 709 /*
693 * Disable interrupts on Phy Ready. This keeps us from 710 * Disable interrupts on Phy Ready. This keeps us from
694 * getting woken up due to spurious phy ready 711 * getting woken up due to spurious phy ready
@@ -704,7 +721,8 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
704 u32 cmd = readl(port_mmio + PORT_CMD); 721 u32 cmd = readl(port_mmio + PORT_CMD);
705 722
706 if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) { 723 if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) {
707 cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); 724 if (!(hints & ATA_LPM_WAKE_ONLY))
725 cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE);
708 cmd |= PORT_CMD_ICC_ACTIVE; 726 cmd |= PORT_CMD_ICC_ACTIVE;
709 727
710 writel(cmd, port_mmio + PORT_CMD); 728 writel(cmd, port_mmio + PORT_CMD);
@@ -712,6 +730,9 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
712 730
713 /* wait 10ms to be sure we've come out of LPM state */ 731 /* wait 10ms to be sure we've come out of LPM state */
714 ata_msleep(ap, 10); 732 ata_msleep(ap, 10);
733
734 if (hints & ATA_LPM_WAKE_ONLY)
735 return 0;
715 } else { 736 } else {
716 cmd |= PORT_CMD_ALPE; 737 cmd |= PORT_CMD_ALPE;
717 if (policy == ATA_LPM_MIN_POWER) 738 if (policy == ATA_LPM_MIN_POWER)
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 851821bfd553..bec2abbd7ab2 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -526,6 +526,7 @@ enum ata_lpm_policy {
526enum ata_lpm_hints { 526enum ata_lpm_hints {
527 ATA_LPM_EMPTY = (1 << 0), /* port empty/probing */ 527 ATA_LPM_EMPTY = (1 << 0), /* port empty/probing */
528 ATA_LPM_HIPM = (1 << 1), /* may use HIPM */ 528 ATA_LPM_HIPM = (1 << 1), /* may use HIPM */
529 ATA_LPM_WAKE_ONLY = (1 << 2), /* only wake up link */
529}; 530};
530 531
531/* forward declarations */ 532/* forward declarations */