blob: 8b8a5d7a3f2233c1dc740fa596996a851e389126 [file] [log] [blame]
/** @file
*
* Copyright (c) 2015, Hisilicon Limited. All rights reserved.
* Copyright (c) 2015, Linaro Limited. All rights reserved.
*
* This program and the accompanying materials
* are licensed and made available under the terms and conditions of the BSD License
* which accompanies this distribution. The full text of the license may be found at
* http://opensource.org/licenses/bsd-license.php
*
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*
**/
#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;
}