summaryrefslogtreecommitdiffstats
path: root/MdeModulePkg/Bus/Spi/SpiBus/SpiBus.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Bus/Spi/SpiBus/SpiBus.c')
-rw-r--r--MdeModulePkg/Bus/Spi/SpiBus/SpiBus.c433
1 files changed, 433 insertions, 0 deletions
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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Protocol/SpiConfiguration.h>
+#include <Protocol/SpiHc.h>
+#include <Protocol/SpiIo.h>
+#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;
+}