aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata
diff options
context:
space:
mode:
authorPang Raymond <Raymond_rule@hotmail.com>2016-07-20 08:13:46 -0400
committerTejun Heo <tj@kernel.org>2016-07-20 11:10:10 -0400
commit0516900adfa1955f91f1c310f9a2fa857949de1c (patch)
treeef78ae7de1456cd75e89be78a0b0d678d4697632 /drivers/ata
parent01c292068e43e72091c22542df5f01b3c09714b2 (diff)
AHCI: Clear GHC.IS to prevent unexpectly asserting INTx
Due to PCI subsystem behaviour, unloading AHCI driver will disable MSI and enable INTx. When HBA supports MSIx or Multiple MSI, Driver's irq handler doesn't clear GHC.IS register. It works well when reading or writing data and GHC.IS is always non-zero. But when unloading driver (or any other operation which causes disable MSIx and enable INTx), PCI subsystem uses config write(Rx04.bit10) to enable INTx. Because GHC.IS is non-zero, HBA will falsely assume some port needs interrupt service. Then it asserts INTx. To make things worse, when AHCI controller shares the same interrupt pin with other PCI device, that PCI device's ISR will be called and nobody de-asserts previous INTx. This patch clears GHC.IS in ahci_port_stop() even when using MSIx or MMSI to prevent this case. It ensures GHC.IS is zero before PCI subsystem enables INTx. tj: Minor updates to the comment. Signed-off-by: Raymond Pang <raymond_rule@hotmail.com> Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/libahci.c8
1 files changed, 8 insertions, 0 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 3e69c20e9d03..7461a587b39b 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -2392,12 +2392,20 @@ static int ahci_port_start(struct ata_port *ap)
2392static void ahci_port_stop(struct ata_port *ap) 2392static void ahci_port_stop(struct ata_port *ap)
2393{ 2393{
2394 const char *emsg = NULL; 2394 const char *emsg = NULL;
2395 struct ahci_host_priv *hpriv = ap->host->private_data;
2396 void __iomem *host_mmio = hpriv->mmio;
2395 int rc; 2397 int rc;
2396 2398
2397 /* de-initialize port */ 2399 /* de-initialize port */
2398 rc = ahci_deinit_port(ap, &emsg); 2400 rc = ahci_deinit_port(ap, &emsg);
2399 if (rc) 2401 if (rc)
2400 ata_port_warn(ap, "%s (%d)\n", emsg, rc); 2402 ata_port_warn(ap, "%s (%d)\n", emsg, rc);
2403
2404 /*
2405 * Clear GHC.IS to prevent stuck INTx after disabling MSI and
2406 * re-enabling INTx.
2407 */
2408 writel(1 << ap->port_no, host_mmio + HOST_IRQ_STAT);
2401} 2409}
2402 2410
2403void ahci_print_info(struct ata_host *host, const char *scc_s) 2411void ahci_print_info(struct ata_host *host, const char *scc_s)