diff options
-rw-r--r-- | drivers/ata/libata-core.c | 98 | ||||
-rw-r--r-- | drivers/ata/libata-sff.c | 62 | ||||
-rw-r--r-- | drivers/ata/libata.h | 2 | ||||
-rw-r--r-- | include/linux/libata.h | 2 |
4 files changed, 114 insertions, 50 deletions
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index eaead76c944..3bad6f18919 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); diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 6e8de3c1595..78912c5011a 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -308,6 +308,17 @@ int ata_sff_busy_sleep(struct ata_port *ap, return 0; } +static int ata_sff_check_ready(struct ata_link *link) +{ + u8 status = link->ap->ops->sff_check_status(link->ap); + + if (!(status & ATA_BUSY)) + return 1; + if (status == 0xff) + return -ENODEV; + return 0; +} + /** * ata_sff_wait_ready - sleep until BSY clears, or timeout * @link: SFF link to wait ready status for @@ -324,56 +335,7 @@ int ata_sff_busy_sleep(struct ata_port *ap, */ int ata_sff_wait_ready(struct ata_link *link, unsigned long deadline) { - struct ata_port *ap = link->ap; - 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) { - u8 status = ap->ops->sff_check_status(ap); - unsigned long now = jiffies; - - if (!(status & ATA_BUSY)) - return 0; - - /* No device status could be transient. Ignore it 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 (status == 0xff) { - if (ata_link_online(link)) - status = ATA_BUSY; - else if ((link->ap->flags & ATA_FLAG_SATA) && - !ata_link_offline(link) && - time_before(now, nodev_deadline)) - status = ATA_BUSY; - if (status == 0xff) - return -ENODEV; - } - - 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 " - "(Status 0x%x)\n", status); - warned = 1; - } - - msleep(50); - } + return ata_wait_ready(link, deadline, ata_sff_check_ready); } /** diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 11b5f67a19d..08af43e2c08 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -81,6 +81,8 @@ extern unsigned ata_exec_internal_sg(struct ata_device *dev, int dma_dir, struct scatterlist *sg, unsigned int n_elem, unsigned long timeout); extern unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd); +extern int ata_wait_ready(struct ata_link *link, unsigned long deadline, + int (*check_ready)(struct ata_link *link)); extern int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, unsigned int flags, u16 *id); extern int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags); diff --git a/include/linux/libata.h b/include/linux/libata.h index da556024478..4bbf2524e47 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -837,6 +837,8 @@ extern void sata_print_link_status(struct ata_link *link); extern void ata_port_probe(struct ata_port *); extern int sata_set_spd(struct ata_link *link); extern int ata_std_prereset(struct ata_link *link, unsigned long deadline); +extern int ata_wait_after_reset(struct ata_link *link, unsigned long deadline, + int (*check_ready)(struct ata_link *link)); extern int sata_link_debounce(struct ata_link *link, const unsigned long *params, unsigned long deadline); extern int sata_link_resume(struct ata_link *link, const unsigned long *params, |