in sata_nv.c [876:1010]
static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
int i, handled = 0;
u32 notifier_clears[2];
spin_lock(&host->lock);
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap = host->ports[i];
struct nv_adma_port_priv *pp = ap->private_data;
void __iomem *mmio = pp->ctl_block;
u16 status;
u32 gen_ctl;
u32 notifier, notifier_error;
notifier_clears[i] = 0;
/* if ADMA is disabled, use standard ata interrupt handler */
if (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) {
u8 irq_stat = readb(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804)
>> (NV_INT_PORT_SHIFT * i);
handled += nv_host_intr(ap, irq_stat);
continue;
}
/* if in ATA register mode, check for standard interrupts */
if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) {
u8 irq_stat = readb(host->iomap[NV_MMIO_BAR] + NV_INT_STATUS_CK804)
>> (NV_INT_PORT_SHIFT * i);
if (ata_tag_valid(ap->link.active_tag))
/** NV_INT_DEV indication seems unreliable
at times at least in ADMA mode. Force it
on always when a command is active, to
prevent losing interrupts. */
irq_stat |= NV_INT_DEV;
handled += nv_host_intr(ap, irq_stat);
}
notifier = readl(mmio + NV_ADMA_NOTIFIER);
notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR);
notifier_clears[i] = notifier | notifier_error;
gen_ctl = readl(pp->gen_block + NV_ADMA_GEN_CTL);
if (!NV_ADMA_CHECK_INTR(gen_ctl, ap->port_no) && !notifier &&
!notifier_error)
/* Nothing to do */
continue;
status = readw(mmio + NV_ADMA_STAT);
/*
* Clear status. Ensure the controller sees the
* clearing before we start looking at any of the CPB
* statuses, so that any CPB completions after this
* point in the handler will raise another interrupt.
*/
writew(status, mmio + NV_ADMA_STAT);
readw(mmio + NV_ADMA_STAT); /* flush posted write */
rmb();
handled++; /* irq handled if we got here */
/* freeze if hotplugged or controller error */
if (unlikely(status & (NV_ADMA_STAT_HOTPLUG |
NV_ADMA_STAT_HOTUNPLUG |
NV_ADMA_STAT_TIMEOUT |
NV_ADMA_STAT_SERROR))) {
struct ata_eh_info *ehi = &ap->link.eh_info;
ata_ehi_clear_desc(ehi);
__ata_ehi_push_desc(ehi, "ADMA status 0x%08x: ", status);
if (status & NV_ADMA_STAT_TIMEOUT) {
ehi->err_mask |= AC_ERR_SYSTEM;
ata_ehi_push_desc(ehi, "timeout");
} else if (status & NV_ADMA_STAT_HOTPLUG) {
ata_ehi_hotplugged(ehi);
ata_ehi_push_desc(ehi, "hotplug");
} else if (status & NV_ADMA_STAT_HOTUNPLUG) {
ata_ehi_hotplugged(ehi);
ata_ehi_push_desc(ehi, "hot unplug");
} else if (status & NV_ADMA_STAT_SERROR) {
/* let EH analyze SError and figure out cause */
ata_ehi_push_desc(ehi, "SError");
} else
ata_ehi_push_desc(ehi, "unknown");
ata_port_freeze(ap);
continue;
}
if (status & (NV_ADMA_STAT_DONE |
NV_ADMA_STAT_CPBERR |
NV_ADMA_STAT_CMD_COMPLETE)) {
u32 check_commands = notifier_clears[i];
u32 done_mask = 0;
int pos, rc;
if (status & NV_ADMA_STAT_CPBERR) {
/* check all active commands */
if (ata_tag_valid(ap->link.active_tag))
check_commands = 1 <<
ap->link.active_tag;
else
check_commands = ap->link.sactive;
}
/* check CPBs for completed commands */
while ((pos = ffs(check_commands))) {
pos--;
rc = nv_adma_check_cpb(ap, pos,
notifier_error & (1 << pos));
if (rc > 0)
done_mask |= 1 << pos;
else if (unlikely(rc < 0))
check_commands = 0;
check_commands &= ~(1 << pos);
}
ata_qc_complete_multiple(ap, ata_qc_get_active(ap) ^ done_mask);
}
}
if (notifier_clears[0] || notifier_clears[1]) {
/* Note: Both notifier clear registers must be written
if either is set, even if one is zero, according to NVIDIA. */
struct nv_adma_port_priv *pp = host->ports[0]->private_data;
writel(notifier_clears[0], pp->notifier_clear_block);
pp = host->ports[1]->private_data;
writel(notifier_clears[1], pp->notifier_clear_block);
}
spin_unlock(&host->lock);
return IRQ_RETVAL(handled);
}