in libahci.c [1702:1811]
static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
{
struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
struct ata_eh_info *host_ehi = &ap->link.eh_info;
struct ata_link *link = NULL;
struct ata_queued_cmd *active_qc;
struct ata_eh_info *active_ehi;
bool fbs_need_dec = false;
u32 serror;
/* determine active link with error */
if (pp->fbs_enabled) {
void __iomem *port_mmio = ahci_port_base(ap);
u32 fbs = readl(port_mmio + PORT_FBS);
int pmp = fbs >> PORT_FBS_DWE_OFFSET;
if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links)) {
link = &ap->pmp_link[pmp];
fbs_need_dec = true;
}
} else
ata_for_each_link(link, ap, EDGE)
if (ata_link_active(link))
break;
if (!link)
link = &ap->link;
active_qc = ata_qc_from_tag(ap, link->active_tag);
active_ehi = &link->eh_info;
/* record irq stat */
ata_ehi_clear_desc(host_ehi);
ata_ehi_push_desc(host_ehi, "irq_stat 0x%08x", irq_stat);
/* AHCI needs SError cleared; otherwise, it might lock up */
ahci_scr_read(&ap->link, SCR_ERROR, &serror);
ahci_scr_write(&ap->link, SCR_ERROR, serror);
host_ehi->serror |= serror;
/* some controllers set IRQ_IF_ERR on device errors, ignore it */
if (hpriv->flags & AHCI_HFLAG_IGN_IRQ_IF_ERR)
irq_stat &= ~PORT_IRQ_IF_ERR;
if (irq_stat & PORT_IRQ_TF_ERR) {
/* If qc is active, charge it; otherwise, the active
* link. There's no active qc on NCQ errors. It will
* be determined by EH by reading log page 10h.
*/
if (active_qc)
active_qc->err_mask |= AC_ERR_DEV;
else
active_ehi->err_mask |= AC_ERR_DEV;
if (hpriv->flags & AHCI_HFLAG_IGN_SERR_INTERNAL)
host_ehi->serror &= ~SERR_INTERNAL;
}
if (irq_stat & PORT_IRQ_UNK_FIS) {
u32 *unk = pp->rx_fis + RX_FIS_UNK;
active_ehi->err_mask |= AC_ERR_HSM;
active_ehi->action |= ATA_EH_RESET;
ata_ehi_push_desc(active_ehi,
"unknown FIS %08x %08x %08x %08x" ,
unk[0], unk[1], unk[2], unk[3]);
}
if (sata_pmp_attached(ap) && (irq_stat & PORT_IRQ_BAD_PMP)) {
active_ehi->err_mask |= AC_ERR_HSM;
active_ehi->action |= ATA_EH_RESET;
ata_ehi_push_desc(active_ehi, "incorrect PMP");
}
if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) {
host_ehi->err_mask |= AC_ERR_HOST_BUS;
host_ehi->action |= ATA_EH_RESET;
ata_ehi_push_desc(host_ehi, "host bus error");
}
if (irq_stat & PORT_IRQ_IF_ERR) {
if (fbs_need_dec)
active_ehi->err_mask |= AC_ERR_DEV;
else {
host_ehi->err_mask |= AC_ERR_ATA_BUS;
host_ehi->action |= ATA_EH_RESET;
}
ata_ehi_push_desc(host_ehi, "interface fatal error");
}
if (irq_stat & (PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY)) {
ata_ehi_hotplugged(host_ehi);
ata_ehi_push_desc(host_ehi, "%s",
irq_stat & PORT_IRQ_CONNECT ?
"connection status changed" : "PHY RDY changed");
}
/* okay, let's hand over to EH */
if (irq_stat & PORT_IRQ_FREEZE)
ata_port_freeze(ap);
else if (fbs_need_dec) {
ata_link_abort(link);
ahci_fbs_dec_intr(ap);
} else
ata_port_abort(ap);
}