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 v4 2/6] StarFive/JH7110Pkg: Add SPI protocol and driver support
Date: Fri, 3 Nov 2023 10:30:51 +0800 [thread overview]
Message-ID: <20231103023055.1629-3-yuinyee.chew@starfivetech.com> (raw)
In-Reply-To: <20231103023055.1629-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>
Acked-by: Sunil V L <sunilvl@ventanamicro.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 (#110595): https://edk2.groups.io/g/devel/message/110595
Mute This Topic: https://groups.io/mt/102357046/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
next prev parent reply other threads:[~2023-11-03 2:34 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-03 2:30 [edk2-devel] [PATCH v4 0/6] StarFive/VisionFive2: Add VisionFive 2 platform John Chew
2023-11-03 2:30 ` [edk2-devel] [PATCH v4 1/6] StarFive/JH7110Pkg: Add Pci controller driver John Chew
2023-11-03 2:30 ` John Chew [this message]
2023-11-03 2:30 ` [edk2-devel] [PATCH v4 3/6] StarFive/JH7110Pkg: Add firmware volume block protocol John Chew
2023-11-03 2:30 ` [edk2-devel] [PATCH v4 4/6] StarFive/JH7110Pkg: Add JH7110 Silicon Package John Chew
2023-11-03 2:30 ` [edk2-devel] [PATCH v4 5/6] StarFive/VisionFive2: Add VisionFive 2 platform John Chew
2023-11-03 2:30 ` [edk2-devel] [PATCH v4 6/6] Maintainers.txt: Add maintainers for StarFive platform John Chew
2023-11-15 1:39 ` [edk2-devel] [PATCH v4 0/6] StarFive/VisionFive2: Add VisionFive 2 platform John Chew
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=20231103023055.1629-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