/******************************************************************************* Copyright (C) 2016 Marvell International Ltd. SPDX-License-Identifier: BSD-2-Clause-Patent *******************************************************************************/ #include "MvSpiOrionDxe.h" SPI_MASTER *mSpiMasterInstance; STATIC EFI_STATUS SpiSetBaudRate ( IN SPI_DEVICE *Slave, IN UINT32 CpuClock, IN UINT32 MaxFreq ) { UINT32 Spr, BestSpr, Sppr, BestSppr, ClockDivider, Match, Reg, MinBaudDiff; UINTN SpiRegBase = Slave->HostRegisterBaseAddress; 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 ( IN SPI_DEVICE *Slave ) { UINT32 Reg; UINTN SpiRegBase = Slave->HostRegisterBaseAddress; Reg = MmioRead32 (SpiRegBase + SPI_CTRL_REG); Reg &= ~SPI_CS_NUM_MASK; Reg |= (Slave->Cs << SPI_CS_NUM_OFFSET); MmioWrite32 (SpiRegBase + SPI_CTRL_REG, Reg); } STATIC VOID SpiActivateCs ( IN SPI_DEVICE *Slave ) { UINT32 Reg; UINTN SpiRegBase = Slave->HostRegisterBaseAddress; SpiSetCs(Slave); Reg = MmioRead32 (SpiRegBase + SPI_CTRL_REG); Reg |= SPI_CS_EN_MASK; MmioWrite32(SpiRegBase + SPI_CTRL_REG, Reg); } STATIC VOID SpiDeactivateCs ( IN SPI_DEVICE *Slave ) { UINT32 Reg; UINTN SpiRegBase = Slave->HostRegisterBaseAddress; 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, CoreClock, SpiMaxFreq; UINTN SpiRegBase; SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This); // Initialize values from PCDs SpiRegBase = Slave->HostRegisterBaseAddress; CoreClock = Slave->CoreClock; SpiMaxFreq = Slave->MaxFreq; EfiAcquireLock (&SpiMaster->Lock); Reg = MmioRead32 (SpiRegBase + SPI_CONF_REG); Reg |= SPI_BYTE_LENGTH; MmioWrite32 (SpiRegBase + SPI_CONF_REG, Reg); SpiSetCs(Slave); SpiSetBaudRate (Slave, 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; UINT8 *DataOutPtr = (UINT8 *)DataOut; UINT8 *DataInPtr = (UINT8 *)DataIn; UINT8 DataToSend = 0; UINTN SpiRegBase; SpiMaster = SPI_MASTER_FROM_SPI_MASTER_PROTOCOL (This); SpiRegBase = Slave->HostRegisterBaseAddress; Length = 8 * DataByteCount; if (!EfiAtRuntime ()) { EfiAcquireLock (&SpiMaster->Lock); } if (Flag & SPI_TRANSFER_BEGIN) { SpiActivateCs (Slave); } // 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)) { if (DataInPtr != NULL) { *DataInPtr = MmioRead32 (SpiRegBase + SPI_DATA_IN_REG); DataInPtr++; } if (DataOutPtr != NULL) { DataOutPtr++; } Length -= 8; break; } } if (Iterator >= SPI_TIMEOUT) { DEBUG ((DEBUG_ERROR, "%a: Timeout\n", __FUNCTION__)); return EFI_TIMEOUT; } } if (Flag & SPI_TRANSFER_END) { SpiDeactivateCs (Slave); } if (!EfiAtRuntime ()) { 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 SPI_DEVICE *Slave, IN UINTN Cs, IN SPI_MODE Mode ) { if (!Slave) { Slave = AllocateZeroPool (sizeof(SPI_DEVICE)); if (Slave == NULL) { DEBUG((DEBUG_ERROR, "Cannot allocate memory\n")); return NULL; } Slave->Cs = Cs; Slave->Mode = Mode; } Slave->HostRegisterBaseAddress = PcdGet32 (PcdSpiRegBase); Slave->CoreClock = PcdGet32 (PcdSpiClockFrequency); Slave->MaxFreq = PcdGet32 (PcdSpiMaxFrequency); SpiSetupTransfer (This, Slave); return Slave; } EFI_STATUS EFIAPI MvSpiFreeSlave ( IN SPI_DEVICE *Slave ) { FreePool (Slave); return EFI_SUCCESS; } EFI_STATUS EFIAPI MvSpiConfigRuntime ( IN SPI_DEVICE *Slave ) { EFI_STATUS Status; UINTN AlignedAddress; // // Host register base may be not aligned to the page size, // which is not accepted when setting memory space attributes. // Add one aligned page of memory space which covers the host // controller registers. // AlignedAddress = Slave->HostRegisterBaseAddress & ~(SIZE_4KB - 1); Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo, AlignedAddress, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Failed to add memory space\n", __FUNCTION__)); return Status; } Status = gDS->SetMemorySpaceAttributes (AlignedAddress, SIZE_4KB, EFI_MEMORY_UC | EFI_MEMORY_RUNTIME); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Failed to set memory attributes\n", __FUNCTION__)); gDS->RemoveMemorySpace (AlignedAddress, SIZE_4KB); return Status; } 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; SpiMasterProtocol->ConfigRuntime = MvSpiConfigRuntime; return EFI_SUCCESS; } EFI_STATUS EFIAPI MvSpiOrionEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; mSpiMasterInstance = AllocateRuntimeZeroPool (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; }