📄 sata_nv.c
字号:
{ void __iomem *mmio = nv_adma_ctl_block(ap); 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; /* clear ADMA status */ writew(status, mmio + NV_ADMA_STAT); writel(notifier | notifier_error, nv_adma_notifier_clear_block(ap)); /** clear legacy status */ outb(inb(dma_stat_addr), dma_stat_addr);}static void nv_adma_bmdma_setup(struct ata_queued_cmd *qc){ 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)) { WARN_ON(1); return; } /* 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; 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 ata_port *ap = qc->ap; struct nv_adma_port_priv *pp = ap->private_data; u8 dmactl; if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) { WARN_ON(1); return; } /* start host DMA transaction */ dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); outb(dmactl | ATA_DMA_START, ap->ioaddr.bmdma_addr + ATA_DMA_CMD);}static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; struct nv_adma_port_priv *pp = ap->private_data; if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) return; /* clear start/stop bit */ 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){ struct nv_adma_port_priv *pp = ap->private_data; WARN_ON(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)); return inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS);}static int nv_adma_port_start(struct ata_port *ap){ struct device *dev = ap->host->dev; struct nv_adma_port_priv *pp; int rc; void *mem; dma_addr_t mem_dma; void __iomem *mmio = nv_adma_ctl_block(ap); u16 tmp; VPRINTK("ENTER\n"); rc = ata_port_start(ap); if (rc) return rc; pp = kzalloc(sizeof(*pp), GFP_KERNEL); if (!pp) { rc = -ENOMEM; goto err_out; } mem = dma_alloc_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ, &mem_dma, GFP_KERNEL); if (!mem) { rc = -ENOMEM; goto err_out_kfree; } memset(mem, 0, NV_ADMA_PORT_PRIV_DMA_SZ); /* * First item in chunk of DMA memory: * 128-byte command parameter block (CPB) * one for each command tag */ pp->cpb = mem; pp->cpb_dma = mem_dma; writel(mem_dma & 0xFFFFFFFF, mmio + NV_ADMA_CPB_BASE_LOW); writel((mem_dma >> 16 ) >> 16, mmio + NV_ADMA_CPB_BASE_HIGH); mem += NV_ADMA_MAX_CPBS * NV_ADMA_CPB_SZ; mem_dma += NV_ADMA_MAX_CPBS * NV_ADMA_CPB_SZ; /* * Second item: block of ADMA_SGTBL_LEN s/g entries */ pp->aprd = mem; pp->aprd_dma = mem_dma; ap->private_data = pp; /* clear any outstanding interrupt conditions */ writew(0xffff, mmio + NV_ADMA_STAT); /* initialize port variables */ pp->flags = NV_ADMA_PORT_REGISTER_MODE; /* clear CPB fetch count */ writew(0, mmio + NV_ADMA_CPB_COUNT); /* clear GO for register mode */ tmp = readw(mmio + NV_ADMA_CTL); writew(tmp & ~NV_ADMA_CTL_GO, mmio + NV_ADMA_CTL); tmp = readw(mmio + NV_ADMA_CTL); writew(tmp | NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); readl( mmio + NV_ADMA_CTL ); /* flush posted write */ udelay(1); writew(tmp & ~NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); readl( mmio + NV_ADMA_CTL ); /* flush posted write */ return 0;err_out_kfree: kfree(pp);err_out: ata_port_stop(ap); return rc;}static void nv_adma_port_stop(struct ata_port *ap){ struct device *dev = ap->host->dev; struct nv_adma_port_priv *pp = ap->private_data; void __iomem *mmio = nv_adma_ctl_block(ap); VPRINTK("ENTER\n"); writew(0, mmio + NV_ADMA_CTL); ap->private_data = NULL; dma_free_coherent(dev, NV_ADMA_PORT_PRIV_DMA_SZ, pp->cpb, pp->cpb_dma); kfree(pp); ata_port_stop(ap);}static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int port){ void __iomem *mmio = probe_ent->mmio_base; struct ata_ioports *ioport = &probe_ent->port[port]; VPRINTK("ENTER\n"); mmio += NV_ADMA_PORT + port * NV_ADMA_PORT_SIZE; ioport->cmd_addr = (unsigned long) mmio; ioport->data_addr = (unsigned long) mmio + (ATA_REG_DATA * 4); ioport->error_addr = ioport->feature_addr = (unsigned long) mmio + (ATA_REG_ERR * 4); ioport->nsect_addr = (unsigned long) mmio + (ATA_REG_NSECT * 4); ioport->lbal_addr = (unsigned long) mmio + (ATA_REG_LBAL * 4); ioport->lbam_addr = (unsigned long) mmio + (ATA_REG_LBAM * 4); ioport->lbah_addr = (unsigned long) mmio + (ATA_REG_LBAH * 4); ioport->device_addr = (unsigned long) mmio + (ATA_REG_DEVICE * 4); ioport->status_addr = ioport->command_addr = (unsigned long) mmio + (ATA_REG_STATUS * 4); ioport->altstatus_addr = ioport->ctl_addr = (unsigned long) mmio + 0x20;}static int nv_adma_host_init(struct ata_probe_ent *probe_ent){ struct pci_dev *pdev = to_pci_dev(probe_ent->dev); unsigned int i; u32 tmp32; VPRINTK("ENTER\n"); /* enable ADMA on the ports */ pci_read_config_dword(pdev, NV_MCP_SATA_CFG_20, &tmp32); tmp32 |= NV_MCP_SATA_CFG_20_PORT0_EN | NV_MCP_SATA_CFG_20_PORT0_PWB_EN | NV_MCP_SATA_CFG_20_PORT1_EN | NV_MCP_SATA_CFG_20_PORT1_PWB_EN; pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, tmp32); for (i = 0; i < probe_ent->n_ports; i++) nv_adma_setup_port(probe_ent, i); for (i = 0; i < probe_ent->n_ports; i++) { void __iomem *mmio = __nv_adma_ctl_block(probe_ent->mmio_base, i); u16 tmp; /* enable interrupt, clear reset if not already clear */ tmp = readw(mmio + NV_ADMA_CTL); writew(tmp | NV_ADMA_CTL_AIEN, mmio + NV_ADMA_CTL); } return 0;}static void nv_adma_fill_aprd(struct ata_queued_cmd *qc, struct scatterlist *sg, int idx, struct nv_adma_prd *aprd){ u8 flags; memset(aprd, 0, sizeof(struct nv_adma_prd)); flags = 0; if (qc->tf.flags & ATA_TFLAG_WRITE) flags |= NV_APRD_WRITE; if (idx == qc->n_elem - 1) flags |= NV_APRD_END; else if (idx != 4) flags |= NV_APRD_CONT; aprd->addr = cpu_to_le64(((u64)sg_dma_address(sg))); aprd->len = cpu_to_le32(((u32)sg_dma_len(sg))); /* len in bytes */ aprd->flags = flags;}static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb){ struct nv_adma_port_priv *pp = qc->ap->private_data; unsigned int idx; struct nv_adma_prd *aprd; struct scatterlist *sg; VPRINTK("ENTER\n"); idx = 0; ata_for_each_sg(sg, qc) { aprd = (idx < 5) ? &cpb->aprd[idx] : &pp->aprd[NV_ADMA_SGTBL_LEN * qc->tag + (idx-5)]; nv_adma_fill_aprd(qc, sg, idx, aprd); idx++; } if (idx > 5) cpb->next_aprd = cpu_to_le64(((u64)(pp->aprd_dma + NV_ADMA_SGTBL_SZ * qc->tag)));}static void nv_adma_qc_prep(struct ata_queued_cmd *qc){ struct nv_adma_port_priv *pp = qc->ap->private_data; struct nv_adma_cpb *cpb = &pp->cpb[qc->tag]; u8 ctl_flags = NV_CPB_CTL_CPB_VALID | NV_CPB_CTL_APRD_VALID | NV_CPB_CTL_IEN; VPRINTK("qc->flags = 0x%lx\n", qc->flags); if (!(qc->flags & ATA_QCFLAG_DMAMAP) || (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE)) { nv_adma_register_mode(qc->ap); ata_qc_prep(qc); return; } memset(cpb, 0, sizeof(struct nv_adma_cpb)); cpb->len = 3; cpb->tag = qc->tag; cpb->next_cpb_idx = 0; /* turn on NCQ flags for NCQ commands */ if (qc->tf.protocol == ATA_PROT_NCQ) ctl_flags |= NV_CPB_CTL_QUEUE | NV_CPB_CTL_FPDMA; nv_adma_tf_to_cpb(&qc->tf, cpb->tf); nv_adma_fill_sg(qc, cpb); /* Be paranoid and don't let the device see NV_CPB_CTL_CPB_VALID until we are finished filling in all of the contents */ wmb(); cpb->ctl_flags = ctl_flags;}static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc){ struct nv_adma_port_priv *pp = qc->ap->private_data; void __iomem *mmio = nv_adma_ctl_block(qc->ap); VPRINTK("ENTER\n"); if (!(qc->flags & ATA_QCFLAG_DMAMAP) || (pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE)) { /* use ATA register mode */ VPRINTK("no dmamap or ATAPI, using ATA register mode: 0x%lx\n", qc->flags); nv_adma_register_mode(qc->ap); return ata_qc_issue_prot(qc); } else nv_adma_mode(qc->ap); /* write append register, command tag in lower 8 bits and (number of cpbs to append -1) in top 8 bits */ wmb(); writew(qc->tag, mmio + NV_ADMA_APPEND); DPRINTK("Issued tag %u\n",qc->tag); return 0;}static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance, struct pt_regs *pt_regs){ struct ata_host *host = dev_instance; unsigned int i; unsigned int handled = 0; unsigned long flags; spin_lock_irqsave(&host->lock, flags); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap; ap = host->ports[i]; if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { struct ata_queued_cmd *qc; 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); } } spin_unlock_irqrestore(&host->lock, flags); return IRQ_RETVAL(handled);}static irqreturn_t nv_do_interrupt(struct ata_host *host, u8 irq_stat){ int i, handled = 0; for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; if (ap && !(ap->flags & ATA_FLAG_DISABLED)) handled += nv_host_intr(ap, irq_stat); irq_stat >>= NV_INT_PORT_SHIFT; } return IRQ_RETVAL(handled);}static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance, struct pt_regs *pt_regs){ struct ata_host *host = dev_instance; u8 irq_stat; irqreturn_t ret; spin_lock(&host->lock); irq_stat = inb(host->ports[0]->ioaddr.scr_addr + NV_INT_STATUS); ret = nv_do_interrupt(host, irq_stat); spin_unlock(&host->lock); return ret;}static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance, struct pt_regs *pt_regs){ struct ata_host *host = dev_instance; u8 irq_stat; irqreturn_t ret; spin_lock(&host->lock); irq_stat = readb(host->mmio_base + NV_INT_STATUS_CK804); ret = nv_do_interrupt(host, irq_stat); spin_unlock(&host->lock); return ret;}static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg){ if (sc_reg > SCR_CONTROL) return 0xffffffffU; return ioread32((void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4));}static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val){ if (sc_reg > SCR_CONTROL) return; iowrite32(val, (void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4));}static void nv_nf2_freeze(struct ata_port *ap){ unsigned long scr_addr = ap->host->ports[0]->ioaddr.scr_addr; int shift = ap->port_no * NV_INT_PORT_SHIFT; u8 mask; mask = inb(scr_addr + NV_INT_ENABLE); mask &= ~(NV_INT_ALL << shift); outb(mask, scr_addr + NV_INT_ENABLE);}static void nv_nf2_thaw(struct ata_port *ap){ unsigned long scr_addr = ap->host->ports[0]->ioaddr.scr_addr; int shift = ap->port_no * NV_INT_PORT_SHIFT; u8 mask; outb(NV_INT_ALL << shift, scr_addr + NV_INT_STATUS); mask = inb(scr_addr + NV_INT_ENABLE); mask |= (NV_INT_MASK << shift); outb(mask, scr_addr + NV_INT_ENABLE);}static void nv_ck804_freeze(struct ata_port *ap){ void __iomem *mmio_base = ap->host->mmio_base; int shift = ap->port_no * NV_INT_PORT_SHIFT; u8 mask; mask = readb(mmio_base + NV_INT_ENABLE_CK804); mask &= ~(NV_INT_ALL << shift); writeb(mask, mmio_base + NV_INT_ENABLE_CK804);}static void nv_ck804_thaw(struct ata_port *ap){ void __iomem *mmio_base = ap->host->mmio_base; int shift = ap->port_no * NV_INT_PORT_SHIFT; u8 mask; writeb(NV_INT_ALL << shift, mmio_base + NV_INT_STATUS_CK804); mask = readb(mmio_base + NV_INT_ENABLE_CK804); mask |= (NV_INT_MASK << shift); writeb(mask, mmio_base + NV_INT_ENABLE_CK804);}static int nv_hardreset(struct ata_port *ap, unsigned int *class){ unsigned int dummy; /* SATA hardreset fails to retrieve proper device signature on * some controllers. Don't classify on hardreset. For more * info, see http://bugme.osdl.org/show_bug.cgi?id=3352 */ return sata_std_hardreset(ap, &dummy);}static void nv_error_handler(struct ata_port *ap){ ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, nv_hardreset, ata_std_postreset);}static void nv_adma_error_handler(struct ata_port *ap){ struct nv_adma_port_priv *pp = ap->private_data; if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) { void __iomem *mmio = nv_adma_ctl_block(ap); int i; u16 tmp; u32 notifier = readl(mmio + NV_ADMA_NOTIFIER); u32 notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR); u32 gen_ctl = readl(nv_adma_gen_block(ap) + NV_ADMA_GEN_CTL); u32 status = readw(mmio + NV_ADMA_STAT); ata_port_printk(ap, KERN_ERR, "EH in ADMA mode, notifier 0x%X " "notifier_error 0x%X gen_ctl 0x%X status 0x%X\n", notifier, notifier_error, gen_ctl, status); for( i=0;i<NV_ADMA_MAX_CPBS;i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -