[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: Tejun Heo <htejun@gmail.com>
This commit is contained in:
Tejun Heo 2006-05-31 18:27:50 +09:00
parent f5914a461e
commit 664faf09a0

View File

@ -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,