+ * mv_sff_irq_clear - Clear hardware interrupt after DMA.
+ * @ap: Port associated with this ATA transaction.
+ *
+ * We need this only for ATAPI bmdma transactions,
+ * as otherwise we experience spurious interrupts
+ * after libata-sff handles the bmdma interrupts.
+ */
+static void mv_sff_irq_clear(struct ata_port *ap)
+{
+ mv_clear_and_enable_port_irqs(ap, mv_ap_base(ap), ERR_IRQ);
+}
+
+/**
+ * mv_check_atapi_dma - Filter ATAPI cmds which are unsuitable for DMA.
+ * @qc: queued command to check for chipset/DMA compatibility.
+ *
+ * The bmdma engines cannot handle speculative data sizes
+ * (bytecount under/over flow). So only allow DMA for
+ * data transfer commands with known data sizes.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+static int mv_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ struct scsi_cmnd *scmd = qc->scsicmd;
+
+ if (scmd) {
+ switch (scmd->cmnd[0]) {
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case GPCMD_READ_CD:
+ case GPCMD_SEND_DVD_STRUCTURE:
+ case GPCMD_SEND_CUE_SHEET:
+ return 0; /* DMA is safe */
+ }
+ }
+ return -EOPNOTSUPP; /* use PIO instead */
+}
+
+/**
+ * mv_bmdma_setup - Set up BMDMA transaction
+ * @qc: queued command to prepare DMA for.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+static void mv_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void __iomem *port_mmio = mv_ap_base(ap);
+ struct mv_port_priv *pp = ap->private_data;
+
+ mv_fill_sg(qc);
+
+ /* clear all DMA cmd bits */
+ writel(0, port_mmio + BMDMA_CMD);
+
+ /* load PRD table addr. */
+ writel((pp->sg_tbl_dma[qc->tag] >> 16) >> 16,
+ port_mmio + BMDMA_PRD_HIGH);
+ writelfl(pp->sg_tbl_dma[qc->tag],
+ port_mmio + BMDMA_PRD_LOW);
+
+ /* issue r/w command */
+ ap->ops->sff_exec_command(ap, &qc->tf);
+}
+
+/**
+ * mv_bmdma_start - Start a BMDMA transaction
+ * @qc: queued command to start DMA on.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+static void mv_bmdma_start(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void __iomem *port_mmio = mv_ap_base(ap);
+ unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
+ u32 cmd = (rw ? 0 : ATA_DMA_WR) | ATA_DMA_START;
+
+ /* start host DMA transaction */
+ writelfl(cmd, port_mmio + BMDMA_CMD);
+}
+
+/**
+ * mv_bmdma_stop - Stop BMDMA transfer
+ * @qc: queued command to stop DMA on.
+ *
+ * Clears the ATA_DMA_START flag in the bmdma control register
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+static void mv_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ void __iomem *port_mmio = mv_ap_base(ap);
+ u32 cmd;
+
+ /* clear start/stop bit */
+ cmd = readl(port_mmio + BMDMA_CMD);
+ cmd &= ~ATA_DMA_START;
+ writelfl(cmd, port_mmio + BMDMA_CMD);
+
+ /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
+ ata_sff_dma_pause(ap);
+}
+
+/**
+ * mv_bmdma_status - Read BMDMA status
+ * @ap: port for which to retrieve DMA status.
+ *
+ * Read and return equivalent of the sff BMDMA status register.
+ *
+ * LOCKING:
+ * Inherited from caller.
+ */
+static u8 mv_bmdma_status(struct ata_port *ap)
+{
+ void __iomem *port_mmio = mv_ap_base(ap);
+ u32 reg, status;
+
+ /*
+ * Other bits are valid only if ATA_DMA_ACTIVE==0,
+ * and the ATA_DMA_INTR bit doesn't exist.
+ */
+ reg = readl(port_mmio + BMDMA_STATUS);
+ if (reg & ATA_DMA_ACTIVE)
+ status = ATA_DMA_ACTIVE;
+ else
+ status = (reg & ATA_DMA_ERR) | ATA_DMA_INTR;
+ return status;
+}
+
+static void mv_rw_multi_errata_sata24(struct ata_queued_cmd *qc)
+{
+ struct ata_taskfile *tf = &qc->tf;
+ /*
+ * Workaround for 88SX60x1 FEr SATA#24.
+ *
+ * Chip may corrupt WRITEs if multi_count >= 4kB.
+ * Note that READs are unaffected.
+ *
+ * It's not clear if this errata really means "4K bytes",
+ * or if it always happens for multi_count > 7
+ * regardless of device sector_size.
+ *
+ * So, for safety, any write with multi_count > 7
+ * gets converted here into a regular PIO write instead:
+ */
+ if ((tf->flags & ATA_TFLAG_WRITE) && is_multi_taskfile(tf)) {
+ if (qc->dev->multi_count > 7) {
+ switch (tf->command) {
+ case ATA_CMD_WRITE_MULTI:
+ tf->command = ATA_CMD_PIO_WRITE;
+ break;
+ case ATA_CMD_WRITE_MULTI_FUA_EXT:
+ tf->flags &= ~ATA_TFLAG_FUA; /* ugh */
+ /* fall through */
+ case ATA_CMD_WRITE_MULTI_EXT:
+ tf->command = ATA_CMD_PIO_WRITE_EXT;
+ break;
+ }
+ }
+ }
+}
+
+/**