Browse Source

qspi: add qspi driver layer

Signed-off-by: Yuin Yee Chew <yuinyee.chew@starfivetech.com>
Yuin Yee Chew 10 months ago
parent
commit
2e50d8d936

+ 1067 - 0
Silicon/StarFive/JH7110/Driver/Spi/SpiDxe/SpiApbDxe.c

@@ -0,0 +1,1067 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Yuin Yee, Chew (John) <yuinyee.chew@starfivetech.com>.
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#include "SpiApbDxe.h"
+
+VOID
+SpiApbControllerEnable (
+  IN UINT32 RegBase
+  )
+{
+  UINT32 Reg;
+  
+  Reg = MmioRead32(RegBase + SPI_REG_CONFIG);
+  Reg |= SPI_REG_CONFIG_ENABLE;
+  MmioWrite32(RegBase + SPI_REG_CONFIG, Reg);
+}
+
+VOID 
+SpiApbControllerDisable (
+  IN UINT32 RegBase
+  )
+{
+  UINT32 Reg;
+
+  Reg = MmioRead32(RegBase + SPI_REG_CONFIG);
+  Reg &= ~SPI_REG_CONFIG_ENABLE;
+  MmioWrite32(RegBase + SPI_REG_CONFIG, Reg);
+}
+
+VOID 
+SpiApbDacModeEnable (
+  IN UINT32 RegBase
+  )
+{
+  UINT32 Reg;
+
+  Reg = MmioRead32(RegBase + SPI_REG_CONFIG);
+  Reg |= SPI_REG_CONFIG_DIRECT;
+  MmioWrite32(RegBase + SPI_REG_CONFIG, Reg);
+}
+
+STATIC
+UINT32 
+SpiApbCalcDummy (
+  IN CONST SPI_MEM_OPS *Ops,
+  IN BOOLEAN Dtr
+  )
+{
+  UINT32 DummyClk;
+
+  if (!Ops->Dummy.NBytes || !Ops->Dummy.BusWidth) {
+    return 0;
+  }
+
+  DummyClk = Ops->Dummy.NBytes * (8 / Ops->Dummy.BusWidth);
+  if (Dtr) {
+    DummyClk /= 2;
+  }
+
+  return DummyClk;
+}
+
+STATIC
+UINT32 
+SpiApbCalcReadReg (
+  IN SPI_DEVICE *Slave
+  )
+{
+  UINT32 ReadReg = 0;
+
+  ReadReg |= Slave->InstWidth << SPI_REG_RD_INSTR_TYPE_INSTR_LSB;
+  ReadReg |= Slave->AddrWidth << SPI_REG_RD_INSTR_TYPE_ADDR_LSB;
+  ReadReg |= Slave->DataWidth << SPI_REG_RD_INSTR_TYPE_DATA_LSB;
+
+  return ReadReg;
+}
+
+STATIC
+INTN 
+SpiApbBuswidthToInstType (
+  IN UINT8 BusWidth
+  )
+{
+  switch (BusWidth) {
+  case 0:
+  case 1:
+    return SPI_INST_TYPE_SINGLE;
+  case 2:
+    return SPI_INST_TYPE_DUAL;
+  case 4:
+    return SPI_INST_TYPE_QUAD;
+  case 8:
+    return SPI_INST_TYPE_OCTAL;
+  default:
+    return -1;
+  }
+}
+
+STATIC
+EFI_STATUS
+SpiApbSetProtocol (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  )
+{
+  INTN Ret;
+
+  Slave->Dtr = Ops->Data.Dtr && Ops->Cmd.Dtr && Ops->Addr.Dtr;
+
+  Ret = SpiApbBuswidthToInstType(Ops->Cmd.BusWidth);
+  if (Ret < 0) {
+    return EFI_UNSUPPORTED;
+  }
+  Slave->InstWidth = Ret;
+
+  Ret = SpiApbBuswidthToInstType(Ops->Addr.BusWidth);
+  if (Ret < 0) {
+    return EFI_UNSUPPORTED;
+  }
+  Slave->AddrWidth = Ret;
+
+  Ret = SpiApbBuswidthToInstType(Ops->Data.BusWidth);
+  if (Ret < 0) {
+    return EFI_UNSUPPORTED;
+  }
+  Slave->DataWidth = Ret;
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS 
+SpiApbWaitIdle (
+  IN UINT32 RegBase
+  )
+{
+  UINT32 Count = 0;
+  UINT32 TimeoutMs = 5000000;
+
+  do {
+      Count = (SPI_REG_IS_IDLE(RegBase)) ? 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;
+}
+
+VOID 
+SpiApbReadDataCapture (
+  IN UINT32 RegBase,
+  IN UINT32 ByPass, 
+  IN UINT32 Delay
+  )
+{
+  UINT32 Reg;
+  SpiApbControllerDisable(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);
+
+  SpiApbControllerEnable(RegBase);
+}
+
+VOID 
+SpiApbConfigBaudrateDiv (
+  IN UINT32 RegBase,
+  IN UINT32 RefClkHz, 
+  IN UINT32 SclkHz
+  )
+{
+  UINT32 Reg;
+  UINT32 Div;
+
+  SpiApbControllerDisable(RegBase);
+  Reg = MmioRead32(RegBase + SPI_REG_CONFIG);
+  Reg &= ~(SPI_REG_CONFIG_BAUD_MASK << SPI_REG_CONFIG_BAUD_LSB);
+
+  Div = DIV_ROUND_UP(RefClkHz, SclkHz * 2) - 1;
+
+  if (Div > SPI_REG_CONFIG_BAUD_MASK) {
+    Div = SPI_REG_CONFIG_BAUD_MASK;
+  }
+
+  DEBUG ((DEBUG_ERROR, "%s: RefClk %dHz sclk %dHz Div 0x%x, actual %dHz\n", __func__,
+         RefClkHz, SclkHz, Div, RefClkHz / (2 * (Div + 1))));
+
+  Reg |= (Div << SPI_REG_CONFIG_BAUD_LSB);
+  MmioWrite32(RegBase + SPI_REG_CONFIG, Reg);
+
+  SpiApbControllerEnable(RegBase);
+}
+
+VOID 
+SpiApbSetClkMode (
+  IN UINT32 RegBase, 
+  IN UINTN Mode
+)
+{
+  UINT32 Reg;
+
+  SpiApbControllerDisable(RegBase);
+  Reg = MmioRead32(RegBase + SPI_REG_CONFIG);
+  Reg &= ~(SPI_REG_CONFIG_CLK_POL | SPI_REG_CONFIG_CLK_PHA);
+
+  if (Mode & SPI_CPOL) {
+    Reg |= SPI_REG_CONFIG_CLK_POL;
+  }
+  if (Mode & SPI_CPHA) {
+    Reg |= SPI_REG_CONFIG_CLK_PHA;
+  }
+
+  MmioWrite32(RegBase + SPI_REG_CONFIG, Reg);
+
+  SpiApbControllerEnable(RegBase);
+}
+
+VOID 
+SpiApbChipSelect (
+  IN UINT32 RegBase,
+  IN UINT32 ChipSel, 
+  IN UINT32 DecoderEnable
+  )
+{
+  UINT32 Reg;
+
+  SpiApbControllerDisable(RegBase);
+
+  Reg = MmioRead32(RegBase + SPI_REG_CONFIG);
+
+  if (DecoderEnable) {
+    Reg |= SPI_REG_CONFIG_DECODE;
+  } else {
+    Reg &= ~SPI_REG_CONFIG_DECODE;
+    /* Convert CS if without decoder.
+     * CS0 to 4b'1110
+     * CS1 to 4b'1101
+     * CS2 to 4b'1011
+     * CS3 to 4b'0111
+     */
+    ChipSel = 0xF & ~(1 << ChipSel);
+  }
+
+  Reg &= ~(SPI_REG_CONFIG_CHIPSELECT_MASK
+      << SPI_REG_CONFIG_CHIPSELECT_LSB);
+  Reg |= (ChipSel & SPI_REG_CONFIG_CHIPSELECT_MASK)
+      << SPI_REG_CONFIG_CHIPSELECT_LSB;
+  MmioWrite32(RegBase + SPI_REG_CONFIG, Reg);
+
+  SpiApbControllerEnable(RegBase);
+}
+
+VOID 
+SpiApbDelay (
+  IN UINT32 RegBase,
+  IN UINT32 RefClk,
+  IN UINT32 SclkHz,
+  IN UINT32 TshslNs, 
+  IN UINT32 Tsd2dNs,
+  IN UINT32 TchshNs, 
+  IN UINT32 TslchNs
+  )
+{
+  UINT32 RefClkNs;
+  UINT32 SclkNs;
+  UINT32 Tshsl, Tchsh, Tslch, Tsd2d;
+  UINT32 Reg;
+
+  SpiApbControllerDisable(RegBase);
+
+  // Convert to ns.
+  RefClkNs = DIV_ROUND_UP(1000000000, RefClk);
+
+  // Convert to ns.
+  SclkNs = DIV_ROUND_UP(1000000000, SclkHz);
+
+  // The controller adds additional delay to that programmed in the Reg
+  if (TshslNs >= SclkNs + RefClkNs) {
+    TshslNs -= SclkNs + RefClkNs;
+  }
+  if (TchshNs >= SclkNs + 3 * RefClkNs) {
+    TchshNs -= SclkNs + 3 * RefClkNs;
+  }
+  Tshsl = DIV_ROUND_UP(TshslNs, RefClkNs);
+  Tchsh = DIV_ROUND_UP(TchshNs, RefClkNs);
+  Tslch = DIV_ROUND_UP(TslchNs, RefClkNs);
+  Tsd2d = DIV_ROUND_UP(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(RegBase + SPI_REG_DELAY, Reg);
+
+  SpiApbControllerEnable(RegBase);
+}
+
+STATIC
+EFI_STATUS 
+SpiApbExecFlashCmd (
+  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, "QSPI: flash command execution Timeout\n"));
+    return EFI_TIMEOUT;
+  }
+
+  // Polling QSPI idle status.
+  Status = SpiApbWaitIdle(RegBase);
+  if (EFI_ERROR (Status)) {
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS 
+SpiApbSetupOpCodeExt (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops,
+  IN UINT32 Shift
+  )
+{
+  UINT32 Reg;
+  UINT8 Ext;
+
+  if (Ops->Cmd.NBytes != 2) {
+    return EFI_UNSUPPORTED;
+  }
+
+  // Opcode extension is the LSB.
+  Ext = Ops->Cmd.OpCode & 0xFF;
+
+  Reg = MmioRead32(Slave->RegBase + SPI_REG_OP_EXT_LOWER);
+  Reg &= ~(0xFF << Shift);
+  Reg |= Ext << Shift;
+  MmioWrite32(Slave->RegBase + SPI_REG_OP_EXT_LOWER, Reg);
+
+  return EFI_SUCCESS;
+}
+
+STATIC 
+EFI_STATUS
+SpiApbEnableDtr (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops,
+  IN UINT32 Shift,
+  IN BOOLEAN Enable
+  )
+{
+  UINT32 Reg;
+  EFI_STATUS Status;
+
+  Reg = MmioRead32(Slave->RegBase + SPI_REG_CONFIG);
+
+  if (Enable) {
+    Reg |= SPI_REG_CONFIG_DTR_PROTO;
+    Reg |= SPI_REG_CONFIG_DUAL_OPCODE;
+
+    // Set up command OpCode extension.
+    Status = SpiApbSetupOpCodeExt(Slave, Ops, Shift);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  } else {
+    Reg &= ~SPI_REG_CONFIG_DTR_PROTO;
+    Reg &= ~SPI_REG_CONFIG_DUAL_OPCODE;
+  }
+
+  MmioWrite32(Slave->RegBase + SPI_REG_CONFIG, Reg);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS 
+SpiApbCommandReadSetup (
+  SPI_DEVICE *Slave,
+  CONST SPI_MEM_OPS *Ops
+  )
+{
+  EFI_STATUS Status;
+  UINT32 Reg;
+
+  Status = SpiApbSetProtocol(Slave, Ops);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = SpiApbEnableDtr(Slave, Ops, SPI_REG_OP_EXT_STIG_LSB,
+              Slave->Dtr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Reg = SpiApbCalcReadReg(Slave);
+  MmioWrite32(Slave->RegBase + SPI_REG_RD_INSTR, Reg);
+
+  return EFI_SUCCESS;
+}
+
+// For command RDID, RDSR.
+EFI_STATUS 
+SpiApbCommandRead (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  )
+{
+  UINT32 RegBase = Slave->RegBase;
+  UINT32 Reg;
+  UINT32 ReadLen;
+  EFI_STATUS Status;
+  UINT32 RxLen = Ops->Data.NBytes;
+  VOID *RxBuf = Ops->Data.Buf.In;
+  UINT32 DummyClk;
+  UINT8 OpCode;
+
+  if (RxLen > SPI_STIG_DATA_LEN_MAX || !RxBuf) {
+    DEBUG ((DEBUG_ERROR, "QSPI: Invalid input arguments RxLen %d\n", RxLen));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  OpCode = (Slave->Dtr) ? (Ops->Cmd.OpCode >> 8) : Ops->Cmd.OpCode; 
+
+  Reg = OpCode << SPI_REG_CMDCTRL_OPCODE_LSB;
+
+  // Set up Dummy cycles.
+  DummyClk = SpiApbCalcDummy(Ops, Slave->Dtr);
+  if (DummyClk > SPI_DUMMY_CLKS_MAX) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (DummyClk) {
+    Reg |= (DummyClk & SPI_REG_CMDCTRL_DUMMY_MASK)
+         << SPI_REG_CMDCTRL_DUMMY_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 = SpiApbExecFlashCmd(RegBase, Reg);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Reg = MmioRead32(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(RegBase + SPI_REG_CMDREADDATAUPPER);
+
+    ReadLen = RxLen - ReadLen;
+    CopyMem(RxBuf, &Reg, ReadLen);
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiApbCommandWriteSetup (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  )
+{
+  EFI_STATUS Status;
+  UINT32 Reg;
+
+  Status = SpiApbSetProtocol(Slave, Ops);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = SpiApbEnableDtr(Slave, Ops, SPI_REG_OP_EXT_STIG_LSB,
+              Slave->Dtr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Reg = SpiApbCalcReadReg(Slave);
+  MmioWrite32(Slave->RegBase + SPI_REG_RD_INSTR, Reg);
+
+  return EFI_SUCCESS;
+}
+
+// For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc.
+EFI_STATUS
+SpiApbCommandWrite (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  )
+{
+  UINT32 Reg = 0;
+  UINT32 WriteData;
+  UINT32 WriteLen;
+  UINT32 TxLen = Ops->Data.NBytes;
+  CONST VOID *TxBuf = Ops->Data.Buf.Out;
+  UINT32 RegBase = Slave->RegBase;
+  UINT32 Addr;
+  UINT8 OpCode;
+  EFI_STATUS Status;
+
+  // Reorder address to SPI bus order if only transferring address
+  if (!TxLen) {
+    Addr = cpu_to_be32(Ops->Addr.Val);
+    if (Ops->Addr.NBytes == 3) {
+      Addr >>= 8;
+    }
+    TxBuf = &Addr;
+    TxLen = Ops->Addr.NBytes;
+  }
+
+  if (TxLen > SPI_STIG_DATA_LEN_MAX) {
+    DEBUG ((DEBUG_ERROR, "QSPI: Invalid input arguments TxLen %d\n", TxLen));
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Slave->Dtr) {
+    OpCode = Ops->Cmd.OpCode >> 8;
+  } else {
+    OpCode = Ops->Cmd.OpCode;
+  }
+
+  Reg |= OpCode << SPI_REG_CMDCTRL_OPCODE_LSB;
+
+  if (TxLen) {
+    // writing Data = yes
+    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(RegBase + SPI_REG_CMDWRITEDATALOWER, WriteData);
+
+    if (TxLen > 4) {
+      TxBuf += WriteLen;
+      WriteLen = TxLen - WriteLen;
+      CopyMem(&WriteData, TxBuf, WriteLen);
+      MmioWrite32(RegBase + SPI_REG_CMDWRITEDATAUPPER, WriteData);
+    }
+  }
+
+  // Execute the command
+  Status = SpiApbExecFlashCmd(RegBase, Reg);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+UINT32
+SpiApbGetReadSramLevel (
+  IN SPI_DEVICE *Slave
+  )
+{
+  UINT32 Reg = MmioRead32(Slave->RegBase + SPI_REG_SDRAMLEVEL);
+  Reg >>= SPI_REG_SDRAMLEVEL_RD_LSB;
+  return Reg & SPI_REG_SDRAMLEVEL_RD_MASK;
+}
+
+// Opcode + Address (3/4 bytes) + Dummy bytes (0-4 bytes)
+EFI_STATUS
+SpiApbReadSetup (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  )
+{
+  UINT32 Reg;
+  UINT32 ReadReg;
+  UINT32 DummyClk;
+  UINT32 DummyBytes = Ops->Dummy.NBytes;
+  EFI_STATUS Status;
+  UINT8 OpCode;
+
+  Status = SpiApbSetProtocol(Slave, Ops);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = SpiApbEnableDtr(Slave, Ops, SPI_REG_OP_EXT_READ_LSB,
+              Slave->Dtr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Setup the indirect trigger address
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTTRIGGER, Slave->TrigAdd);
+
+  // Configure the OpCode
+  if (Slave->Dtr) {
+    OpCode = Ops->Cmd.OpCode >> 8;
+  } else {
+    OpCode = Ops->Cmd.OpCode;
+  }
+
+  ReadReg = OpCode << SPI_REG_RD_INSTR_OPCODE_LSB;
+  ReadReg |= SpiApbCalcReadReg(Slave);
+
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTRDSTARTADDR, Ops->Addr.Val);
+
+  if (DummyBytes) {
+    // Convert to clock cycles.
+    DummyClk = SpiApbCalcDummy(Ops, Slave->Dtr);
+
+    if (DummyClk > SPI_DUMMY_CLKS_MAX) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (DummyClk) {
+      ReadReg |= (DummyClk & SPI_REG_RD_INSTR_DUMMY_MASK)
+        << SPI_REG_RD_INSTR_DUMMY_LSB;
+    }
+  }
+
+
+  MmioWrite32(Slave->RegBase + SPI_REG_RD_INSTR, ReadReg);
+
+  // Set device size
+  Reg = MmioRead32(Slave->RegBase + SPI_REG_SIZE);
+  Reg &= ~SPI_REG_SIZE_ADDRESS_MASK;
+  Reg |= (Ops->Addr.NBytes - 1);
+  MmioWrite32(Slave->RegBase + SPI_REG_SIZE, Reg);
+
+  return EFI_SUCCESS;
+}
+
+STATIC 
+INTN 
+SpiApbWaitForData (
+  SPI_DEVICE *Slave
+  )
+{
+  UINT32 Timeout = 10000;
+  UINT32 Reg;
+
+  while (Timeout--) {
+    Reg = SpiApbGetReadSramLevel(Slave);
+    if (Reg) {
+      return Reg;
+  }
+    gBS->Stall(1);
+  }
+
+  return -1;
+}
+
+STATIC
+VOID
+SpiApbReadByte (
+  IN VOID *Addr, 
+  IN VOID *Data, 
+  IN INTN ByteLen
+  )
+{
+  UINT8 *ptr;
+  UINT8 *ptr2;
+
+  ptr = (UINT8 *)Addr;
+  ptr2 = (UINT8 *)Data;
+
+  while (ByteLen) {
+    *ptr2 = *ptr;
+    ptr2++;
+    ByteLen--;
+  }
+}
+
+STATIC
+VOID 
+SpiApbReadLong (
+  VOID *Addr, 
+  VOID *Data,
+  INTN LongLen
+  )
+{
+  UINT32 *ptr;
+  UINT32 *ptr2;
+
+  ptr = (UINT32 *)Addr;
+  ptr2 = (UINT32 *)Data;
+
+  while (LongLen) {
+    *ptr2 = *ptr;
+    ptr2++;
+    LongLen--;
+  }
+}
+
+STATIC
+EFI_STATUS
+SpiApbWaitForBitLe32 (
+  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
+EFI_STATUS
+SpiApbIndirectReadExecute (
+  IN SPI_DEVICE *Slave,
+  IN UINTN NRx, 
+  IN UINT8 *RxBuf
+  )
+{
+  UINTN Remaining = NRx;
+  UINTN bytes_to_read = 0;
+  EFI_STATUS Status;
+  INTN ret;
+
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTRDBYTES, NRx);
+
+  // Start the indirect read transfer
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTRD, SPI_REG_INDIRECTRD_START);
+
+  while (Remaining > 0) {
+    ret = SpiApbWaitForData(Slave);
+    if (ret < 0) {
+      DEBUG ((DEBUG_ERROR, "Indirect write timed out (%d)\n", ret));
+      goto FailRead;
+    }
+
+    bytes_to_read = ret;
+    while (bytes_to_read != 0) {
+      bytes_to_read *= Slave->FifoWidth;
+      bytes_to_read = bytes_to_read > Remaining ?
+          Remaining : bytes_to_read;
+
+      if (((UINTN)RxBuf % 4) || (bytes_to_read % 4)) {
+        SpiApbReadByte(Slave->AhbBase, RxBuf, bytes_to_read);
+      } else {
+        SpiApbReadLong(Slave->AhbBase, RxBuf, bytes_to_read >> 2);
+    }
+    RxBuf += bytes_to_read;
+      Remaining -= bytes_to_read;
+      bytes_to_read = SpiApbGetReadSramLevel(Slave);
+    }
+  }
+
+  // Check indirect done status
+  Status = SpiApbWaitForBitLe32(Slave->RegBase + SPI_REG_INDIRECTRD,
+        SPI_REG_INDIRECTRD_DONE, 1, 10);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Indirect read completion error (%d)\n", ret));
+    goto FailRead;
+  }
+
+  // Clear indirect completion status
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTRD, SPI_REG_INDIRECTRD_DONE);
+
+  return EFI_SUCCESS;
+
+FailRead:
+  // Cancel the indirect read
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTRD, SPI_REG_INDIRECTRD_CANCEL);
+  return EFI_ABORTED;
+}
+
+EFI_STATUS
+SpiApbReadExecute (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  )
+{
+  UINT64 from = Ops->Addr.Val;
+  VOID *Buf = Ops->Data.Buf.In;
+  UINTN len = Ops->Data.NBytes;
+  EFI_STATUS Status;
+
+  if (Slave->DacMode && (from + len < Slave->AhbSize)) {
+    if (len < 256) { 
+      CopyMem(Buf, (&Slave->AhbSize + from), len);
+    }
+
+  Status = SpiApbWaitIdle(Slave->RegBase);
+    if (EFI_ERROR (Status)) {
+      return EFI_TIMEOUT;
+    }
+    return EFI_SUCCESS;
+  }
+
+  Status = SpiApbIndirectReadExecute(Slave, len, Buf);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+// Opcode + Address (3/4 bytes)
+EFI_STATUS
+SpiApbWriteSetup (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  )
+{
+  UINT32 Reg;
+  EFI_STATUS Status;
+  UINT8 OpCode;
+
+  Status = SpiApbSetProtocol(Slave, Ops);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = SpiApbEnableDtr(Slave, Ops, SPI_REG_OP_EXT_WRITE_LSB,
+              Slave->Dtr);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Setup the indirect trigger address
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTTRIGGER, Slave->TrigAdd);
+
+  // Configure the OpCode
+  OpCode = (Slave->Dtr) ? (Ops->Cmd.OpCode >> 8) : Ops->Cmd.OpCode;
+
+  Reg = OpCode << SPI_REG_WR_INSTR_OPCODE_LSB;
+  Reg |= Slave->DataWidth << SPI_REG_WR_INSTR_TYPE_DATA_LSB;
+  Reg |= Slave->AddrWidth << SPI_REG_WR_INSTR_TYPE_ADDR_LSB;
+  MmioWrite32(Slave->RegBase + SPI_REG_WR_INSTR, Reg);
+
+  Reg = SpiApbCalcReadReg(Slave);
+  MmioWrite32(Slave->RegBase + SPI_REG_RD_INSTR, Reg);
+
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTWRSTARTADDR, Ops->Addr.Val);
+
+  if (Slave->Dtr) {
+    Reg = MmioRead32(Slave->RegBase + SPI_REG_WR_COMPLETION_CTRL);
+    Reg |= SPI_REG_WR_DISABLE_AUTO_POLL;
+    MmioWrite32(Slave->RegBase + SPI_REG_WR_COMPLETION_CTRL, Reg);
+  }
+
+  Reg = MmioRead32(Slave->RegBase + SPI_REG_SIZE);
+  Reg &= ~SPI_REG_SIZE_ADDRESS_MASK;
+  Reg |= (Ops->Addr.NBytes - 1);
+  MmioWrite32(Slave->RegBase + SPI_REG_SIZE, Reg);
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+VOID 
+SpiApbDelayNanoSec (
+  IN UINTN nsec
+  )
+{
+  UINT32 Timeout = DIV_ROUND_UP(nsec, 1000);
+
+  do {
+    Timeout--;
+    gBS->Stall(1);
+  } while(Timeout);
+}
+
+STATIC
+VOID 
+SpiApbWriteLong (
+  IN VOID *Addr, 
+  IN CONST VOID *Data,
+  IN INTN LongLen
+  )
+{
+  UINT32 *ptr;
+  UINT32 *ptr2;
+
+  ptr = (UINT32 *)Addr;
+  ptr2 = (UINT32 *)Data;
+
+  while (LongLen) {
+    *ptr = *ptr2;
+    ptr2++;
+    LongLen--;
+  }
+}
+
+STATIC 
+VOID 
+SpiApbWriteByte (
+  IN VOID *Addr, 
+  IN CONST VOID *Data,
+  IN INTN ByteLen
+  )
+{
+  UINT8 *ptr;
+  UINT8 *ptr2;
+
+  ptr = (UINT8 *)Addr;
+  ptr2 = (UINT8 *)Data;
+
+  while (ByteLen) {
+    *ptr = *ptr2;
+    ptr2++;
+    ByteLen--;
+  }
+}
+
+STATIC
+EFI_STATUS
+SpiApbIndirectWriteExecute (
+  IN SPI_DEVICE *Slave,
+  IN UINT32 NumTx, 
+  IN CONST UINT8 *TxBuf
+  )
+{
+  UINT32 PageSize = Slave->Info->PageSize;
+  UINT32 Remaining = NumTx;
+  CONST UINT8 *TxBufPtr = TxBuf;
+  UINT32 WriteBytes;
+  EFI_STATUS Status;
+
+  // Configure the indirect read transfer bytes
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTWRBYTES, NumTx);
+
+  // Start the indirect write transfer
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTWR, SPI_REG_INDIRECTWR_START);
+
+  // Ddelay is required for QSPI module to synchronized internally
+  SpiApbDelayNanoSec(Slave->WRDelay);
+
+  while (Remaining > 0) {
+    WriteBytes = Remaining > PageSize ? PageSize : Remaining;
+    SpiApbWriteLong(Slave->AhbBase, TxBufPtr, WriteBytes >> 2);
+    if (WriteBytes % 4) {
+      SpiApbWriteByte(Slave->AhbBase,
+        TxBufPtr + rounddown(WriteBytes, 4),
+        WriteBytes % 4);
+    }
+    Status = SpiApbWaitForBitLe32(Slave->RegBase + SPI_REG_SDRAMLEVEL,
+          SPI_REG_SDRAMLEVEL_WR_MASK <<
+          SPI_REG_SDRAMLEVEL_WR_LSB, 0, 10);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "Indirect write timed out (%d)\n", Status));
+      goto FailWrite;
+    }
+    TxBufPtr += WriteBytes;
+    Remaining -= WriteBytes;
+  }
+
+  // Check indirect done status
+  Status = SpiApbWaitForBitLe32(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);
+  return EFI_SUCCESS;
+
+FailWrite:
+  // Cancel the indirect write
+  MmioWrite32(Slave->RegBase + SPI_REG_INDIRECTWR, SPI_REG_INDIRECTWR_CANCEL);
+  return Status;
+}
+
+EFI_STATUS
+SpiApbWriteExecute (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  )
+{
+  UINT32 To = Ops->Addr.Val;
+  CONST VOID *Buf = Ops->Data.Buf.Out;
+  UINT32 Len = Ops->Data.NBytes;
+  EFI_STATUS Status;
+
+  if (!Slave->Dtr && Slave->DacMode && (To + Len < Slave->AhbSize)) {
+    CopyMem((Slave->AhbBase + To), Buf, Len);
+    Status = SpiApbWaitIdle(Slave->RegBase);
+    if (EFI_ERROR (Status)) {
+      return EFI_TIMEOUT;
+    }
+    return EFI_SUCCESS;
+  }
+
+  Status = SpiApbIndirectWriteExecute(Slave, Len, Buf);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}

+ 290 - 0
Silicon/StarFive/JH7110/Driver/Spi/SpiDxe/SpiApbDxe.h

@@ -0,0 +1,290 @@
+/** @file
+
+  Copyright (c) 
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __SPI_APB_DXE_H__
+#define __SPI_APB_DXE_H__
+
+#include <Library/IoLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+
+#include <Protocol/Spi.h>
+
+// Configs
+#define SPI_REG_POLL_US         1
+#define SPI_REG_RETRY           10000
+#define SPI_POLL_IDLE_RETRY     3
+#define SPI_STIG_DATA_LEN_MAX   8
+#define SPI_DUMMY_CLKS_PER_BYTE 8
+#define SPI_DUMMY_CLKS_MAX      31
+
+// Transfer Mode
+#define SPI_INST_TYPE_SINGLE    0
+#define SPI_INST_TYPE_DUAL      1
+#define SPI_INST_TYPE_QUAD      2
+#define SPI_INST_TYPE_OCTAL     3
+
+//
+// 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
+
+
+#define SPI_REG_IS_IDLE(base)            \
+  ((MmioRead32(base + SPI_REG_CONFIG) >> \
+    SPI_REG_CONFIG_IDLE_LSB) & 0x1)
+
+#define SPI_GET_RD_SRAM_LEVEL(RegBase)            \
+  (((MmioRead32(RegBase + SPI_REG_SDRAMLEVEL)) >> \
+  SPI_REG_SDRAMLEVEL_RD_LSB) & SPI_REG_SDRAMLEVEL_RD_MASK)
+
+#define SPI_GET_WR_SRAM_LEVEL(RegBase)            \
+  (((MmioRead32(RegBase + SPI_REG_SDRAMLEVEL)) >> \
+  SPI_REG_SDRAMLEVEL_WR_LSB) & SPI_REG_SDRAMLEVEL_WR_MASK)
+
+
+#define rounddown(x, y) ( \
+{                         \
+  typeof(x) __x = (x);    \
+  __x - (__x % (y));      \
+}                         \
+)
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+
+#define uswap_32(x) \
+  ((((x) & 0xff000000) >> 24) | \
+   (((x) & 0x00ff0000) >>  8) | \
+   (((x) & 0x0000ff00) <<  8) | \
+   (((x) & 0x000000ff) << 24))
+# define cpu_to_be32(x)    uswap_32(x)
+
+
+VOID 
+SpiApbControllerEnable (
+  IN UINT32 RegBaseAddr
+  );
+
+VOID 
+SpiApbControllerDisable (
+  IN UINT32 RegBaseAddr
+  );
+
+VOID 
+SpiApbDacModeEnable (
+  IN UINT32 RegBase
+  );
+
+VOID 
+SpiApbChipSelect (
+  IN UINT32 RegBase,
+  IN UINT32 ChipSel, 
+  IN UINT32 DecoderEnable
+  );
+
+VOID 
+SpiApbSetClkMode (
+  IN UINT32 RegBase, 
+  IN UINTN Mode
+  );
+
+VOID 
+SpiApbConfigBaudrateDiv (
+  IN UINT32 RegBase,
+  IN UINT32 RefClkHz, 
+  IN UINT32 SclkHz
+  );
+
+VOID 
+SpiApbDelay (
+  IN UINT32 RegBase,
+  IN UINT32 RefCLk, 
+  IN UINT32 SclkHz,
+  IN UINT32 TshslNs, 
+  IN UINT32 Tsd2dNs,
+  IN UINT32 TchshNs, 
+  IN UINT32 TslchNs
+  );
+
+VOID 
+SpiApbReadDataCapture (
+  IN UINT32 RegBase,
+  IN UINT32 ByPass, 
+  IN UINT32 Delay
+  );
+
+EFI_STATUS
+SpiApbCommandReadSetup (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  );
+
+EFI_STATUS
+SpiApbCommandRead (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  );
+
+EFI_STATUS
+SpiApbCommandWriteSetup (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  );
+
+EFI_STATUS
+SpiApbCommandWrite (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  );
+
+EFI_STATUS
+SpiApbReadSetup (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  );
+  
+EFI_STATUS
+SpiApbReadExecute (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  );
+
+EFI_STATUS
+SpiApbWriteSetup (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  );
+
+EFI_STATUS
+SpiApbWriteExecute (
+  IN SPI_DEVICE *Slave,
+  IN CONST SPI_MEM_OPS *Ops
+  );
+
+#endif //__SPI_APB_DXE_H__

+ 421 - 0
Silicon/StarFive/JH7110/Driver/Spi/SpiDxe/SpiDxe.c

@@ -0,0 +1,421 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Yuin Yee, Chew (John) <yuinyee.chew@starfivetech.com>.
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#include <Library/PcdLib.h>
+
+#include "SpiDxe.h"
+#include "SpiApbDxe.h"
+
+SPI_MASTER *mSpiMasterInstance;
+
+STATIC
+VOID
+SpiWriteSpeed (
+  IN SPI_DEVICE *Slave,
+  IN UINT32 Hz
+  )
+{
+  // Configure baudrate
+  SpiApbConfigBaudrateDiv(Slave->RegBase,
+               Slave->RefClkHz, Hz);
+
+  // Configure delay timing
+  SpiApbDelay(Slave->RegBase, Slave->RefClkHz, Hz,
+             Slave->TshslNs, Slave->Tsd2dNs,
+             Slave->TchshNs, Slave->TslchNs);
+}
+
+STATIC
+EFI_STATUS
+SpiReadId (
+  IN SPI_DEVICE *Slave,
+  UINT8 Len,
+  UINT8 *IdCode
+  )
+{
+  SPI_MEM_OPS Ops = SPINOR_READID_OP(0, IdCode, Len);
+  return SpiApbCommandRead(Slave, &Ops);
+}
+
+STATIC
+EFI_STATUS
+SpiCalibration (
+  IN SPI_DEVICE *Slave,
+  IN UINT32 Hz
+  )
+{
+  UINTN IdCode = 0, Temp = 0;
+  INTN i, RangeLow = -1, RangeHigh = -1;
+  EFI_STATUS Status;
+
+  // Start calibration with slowest clock speed at 1 MHz
+  SpiWriteSpeed(Slave, 1000000);
+
+  // Set the read data capture delay register to 0
+  SpiApbReadDataCapture(Slave->RegBase, 1, 0);
+
+  SpiApbControllerEnable(Slave->RegBase);
+
+  // Get flash ID value as reference
+  Status = SpiReadId(Slave, 3, (UINT8 *)&IdCode);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "Spi: Calibration failed (read id)\n"));
+    return EFI_ABORTED;
+  }
+
+  // Use the input speed
+  SpiWriteSpeed(Slave, Hz);
+
+  // Find high and low range
+  for (i = 0; i < SPI_READ_CAPTURE_MAX_DELAY; i++) {
+
+    SpiApbControllerDisable(Slave->RegBase);
+
+    // Change the read data capture delay register
+    SpiApbReadDataCapture(Slave->RegBase, 1, i);
+
+    SpiApbControllerEnable(Slave->RegBase);
+
+    // Read flash ID for comparison later
+    Status = SpiReadId(Slave, 3, (UINT8 *)&Temp);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "Spi: Calibration failed (read)\n"));
+      return EFI_ABORTED;
+    }
+
+    // Verify low range
+    if (RangeLow == -1 && Temp == IdCode) {
+      RangeLow = i;
+      continue;
+    }
+ 
+    // Verify high range
+    if (RangeLow != -1 && Temp != IdCode) {
+      RangeHigh = i - 1;
+      break;
+    }
+    RangeHigh = i;
+  }
+
+  if (RangeLow == -1) {
+    DEBUG ((DEBUG_ERROR, "Spi: Calibration failed (low range)\n"));
+    return EFI_ABORTED;
+  }
+
+  SpiApbControllerDisable(Slave->RegBase);
+
+  //
+  // Set the final value for read data capture delay register based
+  // on the calibrated value
+  //
+  SpiApbReadDataCapture(Slave->RegBase, 1, (RangeHigh + RangeLow) / 2);
+  DEBUG ((DEBUG_ERROR, "Spi: Read data capture delay calibrated to %d (%d - %d)\n",
+          (RangeHigh + RangeLow) / 2, RangeLow, RangeHigh));
+
+  Slave->CaliHz = Hz;
+
+  SpiApbControllerEnable(Slave->RegBase);
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SpiSetSpeed (
+  IN SPI_DEVICE *Slave,
+  IN UINT32 Hz
+  )
+{
+  EFI_STATUS  Status;
+
+  if (Hz > Slave->MaxHz) {
+    Hz = Slave->MaxHz;
+  }
+
+  SpiApbControllerDisable(Slave->RegBase);
+
+  //
+  // If ReadDelay value provided then dont need
+  // to do calibration
+  //
+  if (Slave->ReadDelay != 0xFFFF) {
+    SpiWriteSpeed(Slave, Hz);
+    SpiApbReadDataCapture(Slave->RegBase, 1,
+              Slave->ReadDelay);
+  } else if (Slave->PrevHz != Hz || Slave->CaliHz != Hz) {
+    Status = SpiCalibration(Slave, Hz);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    // Prevent re-calibration same speed is requested
+    Slave->PrevHz = Hz;
+  }
+
+  SpiApbControllerEnable(Slave->RegBase);
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SpiSetMode (
+  IN SPI_DEVICE *Slave,
+  IN UINT8 Mode
+  )
+{
+  UINTN SpiRegBase, RegMode;
+
+  SpiRegBase = Slave->RegBase;
+
+  SpiApbControllerDisable(SpiRegBase);
+
+  // Set SPI Mode
+  switch(Mode) {
+    case SPI_MODE0:
+      RegMode = SPI_MODE_0;
+      break;
+    case SPI_MODE1:
+      RegMode = SPI_MODE_1;
+      break;
+    case SPI_MODE2:
+      RegMode = SPI_MODE_2;
+      break;
+    case SPI_MODE3:
+      RegMode = SPI_MODE_3;
+      break;
+    default:
+      return EFI_INVALID_PARAMETER;
+      break;
+  }
+  SpiApbSetClkMode(SpiRegBase, RegMode);
+
+  // Enable Direct Access Controller
+  if (Slave->DacMode) {
+    SpiApbDacModeEnable(SpiRegBase);
+  }
+
+  SpiApbControllerEnable(SpiRegBase);
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SpiSetupTransfer (
+  IN SPI_MASTER_PROTOCOL *This,
+  IN SPI_DEVICE *Slave
+  )
+{
+  UINT32 MaxHz, SpiPrevFreq, SpiCaliFreq;
+  UINTN SpiRegBase;
+  EFI_STATUS Status;
+
+  SpiRegBase  = Slave->RegBase;
+  MaxHz       = Slave->MaxHz;
+  SpiPrevFreq = Slave->PrevHz;
+  SpiCaliFreq = Slave->CaliHz;
+
+  Status = SpiSetSpeed(Slave, Slave->MaxHz);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = SpiSetMode(Slave, Slave->Mode);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+
+}
+
+EFI_STATUS
+EFIAPI
+SpiTransfer (
+  IN SPI_DEVICE *Slave,
+  SPI_MEM_OPS *Ops
+  )
+{
+  UINT32 Mode;
+  UINTN RegBase = Slave->RegBase;
+  EFI_STATUS Status;
+
+  SpiApbChipSelect(RegBase, 0, Slave->IsDecodedCs);
+
+  if (Ops->Data.Dir == SPI_MEM_DATA_IN && Ops->Data.Buf.In) {
+    if (!Ops->Addr.NBytes)
+      Mode = SPI_CMD_READ;
+    else
+      Mode = SPI_READ;
+  } else {
+    if (!Ops->Addr.NBytes || !Ops->Data.Buf.Out)
+      Mode = SPI_CMD_WRITE;
+    else
+      Mode = SPI_WRITE;
+  }
+
+  switch (Mode) {
+  case SPI_CMD_READ:
+    Status = SpiApbCommandReadSetup(Slave, Ops);
+    if (!EFI_ERROR (Status)) {
+      Status = SpiApbCommandRead(Slave, Ops);
+        if (EFI_ERROR (Status)) {
+          return Status;
+        }
+      }
+    break;
+  case SPI_CMD_WRITE:
+    Status = SpiApbCommandWriteSetup(Slave, Ops);
+    if (!EFI_ERROR (Status)) {
+      Status = SpiApbCommandWrite(Slave, Ops);
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+    }
+    break;
+  case SPI_READ:
+    Status = SpiApbReadSetup(Slave, Ops);
+    if (!EFI_ERROR (Status)) {
+        Status = SpiApbReadExecute(Slave, Ops);
+        if (EFI_ERROR (Status)) {
+          return Status;
+        }
+      }
+    break;
+  case SPI_WRITE:
+    Status = SpiApbWriteSetup(Slave, Ops);
+      if (!EFI_ERROR (Status)) {
+        Status = SpiApbWriteExecute(Slave, Ops);
+        if (EFI_ERROR (Status)) {
+          return Status;
+        }
+      }
+    break;
+  }
+
+  return EFI_SUCCESS;
+}
+
+SPI_DEVICE *
+EFIAPI
+SpiSetupSlave (
+  IN SPI_MASTER_PROTOCOL *This,
+  IN SPI_DEVICE *Slave,
+  IN SPI_MODE Mode
+  )
+{
+  EFI_STATUS Status;
+
+  if (!Slave) {
+    Slave = AllocateZeroPool (sizeof(SPI_DEVICE));
+    if (Slave == NULL) {
+      DEBUG((DEBUG_ERROR, "Cannot allocate memory\n"));
+      return NULL;
+    }
+    Slave->Mode = Mode;
+  }
+
+  if (!Slave->Info) {
+    Slave->Info = AllocateZeroPool (sizeof(NOR_FLASH_INFO));
+    if (Slave->Info == NULL) {
+      DEBUG((DEBUG_ERROR, "Cannot allocate memory\n"));
+      return NULL;
+    }
+  }
+
+  Slave->RegBase   = PcdGet32(PcdSpiFlashRegBase);
+  Slave->AhbBase   = (VOID *)(UINTN)PcdGet64(PcdSpiFlashAhbBase);
+  Slave->AhbSize   = PcdGet32(PcdSpiFlashAhbSize);
+  Slave->FifoDepth = PcdGet16(PcdSpiFlashFifoDepth);
+  Slave->FifoWidth = PcdGet8(PcdSpiFlashFifoWidth);
+  Slave->TrigAdd   = PcdGet32(PcdSpiFlashTrigAdd);
+  Slave->ReadDelay = PcdGet16(PcdSpiFlashReadDelay);
+  Slave->MaxHz     = PcdGet32(PcdSpiFlashMaxHz);
+  Slave->RefClkHz  = PcdGet32(PcdSpiFlashRefClkHz);
+  Slave->TshslNs   = PcdGet32(PcdSpiFlashTshslNs);
+  Slave->Tsd2dNs   = PcdGet32(PcdSpiFlashTsd2dNs);
+  Slave->TchshNs   = PcdGet32(PcdSpiFlashTchshNs);
+  Slave->TslchNs   = PcdGet32(PcdSpiFlashTslchNs);
+
+  // Enable DAC mode if MMIO window is more than 8M
+  if (Slave->AhbSize >= SZ_8M) {
+    Slave->DacMode = TRUE;
+  }
+
+  Slave->WRDelay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, Slave->RefClkHz);
+
+  Status = SpiSetupTransfer (This, Slave);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "Fail to setup SPI slave\n"));
+    FreePool (Slave);
+    return NULL;
+  }
+
+  return Slave;
+}
+
+EFI_STATUS
+EFIAPI
+SpiFreeSlave (
+  IN SPI_DEVICE *Slave
+  )
+{
+  FreePool (Slave);
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+SpiMasterInitProtocol (
+  IN SPI_MASTER_PROTOCOL *SpiMasterProtocol
+  )
+{
+  SpiMasterProtocol->SetupDevice = SpiSetupSlave;
+  SpiMasterProtocol->FreeDevice  = SpiFreeSlave;
+  SpiMasterProtocol->Transfer    = SpiTransfer;
+  SpiMasterProtocol->SetSpeed = SpiSetSpeed;
+  SpiMasterProtocol->SetMode = SpiSetMode;
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+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;
+}

+ 45 - 0
Silicon/StarFive/JH7110/Driver/Spi/SpiDxe/SpiDxe.h

@@ -0,0 +1,45 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Yuin Yee, Chew (John) <yuinyee.chew@starfivetech.com>.
+ *
+ *  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)
+
+#define SPI_CMD_READ                    0
+#define SPI_CMD_WRITE                   1
+#define SPI_READ                        2
+#define SPI_WRITE                       3
+
+#define SZ_8M                           0x00800000
+#define NSEC_PER_SEC			              1000000000L
+#define DIV_ROUND_UP(n,d)               (((n) + (d) - 1) / (d))
+#define SPI_READ_CAPTURE_MAX_DELAY	    16
+
+typedef struct {
+  SPI_MASTER_PROTOCOL SpiMasterProtocol;
+  UINTN                   Signature;
+  EFI_HANDLE              Handle;
+  EFI_LOCK                Lock;
+} SPI_MASTER;
+
+#endif //__SPI_DXE_H__

+ 55 - 0
Silicon/StarFive/JH7110/Driver/Spi/SpiDxe/SpiDxe.inf

@@ -0,0 +1,55 @@
+#
+# 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
+  SpiApbDxe.c
+  SpiDxe.h
+  SpiApbDxe.h
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  Silicon/StarFive/JH7110/JH7110.dec
+
+[LibraryClasses]
+  DebugLib
+  DxeServicesTableLib
+  IoLib
+  MemoryAllocationLib
+  NorFlashInfoLib
+  TimerLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+  UefiRuntimeLib
+
+[FixedPcd]
+	gJH7110TokenSpaceGuid.PcdSpiFlashRegBase   
+	gJH7110TokenSpaceGuid.PcdSpiFlashAhbBase   
+  gJH7110TokenSpaceGuid.PcdSpiFlashAhbSize   
+	gJH7110TokenSpaceGuid.PcdSpiFlashFifoDepth 
+	gJH7110TokenSpaceGuid.PcdSpiFlashFifoWidth 
+	gJH7110TokenSpaceGuid.PcdSpiFlashTrigAdd   
+  gJH7110TokenSpaceGuid.PcdSpiFlashReadDelay     
+	gJH7110TokenSpaceGuid.PcdSpiFlashMaxHz     
+  gJH7110TokenSpaceGuid.PcdSpiFlashRefClkHz 
+	gJH7110TokenSpaceGuid.PcdSpiFlashTshslNs   
+	gJH7110TokenSpaceGuid.PcdSpiFlashTsd2dNs   
+	gJH7110TokenSpaceGuid.PcdSpiFlashTchshNs   
+	gJH7110TokenSpaceGuid.PcdSpiFlashTslchNs   
+
+[Protocols]
+  gJH7110SpiMasterProtocolGuid
+
+[Depex]
+  TRUE

+ 304 - 0
Silicon/StarFive/JH7110/Include/Protocol/Spi.h

@@ -0,0 +1,304 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Yuin Yee, Chew (John) <yuinyee.chew@starfivetech.com>.
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#ifndef __SPI_MASTER_PROTOCOL_H__
+#define __SPI_MASTER_PROTOCOL_H__
+
+#include <Library/NorFlashInfoLib.h>
+
+#ifndef BIT
+#define BIT(nr) (1 << (nr))
+#endif
+
+#define SPI_CPHA  BIT(0)  // CLock Phase
+#define SPI_CPOL  BIT(1)  // Clock Polarity
+#define SPI_MODE_0  (0|0)
+#define SPI_MODE_1  (0|SPI_CPHA)
+#define SPI_MODE_2  (SPI_CPOL|0)
+#define SPI_MODE_3  (SPI_CPOL|SPI_CPHA)
+
+extern EFI_GUID gMarvellSpiMasterProtocolGuid;
+
+typedef struct _SPI_MASTER_PROTOCOL SPI_MASTER_PROTOCOL;
+
+#define SPI_MEM_OP_CMD(__OpCode, __BusWidth)      \
+  {              \
+    .BusWidth = __BusWidth,        \
+    .OpCode = __OpCode,        \
+    .NBytes = 1,          \
+  }
+
+#define SPI_MEM_OP_ADDR(__NBytes, __Val, __BusWidth)    \
+  {              \
+    .NBytes = __NBytes,        \
+    .Val = __Val,          \
+    .BusWidth = __BusWidth,        \
+  }
+
+#define SPI_MEM_OP_NO_ADDR  { }
+
+#define SPI_MEM_OP_DUMMY(__NBytes, __BusWidth)      \
+  {              \
+    .NBytes = __NBytes,        \
+    .BusWidth = __BusWidth,        \
+  }
+
+#define SPI_MEM_OP_NO_DUMMY  { }
+
+#define SPI_MEM_OP_DATA_IN(__NBytes, __Buf, __BusWidth)    \
+  {              \
+    .Dir = SPI_MEM_DATA_IN,        \
+    .NBytes = __NBytes,        \
+    .Buf.In = __Buf,        \
+    .BusWidth = __BusWidth,        \
+  }
+
+#define SPI_MEM_OP_DATA_OUT(__NBytes, __Buf, __BusWidth)  \
+  {              \
+    .Dir = SPI_MEM_DATA_OUT,      \
+    .NBytes = __NBytes,        \
+    .Buf.Out = __Buf,        \
+    .BusWidth = __BusWidth,        \
+  }
+
+#define SPI_MEM_OP_NO_DATA  { }
+
+#define SPI_MEM_OP(__Cmd, __Addr, __Dummy, __Data)    \
+  {              \
+    .Cmd = __Cmd,          \
+    .Addr = __Addr,          \
+    .Dummy = __Dummy,        \
+    .Data = __Data,          \
+  }
+
+/**
+ * Standard SPI NOR flash operations
+ */
+#define SPINOR_RESET_OP            \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0xff, 1),        \
+       SPI_MEM_OP_NO_ADDR,          \
+       SPI_MEM_OP_NO_DUMMY,          \
+       SPI_MEM_OP_NO_DATA)
+
+#define SPINOR_WR_EN_DIS_OP(Enable)          \
+  SPI_MEM_OP(SPI_MEM_OP_CMD((Enable) ? 0x06 : 0x04, 1),    \
+       SPI_MEM_OP_NO_ADDR,          \
+       SPI_MEM_OP_NO_DUMMY,          \
+       SPI_MEM_OP_NO_DATA)
+
+#define SPINOR_READID_OP(NDummy, Buf, Len)        \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1),        \
+       SPI_MEM_OP_NO_ADDR,          \
+       SPI_MEM_OP_DUMMY(NDummy, 1),        \
+       SPI_MEM_OP_DATA_IN(Len, Buf, 1))
+
+#define SPINOR_SET_FEATURE_OP(reg, ValPtr)        \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1),        \
+       SPI_MEM_OP_ADDR(1, reg, 1),        \
+       SPI_MEM_OP_NO_DUMMY,          \
+       SPI_MEM_OP_DATA_OUT(1, ValPtr, 1))
+
+#define SPINOR_GET_FEATURE_OP(reg, ValPtr)        \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0x0f, 1),        \
+       SPI_MEM_OP_ADDR(1, reg, 1),        \
+       SPI_MEM_OP_NO_DUMMY,          \
+       SPI_MEM_OP_DATA_IN(1, ValPtr, 1))
+
+#define SPINOR_BLK_ERASE_OP(Addr)          \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0xd8, 1),        \
+       SPI_MEM_OP_ADDR(3, Addr, 1),        \
+       SPI_MEM_OP_NO_DUMMY,          \
+       SPI_MEM_OP_NO_DATA)
+
+#define SPINOR_PAGE_READ_OP(Addr)          \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0x13, 1),        \
+       SPI_MEM_OP_ADDR(3, Addr, 1),        \
+       SPI_MEM_OP_NO_DUMMY,          \
+       SPI_MEM_OP_NO_DATA)
+
+#define SPINOR_PAGE_READ_FROM_CACHE_OP(Fast, Addr, NDummy, Buf, Len)  \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(Fast ? 0x0b : 0x03, 1),    \
+       SPI_MEM_OP_ADDR(2, Addr, 1),        \
+       SPI_MEM_OP_DUMMY(NDummy, 1),        \
+       SPI_MEM_OP_DATA_IN(Len, Buf, 1))
+
+#define SPINOR_PAGE_READ_FROM_CACHE_X2_OP(Addr, NDummy, Buf, Len)  \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1),        \
+       SPI_MEM_OP_ADDR(2, Addr, 1),        \
+       SPI_MEM_OP_DUMMY(NDummy, 1),        \
+       SPI_MEM_OP_DATA_IN(Len, Buf, 2))
+
+#define SPINOR_PAGE_READ_FROM_CACHE_X4_OP(Addr, NDummy, Buf, Len)  \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1),        \
+       SPI_MEM_OP_ADDR(2, Addr, 1),        \
+       SPI_MEM_OP_DUMMY(NDummy, 1),        \
+       SPI_MEM_OP_DATA_IN(Len, Buf, 4))
+
+#define SPINOR_PAGE_READ_FROM_CACHE_DUALIO_OP(Addr, NDummy, Buf, Len)  \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1),        \
+       SPI_MEM_OP_ADDR(2, Addr, 2),        \
+       SPI_MEM_OP_DUMMY(NDummy, 2),        \
+       SPI_MEM_OP_DATA_IN(Len, Buf, 2))
+
+#define SPINOR_PAGE_READ_FROM_CACHE_QUADIO_OP(Addr, NDummy, Buf, Len)  \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1),        \
+       SPI_MEM_OP_ADDR(2, Addr, 4),        \
+       SPI_MEM_OP_DUMMY(NDummy, 4),        \
+       SPI_MEM_OP_DATA_IN(Len, Buf, 4))
+
+#define SPINOR_PROG_EXEC_OP(Addr)          \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1),        \
+       SPI_MEM_OP_ADDR(3, Addr, 1),        \
+       SPI_MEM_OP_NO_DUMMY,          \
+       SPI_MEM_OP_NO_DATA)
+
+#define SPINOR_PROG_LOAD(Reset, Addr, Buf, Len)      \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(Reset ? 0x02 : 0x84, 1),    \
+       SPI_MEM_OP_ADDR(2, Addr, 1),        \
+       SPI_MEM_OP_NO_DUMMY,          \
+       SPI_MEM_OP_DATA_OUT(Len, Buf, 1))
+
+#define SPINOR_PROG_LOAD_X4(Reset, Addr, Buf, Len)      \
+  SPI_MEM_OP(SPI_MEM_OP_CMD(Reset ? 0x32 : 0x34, 1),    \
+       SPI_MEM_OP_ADDR(2, Addr, 1),        \
+       SPI_MEM_OP_NO_DUMMY,          \
+       SPI_MEM_OP_DATA_OUT(Len, Buf, 4))
+
+enum spi_mem_data_dir {
+  SPI_MEM_NO_DATA,
+  SPI_MEM_DATA_IN,
+  SPI_MEM_DATA_OUT,
+};
+
+typedef struct {
+  struct {
+    UINT8 NBytes;
+    UINT8 BusWidth;
+    UINT8 Dtr : 1;
+    UINT16 OpCode;
+  } Cmd;
+
+  struct {
+    UINT8 NBytes;
+    UINT8 BusWidth;
+    UINT8 Dtr : 1;
+    UINT64 Val;
+  } Addr;
+
+  struct {
+    UINT8 NBytes;
+    UINT8 BusWidth;
+    UINT8 Dtr : 1;
+  } Dummy;
+
+  struct {
+    UINT8 BusWidth;
+    UINT8 Dtr : 1;
+    enum spi_mem_data_dir Dir;
+    UINT32 NBytes;
+    union {
+      VOID *In;
+      CONST VOID *Out;
+    } Buf;
+  } Data;
+}SPI_MEM_OPS;
+
+typedef enum {
+  SPI_MODE0, // CPOL = 0 & CPHA = 0
+  SPI_MODE1, // CPOL = 0 & CPHA = 1
+  SPI_MODE2, // CPOL = 1 & CPHA = 0
+  SPI_MODE3  // CPOL = 1 & CPHA = 1
+} SPI_MODE;
+
+typedef struct {
+  SPI_MODE Mode;
+  UINT32 AddrSize;
+  NOR_FLASH_INFO *Info;
+
+  UINT32  RegBase;
+  UINT32  MaxHz;
+  UINT32  PrevHz;
+  UINT32  CaliHz;
+  INTN    ReadDelay;
+  UINT32  RefClkHz;
+  UINT8   CaliCs;
+  UINT8   IsDecodedCs;
+
+  VOID*   AhbBase;
+  UINT32  AhbSize;
+  UINT16  FifoDepth;
+  UINT8   FifoWidth;
+  UINT8   TrigAdd;
+  BOOLEAN DacMode;
+  UINT32  WRDelay;
+
+  /* Flash parameters */
+  UINT8  TshslNs;
+  UINT8  Tsd2dNs;
+  UINT8  TchshNs;
+  UINT8  TslchNs;
+
+  /* Transaction protocol parameters. */
+  UINT8  InstWidth;
+  UINT8  AddrWidth;
+  UINT8  DataWidth;
+  UINT8  Dtr;
+
+} SPI_DEVICE;
+
+typedef
+EFI_STATUS
+(EFIAPI *MV_SPI_INIT) (
+  IN SPI_MASTER_PROTOCOL *This
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *MV_SPI_TRANSFER) (
+  IN SPI_DEVICE *Slave,
+  IN SPI_MEM_OPS *op
+  );
+
+typedef
+SPI_DEVICE *
+(EFIAPI *MV_SPI_SETUP_DEVICE) (
+  IN SPI_MASTER_PROTOCOL *This,
+  IN SPI_DEVICE *Slave,
+  IN SPI_MODE Mode
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *MV_SPI_FREE_DEVICE) (
+  IN SPI_DEVICE *SpiDev
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *MV_SPI_SET_SPEED) (
+  IN SPI_DEVICE *Slave,
+  IN UINT32 Hz
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *MV_SPI_SET_MODE) (
+  IN SPI_DEVICE *Slave,
+  IN UINT8 Mode
+  );
+
+struct _SPI_MASTER_PROTOCOL {
+  MV_SPI_INIT         Init;
+  MV_SPI_TRANSFER     Transfer;
+  MV_SPI_SETUP_DEVICE SetupDevice;
+  MV_SPI_FREE_DEVICE  FreeDevice;
+  MV_SPI_SET_SPEED   SetSpeed;
+  MV_SPI_SET_MODE    SetMode;
+};
+
+#endif // __SPI_MASTER_PROTOCOL_H__