diff --git a/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.c b/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.c new file mode 100644 index 0000000000..b183ca182c --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.c @@ -0,0 +1,433 @@ +/** @file + + SpiBus driver + + Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include +#include +#include +#include +#include "SpiBus.h" + +/** + Checks if two device paths are the same. + + @param[in] DevicePath1 First device path to compare + @param[in] DevicePath2 Second device path to compare + + @retval TRUE The device paths share the same nodes and values + @retval FALSE The device paths differ +**/ +BOOLEAN +EFIAPI +DevicePathsAreEqual ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath1, + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath2 + ) +{ + UINTN Size1; + UINTN Size2; + + Size1 = GetDevicePathSize (DevicePath1); + Size2 = GetDevicePathSize (DevicePath2); + + if (Size1 != Size2) { + return FALSE; + } + + if (CompareMem (DevicePath1, DevicePath2, Size1) != 0) { + return FALSE; + } + + return TRUE; +} + +/** + Calls the SpiPeripherals ChipSelect if it is not null, otherwise + calls the Host Controllers ChipSelect function. + + @param[in] SpiChip The SpiChip to place on the bus via asserting its chip select + @param[in] PinValue Value to place on the chip select pin + + @retval EFI_SUCCESS Chip select pin was placed at requested level + @retval EFI_INVALID_PARAMETER Invalid parameters passed into ChipSelect function +**/ +EFI_STATUS +EFIAPI +SpiChipSelect ( + IN CONST SPI_IO_CHIP *SpiChip, + IN BOOLEAN PinValue + ) +{ + EFI_STATUS Status; + + // Check which chip select function to use + if (SpiChip->Protocol.SpiPeripheral->ChipSelect != NULL) { + Status = SpiChip->Protocol.SpiPeripheral->ChipSelect ( + SpiChip->BusTransaction.SpiPeripheral, + PinValue + ); + } else { + Status = SpiChip->SpiHc->ChipSelect ( + SpiChip->SpiHc, + SpiChip->BusTransaction.SpiPeripheral, + PinValue + ); + } + + return Status; +} + +/** + Checks the SpiChip's BusTransaction attributes to ensure its a valid SPI transaction. + + @param[in] SpiChip The SpiChip where a bus transaction is requested + + @retval EFI_SUCCESS This is a valid SPI bus transaction + @retval EFI_BAD_BUFFER_SIZE The WriteBytes value was invalid + @retval EFI_BAD_BUFFER_SIZE The ReadBytes value was invalid + @retval EFI_INVALID_PARAMETER TransactionType is not valid, + or BusWidth not supported by SPI peripheral or + SPI host controller, + or WriteBytes non-zero and WriteBuffer is + NULL, + or ReadBytes non-zero and ReadBuffer is NULL, + or ReadBuffer != WriteBuffer for full-duplex + type, + or WriteBuffer was NULL, + or TPL is too high + @retval EFI_OUT_OF_RESOURCES Insufficient memory for SPI transaction + @retval EFI_UNSUPPORTED The FrameSize is not supported by the SPI bus + layer or the SPI host controller + @retval EFI_UNSUPPORTED The SPI controller was not able to support +**/ +EFI_STATUS +EFIAPI +IsValidSpiTransaction ( + IN SPI_IO_CHIP *SpiChip + ) +{ + // Error checking + if (SpiChip->BusTransaction.TransactionType > SPI_TRANSACTION_WRITE_THEN_READ) { + return EFI_INVALID_PARAMETER; + } + + if (((SpiChip->BusTransaction.BusWidth != 1) && (SpiChip->BusTransaction.BusWidth != 2) && (SpiChip->BusTransaction.BusWidth != 4) && + (SpiChip->BusTransaction.BusWidth != 8)) || (SpiChip->BusTransaction.FrameSize == 0)) + { + return EFI_INVALID_PARAMETER; + } + + if ((SpiChip->BusTransaction.BusWidth == 8) && (((SpiChip->Protocol.Attributes & SPI_IO_SUPPORTS_8_BIT_DATA_BUS_WIDTH) != SPI_IO_SUPPORTS_8_BIT_DATA_BUS_WIDTH) || + ((SpiChip->BusTransaction.SpiPeripheral->Attributes & SPI_PART_SUPPORTS_8_BIT_DATA_BUS_WIDTH) != SPI_PART_SUPPORTS_8_BIT_DATA_BUS_WIDTH))) + { + return EFI_INVALID_PARAMETER; + } else if ((SpiChip->BusTransaction.BusWidth == 4) && (((SpiChip->Protocol.Attributes & SPI_IO_SUPPORTS_4_BIT_DATA_BUS_WIDTH) != SPI_IO_SUPPORTS_4_BIT_DATA_BUS_WIDTH) || + ((SpiChip->BusTransaction.SpiPeripheral->Attributes & SPI_PART_SUPPORTS_4_BIT_DATA_BUS_WIDTH) != SPI_PART_SUPPORTS_4_BIT_DATA_BUS_WIDTH))) + { + return EFI_INVALID_PARAMETER; + } else if ((SpiChip->BusTransaction.BusWidth == 2) && (((SpiChip->Protocol.Attributes & SPI_IO_SUPPORTS_4_BIT_DATA_BUS_WIDTH) != SPI_IO_SUPPORTS_4_BIT_DATA_BUS_WIDTH) || + ((SpiChip->BusTransaction.SpiPeripheral->Attributes & SPI_PART_SUPPORTS_2_BIT_DATA_BUS_WIDTH) != SPI_PART_SUPPORTS_2_BIT_DATA_BUS_WIDTH))) + { + return EFI_INVALID_PARAMETER; + } + + if (((SpiChip->BusTransaction.WriteBytes > 0) && (SpiChip->BusTransaction.WriteBuffer == NULL)) || ((SpiChip->BusTransaction.ReadBytes > 0) && (SpiChip->BusTransaction.ReadBuffer == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if ((SpiChip->BusTransaction.TransactionType == SPI_TRANSACTION_FULL_DUPLEX) && (SpiChip->BusTransaction.ReadBytes != SpiChip->BusTransaction.WriteBytes)) { + return EFI_INVALID_PARAMETER; + } + + // Check frame size, passed parameter is in bits + if ((SpiChip->Protocol.FrameSizeSupportMask & (1<<(SpiChip->BusTransaction.FrameSize-1))) == 0) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Initiate a SPI transaction between the host and a SPI peripheral. + + This routine must be called at or below TPL_NOTIFY. + This routine works with the SPI bus layer to pass the SPI transaction to the + SPI controller for execution on the SPI bus. There are four types of + supported transactions supported by this routine: + * Full Duplex: WriteBuffer and ReadBuffer are the same size. + * Write Only: WriteBuffer contains data for SPI peripheral, ReadBytes = 0 + * Read Only: ReadBuffer to receive data from SPI peripheral, WriteBytes = 0 + * Write Then Read: WriteBuffer contains control data to write to SPI + peripheral before data is placed into the ReadBuffer. + Both WriteBytes and ReadBytes must be non-zero. + + @param[in] This Pointer to an EFI_SPI_IO_PROTOCOL structure. + @param[in] TransactionType Type of SPI transaction. + @param[in] DebugTransaction Set TRUE only when debugging is desired. + Debugging may be turned on for a single SPI + transaction. Only this transaction will display + debugging messages. All other transactions with + this value set to FALSE will not display any + debugging messages. + @param[in] ClockHz Specify the ClockHz value as zero (0) to use + the maximum clock frequency supported by the + SPI controller and part. Specify a non-zero + value only when a specific SPI transaction + requires a reduced clock rate. + @param[in] BusWidth Width of the SPI bus in bits: 1, 2, 4 + @param[in] FrameSize Frame size in bits, range: 1 - 32 + @param[in] WriteBytes The length of the WriteBuffer in bytes. + Specify zero for read-only operations. + @param[in] WriteBuffer The buffer containing data to be sent from the + host to the SPI chip. Specify NULL for read + only operations. + * Frame sizes 1-8 bits: UINT8 (one byte) per + frame + * Frame sizes 7-16 bits: UINT16 (two bytes) per + frame + * Frame sizes 17-32 bits: UINT32 (four bytes) + per frame The transmit frame is in the least + significant N bits. + @param[in] ReadBytes The length of the ReadBuffer in bytes. + Specify zero for write-only operations. + @param[out] ReadBuffer The buffer to receeive data from the SPI chip + during the transaction. Specify NULL for write + only operations. + * Frame sizes 1-8 bits: UINT8 (one byte) per + frame + * Frame sizes 7-16 bits: UINT16 (two bytes) per + frame + * Frame sizes 17-32 bits: UINT32 (four bytes) + per frame The received frame is in the least + significant N bits. + + @retval EFI_SUCCESS The SPI transaction completed successfully + @retval EFI_BAD_BUFFER_SIZE The WriteBytes value was invalid + @retval EFI_BAD_BUFFER_SIZE The ReadBytes value was invalid + @retval EFI_INVALID_PARAMETER TransactionType is not valid, + or BusWidth not supported by SPI peripheral or + SPI host controller, + or WriteBytes non-zero and WriteBuffer is + NULL, + or ReadBytes non-zero and ReadBuffer is NULL, + or ReadBuffer != WriteBuffer for full-duplex + type, + or WriteBuffer was NULL, + or TPL is too high + @retval EFI_OUT_OF_RESOURCES Insufficient memory for SPI transaction + @retval EFI_UNSUPPORTED The FrameSize is not supported by the SPI bus + layer or the SPI host controller + @retval EFI_UNSUPPORTED The SPI controller was not able to support + +**/ +EFI_STATUS +EFIAPI +Transaction ( + IN CONST EFI_SPI_IO_PROTOCOL *This, + IN EFI_SPI_TRANSACTION_TYPE TransactionType, + IN BOOLEAN DebugTransaction, + IN UINT32 ClockHz OPTIONAL, + IN UINT32 BusWidth, + IN UINT32 FrameSize, + IN UINT32 WriteBytes, + IN UINT8 *WriteBuffer, + IN UINT32 ReadBytes, + OUT UINT8 *ReadBuffer + ) +{ + EFI_STATUS Status; + SPI_IO_CHIP *SpiChip; + UINT32 MaxClockHz; + UINT8 *DummyReadBuffer; + UINT8 *DummyWriteBuffer; + + SpiChip = SPI_IO_CHIP_FROM_THIS (This); + SpiChip->BusTransaction.SpiPeripheral = + (EFI_SPI_PERIPHERAL *)SpiChip->Protocol.SpiPeripheral; + SpiChip->BusTransaction.TransactionType = TransactionType; + SpiChip->BusTransaction.DebugTransaction = DebugTransaction; + SpiChip->BusTransaction.BusWidth = BusWidth; + SpiChip->BusTransaction.FrameSize = FrameSize; + SpiChip->BusTransaction.WriteBytes = WriteBytes; + SpiChip->BusTransaction.WriteBuffer = WriteBuffer; + SpiChip->BusTransaction.ReadBytes = ReadBytes; + SpiChip->BusTransaction.ReadBuffer = ReadBuffer; + + // Ensure valid spi transaction parameters + Status = IsValidSpiTransaction (SpiChip); + if (EFI_ERROR (Status)) { + return Status; + } + + // Setup the proper clock frequency + if (SpiChip->BusTransaction.SpiPeripheral->MaxClockHz != 0) { + MaxClockHz = SpiChip->BusTransaction.SpiPeripheral->MaxClockHz; + } else { + MaxClockHz = SpiChip->BusTransaction.SpiPeripheral->SpiPart->MaxClockHz; + } + + // Call proper clock function + if (SpiChip->Protocol.SpiPeripheral->SpiBus->Clock != NULL) { + Status = SpiChip->Protocol.SpiPeripheral->SpiBus->Clock ( + SpiChip->BusTransaction.SpiPeripheral, + &MaxClockHz + ); + } else { + Status = SpiChip->SpiHc->Clock ( + SpiChip->SpiHc, + SpiChip->BusTransaction.SpiPeripheral, + &MaxClockHz + ); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SpiChipSelect (SpiChip, SpiChip->BusTransaction.SpiPeripheral->SpiPart->ChipSelectPolarity); + + if (EFI_ERROR (Status)) { + return Status; + } + + // Check transaction types and match to HC capabilities + if ((TransactionType == SPI_TRANSACTION_WRITE_ONLY) && + ((SpiChip->SpiHc->Attributes & HC_SUPPORTS_WRITE_ONLY_OPERATIONS) != HC_SUPPORTS_WRITE_ONLY_OPERATIONS)) + { + // Convert to full duplex transaction + SpiChip->BusTransaction.ReadBytes = SpiChip->BusTransaction.WriteBytes; + SpiChip->BusTransaction.ReadBuffer = AllocateZeroPool (SpiChip->BusTransaction.ReadBytes); + + Status = SpiChip->SpiHc->Transaction ( + SpiChip->SpiHc, + &SpiChip->BusTransaction + ); + + SpiChip->BusTransaction.ReadBytes = ReadBytes; // assign to passed parameter + FreePool (SpiChip->BusTransaction.ReadBuffer); // Free temporary buffer + } else if ((TransactionType == SPI_TRANSACTION_READ_ONLY) && + ((SpiChip->SpiHc->Attributes & HC_SUPPORTS_READ_ONLY_OPERATIONS) != HC_SUPPORTS_READ_ONLY_OPERATIONS)) + { + // Convert to full duplex transaction + SpiChip->BusTransaction.WriteBytes = SpiChip->BusTransaction.WriteBytes; + SpiChip->BusTransaction.WriteBuffer = AllocateZeroPool (SpiChip->BusTransaction.WriteBytes); + + Status = SpiChip->SpiHc->Transaction ( + SpiChip->SpiHc, + &SpiChip->BusTransaction + ); + + SpiChip->BusTransaction.WriteBytes = WriteBytes; + FreePool (SpiChip->BusTransaction.WriteBuffer); + } else if ((TransactionType == SPI_TRANSACTION_WRITE_THEN_READ) && + ((SpiChip->SpiHc->Attributes & HC_SUPPORTS_WRITE_THEN_READ_OPERATIONS) != HC_SUPPORTS_WRITE_THEN_READ_OPERATIONS)) + { + // Convert to full duplex transaction + DummyReadBuffer = AllocateZeroPool (WriteBytes); + DummyWriteBuffer = AllocateZeroPool (ReadBytes); + SpiChip->BusTransaction.ReadBuffer = DummyReadBuffer; + SpiChip->BusTransaction.ReadBytes = WriteBytes; + + Status = SpiChip->SpiHc->Transaction ( + SpiChip->SpiHc, + &SpiChip->BusTransaction + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // Write is done, now need to read, restore passed in read buffer info + SpiChip->BusTransaction.ReadBuffer = ReadBuffer; + SpiChip->BusTransaction.ReadBytes = ReadBytes; + + SpiChip->BusTransaction.WriteBuffer = DummyWriteBuffer; + SpiChip->BusTransaction.WriteBytes = ReadBytes; + + Status = SpiChip->SpiHc->Transaction ( + SpiChip->SpiHc, + &SpiChip->BusTransaction + ); + // Restore write data + SpiChip->BusTransaction.WriteBuffer = WriteBuffer; + SpiChip->BusTransaction.WriteBytes = WriteBytes; + + FreePool (DummyReadBuffer); + FreePool (DummyWriteBuffer); + } else { + // Supported transaction type, just pass info the SPI HC Protocol Transaction + Status = SpiChip->SpiHc->Transaction ( + SpiChip->SpiHc, + &SpiChip->BusTransaction + ); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = SpiChipSelect (SpiChip, !SpiChip->BusTransaction.SpiPeripheral->SpiPart->ChipSelectPolarity); + + return Status; +} + +/** + Update the SPI peripheral associated with this SPI 10 SpiChip. + + Support socketed SPI parts by allowing the SPI peripheral driver to replace + the SPI peripheral after the connection is made. An example use is socketed + SPI NOR flash parts, where the size and parameters change depending upon + device is in the socket. + + @param[in] This Pointer to an EFI_SPI_IO_PROTOCOL structure. + @param[in] SpiPeripheral Pointer to an EFI_SPI_PERIPHERAL structure. + + @retval EFI_SUCCESS The SPI peripheral was updated successfully + @retval EFI_INVALID_PARAMETER The SpiPeripheral value is NULL, + or the SpiPeripheral->SpiBus is NULL, + or the SpiPeripheral->SpiBus pointing at + wrong bus, or the SpiPeripheral->SpiPart is NULL +**/ +EFI_STATUS +EFIAPI +UpdateSpiPeripheral ( + IN CONST EFI_SPI_IO_PROTOCOL *This, + IN CONST EFI_SPI_PERIPHERAL *SpiPeripheral + ) +{ + EFI_STATUS Status; + SPI_IO_CHIP *SpiChip; + + DEBUG ((DEBUG_VERBOSE, "%a: SPI Bus - Entry\n", __func__)); + + SpiChip = SPI_IO_CHIP_FROM_THIS (This); + + if ((SpiPeripheral == NULL) || (SpiPeripheral->SpiBus == NULL) || + (SpiPeripheral->SpiPart == NULL)) + { + return EFI_INVALID_PARAMETER; + } + + // EFI_INVALID_PARAMETER if SpiPeripheral->SpiBus is pointing at wrong bus + if (!DevicePathsAreEqual (SpiPeripheral->SpiBus->ControllerPath, SpiChip->SpiBus->ControllerPath)) { + return EFI_INVALID_PARAMETER; + } + + SpiChip->Protocol.OriginalSpiPeripheral = SpiChip->Protocol.SpiPeripheral; + SpiChip->Protocol.SpiPeripheral = SpiPeripheral; + + Status = EFI_SUCCESS; + DEBUG (( + DEBUG_VERBOSE, + "%a: SPI Bus - Exit Status=%r\n", + __func__, + Status + )); + return Status; +} diff --git a/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.h b/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.h new file mode 100644 index 0000000000..7a43f66ac7 --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.h @@ -0,0 +1,167 @@ +/** @file + + SPI bus driver + + Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef SPI_BUS_H_ +#define SPI_BUS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPI_IO_SIGNATURE SIGNATURE_32 ('s', 'i', 'o', 'c') + +typedef struct { + UINTN Signature; + EFI_HANDLE Handle; + EFI_SPI_IO_PROTOCOL Protocol; + EFI_SPI_BUS_TRANSACTION BusTransaction; + EFI_SPI_CONFIGURATION_PROTOCOL *SpiConfig; + EFI_SPI_HC_PROTOCOL *SpiHc; + EFI_SPI_BUS *SpiBus; +} SPI_IO_CHIP; + +#define SPI_IO_CHIP_FROM_THIS(a) \ + CR (a, SPI_IO_CHIP, Protocol, \ + SPI_IO_SIGNATURE) + +/** + Checks if two device paths are the same + + @param[in] DevicePath1 First device path to compare + @param[in] DevicePath2 Second device path to compare + + @retval TRUE The device paths share the same nodes and values + @retval FALSE The device paths differ +**/ +BOOLEAN +EFIAPI +DevicePathsAreEqual ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath1, + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath2 + ); + +/** + Initiate a SPI transaction between the host and a SPI peripheral. + + This routine must be called at or below TPL_NOTIFY. + This routine works with the SPI bus layer to pass the SPI transaction to the + SPI controller for execution on the SPI bus. There are four types of + supported transactions supported by this routine: + * Full Duplex: WriteBuffer and ReadBuffer are the same size. + * Write Only: WriteBuffer contains data for SPI peripheral, ReadBytes = 0 + * Read Only: ReadBuffer to receive data from SPI peripheral, WriteBytes = 0 + * Write Then Read: WriteBuffer contains control data to write to SPI + peripheral before data is placed into the ReadBuffer. + Both WriteBytes and ReadBytes must be non-zero. + + @param[in] This Pointer to an EFI_SPI_IO_PROTOCOL structure. + @param[in] TransactionType Type of SPI transaction. + @param[in] DebugTransaction Set TRUE only when debugging is desired. + Debugging may be turned on for a single SPI + transaction. Only this transaction will display + debugging messages. All other transactions with + this value set to FALSE will not display any + debugging messages. + @param[in] ClockHz Specify the ClockHz value as zero (0) to use + the maximum clock frequency supported by the + SPI controller and part. Specify a non-zero + value only when a specific SPI transaction + requires a reduced clock rate. + @param[in] BusWidth Width of the SPI bus in bits: 1, 2, 4 + @param[in] FrameSize Frame size in bits, range: 1 - 32 + @param[in] WriteBytes The length of the WriteBuffer in bytes. + Specify zero for read-only operations. + @param[in] WriteBuffer The buffer containing data to be sent from the + host to the SPI chip. Specify NULL for read + only operations. + * Frame sizes 1-8 bits: UINT8 (one byte) per + frame + * Frame sizes 7-16 bits: UINT16 (two bytes) per + frame + * Frame sizes 17-32 bits: UINT32 (four bytes) + per frame The transmit frame is in the least + significant N bits. + @param[in] ReadBytes The length of the ReadBuffer in bytes. + Specify zero for write-only operations. + @param[out] ReadBuffer The buffer to receeive data from the SPI chip + during the transaction. Specify NULL for write + only operations. + * Frame sizes 1-8 bits: UINT8 (one byte) per + frame + * Frame sizes 7-16 bits: UINT16 (two bytes) per + frame + * Frame sizes 17-32 bits: UINT32 (four bytes) + per frame The received frame is in the least + significant N bits. + + @retval EFI_SUCCESS The SPI transaction completed successfully + @retval EFI_BAD_BUFFER_SIZE The writeBytes value was invalid + @retval EFI_BAD_BUFFER_SIZE The ReadBytes value was invalid + @retval EFI_INVALID_PARAMETER TransactionType is not valid, + or BusWidth not supported by SPI peripheral or + SPI host controller, + or WriteBytes non-zero and WriteBuffer is + NULL, + or ReadBytes non-zero and ReadBuffer is NULL, + or ReadBuffer != WriteBuffer for full-duplex + type, + or WriteBuffer was NULL, + or TPL is too high + @retval EFI_OUT_OF_RESOURCES Insufficient memory for SPI transaction + @retval EFI_UNSUPPORTED The FrameSize is not supported by the SPI bus + layer or the SPI host controller + @retval EFI_UNSUPPORTED The SPI controller was not able to support + +**/ +EFI_STATUS +EFIAPI +Transaction ( + IN CONST EFI_SPI_IO_PROTOCOL *This, + IN EFI_SPI_TRANSACTION_TYPE TransactionType, + IN BOOLEAN DebugTransaction, + IN UINT32 ClockHz OPTIONAL, + IN UINT32 BusWidth, + IN UINT32 FrameSize, + IN UINT32 WriteBytes, + IN UINT8 *WriteBuffer, + IN UINT32 ReadBytes, + OUT UINT8 *ReadBuffer + ); + +/** + Update the SPI peripheral associated with this SPI 10 instance. + + Support socketed SPI parts by allowing the SPI peripheral driver to replace + the SPI peripheral after the connection is made. An example use is socketed + SPI NOR flash parts, where the size and parameters change depending upon + device is in the socket. + + @param[in] This Pointer to an EFI_SPI_IO_PROTOCOL structure. + @param[in] SpiPeripheral Pointer to an EFI_SPI_PERIPHERAL structure. + + @retval EFI_SUCCESS The SPI peripheral was updated successfully + @retval EFI_INVALID_PARAMETER The SpiPeripheral value is NULL, + or the SpiPeripheral->SpiBus is NULL, + or the SpiPeripheral->SpiBus pointing at + wrong bus, or the SpiPeripheral->SpiPart is NULL + +**/ +EFI_STATUS +EFIAPI +UpdateSpiPeripheral ( + IN CONST EFI_SPI_IO_PROTOCOL *This, + IN CONST EFI_SPI_PERIPHERAL *SpiPeripheral + ); + +#endif //SPI_BUS_H_ diff --git a/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.uni b/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.uni new file mode 100644 index 0000000000..0d913bdbae --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.uni @@ -0,0 +1,10 @@ +// /** @file +// +// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US "SPI Bus driver" diff --git a/MdeModulePkg/Bus/Spi/SpiBus/SpiBusDxe.c b/MdeModulePkg/Bus/Spi/SpiBus/SpiBusDxe.c new file mode 100644 index 0000000000..cd0a2c99a2 --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiBus/SpiBusDxe.c @@ -0,0 +1,198 @@ +/** @file + + SPI bus DXE driver + + Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "SpiBus.h" + +/** + Entry point of the Spi Bus layer + + @param[in] ImageHandle Image handle of this driver. + @param[in] SystemTable Pointer to standard EFI system table. + + @retval EFI_SUCCESS Succeed. + @retval EFI_DEVICE_ERROR SpiPeripheral is NULL. + @retval EFI_NOT_FOUND Fail to locate SpiHcProtocol or SpiIoConfigurationProtocol + @retval EFI_OUT_OF_RESOURCES Failed to allocate SpiIoChip +**/ +EFI_STATUS +EFIAPI +SpiBusEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + SPI_IO_CHIP *SpiChip; + EFI_SPI_HC_PROTOCOL *SpiHc; + EFI_SPI_CONFIGURATION_PROTOCOL *SpiConfiguration; + EFI_SPI_PERIPHERAL *SpiPeripheral; + EFI_SPI_BUS *Bus; + UINTN BusIndex; + UINTN HcIndex; + EFI_HANDLE *SpiHcHandles; + UINTN HandleCount; + EFI_DEVICE_PATH_PROTOCOL *SpiHcDevicePath; + + DEBUG ((DEBUG_VERBOSE, "%a - ENTRY\n", __func__)); + + // Get all SPI HC protocols, could be multiple SPI HC's on a single platform + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSpiHcProtocolGuid, + NULL, + &HandleCount, + &SpiHcHandles + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "No SpiHcProtocol is found\n")); + Status = EFI_NOT_FOUND; + goto Exit; + } + + // Locate the SPI Configuration Protocol + Status = gBS->LocateProtocol ( + &gEfiSpiConfigurationProtocolGuid, + NULL, + (VOID **)&SpiConfiguration + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "No SpiConfigurationProtocol is found\n")); + Status = EFI_NOT_FOUND; + goto Exit; + } + + // Parse through Hc protocols, find correct device path + for (HcIndex = 0; HcIndex < HandleCount; HcIndex++) { + Status = gBS->HandleProtocol ( + SpiHcHandles[HcIndex], + &gEfiDevicePathProtocolGuid, + (VOID **)&SpiHcDevicePath + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "Error locating EFI device path for this SPI controller, status=%r \n", Status)); + continue; // Continue searching + } + + // Parse through SpiConfiguration's SpiBuses, find matching devicepath for SpiHc + for (BusIndex = 0; BusIndex < SpiConfiguration->BusCount; BusIndex++) { + Bus = (EFI_SPI_BUS *)SpiConfiguration->Buslist[BusIndex]; + if (!DevicePathsAreEqual (SpiHcDevicePath, Bus->ControllerPath)) { + DEBUG ((DEBUG_VERBOSE, "SpiHc and SpiConfig device paths dont match, continue parsing\n")); + continue; + } + + DEBUG (( + DEBUG_VERBOSE, + "%a: Found matching device paths, Enumerating SPI BUS: %s with DevicePath: %s\n", + __func__, + Bus->FriendlyName, + ConvertDevicePathToText (SpiHcDevicePath, FALSE, FALSE) + )); + + // Get SpiHc from the SpiHcHandles + Status = gBS->HandleProtocol ( + SpiHcHandles[HcIndex], + &gEfiDevicePathProtocolGuid, + (VOID **)&SpiHc + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "%a - Error getting SpiHc from Handle\n", __func__)); + goto Exit; + } + + SpiPeripheral = (EFI_SPI_PERIPHERAL *)Bus->Peripherallist; + if (SpiPeripheral != NULL) { + do { + DEBUG (( + DEBUG_VERBOSE, + "%a: Installing SPI IO protocol for %s, by %s, PN=%s\n", + __func__, + SpiPeripheral->FriendlyName, + SpiPeripheral->SpiPart->Vendor, + SpiPeripheral->SpiPart->PartNumber + )); + // Allocate the SPI IO Device + SpiChip = AllocateZeroPool (sizeof (SPI_IO_CHIP)); + ASSERT (SpiChip != NULL); + if (SpiChip != NULL) { + // Fill in the SpiChip + SpiChip->Signature = SPI_IO_SIGNATURE; + SpiChip->SpiConfig = SpiConfiguration; + SpiChip->SpiHc = SpiHc; + SpiChip->SpiBus = Bus; + SpiChip->Protocol.SpiPeripheral = SpiPeripheral; + SpiChip->Protocol.OriginalSpiPeripheral = SpiPeripheral; + SpiChip->Protocol.FrameSizeSupportMask = SpiHc->FrameSizeSupportMask; + SpiChip->Protocol.MaximumTransferBytes = SpiHc->MaximumTransferBytes; + if ((SpiHc->Attributes & HC_TRANSFER_SIZE_INCLUDES_ADDRESS) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_TRANSFER_SIZE_INCLUDES_ADDRESS; + } + + if ((SpiHc->Attributes & HC_TRANSFER_SIZE_INCLUDES_OPCODE) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_TRANSFER_SIZE_INCLUDES_OPCODE; + } + + if ((SpiHc->Attributes & HC_SUPPORTS_8_BIT_DATA_BUS_WIDTH) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_SUPPORTS_8_BIT_DATA_BUS_WIDTH; + } + + if ((SpiHc->Attributes & HC_SUPPORTS_4_BIT_DATA_BUS_WIDTH) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_SUPPORTS_4_BIT_DATA_BUS_WIDTH; + } + + if ((SpiHc->Attributes & HC_SUPPORTS_2_BIT_DATA_BUS_WIDTH) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_SUPPORTS_2_BIT_DATA_BUS_WIDTH; + } + + SpiChip->Protocol.Transaction = Transaction; + SpiChip->Protocol.UpdateSpiPeripheral = UpdateSpiPeripheral; + // Install the SPI IO Protocol + Status = gBS->InstallProtocolInterface ( + &SpiChip->Handle, + (GUID *)SpiPeripheral->SpiPeripheralDriverGuid, + EFI_NATIVE_INTERFACE, + &SpiChip->Protocol + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "%a - Error installing SpiIoProtocol\n", __func__)); + continue; + } + } else { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "%a: Out of Memory resources\n", + __func__ + )); + break; + } + + SpiPeripheral = (EFI_SPI_PERIPHERAL *)SpiPeripheral->NextSpiPeripheral; + } while (SpiPeripheral != NULL); + } else { + Status = EFI_DEVICE_ERROR; + } + } + } + +Exit: + DEBUG ((DEBUG_VERBOSE, "%a - EXIT (Status = %r)\n", __func__, Status)); + return Status; +} diff --git a/MdeModulePkg/Bus/Spi/SpiBus/SpiBusDxe.inf b/MdeModulePkg/Bus/Spi/SpiBus/SpiBusDxe.inf new file mode 100644 index 0000000000..3e2cc2daba --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiBus/SpiBusDxe.inf @@ -0,0 +1,41 @@ +## @file +# Component description for the SPI BUS DXE module +# +# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## +[Defines] + INF_VERSION = 1.27 + BASE_NAME = SpiBusDxe + FILE_GUID = 25CE038C-5C3A-4A9B-A111-90DF5897E058 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 0.1 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = SpiBusEntry + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DebugLib + DevicePathLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Sources] + SpiBusDxe.c + SpiBus.c + SpiBus.h + +[Protocols] + gEfiSpiConfigurationProtocolGuid ## CONSUMES + gEfiSpiHcProtocolGuid ## CONSUMES + +[Depex] + gEfiSpiConfigurationProtocolGuid AND + gEfiSpiHcProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + SpiBus.uni diff --git a/MdeModulePkg/Bus/Spi/SpiBus/SpiBusSmm.c b/MdeModulePkg/Bus/Spi/SpiBus/SpiBusSmm.c new file mode 100644 index 0000000000..d9189b9848 --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiBus/SpiBusSmm.c @@ -0,0 +1,162 @@ +/** @file + + SPI bus SMM driver + + Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "SpiBus.h" + +/** + Entry point of the Spi Bus layer + + @param[in] ImageHandle Image handle of this driver. + @param[in] SystemTable Pointer to standard EFI system table. + + @retval EFI_SUCCESS Succeed. + @retval EFI_DEVICE_ERROR Fail to install EFI_SPI_HC_PROTOCOL protocol. + @retval EFI_NOT_FOUND fail to locate SpiHcProtocol or SpiIoConfigurationProtocol + @retval EFI_OUT_OF_RESOURCES Failed to allocate SpiIoChip +**/ +EFI_STATUS +EFIAPI +SpiBusEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + SPI_IO_CHIP *SpiChip; + EFI_SPI_HC_PROTOCOL *SpiHc; + EFI_SPI_CONFIGURATION_PROTOCOL *SpiConfiguration; + EFI_SPI_PERIPHERAL *SpiPeripheral; + EFI_SPI_BUS *Bus; + + DEBUG ((DEBUG_VERBOSE, "%a - ENTRY\n", __func__)); + + // Only a single Spi HC protocol in SMM + Status = gMmst->MmLocateProtocol ( + &gEfiSpiSmmHcProtocolGuid, + NULL, + (VOID **)&SpiHc + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "No SpiHcProtocol is found\n")); + Status = EFI_NOT_FOUND; + goto Exit; + } + + // Locate the SPI Configuration Protocol + Status = gMmst->MmLocateProtocol ( + &gEfiSpiSmmConfigurationProtocolGuid, + NULL, + (VOID **)&SpiConfiguration + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "No SpiConfigurationProtocol is found\n")); + Status = EFI_NOT_FOUND; + goto Exit; + } + + // Only one SpiBus supported in SMM + if (SpiConfiguration->BusCount != 1) { + DEBUG ((DEBUG_VERBOSE, "Only one SPI Bus supported in SMM\n")); + Status = EFI_UNSUPPORTED; + goto Exit; + } + + Bus = (EFI_SPI_BUS *)SpiConfiguration->Buslist[0]; + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "%a - Error getting SpiHc from Handle\n", __func__)); + goto Exit; + } + + SpiPeripheral = (EFI_SPI_PERIPHERAL *)Bus->Peripherallist; + if (SpiPeripheral != NULL) { + do { + DEBUG (( + DEBUG_VERBOSE, + "%a: Installing SPI IO protocol for %s, by %s, PN=%s\n", + __func__, + SpiPeripheral->FriendlyName, + SpiPeripheral->SpiPart->Vendor, + SpiPeripheral->SpiPart->PartNumber + )); + // Allocate the SPI IO Device + SpiChip = AllocateZeroPool (sizeof (SPI_IO_CHIP)); + ASSERT (SpiChip != NULL); + if (SpiChip != NULL) { + // Fill in the SpiChip + SpiChip->Signature = SPI_IO_SIGNATURE; + SpiChip->SpiConfig = SpiConfiguration; + SpiChip->SpiHc = SpiHc; + SpiChip->SpiBus = Bus; + SpiChip->Protocol.SpiPeripheral = SpiPeripheral; + SpiChip->Protocol.OriginalSpiPeripheral = SpiPeripheral; + SpiChip->Protocol.FrameSizeSupportMask = SpiHc->FrameSizeSupportMask; + SpiChip->Protocol.MaximumTransferBytes = SpiHc->MaximumTransferBytes; + if ((SpiHc->Attributes & HC_TRANSFER_SIZE_INCLUDES_ADDRESS) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_TRANSFER_SIZE_INCLUDES_ADDRESS; + } + + if ((SpiHc->Attributes & HC_TRANSFER_SIZE_INCLUDES_OPCODE) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_TRANSFER_SIZE_INCLUDES_OPCODE; + } + + if ((SpiHc->Attributes & HC_SUPPORTS_8_BIT_DATA_BUS_WIDTH) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_SUPPORTS_8_BIT_DATA_BUS_WIDTH; + } + + if ((SpiHc->Attributes & HC_SUPPORTS_4_BIT_DATA_BUS_WIDTH) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_SUPPORTS_4_BIT_DATA_BUS_WIDTH; + } + + if ((SpiHc->Attributes & HC_SUPPORTS_2_BIT_DATA_BUS_WIDTH) != 0) { + SpiChip->Protocol.Attributes |= SPI_IO_SUPPORTS_2_BIT_DATA_BUS_WIDTH; + } + + SpiChip->Protocol.Transaction = Transaction; + SpiChip->Protocol.UpdateSpiPeripheral = UpdateSpiPeripheral; + // Install the SPI IO Protocol + Status = gMmst->MmInstallProtocolInterface ( + &SpiChip->Handle, + (GUID *)SpiPeripheral->SpiPeripheralDriverGuid, + EFI_NATIVE_INTERFACE, + &SpiChip->Protocol + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_VERBOSE, "%a - Error installing SpiIoProtocol\n", __func__)); + continue; + } + } else { + Status = EFI_OUT_OF_RESOURCES; + DEBUG (( + DEBUG_ERROR, + "%a: Out of Memory resources\n", + __func__ + )); + break; + } + + SpiPeripheral = (EFI_SPI_PERIPHERAL *)SpiPeripheral->NextSpiPeripheral; + } while (SpiPeripheral != NULL); + } else { + Status = EFI_DEVICE_ERROR; + } + +Exit: + DEBUG ((DEBUG_VERBOSE, "%a - EXIT (Status = %r)\n", __func__, Status)); + return Status; +} diff --git a/MdeModulePkg/Bus/Spi/SpiBus/SpiBusSmm.inf b/MdeModulePkg/Bus/Spi/SpiBus/SpiBusSmm.inf new file mode 100644 index 0000000000..9e3a5aae7d --- /dev/null +++ b/MdeModulePkg/Bus/Spi/SpiBus/SpiBusSmm.inf @@ -0,0 +1,41 @@ +## @file +# Component description for the SPI BUS SMM module +# +# Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## +[Defines] + INF_VERSION = 1.27 + BASE_NAME = SpiBusSmm + FILE_GUID = 5DBB52E1-3D78-4C9C-A9D7-A43E79E93AC0 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 0.1 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = SpiBusEntry + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + DebugLib + DevicePathLib + MemoryAllocationLib + MmServicesTableLib + UefiDriverEntryPoint + +[Sources] + SpiBus.h + SpiBus.c + SpiBusSmm.c + +[Protocols] + gEfiSpiSmmConfigurationProtocolGuid ## CONSUMES + gEfiSpiSmmHcProtocolGuid ## CONSUMES + +[Depex] + gEfiSpiSmmConfigurationProtocolGuid AND + gEfiSpiSmmHcProtocolGuid + +[UserExtensions.TianoCore."ExtraFiles"] + SpiBus.uni