/** @file
Clock generator setting for multiplatform.
Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#define CLKGEN_EN 1
#define EFI_DEBUG 1
CLOCK_GENERATOR_DETAILS mSupportedClockGeneratorTable[] =
{
{ ClockGeneratorCk410, CK410_GENERATOR_ID , CK410_GENERATOR_SPREAD_SPECTRUM_BYTE, CK410_GENERATOR_SPREAD_SPECTRUM_BIT },
{ ClockGeneratorCk505, CK505_GENERATOR_ID , CK505_GENERATOR_SPREAD_SPECTRUM_BYTE, CK505_GENERATOR_SPREAD_SPECTRUM_BIT }
};
/**
Configure the clock generator using the SMBUS PPI services.
This function performs a block write, and dumps debug information.
@param PeiServices General purpose services available to every PEIM.
@param ClockType Clock generator's model name.
@param ClockAddress SMBUS address of clock generator.
@param ConfigurationTableLength Length of configuration table.
@param ConfigurationTable Pointer of configuration table.
@retval EFI_SUCCESS - Operation success.
**/
EFI_STATUS
ConfigureClockGenerator (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_SMBUS2_PPI *SmbusPpi,
IN CLOCK_GENERATOR_TYPE ClockType,
IN UINT8 ClockAddress,
IN UINTN ConfigurationTableLength,
IN OUT UINT8 *ConfigurationTable
)
{
EFI_STATUS Status;
EFI_SMBUS_DEVICE_ADDRESS SlaveAddress;
UINT8 Buffer[MAX_CLOCK_GENERATOR_BUFFER_LENGTH];
UINTN Length;
EFI_SMBUS_DEVICE_COMMAND Command;
#if CLKGEN_CONFIG_EXTRA
UINT8 j;
#endif
//
// Verify input arguments
//
ASSERT (ConfigurationTableLength >= 6);
ASSERT (ConfigurationTableLength <= MAX_CLOCK_GENERATOR_BUFFER_LENGTH);
ASSERT (ClockType < ClockGeneratorMax);
ASSERT (ConfigurationTable != NULL);
//
// Read the clock generator
//
SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1;
Length = sizeof (Buffer);
Command = 0;
Status = SmbusPpi->Execute (
SmbusPpi,
SlaveAddress,
Command,
EfiSmbusReadBlock,
FALSE,
&Length,
Buffer
);
ASSERT_EFI_ERROR (Status);
#ifdef EFI_DEBUG
{
UINT8 i;
for (i = 0; i < sizeof (Buffer); i++) {
DEBUG((EFI_D_ERROR, "CK505 default Clock Generator Byte %d: %x\n", i, Buffer[i]));
}
#if CLKGEN_EN
for (i = 0; i < ConfigurationTableLength; i++) {
DEBUG((EFI_D_ERROR, "BIOS structure Clock Generator Byte %d: %x\n", i, ConfigurationTable[i]));
}
#endif
}
#endif
DEBUG((EFI_D_ERROR, "Expected Clock Generator ID is %x, expecting %x\n", mSupportedClockGeneratorTable[ClockType].ClockId,(Buffer[7]&0xF)));
//
// Program clock generator
//
Command = 0;
#if CLKGEN_EN
#if CLKGEN_CONFIG_EXTRA
for (j = 0; j < ConfigurationTableLength; j++) {
Buffer[j] = ConfigurationTable[j];
}
Buffer[30] = 0x00;
Status = SmbusPpi->Execute (
SmbusPpi,
SlaveAddress,
Command,
EfiSmbusWriteBlock,
FALSE,
&Length,
Buffer
);
#else
Status = SmbusPpi->Execute (
SmbusPpi,
SlaveAddress,
Command,
EfiSmbusWriteBlock,
FALSE,
&ConfigurationTableLength,
ConfigurationTable
);
#endif // CLKGEN_CONFIG_EXTRA
#else
ConfigurationTable[4] = (ConfigurationTable[4] & 0x3) | (Buffer[4] & 0xFC);
Command = 4;
Length = 1;
Status = SmbusPpi->Execute (
SmbusPpi,
SlaveAddress,
Command,
EfiSmbusWriteBlock,
FALSE,
&Length,
&ConfigurationTable[4]
);
#endif //CLKGEN_EN
ASSERT_EFI_ERROR (Status);
//
// Dump contents after write
//
#ifdef EFI_DEBUG
{
UINT8 i;
SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1;
Length = sizeof (Buffer);
Command = 0;
Status = SmbusPpi->Execute (
SmbusPpi,
SlaveAddress,
Command,
EfiSmbusReadBlock,
FALSE,
&Length,
Buffer
);
for (i = 0; i < ConfigurationTableLength; i++) {
DEBUG((EFI_D_ERROR, "Clock Generator Byte %d: %x\n", i, Buffer[i]));
}
}
#endif
return EFI_SUCCESS;
}
/**
Configure the clock generator using the SMBUS PPI services.
This function performs a block write, and dumps debug information.
@param PeiServices General purpose services available to every PEIM.
@param ClockType Clock generator's model name.
@param ClockAddress SMBUS address of clock generator.
@param ConfigurationTableLength Length of configuration table.
@param ConfigurationTable Pointer of configuration table.
@retval EFI_SUCCESS Operation success.
**/
UINT8
ReadClockGeneratorID (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_SMBUS2_PPI *SmbusPpi,
IN UINT8 ClockAddress
)
{
EFI_SMBUS_DEVICE_ADDRESS SlaveAddress;
UINT8 Buffer[MAX_CLOCK_GENERATOR_BUFFER_LENGTH];
UINTN Length;
EFI_SMBUS_DEVICE_COMMAND Command;
//
// Read the clock generator
//
SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1;
Length = sizeof (Buffer);
Command = 0;
SmbusPpi->Execute (
SmbusPpi,
SlaveAddress,
Command,
EfiSmbusReadBlock,
FALSE,
&Length,
Buffer
);
//
// Sanity check that the requested clock type is present in our supported clocks table
//
DEBUG((EFI_D_ERROR, "Expected Clock Generator ID is 0x%x\n", Buffer[7]));
return (Buffer[7]);
}
/**
Configure the clock generator to enable free-running operation. This keeps
the clocks from being stopped when the system enters C3 or C4.
@param None
@retval EFI_SUCCESS The function completed successfully.
**/
EFI_STATUS
ConfigurePlatformClocks (
IN EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
IN VOID *SmbusPpi
)
{
//
// Comment it out for now
// Not supported by Hybrid model.
//
EFI_STATUS Status;
UINT8 *ConfigurationTable;
CLOCK_GENERATOR_TYPE ClockType = ClockGeneratorCk505;
UINT8 ConfigurationTable_Desktop[] = CLOCK_GENERATOR_SETTINGS_DESKTOP;
UINT8 ConfigurationTable_Mobile[] = CLOCK_GENERATOR_SETTINGS_MOBILE;
UINT8 ConfigurationTable_Tablet[] = CLOCK_GENERATOR_SEETINGS_TABLET;
EFI_PLATFORM_INFO_HOB *PlatformInfoHob;
BOOLEAN EnableSpreadSpectrum;
SYSTEM_CONFIGURATION SystemConfiguration;
UINTN Length;
EFI_SMBUS_DEVICE_COMMAND Command;
EFI_SMBUS_DEVICE_ADDRESS SlaveAddress;
UINT8 Data;
UINT8 ClockAddress = CLOCK_GENERATOR_ADDRESS;
UINTN VariableSize;
EFI_PEI_READ_ONLY_VARIABLE2_PPI *Variable;
//
// Obtain Platform Info from HOB.
//
Status = GetPlatformInfoHob ((CONST EFI_PEI_SERVICES **) PeiServices, &PlatformInfoHob);
ASSERT_EFI_ERROR (Status);
DEBUG((EFI_D_ERROR, "PlatformInfo protocol is working in ConfigurePlatformClocks()...%x\n",PlatformInfoHob->PlatformFlavor));
//
// Locate SMBUS PPI
//
Status = (**PeiServices).LocatePpi (
(CONST EFI_PEI_SERVICES **) PeiServices,
&gEfiPeiSmbus2PpiGuid,
0,
NULL,
&SmbusPpi
);
ASSERT_EFI_ERROR (Status);
Data = 0;
SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1;
Length = 1;
Command = 0x87; //Control Register 7 Vendor ID Check
Status = ((EFI_PEI_SMBUS2_PPI *) SmbusPpi)->Execute (
SmbusPpi,
SlaveAddress,
Command,
EfiSmbusReadByte,
FALSE,
&Length,
&Data
);
if (EFI_ERROR (Status) || ((Data & 0x0F) != CK505_GENERATOR_ID)) {
DEBUG((EFI_D_ERROR, "Clock Generator CK505 Not Present, vendor ID on board is %x\n",(Data & 0x0F)));
return EFI_SUCCESS;
}
EnableSpreadSpectrum = FALSE;
VariableSize = sizeof (SYSTEM_CONFIGURATION);
ZeroMem (&SystemConfiguration, sizeof (SYSTEM_CONFIGURATION));
Status = (*PeiServices)->LocatePpi (
(CONST EFI_PEI_SERVICES **) PeiServices,
&gEfiPeiReadOnlyVariable2PpiGuid,
0,
NULL,
(VOID **) &Variable
);
//
// Use normal setup default from NVRAM variable,
// the Platform Mode (manufacturing/safe/normal) is handle in PeiGetVariable.
//
VariableSize = sizeof(SYSTEM_CONFIGURATION);
Status = Variable->GetVariable (Variable,
L"Setup",
&gEfiSetupVariableGuid,
NULL,
&VariableSize,
&SystemConfiguration);
if (EFI_ERROR (Status) || VariableSize != sizeof(SYSTEM_CONFIGURATION)) {
//The setup variable is corrupted
VariableSize = sizeof(SYSTEM_CONFIGURATION);
Status = Variable->GetVariable(Variable,
L"SetupRecovery",
&gEfiSetupVariableGuid,
NULL,
&VariableSize,
&SystemConfiguration
);
ASSERT_EFI_ERROR (Status);
}
if(!EFI_ERROR (Status)){
EnableSpreadSpectrum = SystemConfiguration.EnableClockSpreadSpec;
}
//
// Perform platform-specific intialization dependent upon Board ID:
//
DEBUG((EFI_D_ERROR, "board id is %x, platform id is %x\n",PlatformInfoHob->BoardId,PlatformInfoHob->PlatformFlavor));
switch (PlatformInfoHob->BoardId) {
case BOARD_ID_MINNOW2:
case BOARD_ID_MINNOW2_TURBOT:
default:
switch(PlatformInfoHob->PlatformFlavor) {
case FlavorTablet:
ConfigurationTable = ConfigurationTable_Tablet;
Length = sizeof (ConfigurationTable_Tablet);
break;
case FlavorMobile:
ConfigurationTable = ConfigurationTable_Mobile;
Length = sizeof (ConfigurationTable_Mobile);
break;
case FlavorDesktop:
default:
ConfigurationTable = ConfigurationTable_Desktop;
Length = sizeof (ConfigurationTable_Desktop);
break;
}
break;
}
//
// Perform common clock initialization:
//
// Program Spread Spectrum function.
//
if (EnableSpreadSpectrum)
{
ConfigurationTable[mSupportedClockGeneratorTable[ClockType].SpreadSpectrumByteOffset] |= mSupportedClockGeneratorTable[ClockType].SpreadSpectrumBitOffset;
} else {
ConfigurationTable[mSupportedClockGeneratorTable[ClockType].SpreadSpectrumByteOffset] &= ~(mSupportedClockGeneratorTable[ClockType].SpreadSpectrumBitOffset);
}
#if CLKGEN_EN
Status = ConfigureClockGenerator (PeiServices, SmbusPpi, ClockType, ClockAddress, Length, ConfigurationTable);
ASSERT_EFI_ERROR (Status);
#endif // CLKGEN_EN
return EFI_SUCCESS;
}
static EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList[] = {
{
EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
&gEfiPeiSmbus2PpiGuid,
ConfigurePlatformClocks
}
};
EFI_STATUS
InstallPlatformClocksNotify (
IN CONST EFI_PEI_SERVICES **PeiServices
)
{
EFI_STATUS Status;
DEBUG ((EFI_D_INFO, "InstallPlatformClocksNotify()...\n"));
Status = (*PeiServices)->NotifyPpi(PeiServices, &mNotifyList[0]);
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}