]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
scsi: NCR5380: Check for phase match during PDMA fixup
authorFinn Thain <fthain@linux-m68k.org>
Wed, 7 Aug 2024 03:36:28 +0000 (13:36 +1000)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 13 Aug 2024 02:05:49 +0000 (22:05 -0400)
It's not an error for a target to change the bus phase during a transfer.
Unfortunately, the FLAG_DMA_FIXUP workaround does not allow for that -- a
phase change produces a DRQ timeout error and the device borken flag will
be set.

Check the phase match bit during FLAG_DMA_FIXUP processing. Don't forget to
decrement the command residual. While we are here, change shost_printk()
into scmd_printk() for better consistency with other DMA error messages.

Tested-by: Stan Johnson <userm57@yahoo.com>
Fixes: 55181be8ced1 ("ncr5380: Replace redundant flags with FLAG_NO_DMA_FIXUP")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Link: https://lore.kernel.org/r/99dc7d1f4c825621b5b120963a69f6cd3e9ca659.1723001788.git.fthain@linux-m68k.org
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/NCR5380.c

index cea3a79d538e4b537a79915f06f4bc1ce7e2cf7c..00e245173320c30cdfc42e6392e943f7a1459726 100644 (file)
@@ -1485,6 +1485,7 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance,
                                unsigned char **data)
 {
        struct NCR5380_hostdata *hostdata = shost_priv(instance);
+       struct NCR5380_cmd *ncmd = NCR5380_to_ncmd(hostdata->connected);
        int c = *count;
        unsigned char p = *phase;
        unsigned char *d = *data;
@@ -1496,7 +1497,7 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance,
                return -1;
        }
 
-       NCR5380_to_ncmd(hostdata->connected)->phase = p;
+       ncmd->phase = p;
 
        if (p & SR_IO) {
                if (hostdata->read_overruns)
@@ -1608,45 +1609,44 @@ static int NCR5380_transfer_dma(struct Scsi_Host *instance,
  * request.
  */
 
-       if (hostdata->flags & FLAG_DMA_FIXUP) {
-               if (p & SR_IO) {
-                       /*
-                        * The workaround was to transfer fewer bytes than we
-                        * intended to with the pseudo-DMA read function, wait for
-                        * the chip to latch the last byte, read it, and then disable
-                        * pseudo-DMA mode.
-                        *
-                        * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
-                        * REQ is deasserted when ACK is asserted, and not reasserted
-                        * until ACK goes false.  Since the NCR5380 won't lower ACK
-                        * until DACK is asserted, which won't happen unless we twiddle
-                        * the DMA port or we take the NCR5380 out of DMA mode, we
-                        * can guarantee that we won't handshake another extra
-                        * byte.
-                        */
-
-                       if (NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG,
-                                                 BASR_DRQ, BASR_DRQ, 0) < 0) {
-                               result = -1;
-                               shost_printk(KERN_ERR, instance, "PDMA read: DRQ timeout\n");
-                       }
-                       if (NCR5380_poll_politely(hostdata, STATUS_REG,
-                                                 SR_REQ, 0, 0) < 0) {
-                               result = -1;
-                               shost_printk(KERN_ERR, instance, "PDMA read: !REQ timeout\n");
-                       }
-                       d[*count - 1] = NCR5380_read(INPUT_DATA_REG);
-               } else {
-                       /*
-                        * Wait for the last byte to be sent.  If REQ is being asserted for
-                        * the byte we're interested, we'll ACK it and it will go false.
-                        */
-                       if (NCR5380_poll_politely2(hostdata,
-                            BUS_AND_STATUS_REG, BASR_DRQ, BASR_DRQ,
-                            BUS_AND_STATUS_REG, BASR_PHASE_MATCH, 0, 0) < 0) {
-                               result = -1;
-                               shost_printk(KERN_ERR, instance, "PDMA write: DRQ and phase timeout\n");
+       if ((hostdata->flags & FLAG_DMA_FIXUP) &&
+           (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) {
+               /*
+                * The workaround was to transfer fewer bytes than we
+                * intended to with the pseudo-DMA receive function, wait for
+                * the chip to latch the last byte, read it, and then disable
+                * DMA mode.
+                *
+                * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
+                * REQ is deasserted when ACK is asserted, and not reasserted
+                * until ACK goes false. Since the NCR5380 won't lower ACK
+                * until DACK is asserted, which won't happen unless we twiddle
+                * the DMA port or we take the NCR5380 out of DMA mode, we
+                * can guarantee that we won't handshake another extra
+                * byte.
+                *
+                * If sending, wait for the last byte to be sent. If REQ is
+                * being asserted for the byte we're interested, we'll ACK it
+                * and it will go false.
+                */
+               if (!NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG,
+                                          BASR_DRQ, BASR_DRQ, 0)) {
+                       if ((p & SR_IO) &&
+                           (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) {
+                               if (!NCR5380_poll_politely(hostdata, STATUS_REG,
+                                                          SR_REQ, 0, 0)) {
+                                       d[c] = NCR5380_read(INPUT_DATA_REG);
+                                       --ncmd->this_residual;
+                               } else {
+                                       result = -1;
+                                       scmd_printk(KERN_ERR, hostdata->connected,
+                                                   "PDMA fixup: !REQ timeout\n");
+                               }
                        }
+               } else if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH) {
+                       result = -1;
+                       scmd_printk(KERN_ERR, hostdata->connected,
+                                   "PDMA fixup: DRQ timeout\n");
                }
        }