/******************************************************************************* | |
Copyright (C) 2016 Marvell International Ltd. | |
Marvell BSD License Option | |
If you received this File from Marvell, you may opt to use, redistribute and/or | |
modify this File under the following licensing terms. | |
Redistribution and use in source and binary forms, with or without modification, | |
are permitted provided that the following conditions are met: | |
* Redistributions of source code must retain the above copyright notice, | |
this list of conditions and the following disclaimer. | |
* Redistributions in binary form must reproduce the above copyright | |
notice, this list of conditions and the following disclaimer in the | |
documentation and/or other materials provided with the distribution. | |
* Neither the name of Marvell nor the names of its contributors may be | |
used to endorse or promote products derived from this software without | |
specific prior written permission. | |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | |
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*******************************************************************************/ | |
#include "MvSpiFlash.h" | |
MARVELL_SPI_MASTER_PROTOCOL *SpiMasterProtocol; | |
SPI_FLASH_INSTANCE *mSpiFlashInstance; | |
STATIC | |
VOID | |
SpiFlashFormatAddress ( | |
IN UINT32 Address, | |
IN UINT8 AddrSize, | |
IN OUT UINT8 *Cmd | |
) | |
{ | |
if (AddrSize == 4) { | |
Cmd[1] = Address >> 24; | |
Cmd[2] = Address >> 16; | |
Cmd[3] = Address >> 8; | |
Cmd[4] = Address; | |
} else { | |
Cmd[1] = Address >> 16; | |
Cmd[2] = Address >> 8; | |
Cmd[3] = Address; | |
} | |
} | |
STATIC | |
EFI_STATUS | |
MvSpiFlashReadCmd ( | |
IN SPI_DEVICE *Slave, | |
IN UINT8 *Cmd, | |
IN UINTN CmdSize, | |
OUT UINT8 *DataIn, | |
IN UINTN DataSize | |
) | |
{ | |
EFI_STATUS Status; | |
// Send command and gather response | |
Status = SpiMasterProtocol->ReadWrite (SpiMasterProtocol, Slave, Cmd, | |
CmdSize, NULL, DataIn, DataSize); | |
return Status; | |
} | |
STATIC | |
EFI_STATUS | |
MvSpiFlashWriteEnableCmd ( | |
IN SPI_DEVICE *Slave | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 CmdEn = CMD_WRITE_ENABLE; | |
// Send write_enable command | |
Status = SpiMasterProtocol->Transfer (SpiMasterProtocol, Slave, 1, | |
&CmdEn, NULL, SPI_TRANSFER_BEGIN | SPI_TRANSFER_END); | |
return Status; | |
} | |
STATIC | |
EFI_STATUS | |
MvSpiFlashWriteCommon ( | |
IN SPI_DEVICE *Slave, | |
IN UINT8 *Cmd, | |
IN UINT32 Length, | |
IN UINT8* Buffer, | |
IN UINT32 BufferLength | |
) | |
{ | |
UINT8 CmdStatus = CMD_READ_STATUS; | |
UINT8 State; | |
UINT32 Counter = 0xFFFFF; | |
UINT8 poll_bit = STATUS_REG_POLL_WIP; | |
UINT8 check_status = 0x0; | |
CmdStatus = (UINT8)PcdGet32 (PcdSpiFlashPollCmd); | |
if (CmdStatus == CMD_FLAG_STATUS) { | |
poll_bit = STATUS_REG_POLL_PEC; | |
check_status = poll_bit; | |
} | |
// Send command | |
MvSpiFlashWriteEnableCmd (Slave); | |
// Write data | |
SpiMasterProtocol->ReadWrite (SpiMasterProtocol, Slave, Cmd, Length, | |
Buffer, NULL, BufferLength); | |
// Poll status register | |
SpiMasterProtocol->Transfer (SpiMasterProtocol, Slave, 1, &CmdStatus, | |
NULL, SPI_TRANSFER_BEGIN); | |
do { | |
SpiMasterProtocol->Transfer (SpiMasterProtocol, Slave, 1, NULL, &State, | |
0); | |
Counter--; | |
if ((State & poll_bit) == check_status) | |
break; | |
} while (Counter > 0); | |
if (Counter == 0) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Timeout while writing to spi flash\n")); | |
return EFI_DEVICE_ERROR; | |
} | |
// Deactivate CS | |
SpiMasterProtocol->Transfer (SpiMasterProtocol, Slave, 0, NULL, NULL, SPI_TRANSFER_END); | |
return EFI_SUCCESS; | |
} | |
STATIC | |
VOID | |
SpiFlashCmdBankaddrWrite ( | |
IN SPI_DEVICE *Slave, | |
IN UINT8 BankSel | |
) | |
{ | |
UINT8 Cmd = CMD_BANK_WRITE; | |
MvSpiFlashWriteCommon (Slave, &Cmd, 1, &BankSel, 1); | |
} | |
STATIC | |
UINT8 | |
SpiFlashBank ( | |
IN SPI_DEVICE *Slave, | |
IN UINT32 Offset | |
) | |
{ | |
UINT8 BankSel; | |
BankSel = Offset / SPI_FLASH_16MB_BOUN; | |
SpiFlashCmdBankaddrWrite (Slave, BankSel); | |
return BankSel; | |
} | |
EFI_STATUS | |
MvSpiFlashErase ( | |
IN SPI_DEVICE *Slave, | |
IN UINTN Offset, | |
IN UINTN Length | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 AddrSize, EraseAddr; | |
UINTN EraseSize; | |
UINT8 Cmd[5]; | |
AddrSize = PcdGet32 (PcdSpiFlashAddressCycles); | |
EraseSize = PcdGet64 (PcdSpiFlashEraseSize); | |
// Check 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; | |
} | |
Cmd[0] = CMD_ERASE_64K; | |
while (Length) { | |
EraseAddr = Offset; | |
SpiFlashBank (Slave, EraseAddr); | |
SpiFlashFormatAddress (EraseAddr, AddrSize, Cmd); | |
// Programm proper erase address | |
Status = MvSpiFlashWriteCommon (Slave, Cmd, AddrSize + 1, NULL, 0); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Error while programming target address\n")); | |
return Status; | |
} | |
Offset += EraseSize; | |
Length -= EraseSize; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
MvSpiFlashRead ( | |
IN SPI_DEVICE *Slave, | |
IN UINT32 Offset, | |
IN UINTN Length, | |
IN VOID *Buf | |
) | |
{ | |
EFI_STATUS Status = EFI_SUCCESS; | |
UINT8 Cmd[6]; | |
UINT32 AddrSize, ReadAddr, ReadLength, RemainLength; | |
UINTN BankSel = 0; | |
AddrSize = PcdGet32 (PcdSpiFlashAddressCycles); | |
Cmd[0] = CMD_READ_ARRAY_FAST; | |
// Sign end of address with 0 byte | |
Cmd[5] = 0; | |
while (Length) { | |
ReadAddr = Offset; | |
BankSel = SpiFlashBank (Slave, ReadAddr); | |
RemainLength = (SPI_FLASH_16MB_BOUN * (BankSel + 1)) - Offset; | |
if (Length < RemainLength) { | |
ReadLength = Length; | |
} else { | |
ReadLength = RemainLength; | |
} | |
SpiFlashFormatAddress (ReadAddr, AddrSize, Cmd); | |
// Program proper read address and read data | |
Status = MvSpiFlashReadCmd (Slave, Cmd, AddrSize + 2, Buf, Length); | |
Offset += ReadLength; | |
Length -= ReadLength; | |
Buf += ReadLength; | |
} | |
return Status; | |
} | |
EFI_STATUS | |
MvSpiFlashWrite ( | |
IN SPI_DEVICE *Slave, | |
IN UINT32 Offset, | |
IN UINTN Length, | |
IN VOID *Buf | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN ByteAddr, ChunkLength, ActualIndex, PageSize; | |
UINT32 WriteAddr; | |
UINT8 Cmd[5], AddrSize; | |
AddrSize = PcdGet32 (PcdSpiFlashAddressCycles); | |
PageSize = PcdGet32 (PcdSpiFlashPageSize); | |
Cmd[0] = CMD_PAGE_PROGRAM; | |
for (ActualIndex = 0; ActualIndex < Length; ActualIndex += ChunkLength) { | |
WriteAddr = Offset; | |
SpiFlashBank (Slave, WriteAddr); | |
ByteAddr = Offset % PageSize; | |
ChunkLength = MIN(Length - ActualIndex, (UINT64) (PageSize - ByteAddr)); | |
SpiFlashFormatAddress (WriteAddr, AddrSize, Cmd); | |
// Program proper write address and write data | |
Status = MvSpiFlashWriteCommon (Slave, Cmd, AddrSize + 1, Buf + ActualIndex, | |
ChunkLength); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Error while programming write address\n")); | |
return Status; | |
} | |
Offset += ChunkLength; | |
} | |
return EFI_SUCCESS; | |
} | |
STATIC | |
EFI_STATUS | |
MvSpiFlashUpdateBlock ( | |
IN SPI_DEVICE *Slave, | |
IN UINT32 Offset, | |
IN UINTN ToUpdate, | |
IN UINT8 *Buf, | |
IN UINT8 *TmpBuf, | |
IN UINTN EraseSize | |
) | |
{ | |
EFI_STATUS Status; | |
// Read backup | |
Status = MvSpiFlashRead (Slave, Offset, EraseSize, TmpBuf); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Update: Error while reading old data\n")); | |
return Status; | |
} | |
// Erase entire sector | |
Status = MvSpiFlashErase (Slave, Offset, EraseSize); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Update: Error while erasing block\n")); | |
return Status; | |
} | |
// Write new data | |
MvSpiFlashWrite (Slave, Offset, ToUpdate, Buf); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Update: Error while writing new data\n")); | |
return Status; | |
} | |
// Write backup | |
if (ToUpdate != EraseSize) { | |
Status = MvSpiFlashWrite (Slave, Offset + ToUpdate, EraseSize - ToUpdate, | |
&TmpBuf[ToUpdate]); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Update: Error while writing backup\n")); | |
return Status; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
MvSpiFlashUpdate ( | |
IN SPI_DEVICE *Slave, | |
IN UINT32 Offset, | |
IN UINTN ByteCount, | |
IN UINT8 *Buf | |
) | |
{ | |
EFI_STATUS Status; | |
UINT64 EraseSize, ToUpdate, Scale = 1; | |
UINT8 *TmpBuf, *End; | |
EraseSize = PcdGet64 (PcdSpiFlashEraseSize); | |
End = Buf + ByteCount; | |
TmpBuf = (UINT8 *)AllocateZeroPool (EraseSize); | |
if (TmpBuf == NULL) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Cannot allocate memory\n")); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
if (End - Buf >= 200) | |
Scale = (End - Buf) / 100; | |
for (; Buf < End; Buf += ToUpdate, Offset += ToUpdate) { | |
ToUpdate = MIN((UINT64)(End - Buf), EraseSize); | |
Print (L" \rUpdating, %d%%", 100 - (End - Buf) / Scale); | |
Status = MvSpiFlashUpdateBlock (Slave, Offset, ToUpdate, Buf, TmpBuf, EraseSize); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Error while updating\n")); | |
return Status; | |
} | |
} | |
Print(L"\n"); | |
FreePool (TmpBuf); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
MvSpiFlashReadId ( | |
IN SPI_DEVICE *SpiDev, | |
IN UINT32 DataByteCount, | |
IN OUT UINT8 *Buffer | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 *DataOut; | |
DataOut = (UINT8 *) AllocateZeroPool (DataByteCount); | |
if (DataOut == NULL) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Cannot allocate memory\n")); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Status = SpiMasterProtocol->Transfer (SpiMasterProtocol, SpiDev, | |
DataByteCount, Buffer, DataOut, SPI_TRANSFER_BEGIN | SPI_TRANSFER_END); | |
if (EFI_ERROR(Status)) { | |
FreePool (DataOut); | |
DEBUG((DEBUG_ERROR, "SpiFlash: Spi transfer error\n")); | |
return Status; | |
} | |
// Bytes 1,2 and 3 contain SPI flash ID | |
Buffer[0] = DataOut[1]; | |
Buffer[1] = DataOut[2]; | |
Buffer[2] = DataOut[3]; | |
FreePool (DataOut); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
MvSpiFlashInit ( | |
IN MARVELL_SPI_FLASH_PROTOCOL *This, | |
IN SPI_DEVICE *Slave | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 Cmd, StatusRegister; | |
UINT32 AddrSize; | |
AddrSize = PcdGet32 (PcdSpiFlashAddressCycles); | |
if (AddrSize == 4) { | |
// Set 4 byte address mode | |
Status = MvSpiFlashWriteEnableCmd (Slave); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Error while setting write_enable\n")); | |
return Status; | |
} | |
Cmd = CMD_4B_ADDR_ENABLE; | |
Status = SpiMasterProtocol->Transfer (SpiMasterProtocol, Slave, 1, &Cmd, NULL, | |
SPI_TRANSFER_BEGIN | SPI_TRANSFER_END); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Error while setting 4B address\n")); | |
return Status; | |
} | |
} | |
// Write flash status register | |
Status = MvSpiFlashWriteEnableCmd (Slave); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Error while setting write_enable\n")); | |
return Status; | |
} | |
Cmd = CMD_WRITE_STATUS_REG; | |
StatusRegister = 0x0; | |
Status = SpiMasterProtocol->ReadWrite (SpiMasterProtocol, Slave, &Cmd, 1, | |
&StatusRegister, NULL, 1); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Error with spi transfer\n")); | |
return Status; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
MvSpiFlashInitProtocol ( | |
IN MARVELL_SPI_FLASH_PROTOCOL *SpiFlashProtocol | |
) | |
{ | |
SpiFlashProtocol->Init = MvSpiFlashInit; | |
SpiFlashProtocol->ReadId = MvSpiFlashReadId; | |
SpiFlashProtocol->Read = MvSpiFlashRead; | |
SpiFlashProtocol->Write = MvSpiFlashWrite; | |
SpiFlashProtocol->Erase = MvSpiFlashErase; | |
SpiFlashProtocol->Update = MvSpiFlashUpdate; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
MvSpiFlashEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
Status = gBS->LocateProtocol ( | |
&gMarvellSpiMasterProtocolGuid, | |
NULL, | |
(VOID **)&SpiMasterProtocol | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Cannot locate SPI Master protocol\n")); | |
return EFI_DEVICE_ERROR; | |
} | |
mSpiFlashInstance = AllocateZeroPool (sizeof (SPI_FLASH_INSTANCE)); | |
if (mSpiFlashInstance == NULL) { | |
DEBUG((DEBUG_ERROR, "SpiFlash: Cannot allocate memory\n")); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
MvSpiFlashInitProtocol (&mSpiFlashInstance->SpiFlashProtocol); | |
mSpiFlashInstance->Signature = SPI_FLASH_SIGNATURE; | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&(mSpiFlashInstance->Handle), | |
&gMarvellSpiFlashProtocolGuid, | |
&(mSpiFlashInstance->SpiFlashProtocol), | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (mSpiFlashInstance); | |
DEBUG((DEBUG_ERROR, "SpiFlash: Cannot install SPI flash protocol\n")); | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} |