Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 70f8b79c authored by Mark Lord's avatar Mark Lord Committed by Jeff Garzik
Browse files

[libata] sata_mv: Implement direct FIS transmission via mv_qc_issue_fis().



This is initially needed to work around NCQ errata,
whereby the READ_LOG_EXT command sometimes fails
when issued in the traditional (sff) fashion.

Portions of this code will likely be reused for
implementation of the target mode feature later on.

Signed-off-by: default avatarMark Lord <mlord@pobox.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 1a660164
Loading
Loading
Loading
Loading
+116 −0
Original line number Diff line number Diff line
@@ -1821,6 +1821,105 @@ static u8 mv_sff_check_status(struct ata_port *ap)
	return stat;
}

/**
 *	mv_send_fis - Send a FIS, using the "Vendor-Unique FIS" register
 *	@fis: fis to be sent
 *	@nwords: number of 32-bit words in the fis
 */
static unsigned int mv_send_fis(struct ata_port *ap, u32 *fis, int nwords)
{
	void __iomem *port_mmio = mv_ap_base(ap);
	u32 ifctl, old_ifctl, ifstat;
	int i, timeout = 200, final_word = nwords - 1;

	/* Initiate FIS transmission mode */
	old_ifctl = readl(port_mmio + SATA_IFCTL_OFS);
	ifctl = 0x100 | (old_ifctl & 0xf);
	writelfl(ifctl, port_mmio + SATA_IFCTL_OFS);

	/* Send all words of the FIS except for the final word */
	for (i = 0; i < final_word; ++i)
		writel(fis[i], port_mmio + VENDOR_UNIQUE_FIS_OFS);

	/* Flag end-of-transmission, and then send the final word */
	writelfl(ifctl | 0x200, port_mmio + SATA_IFCTL_OFS);
	writelfl(fis[final_word], port_mmio + VENDOR_UNIQUE_FIS_OFS);

	/*
	 * Wait for FIS transmission to complete.
	 * This typically takes just a single iteration.
	 */
	do {
		ifstat = readl(port_mmio + SATA_IFSTAT_OFS);
	} while (!(ifstat & 0x1000) && --timeout);

	/* Restore original port configuration */
	writelfl(old_ifctl, port_mmio + SATA_IFCTL_OFS);

	/* See if it worked */
	if ((ifstat & 0x3000) != 0x1000) {
		ata_port_printk(ap, KERN_WARNING,
				"%s transmission error, ifstat=%08x\n",
				__func__, ifstat);
		return AC_ERR_OTHER;
	}
	return 0;
}

/**
 *	mv_qc_issue_fis - Issue a command directly as a FIS
 *	@qc: queued command to start
 *
 *	Note that the ATA shadow registers are not updated
 *	after command issue, so the device will appear "READY"
 *	if polled, even while it is BUSY processing the command.
 *
 *	So we use a status hook to fake ATA_BUSY until the drive changes state.
 *
 *	Note: we don't get updated shadow regs on *completion*
 *	of non-data commands. So avoid sending them via this function,
 *	as they will appear to have completed immediately.
 *
 *	GEN_IIE has special registers that we could get the result tf from,
 *	but earlier chipsets do not.  For now, we ignore those registers.
 */
static unsigned int mv_qc_issue_fis(struct ata_queued_cmd *qc)
{
	struct ata_port *ap = qc->ap;
	struct mv_port_priv *pp = ap->private_data;
	struct ata_link *link = qc->dev->link;
	u32 fis[5];
	int err = 0;

	ata_tf_to_fis(&qc->tf, link->pmp, 1, (void *)fis);
	err = mv_send_fis(ap, fis, sizeof(fis) / sizeof(fis[0]));
	if (err)
		return err;

	switch (qc->tf.protocol) {
	case ATAPI_PROT_PIO:
		pp->pp_flags |= MV_PP_FLAG_FAKE_ATA_BUSY;
		/* fall through */
	case ATAPI_PROT_NODATA:
		ap->hsm_task_state = HSM_ST_FIRST;
		break;
	case ATA_PROT_PIO:
		pp->pp_flags |= MV_PP_FLAG_FAKE_ATA_BUSY;
		if (qc->tf.flags & ATA_TFLAG_WRITE)
			ap->hsm_task_state = HSM_ST_FIRST;
		else
			ap->hsm_task_state = HSM_ST;
		break;
	default:
		ap->hsm_task_state = HSM_ST_LAST;
		break;
	}

	if (qc->tf.flags & ATA_TFLAG_POLLING)
		ata_pio_queue_task(ap, qc, 0);
	return 0;
}

/**
 *      mv_qc_issue - Initiate a command to the host
 *      @qc: queued command to start
@@ -1896,6 +1995,23 @@ static unsigned int mv_qc_issue(struct ata_queued_cmd *qc)
	mv_stop_edma(ap);
	mv_clear_and_enable_port_irqs(ap, mv_ap_base(ap), port_irqs);
	mv_pmp_select(ap, qc->dev->link->pmp);

	if (qc->tf.command == ATA_CMD_READ_LOG_EXT) {
		struct mv_host_priv *hpriv = ap->host->private_data;
		/*
		 * Workaround for 88SX60x1 FEr SATA#25 (part 2).
		 * 
		 * After any NCQ error, the READ_LOG_EXT command
		 * from libata-eh *must* use mv_qc_issue_fis().
		 * Otherwise it might fail, due to chip errata.
		 *
		 * Rather than special-case it, we'll just *always*
		 * use this method here for READ_LOG_EXT, making for
		 * easier testing.
		 */
		if (IS_GEN_II(hpriv))
			return mv_qc_issue_fis(qc);
	}
	return ata_sff_qc_issue(qc);
}