lkml.org 
[lkml]   [2005]   [Dec]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH] sata_sil: improved interrupt handling

    Just committed the following to the 'sii-irq' branch of libata-dev.git,
    and verified it on an Adaptec 1210SA (3112).

    Haven't decided whether I will push it upstream or not, but I think I
    will. It does a bit better job of handling handling errors, and should
    be more efficient (less CPU usage) than the standard ATA interrupt
    handler as well.

    For users seeing sata_sil problems, this may make them happy.


    commit b6abf7755a79383f0e5f108d23a0394f156c54c1
    Author: Jeff Garzik <jgarzik@pobox.com>
    Date: Sat Dec 3 00:30:57 2005 -0500

    [libata sata_sil] improved interrupt handling

    drivers/scsi/sata_sil.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++-
    1 files changed, 117 insertions(+), 1 deletion(-)

    diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
    index 3609186..37398a5 100644
    --- a/drivers/scsi/sata_sil.c
    +++ b/drivers/scsi/sata_sil.c
    @@ -85,6 +85,7 @@ static void sil_dev_config(struct ata_po
    static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg);
    static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
    static void sil_post_set_mode (struct ata_port *ap);
    +static irqreturn_t sil_irq (int irq, void *dev_instance, struct pt_regs *regs);


    static const struct pci_device_id sil_pci_tbl[] = {
    @@ -167,7 +168,7 @@ static const struct ata_port_operations
    .qc_prep = ata_qc_prep,
    .qc_issue = ata_qc_issue_prot,
    .eng_timeout = ata_eng_timeout,
    - .irq_handler = ata_interrupt,
    + .irq_handler = sil_irq,
    .irq_clear = ata_bmdma_irq_clear,
    .scr_read = sil_scr_read,
    .scr_write = sil_scr_write,
    @@ -233,6 +234,121 @@ MODULE_DEVICE_TABLE(pci, sil_pci_tbl);
    MODULE_VERSION(DRV_VERSION);


    +static inline void sil_port_irq(struct ata_port *ap, void __iomem *mmio,
    + u8 dma_stat, u8 dma_stat_mask)
    +{
    + struct ata_queued_cmd *qc = NULL;
    + unsigned int err_mask = AC_ERR_OTHER;
    + int complete = 1;
    + u8 dev_stat;
    +
    + /* Exit now, if port or port's irqs are disabled */
    + if (ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR)) {
    + complete = 0;
    + goto out;
    + }
    +
    + /* Get active command */
    + qc = ata_qc_from_tag(ap, ap->active_tag);
    + if ((!qc) || (qc->tf.ctl & ATA_NIEN)) {
    + complete = 0;
    + goto out;
    + }
    +
    + /* Stop DMA, if doing DMA */
    + switch (qc->tf.protocol) {
    + case ATA_PROT_DMA:
    + case ATA_PROT_ATAPI_DMA:
    + ata_bmdma_stop(qc);
    + break;
    +
    + default:
    + /* do nothing */
    + break;
    + }
    +
    + /* Catch PCI bus errors */
    + if (unlikely(dma_stat_mask & ATA_DMA_ERR)) {
    + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev);
    + u16 pci_stat;
    +
    + pci_read_config_word(pdev, PCI_STATUS, &pci_stat);
    + pci_write_config_word(pdev, PCI_STATUS, pci_stat);
    +
    + err_mask = AC_ERR_HOST_BUS;
    +
    + printk(KERN_ERR "ata%u: PCI error, pci %x, dma %x\n",
    + ap->id, pci_stat, dma_stat);
    + goto out;
    + }
    +
    + /* Read device Status, clear device interrupt */
    + dev_stat = ata_check_status(ap);
    +
    + /* Let timeout handler handle stuck BSY */
    + if (unlikely(dev_stat & ATA_BUSY)) {
    + complete = 0;
    + goto out;
    + }
    +
    + /* Did S/G table specify a size smaller than the transfer size? */
    + if (unlikely(dma_stat_mask == 0)) {
    + printk(KERN_ERR "ata%u: BUG: SG size underflow\n", ap->id);
    + err_mask = AC_ERR_OTHER; /* only occurs due to coder error? */
    + goto out;
    + }
    +
    + /* Clear 311x DMA completion indicator */
    + writeb(ATA_DMA_INTR, mmio + ATA_DMA_STATUS);
    +
    + /* Finally, complete the ATA command transaction */
    + ata_qc_complete(qc, ac_err_mask(dev_stat));
    + return;
    +
    +out:
    + ata_chk_status(ap);
    + writeb(dma_stat_mask, mmio + ATA_DMA_STATUS);
    + if (complete)
    + ata_qc_complete(qc, err_mask);
    +}
    +
    +static irqreturn_t sil_irq (int irq, void *dev_instance, struct pt_regs *regs)
    +{
    + struct ata_host_set *host_set = dev_instance;
    + unsigned int i, handled = 0;
    +
    + spin_lock(&host_set->lock);
    +
    + for (i = 0; i < host_set->n_ports; i++) {
    + struct ata_port *ap;
    + void __iomem *mmio;
    + u8 status, mask;
    + u32 serr;
    +
    + ap = host_set->ports[i];
    + if (!ap)
    + continue;
    +
    + mmio = (void __iomem *) ap->ioaddr.bmdma_addr;
    + status = readb(mmio + ATA_DMA_STATUS);
    + mask = status & (ATA_DMA_INTR | ATA_DMA_ERR | ATA_DMA_ACTIVE);
    + if (mask == ATA_DMA_ACTIVE)
    + continue;
    +
    + handled = 1;
    +
    + sil_port_irq(ap, mmio, status, mask);
    +
    + serr = sil_scr_read(ap, SCR_ERROR);
    + if (serr)
    + sil_scr_write(ap, SCR_ERROR, serr);
    + }
    +
    + spin_unlock(&host_set->lock);
    +
    + return IRQ_RETVAL(handled);
    +}
    +
    static unsigned char sil_get_device_cache_line(struct pci_dev *pdev)
    {
    u8 cache_line = 0;
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2005-12-03 06:43    [W:0.028 / U:59.620 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site