public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "John Chew" <yuinyee.chew@starfivetech.com>
To: <devel@edk2.groups.io>
Cc: John Chew <yuinyee.chew@starfivetech.com>,
	Sunil V L <sunilvl@ventanamicro.com>,
	Leif Lindholm <quic_llindhol@quicinc.com>,
	Michael D Kinney <michael.d.kinney@intel.com>,
	Li Yong <yong.li@intel.com>
Subject: [edk2-devel] [PATCH v3 2/5] StarFive/JH7110Pkg: Add SPI protocol and driver support
Date: Fri, 27 Oct 2023 11:19:03 +0800	[thread overview]
Message-ID: <20231027031906.1814-3-yuinyee.chew@starfivetech.com> (raw)
In-Reply-To: <20231027031906.1814-1-yuinyee.chew@starfivetech.com>

This patch include QSPI driver and Flash driver protocol.
QSPI driver:
	1. Used indirect read/write
	2. Master mode only
	3. Require to setup qspi driver after located protocol
	4. Require to free device if no longer needed
	5. Support command read/write & data read/write
Flash driver:
	1. Require QSPI protocol as prerequisite
	2. Support for flash read/write/update/erase
	3. Require to init flash driver after allocated protocol

Cc: Sunil V L <sunilvl@ventanamicro.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Li Yong <yong.li@intel.com>
Signed-off-by: John Chew <yuinyee.chew@starfivetech.com>
---
 Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.c             | 893 ++++++++++++++++++++
 Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.h             | 188 +++++
 Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.inf           |  52 ++
 Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.c   | 571 +++++++++++++
 Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.h   |  35 +
 Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.inf |  44 +
 Silicon/StarFive/JH7110Pkg/Include/Protocol/Spi.h                               | 163 ++++
 Silicon/StarFive/JH7110Pkg/Include/Protocol/SpiFlash.h                          |  88 ++
 8 files changed, 2034 insertions(+)

diff --git a/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.c b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.c
new file mode 100755
index 000000000000..c345556f8abf
--- /dev/null
+++ b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.c
@@ -0,0 +1,893 @@
+/** @file
+ *
+ *  Copyright (c) 2023, StarFive Technology Co., Ltd. All rights reserved.<BR>
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#include "SpiDxe.h"
+
+SPI_MASTER  *mSpiMasterInstance;
+
+STATIC
+VOID
+SpiControllerEnable (
+  IN UINT32  RegBase
+  )
+{
+  UINT32  Reg;
+
+  Reg  = MmioRead32 (RegBase + SPI_REG_CONFIG);
+  Reg |= SPI_REG_CONFIG_ENABLE;
+  MmioWrite32 (RegBase + SPI_REG_CONFIG, Reg);
+}
+
+STATIC
+VOID
+SpiControllerDisable (
+  IN UINT32  RegBase
+  )
+{
+  UINT32  Reg;
+
+  Reg  = MmioRead32 (RegBase + SPI_REG_CONFIG);
+  Reg &= ~SPI_REG_CONFIG_ENABLE;
+  MmioWrite32 (RegBase + SPI_REG_CONFIG, Reg);
+}
+
+STATIC
+VOID
+SpiWriteSpeed (
+  IN SPI_DEVICE_PARAMS  *Slave,
+  IN UINT32             SclkHz,
+  IN SPI_TIMING_PARAMS  *Timing
+  )
+{
+  UINT32  Reg, Div, RefClkNs, SclkNs;
+  UINT32  Tshsl, Tchsh, Tslch, Tsd2d;
+
+  SpiControllerDisable (Slave->RegBase);
+
+  /* Configure baudrate */
+  Reg  = MmioRead32 (Slave->RegBase + SPI_REG_CONFIG);
+  Reg &= ~(SPI_REG_CONFIG_BAUD_MASK << SPI_REG_CONFIG_BAUD_LSB);
+
+  Div = DIV_ROUND_UP (Timing->RefClkHz, SclkHz * 2) - 1;
+
+  if (Div > SPI_REG_CONFIG_BAUD_MASK) {
+    Div = SPI_REG_CONFIG_BAUD_MASK;
+  }
+
+  DEBUG (
+         (DEBUG_INFO, "%a(): RefClk %dHz sclk %dHz Div 0x%x, actual %dHz\n", __func__,
+          Timing->RefClkHz, SclkHz, Div, Timing->RefClkHz / (2 * (Div + 1)))
+         );
+
+  Reg |= (Div << SPI_REG_CONFIG_BAUD_LSB);
+  MmioWrite32 (Slave->RegBase + SPI_REG_CONFIG, Reg);
+
+  /* Configure delay timing */
+  RefClkNs = DIV_ROUND_UP (1000000000, Timing->RefClkHz);
+  SclkNs = DIV_ROUND_UP (1000000000, SclkHz);
+
+  if (Timing->TshslNs >= SclkNs + RefClkNs) {
+    Timing->TshslNs -= SclkNs + RefClkNs;
+  }
+
+  if (Timing->TchshNs >= SclkNs + 3 * RefClkNs) {
+    Timing->TchshNs -= SclkNs + 3 * RefClkNs;
+  }
+
+  Tshsl = DIV_ROUND_UP (Timing->TshslNs, RefClkNs);
+  Tchsh = DIV_ROUND_UP (Timing->TchshNs, RefClkNs);
+  Tslch = DIV_ROUND_UP (Timing->TslchNs, RefClkNs);
+  Tsd2d = DIV_ROUND_UP (Timing->Tsd2dNs, RefClkNs);
+
+  Reg = ((Tshsl & SPI_REG_DELAY_TSHSL_MASK)
+         << SPI_REG_DELAY_TSHSL_LSB);
+  Reg |= ((Tchsh & SPI_REG_DELAY_TCHSH_MASK)
+          << SPI_REG_DELAY_TCHSH_LSB);
+  Reg |= ((Tslch & SPI_REG_DELAY_TSLCH_MASK)
+          << SPI_REG_DELAY_TSLCH_LSB);
+  Reg |= ((Tsd2d & SPI_REG_DELAY_TSD2D_MASK)
+          << SPI_REG_DELAY_TSD2D_LSB);
+  MmioWrite32 (Slave->RegBase + SPI_REG_DELAY, Reg);
+
+  SpiControllerEnable (Slave->RegBase);
+}
+
+STATIC
+EFI_STATUS
+SpiWaitIdle (
+  IN UINT32  RegBase
+  )
+{
+  BOOLEAN IsIdle;
+  UINT32  Count     = 0;
+  UINT32  TimeoutMs = 5000000;
+
+  do {
+    IsIdle = (BOOLEAN)((MmioRead32(RegBase + SPI_REG_CONFIG) >>
+                        SPI_REG_CONFIG_IDLE_LSB) & 0x1);
+    Count = (IsIdle) ? (Count+1) : 0;
+
+    /*
+     * Make sure the QSPI controller is in really idle
+     * for n period of time before proceed
+     */
+    if (Count >= SPI_POLL_IDLE_RETRY) {
+      return EFI_SUCCESS;
+    }
+
+    gBS->Stall (1);
+  } while (TimeoutMs);
+
+  return EFI_TIMEOUT;
+}
+
+STATIC
+EFI_STATUS
+SpiExecFlashCmd (
+  IN UINT32  RegBase,
+  IN UINT32  Reg
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Retry = SPI_REG_RETRY;
+
+  /* Write the CMDCTRL without start execution */
+  MmioWrite32 (RegBase + SPI_REG_CMDCTRL, Reg);
+  /* Start execute */
+  Reg |= SPI_REG_CMDCTRL_EXECUTE;
+  MmioWrite32 (RegBase + SPI_REG_CMDCTRL, Reg);
+
+  while (Retry--) {
+    Reg = MmioRead32 (RegBase + SPI_REG_CMDCTRL);
+    if ((Reg & SPI_REG_CMDCTRL_INPROGRESS) == 0) {
+      break;
+    }
+    gBS->Stall (1);
+  }
+
+  if (!Retry) {
+    DEBUG ((DEBUG_ERROR, "%a(): flash command execution Timeout\n", __func__));
+    return EFI_TIMEOUT;
+  }
+
+  /* Polling QSPI idle status */
+  Status = SpiWaitIdle (RegBase);
+  if (EFI_ERROR (Status)) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/* For command RDID, RDSR. */
+EFI_STATUS
+SpiCommandRead (
+  IN SPI_MASTER_PROTOCOL *This,
+  IN SPI_DEVICE_PARAMS  *Slave,
+  IN OUT SPI_OP_PARAMS   *Cmds
+  )
+{
+  UINT32      Reg;
+  UINT32      ReadLen;
+  EFI_STATUS  Status;
+  SPI_MASTER *SpiMaster;
+  UINT32      RxLen  = Cmds->Data.NBytes;
+  VOID        *RxBuf = Cmds->Data.Buf.In;
+
+  SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This);
+  if (!EfiAtRuntime ()) {
+    EfiAcquireLock (&SpiMaster->Lock);
+  }
+
+  if ((RxLen > SPI_STIG_DATA_LEN_MAX) || !RxBuf) {
+    DEBUG ((DEBUG_ERROR, "%a(): Invalid input arguments RxLen %d\n", __func__, RxLen));
+    Status = EFI_INVALID_PARAMETER;
+    goto Fail;
+  }
+
+  Reg = Cmds->Cmd.OpCode << SPI_REG_CMDCTRL_OPCODE_LSB;
+  Reg |= (0x1 << SPI_REG_CMDCTRL_RD_EN_LSB);
+
+  /* 0 means 1 byte */
+  Reg |= (((RxLen - 1) & SPI_REG_CMDCTRL_RD_BYTES_MASK)
+          << SPI_REG_CMDCTRL_RD_BYTES_LSB);
+  Status = SpiExecFlashCmd (Slave->RegBase, Reg);
+  if (EFI_ERROR (Status)) {
+    goto Fail;
+  }
+
+  Reg = MmioRead32 (Slave->RegBase + SPI_REG_CMDREADDATALOWER);
+
+  /* Put the read value into rx_buf */
+  ReadLen = (RxLen > 4) ? 4 : RxLen;
+  CopyMem (RxBuf, &Reg, ReadLen);
+  RxBuf += ReadLen;
+
+  if (RxLen > 4) {
+    Reg = MmioRead32 (Slave->RegBase + SPI_REG_CMDREADDATAUPPER);
+
+    ReadLen = RxLen - ReadLen;
+    CopyMem (RxBuf, &Reg, ReadLen);
+  }
+
+  if (!EfiAtRuntime ()) {
+    EfiReleaseLock (&SpiMaster->Lock);
+  }
+  return EFI_SUCCESS;
+
+Fail:
+  if (!EfiAtRuntime ()) {
+    EfiReleaseLock (&SpiMaster->Lock);
+  }
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SpiGetReadSramLevel (
+  IN UINT32   RegBase,
+  OUT UINT16  *SramLvl
+  )
+{
+  UINT32  Reg = MmioRead32 (RegBase + SPI_REG_SDRAMLEVEL);
+  Reg >>= SPI_REG_SDRAMLEVEL_RD_LSB;
+  *SramLvl = (UINT16)(Reg & SPI_REG_SDRAMLEVEL_RD_MASK);
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SpiWaitForData (
+  IN UINT32  RegBase,
+  UINT16    *SramLvl
+  )
+{
+  UINT32  Timeout = 10000;
+
+  while (Timeout--) {
+    SpiGetReadSramLevel (RegBase, SramLvl);
+    if (SramLvl != 0) {
+      return EFI_SUCCESS;
+    }
+    gBS->Stall (1);
+  }
+
+  return EFI_TIMEOUT;
+}
+
+STATIC
+EFI_STATUS
+SpiWaitForBitLe32 (
+  IN INT32          Reg,
+  IN CONST UINT32   Mask,
+  IN CONST BOOLEAN  Set,
+  IN CONST UINT32   TimeoutMs
+  )
+{
+  UINT32  Val;
+  UINTN   Start = TimeoutMs*1000;
+
+  while(1) {
+    Val = MmioRead32 (Reg);
+
+    if (!Set) {
+      Val = ~Val;
+    }
+
+    if ((Val & Mask) == Mask) {
+      return EFI_SUCCESS;
+    }
+
+    if (Start == 0) {
+      break;
+    } else {
+      Start--;
+    }
+
+    gBS->Stall (1);
+  }
+
+  DEBUG ((DEBUG_ERROR, "Timeout (Reg=%lx Mask=%x wait_set=%d)\n", Reg, Mask, Set));
+
+  return EFI_TIMEOUT;
+}
+
+STATIC
+VOID
+SpiReadByte (
+  IN VOID  *Addr,
+  IN VOID  *Data,
+  IN UINT16 ByteLen
+  )
+{
+  UINT8  *AddrPtr;
+  UINT8  *DataPtr;
+
+  AddrPtr  = (UINT8 *)Addr;
+  DataPtr = (UINT8 *)Data;
+
+  while (ByteLen) {
+    *DataPtr = *AddrPtr;
+    DataPtr++;
+    ByteLen--;
+  }
+}
+
+STATIC
+VOID
+SpiReadLong (
+  VOID    *Addr,
+  VOID    *Data,
+  UINT16  LongLen
+  )
+{
+  UINT32  *AddrPtr;
+  UINT32  *DataPtr;
+
+  AddrPtr = (UINT32 *)Addr;
+  DataPtr = (UINT32 *)Data;
+
+  while (LongLen) {
+    *DataPtr = *AddrPtr;
+    DataPtr++;
+    LongLen--;
+  }
+}
+
+EFI_STATUS
+SpiDataRead (
+  IN SPI_MASTER_PROTOCOL *This,
+  IN SPI_DEVICE_PARAMS  *Slave,
+  IN OUT SPI_OP_PARAMS  *Cmds
+  )
+{
+  SPI_MASTER  *SpiMaster;
+  UINT8       *RxBuf      = Cmds->Data.Buf.In;
+  UINT32      Remaining   = Cmds->Data.NBytes;
+  UINT16      BytesToRead = 0;
+  EFI_STATUS  Status;
+  UINT32      Reg;
+
+  SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This);
+  if (!EfiAtRuntime ()) {
+    EfiAcquireLock (&SpiMaster->Lock);
+  }
+
+  if (!Cmds->Addr.NBytes) {
+    Status = EFI_ABORTED;
+    goto Fail;
+  }
+
+  /* Setup the indirect trigger start address */
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTRDSTARTADDR, Cmds->Addr.Val);
+
+  /* Register command */
+  Reg  = Cmds->Cmd.OpCode << SPI_REG_RD_INSTR_OPCODE_LSB;
+  MmioWrite32 (Slave->RegBase + SPI_REG_RD_INSTR, Reg);
+
+  /* Set device size */
+  Reg  = MmioRead32 (Slave->RegBase + SPI_REG_SIZE);
+  Reg &= ~SPI_REG_SIZE_ADDRESS_MASK;
+  Reg |= (Cmds->Addr.NBytes - 1);
+  MmioWrite32 (Slave->RegBase + SPI_REG_SIZE, Reg);
+
+  /* Setup indirect read bytes */
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTRDBYTES, Remaining);
+
+  /* Start the indirect read transfer */
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTRD, SPI_REG_INDIRECTRD_START);
+
+  while (Remaining > 0) {
+    Status = SpiWaitForData (Slave->RegBase, &BytesToRead);
+    if (EFI_ERROR(Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Indirect write timed out\n", __func__));
+      goto Fail;
+    }
+
+    while (BytesToRead != 0) {
+      BytesToRead *= Slave->FifoWidth;
+      if (BytesToRead > Remaining) {
+        BytesToRead = Remaining;
+      }
+
+      if (((UINTN)RxBuf % 4) || (BytesToRead % 4)) {
+        SpiReadByte (Slave->AhbBase, RxBuf, BytesToRead);
+      } else {
+        SpiReadLong (Slave->AhbBase, RxBuf, BytesToRead >> 2);
+      }
+
+      RxBuf        += BytesToRead;
+      Remaining    -= BytesToRead;
+      SpiGetReadSramLevel (Slave->RegBase, &BytesToRead);
+    }
+  }
+
+  /* Check indirect done status */
+  Status = SpiWaitForBitLe32 (
+                              Slave->RegBase + SPI_REG_INDIRECTRD,
+                              SPI_REG_INDIRECTRD_DONE,
+                              1,
+                              10
+                             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Indirect read completion error\n"));
+    goto Fail;
+  }
+
+  /* Clear indirect completion status */
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTRD, SPI_REG_INDIRECTRD_DONE);
+
+  if (!EfiAtRuntime ()) {
+    EfiReleaseLock (&SpiMaster->Lock);
+  }
+  return EFI_SUCCESS;
+
+Fail:
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTRD, SPI_REG_INDIRECTRD_CANCEL);
+  if (!EfiAtRuntime ()) {
+    EfiReleaseLock (&SpiMaster->Lock);
+  }
+  return EFI_ABORTED;
+}
+
+/* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */
+EFI_STATUS
+SpiCommandWrite (
+  IN SPI_MASTER_PROTOCOL *This,
+  IN SPI_DEVICE_PARAMS  *Slave,
+  IN OUT SPI_OP_PARAMS  *Cmds
+  )
+{
+  UINT32      Reg;
+  UINT32      WriteData;
+  UINT32      WriteLen;
+  SPI_MASTER  *SpiMaster;
+  UINT32      TxLen   = Cmds->Data.NBytes;
+  CONST VOID  *TxBuf  = Cmds->Data.Buf.Out;
+  UINT32      Addr;
+  EFI_STATUS  Status;
+
+  SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This);
+  if (!EfiAtRuntime ()) {
+    EfiAcquireLock (&SpiMaster->Lock);
+  }
+
+  /* Reorder address to SPI bus order if only transferring address */
+  if (!TxLen) {
+    Addr = SwapBytes32 (Cmds->Addr.Val);
+    if (Cmds->Addr.NBytes == 3) {
+      Addr >>= 8;
+    }
+
+    TxBuf = &Addr;
+    TxLen = Cmds->Addr.NBytes;
+  }
+
+  if (TxLen > SPI_STIG_DATA_LEN_MAX) {
+    DEBUG ((DEBUG_ERROR, "QSPI: Invalid input arguments TxLen %d\n", TxLen));
+    Status = EFI_INVALID_PARAMETER;
+    goto Fail;
+  }
+
+  Reg = Cmds->Cmd.OpCode << SPI_REG_CMDCTRL_OPCODE_LSB;
+
+  if (TxLen) {
+    Reg |= (0x1 << SPI_REG_CMDCTRL_WR_EN_LSB);
+    Reg |= ((TxLen - 1) & SPI_REG_CMDCTRL_WR_BYTES_MASK)
+           << SPI_REG_CMDCTRL_WR_BYTES_LSB;
+
+    WriteLen = TxLen > 4 ? 4 : TxLen;
+    CopyMem (&WriteData, TxBuf, WriteLen);
+    MmioWrite32 (Slave->RegBase + SPI_REG_CMDWRITEDATALOWER, WriteData);
+
+    if (TxLen > 4) {
+      TxBuf   += WriteLen;
+      WriteLen = TxLen - WriteLen;
+      CopyMem (&WriteData, TxBuf, WriteLen);
+      MmioWrite32 (Slave->RegBase + SPI_REG_CMDWRITEDATAUPPER, WriteData);
+    }
+  }
+
+  Status = SpiExecFlashCmd (Slave->RegBase, Reg);
+  if (EFI_ERROR (Status)) {
+    goto Fail;
+  }
+
+  if (!EfiAtRuntime ()) {
+    EfiReleaseLock (&SpiMaster->Lock);
+  }
+  return EFI_SUCCESS;
+
+Fail:
+  if (!EfiAtRuntime ()) {
+    EfiReleaseLock (&SpiMaster->Lock);
+  }
+  return Status;
+}
+
+STATIC
+VOID
+SpiDelayNanoSec (
+  IN UINTN  nsec
+  )
+{
+  UINT32  Timeout = DIV_ROUND_UP (nsec, 1000);
+
+  do {
+    Timeout--;
+    gBS->Stall (1);
+  } while (Timeout);
+}
+
+STATIC
+VOID
+SpiWriteLong (
+  IN VOID        *Addr,
+  IN CONST VOID  *Data,
+  IN INTN        LongLen
+  )
+{
+  UINT32  *AddrPtr;
+  UINT32  *DataPtr;
+
+  AddrPtr  = (UINT32 *)Addr;
+  DataPtr = (UINT32 *)Data;
+
+  while (LongLen) {
+    *AddrPtr = *DataPtr;
+    DataPtr++;
+    LongLen--;
+  }
+}
+
+STATIC
+VOID
+SpiWriteByte (
+  IN VOID        *Addr,
+  IN CONST VOID  *Data,
+  IN INTN        ByteLen
+  )
+{
+  UINT8  *AddrPtr;
+  UINT8  *DataPtr;
+
+  AddrPtr  = (UINT8 *)Addr;
+  DataPtr  = (UINT8 *)Data;
+
+  while (ByteLen) {
+    *AddrPtr = *DataPtr;
+    DataPtr++;
+    ByteLen--;
+  }
+}
+
+EFI_STATUS
+SpiDataWrite (
+  IN SPI_MASTER_PROTOCOL *This,
+  IN SPI_DEVICE_PARAMS   *Slave,
+  IN OUT SPI_OP_PARAMS  *Cmds
+  )
+{
+  UINT32       Reg;
+  SPI_MASTER   *SpiMaster;
+  UINT32       PageSize  = Slave->Info->PageSize;
+  UINT32       Remaining = Cmds->Data.NBytes;
+  CONST UINT8  *TxBuf = Cmds->Data.Buf.Out;
+  UINT32       WriteBytes;
+  EFI_STATUS   Status;
+
+  SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This);
+  if (!EfiAtRuntime ()) {
+    EfiAcquireLock (&SpiMaster->Lock);
+  }
+
+  if (!Cmds->Addr.NBytes) {
+    return EFI_ABORTED;
+  }
+
+  /* Write opcode to write instruction register */
+  Reg  = Cmds->Cmd.OpCode << SPI_REG_WR_INSTR_OPCODE_LSB;
+  MmioWrite32 (Slave->RegBase + SPI_REG_WR_INSTR, Reg);
+
+  /* Set buffer address */
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTWRSTARTADDR, Cmds->Addr.Val);
+
+  /* Configure device size */
+  Reg  = MmioRead32 (Slave->RegBase + SPI_REG_SIZE);
+  Reg &= ~SPI_REG_SIZE_ADDRESS_MASK;
+  Reg |= (Cmds->Addr.NBytes - 1);
+  MmioWrite32 (Slave->RegBase + SPI_REG_SIZE, Reg);
+
+  /* Configure the indirect read transfer bytes */
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTWRBYTES, Remaining);
+
+  /* Start the indirect write transfer */
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTWR, SPI_REG_INDIRECTWR_START);
+
+  /* Delay is required for QSPI module to synchronized internally */
+  SpiDelayNanoSec (Slave->WriteDelay);
+
+  while (Remaining > 0) {
+    WriteBytes = Remaining > PageSize ? PageSize : Remaining;
+    SpiWriteLong (Slave->AhbBase, TxBuf, WriteBytes >> 2);
+    if (WriteBytes % 4) {
+      SpiWriteByte (
+                    Slave->AhbBase,
+                    TxBuf + ROUND_DOWN (WriteBytes, 4),
+                    WriteBytes % 4
+                    );
+    }
+
+    Status = SpiWaitForBitLe32 (
+                                Slave->RegBase + SPI_REG_SDRAMLEVEL,
+                                SPI_REG_SDRAMLEVEL_WR_MASK <<
+                                SPI_REG_SDRAMLEVEL_WR_LSB,
+                                0,
+                                10
+                                );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Indirect write timed out (%d)\n", __func__, Status));
+      goto FailWrite;
+    }
+
+    TxBuf  += WriteBytes;
+    Remaining -= WriteBytes;
+  }
+
+  /* Check indirect done status */
+  Status = SpiWaitForBitLe32 (
+                              Slave->RegBase + SPI_REG_INDIRECTWR,
+                              SPI_REG_INDIRECTWR_DONE,
+                              1,
+                              10
+                             );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Indirect write completion error (%d)\n", Status));
+    goto FailWrite;
+  }
+
+  /* Clear indirect completion status */
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTWR, SPI_REG_INDIRECTWR_DONE);
+  if (!EfiAtRuntime ()) {
+    EfiReleaseLock (&SpiMaster->Lock);
+  }
+  return EFI_SUCCESS;
+
+FailWrite:
+  MmioWrite32 (Slave->RegBase + SPI_REG_INDIRECTWR, SPI_REG_INDIRECTWR_CANCEL);
+  if (!EfiAtRuntime ()) {
+    EfiReleaseLock (&SpiMaster->Lock);
+  }
+  return Status;
+}
+
+STATIC
+VOID
+SpiConfigGetDataCapture (
+  IN UINT32  RegBase,
+  IN UINT32  ByPass,
+  IN UINT32  Delay
+  )
+{
+  UINT32  Reg;
+
+  SpiControllerDisable (RegBase);
+
+  Reg = MmioRead32 (RegBase + SPI_REG_RD_DATA_CAPTURE);
+
+  if (ByPass) {
+    Reg |= SPI_REG_RD_DATA_CAPTURE_BYPASS;
+  } else {
+    Reg &= ~SPI_REG_RD_DATA_CAPTURE_BYPASS;
+  }
+
+  Reg &= ~(SPI_REG_RD_DATA_CAPTURE_DELAY_MASK
+           << SPI_REG_RD_DATA_CAPTURE_DELAY_LSB);
+
+  Reg |= (Delay & SPI_REG_RD_DATA_CAPTURE_DELAY_MASK)
+         << SPI_REG_RD_DATA_CAPTURE_DELAY_LSB;
+
+  MmioWrite32 (RegBase + SPI_REG_RD_DATA_CAPTURE, Reg);
+
+  SpiControllerEnable (RegBase);
+}
+
+STATIC
+EFI_STATUS
+SpiSpeedCalibration (
+  IN SPI_MASTER_PROTOCOL *This,
+  IN SPI_DEVICE_PARAMS  *Slave,
+  IN SPI_TIMING_PARAMS  *Timing
+  )
+{
+  UINT8         IdLen = 3;
+  UINT32        IdInit = 0, IdCali = 0;
+  INTN          RangeLow = -1, RangeHigh = -1;
+  SPI_OP_PARAMS  CmdsInitialId = SPI_READID_OP ((UINT8 *)&IdInit, IdLen);
+  SPI_OP_PARAMS  CmdsCalibrateId = SPI_READID_OP ((UINT8 *)&IdCali, IdLen);
+  EFI_STATUS    Status;
+
+  /* Start calibration with slowest clock speed at 1 MHz */
+  SpiWriteSpeed (Slave, SPI_MIN_HZ, Timing);
+
+  /* Set the read data capture delay register to 0 */
+  SpiConfigGetDataCapture (Slave->RegBase, 1, 0);
+
+  /* Get flash ID value as reference */
+  Status = SpiCommandRead (This, Slave, &CmdsInitialId);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Spi: Calibration failed (read id)\n"));
+    return EFI_ABORTED;
+  }
+
+  /* Use the input speed */
+  SpiWriteSpeed (Slave, SPI_MAX_HZ, Timing);
+
+  /* Find high and low range */
+  for (UINT8 i = 0; i < SPI_READ_CAPTURE_MAX_DELAY; i++) {
+    /* Change the read data capture delay register */
+    SpiConfigGetDataCapture (Slave->RegBase, 1, i);
+
+    /* Read flash ID for comparison later */
+    Status = SpiCommandRead (This, Slave, &CmdsCalibrateId);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "Spi: Calibration failed (read)\n"));
+      return EFI_ABORTED;
+    }
+
+    /* Verify low range */
+    if ((RangeLow == -1) && (IdCali == IdInit)) {
+      RangeLow = i;
+      continue;
+    }
+
+    /* Verify high range */
+    if ((RangeLow != -1) && (IdCali != IdInit)) {
+      RangeHigh = i - 1;
+      break;
+    }
+
+    RangeHigh = i;
+  }
+
+  if (RangeLow == -1) {
+    DEBUG ((DEBUG_ERROR, "Spi: Calibration failed\n"));
+    return EFI_ABORTED;
+  }
+
+  /*
+  * Set the final value for read data capture delay register based
+  * on the calibrated value
+  */
+  SpiConfigGetDataCapture (Slave->RegBase, 1, (RangeHigh + RangeLow) / 2);
+  DEBUG (
+         (DEBUG_INFO, "Spi: Read data capture delay calibrated to %d (%d - %d)\n",
+          (RangeHigh + RangeLow) / 2, RangeLow, RangeHigh)
+         );
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiSetupDevice (
+  IN SPI_MASTER_PROTOCOL *This,
+  OUT SPI_DEVICE_PARAMS  *Slave
+  )
+{
+  SPI_TIMING_PARAMS  *Timing;
+  EFI_STATUS  Status;
+
+  if (!Slave) {
+    Slave = AllocateZeroPool (sizeof (SPI_DEVICE_PARAMS));
+    if (Slave == NULL) {
+      DEBUG ((DEBUG_ERROR, "%a(): Cannot allocate memory Slave\n", __func__));
+      Status = EFI_OUT_OF_RESOURCES;
+      goto Fail;
+    }
+  }
+
+  if (!Slave->Info) {
+    Slave->Info = AllocateZeroPool (sizeof (NOR_FLASH_INFO));
+    if (Slave->Info == NULL) {
+      DEBUG ((DEBUG_ERROR, "%a(): Cannot allocate memory Slave->Info\n", __func__));
+      Status = EFI_OUT_OF_RESOURCES;
+      goto Fail;
+    }
+  }
+
+  Timing = AllocateZeroPool (sizeof (SPI_TIMING_PARAMS));
+  if (Timing == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a(): Cannot allocate memory Timing\n", __func__));
+    FreePool(Slave);
+    Status = EFI_OUT_OF_RESOURCES;
+    goto Fail;
+  }
+
+  Slave->RegBase    = PcdGet32 (PcdSpiFlashRegBase);
+  Slave->AhbBase    = (VOID *)(UINTN)PcdGet64 (PcdSpiFlashAhbBase);
+  Slave->FifoWidth  = PcdGet8 (PcdSpiFlashFifoWidth);
+  Timing->RefClkHz  = PcdGet32 (PcdSpiFlashRefClkHz);
+  Timing->TshslNs   = PcdGet32 (PcdSpiFlashTshslNs);
+  Timing->Tsd2dNs   = PcdGet32 (PcdSpiFlashTsd2dNs);
+  Timing->TchshNs   = PcdGet32 (PcdSpiFlashTchshNs);
+  Timing->TslchNs   = PcdGet32 (PcdSpiFlashTslchNs);
+
+  Slave->WriteDelay = 50 * DIV_ROUND_UP (NSEC_PER_SEC, Timing->RefClkHz);
+
+  Status = SpiSpeedCalibration (This, Slave, Timing);
+  if (EFI_ERROR (Status)) {
+    goto Fail;
+  }
+
+  FreePool(Timing);
+  return EFI_SUCCESS;
+
+Fail:
+  FreePool(Slave);
+  FreePool(Timing);
+  return Status;
+}
+
+EFI_STATUS
+SpiFreeDevice (
+  IN SPI_DEVICE_PARAMS  *Slave
+  )
+{
+  FreePool (Slave);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiMasterInitProtocol (
+  IN SPI_MASTER_PROTOCOL  *SpiMasterProtocol
+  )
+{
+  SpiMasterProtocol->SetupDevice = SpiSetupDevice;
+  SpiMasterProtocol->FreeDevice  = SpiFreeDevice;
+  SpiMasterProtocol->CmdRead     = SpiCommandRead;
+  SpiMasterProtocol->DataRead    = SpiDataRead;
+  SpiMasterProtocol->CmdWrite    = SpiCommandWrite;
+  SpiMasterProtocol->DataWrite   = SpiDataWrite;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiEntryPoint (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  mSpiMasterInstance = AllocateRuntimeZeroPool (sizeof (SPI_MASTER));
+  if (mSpiMasterInstance == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  EfiInitializeLock (&mSpiMasterInstance->Lock, TPL_NOTIFY);
+
+  SpiMasterInitProtocol (&mSpiMasterInstance->SpiMasterProtocol);
+
+  mSpiMasterInstance->Signature = SPI_MASTER_SIGNATURE;
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                                                   &(mSpiMasterInstance->Handle),
+                                                   &gJH7110SpiMasterProtocolGuid,
+                                                   &(mSpiMasterInstance->SpiMasterProtocol),
+                                                   NULL
+                                                   );
+  if (EFI_ERROR (Status)) {
+    FreePool (mSpiMasterInstance);
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.h b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.h
new file mode 100644
index 000000000000..804ee29c5ff1
--- /dev/null
+++ b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.h
@@ -0,0 +1,188 @@
+/** @file
+ *
+ *  Copyright (c) 2023, StarFive Technology Co., Ltd. All rights reserved.<BR>
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#ifndef __SPI_DXE_H__
+#define __SPI_DXE_H__
+
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <Protocol/Spi.h>
+
+#define SPI_MASTER_SIGNATURE                      SIGNATURE_32 ('M', 'S', 'P', 'I')
+#define SPI_MASTER_FROM_SPI_MASTER_PROTOCOL(a)  CR (a, SPI_MASTER, SpiMasterProtocol, SPI_MASTER_SIGNATURE)
+
+#ifndef BIT
+#define BIT(nr)  (1 << (nr))
+#endif
+
+#define DIV_ROUND_UP(n, d)  (((n) + (d) - 1) / (d))
+#define ROUND_DOWN(x, y)  (\
+{                         \
+  typeof(x) __x = (x);    \
+  __x - (__x % (y));      \
+}                         \
+)
+#define NSEC_PER_SEC  1000000000L
+
+/* Configs */
+#define SPI_READ_CAPTURE_MAX_DELAY  16
+#define SPI_REG_RETRY               10000
+#define SPI_POLL_IDLE_RETRY         3
+#define SPI_STIG_DATA_LEN_MAX       8
+#define SPI_MIN_HZ                  1000000
+#define SPI_MAX_HZ                  100000000
+
+/*
+* QSPI controller's config and status register (offset from QSPI_BASE)
+*/
+#define SPI_REG_CONFIG                  0x00
+#define SPI_REG_CONFIG_ENABLE           BIT(0)
+#define SPI_REG_CONFIG_CLK_POL          BIT(1)
+#define SPI_REG_CONFIG_CLK_PHA          BIT(2)
+#define SPI_REG_CONFIG_DIRECT           BIT(7)
+#define SPI_REG_CONFIG_DECODE           BIT(9)
+#define SPI_REG_CONFIG_XIP_IMM          BIT(18)
+#define SPI_REG_CONFIG_CHIPSELECT_LSB   10
+#define SPI_REG_CONFIG_BAUD_LSB         19
+#define SPI_REG_CONFIG_DTR_PROTO        BIT(24)
+#define SPI_REG_CONFIG_DUAL_OPCODE      BIT(30)
+#define SPI_REG_CONFIG_IDLE_LSB         31
+#define SPI_REG_CONFIG_CHIPSELECT_MASK  0xF
+#define SPI_REG_CONFIG_BAUD_MASK        0xF
+
+#define SPI_REG_RD_INSTR                  0x04
+#define SPI_REG_RD_INSTR_OPCODE_LSB       0
+#define SPI_REG_RD_INSTR_TYPE_INSTR_LSB   8
+#define SPI_REG_RD_INSTR_TYPE_ADDR_LSB    12
+#define SPI_REG_RD_INSTR_TYPE_DATA_LSB    16
+#define SPI_REG_RD_INSTR_MODE_EN_LSB      20
+#define SPI_REG_RD_INSTR_DUMMY_LSB        24
+#define SPI_REG_RD_INSTR_TYPE_INSTR_MASK  0x3
+#define SPI_REG_RD_INSTR_TYPE_ADDR_MASK   0x3
+#define SPI_REG_RD_INSTR_TYPE_DATA_MASK   0x3
+#define SPI_REG_RD_INSTR_DUMMY_MASK       0x1F
+
+#define SPI_REG_WR_INSTR                0x08
+#define SPI_REG_WR_INSTR_OPCODE_LSB     0
+#define SPI_REG_WR_INSTR_TYPE_ADDR_LSB  12
+#define SPI_REG_WR_INSTR_TYPE_DATA_LSB  16
+
+#define SPI_REG_DELAY             0x0C
+#define SPI_REG_DELAY_TSLCH_LSB   0
+#define SPI_REG_DELAY_TCHSH_LSB   8
+#define SPI_REG_DELAY_TSD2D_LSB   16
+#define SPI_REG_DELAY_TSHSL_LSB   24
+#define SPI_REG_DELAY_TSLCH_MASK  0xFF
+#define SPI_REG_DELAY_TCHSH_MASK  0xFF
+#define SPI_REG_DELAY_TSD2D_MASK  0xFF
+#define SPI_REG_DELAY_TSHSL_MASK  0xFF
+
+#define SPI_REG_RD_DATA_CAPTURE             0x10
+#define SPI_REG_RD_DATA_CAPTURE_BYPASS      BIT(0)
+#define SPI_REG_RD_DATA_CAPTURE_DELAY_LSB   1
+#define SPI_REG_RD_DATA_CAPTURE_DELAY_MASK  0xF
+
+#define SPI_REG_SIZE               0x14
+#define SPI_REG_SIZE_ADDRESS_LSB   0
+#define SPI_REG_SIZE_PAGE_LSB      4
+#define SPI_REG_SIZE_BLOCK_LSB     16
+#define SPI_REG_SIZE_ADDRESS_MASK  0xF
+#define SPI_REG_SIZE_PAGE_MASK     0xFFF
+#define SPI_REG_SIZE_BLOCK_MASK    0x3F
+
+#define SPI_REG_SRAMPARTITION    0x18
+#define SPI_REG_INDIRECTTRIGGER  0x1C
+
+#define SPI_REG_REMAP     0x24
+#define SPI_REG_MODE_BIT  0x28
+
+#define SPI_REG_SDRAMLEVEL          0x2C
+#define SPI_REG_SDRAMLEVEL_RD_LSB   0
+#define SPI_REG_SDRAMLEVEL_WR_LSB   16
+#define SPI_REG_SDRAMLEVEL_RD_MASK  0xFFFF
+#define SPI_REG_SDRAMLEVEL_WR_MASK  0xFFFF
+
+#define SPI_REG_WR_COMPLETION_CTRL    0x38
+#define SPI_REG_WR_DISABLE_AUTO_POLL  BIT(14)
+
+#define SPI_REG_IRQSTATUS  0x40
+#define SPI_REG_IRQMASK    0x44
+
+#define SPI_REG_INDIRECTRD             0x60
+#define SPI_REG_INDIRECTRD_START       BIT(0)
+#define SPI_REG_INDIRECTRD_CANCEL      BIT(1)
+#define SPI_REG_INDIRECTRD_INPROGRESS  BIT(2)
+#define SPI_REG_INDIRECTRD_DONE        BIT(5)
+
+#define SPI_REG_INDIRECTRDWATERMARK  0x64
+#define SPI_REG_INDIRECTRDSTARTADDR  0x68
+#define SPI_REG_INDIRECTRDBYTES      0x6C
+
+#define SPI_REG_CMDCTRL                 0x90
+#define SPI_REG_CMDCTRL_EXECUTE         BIT(0)
+#define SPI_REG_CMDCTRL_INPROGRESS      BIT(1)
+#define SPI_REG_CMDCTRL_DUMMY_LSB       7
+#define SPI_REG_CMDCTRL_WR_BYTES_LSB    12
+#define SPI_REG_CMDCTRL_WR_EN_LSB       15
+#define SPI_REG_CMDCTRL_ADD_BYTES_LSB   16
+#define SPI_REG_CMDCTRL_ADDR_EN_LSB     19
+#define SPI_REG_CMDCTRL_RD_BYTES_LSB    20
+#define SPI_REG_CMDCTRL_RD_EN_LSB       23
+#define SPI_REG_CMDCTRL_OPCODE_LSB      24
+#define SPI_REG_CMDCTRL_DUMMY_MASK      0x1F
+#define SPI_REG_CMDCTRL_WR_BYTES_MASK   0x7
+#define SPI_REG_CMDCTRL_ADD_BYTES_MASK  0x3
+#define SPI_REG_CMDCTRL_RD_BYTES_MASK   0x7
+#define SPI_REG_CMDCTRL_OPCODE_MASK     0xFF
+
+#define SPI_REG_INDIRECTWR             0x70
+#define SPI_REG_INDIRECTWR_START       BIT(0)
+#define SPI_REG_INDIRECTWR_CANCEL      BIT(1)
+#define SPI_REG_INDIRECTWR_INPROGRESS  BIT(2)
+#define SPI_REG_INDIRECTWR_DONE        BIT(5)
+
+#define SPI_REG_INDIRECTWRWATERMARK  0x74
+#define SPI_REG_INDIRECTWRSTARTADDR  0x78
+#define SPI_REG_INDIRECTWRBYTES      0x7C
+
+#define SPI_REG_CMDADDRESS         0x94
+#define SPI_REG_CMDREADDATALOWER   0xA0
+#define SPI_REG_CMDREADDATAUPPER   0xA4
+#define SPI_REG_CMDWRITEDATALOWER  0xA8
+#define SPI_REG_CMDWRITEDATAUPPER  0xAC
+
+#define SPI_REG_OP_EXT_LOWER      0xE0
+#define SPI_REG_OP_EXT_READ_LSB   24
+#define SPI_REG_OP_EXT_WRITE_LSB  16
+#define SPI_REG_OP_EXT_STIG_LSB   0
+
+typedef struct {
+  SPI_MASTER_PROTOCOL  SpiMasterProtocol;
+  UINTN                Signature;
+  EFI_HANDLE           Handle;
+  EFI_LOCK             Lock;
+} SPI_MASTER;
+
+typedef struct {
+  UINT32  RefClkHz;
+  UINT32  TshslNs;
+  UINT32  TchshNs;
+  UINT32  TslchNs;
+  UINT32  Tsd2dNs;
+}SPI_TIMING_PARAMS;
+
+#endif //__SPI_DXE_H__
diff --git a/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.inf b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.inf
new file mode 100644
index 000000000000..ed3f639346b2
--- /dev/null
+++ b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiDxe/SpiDxe.inf
@@ -0,0 +1,52 @@
+## @file
+#
+#  Copyright (c) 2023, StarFive Technology Co., Ltd. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SpiDxe
+  FILE_GUID                      = 2FBD9E55-9BC7-4EEF-BF93-0D5582FE647B
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = SpiEntryPoint
+
+[Sources]
+  SpiDxe.c
+  SpiDxe.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  Silicon/StarFive/JH7110Pkg/JH7110Pkg.dec
+
+[LibraryClasses]
+  DebugLib
+  DxeServicesTableLib
+  IoLib
+  MemoryAllocationLib
+  NorFlashInfoLib
+  TimerLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+  UefiRuntimeLib
+
+[FixedPcd]
+  gJH7110TokenSpaceGuid.PcdSpiFlashRegBase
+  gJH7110TokenSpaceGuid.PcdSpiFlashAhbBase
+  gJH7110TokenSpaceGuid.PcdSpiFlashFifoWidth
+  gJH7110TokenSpaceGuid.PcdSpiFlashRefClkHz
+  gJH7110TokenSpaceGuid.PcdSpiFlashTshslNs
+  gJH7110TokenSpaceGuid.PcdSpiFlashTsd2dNs
+  gJH7110TokenSpaceGuid.PcdSpiFlashTchshNs
+  gJH7110TokenSpaceGuid.PcdSpiFlashTslchNs
+
+[Protocols]
+  gJH7110SpiMasterProtocolGuid
+
+[Depex]
+  TRUE
diff --git a/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.c b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.c
new file mode 100755
index 000000000000..cd508e53eb06
--- /dev/null
+++ b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.c
@@ -0,0 +1,571 @@
+/** @file
+ *
+ *  Copyright (c) 2023, StarFive Technology Co., Ltd. All rights reserved.<BR>
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#include <Library/NorFlashInfoLib.h>
+#include "SpiFlashDxe.h"
+
+SPI_MASTER_PROTOCOL  *SpiMasterProtocol;
+SPI_FLASH_INSTANCE   *mSpiFlashInstance;
+
+STATIC
+EFI_STATUS
+SpiFlashWriteEnableCmd (
+  IN  SPI_DEVICE_PARAMS  *Slave
+  )
+{
+  EFI_STATUS  Status;
+  SPI_OP_PARAMS  Op = SPI_WRITE_EN_OP();
+
+  /* Send write enable command */
+  Status = SpiMasterProtocol->CmdWrite (SpiMasterProtocol, Slave, &Op);
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SpiFlashWriteDisableCmd (
+  IN  SPI_DEVICE_PARAMS  *Slave
+  )
+{
+  EFI_STATUS  Status;
+  SPI_OP_PARAMS  Op = SPI_WRITE_DIS_OP();
+
+  /* Send write disable command */
+  Status = SpiMasterProtocol->CmdWrite (SpiMasterProtocol, Slave, &Op);
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SpiFlashPoll (
+  IN SPI_DEVICE_PARAMS *Slave
+)
+{
+  EFI_STATUS  Status;
+  UINT16      State;
+  UINT32      Counter     = 0xFFFFF;
+  UINT8       ReadLength  = 2;
+
+  SPI_OP_PARAMS  OpRdSts = SPI_OP (
+                                  SPI_OP_CMD (SPI_CMD_READ_STATUS),
+                                  SPI_OP_NO_ADDR,
+                                  SPI_OP_NO_DUMMY,
+                                  SPI_OP_DATA_IN (ReadLength, (VOID *)&State)
+                                  );
+
+  Status = SpiMasterProtocol->CmdRead (SpiMasterProtocol, Slave, &OpRdSts);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Spi error while reading status\n", __func__));
+    return Status;
+  }
+
+  do {
+    Status = SpiMasterProtocol->CmdRead (SpiMasterProtocol, Slave, &OpRdSts);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Spi error while reading status\n", __func__));
+      return Status;
+    }
+
+    Counter--;
+    if (!(State & STATUS_REG_POLL_WIP_MSK)) {
+      break;
+    }
+  } while (Counter > 0);
+
+  if (Counter == 0) {
+    DEBUG ((DEBUG_ERROR, "%a(): Timeout while writing to spi flash\n", __func__));
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashErase (
+  IN SPI_DEVICE_PARAMS  *Slave,
+  IN UINT32             Offset,
+  IN UINTN              Length
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      EraseAddr;
+  UINTN      EraseSize;
+  UINT8       EraseCmd;
+
+  if (Slave->Info->Flags & NOR_FLASH_ERASE_4K) {
+    EraseCmd  = SPI_CMD_ERASE_4K;
+    EraseSize = SIZE_4KB;
+  } else if (Slave->Info->Flags & NOR_FLASH_ERASE_32K) {
+    EraseCmd  = SPI_CMD_ERASE_32K;
+    EraseSize = SIZE_32KB;
+  } else {
+    EraseCmd  = SPI_CMD_ERASE_64K;
+    EraseSize = Slave->Info->SectorSize;
+  }
+
+  /* Verify input parameters */
+  if (Offset % EraseSize || Length % EraseSize) {
+    DEBUG (
+           (DEBUG_ERROR, "%a(): Either erase offset or length "
+                         "is not multiple of erase size\n, __func__")
+           );
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status = SpiFlashWriteEnableCmd (Slave);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Error while setting write_enable\n", __func__));
+    return Status;
+  }
+
+  while (Length) {
+    EraseAddr = Offset;
+
+    SPI_OP_PARAMS  OpErase = SPI_OP (
+                                        SPI_OP_CMD (EraseCmd),
+                                        SPI_OP_ADDR (3, EraseAddr),
+                                        SPI_OP_NO_DUMMY,
+                                        SPI_OP_NO_DATA
+                                        );
+
+    Status = SpiMasterProtocol->CmdWrite (SpiMasterProtocol, Slave, &OpErase);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Spi erase fail\n", __func__));
+      return Status;
+    }
+
+    Status = SpiFlashPoll(Slave);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Offset += EraseSize;
+    Length -= EraseSize;
+  }
+
+  Status = SpiFlashWriteDisableCmd (Slave);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Error while setting write_disable\n", __func__));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashRead (
+  IN SPI_DEVICE_PARAMS *Slave,
+  IN UINT32 Offset,
+  IN UINTN Length,
+  OUT VOID *Buffer
+  )
+{
+  EFI_STATUS  Status;
+  UINTN      ReadAddr, ReadLength, RemainLength;
+  UINT32      FlashSize;
+
+  FlashSize = Slave->Info->PageSize * Slave->Info->SectorSize;
+
+  /* Current handling is only limit for single flash Bank */
+  while (Length) {
+    ReadAddr = Offset;
+
+    RemainLength = (FlashSize - Offset);
+    if (Length < RemainLength) {
+      ReadLength = Length;
+    } else {
+      ReadLength = RemainLength;
+    }
+
+    /* Send read command */
+    SPI_OP_PARAMS  OpRead = SPI_OP (
+                                SPI_OP_CMD (SPI_CMD_READ_DATA),
+                                SPI_OP_ADDR (Slave->AddrSize, ReadAddr),
+                                SPI_OP_NO_DUMMY,
+                                SPI_OP_DATA_IN (ReadLength, Buffer)
+                                );
+
+    Status = SpiMasterProtocol->DataRead (SpiMasterProtocol, Slave, &OpRead);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Spi error while reading data\n", __func__));
+      return Status;
+    }
+
+    Offset += ReadLength;
+    Length -= ReadLength;
+    Buffer = (VOID *)((UINTN)Buffer + ReadLength);
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashWrite (
+  IN SPI_DEVICE_PARAMS *Slave,
+  IN UINT32 Offset,
+  IN UINTN Length,
+  IN VOID *Buffer
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       ByteAddr, ChunkLength, ActualIndex, PageSize;
+  UINT32      WriteAddr;
+
+  PageSize = Slave->Info->PageSize;
+
+  Status = SpiFlashWriteEnableCmd (Slave);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Error while setting write enable\n", __func__));
+    return Status;
+  }
+
+  for (ActualIndex = 0; ActualIndex < Length; ActualIndex += ChunkLength) {
+    WriteAddr = Offset;
+
+    ByteAddr    = Offset % PageSize;
+    ChunkLength = MIN (Length - ActualIndex, (UINT64)(PageSize - ByteAddr));
+
+    SPI_OP_PARAMS  OpPgProg = SPI_OP (
+                                      SPI_OP_CMD (SPI_CMD_PAGE_PROGRAM),
+                                      SPI_OP_ADDR (3, WriteAddr),
+                                      SPI_OP_NO_DUMMY,
+                                      SPI_OP_DATA_OUT (ChunkLength, (VOID *)((UINTN)Buffer + ActualIndex))
+                                      );
+
+    Status = SpiMasterProtocol->DataWrite (SpiMasterProtocol, Slave, &OpPgProg);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Error while programming write address\n", __func__));
+      return Status;
+    }
+
+    Status = SpiFlashPoll(Slave);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    Offset += ChunkLength;
+  }
+
+  Status = SpiFlashWriteDisableCmd (Slave);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Error while setting write disable\n", __func__));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SpiFlashUpdateBlock (
+  IN SPI_DEVICE_PARAMS *Slave,
+  IN UINT32 Offset,
+  IN UINTN ToUpdate,
+  IN UINT8 *Buffer,
+  IN UINT8 *TmpBuf,
+  IN UINTN EraseSize
+  )
+{
+  EFI_STATUS Status;
+
+  /* Read backup */
+  Status = SpiFlashRead (Slave, Offset, EraseSize, TmpBuf);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a(): Update: Error while reading old data\n", __func__));
+      return Status;
+    }
+
+  /* Erase entire sector */
+  Status = SpiFlashErase (Slave, Offset, EraseSize);
+  if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a(): Update: Error while erasing block\n", __func__));
+      return Status;
+    }
+
+  /* Write new data */
+  SpiFlashWrite (Slave, Offset, ToUpdate, Buffer);
+  if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a(): Update: Error while writing new data\n", __func__));
+      return Status;
+    }
+
+  /* Write backup */
+  if (ToUpdate != EraseSize) {
+    Status = SpiFlashWrite (Slave, Offset + ToUpdate, EraseSize - ToUpdate,
+      &TmpBuf[ToUpdate]);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a(): Update: Error while writing backup\n", __func__));
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashUpdate (
+  IN SPI_DEVICE_PARAMS *Slave,
+  IN UINT32 Offset,
+  IN UINTN ByteCount,
+  IN UINT8 *Buffer
+  )
+{
+  EFI_STATUS Status;
+  UINT64 SectorSize, ToUpdate, Scale = 1;
+  UINT8 *TmpBuf, *End;
+
+  SectorSize = Slave->Info->SectorSize;
+
+  End = Buffer + ByteCount;
+
+  TmpBuf = (UINT8 *)AllocateZeroPool (SectorSize);
+  if (TmpBuf == NULL) {
+    DEBUG((DEBUG_ERROR, "%a(): Cannot allocate memory\n", __func__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  if (End - Buffer >= 200)
+    Scale = (End - Buffer) / 100;
+
+  for (; Buffer < End; Buffer += ToUpdate, Offset += ToUpdate) {
+    ToUpdate = MIN((UINT64)(End - Buffer), SectorSize);
+    Print (L"   \rUpdating, %d%%", 100 - (End - Buffer) / Scale);
+    Status = SpiFlashUpdateBlock (Slave, Offset, ToUpdate, Buffer, TmpBuf, SectorSize);
+
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "%a(): Error while updating\n", __func__));
+      return Status;
+    }
+  }
+
+  Print(L"\n");
+  FreePool (TmpBuf);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashUpdateWithProgress (
+  IN SPI_DEVICE_PARAMS                             *Slave,
+  IN UINT32                                         Offset,
+  IN UINTN                                          ByteCount,
+  IN UINT8                                         *Buffer,
+  IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS  Progress,        OPTIONAL
+  IN UINTN                                          StartPercentage,
+  IN UINTN                                          EndPercentage
+  )
+{
+  EFI_STATUS Status;
+  UINTN SectorSize;
+  UINTN SectorNum;
+  UINTN ToUpdate;
+  UINTN Index;
+  UINT8 *TmpBuf;
+
+  SectorSize = Slave->Info->SectorSize;
+  SectorNum = (ByteCount / SectorSize) + 1;
+  ToUpdate = SectorSize;
+
+  TmpBuf = (UINT8 *)AllocateZeroPool (SectorSize);
+  if (TmpBuf == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a(): Cannot allocate memory\n", __func__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  for (Index = 0; Index < SectorNum; Index++) {
+    if (Progress != NULL) {
+      Progress (StartPercentage +
+                ((Index * (EndPercentage - StartPercentage)) / SectorNum));
+    }
+
+    /* In the last chunk update only an actual number of remaining bytes */
+    if (Index + 1 == SectorNum) {
+      ToUpdate = ByteCount % SectorSize;
+    }
+
+    Status = SpiFlashUpdateBlock (Slave,
+               Offset + Index * SectorSize,
+               ToUpdate,
+               Buffer + Index * SectorSize,
+               TmpBuf,
+               SectorSize);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Error while updating\n", __func__));
+      return Status;
+    }
+  }
+  FreePool (TmpBuf);
+
+  if (Progress != NULL) {
+    Progress (EndPercentage);
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+SpiFlashReadId (
+  IN SPI_DEVICE_PARAMS  *Slave,
+  IN BOOLEAN            UseInRuntime
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       IdLen = 3;
+  UINT8       Id[IdLen];
+
+  SPI_OP_PARAMS  Op = SPI_READID_OP (Id, IdLen);
+
+  Status = SpiMasterProtocol->CmdRead (SpiMasterProtocol, Slave, &Op);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Spi error while reading id\n", __func__));
+    return Status;
+  }
+
+  Status = NorFlashGetInfo (Id, &Slave->Info, UseInRuntime);
+
+  if (EFI_ERROR (Status)) {
+    DEBUG (
+           (DEBUG_ERROR,
+            "%a: Unrecognized JEDEC Id bytes: 0x%02x%02x%02x\n",
+            __func__,
+            Id[0],
+            Id[1],
+            Id[2])
+           );
+    return Status;
+  }
+
+  NorFlashPrintInfo (Slave->Info);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+SpiFlashInit (
+  IN SPI_FLASH_PROTOCOL  *This,
+  IN SPI_DEVICE_PARAMS   *Slave
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       StatusRegister;
+
+  Slave->AddrSize = (Slave->Info->Flags & NOR_FLASH_4B_ADDR) ? 4 : 3;
+
+  Status = SpiFlashWriteEnableCmd (Slave);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Error while setting write enable\n", __func__));
+    return Status;
+  }
+
+  if (Slave->AddrSize == 4) {
+
+    /* Enable 4byte addressing */
+    SPI_OP_PARAMS  Op4BAddEn = SPI_OP (
+                                        SPI_OP_CMD (SPI_CMD_4B_ADDR_ENABLE),
+                                        SPI_OP_NO_ADDR,
+                                        SPI_OP_NO_DUMMY,
+                                        SPI_OP_NO_DATA
+                                        );
+    Status = SpiMasterProtocol->CmdWrite (SpiMasterProtocol, Slave, &Op4BAddEn);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a(): Error while setting 4B address\n", __func__));
+      return Status;
+    }
+  }
+
+  /* Initialize flash status register */
+  StatusRegister = 0x0;
+  SPI_OP_PARAMS  OpWrSts = SPI_OP (
+                                    SPI_OP_CMD (SPI_CMD_WRITE_STATUS_REG),
+                                    SPI_OP_NO_ADDR,
+                                    SPI_OP_NO_DUMMY,
+                                    SPI_OP_DATA_OUT (1, (VOID *)&StatusRegister)
+                                    );
+
+  Status = SpiMasterProtocol->CmdWrite (SpiMasterProtocol, Slave, &OpWrSts);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Error while writing status register\n", __func__));
+    return Status;
+  }
+
+  Status = SpiFlashWriteDisableCmd (Slave);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Error while setting write disable\n", __func__));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashInitProtocol (
+  IN SPI_FLASH_PROTOCOL  *SpiFlashProtocol
+  )
+{
+  SpiFlashProtocol->Read   = SpiFlashRead;
+  SpiFlashProtocol->Write  = SpiFlashWrite;
+  SpiFlashProtocol->Update = SpiFlashUpdate;
+  SpiFlashProtocol->UpdateWithProgress = SpiFlashUpdateWithProgress;
+  SpiFlashProtocol->Erase  = SpiFlashErase;
+  SpiFlashProtocol->ReadId = SpiFlashReadId;
+  SpiFlashProtocol->Init   = SpiFlashInit;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+SpiFlashEntryPoint (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = gBS->LocateProtocol (
+                                &gJH7110SpiMasterProtocolGuid,
+                                NULL,
+                                (VOID **)&SpiMasterProtocol
+                                );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Cannot locate SPI Master protocol\n", __func__));
+    return EFI_DEVICE_ERROR;
+  }
+
+  mSpiFlashInstance = AllocateRuntimeZeroPool (sizeof (SPI_FLASH_INSTANCE));
+  if (mSpiFlashInstance == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a(): Cannot allocate memory\n", __func__));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  SpiFlashInitProtocol (&mSpiFlashInstance->SpiFlashProtocol);
+
+  mSpiFlashInstance->Signature = SPI_FLASH_SIGNATURE;
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                                                   &(mSpiFlashInstance->Handle),
+                                                   &gJH7110SpiFlashProtocolGuid,
+                                                   &(mSpiFlashInstance->SpiFlashProtocol),
+                                                   NULL
+                                                   );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a(): Cannot install SPI flash protocol\n", __func__));
+    goto ErrorInstallProto;
+  }
+
+  return EFI_SUCCESS;
+
+ErrorInstallProto:
+  FreePool (mSpiFlashInstance);
+
+  return EFI_SUCCESS;
+}
diff --git a/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.h b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.h
new file mode 100755
index 000000000000..f3bb300334c0
--- /dev/null
+++ b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.h
@@ -0,0 +1,35 @@
+/** @file
+ *
+ *  Copyright (c) 2023, StarFive Technology Co., Ltd. All rights reserved.<BR>
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#ifndef __SPI_FLASH_DXE_H__
+#define __SPI_FLASH_DXE_H__
+
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <Protocol/Spi.h>
+#include <Protocol/SpiFlash.h>
+
+#define SPI_FLASH_SIGNATURE      SIGNATURE_32 ('F', 'S', 'P', 'I')
+
+#define STATUS_REG_POLL_WIP_MSK  (1 << 0)
+
+typedef struct {
+  SPI_FLASH_PROTOCOL    SpiFlashProtocol;
+  UINTN                 Signature;
+  EFI_HANDLE            Handle;
+} SPI_FLASH_INSTANCE;
+
+#endif //__SPI_FLASH_DXE_H__
diff --git a/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.inf b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.inf
new file mode 100644
index 000000000000..3ca429fdc2dd
--- /dev/null
+++ b/Silicon/StarFive/JH7110Pkg/Driver/SpiFvbServicesDxe/SpiFlashDxe/SpiFlashDxe.inf
@@ -0,0 +1,44 @@
+## @file
+#
+#  Copyright (c) 2023, StarFive Technology Co., Ltd. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = SpiFlashDxe
+  FILE_GUID                      = D3DF07BE-3810-4521-89EF-C4E22E0B2484
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = SpiFlashEntryPoint
+
+[Sources]
+  SpiFlashDxe.c
+  SpiFlashDxe.h
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  Silicon/StarFive/JH7110Pkg/JH7110Pkg.dec
+
+[LibraryClasses]
+  DebugLib
+  MemoryAllocationLib
+  NorFlashInfoLib
+  TimerLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+  UefiRuntimeLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid
+
+[Protocols]
+  gJH7110SpiFlashProtocolGuid
+  gJH7110SpiMasterProtocolGuid
+
+[Depex]
+  gJH7110SpiMasterProtocolGuid
diff --git a/Silicon/StarFive/JH7110Pkg/Include/Protocol/Spi.h b/Silicon/StarFive/JH7110Pkg/Include/Protocol/Spi.h
new file mode 100644
index 000000000000..b8db3ad4c5b0
--- /dev/null
+++ b/Silicon/StarFive/JH7110Pkg/Include/Protocol/Spi.h
@@ -0,0 +1,163 @@
+/** @file
+ *
+ *  Copyright (c) 2023, StarFive Technology Co., Ltd. All rights reserved.<BR>
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#ifndef __SPI_MASTER_PROTOCOL_H__
+#define __SPI_MASTER_PROTOCOL_H__
+
+#include <Library/NorFlashInfoLib.h>
+
+typedef struct _SPI_MASTER_PROTOCOL SPI_MASTER_PROTOCOL;
+
+
+#define SPI_CMD_WRITE_STATUS_REG  0x01
+#define SPI_CMD_PAGE_PROGRAM      0x02
+#define SPI_CMD_READ_DATA         0x03
+#define SPI_CMD_WRITE_DISABLE     0x04
+#define SPI_CMD_READ_STATUS       0x05
+#define SPI_CMD_WRITE_ENABLE      0x06
+#define SPI_CMD_READ_ARRAY_FAST   0x0b
+#define SPI_CMD_BANKADDR_BRWR     0x17
+#define SPI_CMD_ERASE_4K          0x20
+#define SPI_CMD_ERASE_32K         0x52
+#define SPI_CMD_FLAG_STATUS       0x70
+#define SPI_CMD_READ_ID           0x9f
+#define SPI_CMD_4B_ADDR_ENABLE    0xb7
+#define SPI_CMD_BANK_WRITE        0xc5
+#define SPI_CMD_ERASE_64K         0xd8
+
+
+
+#define SPI_OP_CMD(__OpCode)  \
+  {                      \
+    .OpCode = __OpCode,  \
+    .NBytes = 1,         \
+  }
+
+#define SPI_OP_ADDR(__NBytes, __Val)  \
+  {                      \
+    .NBytes = __NBytes,  \
+    .Val = __Val,        \
+  }
+
+#define SPI_OP_NO_ADDR  { }
+
+#define SPI_OP_DUMMY(__NBytes)  \
+  {                      \
+    .NBytes = __NBytes,  \
+  }
+
+#define SPI_OP_NO_DUMMY  { }
+
+#define SPI_OP_DATA_IN(__NBytes, __Buf)  \
+  {                      \
+    .NBytes = __NBytes,  \
+    .Buf.In = __Buf,     \
+  }
+
+#define SPI_OP_DATA_OUT(__NBytes, __Buf)  \
+  {                     \
+    .NBytes = __NBytes, \
+    .Buf.Out = __Buf,   \
+  }
+
+#define SPI_OP_NO_DATA  { }
+
+#define SPI_OP(__Cmd, __Addr, __Dummy, __Data)  \
+  {                    \
+    .Cmd = __Cmd,      \
+    .Addr = __Addr,    \
+    .Dummy = __Dummy,  \
+    .Data = __Data,    \
+  }
+
+/**
+ * Standard SPI NOR flash operations
+ */
+#define SPI_WRITE_EN_OP()    \
+  SPI_OP(SPI_OP_CMD(SPI_CMD_WRITE_ENABLE),  \
+       SPI_OP_NO_ADDR,        \
+       SPI_OP_NO_DUMMY,       \
+       SPI_OP_NO_DATA)
+
+#define SPI_WRITE_DIS_OP()   \
+  SPI_OP(SPI_OP_CMD(SPI_CMD_WRITE_DISABLE),  \
+       SPI_OP_NO_ADDR,        \
+       SPI_OP_NO_DUMMY,       \
+       SPI_OP_NO_DATA)
+
+#define SPI_READID_OP(Buf, Len)  \
+  SPI_OP(SPI_OP_CMD(SPI_CMD_READ_ID),  \
+       SPI_OP_NO_ADDR,        \
+       SPI_OP_NO_DUMMY,       \
+       SPI_OP_DATA_IN(Len, Buf))
+
+typedef struct {
+  struct {
+    UINT8     NBytes;
+    UINT16    OpCode;
+  } Cmd;
+
+  struct {
+    UINT8     NBytes;
+    UINT64    Val;
+  } Addr;
+
+  struct {
+    UINT8    NBytes;
+  } Dummy;
+
+  struct {
+    UINT32          NBytes;
+    union {
+      VOID          *In;
+      CONST VOID    *Out;
+    } Buf;
+  } Data;
+} SPI_OP_PARAMS;
+
+typedef struct {
+  UINT32            AddrSize;
+  NOR_FLASH_INFO    *Info;
+  UINT32            RegBase;
+  VOID              *AhbBase;
+  UINT8             FifoWidth;
+  UINT32            WriteDelay;
+} SPI_DEVICE_PARAMS;
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_DEVICE_SETUP)(
+                      IN SPI_MASTER_PROTOCOL *This,
+                      OUT SPI_DEVICE_PARAMS *Slave
+                    );
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_DEVICE_FREE)(
+                      IN SPI_DEVICE_PARAMS  *Slave
+                    );
+
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_EXECUTE_RW)(
+                          IN SPI_MASTER_PROTOCOL *This,
+                          IN SPI_DEVICE_PARAMS  *Slave,
+                          IN OUT SPI_OP_PARAMS  *Cmds
+                        );
+
+struct _SPI_MASTER_PROTOCOL {
+  SPI_DEVICE_SETUP  SetupDevice;
+  SPI_DEVICE_FREE   FreeDevice;
+  SPI_EXECUTE_RW    CmdRead;
+  SPI_EXECUTE_RW    DataRead;
+  SPI_EXECUTE_RW    CmdWrite;
+  SPI_EXECUTE_RW    DataWrite;
+};
+
+#endif // __SPI_MASTER_PROTOCOL_H__
diff --git a/Silicon/StarFive/JH7110Pkg/Include/Protocol/SpiFlash.h b/Silicon/StarFive/JH7110Pkg/Include/Protocol/SpiFlash.h
new file mode 100644
index 000000000000..cdbe4d0b6b67
--- /dev/null
+++ b/Silicon/StarFive/JH7110Pkg/Include/Protocol/SpiFlash.h
@@ -0,0 +1,88 @@
+/** @file
+ *
+ *  Copyright (c) 2023, StarFive Technology Co., Ltd. All rights reserved.<BR>
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#ifndef __SPI_FLASH_PROTOCOL_H__
+#define __SPI_FLASH_PROTOCOL_H__
+
+#include <Protocol/FirmwareManagement.h>
+#include <Protocol/Spi.h>
+
+typedef struct _SPI_FLASH_PROTOCOL SPI_FLASH_PROTOCOL;
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_FLASH_INIT) (
+                         IN SPI_FLASH_PROTOCOL *This,
+                         IN SPI_DEVICE_PARAMS *SpiDev
+                         );
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_FLASH_READ_ID) (
+                            IN SPI_DEVICE_PARAMS *Slave,
+                            IN BOOLEAN UseInRuntime
+                            );
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_FLASH_READ) (
+                         IN SPI_DEVICE_PARAMS *Slave,
+                         IN UINT32 Offset,
+                         IN UINTN Length,
+                         OUT VOID *Buffer
+                         );
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_FLASH_WRITE) (
+                          IN SPI_DEVICE_PARAMS *Slave,
+                          IN UINT32 Offset,
+                          IN UINTN Length,
+                          IN VOID *Buffer
+                          );
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_FLASH_ERASE) (
+                          IN SPI_DEVICE_PARAMS *Slave,
+                          IN UINT32 Offset,
+                          IN UINTN Length
+                          );
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_FLASH_UPDATE) (
+                          IN SPI_DEVICE_PARAMS *Slave,
+                          IN UINT32     Address,
+                          IN UINTN      DataByteCount,
+                          IN UINT8      *Buffer
+                          );
+
+typedef
+  EFI_STATUS
+(EFIAPI *SPI_FLASH_UPDATE_WITH_PROGRESS) (
+  IN SPI_DEVICE_PARAMS                              *Slave,
+  IN UINT32                                         Offset,
+  IN UINTN                                          ByteCount,
+  IN UINT8                                         *Buffer,
+  IN EFI_FIRMWARE_MANAGEMENT_UPDATE_IMAGE_PROGRESS  Progress, OPTIONAL
+  IN UINTN                                          StartPercentage,
+  IN UINTN                                          EndPercentage
+  );
+
+struct _SPI_FLASH_PROTOCOL {
+  SPI_FLASH_INIT     Init;
+  SPI_FLASH_READ_ID  ReadId;
+  SPI_FLASH_READ     Read;
+  SPI_FLASH_WRITE    Write;
+  SPI_FLASH_ERASE    Erase;
+  SPI_FLASH_UPDATE   Update;
+  SPI_FLASH_UPDATE_WITH_PROGRESS  UpdateWithProgress;
+};
+
+#endif // __SPI_FLASH_PROTOCOL_H__
-- 
2.34.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#110176): https://edk2.groups.io/g/devel/message/110176
Mute This Topic: https://groups.io/mt/102214529/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



  parent reply	other threads:[~2023-10-27  3:24 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-27  3:19 [edk2-devel] [PATCH v3 0/5] StarFive/VisionFive2: Add VisionFive 2 platform John Chew
2023-10-27  3:19 ` [edk2-devel] [PATCH v3 1/5] StarFive/JH7110Pkg: Add Pci controller driver John Chew
2023-10-27  3:19 ` John Chew [this message]
2023-10-27  3:19 ` [edk2-devel] [PATCH v3 3/5] StarFive/JH7110Pkg: Add firmware volume block protocol John Chew
2023-10-27  3:19 ` [edk2-devel] [PATCH v3 4/5] StarFive/JH7110Pkg: Add JH7110 Silicon Package John Chew
2023-10-27  3:19 ` [edk2-devel] [PATCH v3 5/5] StarFive/VisionFive2: Add VisionFive 2 platform John Chew
2023-10-31  9:26 ` [edk2-devel] [PATCH v3 0/5] " Sunil V L
2023-11-01  1:17   ` John Chew
2023-11-02  4:00     ` Sunil V L

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231027031906.1814-3-yuinyee.chew@starfivetech.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox