/** @file  NorFlashDxe.h

  Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
  Copyright (c) 2017, Socionext Inc. All rights reserved.<BR>
  Copyright (c) 2017, Linaro, Ltd. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#ifndef __NOR_FLASH_DXE_H__
#define __NOR_FLASH_DXE_H__


#include <PiDxe.h>

#include <Guid/EventGroup.h>

#include <Protocol/BlockIo.h>
#include <Protocol/DiskIo.h>
#include <Protocol/FirmwareVolumeBlock.h>

#include <Library/DebugLib.h>
#include <Library/IoLib.h>

#include "Fip006Reg.h"

#define NOR_FLASH_ERASE_RETRY                     10

#define GET_NOR_BLOCK_ADDRESS(BaseAddr, Lba, LbaSize) \
                                      ((BaseAddr) + (UINTN)((Lba) * (LbaSize)))

#define NOR_FLASH_SIGNATURE          SIGNATURE_32('S', 'n', 'o', 'r')
#define INSTANCE_FROM_FVB_THIS(a)    CR(a, NOR_FLASH_INSTANCE, FvbProtocol, \
                                        NOR_FLASH_SIGNATURE)
#define INSTANCE_FROM_BLKIO_THIS(a)  CR(a, NOR_FLASH_INSTANCE, BlockIoProtocol,\
                                        NOR_FLASH_SIGNATURE)
#define INSTANCE_FROM_DISKIO_THIS(a) CR(a, NOR_FLASH_INSTANCE, DiskIoProtocol, \
                                        NOR_FLASH_SIGNATURE)

#define CSDC(Data, Cont, Trp, Dec) \
  (((Data) << 8) | (((Cont) & 1) << 3) | (((Trp) & 3) << 1) | ((Dec) & 1))

#define CSDC_TRP_MBM              0
#define CSDC_TRP_DUAL             1
#define CSDC_TRP_QUAD             2
#define CSDC_TRP_SINGLE           3

#define CSDC_DEC_LEAVE_ASIS       0
#define CSDC_DEC_DECODE           1

#define CSDC_CONT_NON_CONTINUOUS  0
#define CSDC_CONT_CONTINUOUS      1

#define CSDC_ADDRESS_7_0          0x0
#define CSDC_ADDRESS_15_8         0x1
#define CSDC_ADDRESS_23_16        0x2
#define CSDC_ADDRESS_31_24        0x3
#define CSDC_HIGH_Z               0x4
#define CSDC_END                  0x7

typedef struct {
  UINT8       Code;
  BOOLEAN     AddrAccess;
  BOOLEAN     AddrMode4Byte;
  BOOLEAN     HighZ;
  BOOLEAN     ReadWrite;
  UINT8       CscfgMbm;
  UINT8       CsdcTrp;
} CSDC_DEFINITION;

typedef struct _NOR_FLASH_INSTANCE                NOR_FLASH_INSTANCE;

typedef EFI_STATUS (*NOR_FLASH_INITIALIZE)        (NOR_FLASH_INSTANCE* Instance);

#pragma pack(1)
typedef struct {
  VENDOR_DEVICE_PATH                  Vendor;
  UINT8                               Index;
  EFI_DEVICE_PATH_PROTOCOL            End;
} NOR_FLASH_DEVICE_PATH;
#pragma pack()

struct _NOR_FLASH_INSTANCE {
  UINT32                              Signature;
  EFI_HANDLE                          Handle;

  BOOLEAN                             Initialized;
  NOR_FLASH_INITIALIZE                Initialize;

  UINTN                               HostRegisterBaseAddress;
  UINTN                               DeviceBaseAddress;
  UINTN                               RegionBaseAddress;
  UINTN                               Size;
  UINTN                               BlockSize;
  UINTN                               LastBlock;
  EFI_LBA                             StartLba;
  EFI_LBA                             OffsetLba;

  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL FvbProtocol;
  VOID*                               ShadowBuffer;

  NOR_FLASH_DEVICE_PATH               DevicePath;

  UINT32                              Flags;
#define NOR_FLASH_POLL_FSR      BIT0
};

typedef struct {
  EFI_TPL         OriginalTPL;
  BOOLEAN         InterruptsEnabled;
} NOR_FLASH_LOCK_CONTEXT;

VOID
EFIAPI
NorFlashLock (
  NOR_FLASH_LOCK_CONTEXT    *Context
  );

VOID
EFIAPI
NorFlashUnlock (
  NOR_FLASH_LOCK_CONTEXT    *Context
  );

EFI_STATUS
NorFlashReadCfiData (
  IN  UINTN                   DeviceBaseAddress,
  IN  UINTN                   CFI_Offset,
  IN  UINT32                  NumberOfBytes,
  OUT UINT32                  *Data
  );

EFI_STATUS
NorFlashWriteBuffer (
  IN NOR_FLASH_INSTANCE     *Instance,
  IN UINTN                  TargetAddress,
  IN UINTN                  BufferSizeInBytes,
  IN UINT32                 *Buffer
  );

extern UINTN     mFlashNvStorageVariableBase;

EFI_STATUS
NorFlashCreateInstance (
  IN UINTN                  HostRegisterBase,
  IN UINTN                  NorFlashDeviceBase,
  IN UINTN                  NorFlashRegionBase,
  IN UINTN                  NorFlashSize,
  IN UINT32                 Index,
  IN UINT32                 BlockSize,
  IN BOOLEAN                HasVarStore,
  OUT NOR_FLASH_INSTANCE**  NorFlashInstance
  );

EFI_STATUS
EFIAPI
NorFlashFvbInitialize (
  IN NOR_FLASH_INSTANCE* Instance
  );

EFI_STATUS
ValidateFvHeader (
  IN  NOR_FLASH_INSTANCE *Instance
  );

EFI_STATUS
InitializeFvAndVariableStoreHeaders (
  IN NOR_FLASH_INSTANCE *Instance
  );

EFI_STATUS
EFIAPI
FvbGetAttributes(
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL     *This,
  OUT       EFI_FVB_ATTRIBUTES_2                    *Attributes
  );

EFI_STATUS
EFIAPI
FvbSetAttributes(
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL     *This,
  IN OUT    EFI_FVB_ATTRIBUTES_2                    *Attributes
  );

EFI_STATUS
EFIAPI
FvbGetPhysicalAddress(
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL     *This,
  OUT       EFI_PHYSICAL_ADDRESS                    *Address
  );

EFI_STATUS
EFIAPI
FvbGetBlockSize(
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL     *This,
  IN        EFI_LBA                                 Lba,
  OUT       UINTN                                   *BlockSize,
  OUT       UINTN                                   *NumberOfBlocks
  );

EFI_STATUS
EFIAPI
FvbRead(
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL     *This,
  IN        EFI_LBA                                 Lba,
  IN        UINTN                                   Offset,
  IN OUT    UINTN                                   *NumBytes,
  IN OUT    UINT8                                   *Buffer
  );

EFI_STATUS
EFIAPI
FvbWrite(
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL     *This,
  IN        EFI_LBA                                 Lba,
  IN        UINTN                                   Offset,
  IN OUT    UINTN                                   *NumBytes,
  IN        UINT8                                   *Buffer
  );

EFI_STATUS
EFIAPI
FvbEraseBlocks(
  IN CONST  EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL     *This,
  ...
  );

//
// NorFlashDxe.c
//

EFI_STATUS
NorFlashUnlockAndEraseSingleBlock (
  IN NOR_FLASH_INSTANCE     *Instance,
  IN UINTN                  BlockAddress
  );

EFI_STATUS
NorFlashWriteSingleBlock (
  IN        NOR_FLASH_INSTANCE   *Instance,
  IN        EFI_LBA               Lba,
  IN        UINTN                 Offset,
  IN OUT    UINTN                *NumBytes,
  IN        UINT8                *Buffer
  );

EFI_STATUS
NorFlashWriteBlocks (
  IN  NOR_FLASH_INSTANCE *Instance,
  IN  EFI_LBA           Lba,
  IN  UINTN             BufferSizeInBytes,
  IN  VOID              *Buffer
  );

EFI_STATUS
NorFlashReadBlocks (
  IN NOR_FLASH_INSTANCE   *Instance,
  IN EFI_LBA              Lba,
  IN UINTN                BufferSizeInBytes,
  OUT VOID                *Buffer
  );

EFI_STATUS
NorFlashRead (
  IN NOR_FLASH_INSTANCE   *Instance,
  IN EFI_LBA              Lba,
  IN UINTN                Offset,
  IN UINTN                BufferSizeInBytes,
  OUT VOID                *Buffer
  );

EFI_STATUS
NorFlashWrite (
  IN        NOR_FLASH_INSTANCE   *Instance,
  IN        EFI_LBA               Lba,
  IN        UINTN                 Offset,
  IN OUT    UINTN                *NumBytes,
  IN        UINT8                *Buffer
  );

EFI_STATUS
NorFlashReset (
  IN  NOR_FLASH_INSTANCE *Instance
  );

EFI_STATUS
NorFlashReadID (
  IN  NOR_FLASH_INSTANCE  *Instance,
  OUT UINT8               JedecId[3]
  );

typedef struct {
  UINTN    DeviceBaseAddress;       // Start address of the Device Base Address (DBA)
  UINTN    RegionBaseAddress;       // Start address of one single region
  UINTN    Size;
  UINTN    BlockSize;
} NOR_FLASH_DESCRIPTION;

EFI_STATUS
NorFlashPlatformGetDevices (
  OUT NOR_FLASH_DESCRIPTION   **NorFlashDevices,
  OUT UINT32                  *Count
  );

#define SPINOR_SR_WIP                 BIT0  // Write in progress
#define SPINOR_FSR_READY              BIT7  // Flag Status Register: ready

#define SPINOR_OP_WREN                0x06  // Write enable
#define SPINOR_OP_WRDIS               0x04  // Write enable
#define SPINOR_OP_RDSR                0x05  // Read status register
#define SPINOR_OP_WRSR                0x01  // Write status register 1 byte
#define SPINOR_OP_RDSR2               0x3f  // Read status register 2
#define SPINOR_OP_WRSR2               0x3e  // Write status register 2
#define SPINOR_OP_READ                0x03  // Read data bytes (low frequency)
#define SPINOR_OP_READ_FAST           0x0b  // Read data bytes (high frequency)
#define SPINOR_OP_READ_1_1_2          0x3b  // Read data bytes (Dual Output SPI)
#define SPINOR_OP_READ_1_2_2          0xbb  // Read data bytes (Dual I/O SPI)
#define SPINOR_OP_READ_1_1_4          0x6b  // Read data bytes (Quad Output SPI)
#define SPINOR_OP_READ_1_4_4          0xeb  // Read data bytes (Quad I/O SPI)
#define SPINOR_OP_PP                  0x02  // Page program (up to 256 bytes)
#define SPINOR_OP_PP_1_1_4            0x32  // Quad page program
#define SPINOR_OP_PP_1_4_4            0x38  // Quad page program
#define SPINOR_OP_BE_4K               0x20  // Erase 4KiB block
#define SPINOR_OP_BE_4K_PMC           0xd7  // Erase 4KiB block on PMC chips
#define SPINOR_OP_BE_32K              0x52  // Erase 32KiB block
#define SPINOR_OP_CHIP_ERASE          0xc7  // Erase whole flash chip
#define SPINOR_OP_BE                  0xd8  // Block erase (usually 64KiB)
#define SPINOR_OP_RDID                0x9f  // Read JEDEC ID
#define SPINOR_OP_RDSFDP              0x5a  // Read SFDP
#define SPINOR_OP_RDCR                0x35  // Read configuration register
#define SPINOR_OP_RDFSR               0x70  // Read flag status register
#define SPINOR_OP_READ_4B             0x13  // Read data bytes (low frequency)
#define SPINOR_OP_READ_FAST_4B        0x0c  // Read data bytes (high frequency)
#define SPINOR_OP_READ_1_1_2_4B       0x3c  // Read data bytes (Dual Output SPI)
#define SPINOR_OP_READ_1_2_2_4B       0xbc  // Read data bytes (Dual I/O SPI)
#define SPINOR_OP_READ_1_1_4_4B       0x6c  // Read data bytes (Quad Output SPI)
#define SPINOR_OP_READ_1_4_4_4B       0xec  // Read data bytes (Quad I/O SPI)
#define SPINOR_OP_PP_4B               0x12  // Page program (up to 256 bytes)
#define SPINOR_OP_PP_1_1_4_4B         0x34  // Quad page program
#define SPINOR_OP_PP_1_4_4_4B         0x3e  // Quad page program
#define SPINOR_OP_BE_4K_4B            0x21  // Erase 4KiB block
#define SPINOR_OP_BE_32K_4B           0x5c  // Erase 32KiB block
#define SPINOR_OP_BE_4B               0xdc  // Block erase (usually 64KiB)
#define SPINOR_OP_RD_ARRAY            0xe8  // Read array
#define SPINOR_OP_RD_NVCFG            0xb5  // Read non-volatile config register
#define SPINOR_OP_RD_VCR              0x85  // Read VCR register
#define SPINOR_OP_RD_EVCR             0x65  // Read EVCR register

#endif /* __NOR_FLASH_DXE_H__ */
