/** @file Platform Clocks Lib file @copyright Copyright 1999 - 2021 Intel Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #define MAX_SMBUS_RETRIES 10 EFI_STATUS ConfigureClockGeneratorOnSecondarySmbus ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_SMBUS2_PPI *SmbusPpi, IN UINT8 ClockAddress, IN UINTN ConfigurationTableLength, IN BOOLEAN EnableSpreadSpectrum, IN CLOCK_GENERATOR_DETAILS *mSupportedClockGeneratorT, IN OUT UINT8 *ConfigurationTable ); /** 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 CLOCK_GENERATOR_TYPE ClockType, IN UINT8 ClockAddress, IN UINTN ConfigurationTableLength, IN OUT UINT8 *ConfigurationTable, IN BOOLEAN EnableSpreadSpectrum, IN CLOCK_GENERATOR_DETAILS *mSupportedClockGeneratorT, IN UINT8 SecondarySmbus ) { EFI_STATUS Status; EFI_SMBUS_DEVICE_ADDRESS SlaveAddress; UINT8 Buffer[MAX_CLOCK_GENERATOR_BUFFER_LENGTH]; UINTN Length; EFI_SMBUS_DEVICE_COMMAND Command; EFI_PEI_SMBUS2_PPI *SmbusPpi; UINT8 SmbErrorsCounter; // // Locate SmBus Ppi // Status = (**PeiServices).LocatePpi ( PeiServices, &gEfiPeiSmbus2PpiGuid, 0, NULL, &SmbusPpi ); ASSERT_EFI_ERROR (Status); // // Verify input arguments // ASSERT (ConfigurationTableLength >= 8); ASSERT (ConfigurationTableLength <= MAX_CLOCK_GENERATOR_BUFFER_LENGTH); ASSERT (ClockType < ClockGeneratorMax); ASSERT (ConfigurationTable != NULL); // // Init some local vars // SlaveAddress.SmbusDeviceAddress = ClockAddress >> 1; Length = sizeof (Buffer); Command = 0; if (SecondarySmbus == TRUE) { return (ConfigureClockGeneratorOnSecondarySmbus(PeiServices, SmbusPpi, ClockAddress, ConfigurationTableLength, EnableSpreadSpectrum, mSupportedClockGeneratorT, ConfigurationTable )); } else { // // Not LightningRidge // Read the clock generator on the primary SMBus // SmbErrorsCounter = 0; do { Status = SmbusPpi->Execute ( SmbusPpi, SlaveAddress, Command, EfiSmbusReadBlock, FALSE, &Length, Buffer ); if(Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "SMBUS reading error\n")); } SmbErrorsCounter ++; } while ((Status != EFI_SUCCESS) && (SmbErrorsCounter < MAX_SMBUS_RETRIES)); // // Sanity check that the requested clock type is present in our supported clocks table // DEBUG ((DEBUG_INFO, "Expected Clock Generator ID is %x, populated %x\n", mSupportedClockGeneratorT->ClockId, Buffer[7])); if (EnableSpreadSpectrum) { Buffer[mSupportedClockGeneratorT->SpreadSpectrumByteOffset] |= mSupportedClockGeneratorT->SpreadSpectrumBitOffset; if (ClockType == ClockGeneratorCk420) { // Ensure that the clock chip is operating in normal mode. // Buffer[10] &= ~BIT7; } } else { Buffer[mSupportedClockGeneratorT->SpreadSpectrumByteOffset] &= ~(mSupportedClockGeneratorT->SpreadSpectrumBitOffset); } #ifdef EFI_DEBUG { UINT8 i; DEBUG ((DEBUG_INFO, "SlaveAddress.SmbusDeviceAddress %x: Size =%x\n", SlaveAddress.SmbusDeviceAddress, Length)); for (i = 0; i < ConfigurationTableLength; i++) { DEBUG ((DEBUG_INFO, "Default Clock Generator Byte %d: %x\n", i, Buffer[i])); } } #endif // // Program clock generator, using the platform default values // SmbErrorsCounter = 0; do { Command = 0; Status = SmbusPpi->Execute ( SmbusPpi, SlaveAddress, Command, EfiSmbusWriteBlock, FALSE, &Length, // &ConfigurationTableLength, Buffer //ConfigurationTable ); // ASSERT_EFI_ERROR (Status); if(Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "SMBUS writing error\n")); } SmbErrorsCounter ++; } while ((Status != EFI_SUCCESS) && (SmbErrorsCounter < MAX_SMBUS_RETRIES)); // // 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 ((DEBUG_INFO, "Clock Generator Byte %d: %x\n", i, Buffer[i])); } } #endif return EFI_SUCCESS; } // else () } /** Configure clock generator on Kahuna Clock gen is on a secondary SMBus @param IN EFI_PEI_SERVICES **PeiServices - Pointer to PEI Services table @param IN EFI_PEI_SMBUS2_PPI *SmbusPpi - Pointer to SMBUs services PPI @param IN UINT8 ClockAddress - SMBus address of clock gen @param IN UINT8 *Buffer - Pointer to buffer containing byte stream to send to clock gen @param IN UINTN Length - Number of bytes in buffer @retval EFI_SUCCESS The function completed successfully. @retval EFI_INVALID_PARAMETER The function cannot continue due to length is out of bound. **/ EFI_STATUS ConfigureClockGeneratorOnSecondarySmbus ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_SMBUS2_PPI *SmbusPpi, IN UINT8 ClockAddress, IN UINTN ConfigurationTableLength, IN BOOLEAN EnableSpreadSpectrum, IN CLOCK_GENERATOR_DETAILS *mSupportedClockGeneratorT, IN OUT UINT8 *ConfigurationTable ) { EFI_PEI_STALL_PPI *StallPpi; EFI_STATUS Status; EFI_SMBUS_DEVICE_ADDRESS SlaveAddress; EFI_SMBUS_DEVICE_COMMAND SmbusCommand; UINTN SmbusLength; UINT8 SmbusData[MAX_CLOCK_GENERATOR_BUFFER_LENGTH]; UINT8 Buffer[MAX_CLOCK_GENERATOR_BUFFER_LENGTH]; UINT8 Length; ZeroMem (Buffer, sizeof(Buffer)); if (ConfigurationTableLength > MAX_CLOCK_GENERATOR_BUFFER_LENGTH) { return EFI_INVALID_PARAMETER; } // // Locate Stall PPI // Status = (**PeiServices).LocatePpi ( PeiServices, &gEfiPeiStallPpiGuid, 0, NULL, &StallPpi ); ASSERT_EFI_ERROR (Status); // // Get length of payload to send to clock gen // Length = (UINT8) ConfigurationTableLength; // // Copy the default clock generator data into Buffer // CopyMem ((VOID*)Buffer, ConfigurationTable, Length); // // Set spread spectrum bit in Buffer or clear it? // if (EnableSpreadSpectrum) { Buffer[mSupportedClockGeneratorT->SpreadSpectrumByteOffset] |= mSupportedClockGeneratorT->SpreadSpectrumBitOffset; if (mSupportedClockGeneratorT->ClockType == ClockGeneratorCk420) { // // Ensure that the clock chip is operating in normal mode. // Buffer[10] &= ~BIT7; } } else { Buffer[mSupportedClockGeneratorT->SpreadSpectrumByteOffset] &= ~(mSupportedClockGeneratorT->SpreadSpectrumBitOffset); } // // Now encapsulate the data for a Write Block command to the clock gen // as the payload in a Write Block command to the SMBus bridge // // Segment address = 0xF2, this becomes slave address // Slave address (clock gen) = ClockAddress, this becomes slave command // SlaveAddress.SmbusDeviceAddress = (0xF2 >> 1); SmbusCommand = ClockAddress; // // Set byte index in clock gen to start with, always 0 // SmbusData[0] = 0; // // Set byte count clock gen wants to see // SmbusData[1] = (UINT8) Length; // // Payload byte count for SMBus bridge // SmbusLength = Length + 2; if (SmbusLength > MAX_CLOCK_GENERATOR_BUFFER_LENGTH) { return EFI_INVALID_PARAMETER; } // // Copy the clock gen data to the SMBus buffer // CopyMem ((VOID *)(((UINT8*)SmbusData) + 2), (VOID *)Buffer, Length); // // Use EfiSmbusWriteBlock to communicate with clock gen // Status = SmbusPpi->Execute( SmbusPpi, SlaveAddress, SmbusCommand, EfiSmbusWriteBlock, FALSE, &SmbusLength, &SmbusData ); return Status; }