diff options
Diffstat (limited to 'drivers/ata/libahci.c')
-rw-r--r-- | drivers/ata/libahci.c | 171 |
1 files changed, 132 insertions, 39 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 29acc35bf4a6..8f216de76648 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -55,7 +55,8 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state, static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); -static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc); +static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc); +static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); static enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc); @@ -157,6 +158,7 @@ struct ata_port_operations ahci_ops = { .qc_prep = ahci_qc_prep, .qc_issue = ahci_qc_issue, .qc_fill_rtf = ahci_qc_fill_rtf, + .qc_ncq_fill_rtf = ahci_qc_ncq_fill_rtf, .freeze = ahci_freeze, .thaw = ahci_thaw, @@ -1847,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)) { @@ -1867,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; } @@ -1903,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) @@ -2053,11 +2066,18 @@ unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) } EXPORT_SYMBOL_GPL(ahci_qc_issue); -static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) +static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc) { struct ahci_port_priv *pp = qc->ap->private_data; u8 *rx_fis = pp->rx_fis; + /* + * rtf may already be filled (e.g. for successful NCQ commands). + * If that is the case, we have nothing to do. + */ + if (qc->flags & ATA_QCFLAG_RTF_FILLED) + return; + if (pp->fbs_enabled) rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ; @@ -2068,9 +2088,12 @@ static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) * Setup FIS. */ if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE && - !(qc->flags & ATA_QCFLAG_FAILED)) { + !(qc->flags & ATA_QCFLAG_EH)) { ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf); qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15]; + qc->flags |= ATA_QCFLAG_RTF_FILLED; + return; + } /* * For NCQ commands, we never get a D2H FIS, so reading the D2H Register @@ -2080,15 +2103,85 @@ static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc) * instead. However, the SDB FIS does not contain the LBA, so we can't * use the ata_tf_from_fis() helper. */ - } else if (ata_is_ncq(qc->tf.protocol)) { + if (ata_is_ncq(qc->tf.protocol)) { const u8 *fis = rx_fis + RX_FIS_SDB; + /* + * Successful NCQ commands have been filled already. + * A failed NCQ command will read the status here. + * (Note that a failed NCQ command will get a more specific + * error when reading the NCQ Command Error log.) + */ qc->result_tf.status = fis[2]; qc->result_tf.error = fis[3]; - } else - ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf); + qc->flags |= ATA_QCFLAG_RTF_FILLED; + return; + } + + ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf); + qc->flags |= ATA_QCFLAG_RTF_FILLED; +} + +static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask) +{ + struct ahci_port_priv *pp = ap->private_data; + const u8 *fis; + + /* No outstanding commands. */ + if (!ap->qc_active) + return; + + /* + * FBS not enabled, so read status and error once, since they are shared + * for all QCs. + */ + if (!pp->fbs_enabled) { + u8 status, error; + + /* No outstanding NCQ commands. */ + if (!pp->active_link->sactive) + return; + + fis = pp->rx_fis + RX_FIS_SDB; + status = fis[2]; + error = fis[3]; - return true; + while (done_mask) { + struct ata_queued_cmd *qc; + unsigned int tag = __ffs64(done_mask); + + qc = ata_qc_from_tag(ap, tag); + if (qc && ata_is_ncq(qc->tf.protocol)) { + qc->result_tf.status = status; + qc->result_tf.error = error; + qc->flags |= ATA_QCFLAG_RTF_FILLED; + } + done_mask &= ~(1ULL << tag); + } + + return; + } + + /* + * FBS enabled, so read the status and error for each QC, since the QCs + * can belong to different PMP links. (Each PMP link has its own FIS + * Receive Area.) + */ + while (done_mask) { + struct ata_queued_cmd *qc; + unsigned int tag = __ffs64(done_mask); + + qc = ata_qc_from_tag(ap, tag); + if (qc && ata_is_ncq(qc->tf.protocol)) { + fis = pp->rx_fis; + fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ; + fis += RX_FIS_SDB; + qc->result_tf.status = fis[2]; + qc->result_tf.error = fis[3]; + qc->flags |= ATA_QCFLAG_RTF_FILLED; + } + done_mask &= ~(1ULL << tag); + } } static void ahci_freeze(struct ata_port *ap) @@ -2138,7 +2231,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; /* make DMA engine forget about the failed command */ - if (qc->flags & ATA_QCFLAG_FAILED) + if (qc->flags & ATA_QCFLAG_EH) ahci_kick_engine(ap); } |