From: matthieu castet I tried the 2.6.17-rc6-mm2 kernel with PATA VIA. The via controller still fails to set xfermode for an ATAPI drive plugged on a via controller (vt8235). Now only this drive is disabled instead both device on the same port. But the libata core tries various speed for the failling drive and sets this speed also for the good device. In my case, the ata core starts with UDMA/33, then tries UDMA/25 and finaly PIO0. And at the end ata4.00 is at PIO0 and ata4.01 disabled. Why not changing only the speed of ata4.01 ? I make a patch that seem to make work the drive. I don't know if is the correct thing to do. Tejun said: The correct thing to do is configure the highest possible UDMA mode and PIO0 for ata4.00. The reason why libata does what it currently does is mostly historical. Hmmm... This is weird, so the device raises interrupt *before* it updates status register? If this is the CDR's fault, it probably needs some sort of quirk list so that drivers which don't use ata_host_intr() can handle it. If it's something which occurs only one particular device + controller combination, the via driver should not pollute the generic ata_host_intr() but needs to implement its own interrupt handler. Cc: Alan Cox Cc: Jeff Garzik Cc: Tejun Heo Signed-off-by: Andrew Morton --- drivers/scsi/libata-core.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff -puN drivers/scsi/libata-core.c~via-pata-fails-on-some-atapi-drives drivers/scsi/libata-core.c --- a/drivers/scsi/libata-core.c~via-pata-fails-on-some-atapi-drives +++ a/drivers/scsi/libata-core.c @@ -4692,6 +4692,7 @@ inline unsigned int ata_host_intr (struc struct ata_queued_cmd *qc) { u8 status, host_stat = 0; + int i; VPRINTK("ata%u: protocol %d task_state %d\n", ap->id, qc->tf.protocol, ap->hsm_task_state); @@ -4738,9 +4739,27 @@ inline unsigned int ata_host_intr (struc } /* check altstatus */ - status = ata_altstatus(ap); - if (status & ATA_BUSY) - goto idle_irq; + if (qc->tf.command == ATA_CMD_SET_FEATURES && + qc->tf.feature == SETFEATURES_XFER) { + /* + * With some configurations (PATA VIA and CDR-6S48), the + * ata_altstatus take some times (~ 3us) to become not busy. + * Without this quirk, it is impossible to set the xfer mode. + */ + for (i = 0; ata_altstatus(ap) & ATA_BUSY; i++) { + if (i > 10) + goto idle_irq; + udelay(1); + } + + if (i) + ata_port_printk(ap, KERN_WARNING, + "ata_altstatus take %dus\n", i); + } else { + status = ata_altstatus(ap); + if (status & ATA_BUSY) + goto idle_irq; + } /* check main status, clearing INTRQ */ status = ata_chk_status(ap); _