/** @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; }