Brit Chesley efc7ccf906 MdeModulePkg/Bus/Spi/SpiBus: Adding SpiBus Drivers
Added SpiBus DXE and SMM drivers. This code translates SPI requests from
the application layer into SPI Bus transactions on the SPI host
controller. The code is responsible for checking if the transaction is
valid, then setting up the SPI clock and chip select properly before
passing the bus transaction to the host controller.

Platform Initialization Spec 1.7 volume 5 section 18.1.6

Bugzilla #4753

Cc: Abner Chang <abner.chang@amd.com>
Cc: Abdul Lateef Attar <AbdulLateef.Attar@amd.com>
Signed-off-by: Brit Chesley <brit.chesley@amd.com>
Reviewed-by: Abner Chang <abner.chang@amd.com>
2024-05-08 04:43:58 +00:00

199 lines
7.1 KiB
C

/** @file
SPI bus DXE driver
Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Base.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/SpiConfiguration.h>
#include <Protocol/SpiHc.h>
#include <Protocol/SpiIo.h>
#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;
}