Skip to content

Commit

Permalink
[PATCH] libata: handle 0xff status properly
Browse files Browse the repository at this point in the history
libata waits for !BSY even when the status register reports 0xff.
This causes long boot delays when D8 isn't pulled down properly.  This
patch does the followings.

* don't wait if status register is 0xff in all wait functions

* make ata_busy_sleep() return 0 on success and -errno on failure.
  -ENODEV is returned on 0xff status and -EBUSY on other failures.

* make ata_bus_softreset() succeed on 0xff status.  0xff status is not
  reset failure.  It indicates no device.  This removes unnecessary
  retries on such ports.  Note that the code change assumes unoccupied
  port reporting 0xff status does not produce valid device signature.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Cc: Joe Jin <lkmaillist@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
  • Loading branch information
Tejun Heo authored and Jeff Garzik committed Dec 2, 2006
1 parent a20c9e8 commit d1adc1b
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 17 deletions.
30 changes: 18 additions & 12 deletions drivers/ata/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2328,39 +2328,47 @@ static inline void ata_tf_to_host(struct ata_port *ap,
* Sleep until ATA Status register bit BSY clears,
* or a timeout occurs.
*
* LOCKING: None.
* LOCKING:
* Kernel thread context (may sleep).
*
* RETURNS:
* 0 on success, -errno otherwise.
*/

unsigned int ata_busy_sleep (struct ata_port *ap,
unsigned long tmout_pat, unsigned long tmout)
int ata_busy_sleep(struct ata_port *ap,
unsigned long tmout_pat, unsigned long tmout)
{
unsigned long timer_start, timeout;
u8 status;

status = ata_busy_wait(ap, ATA_BUSY, 300);
timer_start = jiffies;
timeout = timer_start + tmout_pat;
while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) {
while (status != 0xff && (status & ATA_BUSY) &&
time_before(jiffies, timeout)) {
msleep(50);
status = ata_busy_wait(ap, ATA_BUSY, 3);
}

if (status & ATA_BUSY)
if (status != 0xff && (status & ATA_BUSY))
ata_port_printk(ap, KERN_WARNING,
"port is slow to respond, please be patient "
"(Status 0x%x)\n", status);

timeout = timer_start + tmout;
while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) {
while (status != 0xff && (status & ATA_BUSY) &&
time_before(jiffies, timeout)) {
msleep(50);
status = ata_chk_status(ap);
}

if (status == 0xff)
return -ENODEV;

if (status & ATA_BUSY) {
ata_port_printk(ap, KERN_ERR, "port failed to respond "
"(%lu secs, Status 0x%x)\n",
tmout / HZ, status);
return 1;
return -EBUSY;
}

return 0;
Expand Down Expand Up @@ -2451,10 +2459,8 @@ static unsigned int ata_bus_softreset(struct ata_port *ap,
* the bus shows 0xFF because the odd clown forgets the D7
* pulldown resistor.
*/
if (ata_check_status(ap) == 0xFF) {
ata_port_printk(ap, KERN_ERR, "SRST failed (status 0xFF)\n");
return AC_ERR_OTHER;
}
if (ata_check_status(ap) == 0xFF)
return 0;

ata_bus_post_reset(ap, devmask);

Expand Down
9 changes: 4 additions & 5 deletions include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -746,9 +746,8 @@ extern int ata_scsi_device_suspend(struct scsi_device *, pm_message_t mesg);
extern int ata_host_suspend(struct ata_host *host, pm_message_t mesg);
extern void ata_host_resume(struct ata_host *host);
extern int ata_ratelimit(void);
extern unsigned int ata_busy_sleep(struct ata_port *ap,
unsigned long timeout_pat,
unsigned long timeout);
extern int ata_busy_sleep(struct ata_port *ap,
unsigned long timeout_pat, unsigned long timeout);
extern void ata_port_queue_task(struct ata_port *ap, void (*fn)(void *),
void *data, unsigned long delay);
extern u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val,
Expand Down Expand Up @@ -1064,7 +1063,7 @@ static inline u8 ata_busy_wait(struct ata_port *ap, unsigned int bits,
udelay(10);
status = ata_chk_status(ap);
max--;
} while ((status & bits) && (max > 0));
} while (status != 0xff && (status & bits) && (max > 0));

return status;
}
Expand All @@ -1085,7 +1084,7 @@ static inline u8 ata_wait_idle(struct ata_port *ap)
{
u8 status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);

if (status & (ATA_BUSY | ATA_DRQ)) {
if (status != 0xff && (status & (ATA_BUSY | ATA_DRQ))) {
unsigned long l = ap->ioaddr.status_addr;
if (ata_msg_warn(ap))
printk(KERN_WARNING "ATA: abnormal status 0x%X on port 0x%lX\n",
Expand Down

0 comments on commit d1adc1b

Please sign in to comment.