A unit reports the power-on unit attention to the first command it sees.
For an ordinary logical unit that lands on the TEST UNIT READY, which is
already retried, but a UFS boot well known unit reports it to the INQUIRY
and so was written off as absent.

Retry only for a unit attention, since a scan that walks a wide LUN range
would otherwise pay three times over for every unit that is not there.

Signed-off-by: Alexey Charkov <[email protected]>
---
 drivers/scsi/scsi.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 49 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 50e7d7499212..aa7f9f1004d0 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -467,6 +467,38 @@ static void scsi_init_dev_desc_priv(struct blk_desc 
*dev_desc)
 #endif /* CONFIG_BOUNCE_BUFFER */
 }
 
+/**
+ * scsi_unit_attention() - Did a command fail on a unit attention condition?
+ *
+ * A unit attention is a notification rather than a failure, and is cleared by
+ * being reported, so the command that collected one is worth repeating.
+ *
+ * @pccb: Command that has just failed
+ * Return: true if the unit reported a unit attention
+ */
+static bool scsi_unit_attention(struct scsi_cmd *pccb)
+{
+       u8 key;
+
+       if (pccb->sensedatalen < 3)
+               return false;
+
+       switch (pccb->sense_buf[0] & 0x7f) {
+       case 0x70:                              /* fixed format */
+       case 0x71:
+               key = pccb->sense_buf[2];
+               break;
+       case 0x72:                              /* descriptor format */
+       case 0x73:
+               key = pccb->sense_buf[1];
+               break;
+       default:
+               return false;
+       }
+
+       return (key & 0xf) == SENSE_UNIT_ATTENTION;
+}
+
 /**
  * scsi_detect_dev - Detect scsi device
  *
@@ -490,11 +522,19 @@ static int scsi_detect_dev(struct udevice *dev, int 
target, int lun,
 
        pccb->target = target;
        pccb->lun = lun;
-       pccb->pdata = tempbuff;
-       pccb->datalen = 36;
-       pccb->dma_dir = DMA_FROM_DEVICE;
-       scsi_setup_inquiry(pccb);
-       if (scsi_exec(dev, pccb)) {
+
+       /*
+        * A unit reports the power-on unit attention to the first command it
+        * sees, so give it a couple of chances to get that out of the way.
+        */
+       for (count = 0; count < 3; count++) {
+               pccb->pdata = tempbuff;
+               pccb->datalen = 36;
+               pccb->dma_dir = DMA_FROM_DEVICE;
+               scsi_setup_inquiry(pccb);
+               err = scsi_exec(dev, pccb);
+               if (!err)
+                       break;
                if (pccb->contr_stat == SCSI_SEL_TIME_OUT) {
                        /*
                          * selection timeout => assuming no
@@ -504,6 +544,10 @@ static int scsi_detect_dev(struct udevice *dev, int 
target, int lun,
                              pccb->target);
                        return -ETIMEDOUT;
                }
+               if (!scsi_unit_attention(pccb))
+                       break;
+       }
+       if (err) {
                scsi_print_error(pccb);
                return -ENODEV;
        }

-- 
2.54.0

Reply via email to