/******************************************************************************* | |
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 "MvSpiDxe.h" | |
SPI_MASTER *mSpiMasterInstance; | |
STATIC | |
EFI_STATUS | |
SpiSetBaudRate ( | |
IN UINT32 CpuClock, | |
IN UINT32 MaxFreq | |
) | |
{ | |
UINT32 Spr, BestSpr, Sppr, BestSppr, ClockDivider, Match, Reg, MinBaudDiff; | |
UINTN SpiRegBase = PcdGet32 (PcdSpiRegBase); | |
MinBaudDiff = 0xFFFFFFFF; | |
BestSppr = 0; | |
//Spr is in range 1-15 and Sppr in range 0-8 | |
for (Spr = 1; Spr <= 15; Spr++) { | |
for (Sppr = 0; Sppr <= 7; Sppr++) { | |
ClockDivider = Spr * (1 << Sppr); | |
if ((CpuClock / ClockDivider) > MaxFreq) { | |
continue; | |
} | |
if ((CpuClock / ClockDivider) == MaxFreq) { | |
BestSpr = Spr; | |
BestSppr = Sppr; | |
Match = 1; | |
break; | |
} | |
if ((MaxFreq - (CpuClock / ClockDivider)) < MinBaudDiff) { | |
MinBaudDiff = (MaxFreq - (CpuClock / ClockDivider)); | |
BestSpr = Spr; | |
BestSppr = Sppr; | |
} | |
} | |
if (Match == 1) { | |
break; | |
} | |
} | |
if (BestSpr == 0) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
Reg = MmioRead32 (SpiRegBase + SPI_CONF_REG); | |
Reg &= ~(SPI_SPR_MASK | SPI_SPPR_0_MASK | SPI_SPPR_HI_MASK); | |
Reg |= (BestSpr << SPI_SPR_OFFSET) | | |
((BestSppr & 0x1) << SPI_SPPR_0_OFFSET) | | |
((BestSppr >> 1) << SPI_SPPR_HI_OFFSET); | |
MmioWrite32 (SpiRegBase + SPI_CONF_REG, Reg); | |
return EFI_SUCCESS; | |
} | |
STATIC | |
VOID | |
SpiSetCs ( | |
UINT8 CsId | |
) | |
{ | |
UINT32 Reg, SpiRegBase = PcdGet32 (PcdSpiRegBase); | |
Reg = MmioRead32 (SpiRegBase + SPI_CTRL_REG); | |
Reg &= ~SPI_CS_NUM_MASK; | |
Reg |= (CsId << SPI_CS_NUM_OFFSET); | |
MmioWrite32 (SpiRegBase + SPI_CTRL_REG, Reg); | |
} | |
STATIC | |
VOID | |
SpiActivateCs ( | |
UINT8 IN CsId | |
) | |
{ | |
UINT32 Reg, SpiRegBase = PcdGet32 (PcdSpiRegBase); | |
SpiSetCs(CsId); | |
Reg = MmioRead32 (SpiRegBase + SPI_CTRL_REG); | |
Reg |= SPI_CS_EN_MASK; | |
MmioWrite32(SpiRegBase + SPI_CTRL_REG, Reg); | |
} | |
STATIC | |
VOID | |
SpiDeactivateCs ( | |
VOID | |
) | |
{ | |
UINT32 Reg, SpiRegBase = PcdGet32 (PcdSpiRegBase); | |
Reg = MmioRead32 (SpiRegBase + SPI_CTRL_REG); | |
Reg &= ~SPI_CS_EN_MASK; | |
MmioWrite32(SpiRegBase + SPI_CTRL_REG, Reg); | |
} | |
STATIC | |
VOID | |
SpiSetupTransfer ( | |
IN MARVELL_SPI_MASTER_PROTOCOL *This, | |
IN SPI_DEVICE *Slave | |
) | |
{ | |
SPI_MASTER *SpiMaster; | |
UINT32 Reg, SpiRegBase, CoreClock, SpiMaxFreq; | |
SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This); | |
// Initialize values from PCDs | |
SpiRegBase = PcdGet32 (PcdSpiRegBase); | |
CoreClock = PcdGet32 (PcdSpiClockFrequency); | |
SpiMaxFreq = PcdGet32 (PcdSpiMaxFrequency); | |
EfiAcquireLock (&SpiMaster->Lock); | |
Reg = MmioRead32 (SpiRegBase + SPI_CONF_REG); | |
Reg |= SPI_BYTE_LENGTH; | |
MmioWrite32 (SpiRegBase + SPI_CONF_REG, Reg); | |
SpiSetCs(Slave->Cs); | |
SpiSetBaudRate (CoreClock, SpiMaxFreq); | |
Reg = MmioRead32 (SpiRegBase + SPI_CONF_REG); | |
Reg &= ~(SPI_CPOL_MASK | SPI_CPHA_MASK | SPI_TXLSBF_MASK | SPI_RXLSBF_MASK); | |
switch (Slave->Mode) { | |
case SPI_MODE0: | |
break; | |
case SPI_MODE1: | |
Reg |= SPI_CPHA_MASK; | |
break; | |
case SPI_MODE2: | |
Reg |= SPI_CPOL_MASK; | |
break; | |
case SPI_MODE3: | |
Reg |= SPI_CPOL_MASK; | |
Reg |= SPI_CPHA_MASK; | |
break; | |
} | |
MmioWrite32 (SpiRegBase + SPI_CONF_REG, Reg); | |
EfiReleaseLock (&SpiMaster->Lock); | |
} | |
EFI_STATUS | |
EFIAPI | |
MvSpiTransfer ( | |
IN MARVELL_SPI_MASTER_PROTOCOL *This, | |
IN SPI_DEVICE *Slave, | |
IN UINTN DataByteCount, | |
IN VOID *DataOut, | |
IN VOID *DataIn, | |
IN UINTN Flag | |
) | |
{ | |
SPI_MASTER *SpiMaster; | |
UINT64 Length; | |
UINT32 Iterator, Reg, SpiRegBase; | |
UINT8 *DataOutPtr = (UINT8 *)DataOut; | |
UINT8 *DataInPtr = (UINT8 *)DataIn; | |
UINT8 DataToSend = 0; | |
SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This); | |
SpiRegBase = PcdGet32 (PcdSpiRegBase); | |
Length = 8 * DataByteCount; | |
EfiAcquireLock (&SpiMaster->Lock); | |
if (Flag & SPI_TRANSFER_BEGIN) { | |
SpiActivateCs (Slave->Cs); | |
} | |
// Set 8-bit mode | |
Reg = MmioRead32 (SpiRegBase + SPI_CONF_REG); | |
Reg &= ~SPI_BYTE_LENGTH; | |
MmioWrite32 (SpiRegBase + SPI_CONF_REG, Reg); | |
while (Length > 0) { | |
if (DataOut != NULL) { | |
DataToSend = *DataOutPtr & 0xFF; | |
} | |
// Transmit Data | |
MmioWrite32 (SpiRegBase + SPI_INT_CAUSE_REG, 0x0); | |
MmioWrite32 (SpiRegBase + SPI_DATA_OUT_REG, DataToSend); | |
// Wait for memory ready | |
for (Iterator = 0; Iterator < SPI_TIMEOUT; Iterator++) { | |
if (MmioRead32 (SpiRegBase + SPI_INT_CAUSE_REG)) { | |
*DataInPtr = MmioRead32 (SpiRegBase + SPI_DATA_IN_REG); | |
if (DataInPtr != NULL) { | |
DataInPtr++; | |
} | |
if (DataOutPtr != NULL) { | |
DataOutPtr++; | |
} | |
Length -= 8; | |
break; | |
} | |
} | |
if (Iterator >= SPI_TIMEOUT) { | |
DEBUG ((DEBUG_ERROR, "Timeout\n")); | |
} | |
} | |
if (Flag & SPI_TRANSFER_END) { | |
SpiDeactivateCs (); | |
} | |
EfiReleaseLock (&SpiMaster->Lock); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
MvSpiReadWrite ( | |
IN MARVELL_SPI_MASTER_PROTOCOL *This, | |
IN SPI_DEVICE *Slave, | |
IN UINT8 *Cmd, | |
IN UINTN CmdSize, | |
IN UINT8 *DataOut, | |
OUT UINT8 *DataIn, | |
IN UINTN DataSize | |
) | |
{ | |
EFI_STATUS Status; | |
Status = MvSpiTransfer (This, Slave, CmdSize, Cmd, NULL, SPI_TRANSFER_BEGIN); | |
if (EFI_ERROR (Status)) { | |
Print (L"Spi Transfer Error\n"); | |
return EFI_DEVICE_ERROR; | |
} | |
Status = MvSpiTransfer (This, Slave, DataSize, DataOut, DataIn, SPI_TRANSFER_END); | |
if (EFI_ERROR (Status)) { | |
Print (L"Spi Transfer Error\n"); | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
MvSpiInit ( | |
IN MARVELL_SPI_MASTER_PROTOCOL * This | |
) | |
{ | |
return EFI_SUCCESS; | |
} | |
SPI_DEVICE * | |
EFIAPI | |
MvSpiSetupSlave ( | |
IN MARVELL_SPI_MASTER_PROTOCOL *This, | |
IN UINTN Cs, | |
IN SPI_MODE Mode | |
) | |
{ | |
SPI_DEVICE *Slave; | |
Slave = AllocateZeroPool (sizeof(SPI_DEVICE)); | |
if (Slave == NULL) { | |
DEBUG((DEBUG_ERROR, "Cannot allocate memory\n")); | |
return NULL; | |
} | |
Slave->Cs = Cs; | |
Slave->Mode = Mode; | |
SpiSetupTransfer (This, Slave); | |
return Slave; | |
} | |
EFI_STATUS | |
EFIAPI | |
MvSpiFreeSlave ( | |
IN SPI_DEVICE *Slave | |
) | |
{ | |
FreePool (Slave); | |
return EFI_SUCCESS; | |
} | |
STATIC | |
EFI_STATUS | |
SpiMasterInitProtocol ( | |
IN MARVELL_SPI_MASTER_PROTOCOL *SpiMasterProtocol | |
) | |
{ | |
SpiMasterProtocol->Init = MvSpiInit; | |
SpiMasterProtocol->SetupDevice = MvSpiSetupSlave; | |
SpiMasterProtocol->FreeDevice = MvSpiFreeSlave; | |
SpiMasterProtocol->Transfer = MvSpiTransfer; | |
SpiMasterProtocol->ReadWrite = MvSpiReadWrite; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
SpiMasterEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
mSpiMasterInstance = AllocateZeroPool (sizeof (SPI_MASTER)); | |
if (mSpiMasterInstance == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
EfiInitializeLock (&mSpiMasterInstance->Lock, TPL_NOTIFY); | |
SpiMasterInitProtocol (&mSpiMasterInstance->SpiMasterProtocol); | |
mSpiMasterInstance->Signature = SPI_MASTER_SIGNATURE; | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&(mSpiMasterInstance->Handle), | |
&gMarvellSpiMasterProtocolGuid, | |
&(mSpiMasterInstance->SpiMasterProtocol), | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (mSpiMasterInstance); | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} |