diff options
author | Tejun Heo <htejun@gmail.com> | 2006-05-31 05:27:50 -0400 |
---|---|---|
committer | Tejun Heo <htejun@gmail.com> | 2006-05-31 05:27:50 -0400 |
commit | 664faf09a05d74085c0b31e2c621d7647322325b (patch) | |
tree | 6506775bb94e7e99f1730f4fd50eb2f3f7359620 | |
parent | f5914a461eb9703773226a0813f6ffcae10c0861 (diff) |
[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>
-rw-r--r-- | drivers/scsi/libata-eh.c | 58 |
1 files changed, 52 insertions, 6 deletions
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index 0e66f140e53b..8ecb8424d7ba 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c | |||
@@ -1318,16 +1318,28 @@ static void ata_eh_report(struct ata_port *ap) | |||
1318 | } | 1318 | } |
1319 | } | 1319 | } |
1320 | 1320 | ||
1321 | static int ata_eh_reset(struct ata_port *ap, | 1321 | static int ata_eh_followup_srst_needed(int rc, int classify, |
1322 | const unsigned int *classes) | ||
1323 | { | ||
1324 | if (rc == -EAGAIN) | ||
1325 | return 1; | ||
1326 | if (rc != 0) | ||
1327 | return 0; | ||
1328 | if (classify && classes[0] == ATA_DEV_UNKNOWN) | ||
1329 | return 1; | ||
1330 | return 0; | ||
1331 | } | ||
1332 | |||
1333 | static int ata_eh_reset(struct ata_port *ap, int classify, | ||
1322 | ata_prereset_fn_t prereset, ata_reset_fn_t softreset, | 1334 | ata_prereset_fn_t prereset, ata_reset_fn_t softreset, |
1323 | ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) | 1335 | ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) |
1324 | { | 1336 | { |
1325 | struct ata_eh_context *ehc = &ap->eh_context; | 1337 | struct ata_eh_context *ehc = &ap->eh_context; |
1326 | unsigned int classes[ATA_MAX_DEVICES]; | 1338 | unsigned int *classes = ehc->classes; |
1327 | int tries = ATA_EH_RESET_TRIES; | 1339 | int tries = ATA_EH_RESET_TRIES; |
1328 | unsigned int action; | 1340 | unsigned int action; |
1329 | ata_reset_fn_t reset; | 1341 | ata_reset_fn_t reset; |
1330 | int i, rc; | 1342 | int i, did_followup_srst, rc; |
1331 | 1343 | ||
1332 | /* Determine which reset to use and record in ehc->i.action. | 1344 | /* Determine which reset to use and record in ehc->i.action. |
1333 | * prereset() may examine and modify it. | 1345 | * prereset() may examine and modify it. |
@@ -1381,10 +1393,44 @@ static int ata_eh_reset(struct ata_port *ap, | |||
1381 | 1393 | ||
1382 | rc = ata_do_reset(ap, reset, classes); | 1394 | rc = ata_do_reset(ap, reset, classes); |
1383 | 1395 | ||
1396 | did_followup_srst = 0; | ||
1397 | if (reset == hardreset && | ||
1398 | ata_eh_followup_srst_needed(rc, classify, classes)) { | ||
1399 | /* okay, let's do follow-up softreset */ | ||
1400 | did_followup_srst = 1; | ||
1401 | reset = softreset; | ||
1402 | |||
1403 | if (!reset) { | ||
1404 | ata_port_printk(ap, KERN_ERR, | ||
1405 | "follow-up softreset required " | ||
1406 | "but no softreset avaliable\n"); | ||
1407 | return -EINVAL; | ||
1408 | } | ||
1409 | |||
1410 | ata_eh_about_to_do(ap, ATA_EH_RESET_MASK); | ||
1411 | rc = ata_do_reset(ap, reset, classes); | ||
1412 | |||
1413 | if (rc == 0 && classify && | ||
1414 | classes[0] == ATA_DEV_UNKNOWN) { | ||
1415 | ata_port_printk(ap, KERN_ERR, | ||
1416 | "classification failed\n"); | ||
1417 | return -EINVAL; | ||
1418 | } | ||
1419 | } | ||
1420 | |||
1384 | if (rc && --tries) { | 1421 | if (rc && --tries) { |
1422 | const char *type; | ||
1423 | |||
1424 | if (reset == softreset) { | ||
1425 | if (did_followup_srst) | ||
1426 | type = "follow-up soft"; | ||
1427 | else | ||
1428 | type = "soft"; | ||
1429 | } else | ||
1430 | type = "hard"; | ||
1431 | |||
1385 | ata_port_printk(ap, KERN_WARNING, | 1432 | ata_port_printk(ap, KERN_WARNING, |
1386 | "%sreset failed, retrying in 5 secs\n", | 1433 | "%sreset failed, retrying in 5 secs\n", type); |
1387 | reset == softreset ? "soft" : "hard"); | ||
1388 | ssleep(5); | 1434 | ssleep(5); |
1389 | 1435 | ||
1390 | if (reset == hardreset) | 1436 | if (reset == hardreset) |
@@ -1508,7 +1554,7 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, | |||
1508 | if (ehc->i.action & ATA_EH_RESET_MASK) { | 1554 | if (ehc->i.action & ATA_EH_RESET_MASK) { |
1509 | ata_eh_freeze_port(ap); | 1555 | ata_eh_freeze_port(ap); |
1510 | 1556 | ||
1511 | rc = ata_eh_reset(ap, prereset, softreset, hardreset, | 1557 | rc = ata_eh_reset(ap, 0, prereset, softreset, hardreset, |
1512 | postreset); | 1558 | postreset); |
1513 | if (rc) { | 1559 | if (rc) { |
1514 | ata_port_printk(ap, KERN_ERR, | 1560 | ata_port_printk(ap, KERN_ERR, |