From e47fc79a2a4c602eb2921543a659722f3a35792a Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 2 Jun 2016 10:01:41 +0800 Subject: BraswellPlatformPkg: Add SpiDeviceDxe.c Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.c | 659 +++++++++++++++++++++ BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.h | 211 +++++++ .../Flash/SpiDeviceDxe/SpiDeviceDxe.c | 152 +++++ .../Flash/SpiDeviceDxe/SpiDeviceDxe.inf | 64 ++ .../Flash/SpiDeviceDxe/SpiDeviceSmm.c | 222 +++++++ .../Flash/SpiDeviceDxe/SpiDeviceSmm.inf | 62 ++ .../Flash/SpiDeviceDxe/SpiDeviceSmmComm.h | 73 +++ .../Flash/SpiDeviceDxe/SpiDeviceSmmDxe.c | 437 ++++++++++++++ .../Flash/SpiDeviceDxe/SpiDeviceSmmDxe.inf | 51 ++ 9 files changed, 1931 insertions(+) create mode 100644 BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.c create mode 100644 BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.h create mode 100644 BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceDxe.c create mode 100644 BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceDxe.inf create mode 100644 BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmm.c create mode 100644 BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmm.inf create mode 100644 BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmComm.h create mode 100644 BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmDxe.c create mode 100644 BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmDxe.inf (limited to 'BraswellPlatformPkg/Flash') diff --git a/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.c b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.c new file mode 100644 index 0000000000..d4eaced0d4 --- /dev/null +++ b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.c @@ -0,0 +1,659 @@ +/** @file + SPI Device driver for Braswell Platform. + + Copyright (c) 2015, Intel Corporation. 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 +#include +#include "SpiDevice.h" + +EFI_SPI_PROTOCOL *mSpiProtocol; + +UINTN mNvStorageBase = 0; + +EFI_STATUS +EFIAPI +SpiRead ( + IN UINTN SpiOffset, + IN OUT UINTN *Size, + OUT UINT8 *Buffer + ) +{ + EFI_STATUS Status; + VOID *BiosMmioAddress; + UINTN RegionOffset; + UINTN Length; + UINTN RemainingBytes; + + // + // Validate parameters. + // + if (Size == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + if (SpiOffset + *Size > PcdGet32 (PcdFlashAreaSize)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if the read is taking place in a memory mapped part of the flash. + // Some flash regions may not be mapped for runtime access by the OS and must + // be accessed through the controller and not MMIO. + // + if (ReadUsingMmio (SpiOffset)) { + // + // Convert BIOS region offset into an actual memory address. + // + BiosMmioAddress = (VOID*) (SpiOffset + PcdGet32 (PcdFlashAreaBaseAddress)); + + // + // Do memory copy instead of using SPI controller. + // + CopyMem ((VOID*) Buffer, BiosMmioAddress, *Size); + } else if ((SpiOffset >= VN_STORAGE_REGION_FLASH_OFFSET) && (SpiOffset < (VN_STORAGE_REGION_FLASH_OFFSET + PcdGet32 (PcdFlashAreaSize)))) { + // + // Convert the offset into a memory address into the NV Storage region. At + // runtime this is the only region of the flash that is mapped for runtime + // access. Prior to runtime the preceding case will cover MMIO flash access. + // + BiosMmioAddress = (VOID*) ((SpiOffset - VN_STORAGE_REGION_FLASH_OFFSET) + mNvStorageBase); + + // + // Do memory copy instead of using SPI controller. + // + CopyMem ((VOID*) Buffer, BiosMmioAddress, *Size); + } else { + Status = EFI_SUCCESS; + RemainingBytes = *Size; + RegionOffset = SpiOffset; + while (RemainingBytes > 0) { + if (RemainingBytes > SIZE_4KB) { + Length = SIZE_4KB; + } else { + Length = RemainingBytes; + } + Status = mSpiProtocol->Execute ( + mSpiProtocol, + SPI_READ, + 0, + TRUE, + TRUE, + FALSE, + (UINT32) RegionOffset, + (UINT32) Length, + Buffer, + EnumSpiRegionAll + ); + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "Failed to read SPI region.\n")); + break; + } + RemainingBytes -= Length; + RegionOffset += Length; + Buffer += Length; + } + } + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +SpiWrite ( + IN UINTN SpiOffset, + IN OUT UINTN *Size, + IN UINT8 *Buffer + ) +{ + EFI_STATUS Status; + UINTN RegionOffset; + UINTN Length; + UINTN RemainingBytes; + + // + // Validate the input parameters + // + if (Size == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + if (SpiOffset + *Size > PcdGet32 (PcdFlashAreaSize)) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + RemainingBytes = *Size; + RegionOffset = SpiOffset; + + while (RemainingBytes > 0) { + if (RemainingBytes > SIZE_4KB) { + Length = SIZE_4KB; + } else { + Length = RemainingBytes; + } + Status = mSpiProtocol->Execute ( + mSpiProtocol, + SPI_PROG, + SPI_WREN, + TRUE, + TRUE, + TRUE, + (UINT32) RegionOffset, + (UINT32) Length, + Buffer, + EnumSpiRegionAll + ); + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "Failed to write SPI region.\n")); + break; + } + RemainingBytes -= Length; + RegionOffset += Length; + Buffer += Length; + } + + return Status; +} + +EFI_STATUS +EFIAPI +SpiErase ( + IN UINTN SpiOffset, + IN OUT UINTN Size + ) +{ + EFI_STATUS Status; + UINTN RegionOffset; + UINTN BytesRemaining; + + // + // Validate the input parameters + // + Status = EFI_INVALID_PARAMETER; + if (SpiOffset + Size > PcdGet32 (PcdFlashAreaSize)) { + return EFI_INVALID_PARAMETER; + } + + // + // Force the minimal alignment of 4k. + // + BytesRemaining = Size; + RegionOffset = SpiOffset; + if (RegionOffset & (SIZE_4KB - 1)) { + DEBUG((EFI_D_INFO, "Forcing SPI Device Erase alignment to a 4k base.\n")); + BytesRemaining += (RegionOffset & (SIZE_4KB - 1)); + RegionOffset = RegionOffset & (SIZE_4KB - 1); + } + + // + // Perform as many erase operations as needed to erase requested region. + // + while (BytesRemaining > 0) { + Status = mSpiProtocol->Execute ( + mSpiProtocol, + SPI_SERASE, + SPI_WREN, + FALSE, + TRUE, + FALSE, + (UINT32) RegionOffset, + 0, + NULL, + EnumSpiRegionAll + ); + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "Failed to erase SPI region.\n")); + break; + } + + // + // Update the number of bytes left to erase. + // + BytesRemaining -= SIZE_4KB; + RegionOffset += SIZE_4KB; + } + + return Status; +} + +EFI_STATUS +EFIAPI +SpiLock ( + IN UINTN SpiOffset, + IN OUT UINTN Size, + IN BOOLEAN Lock + ) +{ + // + // Block/Sector locking is not supported in this implementation. Use SpiSetRange + // and SpiLockRanges to protect areas of the flash. + // + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +SpiSetRange ( + IN UINTN SpiOffset, + IN UINTN Size, + IN BOOLEAN ReadLock, + IN BOOLEAN WriteLock + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +SpiLockRanges ( + ) +{ + // + // Call lock on the SPI interface. This will lock down further configuration + // changes in the SPI controller. + // + return mSpiProtocol->Lock (mSpiProtocol); +} + +/** + Get the JEDED ID from the SPI flash part. + + @param[in] Context Pointer to a context data structure + needed by the SPI controller driver + @param[in] Description Description of the flash device + @param[in] BufferLength Length of the JedecId buffer + @param[out] JedecId Pointer to a buffer to fill with + the JEDEC ID value + + @retval EFI_SUCCESS The JEDEC ID value is in the buffer + @retval EFI_INVALID_PARAMETER JedecId is NULL + @retval EFI_INVALID_PARAMETER Description is NULL + @retval EFI_INVALID_PARAMETER Too few opcode entries + @retval EFI_INVALID_PARAMETER JEDEC ID response buffer too small + @retval EFI_UNSUPPORTED JEDEC ID opcode not found + +**/ +EFI_STATUS +EFIAPI +JedecIdRead ( + IN VOID *Context, + IN CONST FLASH_PART_DESCRIPTION *Description, + IN UINTN BufferLength, + OUT UINT8 *JedecId + ) +{ + // + // Validate parameters. + // + if ((JedecId == NULL) + || (Description == NULL) + || (BufferLength < Description->JededIdResponseLengthInBytes)) { + return EFI_INVALID_PARAMETER; + } + + return mSpiProtocol->ReadId (mSpiProtocol, 0, JedecId); +} + +/** + Determine the flash size and description + + @param[in] PerformJedecIdOperation Callback routine to initiate + the JEDEC ID operation using + the SPI controller to identify + the flash part. + @param[in] Context Pointer to a context structure to pass + to PerformJedecIdOperation + @param[out] FlashDescription Pointer to a buffer to receive a + pointer to a FLASH_PART_DESCRIPTION + data structure containing the flash + part information. + + @return This routine returns the size of the flash part if it is + supported. Zero is returned if the flash part is not + supported. + +**/ +UINT64 +EFIAPI +FindFlashSupport ( + IN PERFORM_JEDEC_ID_OPERATION PerformJedecIdOperation, + IN VOID *Context, + OUT CONST FLASH_PART_DESCRIPTION **FlashDescription + ) +{ + UINTN BufferLength; + CONST FLASH_PART_DESCRIPTION *Description; + UINT64 FlashSize; + EFI_HANDLE *HandleArray; + UINTN HandleCount; + UINTN HandleIndex; + UINT8 *JedecId; + UINT32 MaxPriority; + UINT32 Priority; + SPI_FLASH_PART_PROTOCOL *Protocol; + SPI_FLASH_PART_PROTOCOL **SpiFlashPartProtocol; + EFI_STATUS Status; + + // + // Assume failure + // + FlashSize = 0; + HandleArray = NULL; + JedecId = NULL; + SpiFlashPartProtocol = NULL; + + // + // Locate handles containing SPI_FLASH_PART_PROTOCOLS + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gSpiFlashPartProtocolGuid, + NULL, + &HandleCount, + &HandleArray + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ERROR - Failed to locate SPI_FLASH_PART_PROTOCOL, Status: %r\r\n", Status)); + } else { + // + // Allocate and fill in the protocol array + // + DEBUG ((DEBUG_INFO, "%d SPI flash part descriptions found\r\n", HandleCount)); + SpiFlashPartProtocol = AllocatePool (HandleCount * sizeof (*SpiFlashPartProtocol)); + if (SpiFlashPartProtocol == NULL) { + DEBUG ((DEBUG_ERROR, "ERROR - Failed to allocate SpiFlashDataProtocol buffer\r\n")); + } else { + for (HandleIndex = 0; HandleCount > HandleIndex; HandleIndex++) { + Status = gBS->OpenProtocol ( + HandleArray [HandleIndex], + &gSpiFlashPartProtocolGuid, + (VOID **) &SpiFlashPartProtocol [HandleIndex], + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "ERROR - Failed to open SPI_FLASH_DATA_PROTOCOL, Status: %r\r\n", Status)); + break; + } + } + if (!EFI_ERROR (Status)) { + // + // Allocate the JEDEC ID buffer + // + BufferLength = 0; + for (HandleIndex = 0; HandleCount > HandleIndex; HandleIndex++) { + // + // Get the JEDEC ID opcode description + // + Protocol = SpiFlashPartProtocol [HandleIndex]; + Description = Protocol->GetFlashDescription ( + Protocol, + NULL, + NULL + ); + if (BufferLength < Description->JededIdResponseLengthInBytes) { + BufferLength = Description->JededIdResponseLengthInBytes; + } + } + JedecId = AllocatePool (BufferLength); + if (JedecId == NULL) { + DEBUG ((DEBUG_ERROR, "ERROR - Failed to allocate JedecId buffer\r\n")); + } else { + // + // Start with the first flash type description; + // + MaxPriority = 0xffffffff; + do { + // + // Determine the highest priority protocol + // + Priority = 0; + for (HandleIndex = 0; HandleCount > HandleIndex; HandleIndex++) { + Protocol = SpiFlashPartProtocol [HandleIndex]; + if ((MaxPriority >= Protocol->Priority) + && (Priority < Protocol->Priority)) + Priority = Protocol->Priority; + } + if (Priority == 0) { + // + // The flash is not supported + // + break; + } + + // + // Walk the handles containing the SPI flash part protocol + // + HandleIndex = 0; + do { + // + // Verify the description type matches and the opcode table + // supports the minimum number of entries required for the code + // + Protocol = SpiFlashPartProtocol [HandleIndex]; + if (Priority == Protocol->Priority) { + // + // Get the JEDEC ID opcode description + // + Description = Protocol->GetFlashDescription ( + Protocol, + NULL, + NULL + ); + if ((Description == NULL) + || (SPI_FLASH_PART_OPCODE_JEDEC_ID == Description->OpcodeTableEntries)) { + DEBUG ((DEBUG_ERROR, "ERROR - JEDEC ID opcode not available\r\n")); + } else { + // + // Display the flash part + // + DEBUG ((DEBUG_INFO, "Priority: 0x%08x, SPI Flash Part: %s\r\n", Priority, Description->PartNumber )); + + // + // Attempt to read the JEDEC ID + // + Status = PerformJedecIdOperation ( + Context, + Description, + Description->JededIdResponseLengthInBytes, + JedecId + ); + if (!EFI_ERROR (Status)) { + // + // Display the JEDEC ID + // + DEBUG_CODE_BEGIN (); + { + UINTN Index; + + DEBUG ((DEBUG_INFO, "JEDEC ID:")); + for (Index = 0; Description->JededIdResponseLengthInBytes > Index; Index++) { + DEBUG ((DEBUG_INFO, " 0x%02x", JedecId [Index])); + } + DEBUG ((DEBUG_INFO, "\r\n")); + } + DEBUG_CODE_END (); + + // + // Verify support and determine flash size + // + Description = Protocol->GetFlashDescription ( + Protocol, + JedecId, + &FlashSize + ); + if (Description != NULL) { + // + // The flash device is supported + // Return the table for this flash device + // + DEBUG ((DEBUG_INFO, "SPI flash device found: %s\r\n", Description->PartNumber)); + *FlashDescription = Description; + goto PartFound; + } + } + } + } + + // + // Set next handle + // + HandleIndex += 1; + } while (HandleCount > HandleIndex); + + // + // Set the next priority + // + MaxPriority = Priority - 1; + } while (Priority != 0); + + // + // No flash device found + // + DEBUG ((DEBUG_ERROR, "Matching SPI flash description not found\r\n")); + } + } + } + } + +PartFound: + // + // Free the buffers + // + if (JedecId != NULL) { + FreePool (JedecId); + } + if (SpiFlashPartProtocol != NULL) { + FreePool (SpiFlashPartProtocol); + } + if (HandleArray != NULL) { + FreePool (HandleArray); + } + + // + // Return the flash size + // Zero (0) indicates flash not found or not supported + // + return FlashSize; +} + +/** + Load opcode into the SPI controller for specific flash device + + @param[in] FlashDescription Description of the flash device + + @retval EFI_SUCCESS The opcode was successfully loaded + @retval EFI_UNSUPPORTED The opcode was not found + +**/ +EFI_STATUS +SpiFlashInit ( + IN CONST FLASH_PART_DESCRIPTION *FlashDescription + ) +{ + EFI_STATUS Status; + SPI_INIT_DATA SpiInitTable; + UINTN Index; + UINT8 CmdCfgIndex; + CONST UINT8 OpcodeMap [] = { + SPI_READ_ID, // SPI_FLASH_PART_OPCODE_JEDEC_ID + SPI_RDSR, // SPI_FLASH_PART_OPCODE_READ_STATUS + SPI_WRSR, // SPI_FLASH_PART_OPCODE_WRITE_STATUS + SPI_READ, // SPI_FLASH_PART_OPCODE_READ_BYTES + SPI_PROG, // SPI_FLASH_PART_OPCODE_WRITE_256_BYTE_PAGE + SPI_SERASE, // SPI_FLASH_PART_OPCODE_ERASE_4K_BYTE_BLOCK + SPI_BERASE, // SPI_FLASH_PART_OPCODE_ERASE_64K_BYTE_BLOCK + SPI_WRDI_SFDP // SPI_FLASH_PART_OPCODE_WRITE_DISABLE + }; + + Status = EFI_SUCCESS; + + ZeroMem (&SpiInitTable, sizeof (SPI_INIT_DATA)); + + SpiInitTable.PrefixOpcode[SPI_WREN] = FlashDescription->WriteEnable; + SpiInitTable.PrefixOpcode[SPI_EWSR] = FlashDescription->WriteStatusEnable; + + SpiInitTable.BiosStartOffset = (UINTN)BIOS_REGION_FLASH_OFFSET; + SpiInitTable.BiosSize = (UINTN)PcdGet32 (PcdBiosImageSize); + SpiInitTable.SpecialOpcodeEntry = NULL; + + ASSERT (FlashDescription->OpcodeTableEntries <= SPI_NUM_OPCODE); + + for (Index = 0;Index < FlashDescription->OpcodeTableEntries;Index++) { + CmdCfgIndex = OpcodeMap[Index]; + switch (FlashDescription->OpcodeTable[Index].MaxFrequency) { + case 20000000: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Frequency = EnumSpiCycle20MHz; + break; + + case 33000000: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Frequency = EnumSpiCycle33MHz; + break; + + case 50000000: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Frequency = EnumSpiCycle50MHz; + break; + + case 66000000: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Frequency = EnumSpiCycle66MHz; + break; + + default: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Frequency = EnumSpiCycle33MHz; + break; + } + + switch (FlashDescription->OpcodeTable[Index].OpcodeIndex) { + case SPI_FLASH_PART_OPCODE_JEDEC_ID: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Operation = EnumSpiOperationJedecId; + break; + + case SPI_FLASH_PART_OPCODE_READ_STATUS: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Operation = EnumSpiOperationReadStatus; + break; + + case SPI_FLASH_PART_OPCODE_WRITE_STATUS: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Operation = EnumSpiOperationWriteStatus; + break; + + case SPI_FLASH_PART_OPCODE_READ_BYTES: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Operation = EnumSpiOperationReadData; + break; + + case SPI_FLASH_PART_OPCODE_WRITE_256_BYTE_PAGE: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Operation = EnumSpiOperationProgramData_1_Byte; + break; + + case SPI_FLASH_PART_OPCODE_ERASE_4K_BYTE_BLOCK: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Operation = EnumSpiOperationErase_4K_Byte; + break; + + case SPI_FLASH_PART_OPCODE_ERASE_64K_BYTE_BLOCK: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Operation = EnumSpiOperationErase_64K_Byte; + break; + + case SPI_FLASH_PART_OPCODE_WRITE_DISABLE_DISCOVERY: + SpiInitTable.SpiCmdConfig[CmdCfgIndex].Operation = EnumSpiOperationWriteDisable; + break; + + default: + DEBUG ((DEBUG_ERROR, "Unrecognized opcode index\r\n")); + ASSERT(FALSE); + break; + } + } + + Status = mSpiProtocol->Init (mSpiProtocol, &SpiInitTable); + + return Status; +} diff --git a/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.h b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.h new file mode 100644 index 0000000000..cb8229629d --- /dev/null +++ b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDevice.h @@ -0,0 +1,211 @@ +/** @file + SPI Device driver for Braswell Platform. + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SPI_DEVICE_H_ +#define _SPI_DEVICE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Defines the offset in the SPI device where the BIOS region starts. +// +#define BIOS_REGION_FLASH_OFFSET (PcdGet32 (PcdBiosImageBase) - PcdGet32 (PcdFlashAreaBaseAddress)) +#define VN_STORAGE_REGION_FLASH_OFFSET (PcdGet32 (PcdFlashNvStorageVariableBase) - PcdGet32 (PcdFlashAreaBaseAddress)) + +extern EFI_SPI_PROTOCOL *mSpiProtocol; +extern UINTN mNvStorageBase; + +// +// Prefix Opcode Index on the host SPI controller +// +typedef enum { + SPI_WREN, // Prefix Opcode 0: Write Enable + SPI_EWSR, // Prefix Opcode 1: Enable Write Status Register +} PREFIX_OPCODE_INDEX; + +// +// Opcode Menu Index on the host SPI controller +// +typedef enum { + SPI_READ_ID, // Opcode 0: READ ID, Read cycle with address + SPI_READ, // Opcode 1: READ, Read cycle with address + SPI_RDSR, // Opcode 2: Read Status Register, No address + SPI_WRDI_SFDP, // Opcode 3: Write Disable or Discovery Parameters, No address + SPI_SERASE, // Opcode 4: Sector Erase (4KB), Write cycle with address + SPI_BERASE, // Opcode 5: Block Erase (32KB), Write cycle with address + SPI_PROG, // Opcode 6: Byte Program, Write cycle with address + SPI_WRSR, // Opcode 7: Write Status Register, No address +} SPI_OPCODE_INDEX; + +EFI_STATUS +EFIAPI +InitSpiDevice ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +EFI_STATUS +EFIAPI +SpiRead ( + IN UINTN SpiOffset, + IN OUT UINTN *Size, + OUT UINT8 *Buffer + ); + +EFI_STATUS +EFIAPI +SpiWrite ( + IN UINTN SpiOffset, + IN OUT UINTN *Size, + IN UINT8 *Buffer + ); + +EFI_STATUS +EFIAPI +SpiErase ( + IN UINTN SpiOffset, + IN OUT UINTN Size + ); + +EFI_STATUS +EFIAPI +SpiLock ( + IN UINTN SpiOffset, + IN OUT UINTN Size, + IN BOOLEAN Lock + ); + +EFI_STATUS +EFIAPI +SpiSetRange ( + IN UINTN SpiOffset, + IN UINTN Size, + IN BOOLEAN ReadLock, + IN BOOLEAN WriteLock + ); + +EFI_STATUS +EFIAPI +SpiLockRanges ( + ); + +BOOLEAN +ReadUsingMmio ( + IN UINTN SpiOffset + ); + +/** + Get the JEDED ID from the SPI flash part. + + @param[in] Context Pointer to a context data structure + needed by the SPI controller driver + @param[in] Description Description of the flash device + @param[in] BufferLength Length of the JedecId buffer + @param[out] JedecId Pointer to a buffer to fill with + the JEDEC ID value + + @retval EFI_SUCCESS The JEDEC ID value is in the buffer + @retval EFI_INVALID_PARAMETER JedecId is NULL + @retval EFI_INVALID_PARAMETER Description is NULL + @retval EFI_INVALID_PARAMETER Too few opcode entries + @retval EFI_INVALID_PARAMETER JEDEC ID response buffer too small + @retval EFI_UNSUPPORTED JEDEC ID opcode not found + +**/ +EFI_STATUS +EFIAPI +JedecIdRead ( + IN VOID *Context, + IN CONST FLASH_PART_DESCRIPTION *Description, + IN UINTN BufferLength, + OUT UINT8 *JedecId + ); + +/** + Get the JEDED ID from the SPI flash part. + + @param[in] Context Pointer to a context data structure + needed by the SPI controller driver + @param[in] Description Description of the flash device + @param[in] BufferLength Length of the JedecId buffer + @param[out] JedecId Pointer to a buffer to fill with + the JEDEC ID value + + @retval EFI_SUCCESS The JEDEC ID value is in the buffer + @retval EFI_INVALID_PARAMETER JedecId is NULL + @retval EFI_INVALID_PARAMETER Description is NULL + @retval EFI_INVALID_PARAMETER Too few opcode entries + @retval EFI_INVALID_PARAMETER JEDEC ID response buffer too small + +**/ +typedef +EFI_STATUS +(EFIAPI *PERFORM_JEDEC_ID_OPERATION) ( + IN VOID *Context, + IN CONST FLASH_PART_DESCRIPTION *Description, + IN UINTN BufferLength, + OUT UINT8 *JedecId + ); + +/** + Determine the flash size and description + + @param[in] PerformJedecIdOperation Callback routine to initiate + the JEDEC ID operation using + the SPI controller to identify + the flash part. + @param[in] Context Pointer to a context structure to pass + to PerformJedecIdOperation + @param[out] FlashDescription Pointer to a buffer to receive a + pointer to a FLASH_PART_DESCRIPTION + data structure containing the flash + part information. + + @return This routine returns the size of the flash part if it is + supported. Zero is returned if the flash part is not + supported. + +**/ +UINT64 +EFIAPI +FindFlashSupport ( + IN PERFORM_JEDEC_ID_OPERATION PerformJedecIdOperation, + IN VOID *Context, + OUT CONST FLASH_PART_DESCRIPTION **FlashDescription + ); + + +/** + Load opcode into the SPI controller for specific flash device + + @param[in] FlashDescription Description of the flash device + + @retval EFI_SUCCESS The opcode was successfully loaded + @retval EFI_UNSUPPORTED The opcode was not found + +**/ +EFI_STATUS +SpiFlashInit ( + IN CONST FLASH_PART_DESCRIPTION *FlashDescription + ); + +#endif diff --git a/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceDxe.c b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceDxe.c new file mode 100644 index 0000000000..907b8e9517 --- /dev/null +++ b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceDxe.c @@ -0,0 +1,152 @@ +/** @file + This driver for SPI Device initialization + + Copyright (c) 2015, Intel Corporation. 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 +#include +#include +#include +#include +#include +#include +#include "SpiDevice.h" + +SPI_DEVICE_PROTOCOL mSpiDevProtocol = { + SpiRead, + SpiWrite, + SpiErase, + SpiLock, + SpiSetRange, + SpiLockRanges +}; + +VOID +EFIAPI +SpiDeviceVirtualAddressChangeEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Update protocol pointer to the SPI Controller interface. + // + EfiConvertPointer (0x00, (VOID**) &(mSpiProtocol)); + + // + // Update the NV Storage location for runtime access. + // + EfiConvertPointer (0x00, (VOID**) &(mNvStorageBase)); + + // + // Take care of pointers in protocol. + // + EfiConvertPointer (0x00, (VOID**) &(mSpiDevProtocol.SpiRead)); + EfiConvertPointer (0x00, (VOID**) &(mSpiDevProtocol.SpiWrite)); + EfiConvertPointer (0x00, (VOID**) &(mSpiDevProtocol.SpiErase)); + EfiConvertPointer (0x00, (VOID**) &(mSpiDevProtocol.SpiLock)); +} + +EFI_STATUS +EFIAPI +InitSpiDevice ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_EVENT Event; + CONST FLASH_PART_DESCRIPTION *FlashDescription; + UINT64 FlashSize; + EFI_HANDLE Handle; + EFI_STATUS Status; + + mNvStorageBase = PcdGet32 (PcdFlashNvStorageVariableBase); + + // + // Locate the SPI controller protocol and save it for later. + // + DEBUG((EFI_D_INFO, "Locating SPI Controller Protocol.\n")); + Status = gBS->LocateProtocol ( + &gEfiSpiProtocolGuid, + NULL, + (VOID **) &mSpiProtocol + ); + ASSERT_EFI_ERROR(Status); + + // + // Loop through all the flash devices that are supported and see if one will + // initialize the SPI Controller interface. + // + FlashSize = FindFlashSupport ( + &JedecIdRead, + NULL, + &FlashDescription + ); + if (FlashSize == 0) { + DEBUG((EFI_D_ERROR, "No SPI flash part description found!\r\n")); + } else { + // + // Attempt to configure the SPI controller for this device. + // + DEBUG((EFI_D_INFO, "SPI flash size: %d MBytes\n", DivU64x32(FlashSize, 1024 * 1024 ))); + DEBUG((EFI_D_INFO, "Configuring SPI Controller.\n")); + + Status = SpiFlashInit (FlashDescription); + if (!EFI_ERROR (Status)) { + // + // Publish the SPI Device protocol. + // + DEBUG((EFI_D_INFO, "Installing SPI Device Protocol.\n")); + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gSpiDeviceProtocolGuid, + &mSpiDevProtocol, + NULL + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Make sure we can handle virtual address changes. + // + Event = NULL; + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + SpiDeviceVirtualAddressChangeEvent, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &Event + ); + + return EFI_SUCCESS; + } + } + + // + // Unable to find a supported SPI device + // + DEBUG((EFI_D_ERROR, "Unable to configure SPI Controller for SPI device present.\n")); + + return EFI_UNSUPPORTED; +} + +BOOLEAN +ReadUsingMmio ( + IN UINTN SpiOffset + ) +{ + return (BOOLEAN) ((SpiOffset >= BIOS_REGION_FLASH_OFFSET) && (SpiOffset < (BIOS_REGION_FLASH_OFFSET + PcdGet32 (PcdBiosImageSize))) && (!EfiAtRuntime ())); +} diff --git a/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceDxe.inf b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceDxe.inf new file mode 100644 index 0000000000..9db458076c --- /dev/null +++ b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceDxe.inf @@ -0,0 +1,64 @@ +## @file +# SPI Flash Device Driver +# +# Adds platform support to configure the SPI controller with the correct values +# to be used when using software sequencing. +# +# Copyright (c) 2015, Intel Corporation. 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. +# +## + +[Defines] + INF_VERSION = 0x00010018 + BASE_NAME = SpiDeviceDxe + FILE_GUID = DA28E378-C84B-4969-BD4D-90AA883C091A + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitSpiDevice + +[Sources] + SpiDeviceDxe.c + SpiDevice.c + SpiDevice.h + +[Packages] + ChvRefCodePkg/ChvRefCodePkg.dec + BraswellPlatformPkg/BraswellPlatformPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + DebugLib + DxeServicesTableLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeLib + MemoryAllocationLib + +[Protocols] + gEfiSpiProtocolGuid ## CONSUMES + gSpiDeviceProtocolGuid ## PRODUCES + gSpiFlashPartProtocolGuid ## CONSUMES + gEfiSmmCommunicationProtocolGuid ## UNDEFINED + +[Guids] + gEfiEventVirtualAddressChangeGuid ## SOMETIMES_CONSUMES ## NOTIFY + +[Pcd] + gPlatformModuleTokenSpaceGuid.PcdFlashAreaBaseAddress ## CONSUMES + gPlatformModuleTokenSpaceGuid.PcdFlashAreaSize ## CONSUMES + gPlatformModuleTokenSpaceGuid.PcdBiosImageBase ## CONSUMES + gPlatformModuleTokenSpaceGuid.PcdBiosImageSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase ## CONSUMES + +[Depex] + gEfiSpiProtocolGuid + diff --git a/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmm.c b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmm.c new file mode 100644 index 0000000000..7d02aff9d0 --- /dev/null +++ b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmm.c @@ -0,0 +1,222 @@ +/** @file + SMM driver for SPI Device initialization. + + Copyright (c) 2015, Intel Corporation. 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 +#include +#include +#include +#include +#include "SpiDevice.h" +#include "SpiDeviceSmmComm.h" + +SPI_DEVICE_PROTOCOL mSpiDevProtocol = { + SpiRead, + SpiWrite, + SpiErase, + SpiLock, + SpiSetRange, + SpiLockRanges +}; + +EFI_STATUS +EFIAPI +SpiDeviceSmmHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *RegisterContext, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status; + SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER *SpiDevCommHeader; + SMM_SPI_DEV_READ_WRITE_ERASE_HEADER *SpiDevDataOpHeader; + SMM_SPI_DEV_LOCK_HEADER *SpiDevLockHeader; + SMM_SPI_DEV_SET_RANGE_HEADER *SpiDevSetRangeHeader; + + ASSERT (CommBuffer != NULL); + + SpiDevCommHeader = (SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER*) CommBuffer; + switch (SpiDevCommHeader->Function) { + case SPI_DEV_FUNCTION_READ: + SpiDevDataOpHeader = (SMM_SPI_DEV_READ_WRITE_ERASE_HEADER*) SpiDevCommHeader->Data; + Status = SpiRead ( + SpiDevDataOpHeader->Offset, + &SpiDevDataOpHeader->Size, + (UINT8*) (SpiDevDataOpHeader + 1) + ); + break; + case SPI_DEV_FUNCTION_WRITE: + SpiDevDataOpHeader = (SMM_SPI_DEV_READ_WRITE_ERASE_HEADER*) SpiDevCommHeader->Data; + Status = SpiWrite ( + SpiDevDataOpHeader->Offset, + &SpiDevDataOpHeader->Size, + (UINT8*) (SpiDevDataOpHeader + 1) + ); + break; + case SPI_DEV_FUNCTION_ERASE: + SpiDevDataOpHeader = (SMM_SPI_DEV_READ_WRITE_ERASE_HEADER*) SpiDevCommHeader->Data; + Status = SpiErase ( + SpiDevDataOpHeader->Offset, + SpiDevDataOpHeader->Size + ); + break; + case SPI_DEV_FUNCTION_LOCK: + SpiDevLockHeader = (SMM_SPI_DEV_LOCK_HEADER*) SpiDevCommHeader->Data; + Status = SpiLock ( + SpiDevLockHeader->Offset, + SpiDevLockHeader->Size, + SpiDevLockHeader->Lock + ); + break; + case SPI_DEV_FUNCTION_SET_RANGE: + SpiDevSetRangeHeader = (SMM_SPI_DEV_SET_RANGE_HEADER*) SpiDevCommHeader->Data; + Status = SpiSetRange ( + SpiDevSetRangeHeader->Offset, + SpiDevSetRangeHeader->Size, + SpiDevSetRangeHeader->ReadLock, + SpiDevSetRangeHeader->WriteLock + ); + break; + case SPI_DEV_FUNCTION_LOCK_RANGES: + Status = SpiLockRanges (); + break; + default: + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + break; + } + + // + // Set the return value. + // + SpiDevCommHeader->ReturnStatus = Status; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +InitSpiDevice ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + CONST FLASH_PART_DESCRIPTION *FlashDescription; + UINT64 FlashSize; + EFI_HANDLE Handle; + EFI_STATUS Status; + + //-------------------------------------------------- + // + // Note only this routine is able to make calls + // into the DXE environment since it is called + // synchronously from that environment and DXE + // is still executing in physical mode. + // + //-------------------------------------------------- + + mNvStorageBase = PcdGet32 (PcdFlashNvStorageVariableBase); + + // + // Locate the SPI controller protocol and save it for later. + // + DEBUG((EFI_D_INFO, "Locating SPI Controller Protocol.\n")); + Status = gSmst->SmmLocateProtocol ( + &gEfiSmmSpi2ProtocolGuid, + NULL, + (VOID **) &mSpiProtocol + ); + ASSERT_EFI_ERROR(Status); + + + // + // Loop through all the flash devices that are supported and see if one will + // initialize the SPI Controller interface. + // + FlashSize = FindFlashSupport ( + &JedecIdRead, + NULL, + &FlashDescription + ); + if (FlashSize == 0) { + DEBUG((EFI_D_ERROR, "No SPI flash part description found!\r\n")); + } else { + // + // Attempt to configure the SPI controller for this device. + // + DEBUG((EFI_D_INFO, "SPI flash size: %d MBytes\n", DivU64x32(FlashSize, 1024 * 1024 ))); + DEBUG((EFI_D_INFO, "Configuring SPI Controller.\n")); + Status = SpiFlashInit (FlashDescription); + if (!EFI_ERROR (Status)) { + // + // Publish the SMM SPI Device protocol for FVB service + // + DEBUG((EFI_D_INFO, "Installing SPI Device Protocol.\n")); + Handle = NULL; + Status = gSmst->SmmInstallProtocolInterface ( + &Handle, + &gSmmSpiDeviceProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSpiDevProtocol + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Install protocol to inform other DXE drivers the SMM service is available. + // + Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Handle, + &gSmmSpiDeviceProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSpiDevProtocol + ); + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "Unable to install SMM SPI device protocol, Status: %r\n", Status)); + return Status; + } + + // + // Install communication handler. + // + Handle = NULL; + Status = gSmst->SmiHandlerRegister (SpiDeviceSmmHandler, &gSmmSpiDeviceProtocolGuid, &Handle); + if (EFI_ERROR (Status)) { + DEBUG((EFI_D_ERROR, "Unable to register SMM SPI handler, Status: %r\n", Status)); + return Status; + } + + DEBUG((EFI_D_INFO, "SPI flash controller configured successfully\n", Status)); + return EFI_SUCCESS; + } + } + + // + // Unable to find a supported SPI device + // + DEBUG((EFI_D_ERROR, "Unable to configure SPI Controller for SPI device present.\n")); + + return EFI_UNSUPPORTED; +} + +BOOLEAN +ReadUsingMmio ( + IN UINTN SpiOffset + ) +{ + return (BOOLEAN) ((SpiOffset >= BIOS_REGION_FLASH_OFFSET) && (SpiOffset < (BIOS_REGION_FLASH_OFFSET + PcdGet32 (PcdBiosImageSize)))); +} diff --git a/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmm.inf b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmm.inf new file mode 100644 index 0000000000..d698f25159 --- /dev/null +++ b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmm.inf @@ -0,0 +1,62 @@ +## @file +# SPI Device SMM Driver +# +# Adds platform support to configure the SPI controller with the correct values +# to be used when using software sequencing. This driver initializes EMST* F25L016A +# SPI flash device and installs gSmmSpiDeviceProtocolGuid protocol. +# +# Copyright (c) 2015, Intel Corporation. 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. +# +## + +[Defines] + INF_VERSION = 0x00010018 + BASE_NAME = SpiDeviceSmm + FILE_GUID = 163774A8-917F-40E5-AB54-B4BFA11D41F9 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = InitSpiDevice + +[Sources] + SpiDeviceSmm.c + SpiDevice.c + SpiDevice.h + +[Packages] + ChvRefCodePkg/ChvRefCodePkg.dec + BraswellPlatformPkg/BraswellPlatformPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + UefiBootServicesTableLib + UefiDriverEntryPoint + DebugLib + SmmServicesTableLib + BaseLib + MemoryAllocationLib + +[Protocols] + gEfiSmmSpi2ProtocolGuid ## CONSUMES + gSmmSpiDeviceProtocolGuid ## PRODUCES + gSpiFlashPartProtocolGuid ## CONSUMES + +[Pcd] + gPlatformModuleTokenSpaceGuid.PcdFlashAreaBaseAddress ## CONSUMES + gPlatformModuleTokenSpaceGuid.PcdFlashAreaSize ## CONSUMES + gPlatformModuleTokenSpaceGuid.PcdBiosImageBase ## CONSUMES + gPlatformModuleTokenSpaceGuid.PcdBiosImageSize ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase ## CONSUMES + +[Depex] + gEfiSmmSpi2ProtocolGuid + diff --git a/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmComm.h b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmComm.h new file mode 100644 index 0000000000..ee2d0a42a4 --- /dev/null +++ b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmComm.h @@ -0,0 +1,73 @@ +/** @file + SMM Communication formats for the SPI Device protocols. + + Copyright (c) 2015, Intel Corporation. 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. + +**/ + +#ifndef _SPI_DEVICE_SMM_COMM_H_ +#define _SPI_DEVICE_SMM_COMM_H_ + +#include + +// +// Define communication constants +// +#define SPI_DEV_FUNCTION_READ 1 +#define SPI_DEV_FUNCTION_WRITE 2 +#define SPI_DEV_FUNCTION_ERASE 3 +#define SPI_DEV_FUNCTION_LOCK 4 +#define SPI_DEV_FUNCTION_SET_RANGE 5 +#define SPI_DEV_FUNCTION_LOCK_RANGES 6 + +// +// Generic SPI Device communication structure header. +// +typedef struct { + UINTN Function; + EFI_STATUS ReturnStatus; + UINT8 Data[1]; +} SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER; + +// +// Macros used to determine size of the headers without data size. +// +#define SMM_COMMUNICATE_HEADER_SIZE (OFFSET_OF (EFI_SMM_COMMUNICATE_HEADER, Data)) +#define SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER_SIZE (OFFSET_OF (SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER, Data)) + +// +// SPI Read, Write and Erase Data. Erase will not have any extra data. +// +typedef struct { + UINTN Offset; + UINTN Size; +} SMM_SPI_DEV_READ_WRITE_ERASE_HEADER; + +// +// SPI Lock +// +typedef struct { + UINTN Offset; + UINTN Size; + BOOLEAN Lock; +} SMM_SPI_DEV_LOCK_HEADER; + +// +// SPI Set Range +// +typedef struct { + UINTN Offset; + UINTN Size; + BOOLEAN ReadLock; + BOOLEAN WriteLock; +} SMM_SPI_DEV_SET_RANGE_HEADER; + +#endif diff --git a/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmDxe.c b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmDxe.c new file mode 100644 index 0000000000..392c549a95 --- /dev/null +++ b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmDxe.c @@ -0,0 +1,437 @@ +/** @file + Provides an interface to the SMM SPI Device driver. + + gSpiDeviceProtocolGuid (DXE: SpiDeviceSmmDxe) + | + | via gEfiSmmCommunicationProtocolGuid + V + gSmmSpiDeviceProtocolGuid (SMM: SpiDeviceSmm) + | + | + V + gEfiSmmSpi2ProtocolGuid (SMM: SpiSmm) + + Copyright (c) 2015, Intel Corporation. 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 +#include +#include +#include +#include +#include +#include "SpiDevice.h" +#include "SpiDeviceSmmComm.h" + +EFI_SMM_COMMUNICATION_PROTOCOL *mSmmComm = NULL; + +SPI_DEVICE_PROTOCOL mSpiDevProtocol = { + SpiRead, + SpiWrite, + SpiErase, + SpiLock, + SpiSetRange, + SpiLockRanges +}; + +VOID +EFIAPI +SmmSpiDeviceReady ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +EFI_STATUS +CreateCommBuffer ( + OUT VOID **CommBuffer, + OUT VOID **DataArea, + IN UINTN DataSize, + IN UINTN Function + ) +{ + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER *SmmSpiDevFunctionHeader; + + // + // Allocate communication buffer. + // + SmmCommunicateHeader = AllocatePool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER_SIZE); + if (SmmCommunicateHeader == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Fill in new structure will data from caller. + // + CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gSmmSpiDeviceProtocolGuid); + SmmCommunicateHeader->MessageLength = DataSize + SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER_SIZE; + SmmSpiDevFunctionHeader = (SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER*) SmmCommunicateHeader->Data; + SmmSpiDevFunctionHeader->Function = Function; + + // + // Assign return values. + // + *CommBuffer = SmmCommunicateHeader; + if (DataArea != NULL) { + *DataArea = SmmSpiDevFunctionHeader->Data; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +SendCommBuffer ( + IN OUT EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader, + IN UINTN DataSize + ) +{ + EFI_STATUS Status; + UINTN CommSize; + SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER *SmmSpiDevFunctionHeader; + + // + // Compute actual size of communication data. + // + CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER_SIZE; + + // + // Send the message to be processed in SMM. + // + Status = mSmmComm->Communicate (mSmmComm, SmmCommunicateHeader, &CommSize); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the return value from the SMM function. + // + SmmSpiDevFunctionHeader = (SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER*) SmmCommunicateHeader->Data; + + return SmmSpiDevFunctionHeader->ReturnStatus; +} + +EFI_STATUS +EFIAPI +SpiRead ( + IN UINTN SpiOffset, + IN OUT UINTN *Size, + OUT UINT8 *Buffer + ) +{ + EFI_STATUS Status; + UINTN DataSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_SPI_DEV_READ_WRITE_ERASE_HEADER *SpiDevReadHeader; + + // + // Validate input parameters. + // + if (Size == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Determine the actual data size required for the transaction. + // + DataSize = *Size + sizeof(SMM_SPI_DEV_READ_WRITE_ERASE_HEADER); + + // + // Create the communication buffer. + // + Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SpiDevReadHeader, DataSize, SPI_DEV_FUNCTION_READ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Fill in communication buffer parameters. + // + SpiDevReadHeader->Offset = SpiOffset; + SpiDevReadHeader->Size = *Size; + + // + // Communicate request to SMM driver and fill in return values. + // + Status = SendCommBuffer (SmmCommunicateHeader, DataSize); + *Size = SpiDevReadHeader->Size; + if (!EFI_ERROR (Status)) { + CopyMem (Buffer, (UINT8*)(SpiDevReadHeader + 1), *Size); + } + + return Status; +} + +EFI_STATUS +EFIAPI +SpiWrite ( + IN UINTN SpiOffset, + IN OUT UINTN *Size, + IN UINT8 *Buffer + ) +{ + EFI_STATUS Status; + UINTN DataSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_SPI_DEV_READ_WRITE_ERASE_HEADER *SpiDevWriteHeader; + + // + // Validate input parameters. + // + if (Size == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Determine the actual data size required for the transaction. + // + DataSize = *Size + sizeof(SMM_SPI_DEV_READ_WRITE_ERASE_HEADER); + + // + // Create the communication buffer. + // + Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SpiDevWriteHeader, DataSize, SPI_DEV_FUNCTION_WRITE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Fill in communication buffer parameters. + // + SpiDevWriteHeader->Offset = SpiOffset; + SpiDevWriteHeader->Size = *Size; + CopyMem ((UINT8*)(SpiDevWriteHeader + 1), Buffer, *Size); + + // + // Communicate request to SMM driver and fill in return values. + // + Status = SendCommBuffer (SmmCommunicateHeader, DataSize); + *Size = SpiDevWriteHeader->Size; + + return Status; +} + +EFI_STATUS +EFIAPI +SpiErase ( + IN UINTN SpiOffset, + IN OUT UINTN Size + ) +{ + EFI_STATUS Status; + UINTN DataSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_SPI_DEV_READ_WRITE_ERASE_HEADER *SpiDevEraseHeader; + + // + // Determine the actual data size required for the transaction. + // + DataSize = sizeof(SMM_SPI_DEV_READ_WRITE_ERASE_HEADER); + + // + // Create the communication buffer. + // + Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SpiDevEraseHeader, DataSize, SPI_DEV_FUNCTION_ERASE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Fill in communication buffer parameters. + // + SpiDevEraseHeader->Offset = SpiOffset; + SpiDevEraseHeader->Size = Size; + + // + // Communicate request to SMM driver and fill in return values. + // + Status = SendCommBuffer (SmmCommunicateHeader, DataSize); + + return Status; +} + +EFI_STATUS +EFIAPI +SpiLock ( + IN UINTN SpiOffset, + IN OUT UINTN Size, + IN BOOLEAN Lock + ) +{ + EFI_STATUS Status; + UINTN DataSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_SPI_DEV_LOCK_HEADER *SmmSpiDevLockHeader; + + // + // Compute data size required for the transaction. + // + DataSize = sizeof(SMM_SPI_DEV_LOCK_HEADER); + + // + // Create the communication buffer. + // + Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SmmSpiDevLockHeader, DataSize, SPI_DEV_FUNCTION_LOCK); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Fill in communication buffer parameters. + // + SmmSpiDevLockHeader->Offset = SpiOffset; + SmmSpiDevLockHeader->Size = Size; + SmmSpiDevLockHeader->Lock = Lock; + + // + // Communicate request to SMM driver and fill in return values. + // + Status = SendCommBuffer (SmmCommunicateHeader, DataSize); + + return Status; +} + +EFI_STATUS +EFIAPI +SpiSetRange ( + IN UINTN SpiOffset, + IN UINTN Size, + IN BOOLEAN ReadLock, + IN BOOLEAN WriteLock + ) +{ + EFI_STATUS Status; + UINTN DataSize; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + SMM_SPI_DEV_SET_RANGE_HEADER *SmmSpiDevSetRangeHeader; + + // + // Compute data size required for the transaction. + // + DataSize = sizeof(SMM_SPI_DEV_SET_RANGE_HEADER); + + // + // Create the communication buffer. + // + Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SmmSpiDevSetRangeHeader, DataSize, SPI_DEV_FUNCTION_SET_RANGE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Fill in communication buffer parameters. + // + SmmSpiDevSetRangeHeader->Offset = SpiOffset; + SmmSpiDevSetRangeHeader->Size = Size; + SmmSpiDevSetRangeHeader->ReadLock = ReadLock; + SmmSpiDevSetRangeHeader->WriteLock = WriteLock; + + // + // Communicate request to SMM driver and fill in return values. + // + Status = SendCommBuffer (SmmCommunicateHeader, DataSize); + + return Status; +} + +EFI_STATUS +EFIAPI +SpiLockRanges ( + ) +{ + EFI_STATUS Status; + EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; + + // + // Create the communication buffer. + // + Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, NULL, 0, SPI_DEV_FUNCTION_LOCK_RANGES); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Communicate request to SMM driver and fill in return values. + // + Status = SendCommBuffer (SmmCommunicateHeader, 0); + + return Status; +} + +EFI_STATUS +EFIAPI +InitSpiDevice ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *SmmSpiDeviceReg; + + // + // Register for a callback when the SMM version of the SPI Device protocol + // is installed. + // + EfiCreateProtocolNotifyEvent ( + &gSmmSpiDeviceProtocolGuid, + TPL_CALLBACK, + SmmSpiDeviceReady, + NULL, + &SmmSpiDeviceReg + ); + + return EFI_SUCCESS; +} + +VOID +EFIAPI +SmmSpiDeviceReady ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_HANDLE Handle; + SPI_DEVICE_PROTOCOL *SmmSpiDevice; + EFI_STATUS Status; + + // + // Locate the protocol first just to make sure it was actually installed. + // + Status = gBS->LocateProtocol ( + &gSmmSpiDeviceProtocolGuid, + NULL, + (VOID **) &SmmSpiDevice + ); + if (EFI_ERROR (Status)) { + return; + } + + // + // SMM Service installed so get communication link to SMM + // + Status = gBS->LocateProtocol ( + &gEfiSmmCommunicationProtocolGuid, + NULL, + (VOID **) &mSmmComm + ); + ASSERT_EFI_ERROR (Status); + + // + // Install DXE protocol so it can be used by drivers. + // + Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Handle, + &gSpiDeviceProtocolGuid, + EFI_NATIVE_INTERFACE, + &mSpiDevProtocol + ); + ASSERT_EFI_ERROR (Status); +} diff --git a/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmDxe.inf b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmDxe.inf new file mode 100644 index 0000000000..d7effc6668 --- /dev/null +++ b/BraswellPlatformPkg/Flash/SpiDeviceDxe/SpiDeviceSmmDxe.inf @@ -0,0 +1,51 @@ +## @file +# SMM Based SPI Device Dxe Driver +# +# Adds platform support to configure the SPI controller with the correct values +# to be used when using software sequencing. This driver installs gSpiDeviceProtocolGuid +# protocol based on SMM based SPI device driver. +# +# Copyright (c) 2015, Intel Corporation. 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. +# +## + +[Defines] + INF_VERSION = 0x00010018 + BASE_NAME = SpiDeviceSmmDxe + FILE_GUID = D7AC2008-CFBE-44A4-AD92-573F1AB9DF45 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitSpiDevice + +[Sources] + SpiDeviceSmmDxe.c + SpiDevice.h + +[Packages] + ChvRefCodePkg/ChvRefCodePkg.dec + BraswellPlatformPkg/BraswellPlatformPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + DebugLib + DxeServicesTableLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gSpiDeviceProtocolGuid ## PRODUCES + gSmmSpiDeviceProtocolGuid ## CONSUMES + gEfiSmmCommunicationProtocolGuid ## CONSUMES + +[Depex] + gEfiSmmCommunicationProtocolGuid + -- cgit v1.2.3