Browse Source

qspi flash: add qspi flash layer

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

+ 457 - 0
Silicon/StarFive/JH7110/Driver/Spi/SpiFlashDxe/SpiFlashDxe.c

@@ -0,0 +1,457 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Yuin Yee, Chew (John) <yuinyee.chew@starfivetech.com>.
+ *
+ *  SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#include "SpiFlashDxe.h"
+
+STATIC EFI_EVENT    mSpiFlashVirtualAddrChangeEvent;
+SPI_MASTER_PROTOCOL *SpiMasterProtocol;
+SPI_FLASH_INSTANCE  *mSpiFlashInstance;
+
+STATIC
+EFI_STATUS
+SpiFlashWriteEnableCmd (
+  IN  SPI_DEVICE   *Slave
+  )
+{
+  EFI_STATUS Status;
+
+  SPI_MEM_OPS Ops = SPINOR_WR_EN_DIS_OP(TRUE);
+
+  // Send write enable command
+  Status = SpiMasterProtocol->Transfer (Slave, &Ops);
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SpiFlashWriteDisableCmd (
+  IN  SPI_DEVICE   *Slave
+  )
+{
+  EFI_STATUS Status;
+
+  SPI_MEM_OPS Ops = SPINOR_WR_EN_DIS_OP(FALSE);
+
+  // Send write disable command
+  Status = SpiMasterProtocol->Transfer (Slave, &Ops);
+
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+SpiFlashWriteCommon (
+  IN SPI_DEVICE *Slave,
+  IN UINT32 WriteAddr,
+  IN UINT32 Length,
+  IN UINT8* Buffer,
+  IN UINT32 BufferLength
+  )
+{
+  EFI_STATUS Status;
+  UINT32 Counter = 0xFFFFF;
+  UINT8 PollBitMsk = STATUS_REG_POLL_WIP_MSK;
+  UINT8 CheckStatus = 0x0;
+
+  Status = SpiFlashWriteEnableCmd(Slave);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "SpiFlash: Error while setting write_enable\n"));
+    return Status;
+  }
+
+  SPI_MEM_OPS OpsPgProg = SPI_MEM_OP(SPI_MEM_OP_CMD(CMD_PAGE_PROGRAM, 0),
+                      SPI_MEM_OP_ADDR(3, WriteAddr, 0),
+                      SPI_MEM_OP_NO_DUMMY,
+                      SPI_MEM_OP_DATA_OUT(BufferLength, (VOID*)Buffer, 0));
+
+  // Page program command - write to flash
+  Status = SpiMasterProtocol->Transfer(Slave, &OpsPgProg);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  // Poll status register
+  UINT8 Len = 2;
+  UINT8 List[2] = {0};
+  UINT8 *State = List;
+  SPI_MEM_OPS OpsRdSts = SPI_MEM_OP(SPI_MEM_OP_CMD(CMD_READ_STATUS, 1),
+                      SPI_MEM_OP_NO_ADDR,
+                      SPI_MEM_OP_NO_DUMMY,
+                      SPI_MEM_OP_DATA_IN(Len, (VOID *)State, 1));
+
+  Status = SpiMasterProtocol->Transfer(Slave, &OpsRdSts);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+
+
+  do {
+    Status = SpiMasterProtocol->Transfer(Slave, &OpsRdSts);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "SpiFlash: Spi terror while reading status\n"));
+      return Status;
+    }
+    Counter--;
+    if ((*State & PollBitMsk) == CheckStatus) {
+      break;
+    }
+  } while (Counter > 0);
+  if (Counter == 0) {
+    DEBUG((DEBUG_ERROR, "SpiFlash: Timeout while writing to spi flash\n"));
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashErase (
+  IN SPI_DEVICE *Slave,
+  IN UINTN Offset,
+  IN UINTN Length
+  )
+{
+  EFI_STATUS Status;
+  UINT32 EraseAddr;
+  UINTN EraseSize;
+  UINT8 EraseCmd;
+  UINT32 Counter = 0xFFFFF;
+  UINT8 PollBitMsk = STATUS_REG_POLL_WIP_MSK;
+  UINT8 CheckStatus = 0x0;
+
+
+  // Get erase flash size
+  if (Slave->Info->Flags & NOR_FLASH_ERASE_4K) {
+    EraseCmd = CMD_ERASE_4K;
+    EraseSize = SIZE_4KB;
+  } else if (Slave->Info->Flags & NOR_FLASH_ERASE_32K) {
+    EraseCmd = CMD_ERASE_32K;
+    EraseSize = SIZE_32KB;
+  } else {
+    EraseCmd = CMD_ERASE_64K;
+    EraseSize = Slave->Info->SectorSize;
+  }
+
+  // Verify input parameters
+  if (Offset % EraseSize || Length % EraseSize) {
+    DEBUG((DEBUG_ERROR, "SpiFlash: Either erase offset or length "
+      "is not multiple of erase size\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  while (Length) {
+    EraseAddr = Offset;
+
+    Status = SpiFlashWriteEnableCmd(Slave);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "SpiFlash: Error while setting write_enable\n"));
+      return Status;
+    }
+  
+    // Send erase command
+    SPI_MEM_OPS OpsErase = SPI_MEM_OP(SPI_MEM_OP_CMD(EraseCmd, 1),
+                        SPI_MEM_OP_ADDR(3, EraseAddr, 1),
+                        SPI_MEM_OP_NO_DUMMY,
+                        SPI_MEM_OP_NO_DATA);
+
+    Status = SpiMasterProtocol->Transfer(Slave, &OpsErase);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "SpiFlash: Spi erase fail\n"));
+      return Status;
+    }
+
+    // Polling for status register
+    UINT8 Len = 2;
+    UINT8 List[2] = {0};
+    UINT8 *State = List;
+    SPI_MEM_OPS OpsRdSts = SPI_MEM_OP(SPI_MEM_OP_CMD(CMD_READ_STATUS, 1),
+                            SPI_MEM_OP_NO_ADDR,
+                            SPI_MEM_OP_NO_DUMMY,
+                            SPI_MEM_OP_DATA_IN(Len, (VOID *)State, 1));
+
+    do {
+      Status = SpiMasterProtocol->Transfer(Slave, &OpsRdSts);
+      if (EFI_ERROR (Status)) {
+        DEBUG((DEBUG_ERROR, "SpiFlash: Spi error while reading status\n"));
+        return Status;
+      }
+      Counter--;
+      if ((*State & PollBitMsk) == CheckStatus) {
+        break;
+      }
+    } while (Counter > 0);
+
+    if (Counter == 0) {
+      DEBUG((DEBUG_ERROR, "SpiFlash: Timeout while writing to spi flash\n"));
+      return EFI_TIMEOUT;
+    }
+
+    Offset += EraseSize;
+    Length -= EraseSize;
+  }
+
+  Status = SpiFlashWriteDisableCmd(Slave);
+  if (EFI_ERROR (Status)) {
+   DEBUG((DEBUG_ERROR, "SpiFlash: Error while setting write_disable\n"));
+   return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashRead (
+  IN SPI_DEVICE *Slave,
+  IN UINT32     Offset,
+  IN UINTN      Length,
+  IN VOID       *Buf
+  )
+{
+  EFI_STATUS Status;
+  UINT32 ReadAddr, ReadLength, RemainLength;
+  UINTN BankSel = 0;
+
+  while (Length) {
+    ReadAddr = Offset;
+
+    RemainLength = (SPI_FLASH_16MB_BOUND * (BankSel + 1)) - Offset;
+    ReadLength = (Length < RemainLength) ? Length : RemainLength;
+
+    // Send read command
+    SPI_MEM_OPS OpsRead = SPI_MEM_OP(SPI_MEM_OP_CMD(CMD_READ_DATA, 1),
+                            SPI_MEM_OP_ADDR(3, Offset, 1),
+                            SPI_MEM_OP_NO_DUMMY,
+                            SPI_MEM_OP_DATA_IN(ReadLength, Buf, 1));
+
+    Status = SpiMasterProtocol->Transfer(Slave, &OpsRead);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "SpiFlash: Spi error while reading data\n"));
+      return Status;
+    }
+
+    Offset += ReadLength;
+    Length -= ReadLength;
+    Buf = (VOID*)((UINTN)Buf + ReadLength);
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashWrite (
+  IN SPI_DEVICE *Slave,
+  IN UINT32     Offset,
+  IN UINTN      Length,
+  IN VOID       *Buf
+  )
+{
+  EFI_STATUS Status;
+  UINTN ByteAddr, ChunkLength, ActualIndex, PageSize;
+  UINT32 WriteAddr;
+
+  PageSize = Slave->Info->PageSize;
+
+  for (ActualIndex = 0; ActualIndex < Length; ActualIndex += ChunkLength) {
+    WriteAddr = Offset;
+
+    ByteAddr = Offset % PageSize;
+    ChunkLength = MIN(Length - ActualIndex, (UINT64) (PageSize - ByteAddr));
+
+    Status = SpiFlashWriteCommon (
+      Slave, WriteAddr, Slave->AddrSize + 1,
+      (VOID*)((UINTN)Buf + ActualIndex), ChunkLength
+      );
+
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "SpiFlash: Error while programming write address\n"));
+      return Status;
+    }
+
+    Offset += ChunkLength;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+SpiFlashReadId (
+  IN     SPI_DEVICE *SpiDev,
+  IN     BOOLEAN     UseInRuntime
+  )
+{
+  EFI_STATUS Status;
+  UINT8 Id[NOR_FLASH_MAX_ID_LEN];
+
+  SPI_MEM_OPS Ops = SPINOR_READID_OP(0, Id, 3);
+  Status = SpiMasterProtocol->Transfer(SpiDev, &Ops);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "ReadId: Spi error while reading id\n"));
+    return Status;
+  }
+
+  Status = NorFlashGetInfo (Id, &SpiDev->Info, UseInRuntime);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Unrecognized JEDEC Id bytes: 0x%02x%02x%02x\n",
+      __FUNCTION__,
+      Id[0],
+      Id[1],
+      Id[2]));
+    return Status;
+  }
+
+  NorFlashPrintInfo (SpiDev->Info);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+SpiFlashInit (
+  IN SPI_FLASH_PROTOCOL *This,
+  IN SPI_DEVICE *Slave
+  )
+{
+  EFI_STATUS Status;
+  UINT8 StatusRegister;
+
+  Slave->AddrSize = (Slave->Info->Flags & NOR_FLASH_4B_ADDR) ? 4 : 3;
+
+  if (Slave->AddrSize == 4) {
+    Status = SpiFlashWriteEnableCmd (Slave);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "SpiFlash: Error while setting write_enable\n"));
+      return Status;
+    }
+
+    // Enable 4byte addressing
+    SPI_MEM_OPS Ops4BAddEn = SPI_MEM_OP(SPI_MEM_OP_CMD(CMD_4B_ADDR_ENABLE, 1),
+            SPI_MEM_OP_NO_ADDR,
+            SPI_MEM_OP_NO_DUMMY,
+            SPI_MEM_OP_NO_DATA);
+    Status = SpiMasterProtocol->Transfer (Slave, &Ops4BAddEn);
+    if (EFI_ERROR (Status)) {
+      DEBUG((DEBUG_ERROR, "SpiFlash: Error while setting 4B address\n"));
+      return Status;
+    }
+  }
+
+  Status = SpiFlashWriteEnableCmd (Slave);
+  if (EFI_ERROR (Status)) {
+  DEBUG((DEBUG_ERROR, "SpiFlash: Error while setting write_enable\n"));
+    return Status;
+  }
+
+  // Initialize flash status register
+  StatusRegister = 0x0;
+  SPI_MEM_OPS OpsWrSts = SPI_MEM_OP(SPI_MEM_OP_CMD(CMD_WRITE_STATUS_REG, 1),
+          SPI_MEM_OP_NO_ADDR,
+          SPI_MEM_OP_NO_DUMMY,
+          SPI_MEM_OP_DATA_OUT(1, (VOID *)&StatusRegister, 1));
+  Status = SpiMasterProtocol->Transfer (Slave, &OpsWrSts);
+  if (EFI_ERROR (Status)) {
+    DEBUG((DEBUG_ERROR, "SpiFlash: Error while writing status register\n"));
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SpiFlashInitProtocol (
+  IN SPI_FLASH_PROTOCOL *SpiFlashProtocol
+  )
+{
+  SpiFlashProtocol->Init   = SpiFlashInit;
+  SpiFlashProtocol->ReadId = SpiFlashReadId;
+  SpiFlashProtocol->Read   = SpiFlashRead;
+  SpiFlashProtocol->Write  = SpiFlashWrite;
+  SpiFlashProtocol->Erase  = SpiFlashErase;
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+VOID
+EFIAPI
+SpiFlashVirtualNotifyEvent (
+  IN EFI_EVENT        Event,
+  IN VOID             *Context
+  )
+{
+  EfiConvertPointer (0x0, (VOID**)&SpiMasterProtocol->Transfer);
+  EfiConvertPointer (0x0, (VOID**)&SpiMasterProtocol);
+}
+
+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, "SpiFlash: Cannot locate SPI Master protocol\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
+  mSpiFlashInstance = AllocateRuntimeZeroPool (sizeof (SPI_FLASH_INSTANCE));
+  if (mSpiFlashInstance == NULL) {
+    DEBUG((DEBUG_ERROR, "SpiFlash: Cannot allocate memory\n"));
+    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, "SpiFlash: Cannot install SPI flash protocol\n"));
+    goto ErrorInstallProto;
+  }
+
+  // Register for the virtual address change event
+  Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  SpiFlashVirtualNotifyEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &mSpiFlashVirtualAddrChangeEvent);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: Failed to register VA change event\n", __FUNCTION__));
+    goto ErrorCreateEvent;
+  }
+
+  return EFI_SUCCESS;
+
+ErrorCreateEvent:
+  gBS->UninstallMultipleProtocolInterfaces (&mSpiFlashInstance->Handle,
+    &gJH7110SpiFlashProtocolGuid,
+    NULL);
+
+ErrorInstallProto:
+  FreePool (mSpiFlashInstance);
+
+  return EFI_SUCCESS;
+}

+ 61 - 0
Silicon/StarFive/JH7110/Driver/Spi/SpiFlashDxe/SpiFlashDxe.h

@@ -0,0 +1,61 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Yuin Yee, Chew (John) <yuinyee.chew@starfivetech.com>.
+ *
+ *  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 CMD_READ_ID                     0x9f
+#define READ_STATUS_REG_CMD             0x0b
+#define CMD_WRITE_ENABLE                0x06
+#define CMD_READ_STATUS                 0x05
+#define CMD_FLAG_STATUS                 0x70
+#define CMD_WRITE_STATUS_REG            0x01
+#define CMD_READ_ARRAY_FAST             0x0b
+#define CMD_PAGE_PROGRAM                0x02
+#define CMD_BANK_WRITE                  0xc5
+#define CMD_BANKADDR_BRWR               0x17
+#define CMD_ERASE_4K                    0x20
+#define CMD_ERASE_32K                   0x52
+#define CMD_ERASE_64K                   0xd8
+#define CMD_4B_ADDR_ENABLE              0xb7
+#define CMD_READ_DATA                   0x03
+
+#define STATUS_REG_POLL_WIP_MSK         BIT(0)
+#define STATUS_REG_POLL_PEC_MSK         BIT(7)
+
+#define SPI_FLASH_16MB_BOUND            0x1000000
+
+typedef struct {
+  SPI_FLASH_PROTOCOL  SpiFlashProtocol;
+  UINTN                   Signature;
+  EFI_HANDLE              Handle;
+} SPI_FLASH_INSTANCE;
+
+EFI_STATUS
+EFIAPI
+EfiSpiFlashInit (
+  IN SPI_FLASH_PROTOCOL *This,
+  IN SPI_DEVICE *Slave
+  );
+
+#endif //__SPI_FLASH_DXE_H__

+ 40 - 0
Silicon/StarFive/JH7110/Driver/Spi/SpiFlashDxe/SpiFlashDxe.inf

@@ -0,0 +1,40 @@
+#
+# 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/JH7110/JH7110.dec
+
+[LibraryClasses]
+  DebugLib
+  MemoryAllocationLib
+  NorFlashInfoLib
+  TimerLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+  UefiRuntimeLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid
+
+[Protocols]
+  gJH7110SpiFlashProtocolGuid
+  gJH7110SpiMasterProtocolGuid
+
+[Depex]
+  gJH7110SpiMasterProtocolGuid

+ 67 - 0
Silicon/StarFive/JH7110/Include/Protocol/SpiFlash.h

@@ -0,0 +1,67 @@
+/** @file
+ *
+ *  Copyright (c) 2023, Yuin Yee, Chew (John) <yuinyee.chew@starfivetech.com>.
+ *
+ *  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>
+
+extern EFI_GUID gMarvellSpiFlashProtocolGuid;
+
+typedef struct _SPI_FLASH_PROTOCOL SPI_FLASH_PROTOCOL;
+
+typedef
+EFI_STATUS
+(EFIAPI *SPI_FLASH_INIT) (
+  IN SPI_FLASH_PROTOCOL *This,
+  IN SPI_DEVICE *SpiDev
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *SPI_FLASH_READ_ID) (
+  IN SPI_DEVICE *SpiDev,
+  IN BOOLEAN UseInRuntime
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *SPI_FLASH_READ) (
+  IN SPI_DEVICE *SpiDev,
+  IN UINT32 Address,
+  IN UINTN DataByteCount,
+  IN VOID *Buffer
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *SPI_FLASH_WRITE) (
+  IN SPI_DEVICE *SpiDev,
+  IN UINT32 Address,
+  IN UINTN DataByteCount,
+  IN VOID *Buffer
+  );
+
+typedef
+EFI_STATUS
+(EFIAPI *SPI_FLASH_ERASE) (
+  IN SPI_DEVICE *SpiDev,
+  IN UINTN Address,
+  IN UINTN DataByteCount
+  );
+
+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;
+};
+
+#endif // __SPI_FLASH_PROTOCOL_H__