📄 sata_sil24.c
字号:
void __iomem *port = dev->link->ap->ioaddr.cmd_addr; if (dev->cdb_len == 16) writel(PORT_CS_CDB16, port + PORT_CTRL_STAT); else writel(PORT_CS_CDB16, port + PORT_CTRL_CLR);}static void sil24_read_tf(struct ata_port *ap, int tag, struct ata_taskfile *tf){ void __iomem *port = ap->ioaddr.cmd_addr; struct sil24_prb __iomem *prb; u8 fis[6 * 4]; prb = port + PORT_LRAM + sil24_tag(tag) * PORT_LRAM_SLOT_SZ; memcpy_fromio(fis, prb->fis, sizeof(fis)); ata_tf_from_fis(fis, tf);}static u8 sil24_check_status(struct ata_port *ap){ struct sil24_port_priv *pp = ap->private_data; return pp->tf.command;}static int sil24_scr_map[] = { [SCR_CONTROL] = 0, [SCR_STATUS] = 1, [SCR_ERROR] = 2, [SCR_ACTIVE] = 3,};static int sil24_scr_read(struct ata_port *ap, unsigned sc_reg, u32 *val){ void __iomem *scr_addr = ap->ioaddr.scr_addr; if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { void __iomem *addr; addr = scr_addr + sil24_scr_map[sc_reg] * 4; *val = readl(scr_addr + sil24_scr_map[sc_reg] * 4); return 0; } return -EINVAL;}static int sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val){ void __iomem *scr_addr = ap->ioaddr.scr_addr; if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { void __iomem *addr; addr = scr_addr + sil24_scr_map[sc_reg] * 4; writel(val, scr_addr + sil24_scr_map[sc_reg] * 4); return 0; } return -EINVAL;}static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf){ struct sil24_port_priv *pp = ap->private_data; *tf = pp->tf;}static void sil24_config_port(struct ata_port *ap){ void __iomem *port = ap->ioaddr.cmd_addr; /* configure IRQ WoC */ if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); else writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); /* zero error counters. */ writel(0x8000, port + PORT_DECODE_ERR_THRESH); writel(0x8000, port + PORT_CRC_ERR_THRESH); writel(0x8000, port + PORT_HSHK_ERR_THRESH); writel(0x0000, port + PORT_DECODE_ERR_CNT); writel(0x0000, port + PORT_CRC_ERR_CNT); writel(0x0000, port + PORT_HSHK_ERR_CNT); /* always use 64bit activation */ writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); /* clear port multiplier enable and resume bits */ writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR);}static void sil24_config_pmp(struct ata_port *ap, int attached){ void __iomem *port = ap->ioaddr.cmd_addr; if (attached) writel(PORT_CS_PMP_EN, port + PORT_CTRL_STAT); else writel(PORT_CS_PMP_EN, port + PORT_CTRL_CLR);}static void sil24_clear_pmp(struct ata_port *ap){ void __iomem *port = ap->ioaddr.cmd_addr; int i; writel(PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR); for (i = 0; i < SATA_PMP_MAX_PORTS; i++) { void __iomem *pmp_base = port + PORT_PMP + i * PORT_PMP_SIZE; writel(0, pmp_base + PORT_PMP_STATUS); writel(0, pmp_base + PORT_PMP_QACTIVE); }}static int sil24_init_port(struct ata_port *ap){ void __iomem *port = ap->ioaddr.cmd_addr; struct sil24_port_priv *pp = ap->private_data; u32 tmp; /* clear PMP error status */ if (ap->nr_pmp_links) sil24_clear_pmp(ap); writel(PORT_CS_INIT, port + PORT_CTRL_STAT); ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_INIT, PORT_CS_INIT, 10, 100); tmp = ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_RDY, 0, 10, 100); if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY) { pp->do_port_rst = 1; ap->link.eh_context.i.action |= ATA_EH_HARDRESET; return -EIO; } return 0;}static int sil24_exec_polled_cmd(struct ata_port *ap, int pmp, const struct ata_taskfile *tf, int is_cmd, u32 ctrl, unsigned long timeout_msec){ void __iomem *port = ap->ioaddr.cmd_addr; struct sil24_port_priv *pp = ap->private_data; struct sil24_prb *prb = &pp->cmd_block[0].ata.prb; dma_addr_t paddr = pp->cmd_block_dma; u32 irq_enabled, irq_mask, irq_stat; int rc; prb->ctrl = cpu_to_le16(ctrl); ata_tf_to_fis(tf, pmp, is_cmd, prb->fis); /* temporarily plug completion and error interrupts */ irq_enabled = readl(port + PORT_IRQ_ENABLE_SET); writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR, port + PORT_IRQ_ENABLE_CLR); writel((u32)paddr, port + PORT_CMD_ACTIVATE); writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4); irq_mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT; irq_stat = ata_wait_register(port + PORT_IRQ_STAT, irq_mask, 0x0, 10, timeout_msec); writel(irq_mask, port + PORT_IRQ_STAT); /* clear IRQs */ irq_stat >>= PORT_IRQ_RAW_SHIFT; if (irq_stat & PORT_IRQ_COMPLETE) rc = 0; else { /* force port into known state */ sil24_init_port(ap); if (irq_stat & PORT_IRQ_ERROR) rc = -EIO; else rc = -EBUSY; } /* restore IRQ enabled */ writel(irq_enabled, port + PORT_IRQ_ENABLE_SET); return rc;}static int sil24_do_softreset(struct ata_link *link, unsigned int *class, int pmp, unsigned long deadline){ struct ata_port *ap = link->ap; unsigned long timeout_msec = 0; struct ata_taskfile tf; const char *reason; int rc; DPRINTK("ENTER\n"); if (ata_link_offline(link)) { DPRINTK("PHY reports no device\n"); *class = ATA_DEV_NONE; goto out; } /* put the port into known state */ if (sil24_init_port(ap)) { reason = "port not ready"; goto err; } /* do SRST */ if (time_after(deadline, jiffies)) timeout_msec = jiffies_to_msecs(deadline - jiffies); ata_tf_init(link->device, &tf); /* doesn't really matter */ rc = sil24_exec_polled_cmd(ap, pmp, &tf, 0, PRB_CTRL_SRST, timeout_msec); if (rc == -EBUSY) { reason = "timeout"; goto err; } else if (rc) { reason = "SRST command error"; goto err; } sil24_read_tf(ap, 0, &tf); *class = ata_dev_classify(&tf); if (*class == ATA_DEV_UNKNOWN) *class = ATA_DEV_NONE; out: DPRINTK("EXIT, class=%u\n", *class); return 0; err: ata_link_printk(link, KERN_ERR, "softreset failed (%s)\n", reason); return -EIO;}static int sil24_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline){ return sil24_do_softreset(link, class, SATA_PMP_CTRL_PORT, deadline);}static int sil24_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline){ struct ata_port *ap = link->ap; void __iomem *port = ap->ioaddr.cmd_addr; struct sil24_port_priv *pp = ap->private_data; int did_port_rst = 0; const char *reason; int tout_msec, rc; u32 tmp; retry: /* Sometimes, DEV_RST is not enough to recover the controller. * This happens often after PM DMA CS errata. */ if (pp->do_port_rst) { ata_port_printk(ap, KERN_WARNING, "controller in dubious " "state, performing PORT_RST\n"); writel(PORT_CS_PORT_RST, port + PORT_CTRL_STAT); msleep(10); writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR); ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_RDY, 0, 10, 5000); /* restore port configuration */ sil24_config_port(ap); sil24_config_pmp(ap, ap->nr_pmp_links); pp->do_port_rst = 0; did_port_rst = 1; } /* sil24 does the right thing(tm) without any protection */ sata_set_spd(link); tout_msec = 100; if (ata_link_online(link)) tout_msec = 5000; writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT); tmp = ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_DEV_RST, PORT_CS_DEV_RST, 10, tout_msec); /* SStatus oscillates between zero and valid status after * DEV_RST, debounce it. */ rc = sata_link_debounce(link, sata_deb_timing_long, deadline); if (rc) { reason = "PHY debouncing failed"; goto err; } if (tmp & PORT_CS_DEV_RST) { if (ata_link_offline(link)) return 0; reason = "link not ready"; goto err; } /* Sil24 doesn't store signature FIS after hardreset, so we * can't wait for BSY to clear. Some devices take a long time * to get ready and those devices will choke if we don't wait * for BSY clearance here. Tell libata to perform follow-up * softreset. */ return -EAGAIN; err: if (!did_port_rst) { pp->do_port_rst = 1; goto retry; } ata_link_printk(link, KERN_ERR, "hardreset failed (%s)\n", reason); return -EIO;}static inline void sil24_fill_sg(struct ata_queued_cmd *qc, struct sil24_sge *sge){ struct scatterlist *sg; struct sil24_sge *last_sge = NULL; ata_for_each_sg(sg, qc) { sge->addr = cpu_to_le64(sg_dma_address(sg)); sge->cnt = cpu_to_le32(sg_dma_len(sg)); sge->flags = 0; last_sge = sge; sge++; } if (likely(last_sge)) last_sge->flags = cpu_to_le32(SGE_TRM);}static int sil24_qc_defer(struct ata_queued_cmd *qc){ struct ata_link *link = qc->dev->link; struct ata_port *ap = link->ap; u8 prot = qc->tf.protocol; /* * There is a bug in the chip: * Port LRAM Causes the PRB/SGT Data to be Corrupted * If the host issues a read request for LRAM and SActive registers * while active commands are available in the port, PRB/SGT data in * the LRAM can become corrupted. This issue applies only when * reading from, but not writing to, the LRAM. * * Therefore, reading LRAM when there is no particular error [and * other commands may be outstanding] is prohibited. * * To avoid this bug there are two situations where a command must run * exclusive of any other commands on the port: * * - ATAPI commands which check the sense data * - Passthrough ATA commands which always have ATA_QCFLAG_RESULT_TF * set. * */ int is_excl = (prot == ATA_PROT_ATAPI || prot == ATA_PROT_ATAPI_NODATA || prot == ATA_PROT_ATAPI_DMA || (qc->flags & ATA_QCFLAG_RESULT_TF)); if (unlikely(ap->excl_link)) { if (link == ap->excl_link) { if (ap->nr_active_links) return ATA_DEFER_PORT; qc->flags |= ATA_QCFLAG_CLEAR_EXCL; } else return ATA_DEFER_PORT; } else if (unlikely(is_excl)) { ap->excl_link = link; if (ap->nr_active_links) return ATA_DEFER_PORT; qc->flags |= ATA_QCFLAG_CLEAR_EXCL; } return ata_std_qc_defer(qc);}static void sil24_qc_prep(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; struct sil24_port_priv *pp = ap->private_data; union sil24_cmd_block *cb; struct sil24_prb *prb; struct sil24_sge *sge; u16 ctrl = 0; cb = &pp->cmd_block[sil24_tag(qc->tag)]; switch (qc->tf.protocol) { case ATA_PROT_PIO: case ATA_PROT_DMA: case ATA_PROT_NCQ: case ATA_PROT_NODATA: prb = &cb->ata.prb; sge = cb->ata.sge; break; case ATA_PROT_ATAPI: case ATA_PROT_ATAPI_DMA: case ATA_PROT_ATAPI_NODATA: prb = &cb->atapi.prb; sge = cb->atapi.sge; memset(cb->atapi.cdb, 0, 32); memcpy(cb->atapi.cdb, qc->cdb, qc->dev->cdb_len); if (qc->tf.protocol != ATA_PROT_ATAPI_NODATA) { if (qc->tf.flags & ATA_TFLAG_WRITE) ctrl = PRB_CTRL_PACKET_WRITE; else ctrl = PRB_CTRL_PACKET_READ; } break; default: prb = NULL; /* shut up, gcc */ sge = NULL; BUG(); } prb->ctrl = cpu_to_le16(ctrl); ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, prb->fis); if (qc->flags & ATA_QCFLAG_DMAMAP) sil24_fill_sg(qc, sge);}static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; struct sil24_port_priv *pp = ap->private_data; void __iomem *port = ap->ioaddr.cmd_addr; unsigned int tag = sil24_tag(qc->tag); dma_addr_t paddr; void __iomem *activate; paddr = pp->cmd_block_dma + tag * sizeof(*pp->cmd_block); activate = port + PORT_CMD_ACTIVATE + tag * 8; writel((u32)paddr, activate); writel((u64)paddr >> 32, activate + 4); return 0;}static void sil24_irq_clear(struct ata_port *ap){ /* unused */}static void sil24_pmp_attach(struct ata_port *ap){ sil24_config_pmp(ap, 1); sil24_init_port(ap);}static void sil24_pmp_detach(struct ata_port *ap){ sil24_init_port(ap); sil24_config_pmp(ap, 0);}static int sil24_pmp_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline){ return sil24_do_softreset(link, class, link->pmp, deadline);}static int sil24_pmp_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline){ int rc; rc = sil24_init_port(link->ap);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -