alder_lake_bios/Intel/AlderLake/AlderLakeBoardPkg/Library/DccProgramLib/DccProgramLib.c

534 lines
16 KiB
C

/** @file
Discrete clock generator program library.
@copyright
INTEL CONFIDENTIAL
Copyright 2021 Intel Corporation.
The source code contained or described herein and all documents related to the
source code ("Material") are owned by Intel Corporation or its suppliers or
licensors. Title to the Material remains with Intel Corporation or its suppliers
and licensors. The Material may contain trade secrets and proprietary and
confidential information of Intel Corporation and its suppliers and licensors,
and is protected by worldwide copyright and trade secret laws and treaty
provisions. No part of the Material may be used, copied, reproduced, modified,
published, uploaded, posted, transmitted, distributed, or disclosed in any way
without Intel's prior express written permission.
No license under any patent, copyright, trade secret or other intellectual
property right is granted to or conferred upon you by disclosure or delivery
of the Materials, either expressly, by implication, inducement, estoppel or
otherwise. Any license under such intellectual property rights must be
express and approved by Intel in writing.
Unless otherwise agreed by Intel in writing, you may not remove or alter
this notice or any other notice embedded in Materials by Intel or
Intel's suppliers or licensors in any way.
This file contains a 'Sample Driver' and is licensed as such under the terms
of your license agreement with Intel or your vendor. This file may be modified
by the user, subject to the additional terms of the license agreement.
@par Specification Reference:
**/
#include <Uefi.h>
#include <Ppi/Smbus2.h>
#include <Library/SmbusLib.h>
#include <Library/DebugLib.h>
#include <Library/DccProgramLib.h>
//
// If Next Divider Frequency is equal to Current, divider won't be changed.
//
#define IS_DIVIDER_NEED_TO_CHANGE(int1, int2, frac1, frac2) ((((int1) != (int2)) || ((frac1) != (frac2)))? TRUE : FALSE)
//
// If Next Divider Frequency is greater than Current, divider frequence is increasing, otherwise, it is decreasing.
//
#define IS_DIVIDER_INCREASING(nextint, curint, nextfrac, curfrac) (((nextint) == (curint))? (((nextfrac) > (curfrac))? TRUE : FALSE) \
: (((nextint) > (curint))? TRUE : FALSE))
#define GET_QUOTIENT(a, b) (UINT8)((((a) > 0) && ((b) > 0))? (a / b) : 0)
#define GET_REMAINDER(a, b) (UINT8)((((a) > 0) && ((b) > 0))? (a % b) : 0)
/**
Set register page address.
@param[in] Page Page address of the register.
@retval Status
**/
EFI_STATUS
DccRc260xSetPage (
IN UINT8 Page
)
{
EFI_STATUS Status;
EFI_SMBUS_DEVICE_ADDRESS SmbusDeviceAddr;
SmbusDeviceAddr.SmbusDeviceAddress = RENESAS_260X_ADDRESS;
SmBusWriteDataByte (
SMBUS_LIB_ADDRESS (SmbusDeviceAddr.SmbusDeviceAddress, RENESAS_260X_1B_MODE_SET_PAGE_CMD, 0, FALSE),
Page,
&Status
);
return Status;
}
/**
Read data from RC260X register.
@param[in] Register Register address
@retval Data Data read from RC260X register.
**/
UINT8
DccRegisterValueRead (
IN UINT8 Register
)
{
EFI_STATUS Status;
EFI_SMBUS_DEVICE_ADDRESS SmbusDeviceAddr;
UINT8 Data;
SmbusDeviceAddr.SmbusDeviceAddress = RENESAS_260X_ADDRESS;
Data = SmBusReadDataByte (
SMBUS_LIB_ADDRESS (SmbusDeviceAddr.SmbusDeviceAddress, Register, 0, FALSE),
&Status
);
return Data;
}
/**
Write data to RC260X register.
@param[in] Register Register address
@param[in] Data Data that will be set.
**/
VOID
DccRegisterValueWrite (
IN UINT8 Register,
IN UINT8 Data
)
{
EFI_STATUS Status;
EFI_SMBUS_DEVICE_ADDRESS SmbusDeviceAddr;
SmbusDeviceAddr.SmbusDeviceAddress = RENESAS_260X_ADDRESS;
SmBusWriteDataByte (
SMBUS_LIB_ADDRESS (SmbusDeviceAddr.SmbusDeviceAddress, Register, 0, FALSE),
Data,
&Status
);
return;
}
/**
Set divider value to shadow register0.
@param[in] Integer The integer parts of divider register.
@param[in] Fractional The fractional parts of divider register.
**/
VOID
SetDividerValueToShadowReg0 (
IN REGISTER_INTEGER Integer,
IN REGISTER_FRACTIONAL Fractional
)
{
DccRegisterValueWrite (0xd9, Integer.Parts.Byte1);
DccRegisterValueWrite (0xd8, Integer.Parts.Byte0);
DccRegisterValueWrite (0xd7, Fractional.Parts.Byte3);
DccRegisterValueWrite (0xd6, Fractional.Parts.Byte2);
DccRegisterValueWrite (0xd5, Fractional.Parts.Byte1);
DccRegisterValueWrite (0xd4, Fractional.Parts.Byte0);
}
/**
Set divider value to shadow register1.
@param[in] Integer The integer parts of divider register.
@param[in] Fractional The fractional parts of divider register.
**/
VOID
SetDividerValueToShadowReg1 (
IN REGISTER_INTEGER Integer,
IN REGISTER_FRACTIONAL Fractional
)
{
DccRegisterValueWrite (0xdb, Integer.Parts.Byte1);
DccRegisterValueWrite (0xda, Integer.Parts.Byte0);
DccRegisterValueWrite (0xdf, Fractional.Parts.Byte3);
DccRegisterValueWrite (0xde, Fractional.Parts.Byte2);
DccRegisterValueWrite (0xdd, Fractional.Parts.Byte1);
DccRegisterValueWrite (0xdc, Fractional.Parts.Byte0);
}
/**
Apply current divider settings to a shadow register.
@param[in] OcFodSelect Choose Fractional Output Divider that will be set.
@param[in] Integer The integer parts of divider register.
@param[in] Fractional The fractional parts of divider register.
**/
VOID
ApplyDividerValue (
IN UINT8 OcFodSelect,
IN REGISTER_INTEGER Integer,
IN REGISTER_FRACTIONAL Fractional
)
{
switch (OcFodSelect) {
case CPU_BCLK_OC_FOD:
SetDividerValueToShadowReg0 (Integer, Fractional);
return;
case PEG_DMI_CLK_FOD:
SetDividerValueToShadowReg1 (Integer, Fractional);
return;
default:
return;
}
}
/**
Set divide value to divider register.
@param[in] Integer The integer parts of divider register.
@param[in] Fractional The fractional parts of divider register.
**/
VOID
SetDivider (
IN REGISTER_INTEGER Integer,
IN REGISTER_FRACTIONAL Fractional
)
{
DEBUG ((DEBUG_INFO, "Set Divider Start\n"));
DccRc260xSetPage (0x02);
DccRegisterValueWrite (0xC7, Integer.Parts.Byte1);
DccRegisterValueWrite (0xC6, Integer.Parts.Byte0);
if (Fractional.Uint32 > 0) {
DccRegisterValueWrite (0xCB, Fractional.Parts.Byte3);
DccRegisterValueWrite (0xCA, Fractional.Parts.Byte2);
DccRegisterValueWrite (0xC9, Fractional.Parts.Byte1);
DccRegisterValueWrite (0xC8, Fractional.Parts.Byte0);
}
DEBUG ((DEBUG_INFO, "Set Divider End\n"));
}
/**
Get original divider setting from the shadow register0
@param[in, out] Integer The integer parts of divider register.
@param[in, out] Fractional The fractional parts of divider register.
**/
VOID
GetCurrentDividerSettingsFromShadowReg0 (
IN REGISTER_INTEGER *Integer,
IN REGISTER_FRACTIONAL *Fractional
)
{
Integer->Parts.Byte1 = DccRegisterValueRead (0xd9);
Integer->Parts.Byte0 = DccRegisterValueRead (0xd8);
Fractional->Parts.Byte3 = DccRegisterValueRead (0xd7);
Fractional->Parts.Byte2 = DccRegisterValueRead (0xd6);
Fractional->Parts.Byte1 = DccRegisterValueRead (0xd5);
Fractional->Parts.Byte0 = DccRegisterValueRead (0xd4);
}
/**
Get original divider setting from the shadow register1
@param[in, out] Integer The integer parts of divider register.
@param[in, out] Fractional The fractional parts of divider register.
**/
VOID
GetCurrentDividerSettingsFromShadowReg1 (
IN REGISTER_INTEGER *Integer,
IN REGISTER_FRACTIONAL *Fractional
)
{
Integer->Parts.Byte1 = DccRegisterValueRead (0xdb);
Integer->Parts.Byte0 = DccRegisterValueRead (0xda);
Fractional->Parts.Byte3 = DccRegisterValueRead (0xdf);
Fractional->Parts.Byte2 = DccRegisterValueRead (0xde);
Fractional->Parts.Byte1 = DccRegisterValueRead (0xdd);
Fractional->Parts.Byte0 = DccRegisterValueRead (0xdc);
}
/**
Get original divider setting from a register.
@param[in] OcFodSelect Choose Fractional Output Divider that will be read.
@param[in, out] Integer The integer parts of divider register.
@param[in, out] Fractional The fractional parts of divider register.
**/
VOID
GetCurrentDividerSettings (
IN UINT8 OcFodSelect,
IN REGISTER_INTEGER *Integer,
IN REGISTER_FRACTIONAL *Fractional
)
{
switch (OcFodSelect) {
case CPU_BCLK_OC_FOD:
GetCurrentDividerSettingsFromShadowReg0 (Integer, Fractional);
return;
case PEG_DMI_CLK_FOD:
GetCurrentDividerSettingsFromShadowReg1 (Integer, Fractional);
return;
default:
return;
}
}
/**
Calculate the divider value that will be set.
DividerFreq = ChipFreq / TargetFreq.
@param[in] TargetFreq Frequency of the target device that will be set.
@param[in, out] Integer The integer parts of divider register.
@param[in, out] Fractional The fractional parts of divider register.
**/
VOID
GetNextDividerSettings (
IN UINT16 TargetFreq,
IN REGISTER_INTEGER *Integer,
IN REGISTER_FRACTIONAL *Fractional
)
{
UINT16 Remainder;
Integer->Uint16 = GET_QUOTIENT (RENESAS_260X_CHIP_FREQUENCY, TargetFreq);
Remainder = GET_REMAINDER (RENESAS_260X_CHIP_FREQUENCY, TargetFreq);
if (Remainder > 0) {
//
// Fractional method:
// Example: 100/110 = 0.909090909.... equals to 0.e8ba2e8 (in hex)
// 1. (100 * 16) / 110 = 0xe [27:24], remainder is (100 * 16) % 110 = 60
// 2. (60 * 256) / 110 = 0x8b [23:16], remainder is (60 * 256) % 110 = 70
// 3. (70 * 256) / 110 = 0xa2 [15:8], remainder is (70 * 256) % 110 = 100
// 4. (100 * 256) / 110 = 0xe8 [7:0], remainder is (100 * 256) % 110 = 80
// 5. 0xe8ba2e8 in [27:0] = 0x0e8ba2e8 in [31:0].
// Because the RC260X chip only prepare 28 bits for fractional, [31:28] is reserved.
//
Fractional->Parts.Byte3 = GET_QUOTIENT ((Remainder * 16), TargetFreq);
Remainder = GET_REMAINDER ((Remainder * 16 ), TargetFreq);
Fractional->Parts.Byte2 = GET_QUOTIENT ((Remainder * 256), TargetFreq);
Remainder = GET_REMAINDER ((Remainder * 256 ), TargetFreq);
Fractional->Parts.Byte1 = GET_QUOTIENT ((Remainder * 256), TargetFreq);
Remainder = GET_REMAINDER ((Remainder * 256), TargetFreq);
Fractional->Parts.Byte0 = GET_QUOTIENT ((Remainder * 256), TargetFreq);
}
}
/**
Program chip to adjust clock frequencey for target device.
@param[in] OcFodSelect Choose Fractional Output Divider which will be modified.
@param[in] TargetFreq Frequency of the target device that will be set.
**/
VOID
ProgramBclkFreq (
IN UINT8 OcFodSelect,
IN UINT16 TargetFreq
)
{
REGISTER_2C0H Reg0x2c0;
REGISTER_2C1H Reg0x2c1;
REGISTER_2CCH Reg0x2cc;
REGISTER_INTEGER NextInteger;
REGISTER_INTEGER CurInteger;
REGISTER_FRACTIONAL NextFractional;
REGISTER_FRACTIONAL CurFractional;
DEBUG ((DEBUG_INFO, "Program DCC CLK Start\n"));
NextInteger.Uint16 = 0;
NextFractional.Uint32 = 0;
CurInteger.Uint16 = 0;
CurFractional.Uint32 = 0;
//
// Set clk chip page register to 0x02
//
DccRc260xSetPage (0x02);
Reg0x2c0.Uint8 = DccRegisterValueRead (0xC0);
Reg0x2c0.Bits.OcFodSel = OcFodSelect;
Reg0x2c0.Bits.OcTrigMode = 1;
DccRegisterValueWrite (0xC0, Reg0x2c0.Uint8);
GetNextDividerSettings (TargetFreq, &NextInteger, &NextFractional);
GetCurrentDividerSettings (OcFodSelect, &CurInteger, &CurFractional);
DEBUG ((DEBUG_ERROR, "BCLK NextInteger.Uint16 = 0x%02X\n", NextInteger.Uint16));
DEBUG ((DEBUG_ERROR, "BCLK NextFractional.Uint32 = 0x%08X\n", NextFractional.Uint32));
DEBUG ((DEBUG_ERROR, "BCLK CurInteger.Uint16 = 0x%02X\n", CurInteger.Uint16));
DEBUG ((DEBUG_ERROR, "BCLK CurFractional.Uint32 = 0x%08X\n", CurFractional.Uint32));
//
// If the next divider frequency is not equal to current frequency, divider need to be modified.
//
if (IS_DIVIDER_NEED_TO_CHANGE (NextInteger.Uint16, CurInteger.Uint16, NextFractional.Uint32, CurFractional.Uint32)) {
Reg0x2c1.Uint8 = DccRegisterValueRead (0xC1);
Reg0x2c1.Bits.OcMode = 1;
Reg0x2c1.Bits.OcEnable = 1;
//
// TargetFreq = ClockGenFreq / DividerFreq.
// If DividerFreq is increasing, that means the TargetFreq will be slower,
// and if the DividerFreq is decreasing, the TargetFreq will be faster.
//
Reg0x2cc.Uint8 = DccRegisterValueRead (0xCC);
Reg0x2cc.Bits.OcIncr = !(IS_DIVIDER_INCREASING (NextInteger.Uint16, CurInteger.Uint16, NextFractional.Uint32, CurFractional.Uint32));
Reg0x2cc.Bits.OcDecr = IS_DIVIDER_INCREASING (NextInteger.Uint16, CurInteger.Uint16, NextFractional.Uint32, CurFractional.Uint32);
SetDivider (NextInteger, NextFractional);
DccRegisterValueWrite (0xC1, Reg0x2c1.Uint8);
DccRegisterValueWrite (0xCC, Reg0x2cc.Uint8);
DccRegisterValueWrite (0xCC, Reg0x2cc.Uint8);
ApplyDividerValue (OcFodSelect, NextInteger, NextFractional);
}
DEBUG ((DEBUG_INFO, "Program DCC CLK End\n"));
}
/**
Program Dcc chip for Cpu Bclk
@param[in] TargetFreq Frequency of the target device that will be set.
**/
VOID
ProgramCpuBclkFreq (
IN UINT16 TargetFreq
)
{
DEBUG ((DEBUG_INFO, "Program CPU Bclk Start\n"));
ProgramBclkFreq (CPU_BCLK_OC_FOD, TargetFreq);
DEBUG ((DEBUG_INFO, "Program CPU Bclk End\n"));
}
/**
Program Dcc chip for Peg/Dmi Clk
@param[in] TargetFreq Frequency of the target device that will be set.
**/
VOID
ProgramPegDmiClkFreq (
IN UINT16 TargetFreq
)
{
DEBUG ((DEBUG_INFO, "Program Peg/Dmi Clk Start\n"));
ProgramBclkFreq (PEG_DMI_CLK_FOD, TargetFreq);
DEBUG ((DEBUG_INFO, "Program Peg/Dmi Clk End\n"));
}
/**
Check if Renesas RC260X exist or not.
**/
BOOLEAN
CheckDccClkChipExist (
VOID
)
{
//
// If set page success, it means RC260X chip exists.
//
return (DccRc260xSetPage (0x02) == EFI_SUCCESS)? TRUE : FALSE;
}
/**
Disable Output Clock on all Out[X]/Out[X]b
**/
VOID
DisableExternalClockOutput (
VOID
)
{
REGISTER_ODRV_EN OutPutState;
UINT8 Output;
UINT8 OutDrvEn;
DccRc260xSetPage (0x02);
for (Output = 0; Output <= 11; Output++) {
OutDrvEn = 0x40 + (4 * Output);
OutPutState.Uint8 = DccRegisterValueRead (OutDrvEn);
DEBUG ((DEBUG_ERROR, "OUT[%d] = 0x%02X\n", Output, OutPutState.Uint8));
if (OutPutState.Bits.OutDis == 1) {
DEBUG ((DEBUG_ERROR, "OUT[%d] is already Disabled\n", Output));
continue;
}
OutPutState.Uint8 |= RENESAS_260X_OUTPUT_DISABLE;
DccRegisterValueWrite (OutDrvEn, OutPutState.Uint8);
OutPutState.Uint8 = DccRegisterValueRead (OutDrvEn);
DEBUG ((DEBUG_ERROR, "OUT[%d] after set as Disabled = 0x%02X\n", Output, OutPutState.Uint8));
}
}
/**
Enable Output Clock on all Out[X]/Out[X]b
**/
VOID
EnableExternalClockOutput (
VOID
)
{
REGISTER_ODRV_EN OutPutState;
UINT8 Output;
UINT8 OutDrvEn;
DccRc260xSetPage (0x02);
for (Output = 0; Output <= 11; Output++) {
OutDrvEn = 0x40 + (4 * Output);
OutPutState.Uint8 = DccRegisterValueRead (OutDrvEn);
DEBUG ((DEBUG_ERROR, "OUT[%d] = 0x%02X\n", Output, OutPutState.Uint8));
if (OutPutState.Bits.OutDis == 0) {
DEBUG ((DEBUG_ERROR, "OUT[%d] is already Enabled\n", Output));
continue;
}
OutPutState.Uint8 &= ~RENESAS_260X_OUTPUT_DISABLE;
DccRegisterValueWrite (OutDrvEn, OutPutState.Uint8);
OutPutState.Uint8 = DccRegisterValueRead (OutDrvEn);
DEBUG ((DEBUG_ERROR, "OUT[%d] after set as Enabled = 0x%02X\n", Output, OutPutState.Uint8));
}
}