mirror of https://github.com/acidanthera/audk.git
434 lines
18 KiB
C
434 lines
18 KiB
C
/** @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;
|
|
}
|