From 6d274379f584a638c1f2b4b8a19014d4baef1d9f Mon Sep 17 00:00:00 2001
From: sahil <sahil@arm.com>
Date: Thu, 11 Aug 2022 11:26:29 +0530
Subject: [PATCH] Platform/ARM/N1Sdp: manually poll QSPI status bit after
 erase/write

This patch adds a function to poll Nor flash memory's status register
bit (WIP bit) to wait for an erase/write operation to complete.
The polling timeout is set to 1 second.

Upstream-Status: Pending
Signed-off-by: Xueliang Zhong <xueliang.zhong@arm.com>
Signed-off-by: sahil <sahil@arm.com>
Change-Id: Ie678b7586671964ae0f8506a0542d73cbddddfe4
---
 .../Drivers/CadenceQspiDxe/CadenceQspiDxe.inf |  1 +
 .../Drivers/CadenceQspiDxe/CadenceQspiReg.h   |  6 +-
 .../N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c   | 80 ++++++++++++++++++-
 .../N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h   |  5 ++
 4 files changed, 88 insertions(+), 4 deletions(-)

diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf
index 4f20c3ba..7a39eb2d 100644
--- a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf
+++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiDxe.inf
@@ -39,6 +39,7 @@
   MemoryAllocationLib
   NorFlashInfoLib
   NorFlashPlatformLib
+  TimerLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
   UefiLib
diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h
index fe3b327c..1971631d 100644
--- a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h
+++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/CadenceQspiReg.h
@@ -16,13 +16,15 @@
 #define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BIT_POS         19
 #define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_BIT_POS    16
 #define CDNS_QSPI_FLASH_CMD_CTRL_REG_STATUS_BIT           0x02
-#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_4B         0x03
-#define CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_3B         0x02
 #define CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS       24
 #define CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_ENABLE          0x01
 #define CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_BYTE_3B         0x02
 #define CDNS_QSPI_FLASH_CMD_CTRL_REG_READEN_BIT_POS       23
 #define CDNS_QSPI_FLASH_CMD_CTRL_REG_READBYTE_BIT_POS     20
+#define CDNS_QSPI_FLASH_CMD_CTRL_REG_DUMMY_8C             0x8
+#define CDNS_QSPI_FLASH_CMD_CTRL_REG_DUMMY_BIT_POS        7
+#define CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_DATA_BYTES(x) ((x - 1) << CDNS_QSPI_FLASH_CMD_CTRL_REG_READBYTE_BIT_POS)
+#define CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_ADDR_BYTES(x) ((x - 1) << CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_BIT_POS)
 
 #define CDNS_QSPI_FLASH_CMD_READ_DATA_REG_OFFSET          0xA0
 
diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c
index 188c75e2..6832351a 100644
--- a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c
+++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.c
@@ -10,6 +10,7 @@
 #include <Library/MemoryAllocationLib.h>
 #include <Library/NorFlashInfoLib.h>
 #include <Library/PcdLib.h>
+#include <Library/TimerLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
 
@@ -184,6 +185,74 @@ FreeInstance:
   return Status;
 }
 
+/**
+  Converts milliseconds into number of ticks of the performance counter.
+
+  @param[in] Milliseconds  Milliseconds to convert into ticks.
+
+  @retval Milliseconds expressed as number of ticks.
+
+**/
+STATIC
+UINT64
+MilliSecondsToTicks (
+  IN UINTN Milliseconds
+  )
+{
+  CONST UINT64  NanoSecondsPerTick = GetTimeInNanoSecond (1);
+
+  return (Milliseconds * 1000000) / NanoSecondsPerTick;
+}
+
+/**
+  Poll Status register for NOR flash erase/write completion.
+
+  @param[in]      Instance           NOR flash Instance.
+
+  @retval         EFI_SUCCESS        Request is executed successfully.
+  @retval         EFI_TIMEOUT        Operation timed out.
+  @retval         EFI_DEVICE_ERROR   Controller operartion failed.
+
+**/
+STATIC
+EFI_STATUS
+NorFlashPollStatusRegister (
+  IN NOR_FLASH_INSTANCE     *Instance
+  )
+{
+  BOOLEAN     SRegDone;
+  UINT32      val;
+
+  val = SPINOR_OP_RDSR << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS |
+      CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_ENABLE << CDNS_QSPI_FLASH_CMD_CTRL_REG_READEN_BIT_POS |
+      CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_DATA_BYTES(1) |
+      CDNS_QSPI_FLASH_CMD_CTRL_REG_DUMMY_8C << CDNS_QSPI_FLASH_CMD_CTRL_REG_DUMMY_BIT_POS;
+
+  CONST UINT64  TickOut =
+    GetPerformanceCounter () + MilliSecondsToTicks (SPINOR_SR_WIP_POLL_TIMEOUT_MS);
+
+  do {
+    if (GetPerformanceCounter () > TickOut) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "NorFlashPollStatusRegister: Timeout waiting for erase/write.\n"
+        ));
+      return EFI_TIMEOUT;
+    }
+
+    if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, val))) {
+      return EFI_DEVICE_ERROR;
+    }
+
+    SRegDone =
+      (MmioRead8 (Instance->HostRegisterBaseAddress + CDNS_QSPI_FLASH_CMD_READ_DATA_REG_OFFSET)
+      & SPINOR_SR_WIP) == 0;
+
+  } while (!SRegDone);
+
+  return EFI_SUCCESS;
+}
+
 /**
   Check whether NOR flash opertions are Locked.
 
@@ -305,12 +374,16 @@ NorFlashEraseSingleBlock (
 
   DevConfigVal = SPINOR_OP_BE_4K << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS |
                  CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_ENABLE << CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BIT_POS |
-                 CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_3B << CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_BIT_POS;
+                 CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_ADDR_BYTES(3);
 
   if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, DevConfigVal))) {
     return EFI_DEVICE_ERROR;
   }
 
+  if (EFI_ERROR (NorFlashPollStatusRegister (Instance))) {
+      return EFI_DEVICE_ERROR;
+  }
+
   return EFI_SUCCESS;
 }
 
@@ -383,6 +456,9 @@ NorFlashWriteSingleWord (
     return EFI_DEVICE_ERROR;
   }
   MmioWrite32 (WordAddress, WriteData);
+  if (EFI_ERROR (NorFlashPollStatusRegister (Instance))) {
+    return EFI_DEVICE_ERROR;
+  }
   return EFI_SUCCESS;
 }
 
@@ -907,7 +983,7 @@ NorFlashReadID (
 
   val = SPINOR_OP_RDID << CDNS_QSPI_FLASH_CMD_CTRL_REG_OPCODE_BIT_POS |
         CDNS_QSPI_FLASH_CMD_CTRL_REG_READ_ENABLE << CDNS_QSPI_FLASH_CMD_CTRL_REG_READEN_BIT_POS |
-        CDNS_QSPI_FLASH_CMD_CTRL_REG_ADDR_BYTE_3B << CDNS_QSPI_FLASH_CMD_CTRL_REG_READBYTE_BIT_POS;
+        CDNS_QSPI_FLASH_CMD_CTRL_REG_NUM_DATA_BYTES(3);
 
   if (EFI_ERROR (CdnsQspiExecuteCommand (Instance, val))) {
     return EFI_DEVICE_ERROR;
diff --git a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h
index e720937e..eb0afc60 100644
--- a/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h
+++ b/Platform/ARM/N1Sdp/Drivers/CadenceQspiDxe/NorFlash.h
@@ -477,8 +477,13 @@ NorFlashReadID (
   OUT UINT8               JedecId[3]
   );
 
+#define SPINOR_SR_WIP                 BIT0  // Write in progress
+
 #define SPINOR_OP_WREN                0x06  // Write enable
 #define SPINOR_OP_BE_4K               0x20  // Erase 4KiB block
 #define SPINOR_OP_RDID                0x9f  // Read JEDEC ID
+#define SPINOR_OP_RDSR                0x05  // Read status register
+
+#define SPINOR_SR_WIP_POLL_TIMEOUT_MS  1000u // Status Register read timeout
 
 #endif /* NOR_FLASH_DXE_H_ */
