534 lines
16 KiB
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));
|
|
}
|
|
}
|