📄 pata_scc.c
字号:
* Note: Original code is ata_std_dev_select(). */static void scc_std_dev_select (struct ata_port *ap, unsigned int device){ u8 tmp; if (device == 0) tmp = ATA_DEVICE_OBS; else tmp = ATA_DEVICE_OBS | ATA_DEV1; out_be32(ap->ioaddr.device_addr, tmp); ata_pause(ap);}/** * scc_bmdma_setup - Set up PCI IDE BMDMA transaction * @qc: Info associated with this ATA transaction. * * Note: Original code is ata_bmdma_setup(). */static void scc_bmdma_setup (struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); u8 dmactl; void __iomem *mmio = ap->ioaddr.bmdma_addr; /* load PRD table addr */ out_be32(mmio + SCC_DMA_TABLE_OFS, ap->prd_dma); /* specify data direction, triple-check start bit is clear */ dmactl = in_be32(mmio + SCC_DMA_CMD); dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); if (!rw) dmactl |= ATA_DMA_WR; out_be32(mmio + SCC_DMA_CMD, dmactl); /* issue r/w command */ ap->ops->exec_command(ap, &qc->tf);}/** * scc_bmdma_start - Start a PCI IDE BMDMA transaction * @qc: Info associated with this ATA transaction. * * Note: Original code is ata_bmdma_start(). */static void scc_bmdma_start (struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; u8 dmactl; void __iomem *mmio = ap->ioaddr.bmdma_addr; /* start host DMA transaction */ dmactl = in_be32(mmio + SCC_DMA_CMD); out_be32(mmio + SCC_DMA_CMD, dmactl | ATA_DMA_START);}/** * scc_devchk - PATA device presence detection * @ap: ATA channel to examine * @device: Device to examine (starting at zero) * * Note: Original code is ata_devchk(). */static unsigned int scc_devchk (struct ata_port *ap, unsigned int device){ struct ata_ioports *ioaddr = &ap->ioaddr; u8 nsect, lbal; ap->ops->dev_select(ap, device); out_be32(ioaddr->nsect_addr, 0x55); out_be32(ioaddr->lbal_addr, 0xaa); out_be32(ioaddr->nsect_addr, 0xaa); out_be32(ioaddr->lbal_addr, 0x55); out_be32(ioaddr->nsect_addr, 0x55); out_be32(ioaddr->lbal_addr, 0xaa); nsect = in_be32(ioaddr->nsect_addr); lbal = in_be32(ioaddr->lbal_addr); if ((nsect == 0x55) && (lbal == 0xaa)) return 1; /* we found a device */ return 0; /* nothing found */}/** * scc_bus_post_reset - PATA device post reset * * Note: Original code is ata_bus_post_reset(). */static int scc_bus_post_reset(struct ata_port *ap, unsigned int devmask, unsigned long deadline){ struct ata_ioports *ioaddr = &ap->ioaddr; unsigned int dev0 = devmask & (1 << 0); unsigned int dev1 = devmask & (1 << 1); int rc; /* if device 0 was found in ata_devchk, wait for its * BSY bit to clear */ if (dev0) { rc = ata_wait_ready(ap, deadline); if (rc && rc != -ENODEV) return rc; } /* if device 1 was found in ata_devchk, wait for * register access, then wait for BSY to clear */ while (dev1) { u8 nsect, lbal; ap->ops->dev_select(ap, 1); nsect = in_be32(ioaddr->nsect_addr); lbal = in_be32(ioaddr->lbal_addr); if ((nsect == 1) && (lbal == 1)) break; if (time_after(jiffies, deadline)) return -EBUSY; msleep(50); /* give drive a breather */ } if (dev1) { rc = ata_wait_ready(ap, deadline); if (rc && rc != -ENODEV) return rc; } /* is all this really necessary? */ ap->ops->dev_select(ap, 0); if (dev1) ap->ops->dev_select(ap, 1); if (dev0) ap->ops->dev_select(ap, 0); return 0;}/** * scc_bus_softreset - PATA device software reset * * Note: Original code is ata_bus_softreset(). */static unsigned int scc_bus_softreset(struct ata_port *ap, unsigned int devmask, unsigned long deadline){ struct ata_ioports *ioaddr = &ap->ioaddr; DPRINTK("ata%u: bus reset via SRST\n", ap->print_id); /* software reset. causes dev0 to be selected */ out_be32(ioaddr->ctl_addr, ap->ctl); udelay(20); out_be32(ioaddr->ctl_addr, ap->ctl | ATA_SRST); udelay(20); out_be32(ioaddr->ctl_addr, ap->ctl); /* wait a while before checking status */ ata_wait_after_reset(ap, deadline); /* Before we perform post reset processing we want to see if * the bus shows 0xFF because the odd clown forgets the D7 * pulldown resistor. */ if (scc_check_status(ap) == 0xFF) return 0; scc_bus_post_reset(ap, devmask, deadline); return 0;}/** * scc_std_softreset - reset host port via ATA SRST * @ap: port to reset * @classes: resulting classes of attached devices * @deadline: deadline jiffies for the operation * * Note: Original code is ata_std_softreset(). */static int scc_std_softreset(struct ata_link *link, unsigned int *classes, unsigned long deadline){ struct ata_port *ap = link->ap; unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS; unsigned int devmask = 0, err_mask; u8 err; DPRINTK("ENTER\n"); if (ata_link_offline(link)) { classes[0] = ATA_DEV_NONE; goto out; } /* determine if device 0/1 are present */ if (scc_devchk(ap, 0)) devmask |= (1 << 0); if (slave_possible && scc_devchk(ap, 1)) devmask |= (1 << 1); /* select device 0 again */ ap->ops->dev_select(ap, 0); /* issue bus reset */ DPRINTK("about to softreset, devmask=%x\n", devmask); err_mask = scc_bus_softreset(ap, devmask, deadline); if (err_mask) { ata_port_printk(ap, KERN_ERR, "SRST failed (err_mask=0x%x)\n", err_mask); return -EIO; } /* determine by signature whether we have ATA or ATAPI devices */ classes[0] = ata_dev_try_classify(&ap->link.device[0], devmask & (1 << 0), &err); if (slave_possible && err != 0x81) classes[1] = ata_dev_try_classify(&ap->link.device[1], devmask & (1 << 1), &err); out: DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]); return 0;}/** * scc_bmdma_stop - Stop PCI IDE BMDMA transfer * @qc: Command we are ending DMA for */static void scc_bmdma_stop (struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR]; void __iomem *bmid_base = ap->host->iomap[SCC_BMID_BAR]; u32 reg; while (1) { reg = in_be32(bmid_base + SCC_DMA_INTST); if (reg & INTSTS_SERROR) { printk(KERN_WARNING "%s: SERROR\n", DRV_NAME); out_be32(bmid_base + SCC_DMA_INTST, INTSTS_SERROR|INTSTS_BMSINT); out_be32(bmid_base + SCC_DMA_CMD, in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); continue; } if (reg & INTSTS_PRERR) { u32 maea0, maec0; maea0 = in_be32(ctrl_base + SCC_CTL_MAEA0); maec0 = in_be32(ctrl_base + SCC_CTL_MAEC0); printk(KERN_WARNING "%s: PRERR [addr:%x cmd:%x]\n", DRV_NAME, maea0, maec0); out_be32(bmid_base + SCC_DMA_INTST, INTSTS_PRERR|INTSTS_BMSINT); out_be32(bmid_base + SCC_DMA_CMD, in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); continue; } if (reg & INTSTS_RERR) { printk(KERN_WARNING "%s: Response Error\n", DRV_NAME); out_be32(bmid_base + SCC_DMA_INTST, INTSTS_RERR|INTSTS_BMSINT); out_be32(bmid_base + SCC_DMA_CMD, in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); continue; } if (reg & INTSTS_ICERR) { out_be32(bmid_base + SCC_DMA_CMD, in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); printk(KERN_WARNING "%s: Illegal Configuration\n", DRV_NAME); out_be32(bmid_base + SCC_DMA_INTST, INTSTS_ICERR|INTSTS_BMSINT); continue; } if (reg & INTSTS_BMSINT) { unsigned int classes; unsigned long deadline = jiffies + ATA_TMOUT_BOOT; printk(KERN_WARNING "%s: Internal Bus Error\n", DRV_NAME); out_be32(bmid_base + SCC_DMA_INTST, INTSTS_BMSINT); /* TBD: SW reset */ scc_std_softreset(&ap->link, &classes, deadline); continue; } if (reg & INTSTS_BMHE) { out_be32(bmid_base + SCC_DMA_INTST, INTSTS_BMHE); continue; } if (reg & INTSTS_ACTEINT) { out_be32(bmid_base + SCC_DMA_INTST, INTSTS_ACTEINT); continue; } if (reg & INTSTS_IOIRQS) { out_be32(bmid_base + SCC_DMA_INTST, INTSTS_IOIRQS); continue; } break; } /* clear start/stop bit */ out_be32(bmid_base + SCC_DMA_CMD, in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ ata_altstatus(ap); /* dummy read */}/** * scc_bmdma_status - Read PCI IDE BMDMA status * @ap: Port associated with this ATA transaction. */static u8 scc_bmdma_status (struct ata_port *ap){ void __iomem *mmio = ap->ioaddr.bmdma_addr; u8 host_stat = in_be32(mmio + SCC_DMA_STATUS); u32 int_status = in_be32(mmio + SCC_DMA_INTST); struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->link.active_tag); static int retry = 0; /* return if IOS_SS is cleared */ if (!(in_be32(mmio + SCC_DMA_CMD) & ATA_DMA_START)) return host_stat; /* errata A252,A308 workaround: Step4 */ if ((ata_altstatus(ap) & ATA_ERR) && (int_status & INTSTS_INTRQ)) return (host_stat | ATA_DMA_INTR); /* errata A308 workaround Step5 */ if (int_status & INTSTS_IOIRQS) { host_stat |= ATA_DMA_INTR; /* We don't check ATAPI DMA because it is limited to UDMA4 */ if ((qc->tf.protocol == ATA_PROT_DMA && qc->dev->xfer_mode > XFER_UDMA_4)) { if (!(int_status & INTSTS_ACTEINT)) { printk(KERN_WARNING "ata%u: operation failed (transfer data loss)\n", ap->print_id); host_stat |= ATA_DMA_ERR; if (retry++) ap->udma_mask &= ~(1 << qc->dev->xfer_mode); } else retry = 0; } } return host_stat;}/** * scc_data_xfer - Transfer data by PIO * @adev: device for this I/O * @buf: data buffer * @buflen: buffer length * @write_data: read/write * * Note: Original code is ata_data_xfer(). */static void scc_data_xfer (struct ata_device *adev, unsigned char *buf, unsigned int buflen, int write_data){ struct ata_port *ap = adev->link->ap; unsigned int words = buflen >> 1; unsigned int i; u16 *buf16 = (u16 *) buf; void __iomem *mmio = ap->ioaddr.data_addr; /* Transfer multiple of 2 bytes */ if (write_data) { for (i = 0; i < words; i++) out_be32(mmio, cpu_to_le16(buf16[i])); } else { for (i = 0; i < words; i++) buf16[i] = le16_to_cpu(in_be32(mmio)); } /* Transfer trailing 1 byte, if any. */ if (unlikely(buflen & 0x01)) { u16 align_buf[1] = { 0 }; unsigned char *trailing_buf = buf + buflen - 1; if (write_data) { memcpy(align_buf, trailing_buf, 1); out_be32(mmio, cpu_to_le16(align_buf[0]));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -