/** @file
|
*
|
* Copyright (c) 2015, Hisilicon Limited. All rights reserved.
|
* Copyright (c) 2015, Linaro Limited. All rights reserved.
|
*
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
*
|
**/
|
|
|
#include <Uefi.h>
|
#include <Library/DebugLib.h>
|
#include <Library/UefiBootServicesTableLib.h>
|
#include <Library/ArmLib.h>
|
#include <Library/PcdLib.h>
|
#include <Protocol/NorFlashProtocol.h>
|
#include <Library/DxeServicesTableLib.h>
|
#include <Protocol/Cpu.h>
|
#include "NorFlashHw.h"
|
|
|
EFI_STATUS Erase(
|
IN UNI_NOR_FLASH_PROTOCOL *This,
|
IN UINT32 Offset,
|
IN UINT32 Length
|
);
|
|
EFI_STATUS Write(
|
IN UNI_NOR_FLASH_PROTOCOL *This,
|
IN UINT32 Offset,
|
IN UINT8 *Buffer,
|
UINT32 ulLength
|
);
|
|
EFI_STATUS Read(
|
IN UNI_NOR_FLASH_PROTOCOL *This,
|
IN UINT32 Offset,
|
IN OUT UINT8 *Buffer,
|
IN UINT32 ulLen
|
);
|
|
UNI_NOR_FLASH_PROTOCOL gUniNorFlash = {
|
Erase,
|
Write,
|
Read
|
};
|
|
|
EFI_STATUS
|
EFIAPI Read(
|
IN UNI_NOR_FLASH_PROTOCOL *This,
|
IN UINT32 Offset,
|
IN OUT UINT8 *Buffer,
|
IN UINT32 ulLen
|
)
|
{
|
UINT32 index;
|
UINT64 ullAddr;
|
UINT32 ullCnt = 0;
|
UINT32 *puiBuffer32 = NULL;
|
UINT32 *puiDst32 = NULL;
|
UINT8 *pucBuffer8 = NULL;
|
UINT8 *pucDst8 = NULL;
|
|
if (Offset + ulLen > (gFlashInfo[gIndex.InfIndex].SingleChipSize * gFlashInfo[gIndex.InfIndex].ParallelNum))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:Exceed the flash scope!\n", __FUNCTION__,__LINE__));
|
return EFI_INVALID_PARAMETER;
|
}
|
if (0 == ulLen)
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:Length is Zero!\n", __FUNCTION__,__LINE__));
|
return EFI_INVALID_PARAMETER;
|
}
|
if (NULL == Buffer)
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:Buffer is NULL!\n", __FUNCTION__,__LINE__));
|
return EFI_BAD_BUFFER_SIZE;
|
}
|
|
|
ullAddr = gIndex.Base + Offset;
|
|
pucBuffer8 = (UINT8 *)Buffer;
|
pucDst8 = (UINT8 *)((UINTN)ullAddr);
|
|
|
if (ulLen < FOUR_BYTE_UNIT)
|
{
|
for(index = 0; index< ulLen; index++)
|
{
|
*pucBuffer8++ = *pucDst8++;
|
}
|
}
|
else
|
{
|
|
ullCnt = Offset % FOUR_BYTE_UNIT;
|
ullCnt = FOUR_BYTE_UNIT - ullCnt;
|
|
for(index = 0; index < ullCnt; index++)
|
{
|
*pucBuffer8++ = *pucDst8++;
|
}
|
|
ulLen -= ullCnt;
|
|
puiBuffer32 = (UINT32 *)pucBuffer8;
|
puiDst32 = (UINT32 *)pucDst8;
|
ullCnt = ulLen / FOUR_BYTE_UNIT;
|
|
for(index = 0; index < ullCnt; index++)
|
{
|
*puiBuffer32++ = *puiDst32++;
|
}
|
|
ullCnt = ulLen % FOUR_BYTE_UNIT;
|
pucBuffer8 = (UINT8 *)puiBuffer32;
|
pucDst8 = (UINT8 *)puiDst32;
|
|
for(index = 0; index < ullCnt; index++)
|
{
|
*pucBuffer8++ = *pucDst8++;
|
}
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
|
|
static EFI_STATUS WriteAfterErase_Fill(
|
IN const UINT32 Offset,
|
IN const UINT8 *Buffer,
|
IN const UINT32 Length
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 Loop;
|
UINT32 DataOffset;
|
UINT32 NewOffset;
|
UINT8 *NewDataUnit;
|
|
UINT32 FlashUnitLength;
|
|
FlashUnitLength = gFlashInfo[gIndex.InfIndex].BufferProgramSize << gFlashInfo[gIndex.InfIndex].ParallelNum;
|
|
if (0 == Length)
|
{
|
return EFI_SUCCESS;
|
}
|
if ((Offset % FlashUnitLength + Length) > FlashUnitLength)
|
{
|
DEBUG ((EFI_D_INFO, "[%a]:[%dL]:Exceed the Flash Size!\n", __FUNCTION__,__LINE__));
|
return EFI_UNSUPPORTED;
|
}
|
|
|
Status = gBS->AllocatePool(EfiBootServicesData, FlashUnitLength, (VOID *)&NewDataUnit);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:Allocate Pool failed, %r!\n", __FUNCTION__,__LINE__, Status));
|
return Status;
|
}
|
|
|
NewOffset = Offset - (Offset % FlashUnitLength);
|
|
gBS->CopyMem((VOID *)NewDataUnit, (VOID *)(UINTN)(gIndex.Base + NewOffset), FlashUnitLength);
|
|
DataOffset = Offset % FlashUnitLength;
|
for (Loop = 0; Loop < Length; Loop ++)
|
{
|
NewDataUnit[(UINT32)(DataOffset + Loop)] = Buffer[Loop];
|
}
|
|
Status = BufferWrite(NewOffset, (void *)NewDataUnit, FlashUnitLength);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:BufferWrite %r!\n", __FUNCTION__,__LINE__, Status));
|
return Status;
|
}
|
|
(void)gBS->FreePool((VOID *)NewDataUnit);
|
return Status;
|
}
|
|
|
static EFI_STATUS WriteAfterErase_Final(
|
IN UINT32 Offset,
|
IN UINT8 *Buffer,
|
IN UINT32 Length
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 Loop;
|
UINT32 FlashUnitLength;
|
|
FlashUnitLength = gFlashInfo[gIndex.InfIndex].BufferProgramSize << gFlashInfo[gIndex.InfIndex].ParallelNum;
|
|
if (0 == Length)
|
{
|
return EFI_SUCCESS;
|
}
|
|
if (0 != (Offset % FlashUnitLength))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]: Offset must be a multiple of 0x%x!\n", __FUNCTION__,__LINE__,FlashUnitLength));
|
return EFI_UNSUPPORTED;
|
}
|
|
|
Loop = Length / FlashUnitLength;
|
while (Loop --)
|
{
|
Status = BufferWrite(Offset, (void *)Buffer, FlashUnitLength);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:BufferWrite Failed: %r!\n", __FUNCTION__,__LINE__, Status));
|
return EFI_DEVICE_ERROR;
|
}
|
Offset += FlashUnitLength;
|
Buffer += FlashUnitLength;
|
}
|
|
|
Length = Length % FlashUnitLength;
|
if (Length)
|
{
|
Status = WriteAfterErase_Fill(Offset, Buffer, Length);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:WriteAfterErase_Fill failed,%r!\n", __FUNCTION__,__LINE__, Status));
|
return Status;
|
}
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
EFI_STATUS
|
WriteAfterErase(
|
UINT32 TempBase,
|
UINT32 Offset,
|
UINT8 *Buffer,
|
UINT32 Length
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 FlashUnitLength;
|
|
FlashUnitLength = gFlashInfo[gIndex.InfIndex].BufferProgramSize << gFlashInfo[gIndex.InfIndex].ParallelNum;
|
|
if (0 == Length)
|
{
|
return EFI_SUCCESS;
|
}
|
|
|
if (Offset % FlashUnitLength)
|
{
|
UINT32 TempLength;
|
|
|
TempLength = FlashUnitLength - (Offset % FlashUnitLength);
|
if (TempLength > Length)
|
{
|
TempLength = Length;
|
}
|
Status = WriteAfterErase_Fill(Offset, Buffer, TempLength);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]: %r!\n", __FUNCTION__,__LINE__, Status));
|
return Status;
|
}
|
|
Offset += TempLength;
|
Length -= TempLength;
|
Buffer += TempLength;
|
|
//Desc:if Offset >= gOneFlashSize,modify base
|
if (0 < (Offset / gFlashInfo[gIndex.InfIndex].SingleChipSize))
|
{
|
TempBase += gFlashInfo[gIndex.InfIndex].SingleChipSize;
|
gIndex.Base = TempBase;
|
Offset = 0;
|
}
|
}
|
|
|
Status = WriteAfterErase_Final(Offset, Buffer, Length);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]: %r!\n", __FUNCTION__,__LINE__, Status));
|
return Status;
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
|
EFI_STATUS
|
FlashSectorErase(
|
UINT32 TempBase,
|
UINT32 Offset,
|
UINT32 Length
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 SectorOffset;
|
UINT8 *StaticBuffer;
|
UINT8 *Buffer;
|
UINT32 TempOffset;
|
UINT32 TempLength;
|
UINT32 LeftLength;
|
|
|
if (0 == Length)
|
{
|
return EFI_SUCCESS;
|
}
|
|
LeftLength = gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum - (Offset % (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum));
|
if (LeftLength < Length)
|
{
|
return EFI_UNSUPPORTED;
|
}
|
|
|
SectorOffset = Offset - (Offset % (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum));
|
|
Status = gBS->AllocatePool(EfiBootServicesData, gFlashInfo[gIndex.InfIndex].BlockSize * (UINTN)gFlashInfo[gIndex.InfIndex].ParallelNum, (VOID *)&StaticBuffer);
|
if (EFI_ERROR(Status))
|
{
|
return Status;
|
}
|
|
Buffer = StaticBuffer;
|
|
gBS->CopyMem((VOID *)Buffer, (VOID *)(UINTN)(TempBase + SectorOffset),
|
(gFlashInfo[gIndex.InfIndex].BlockSize * (UINTN)gFlashInfo[gIndex.InfIndex].ParallelNum));
|
|
|
Status = SectorErase(TempBase, SectorOffset);
|
if (EFI_ERROR(Status))
|
{
|
goto DO;
|
}
|
|
|
TempOffset = SectorOffset;
|
TempLength = Offset % (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum);
|
|
Status = WriteAfterErase(TempBase, TempOffset, Buffer, TempLength);
|
if (EFI_ERROR(Status))
|
{
|
goto DO;
|
}
|
|
|
Buffer = Buffer + TempLength + Length;
|
TempOffset = Offset + Length;
|
TempLength = SectorOffset + (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum) - TempOffset;
|
|
Status = WriteAfterErase(TempBase, TempOffset, Buffer, TempLength);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]: %r!\n", __FUNCTION__,__LINE__,Status));
|
goto DO;
|
}
|
|
(void)gBS->FreePool((VOID *)StaticBuffer);
|
return EFI_SUCCESS;
|
|
DO:
|
(void)gBS->FreePool((VOID *)StaticBuffer);
|
return Status;
|
}
|
|
|
EFI_STATUS
|
EFIAPI Erase(
|
IN UNI_NOR_FLASH_PROTOCOL *This,
|
IN UINT32 Offset,
|
IN UINT32 Length
|
)
|
{
|
EFI_STATUS Status = EFI_SUCCESS;
|
UINT32 Sectors;
|
UINT32 TempLength;
|
UINT32 TempBase;
|
UINT32 Loop;
|
|
|
if (Offset + Length > (gFlashInfo[gIndex.InfIndex].SingleChipSize * gFlashInfo[gIndex.InfIndex].ParallelNum))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:Exceed the Flash Size!\n", __FUNCTION__,__LINE__));
|
return EFI_ABORTED;
|
}
|
if (0 == Length)
|
{
|
return EFI_SUCCESS;
|
}
|
|
|
Sectors = ((Offset + Length - 1) / (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum)) - (Offset / (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum)) + 1;
|
TempBase = gIndex.Base;
|
|
//if Offset >= gOneFlashSize,modify base
|
if(0 < (Offset / gFlashInfo[gIndex.InfIndex].SingleChipSize))
|
{
|
TempBase += gFlashInfo[gIndex.InfIndex].SingleChipSize * (Offset/gFlashInfo[gIndex.InfIndex].SingleChipSize);
|
Offset = Offset - (Offset & gFlashInfo[gIndex.InfIndex].SingleChipSize);
|
}
|
|
for (Loop = 0; Loop <= Sectors; Loop ++)
|
{
|
|
TempLength = gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum - (Offset % (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum));
|
|
|
if (TempLength > Length)
|
{
|
TempLength = Length;
|
}
|
|
Status = FlashSectorErase(TempBase, Offset, TempLength);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]: FlashErase One Sector Error, Status = %r!\n", __FUNCTION__,__LINE__,Status));
|
return Status;
|
}
|
|
Offset += TempLength;
|
|
//if Offset >= gOneFlashSize,modify base
|
if (0 < (Offset / gFlashInfo[gIndex.InfIndex].SingleChipSize))
|
{
|
TempBase += gFlashInfo[gIndex.InfIndex].SingleChipSize;
|
Offset = 0;
|
}
|
Length -= TempLength;
|
}
|
|
return Status;
|
}
|
|
|
EFI_STATUS
|
EFIAPI Write(
|
IN UNI_NOR_FLASH_PROTOCOL *This,
|
IN UINT32 Offset,
|
IN UINT8 *Buffer,
|
UINT32 ulLength
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 TempLength;
|
UINT32 TempBase;
|
UINT32 Loop;
|
UINT32 Sectors;
|
|
if((Offset + ulLength) > (gFlashInfo[gIndex.InfIndex].SingleChipSize * gFlashInfo[gIndex.InfIndex].ParallelNum))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:Exceed the Flash Size!\n", __FUNCTION__,__LINE__));
|
return EFI_INVALID_PARAMETER;
|
}
|
if (0 == ulLength)
|
{
|
return EFI_SUCCESS;
|
}
|
|
|
Sectors = ((Offset + ulLength - 1) / (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum)) - (Offset / (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum)) + 1;
|
TempBase = gIndex.Base;
|
|
//if Offset >= gOneFlashSize,modify base
|
if(0 < (Offset / gFlashInfo[gIndex.InfIndex].SingleChipSize))
|
{
|
TempBase += gFlashInfo[gIndex.InfIndex].SingleChipSize * (Offset/gFlashInfo[gIndex.InfIndex].SingleChipSize);
|
Offset = Offset - (Offset & gFlashInfo[gIndex.InfIndex].SingleChipSize);
|
}
|
|
for (Loop = 0; Loop <= Sectors; Loop ++)
|
{
|
|
TempLength = gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum - (Offset % (gFlashInfo[gIndex.InfIndex].BlockSize * gFlashInfo[gIndex.InfIndex].ParallelNum));
|
|
|
if (TempLength > ulLength)
|
{
|
TempLength = ulLength;
|
}
|
|
|
if (TRUE == IsNeedToWrite(TempBase, Offset, Buffer, TempLength))
|
{
|
Status = FlashSectorErase(TempBase, Offset, TempLength);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:FlashErase One Sector Error, Status = %r!\n", __FUNCTION__,__LINE__,Status));
|
return Status;
|
}
|
|
|
Status = WriteAfterErase(TempBase, Offset, Buffer, TempLength);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:WriteAfterErase Status = %r!\n", __FUNCTION__,__LINE__,Status));
|
return Status;
|
}
|
}
|
|
Offset += TempLength;
|
Buffer += TempLength;
|
|
//if Offset >= gOneFlashSize,modify base
|
if (0 < (Offset / gFlashInfo[gIndex.InfIndex].SingleChipSize))
|
{
|
TempBase += gFlashInfo[gIndex.InfIndex].SingleChipSize;
|
Offset = 0;
|
}
|
ulLength -= TempLength;
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
|
VOID SetFlashAttributeToUncache(VOID)
|
{
|
EFI_CPU_ARCH_PROTOCOL *gCpu = NULL;
|
EFI_STATUS Status;
|
|
Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG((EFI_D_ERROR, "LocateProtocol gEfiCpuArchProtocolGuid Status = %r !\n", Status));
|
}
|
|
Status = gCpu->SetMemoryAttributes(
|
gCpu,
|
PcdGet64(PcdNORFlashBase),
|
PcdGet32(PcdNORFlashCachableSize),
|
EFI_MEMORY_UC
|
);
|
|
if (EFI_ERROR(Status))
|
{
|
DEBUG((EFI_D_ERROR, "gCpu->SetMemoryAttributes Status = %r !\n", Status));
|
}
|
|
}
|
|
EFI_STATUS
|
EFIAPI InitializeFlash (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable)
|
{
|
EFI_STATUS Status;
|
|
|
gIndex.Base = (UINT32)PcdGet64(PcdNORFlashBase);
|
|
SetFlashAttributeToUncache();
|
Status = FlashInit(gIndex.Base);
|
if (EFI_ERROR(Status))
|
{
|
DEBUG((EFI_D_ERROR, "Init Flash Error !\n"));
|
return Status;
|
}
|
else
|
{
|
DEBUG((EFI_D_ERROR, "Init Flash OK!\n"));
|
}
|
|
Status = gBS->InstallProtocolInterface (
|
&ImageHandle,
|
&gUniNorFlashProtocolGuid,
|
EFI_NATIVE_INTERFACE,
|
&gUniNorFlash);
|
if(EFI_SUCCESS != Status)
|
{
|
DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:Install Protocol Interface %r!\n", __FUNCTION__,__LINE__,Status));
|
}
|
|
return Status;
|
}
|