ata: pata_parport: add custom version of wait_after_reset
authorOndrej Zary <linux@zary.sk>
Thu, 5 Oct 2023 20:55:58 +0000 (22:55 +0200)
committerDamien Le Moal <dlemoal@kernel.org>
Tue, 10 Oct 2023 05:42:22 +0000 (14:42 +0900)
Some parallel adapters (e.g. EXP Computer MC-1285B EPP Cable) return
bogus values when there's no master device present. This can cause
reset to fail, preventing the lone slave device (such as EXP Computer
CD-865) from working.

Add custom version of wait_after_reset that ignores master failure when
a slave device is present. The custom version is also needed because
the generic ata_sff_wait_after_reset uses direct port I/O for slave
device detection.

Signed-off-by: Ondrej Zary <linux@zary.sk>
Reviewed-by: Sergey Shtylyov <s.shtylyov@omp.ru>
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
drivers/ata/pata_parport/pata_parport.c

index cf87bbb..a7adfdc 100644 (file)
@@ -80,6 +80,72 @@ static bool pata_parport_devchk(struct ata_port *ap, unsigned int device)
        return (nsect == 0x55) && (lbal == 0xaa);
 }
 
+static int pata_parport_wait_after_reset(struct ata_link *link,
+                                        unsigned int devmask,
+                                        unsigned long deadline)
+{
+       struct ata_port *ap = link->ap;
+       struct pi_adapter *pi = ap->host->private_data;
+       unsigned int dev0 = devmask & (1 << 0);
+       unsigned int dev1 = devmask & (1 << 1);
+       int rc, ret = 0;
+
+       ata_msleep(ap, ATA_WAIT_AFTER_RESET);
+
+       /* always check readiness of the master device */
+       rc = ata_sff_wait_ready(link, deadline);
+       if (rc) {
+               /*
+                * some adapters return bogus values if master device is not
+                * present, so don't abort now if a slave device is present
+                */
+               if (!dev1)
+                       return rc;
+               ret = -ENODEV;
+       }
+
+       /*
+        * if device 1 was found in ata_devchk, wait for register
+        * access briefly, then wait for BSY to clear.
+        */
+       if (dev1) {
+               int i;
+
+               pata_parport_dev_select(ap, 1);
+
+               /*
+                * Wait for register access.  Some ATAPI devices fail
+                * to set nsect/lbal after reset, so don't waste too
+                * much time on it.  We're gonna wait for !BSY anyway.
+                */
+               for (i = 0; i < 2; i++) {
+                       u8 nsect, lbal;
+
+                       nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
+                       lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
+                       if (nsect == 1 && lbal == 1)
+                               break;
+                       /* give drive a breather */
+                       ata_msleep(ap, 50);
+               }
+
+               rc = ata_sff_wait_ready(link, deadline);
+               if (rc) {
+                       if (rc != -ENODEV)
+                               return rc;
+                       ret = rc;
+               }
+       }
+
+       pata_parport_dev_select(ap, 0);
+       if (dev1)
+               pata_parport_dev_select(ap, 1);
+       if (dev0)
+               pata_parport_dev_select(ap, 0);
+
+       return ret;
+}
+
 static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
                                      unsigned long deadline)
 {
@@ -94,7 +160,7 @@ static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
        ap->last_ctl = ap->ctl;
 
        /* wait the port to become ready */
-       return ata_sff_wait_after_reset(&ap->link, devmask, deadline);
+       return pata_parport_wait_after_reset(&ap->link, devmask, deadline);
 }
 
 static int pata_parport_softreset(struct ata_link *link, unsigned int *classes,