📄 sata_promise.c
字号:
ap->prd[idx].addr = cpu_to_le32(addr); ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff); VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len); idx++; sg_len -= len; addr += len; } } if (idx) { u32 len = le32_to_cpu(ap->prd[idx - 1].flags_len); if (len > SG_COUNT_ASIC_BUG) { u32 addr; VPRINTK("Splitting last PRD.\n"); addr = le32_to_cpu(ap->prd[idx - 1].addr); ap->prd[idx - 1].flags_len = cpu_to_le32(len - SG_COUNT_ASIC_BUG); VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx - 1, addr, SG_COUNT_ASIC_BUG); addr = addr + len - SG_COUNT_ASIC_BUG; len = SG_COUNT_ASIC_BUG; ap->prd[idx].addr = cpu_to_le32(addr); ap->prd[idx].flags_len = cpu_to_le32(len); VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len); idx++; } ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); }}static void pdc_qc_prep(struct ata_queued_cmd *qc){ struct pdc_port_priv *pp = qc->ap->private_data; unsigned int i; VPRINTK("ENTER\n"); switch (qc->tf.protocol) { case ATA_PROT_DMA: pdc_fill_sg(qc); /* fall through */ case ATA_PROT_NODATA: i = pdc_pkt_header(&qc->tf, qc->ap->prd_dma, qc->dev->devno, pp->pkt); if (qc->tf.flags & ATA_TFLAG_LBA48) i = pdc_prep_lba48(&qc->tf, pp->pkt, i); else i = pdc_prep_lba28(&qc->tf, pp->pkt, i); pdc_pkt_footer(&qc->tf, pp->pkt, i); break; case ATA_PROT_ATAPI: pdc_fill_sg(qc); break; case ATA_PROT_ATAPI_DMA: pdc_fill_sg(qc); /*FALLTHROUGH*/ case ATA_PROT_ATAPI_NODATA: pdc_atapi_pkt(qc); break; default: break; }}static void pdc_freeze(struct ata_port *ap){ void __iomem *mmio = ap->ioaddr.cmd_addr; u32 tmp; tmp = readl(mmio + PDC_CTLSTAT); tmp |= PDC_IRQ_DISABLE; tmp &= ~PDC_DMA_ENABLE; writel(tmp, mmio + PDC_CTLSTAT); readl(mmio + PDC_CTLSTAT); /* flush */}static void pdc_thaw(struct ata_port *ap){ void __iomem *mmio = ap->ioaddr.cmd_addr; u32 tmp; /* clear IRQ */ readl(mmio + PDC_INT_SEQMASK); /* turn IRQ back on */ tmp = readl(mmio + PDC_CTLSTAT); tmp &= ~PDC_IRQ_DISABLE; writel(tmp, mmio + PDC_CTLSTAT); readl(mmio + PDC_CTLSTAT); /* flush */}static void pdc_common_error_handler(struct ata_port *ap, ata_reset_fn_t hardreset){ if (!(ap->pflags & ATA_PFLAG_FROZEN)) pdc_reset_port(ap); /* perform recovery */ ata_do_eh(ap, ata_std_prereset, ata_std_softreset, hardreset, ata_std_postreset);}static void pdc_pata_error_handler(struct ata_port *ap){ pdc_common_error_handler(ap, NULL);}static void pdc_sata_error_handler(struct ata_port *ap){ pdc_common_error_handler(ap, sata_std_hardreset);}static void pdc_post_internal_cmd(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; /* make DMA engine forget about the failed command */ if (qc->flags & ATA_QCFLAG_FAILED) pdc_reset_port(ap);}static void pdc_error_intr(struct ata_port *ap, struct ata_queued_cmd *qc, u32 port_status, u32 err_mask){ struct ata_eh_info *ehi = &ap->link.eh_info; unsigned int ac_err_mask = 0; ata_ehi_clear_desc(ehi); ata_ehi_push_desc(ehi, "port_status 0x%08x", port_status); port_status &= err_mask; if (port_status & PDC_DRIVE_ERR) ac_err_mask |= AC_ERR_DEV; if (port_status & (PDC_OVERRUN_ERR | PDC_UNDERRUN_ERR)) ac_err_mask |= AC_ERR_HSM; if (port_status & (PDC2_ATA_HBA_ERR | PDC2_ATA_DMA_CNT_ERR)) ac_err_mask |= AC_ERR_ATA_BUS; if (port_status & (PDC_PH_ERR | PDC_SH_ERR | PDC_DH_ERR | PDC2_HTO_ERR | PDC_PCI_SYS_ERR | PDC1_PCI_PARITY_ERR)) ac_err_mask |= AC_ERR_HOST_BUS; if (sata_scr_valid(&ap->link)) { u32 serror; pdc_sata_scr_read(ap, SCR_ERROR, &serror); ehi->serror |= serror; } qc->err_mask |= ac_err_mask; pdc_reset_port(ap); ata_port_abort(ap);}static inline unsigned int pdc_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc){ unsigned int handled = 0; void __iomem *port_mmio = ap->ioaddr.cmd_addr; u32 port_status, err_mask; err_mask = PDC_ERR_MASK; if (ap->flags & PDC_FLAG_GEN_II) err_mask &= ~PDC1_ERR_MASK; else err_mask &= ~PDC2_ERR_MASK; port_status = readl(port_mmio + PDC_GLOBAL_CTL); if (unlikely(port_status & err_mask)) { pdc_error_intr(ap, qc, port_status, err_mask); return 1; } switch (qc->tf.protocol) { case ATA_PROT_DMA: case ATA_PROT_NODATA: case ATA_PROT_ATAPI_DMA: case ATA_PROT_ATAPI_NODATA: qc->err_mask |= ac_err_mask(ata_wait_idle(ap)); ata_qc_complete(qc); handled = 1; break; default: ap->stats.idle_irq++; break; } return handled;}static void pdc_irq_clear(struct ata_port *ap){ struct ata_host *host = ap->host; void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; readl(mmio + PDC_INT_SEQMASK);}static int pdc_is_sataii_tx4(unsigned long flags){ const unsigned long mask = PDC_FLAG_GEN_II | PDC_FLAG_4_PORTS; return (flags & mask) == mask;}static unsigned int pdc_port_no_to_ata_no(unsigned int port_no, int is_sataii_tx4){ static const unsigned char sataii_tx4_port_remap[4] = { 3, 1, 0, 2}; return is_sataii_tx4 ? sataii_tx4_port_remap[port_no] : port_no;}static irqreturn_t pdc_interrupt(int irq, void *dev_instance){ struct ata_host *host = dev_instance; struct ata_port *ap; u32 mask = 0; unsigned int i, tmp; unsigned int handled = 0; void __iomem *mmio_base; unsigned int hotplug_offset, ata_no; u32 hotplug_status; int is_sataii_tx4; VPRINTK("ENTER\n"); if (!host || !host->iomap[PDC_MMIO_BAR]) { VPRINTK("QUICK EXIT\n"); return IRQ_NONE; } mmio_base = host->iomap[PDC_MMIO_BAR]; /* read and clear hotplug flags for all ports */ if (host->ports[0]->flags & PDC_FLAG_GEN_II) hotplug_offset = PDC2_SATA_PLUG_CSR; else hotplug_offset = PDC_SATA_PLUG_CSR; hotplug_status = readl(mmio_base + hotplug_offset); if (hotplug_status & 0xff) writel(hotplug_status | 0xff, mmio_base + hotplug_offset); hotplug_status &= 0xff; /* clear uninteresting bits */ /* reading should also clear interrupts */ mask = readl(mmio_base + PDC_INT_SEQMASK); if (mask == 0xffffffff && hotplug_status == 0) { VPRINTK("QUICK EXIT 2\n"); return IRQ_NONE; } spin_lock(&host->lock); mask &= 0xffff; /* only 16 tags possible */ if (mask == 0 && hotplug_status == 0) { VPRINTK("QUICK EXIT 3\n"); goto done_irq; } writel(mask, mmio_base + PDC_INT_SEQMASK); is_sataii_tx4 = pdc_is_sataii_tx4(host->ports[0]->flags); for (i = 0; i < host->n_ports; i++) { VPRINTK("port %u\n", i); ap = host->ports[i]; /* check for a plug or unplug event */ ata_no = pdc_port_no_to_ata_no(i, is_sataii_tx4); tmp = hotplug_status & (0x11 << ata_no); if (tmp && ap && !(ap->flags & ATA_FLAG_DISABLED)) { struct ata_eh_info *ehi = &ap->link.eh_info; ata_ehi_clear_desc(ehi); ata_ehi_hotplugged(ehi); ata_ehi_push_desc(ehi, "hotplug_status %#x", tmp); ata_port_freeze(ap); ++handled; continue; } /* check for a packet interrupt */ tmp = mask & (1 << (i + 1)); if (tmp && ap && !(ap->flags & ATA_FLAG_DISABLED)) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->link.active_tag); if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) handled += pdc_host_intr(ap, qc); } } VPRINTK("EXIT\n");done_irq: spin_unlock(&host->lock); return IRQ_RETVAL(handled);}static inline void pdc_packet_start(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; struct pdc_port_priv *pp = ap->private_data; void __iomem *mmio = ap->host->iomap[PDC_MMIO_BAR]; unsigned int port_no = ap->port_no; u8 seq = (u8) (port_no + 1); VPRINTK("ENTER, ap %p\n", ap); writel(0x00000001, mmio + (seq * 4)); readl(mmio + (seq * 4)); /* flush */ pp->pkt[2] = seq; wmb(); /* flush PRD, pkt writes */ writel(pp->pkt_dma, ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); readl(ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); /* flush */}static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc){ switch (qc->tf.protocol) { case ATA_PROT_ATAPI_NODATA: if (qc->dev->flags & ATA_DFLAG_CDB_INTR) break; /*FALLTHROUGH*/ case ATA_PROT_NODATA: if (qc->tf.flags & ATA_TFLAG_POLLING) break; /*FALLTHROUGH*/ case ATA_PROT_ATAPI_DMA: case ATA_PROT_DMA: pdc_packet_start(qc); return 0; default: break; } return ata_qc_issue_prot(qc);}static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf){ WARN_ON(tf->protocol == ATA_PROT_DMA || tf->protocol == ATA_PROT_ATAPI_DMA); ata_tf_load(ap, tf);}static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf){ WARN_ON(tf->protocol == ATA_PROT_DMA || tf->protocol == ATA_PROT_ATAPI_DMA); ata_exec_command(ap, tf);}static int pdc_check_atapi_dma(struct ata_queued_cmd *qc){ u8 *scsicmd = qc->scsicmd->cmnd; int pio = 1; /* atapi dma off by default */ /* Whitelist commands that may use DMA. */ switch (scsicmd[0]) { case WRITE_12: case WRITE_10: case WRITE_6: case READ_12: case READ_10: case READ_6: case 0xad: /* READ_DVD_STRUCTURE */ case 0xbe: /* READ_CD */ pio = 0; } /* -45150 (FFFF4FA2) to -1 (FFFFFFFF) shall use PIO mode */ if (scsicmd[0] == WRITE_10) { unsigned int lba = (scsicmd[2] << 24) | (scsicmd[3] << 16) | (scsicmd[4] << 8) | scsicmd[5]; if (lba >= 0xFFFF4FA2) pio = 1; } return pio;}static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc){ /* First generation chips cannot use ATAPI DMA on SATA ports */ return 1;}static void pdc_ata_setup_port(struct ata_port *ap, void __iomem *base, void __iomem *scr_addr){ ap->ioaddr.cmd_addr = base; ap->ioaddr.data_addr = base; ap->ioaddr.feature_addr = ap->ioaddr.error_addr = base + 0x4; ap->ioaddr.nsect_addr = base + 0x8; ap->ioaddr.lbal_addr = base + 0xc; ap->ioaddr.lbam_addr = base + 0x10; ap->ioaddr.lbah_addr = base + 0x14; ap->ioaddr.device_addr = base + 0x18; ap->ioaddr.command_addr = ap->ioaddr.status_addr = base + 0x1c; ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr = base + 0x38; ap->ioaddr.scr_addr = scr_addr;}static void pdc_host_init(struct ata_host *host){ void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; int is_gen2 = host->ports[0]->flags & PDC_FLAG_GEN_II; int hotplug_offset; u32 tmp; if (is_gen2) hotplug_offset = PDC2_SATA_PLUG_CSR; else hotplug_offset = PDC_SATA_PLUG_CSR; /* * Except for the hotplug stuff, this is voodoo from the * Promise driver. Label this entire section * "TODO: figure out why we do this" */ /* enable BMR_BURST, maybe change FIFO_SHD to 8 dwords */ tmp = readl(mmio + PDC_FLASH_CTL); tmp |= 0x02000; /* bit 13 (enable bmr burst) */ if (!is_gen2) tmp |= 0x10000; /* bit 16 (fifo threshold at 8 dw) */ writel(tmp, mmio + PDC_FLASH_CTL); /* clear plug/unplug flags for all ports */ tmp = readl(mmio + hotplug_offset); writel(tmp | 0xff, mmio + hotplug_offset); /* unmask plug/unplug ints */ tmp = readl(mmio + hotplug_offset); writel(tmp & ~0xff0000, mmio + hotplug_offset); /* don't initialise TBG or SLEW on 2nd generation chips */ if (is_gen2) return; /* reduce TBG clock to 133 Mhz. */ tmp = readl(mmio + PDC_TBG_MODE); tmp &= ~0x30000; /* clear bit 17, 16*/ tmp |= 0x10000; /* set bit 17:16 = 0:1 */ writel(tmp, mmio + PDC_TBG_MODE); readl(mmio + PDC_TBG_MODE); /* flush */ msleep(10); /* adjust slew rate control register. */ tmp = readl(mmio + PDC_SLEW_CTL); tmp &= 0xFFFFF03F; /* clear bit 11 ~ 6 */ tmp |= 0x00000900; /* set bit 11-9 = 100b , bit 8-6 = 100 */ writel(tmp, mmio + PDC_SLEW_CTL);}static int pdc_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent){ static int printed_version; const struct ata_port_info *pi = &pdc_port_info[ent->driver_data]; const struct ata_port_info *ppi[PDC_MAX_PORTS]; struct ata_host *host; void __iomem *base; int n_ports, i, rc; int is_sataii_tx4; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); /* enable and acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; rc = pcim_iomap_regions(pdev, 1 << PDC_MMIO_BAR, DRV_NAME); if (rc == -EBUSY) pcim_pin_device(pdev); if (rc) return rc; base = pcim_iomap_table(pdev)[PDC_MMIO_BAR]; /* determine port configuration and setup host */ n_ports = 2; if (pi->flags & PDC_FLAG_4_PORTS) n_ports = 4; for (i = 0; i < n_ports; i++) ppi[i] = pi; if (pi->flags & PDC_FLAG_SATA_PATA) { u8 tmp = readb(base + PDC_FLASH_CTL+1); if (!(tmp & 0x80)) ppi[n_ports++] = pi + 1; } host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); if (!host) { dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n"); return -ENOMEM; } host->iomap = pcim_iomap_table(pdev); is_sataii_tx4 = pdc_is_sataii_tx4(pi->flags); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; unsigned int ata_no = pdc_port_no_to_ata_no(i, is_sataii_tx4); unsigned int port_offset = 0x200 + ata_no * 0x80; unsigned int scr_offset = 0x400 + ata_no * 0x100; pdc_ata_setup_port(ap, base + port_offset, base + scr_offset); ata_port_pbar_desc(ap, PDC_MMIO_BAR, -1, "mmio"); ata_port_pbar_desc(ap, PDC_MMIO_BAR, port_offset, "port"); } /* initialize adapter */ pdc_host_init(host); rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) return rc; rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); if (rc) return rc; /* start host, request IRQ and attach */ pci_set_master(pdev); return ata_host_activate(host, pdev->irq, pdc_interrupt, IRQF_SHARED, &pdc_ata_sht);}static int __init pdc_ata_init(void){ return pci_register_driver(&pdc_ata_pci_driver);}static void __exit pdc_ata_exit(void){ pci_unregister_driver(&pdc_ata_pci_driver);}MODULE_AUTHOR("Jeff Garzik");MODULE_DESCRIPTION("Promise ATA TX2/TX4/TX4000 low-level driver");MODULE_LICENSE("GPL");MODULE_DEVICE_TABLE(pci, pdc_ata_pci_tbl);MODULE_VERSION(DRV_VERSION);module_init(pdc_ata_init);module_exit(pdc_ata_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -