/** @file Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent Module Name: Dimm.c Abstract: PPI for reading SPD modules on DIMMs. --*/ // // Header Files // #include "Platformearlyinit.h" #define DIMM_SOCKETS 4 // Total number of DIMM sockets allowed on // the platform #define DIMM_SEGMENTS 1 // Total number of Segments Per DIMM. #define MEMORY_CHANNELS 2 // Total number of memory channels // populated on the system board // // Prototypes // EFI_STATUS EFIAPI GetDimmState ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_PLATFORM_DIMM_PPI *This, IN UINT8 Dimm, OUT PEI_PLATFORM_DIMM_STATE *State ); EFI_STATUS EFIAPI SetDimmState ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_PLATFORM_DIMM_PPI *This, IN UINT8 Dimm, IN PEI_PLATFORM_DIMM_STATE *State ); EFI_STATUS EFIAPI ReadSpd ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_PLATFORM_DIMM_PPI *This, IN UINT8 Dimm, IN UINT8 Offset, IN UINTN Count, IN OUT UINT8 *Buffer ); static PEI_PLATFORM_DIMM_PPI mGchDimmPpi = { DIMM_SOCKETS, DIMM_SEGMENTS, MEMORY_CHANNELS, GetDimmState, SetDimmState, ReadSpd }; static EFI_PEI_PPI_DESCRIPTOR mPpiPlatformDimm = { (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gPeiPlatformDimmPpiGuid, &mGchDimmPpi }; // // Functions // /** This function returns the current state of a single DIMM. Present indicates that the DIMM slot is physically populated. Disabled indicates that the DIMM should not be used. @param PeiServices PEI services table pointer @param This PPI pointer @param Dimm DIMM to read from @param State Pointer to a return buffer to be updated with the current state of the DIMM @retval EFI_SUCCESS The function completed successfully. **/ EFI_STATUS EFIAPI GetDimmState ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_PLATFORM_DIMM_PPI *This, IN UINT8 Dimm, OUT PEI_PLATFORM_DIMM_STATE *State ) { EFI_STATUS Status; UINT8 Buffer; PEI_ASSERT (PeiServices, (Dimm < This->DimmSockets)); // // A failure here does not necessarily mean that no DIMM is present. // Read a single byte. All we care about is the return status. // Status = ReadSpd ( PeiServices, This, Dimm, 0, 1, &Buffer ); if (EFI_ERROR (Status)) { State->Present = 0; } else { State->Present = 1; } // // BUGBUG: Update to check platform variable when it is available // State->Disabled = 0; State->Reserved = 0; return EFI_SUCCESS; } /** This function updates the state of a single DIMM. @param PeiServices PEI services table pointer @param This PPI pointer @param Dimm DIMM to set state for @param State Pointer to the state information to set. @retval EFI_SUCCESS The function completed successfully. @retval EFI_UNSUPPORTED The function is not supported. **/ EFI_STATUS EFIAPI SetDimmState ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_PLATFORM_DIMM_PPI *This, IN UINT8 Dimm, IN PEI_PLATFORM_DIMM_STATE *State ) { return EFI_UNSUPPORTED; } /** This function reads SPD information from a DIMM. PeiServices PEI services table pointer This PPI pointer Dimm DIMM to read from Offset Offset in DIMM Count Number of bytes Buffer Return buffer @param EFI_SUCCESS The function completed successfully. @param EFI_DEVICE_ERROR The DIMM being accessed reported a device error, does not have an SPD module, or is not installed in the system. @retval EFI_TIMEOUT Time out trying to read the SPD module. @retval EFI_INVALID_PARAMETER A parameter was outside the legal limits. **/ EFI_STATUS EFIAPI ReadSpd ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_PLATFORM_DIMM_PPI *This, IN UINT8 Dimm, IN UINT8 Offset, IN UINTN Count, IN OUT UINT8 *Buffer ) { EFI_STATUS Status; PEI_SMBUS_PPI *Smbus; UINTN Index; UINTN Index1; EFI_SMBUS_DEVICE_ADDRESS SlaveAddress; EFI_SMBUS_DEVICE_COMMAND Command; UINTN Length; Status = (**PeiServices).LocatePpi ( PeiServices, &gPeiSmbusPpiGuid, // GUID 0, // INSTANCE NULL, // EFI_PEI_PPI_DESCRIPTOR &Smbus // PPI ); ASSERT_PEI_ERROR (PeiServices, Status); switch (Dimm) { case 0: SlaveAddress.SmbusDeviceAddress = SMBUS_ADDR_CH_A_1 >> 1; break; case 1: SlaveAddress.SmbusDeviceAddress = SMBUS_ADDR_CH_A_2 >> 1; break; case 2: SlaveAddress.SmbusDeviceAddress = SMBUS_ADDR_CH_B_1 >> 1; break; case 3: SlaveAddress.SmbusDeviceAddress = SMBUS_ADDR_CH_B_2 >> 1; break; default: return EFI_INVALID_PARAMETER; } Index = Count % 4; if (Index != 0) { // // read the first serveral bytes to speed up following reading // for (Index1 = 0; Index1 < Index; Index1++) { Length = 1; Command = Offset + Index1; Status = Smbus->Execute ( PeiServices, Smbus, SlaveAddress, Command, EfiSmbusReadByte, FALSE, &Length, &Buffer[Index1] ); if (EFI_ERROR(Status)) { return Status; } } } // // Now collect all the remaining bytes on 4 bytes block // for (; Index < Count; Index += 2) { Command = Index + Offset; Length = 2; Status = Smbus->Execute ( PeiServices, Smbus, SlaveAddress, Command, EfiSmbusReadWord, FALSE, &Length, &Buffer[Index] ); if (EFI_ERROR(Status)) { return Status; } Index += 2; Command = Index + Offset; Length = 2; Status = Smbus->Execute ( PeiServices, Smbus, SlaveAddress, Command, EfiSmbusReadWord, FALSE, &Length, &Buffer[Index] ); if (EFI_ERROR(Status)) { return Status; } } return EFI_SUCCESS; } /** This function initializes the PEIM. It simply installs the DIMM PPI. @param FfsHeader Not used by this function @param PeiServices Pointer to PEI services table @retval EFI_SUCCESS The function completed successfully. **/ EFI_STATUS EFIAPI PeimInitializeDimm ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, IN VOID *SmbusPpi ) { EFI_STATUS Status; Status = (**PeiServices).InstallPpi ( PeiServices, &mPpiPlatformDimm ); ASSERT_PEI_ERROR (PeiServices, Status); return EFI_SUCCESS; }