Skip to content

Commit

Permalink
sata_mv: tidy host controller interrupt handling
Browse files Browse the repository at this point in the history
Tidy up host controller interrupt handling, by moving the weirdo
bit shifting from mv_interrupt() to mv_host_intr().

This lets us take advantage of the MV_PORT_TO_SHIFT_AND_HARDPORT() macro
from an earlier patch to greatly simplify the port numbering logic.

Also, defer reading the hc_irq_cause (one per hc) until it is
actually proven to be needed.  This may save a microsecond or
so per interrupt, on average (a later patchset will further reduce
unnecessary register reads throughout the driver).

Apart from that, we still leave the actual IRQ handling logic alone.
Subsequent patches in this series will address that.

Signed-off-by: Mark Lord <mlord@pobox.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
  • Loading branch information
Mark Lord authored and Jeff Garzik committed Apr 25, 2008
1 parent fcfb1f7 commit a3718c1
Showing 1 changed file with 45 additions and 62 deletions.
107 changes: 45 additions & 62 deletions drivers/ata/sata_mv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1693,50 +1693,48 @@ static void mv_process_crpb_entries(struct ata_port *ap, struct mv_port_priv *pp
* LOCKING:
* Inherited from caller.
*/
static void mv_host_intr(struct ata_host *host, u32 relevant, unsigned int hc)
static int mv_host_intr(struct ata_host *host, u32 main_cause)
{
struct mv_host_priv *hpriv = host->private_data;
void __iomem *mmio = hpriv->base;
void __iomem *hc_mmio = mv_hc_base(mmio, hc);
u32 hc_irq_cause;
int port, port0, last_port;

if (hc == 0)
port0 = 0;
else
port0 = MV_PORTS_PER_HC;

if (HAS_PCI(host))
last_port = port0 + MV_PORTS_PER_HC;
else
last_port = port0 + hpriv->n_ports;
/* we'll need the HC success int register in most cases */
hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS);
if (!hc_irq_cause)
return;

writelfl(~hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS);
void __iomem *mmio = hpriv->base, *hc_mmio = NULL;
u32 hc_irq_cause = 0;
unsigned int handled = 0, port;

VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n",
hc, relevant, hc_irq_cause);

for (port = port0; port < last_port; port++) {
for (port = 0; port < hpriv->n_ports; port++) {
struct ata_port *ap = host->ports[port];
struct mv_port_priv *pp;
int have_err_bits, hardport, shift;

if ((!ap) || (ap->flags & ATA_FLAG_DISABLED))
unsigned int shift, hardport, port_cause;
/*
* When we move to the second hc, flag our cached
* copies of hc_mmio (and hc_irq_cause) as invalid again.
*/
if (port == MV_PORTS_PER_HC)
hc_mmio = NULL;
/*
* Do nothing if port is not interrupting or is disabled:
*/
MV_PORT_TO_SHIFT_AND_HARDPORT(port, shift, hardport);
port_cause = (main_cause >> shift) & (DONE_IRQ | ERR_IRQ);
if (!port_cause || !ap || (ap->flags & ATA_FLAG_DISABLED))
continue;
/*
* Each hc within the host has its own hc_irq_cause register.
* We defer reading it until we know we need it, right now:
*
* FIXME later: we don't really need to read this register
* (some logic changes required below if we go that way),
* because it doesn't tell us anything new. But we do need
* to write to it, outside the top of this loop,
* to reset the interrupt triggers for next time.
*/
if (!hc_mmio) {
hc_mmio = mv_hc_base_from_port(mmio, port);
hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS);
writelfl(~hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS);
handled = 1;
}

pp = ap->private_data;

shift = port << 1; /* (port * 2) */
if (port >= MV_PORTS_PER_HC)
shift++; /* skip bit 8 in the HC Main IRQ reg */

have_err_bits = ((ERR_IRQ << shift) & relevant);

if (unlikely(have_err_bits)) {
if (unlikely(port_cause & ERR_IRQ)) {
struct ata_queued_cmd *qc;

qc = ata_qc_from_tag(ap, ap->link.active_tag);
Expand All @@ -1747,8 +1745,7 @@ static void mv_host_intr(struct ata_host *host, u32 relevant, unsigned int hc)
continue;
}

hardport = mv_hardport_from_port(port); /* range 0..3 */

pp = ap->private_data;
if (pp->pp_flags & MV_PP_FLAG_EDMA_EN) {
if ((DMA_IRQ << hardport) & hc_irq_cause)
mv_process_crpb_entries(ap, pp);
Expand All @@ -1757,10 +1754,10 @@ static void mv_host_intr(struct ata_host *host, u32 relevant, unsigned int hc)
mv_intr_pio(ap);
}
}
VPRINTK("EXIT\n");
return handled;
}

static void mv_pci_error(struct ata_host *host, void __iomem *mmio)
static int mv_pci_error(struct ata_host *host, void __iomem *mmio)
{
struct mv_host_priv *hpriv = host->private_data;
struct ata_port *ap;
Expand Down Expand Up @@ -1798,6 +1795,7 @@ static void mv_pci_error(struct ata_host *host, void __iomem *mmio)
ata_port_freeze(ap);
}
}
return 1; /* handled */
}

/**
Expand All @@ -1818,8 +1816,7 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance)
{
struct ata_host *host = dev_instance;
struct mv_host_priv *hpriv = host->private_data;
unsigned int hc, handled = 0, n_hcs;
void __iomem *mmio = hpriv->base;
unsigned int handled = 0;
u32 main_cause, main_mask;

spin_lock(&host->lock);
Expand All @@ -1829,26 +1826,12 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance)
* Deal with cases where we either have nothing pending, or have read
* a bogus register value which can indicate HW removal or PCI fault.
*/
if (!(main_cause & main_mask) || (main_cause == 0xffffffffU))
goto out_unlock;

n_hcs = mv_get_hc_count(host->ports[0]->flags);

if (unlikely((main_cause & PCI_ERR) && HAS_PCI(host))) {
mv_pci_error(host, mmio);
handled = 1;
goto out_unlock; /* skip all other HC irq handling */
}

for (hc = 0; hc < n_hcs; hc++) {
u32 relevant = main_cause & (HC0_IRQ_PEND << (hc * HC_SHIFT));
if (relevant) {
mv_host_intr(host, relevant, hc);
handled = 1;
}
if ((main_cause & main_mask) && (main_cause != 0xffffffffU)) {
if (unlikely((main_cause & PCI_ERR) && HAS_PCI(host)))
handled = mv_pci_error(host, hpriv->base);
else
handled = mv_host_intr(host, main_cause);
}

out_unlock:
spin_unlock(&host->lock);
return IRQ_RETVAL(handled);
}
Expand Down

0 comments on commit a3718c1

Please sign in to comment.