diff options
Diffstat (limited to 'drivers/ata/ahci.c')
-rw-r--r-- | drivers/ata/ahci.c | 54 |
1 files changed, 51 insertions, 3 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index d4cd9c203314..0872d321264d 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -45,6 +45,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> #include <linux/libata.h> +#include <linux/blk-iopoll.h> #define DRV_NAME "ahci" #define DRV_VERSION "3.0" @@ -2114,7 +2115,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) ata_port_abort(ap); } -static void ahci_port_intr(struct ata_port *ap) +static int ahci_port_intr(struct ata_port *ap) { void __iomem *port_mmio = ahci_port_base(ap); struct ata_eh_info *ehi = &ap->link.eh_info; @@ -2144,7 +2145,7 @@ static void ahci_port_intr(struct ata_port *ap) if (unlikely(status & PORT_IRQ_ERROR)) { ahci_error_intr(ap, status); - return; + return 0; } if (status & PORT_IRQ_SDB_FIS) { @@ -2185,7 +2186,43 @@ static void ahci_port_intr(struct ata_port *ap) ehi->err_mask |= AC_ERR_HSM; ehi->action |= ATA_EH_RESET; ata_port_freeze(ap); + rc = 0; + } + + return rc; +} + +static void ap_irq_disable(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + + writel(0, port_mmio + PORT_IRQ_MASK); +} + +static void ap_irq_enable(struct ata_port *ap) +{ + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_port_priv *pp = ap->private_data; + + writel(pp->intr_mask, port_mmio + PORT_IRQ_MASK); +} + +static int ahci_iopoll(struct blk_iopoll *iop, int budget) +{ + struct ata_port *ap = container_of(iop, struct ata_port, iopoll); + unsigned long flags; + int ret; + + spin_lock_irqsave(&ap->host->lock, flags); + ret = ahci_port_intr(ap); + spin_unlock_irqrestore(&ap->host->lock, flags); + + if (ret < budget) { + blk_iopoll_complete(iop); + ap_irq_enable(ap); } + + return ret; } static irqreturn_t ahci_interrupt(int irq, void *dev_instance) @@ -2218,7 +2255,12 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) ap = host->ports[i]; if (ap) { - ahci_port_intr(ap); + if (!blk_iopoll_enabled) + ahci_port_intr(ap); + else if (!blk_iopoll_sched_prep(&ap->iopoll)) { + ap_irq_disable(ap); + blk_iopoll_sched(&ap->iopoll); + } VPRINTK("port %u\n", i); } else { VPRINTK("port %u (no irq)\n", i); @@ -2352,6 +2394,8 @@ static void ahci_pmp_detach(struct ata_port *ap) static int ahci_port_resume(struct ata_port *ap) { + blk_iopoll_enable(&ap->iopoll); + ahci_power_up(ap); ahci_start_port(ap); @@ -2482,6 +2526,8 @@ static int ahci_port_start(struct ata_port *ap) ap->private_data = pp; + blk_iopoll_init(&ap->iopoll, 32, ahci_iopoll); + /* engage engines, captain */ return ahci_port_resume(ap); } @@ -2495,6 +2541,8 @@ static void ahci_port_stop(struct ata_port *ap) rc = ahci_deinit_port(ap, &emsg); if (rc) ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc); + + blk_iopoll_disable(&ap->iopoll); } static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) |