diff options
-rw-r--r-- | drivers/ata/libahci.c | 73 |
1 files changed, 42 insertions, 31 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index e5d67eb46f3c..8f216de76648 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1849,18 +1849,47 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) ata_port_abort(ap); } -static void ahci_handle_port_interrupt(struct ata_port *ap, - void __iomem *port_mmio, u32 status) +static void ahci_qc_complete(struct ata_port *ap, void __iomem *port_mmio) { struct ata_eh_info *ehi = &ap->link.eh_info; struct ahci_port_priv *pp = ap->private_data; - struct ahci_host_priv *hpriv = ap->host->private_data; - int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING); u32 qc_active = 0; int rc; + /* + * pp->active_link is not reliable once FBS is enabled, both + * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because + * NCQ and non-NCQ commands may be in flight at the same time. + */ + if (pp->fbs_enabled) { + if (ap->qc_active) { + qc_active = readl(port_mmio + PORT_SCR_ACT); + qc_active |= readl(port_mmio + PORT_CMD_ISSUE); + } + } else { + /* pp->active_link is valid iff any command is in flight */ + if (ap->qc_active && pp->active_link->sactive) + qc_active = readl(port_mmio + PORT_SCR_ACT); + else + qc_active = readl(port_mmio + PORT_CMD_ISSUE); + } + + rc = ata_qc_complete_multiple(ap, qc_active); + if (unlikely(rc < 0 && !(ap->pflags & ATA_PFLAG_RESETTING))) { + ehi->err_mask |= AC_ERR_HSM; + ehi->action |= ATA_EH_RESET; + ata_port_freeze(ap); + } +} + +static void ahci_handle_port_interrupt(struct ata_port *ap, + void __iomem *port_mmio, u32 status) +{ + struct ahci_port_priv *pp = ap->private_data; + struct ahci_host_priv *hpriv = ap->host->private_data; + /* ignore BAD_PMP while resetting */ - if (unlikely(resetting)) + if (unlikely(ap->pflags & ATA_PFLAG_RESETTING)) status &= ~PORT_IRQ_BAD_PMP; if (sata_lpm_ignore_phy_events(&ap->link)) { @@ -1869,6 +1898,12 @@ static void ahci_handle_port_interrupt(struct ata_port *ap, } if (unlikely(status & PORT_IRQ_ERROR)) { + /* + * Before getting the error notification, we may have + * received SDB FISes notifying successful completions. + * Handle these first and then handle the error. + */ + ahci_qc_complete(ap, port_mmio); ahci_error_intr(ap, status); return; } @@ -1905,32 +1940,8 @@ static void ahci_handle_port_interrupt(struct ata_port *ap, } } - /* pp->active_link is not reliable once FBS is enabled, both - * PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because - * NCQ and non-NCQ commands may be in flight at the same time. - */ - if (pp->fbs_enabled) { - if (ap->qc_active) { - qc_active = readl(port_mmio + PORT_SCR_ACT); - qc_active |= readl(port_mmio + PORT_CMD_ISSUE); - } - } else { - /* pp->active_link is valid iff any command is in flight */ - if (ap->qc_active && pp->active_link->sactive) - qc_active = readl(port_mmio + PORT_SCR_ACT); - else - qc_active = readl(port_mmio + PORT_CMD_ISSUE); - } - - - rc = ata_qc_complete_multiple(ap, qc_active); - - /* while resetting, invalid completions are expected */ - if (unlikely(rc < 0 && !resetting)) { - ehi->err_mask |= AC_ERR_HSM; - ehi->action |= ATA_EH_RESET; - ata_port_freeze(ap); - } + /* Handle completed commands */ + ahci_qc_complete(ap, port_mmio); } static void ahci_port_intr(struct ata_port *ap) |