--- linux-2.6.19-rc5-mm2/drivers/ata/sata_nv.c 2006-11-16 18:21:01.000000000 -0600 +++ linux-2.6.19-rc5-mm2-fix/drivers/ata/sata_nv.c 2006-11-18 17:29:34.000000000 -0600 @@ -243,6 +243,7 @@ static void nv_adma_bmdma_setup(struct a static void nv_adma_bmdma_start(struct ata_queued_cmd *qc); static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc); static u8 nv_adma_bmdma_status(struct ata_port *ap); +static int nv_host_intr(struct ata_port *ap, u8 irq_stat); enum nv_host_type { @@ -638,11 +639,13 @@ static irqreturn_t nv_adma_interrupt(int { 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]; + notifier_clears[i] = 0; if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { struct nv_adma_port_priv *pp = ap->private_data; @@ -654,30 +657,20 @@ static irqreturn_t nv_adma_interrupt(int /* if in ATA register mode, use standard ata interrupt handler */ if (pp->flags & NV_ADMA_PORT_REGISTER_MODE) { - struct ata_queued_cmd *qc; - VPRINTK("in ATA register mode\n"); - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) - handled += ata_host_intr(ap, qc); - else { - /* No request pending? Clear interrupt status - anyway, in case there's one pending. */ - ap->ops->check_status(ap); - handled++; - } + u8 irq_stat; + irq_stat = readb(host->mmio_base + NV_INT_STATUS_CK804) + >> (NV_INT_PORT_SHIFT * i); + ata_port_printk(ap,KERN_INFO,"in ATA register mode,irq_stat 0x%X\n",irq_stat); + handled += nv_host_intr(ap, irq_stat); continue; } notifier = readl(mmio + NV_ADMA_NOTIFIER); notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR); + notifier_clears[i] = notifier | notifier_error; gen_ctl = readl(nv_adma_gen_block(ap) + NV_ADMA_GEN_CTL); - /* Seems necessary to clear notifiers even when they were 0. - Otherwise we seem to stop receiving further interrupts. - Unsure why. */ - writel(notifier | notifier_error, nv_adma_notifier_clear_block(ap)); - if( !NV_ADMA_CHECK_INTR(gen_ctl, ap->port_no) && !notifier && !notifier_error) /* Nothing to do */ @@ -730,6 +723,15 @@ static irqreturn_t nv_adma_interrupt(int handled++; /* irq handled if we got here */ } } + + if( handled && (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. */ + writel(notifier_clears[0], + nv_adma_notifier_clear_block(host->ports[0])); + writel(notifier_clears[1], + nv_adma_notifier_clear_block(host->ports[1])); + } spin_unlock(&host->lock); @@ -742,6 +744,8 @@ static void nv_adma_irq_clear(struct ata u16 status = readw(mmio + NV_ADMA_STAT); u32 notifier = readl(mmio + NV_ADMA_NOTIFIER); u32 notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR); + unsigned long dma_stat_addr = ap->ioaddr.bmdma_addr + ATA_DMA_STATUS; + u8 dma_stat; /* clear ADMA status */ writew(status, mmio + NV_ADMA_STAT); @@ -749,62 +753,83 @@ static void nv_adma_irq_clear(struct ata nv_adma_notifier_clear_block(ap)); /** clear legacy status */ - ap->flags &= ~ATA_FLAG_MMIO; - ata_bmdma_irq_clear(ap); - ap->flags |= ATA_FLAG_MMIO; + dma_stat = inb(dma_stat_addr); + ata_port_printk(ap,KERN_INFO,"clearing legacy status 0x%X\n",dma_stat); + outb(dma_stat, dma_stat_addr); } static void nv_adma_bmdma_setup(struct ata_queued_cmd *qc) { - struct nv_adma_port_priv *pp = qc->ap->private_data; + struct ata_port *ap = qc->ap; + unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); + struct nv_adma_port_priv *pp = ap->private_data; + u8 dmactl; - if(pp->flags & NV_ADMA_PORT_REGISTER_MODE) { + if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) { WARN_ON(1); return; } - qc->ap->flags &= ~ATA_FLAG_MMIO; - ata_bmdma_setup(qc); - qc->ap->flags |= ATA_FLAG_MMIO; + /* load PRD table addr. */ + outl(ap->prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS); + + /* specify data direction, triple-check start bit is clear */ + dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); + if (!rw) + dmactl |= ATA_DMA_WR; + ata_port_printk(ap,KERN_INFO,"setup writing 0x%X to dma cmd\n",dmactl); + outb(dmactl, ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + + /* issue r/w command */ + ata_exec_command(ap, &qc->tf); } static void nv_adma_bmdma_start(struct ata_queued_cmd *qc) { - struct nv_adma_port_priv *pp = qc->ap->private_data; + struct ata_port *ap = qc->ap; + struct nv_adma_port_priv *pp = ap->private_data; + u8 dmactl; - if(pp->flags & NV_ADMA_PORT_REGISTER_MODE) { + if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) { WARN_ON(1); return; } - qc->ap->flags &= ~ATA_FLAG_MMIO; - ata_bmdma_start(qc); - qc->ap->flags |= ATA_FLAG_MMIO; + /* start host DMA transaction */ + dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + ata_port_printk(ap,KERN_INFO,"start writing 0x%X to dma cmd\n",dmactl | ATA_DMA_START); + outb(dmactl | ATA_DMA_START, + ap->ioaddr.bmdma_addr + ATA_DMA_CMD); } static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc) { - struct nv_adma_port_priv *pp = qc->ap->private_data; + struct ata_port *ap = qc->ap; + struct nv_adma_port_priv *pp = ap->private_data; - if(pp->flags & NV_ADMA_PORT_REGISTER_MODE) + if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) return; - qc->ap->flags &= ~ATA_FLAG_MMIO; - ata_bmdma_stop(qc); - qc->ap->flags |= ATA_FLAG_MMIO; + /* clear start/stop bit */ + ata_port_printk(ap,KERN_INFO,"clearing start from cmd\n"); + outb(inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD) & ~ATA_DMA_START, + ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + + /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ + ata_altstatus(ap); /* dummy read */ } static u8 nv_adma_bmdma_status(struct ata_port *ap) { - u8 status; struct nv_adma_port_priv *pp = ap->private_data; + u8 status; - WARN_ON(pp->flags & NV_ADMA_PORT_REGISTER_MODE); + WARN_ON(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)); - ap->flags &= ~ATA_FLAG_MMIO; - status = ata_bmdma_status(ap); - ap->flags |= ATA_FLAG_MMIO; - return status; + status = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + ata_port_printk(ap,KERN_INFO,"read status 0x%X\n",status); + return inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); } static void nv_adma_register_mode(struct ata_port *ap) @@ -1046,6 +1071,7 @@ static void nv_adma_qc_prep(struct ata_q if (!(qc->flags & ATA_QCFLAG_DMAMAP) || qc->tf.protocol == ATA_PROT_ATAPI_DMA) { + nv_adma_register_mode(qc->ap); ata_qc_prep(qc); return; }