From fb3296335500aaff61333df8eabbccf28761c79d Mon Sep 17 00:00:00 2001 From: Danesh Petigara Date: Mon, 11 Jan 2016 13:22:26 -0800 Subject: 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 Reviewed-by: Markus Mayer Signed-off-by: Tejun Heo --- drivers/ata/libahci.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers/ata/libahci.c') 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); int ahci_stop_engine(struct ata_port *ap) { void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_host_priv *hpriv = ap->host->private_data; u32 tmp; + /* + * On some controllers, stopping a port's DMA engine while the port + * is in ALPM state (partial or slumber) results in failures on + * subsequent DMA engine starts. For those controllers, put the + * port back in active state before stopping its DMA engine. + */ + if ((hpriv->flags & AHCI_HFLAG_WAKE_BEFORE_STOP) && + (ap->link.lpm_policy > ATA_LPM_MAX_POWER) && + ahci_set_lpm(&ap->link, ATA_LPM_MAX_POWER, ATA_LPM_WAKE_ONLY)) { + dev_err(ap->host->dev, "Failed to wake up port before engine stop\n"); + return -EIO; + } + tmp = readl(port_mmio + PORT_CMD); /* check if the HBA is idle */ @@ -689,6 +703,9 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, void __iomem *port_mmio = ahci_port_base(ap); if (policy != ATA_LPM_MAX_POWER) { + /* wakeup flag only applies to the max power policy */ + hints &= ~ATA_LPM_WAKE_ONLY; + /* * Disable interrupts on Phy Ready. This keeps us from * 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, u32 cmd = readl(port_mmio + PORT_CMD); if (policy == ATA_LPM_MAX_POWER || !(hints & ATA_LPM_HIPM)) { - cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); + if (!(hints & ATA_LPM_WAKE_ONLY)) + cmd &= ~(PORT_CMD_ASP | PORT_CMD_ALPE); cmd |= PORT_CMD_ICC_ACTIVE; writel(cmd, port_mmio + PORT_CMD); @@ -712,6 +730,9 @@ static int ahci_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, /* wait 10ms to be sure we've come out of LPM state */ ata_msleep(ap, 10); + + if (hints & ATA_LPM_WAKE_ONLY) + return 0; } else { cmd |= PORT_CMD_ALPE; if (policy == ATA_LPM_MIN_POWER) -- cgit v1.2.3 From 566d1827df2ef0cbe921d3d6946ac3007b1a6938 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 15 Jan 2016 15:13:05 -0500 Subject: libata: disable forced PORTS_IMPL for >= AHCI 1.3 Some early controllers incorrectly reported zero ports in PORTS_IMPL register and the ahci driver fabricates PORTS_IMPL from the number of ports in those cases. This hasn't mattered but with the new nvme controllers there are cases where zero PORTS_IMPL is valid and should be honored. Disable the workaround for >= AHCI 1.3. Signed-off-by: Tejun Heo Reported-by: Andy Lutomirski Link: http://lkml.kernel.org/g/CALCETrU7yMvXEDhjAUShoHEhDwifJGapdw--BKxsP0jmjKGmRw@mail.gmail.com Cc: Sergei Shtylyov Cc: stable@vger.kernel.org --- drivers/ata/libahci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/ata/libahci.c') diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 284a176076f5..402967902cbe 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -496,8 +496,8 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) } } - /* fabricate port_map from cap.nr_ports */ - if (!port_map) { + /* fabricate port_map from cap.nr_ports for < AHCI 1.3 */ + if (!port_map && vers < 0x10300) { port_map = (1 << ahci_nr_ports(cap)) - 1; dev_warn(dev, "forcing PORTS_IMPL to 0x%x\n", port_map); -- cgit v1.2.3 From f070d6715509dafc0af223577c896fe3d204ca88 Mon Sep 17 00:00:00 2001 From: Suman Tripathi Date: Sat, 6 Feb 2016 11:25:22 +0530 Subject: libahci: Implement the capability to override the generic ahci interrupt handler. This patch implements the capability to override the generic AHCI interrupt handler so that specific ahci drivers can implement their own custom interrupt handler routines. It also exports ahci_handle_port_intr so that custom irq_handler implementations can use it. tj: s/ahci_irq_handler/irq_handler/ and updated description. Signed-off-by: Suman Tripathi Signed-off-by: Tejun Heo --- drivers/ata/ahci.h | 2 ++ drivers/ata/libahci.c | 27 ++++++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) (limited to 'drivers/ata/libahci.c') diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index a44c75d4c284..cf48e3ed234a 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -361,6 +361,7 @@ struct ahci_host_priv { * be overridden anytime before the host is activated. */ void (*start_engine)(struct ata_port *ap); + irqreturn_t (*irq_handler)(int irq, void *dev_instance); }; #ifdef CONFIG_PCI_MSI @@ -424,6 +425,7 @@ int ahci_reset_em(struct ata_host *host); void ahci_print_info(struct ata_host *host, const char *scc_s); int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht); void ahci_error_handler(struct ata_port *ap); +u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked); static inline void __iomem *__ahci_port_base(struct ata_host *host, unsigned int port_no) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 402967902cbe..cbfe8a2de4c5 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -113,6 +113,9 @@ static ssize_t ahci_store_em_buffer(struct device *dev, const char *buf, size_t size); static ssize_t ahci_show_em_supported(struct device *dev, struct device_attribute *attr, char *buf); +static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance); + +static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance); static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL); static DEVICE_ATTR(ahci_host_cap2, S_IRUGO, ahci_show_host_cap2, NULL); @@ -512,6 +515,11 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) if (!hpriv->start_engine) hpriv->start_engine = ahci_start_engine; + + if (!hpriv->irq_handler) + hpriv->irq_handler = (hpriv->flags & AHCI_HFLAG_EDGE_IRQ) ? + ahci_single_edge_irq_intr : + ahci_single_level_irq_intr; } EXPORT_SYMBOL_GPL(ahci_save_initial_config); @@ -1846,7 +1854,7 @@ static irqreturn_t ahci_multi_irqs_intr_hard(int irq, void *dev_instance) return IRQ_HANDLED; } -static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked) +u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked) { unsigned int i, handled = 0; @@ -1872,6 +1880,7 @@ static u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked) return handled; } +EXPORT_SYMBOL_GPL(ahci_handle_port_intr); static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance) { @@ -2535,14 +2544,18 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht) int irq = hpriv->irq; int rc; - if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX)) + if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX)) { + if (hpriv->irq_handler) + dev_warn(host->dev, "both AHCI_HFLAG_MULTI_MSI flag set \ + and custom irq handler implemented\n"); + rc = ahci_host_activate_multi_irqs(host, sht); - else if (hpriv->flags & AHCI_HFLAG_EDGE_IRQ) - rc = ata_host_activate(host, irq, ahci_single_edge_irq_intr, - IRQF_SHARED, sht); - else - rc = ata_host_activate(host, irq, ahci_single_level_irq_intr, + } else { + rc = ata_host_activate(host, irq, hpriv->irq_handler, IRQF_SHARED, sht); + } + + return rc; } EXPORT_SYMBOL_GPL(ahci_host_activate); -- cgit v1.2.3 From d867b95f965457b9e85fb061ef8e3fdc029116ed Mon Sep 17 00:00:00 2001 From: Suman Tripathi Date: Sat, 6 Feb 2016 11:25:23 +0530 Subject: ata: Remove the AHCI_HFLAG_EDGE_IRQ support from libahci. The flexibility to override the irq handles in the LLD's are already present, so controllers implementing a edge trigger latch can implement their own interrupt handler inside the driver. This patch removes the AHCI_HFLAG_EDGE_IRQ support from libahci and moves edge irq handling to ahci_xgene. tj: Minor update to description. Signed-off-by: Suman Tripathi Signed-off-by: Tejun Heo --- drivers/ata/ahci.h | 3 +-- drivers/ata/ahci_xgene.c | 40 +++++++++++++++++++++++++++++++++++++++- drivers/ata/libahci.c | 43 +------------------------------------------ 3 files changed, 41 insertions(+), 45 deletions(-) (limited to 'drivers/ata/libahci.c') diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index cf48e3ed234a..167ba7e3b92e 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -240,8 +240,7 @@ enum { error-handling stage) */ AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */ AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */ - AHCI_HFLAG_EDGE_IRQ = (1 << 19), /* HOST_IRQ_STAT behaves as - Edge Triggered */ + #ifdef CONFIG_PCI_MSI AHCI_HFLAG_MULTI_MSI = (1 << 20), /* multiple PCI MSIs */ AHCI_HFLAG_MULTI_MSIX = (1 << 21), /* per-port MSI-X */ diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index e2c6d9e0c5ac..8b8ccb6434e2 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -548,6 +548,43 @@ softreset_retry: return rc; } +static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance) +{ + struct ata_host *host = dev_instance; + struct ahci_host_priv *hpriv; + unsigned int rc = 0; + void __iomem *mmio; + u32 irq_stat, irq_masked; + + VPRINTK("ENTER\n"); + + hpriv = host->private_data; + mmio = hpriv->mmio; + + /* sigh. 0xffffffff is a valid return from h/w */ + irq_stat = readl(mmio + HOST_IRQ_STAT); + if (!irq_stat) + return IRQ_NONE; + + irq_masked = irq_stat & hpriv->port_map; + + spin_lock(&host->lock); + + /* + * HOST_IRQ_STAT behaves as edge triggered latch meaning that + * it should be cleared before all the port events are cleared. + */ + writel(irq_stat, mmio + HOST_IRQ_STAT); + + rc = ahci_handle_port_intr(host, irq_masked); + + spin_unlock(&host->lock); + + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(rc); +} + static struct ata_port_operations xgene_ahci_v1_ops = { .inherits = &ahci_ops, .host_stop = xgene_ahci_host_stop, @@ -779,7 +816,8 @@ skip_clk_phy: hpriv->flags = AHCI_HFLAG_NO_NCQ; break; case XGENE_AHCI_V2: - hpriv->flags |= AHCI_HFLAG_YES_FBS | AHCI_HFLAG_EDGE_IRQ; + hpriv->flags |= AHCI_HFLAG_YES_FBS; + hpriv->irq_handler = xgene_ahci_irq_intr; break; default: break; diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index cbfe8a2de4c5..513b3fa74d78 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -113,8 +113,6 @@ static ssize_t ahci_store_em_buffer(struct device *dev, const char *buf, size_t size); static ssize_t ahci_show_em_supported(struct device *dev, struct device_attribute *attr, char *buf); -static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance); - static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance); static DEVICE_ATTR(ahci_host_caps, S_IRUGO, ahci_show_host_caps, NULL); @@ -517,9 +515,7 @@ void ahci_save_initial_config(struct device *dev, struct ahci_host_priv *hpriv) hpriv->start_engine = ahci_start_engine; if (!hpriv->irq_handler) - hpriv->irq_handler = (hpriv->flags & AHCI_HFLAG_EDGE_IRQ) ? - ahci_single_edge_irq_intr : - ahci_single_level_irq_intr; + hpriv->irq_handler = ahci_single_level_irq_intr; } EXPORT_SYMBOL_GPL(ahci_save_initial_config); @@ -1882,43 +1878,6 @@ u32 ahci_handle_port_intr(struct ata_host *host, u32 irq_masked) } EXPORT_SYMBOL_GPL(ahci_handle_port_intr); -static irqreturn_t ahci_single_edge_irq_intr(int irq, void *dev_instance) -{ - struct ata_host *host = dev_instance; - struct ahci_host_priv *hpriv; - unsigned int rc = 0; - void __iomem *mmio; - u32 irq_stat, irq_masked; - - VPRINTK("ENTER\n"); - - hpriv = host->private_data; - mmio = hpriv->mmio; - - /* sigh. 0xffffffff is a valid return from h/w */ - irq_stat = readl(mmio + HOST_IRQ_STAT); - if (!irq_stat) - return IRQ_NONE; - - irq_masked = irq_stat & hpriv->port_map; - - spin_lock(&host->lock); - - /* - * HOST_IRQ_STAT behaves as edge triggered latch meaning that - * it should be cleared before all the port events are cleared. - */ - writel(irq_stat, mmio + HOST_IRQ_STAT); - - rc = ahci_handle_port_intr(host, irq_masked); - - spin_unlock(&host->lock); - - VPRINTK("EXIT\n"); - - return IRQ_RETVAL(rc); -} - static irqreturn_t ahci_single_level_irq_intr(int irq, void *dev_instance) { struct ata_host *host = dev_instance; -- cgit v1.2.3 From dc8b4afc4a04fac8ee55a19b59f2356a25e7e778 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Sat, 27 Feb 2016 16:10:05 +0100 Subject: ata: ahci: don't mark HotPlugCapable Ports as external/removable The HPCP bit is set by bioses for on-board sata ports either because they think sata is hotplug capable in general or to allow Windows to display a "device eject" icon on ports which are routed to an external connector bracket. However in Redhat Bugzilla #1310682, users report that with kernel 4.4, where this bit test first appeared, a lot of partitions on sata drives are now mounted automatically. This patch should fix redhat and a lot of other distros which unconditionally automount all devices which have the "removable" bit set. Signed-off-by: Manuel Lauss Signed-off-by: Tejun Heo Fixes: 8a3e33cf92c7 ("ata: ahci: find eSATA ports and flag them as removable" changes userspace behavior) Link: http://lkml.kernel.org/g/56CF35FA.1070500@redhat.com Cc: stable@vger.kernel.org #v4.4+ --- drivers/ata/libahci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/ata/libahci.c') diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 513b3fa74d78..85ea5142a095 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1168,8 +1168,7 @@ static void ahci_port_init(struct device *dev, struct ata_port *ap, /* mark esata ports */ tmp = readl(port_mmio + PORT_CMD); - if ((tmp & PORT_CMD_HPCP) || - ((tmp & PORT_CMD_ESP) && (hpriv->cap & HOST_CAP_SXS))) + if ((tmp & PORT_CMD_ESP) && (hpriv->cap & HOST_CAP_SXS)) ap->pflags |= ATA_PFLAG_EXTERNAL; } -- cgit v1.2.3