mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-19 19:14:28 +00:00
feat(imx8ulp): enable the DDR frequency scaling support
Enable the DDR frequency scaling support on i.MX8ULP. Normally, the freq_index define is as below: 0: boot frequency; 1: low frequency(PLL bypassed); 2. high frequency(PLL ON). Currently, DDR DFS only do frequency switching between Low freq and high freq. Signed-off-by: Jacky Bai <ping.bai@nxp.com> Reviewed-by: Ye Li <ye.li@nxp.com> Change-Id: I3acd8bdf75e2dd6dff645b9f597dcfc0a756c428
This commit is contained in:
parent
68f132b88b
commit
caee2733ba
5 changed files with 256 additions and 0 deletions
|
@ -40,6 +40,8 @@ static uintptr_t imx_sip_handler(unsigned int smc_fid,
|
||||||
case IMX_SIP_HIFI_XRDC:
|
case IMX_SIP_HIFI_XRDC:
|
||||||
SMC_RET1(handle, imx_hifi_xrdc(smc_fid));
|
SMC_RET1(handle, imx_hifi_xrdc(smc_fid));
|
||||||
break;
|
break;
|
||||||
|
case IMX_SIP_DDR_DVFS:
|
||||||
|
return dram_dvfs_handler(smc_fid, handle, x1, x2, x3);
|
||||||
#endif
|
#endif
|
||||||
#if defined(PLAT_imx8mq)
|
#if defined(PLAT_imx8mq)
|
||||||
case IMX_SIP_GET_SOC_INFO:
|
case IMX_SIP_GET_SOC_INFO:
|
||||||
|
|
|
@ -104,4 +104,9 @@ uint64_t imx_buildinfo_handler(uint32_t smc_fid, u_register_t x1,
|
||||||
int scmi_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3);
|
int scmi_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2, u_register_t x3);
|
||||||
int imx_hifi_xrdc(uint32_t smc_fid);
|
int imx_hifi_xrdc(uint32_t smc_fid);
|
||||||
|
|
||||||
|
#if defined(PLAT_imx8ulp)
|
||||||
|
int dram_dvfs_handler(uint32_t smc_fid, void *handle,
|
||||||
|
u_register_t x1, u_register_t x2, u_register_t x3);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __IMX_SIP_SVC_H__ */
|
#endif /* __IMX_SIP_SVC_H__ */
|
||||||
|
|
|
@ -7,9 +7,16 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <arch_helpers.h>
|
||||||
|
#include <bl31/interrupt_mgmt.h>
|
||||||
|
#include <common/runtime_svc.h>
|
||||||
#include <lib/mmio.h>
|
#include <lib/mmio.h>
|
||||||
|
#include <lib/spinlock.h>
|
||||||
|
#include <plat/common/platform.h>
|
||||||
|
|
||||||
#include <platform_def.h>
|
#include <platform_def.h>
|
||||||
|
|
||||||
|
#include <dram.h>
|
||||||
#include <upower_api.h>
|
#include <upower_api.h>
|
||||||
|
|
||||||
#define PHY_FREQ_SEL_INDEX(x) ((x) << 16)
|
#define PHY_FREQ_SEL_INDEX(x) ((x) << 16)
|
||||||
|
@ -130,6 +137,12 @@ uint32_t freq_specific_reg_array[PHY_DIFF_NUM] = {
|
||||||
871, 872, 882, 1063, 1319, 1566, 1624, 1625
|
871, 872, 882, 1063, 1319, 1566, 1624, 1625
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* lock used for DDR DVFS */
|
||||||
|
spinlock_t dfs_lock;
|
||||||
|
static volatile uint32_t core_count;
|
||||||
|
static volatile bool in_progress;
|
||||||
|
static int num_fsp;
|
||||||
|
|
||||||
static void ddr_init(void)
|
static void ddr_init(void)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
@ -425,3 +438,223 @@ void dram_exit_retention(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define LPDDR_DONE (0x1<<4)
|
||||||
|
#define SOC_FREQ_CHG_ACK (0x1<<6)
|
||||||
|
#define SOC_FREQ_CHG_REQ (0x1<<7)
|
||||||
|
#define LPI_WAKEUP_EN (0x4<<8)
|
||||||
|
#define SOC_FREQ_REQ (0x1<<11)
|
||||||
|
|
||||||
|
#define LPDDR_EN_CLKGATE (0x1<<17)
|
||||||
|
|
||||||
|
static void set_cgc2_ddrclk(uint8_t src, uint8_t div)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Wait until the reg is unlocked for writing */
|
||||||
|
while (mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(31))
|
||||||
|
;
|
||||||
|
|
||||||
|
mmio_write_32(IMX_CGC2_BASE + 0x40, (src << 28) | (div << 21));
|
||||||
|
/* Wait for the clock switching done */
|
||||||
|
while (!(mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(27)))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
static void set_ddr_clk(uint32_t ddr_freq)
|
||||||
|
{
|
||||||
|
/* Disable DDR clock */
|
||||||
|
mmio_clrbits_32(IMX_PCC5_BASE + 0x108, BIT(30));
|
||||||
|
switch (ddr_freq) {
|
||||||
|
/* boot frequency ? */
|
||||||
|
case 48:
|
||||||
|
set_cgc2_ddrclk(2, 0);
|
||||||
|
break;
|
||||||
|
/* default bypass frequency for fsp 1 */
|
||||||
|
case 192:
|
||||||
|
set_cgc2_ddrclk(0, 1);
|
||||||
|
break;
|
||||||
|
case 384:
|
||||||
|
set_cgc2_ddrclk(0, 0);
|
||||||
|
break;
|
||||||
|
case 264:
|
||||||
|
set_cgc2_ddrclk(4, 3);
|
||||||
|
break;
|
||||||
|
case 528:
|
||||||
|
set_cgc2_ddrclk(4, 1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Enable DDR clock */
|
||||||
|
mmio_setbits_32(IMX_PCC5_BASE + 0x108, BIT(30));
|
||||||
|
|
||||||
|
/* Wait until the reg is unlocked for writing */
|
||||||
|
while (mmio_read_32(IMX_CGC2_BASE + 0x40) & BIT(31)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define AVD_SIM_LPDDR_CTRL (IMX_LPAV_SIM_BASE + 0x14)
|
||||||
|
#define AVD_SIM_LPDDR_CTRL2 (IMX_LPAV_SIM_BASE + 0x18)
|
||||||
|
#define MAX_FSP_NUM U(3)
|
||||||
|
#define DDR_DFS_GET_FSP_COUNT 0x10
|
||||||
|
#define DDR_BYPASS_DRATE U(400)
|
||||||
|
|
||||||
|
/* Normally, we only switch frequency between 1(bypass) and 2(highest) */
|
||||||
|
int lpddr4_dfs(uint32_t freq_index)
|
||||||
|
{
|
||||||
|
uint32_t lpddr_ctrl, lpddr_ctrl2;
|
||||||
|
uint32_t ddr_ctl_144;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Valid index: 0 to 2
|
||||||
|
* index 0: boot frequency
|
||||||
|
* index 1: bypass frequency
|
||||||
|
* index 2: highest frequency
|
||||||
|
*/
|
||||||
|
if (freq_index > 2U) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable LPI_WAKEUP_EN */
|
||||||
|
ddr_ctl_144 = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_144);
|
||||||
|
mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, LPI_WAKEUP_EN);
|
||||||
|
|
||||||
|
/* put DRAM into long self-refresh & clock gating */
|
||||||
|
lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL);
|
||||||
|
lpddr_ctrl = (lpddr_ctrl & ~((0x3f << 15) | (0x3 << 9))) | (0x28 << 15) | (freq_index << 9);
|
||||||
|
mmio_write_32(AVD_SIM_LPDDR_CTRL, lpddr_ctrl);
|
||||||
|
|
||||||
|
/* Gating the clock */
|
||||||
|
lpddr_ctrl2 = mmio_read_32(AVD_SIM_LPDDR_CTRL2);
|
||||||
|
mmio_setbits_32(AVD_SIM_LPDDR_CTRL2, LPDDR_EN_CLKGATE);
|
||||||
|
|
||||||
|
/* Request frequency change */
|
||||||
|
mmio_setbits_32(AVD_SIM_LPDDR_CTRL, SOC_FREQ_REQ);
|
||||||
|
|
||||||
|
do {
|
||||||
|
lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL);
|
||||||
|
if (lpddr_ctrl & SOC_FREQ_CHG_REQ) {
|
||||||
|
/* Bypass mode */
|
||||||
|
if (info->fsp_table[freq_index] < DDR_BYPASS_DRATE) {
|
||||||
|
/* Change to PLL bypass mode */
|
||||||
|
mmio_write_32(IMX_LPAV_SIM_BASE, 0x1);
|
||||||
|
/* change the ddr clock source & frequency */
|
||||||
|
set_ddr_clk(info->fsp_table[freq_index]);
|
||||||
|
} else {
|
||||||
|
/* Change to PLL unbypass mode */
|
||||||
|
mmio_write_32(IMX_LPAV_SIM_BASE, 0x0);
|
||||||
|
/* change the ddr clock source & frequency */
|
||||||
|
set_ddr_clk(info->fsp_table[freq_index] >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mmio_clrsetbits_32(AVD_SIM_LPDDR_CTRL, SOC_FREQ_CHG_REQ, SOC_FREQ_CHG_ACK);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} while ((lpddr_ctrl & LPDDR_DONE) != 0); /* several try? */
|
||||||
|
|
||||||
|
/* restore the original setting */
|
||||||
|
mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, ddr_ctl_144);
|
||||||
|
mmio_write_32(AVD_SIM_LPDDR_CTRL2, lpddr_ctrl2);
|
||||||
|
|
||||||
|
/* Check the DFS result */
|
||||||
|
lpddr_ctrl = mmio_read_32(AVD_SIM_LPDDR_CTRL) & 0xF;
|
||||||
|
if (lpddr_ctrl != 0U) {
|
||||||
|
/* Must be something wrong, return failure */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DFS done successfully */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for the non-primary core, waiting for DFS done */
|
||||||
|
static uint64_t waiting_dvfs(uint32_t id, uint32_t flags,
|
||||||
|
void *handle, void *cookie)
|
||||||
|
{
|
||||||
|
uint32_t irq;
|
||||||
|
|
||||||
|
irq = plat_ic_acknowledge_interrupt();
|
||||||
|
if (irq < 1022U) {
|
||||||
|
plat_ic_end_of_interrupt(irq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the WFE done status */
|
||||||
|
spin_lock(&dfs_lock);
|
||||||
|
core_count++;
|
||||||
|
dsb();
|
||||||
|
spin_unlock(&dfs_lock);
|
||||||
|
|
||||||
|
while (in_progress) {
|
||||||
|
wfe();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dram_dvfs_handler(uint32_t smc_fid, void *handle,
|
||||||
|
u_register_t x1, u_register_t x2, u_register_t x3)
|
||||||
|
{
|
||||||
|
unsigned int fsp_index = x1;
|
||||||
|
uint32_t online_cpus = x2 - 1;
|
||||||
|
uint64_t mpidr = read_mpidr_el1();
|
||||||
|
unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr);
|
||||||
|
|
||||||
|
/* Get the number of FSPs */
|
||||||
|
if (x1 == DDR_DFS_GET_FSP_COUNT) {
|
||||||
|
SMC_RET2(handle, num_fsp, info->fsp_table[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* start lpddr frequency scaling */
|
||||||
|
in_progress = true;
|
||||||
|
dsb();
|
||||||
|
|
||||||
|
/* notify other core wait for scaling done */
|
||||||
|
for (unsigned int i = 0; i < PLATFORM_CORE_COUNT; i++)
|
||||||
|
/* Skip raise SGI for current CPU */
|
||||||
|
if (i != cpu_id) {
|
||||||
|
plat_ic_raise_el3_sgi(0x8, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure all the cpu in WFE */
|
||||||
|
while (online_cpus != core_count) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flush the L1/L2 cache */
|
||||||
|
dcsw_op_all(DCCSW);
|
||||||
|
|
||||||
|
lpddr4_dfs(fsp_index);
|
||||||
|
|
||||||
|
in_progress = false;
|
||||||
|
core_count = 0;
|
||||||
|
dsb();
|
||||||
|
sev();
|
||||||
|
isb();
|
||||||
|
|
||||||
|
SMC_RET1(handle, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dram_init(void)
|
||||||
|
{
|
||||||
|
uint32_t flags = 0;
|
||||||
|
uint32_t rc;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* Register the EL3 handler for DDR DVFS */
|
||||||
|
set_interrupt_rm_flag(flags, NON_SECURE);
|
||||||
|
rc = register_interrupt_type_handler(INTR_TYPE_EL3, waiting_dvfs, flags);
|
||||||
|
if (rc) {
|
||||||
|
panic();
|
||||||
|
}
|
||||||
|
|
||||||
|
info = (struct dram_timing_info *)SAVED_DRAM_DATA_BASE;
|
||||||
|
|
||||||
|
/* Get the num of the supported Fsp */
|
||||||
|
for (i = 0; i < MAX_FSP_NUM; i++) {
|
||||||
|
if (!info->fsp_table[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
num_fsp = (i > MAX_FSP_NUM) ? MAX_FSP_NUM : i;
|
||||||
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <plat/common/platform.h>
|
#include <plat/common/platform.h>
|
||||||
#include <platform_def.h>
|
#include <platform_def.h>
|
||||||
|
|
||||||
|
#include <dram.h>
|
||||||
#include <imx8_lpuart.h>
|
#include <imx8_lpuart.h>
|
||||||
#include <imx8ulp_caam.h>
|
#include <imx8ulp_caam.h>
|
||||||
#include <imx_plat_common.h>
|
#include <imx_plat_common.h>
|
||||||
|
@ -153,6 +154,8 @@ void bl31_platform_setup(void)
|
||||||
xrdc_enable();
|
xrdc_enable();
|
||||||
|
|
||||||
imx8ulp_caam_init();
|
imx8ulp_caam_init();
|
||||||
|
|
||||||
|
dram_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
entry_point_info_t *bl31_plat_get_next_image_ep_info(unsigned int type)
|
entry_point_info_t *bl31_plat_get_next_image_ep_info(unsigned int type)
|
||||||
|
|
13
plat/imx/imx8ulp/include/dram.h
Normal file
13
plat/imx/imx8ulp/include/dram.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 NXP
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DRAM_H
|
||||||
|
#define DRAM_H
|
||||||
|
|
||||||
|
void dram_init(void);
|
||||||
|
|
||||||
|
#endif /* DRAM_H */
|
||||||
|
|
Loading…
Add table
Reference in a new issue