diff options
Diffstat (limited to 'drivers/ata/libata-core.c')
-rw-r--r-- | drivers/ata/libata-core.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index eaead76c9443..3bad6f189190 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3311,6 +3311,103 @@ int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev) } /** + * ata_wait_ready - wait for link to become ready + * @link: link to be waited on + * @deadline: deadline jiffies for the operation + * @check_ready: callback to check link readiness + * + * Wait for @link to become ready. @check_ready should return + * positive number if @link is ready, 0 if it isn't, -ENODEV if + * link doesn't seem to be occupied, other errno for other error + * conditions. + * + * Transient -ENODEV conditions are allowed for + * ATA_TMOUT_FF_WAIT. + * + * LOCKING: + * EH context. + * + * RETURNS: + * 0 if @linke is ready before @deadline; otherwise, -errno. + */ +int ata_wait_ready(struct ata_link *link, unsigned long deadline, + int (*check_ready)(struct ata_link *link)) +{ + unsigned long start = jiffies; + unsigned long nodev_deadline = start + ATA_TMOUT_FF_WAIT; + int warned = 0; + + if (time_after(nodev_deadline, deadline)) + nodev_deadline = deadline; + + while (1) { + unsigned long now = jiffies; + int ready, tmp; + + ready = tmp = check_ready(link); + if (ready > 0) + return 0; + + /* -ENODEV could be transient. Ignore -ENODEV if link + * is online. Also, some SATA devices take a long + * time to clear 0xff after reset. For example, + * HHD424020F7SV00 iVDR needs >= 800ms while Quantum + * GoVault needs even more than that. Wait for + * ATA_TMOUT_FF_WAIT on -ENODEV if link isn't offline. + * + * Note that some PATA controllers (pata_ali) explode + * if status register is read more than once when + * there's no device attached. + */ + if (ready == -ENODEV) { + if (ata_link_online(link)) + ready = 0; + else if ((link->ap->flags & ATA_FLAG_SATA) && + !ata_link_offline(link) && + time_before(now, nodev_deadline)) + ready = 0; + } + + if (ready) + return ready; + if (time_after(now, deadline)) + return -EBUSY; + + if (!warned && time_after(now, start + 5 * HZ) && + (deadline - now > 3 * HZ)) { + ata_link_printk(link, KERN_WARNING, + "link is slow to respond, please be patient " + "(ready=%d)\n", tmp); + warned = 1; + } + + msleep(50); + } +} + +/** + * ata_wait_after_reset - wait for link to become ready after reset + * @link: link to be waited on + * @deadline: deadline jiffies for the operation + * @check_ready: callback to check link readiness + * + * Wait for @link to become ready after reset. + * + * LOCKING: + * EH context. + * + * RETURNS: + * 0 if @linke is ready before @deadline; otherwise, -errno. + */ +extern int ata_wait_after_reset(struct ata_link *link, unsigned long deadline, + int (*check_ready)(struct ata_link *link)) +{ + msleep(ATA_WAIT_AFTER_RESET_MSECS); + + return ata_wait_ready(link, deadline, check_ready); +} + +/** * sata_link_debounce - debounce SATA phy status * @link: ATA link to debounce SATA phy status for * @params: timing parameters { interval, duratinon, timeout } in msec @@ -6075,6 +6172,7 @@ EXPORT_SYMBOL_GPL(ata_noop_qc_prep); EXPORT_SYMBOL_GPL(ata_port_probe); EXPORT_SYMBOL_GPL(ata_dev_disable); EXPORT_SYMBOL_GPL(sata_set_spd); +EXPORT_SYMBOL_GPL(ata_wait_after_reset); EXPORT_SYMBOL_GPL(sata_link_debounce); EXPORT_SYMBOL_GPL(sata_link_resume); EXPORT_SYMBOL_GPL(ata_std_prereset); |