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

Commit 664faf09 authored by Tejun Heo's avatar Tejun Heo
Browse files

[PATCH] libata-hp-prep: implement followup softreset handling



In some cases, hardreset must be followed by SRST.

* some controllers can't classify with hardreset
* some controllers can't wait for !BSY after hardreset (LLDD should
  explicitly request followup softreset by returning -EAGAIN)
* (later) PM needs SRST w/ PMP==15 to operate after hardreset

To handle above cases, this patch implements follow-up softreset.
After a hardreset, ata_eh_reset() checks whether any of above
conditions are met and do a follow-up softreset if necessary.

Signed-off-by: default avatarTejun Heo <htejun@gmail.com>
parent f5914a46
Loading
Loading
Loading
Loading
+52 −6
Original line number Diff line number Diff line
@@ -1318,16 +1318,28 @@ static void ata_eh_report(struct ata_port *ap)
	}
}

static int ata_eh_reset(struct ata_port *ap,
static int ata_eh_followup_srst_needed(int rc, int classify,
				       const unsigned int *classes)
{
	if (rc == -EAGAIN)
		return 1;
	if (rc != 0)
		return 0;
	if (classify && classes[0] == ATA_DEV_UNKNOWN)
		return 1;
	return 0;
}

static int ata_eh_reset(struct ata_port *ap, int classify,
			ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
			ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
{
	struct ata_eh_context *ehc = &ap->eh_context;
	unsigned int classes[ATA_MAX_DEVICES];
	unsigned int *classes = ehc->classes;
	int tries = ATA_EH_RESET_TRIES;
	unsigned int action;
	ata_reset_fn_t reset;
	int i, rc;
	int i, did_followup_srst, rc;

	/* Determine which reset to use and record in ehc->i.action.
	 * prereset() may examine and modify it.
@@ -1381,10 +1393,44 @@ static int ata_eh_reset(struct ata_port *ap,

	rc = ata_do_reset(ap, reset, classes);

	did_followup_srst = 0;
	if (reset == hardreset &&
	    ata_eh_followup_srst_needed(rc, classify, classes)) {
		/* okay, let's do follow-up softreset */
		did_followup_srst = 1;
		reset = softreset;

		if (!reset) {
			ata_port_printk(ap, KERN_ERR,
					"follow-up softreset required "
					"but no softreset avaliable\n");
			return -EINVAL;
		}

		ata_eh_about_to_do(ap, ATA_EH_RESET_MASK);
		rc = ata_do_reset(ap, reset, classes);

		if (rc == 0 && classify &&
		    classes[0] == ATA_DEV_UNKNOWN) {
			ata_port_printk(ap, KERN_ERR,
					"classification failed\n");
			return -EINVAL;
		}
	}

	if (rc && --tries) {
		const char *type;

		if (reset == softreset) {
			if (did_followup_srst)
				type = "follow-up soft";
			else
				type = "soft";
		} else
			type = "hard";

		ata_port_printk(ap, KERN_WARNING,
				"%sreset failed, retrying in 5 secs\n",
				reset == softreset ? "soft" : "hard");
				"%sreset failed, retrying in 5 secs\n", type);
		ssleep(5);

		if (reset == hardreset)
@@ -1508,7 +1554,7 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
	if (ehc->i.action & ATA_EH_RESET_MASK) {
		ata_eh_freeze_port(ap);

		rc = ata_eh_reset(ap, prereset, softreset, hardreset,
		rc = ata_eh_reset(ap, 0, prereset, softreset, hardreset,
				  postreset);
		if (rc) {
			ata_port_printk(ap, KERN_ERR,