blob: b8245db594a509f8b9aeae78c13f6bc42b8952f8 [file] [log] [blame]
/*
* Copyright 2018 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <debug.h>
#include <ddrc.h>
#include <dram.h>
#include <mmio.h>
#include <spinlock.h>
static struct dram_info dram_info;
/* lock used for DDR DVFS */
spinlock_t dfs_lock;
/* IRQ used for DDR DVFS */
#if defined(PLAT_IMX8M)
static uint32_t irqs_used [] = {102, 109, 110, 111};
/* ocram used to dram timing */
static uint8_t dram_timing_saved[13 * 1024] __aligned(8);
#else
static uint32_t irqs_used[] = {74, 75, 76, 77};
#endif
static volatile uint32_t wfe_done;
static volatile bool wait_ddrc_hwffc_done = true;
static unsigned int dev_fsp = 0x1;
bool bypass_mode_supported = true;
#if defined (PLAT_IMX8M)
/* copy the dram timing info from DRAM to OCRAM */
void imx8mq_dram_timing_copy(struct dram_timing_info *from,
struct dram_timing_info *to)
{
struct dram_cfg_param *cfg1, *cfg2;
unsigned int num;
/* copy the dram_timing info header */
cfg1 = (struct dram_cfg_param *) ((unsigned long) to + sizeof(struct dram_timing_info));
cfg2 = from->ddrc_cfg;
/* if no valid dram timing info, return */
if (((unsigned long)from + sizeof(struct dram_timing_info)) != (unsigned long)cfg2)
return;
/* copy the ddrc init config */
to->ddrc_cfg_num = from->ddrc_cfg_num;
to->ddrphy_cfg_num = from->ddrphy_cfg_num;
to->ddrphy_trained_csr_num = from->ddrphy_trained_csr_num;
to->ddrphy_pie_num = from->ddrphy_pie_num;
/* copy the fsp table */
for (int i = 0; i < 4; i++)
to->fsp_table[i] = from->fsp_table[i];
/* copy the ddrc config */
to->ddrc_cfg = cfg1;
num = from->ddrc_cfg_num;
for (int i = 0; i < num; i++) {
cfg1->reg = cfg2->reg;
cfg1->val = cfg2->val;
cfg1++;
cfg2++;
}
/* copy the ddrphy init config */
to->ddrphy_cfg = cfg1;
num = from->ddrphy_cfg_num;
for (int i = 0; i < num; i++) {
cfg1->reg = cfg2->reg;
cfg1->val = cfg2->val;
cfg1++;
cfg2++;
}
/* copy the ddrphy csr */
to->ddrphy_trained_csr = cfg1;
num = from->ddrphy_trained_csr_num;
for (int i = 0; i < num; i++) {
cfg1->reg = cfg2->reg;
cfg1->val = cfg2->val;
cfg1++;
cfg2++;
}
/* copy the PIE image */
to->ddrphy_pie = cfg1;
num = from->ddrphy_pie_num;
for (int i = 0; i < num; i++) {
cfg1->reg = cfg2->reg;
cfg1->val = cfg2->val;
cfg1++;
cfg2++;
}
}
#endif
/* restore the ddrc config */
void dram_umctl2_init(void)
{
struct dram_timing_info *timing = dram_info.timing_info;
struct dram_cfg_param *ddrc_cfg = timing->ddrc_cfg;
int num = timing->ddrc_cfg_num;
for (int i = 0; i < num; i++) {
mmio_write_32(ddrc_cfg->reg, ddrc_cfg->val);
ddrc_cfg++;
}
/* set the default fsp to P0 */
mmio_write_32(DDRC_MSTR2(0), 0x0);
}
/* resotre the dram phy config */
void dram_phy_init(void)
{
struct dram_timing_info *timing = dram_info.timing_info;
struct dram_cfg_param *ddrphy_cfg = timing->ddrphy_cfg;
int num = timing->ddrphy_cfg_num;
/* restore the phy init config */
for (int i = 0; i < num; i++) {
dwc_ddrphy_apb_wr(ddrphy_cfg->reg, ddrphy_cfg->val);
ddrphy_cfg++;
}
/* restore the ddr phy csr */
num = timing->ddrphy_trained_csr_num;
ddrphy_cfg = timing->ddrphy_trained_csr;
for (int i = 0; i < num; i++) {
dwc_ddrphy_apb_wr(ddrphy_cfg->reg, ddrphy_cfg->val);
ddrphy_cfg++;
}
/* load the PIE image */
num = timing->ddrphy_pie_num;
ddrphy_cfg = timing->ddrphy_pie;
for (int i = 0; i < num; i++) {
dwc_ddrphy_apb_wr(ddrphy_cfg->reg, ddrphy_cfg->val);
ddrphy_cfg++;
}
}
#define BYPASS_MODE_DRATE 666
static bool is_bypass_mode_enabled(struct dram_timing_info *info)
{
/*
* if there is a fsp drate is lower than 666, we assume
* that the bypass mode is enanbled.
*/
if(info->fsp_table[1] > BYPASS_MODE_DRATE ||
info->fsp_table[2] > BYPASS_MODE_DRATE)
return false;
else
return true;
}
void dram_info_init(unsigned long dram_timing_base)
{
uint32_t current_fsp, ddr_type, ddrc_mstr;
/* get the dram type */
ddrc_mstr = mmio_read_32(DDRC_MSTR(0));
ddr_type = ddrc_mstr & DDR_TYPE_MASK;
dram_info.dram_type = ddr_type;
dram_info.num_rank = (ddrc_mstr >> 24) & DDRC_ACTIVE_RANK_MASK;
/* init the boot_fsp & current_fsp */
current_fsp = mmio_read_32(DDRC_DFIMISC(0));
current_fsp = (current_fsp >> 8) & 0xf;
dram_info.boot_fsp = current_fsp;
dram_info.current_fsp = current_fsp;
#if defined(PLAT_IMX8M)
imx8mq_dram_timing_copy((struct dram_timing_info *)dram_timing_base,
(struct dram_timing_info *)dram_timing_saved);
dram_timing_base = (unsigned long) dram_timing_saved;
#endif
bypass_mode_supported = is_bypass_mode_enabled((struct dram_timing_info *)dram_timing_base);
/*
* No need to do save for ddrc and phy config register,
* we have done it in SPL stage and save in memory
*/
dram_info.timing_info = (struct dram_timing_info *)dram_timing_base;
/* switch to the highest frequency point */
if(ddr_type == DDRC_LPDDR4 && current_fsp != 0x0) {
/* flush the L1/L2 cache */
dcsw_op_all(DCCSW);
lpddr4_swffc(&dram_info, dev_fsp, 0x0, bypass_mode_supported);
dev_fsp = (~dev_fsp) & 0x1;
} else if (ddr_type == DDRC_DDR4 && current_fsp != 0x0) {
/* flush the L1/L2 cache */
dcsw_op_all(DCCSW);
ddr4_swffc(&dram_info, 0x0);
}
}
void dram_enter_retention(void)
{
if (dram_info.dram_type == DDRC_LPDDR4)
lpddr4_enter_retention();
else if (dram_info.dram_type == DDRC_DDR4)
ddr4_enter_retention();
}
void dram_exit_retention(void)
{
if (dram_info.dram_type == DDRC_LPDDR4)
lpddr4_exit_retention();
else if (dram_info.dram_type == DDRC_DDR4)
ddr4_exit_retention();
}
int dram_dvfs_handler(uint32_t smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3)
{
uint64_t mpidr = read_mpidr_el1();
unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr);
unsigned int target_freq = x1;
uint32_t online_cores = x2;
if (target_freq == 0xf) {
/* set the WFE done status */
spin_lock(&dfs_lock);
wfe_done |= (1 << cpu_id * 8);
spin_unlock(&dfs_lock);
while (1) {
/* ddr frequency change done */
wfe();
if (!wait_ddrc_hwffc_done) {
break;
}
}
} else {
wait_ddrc_hwffc_done = true;
/* trigger the IRQ */
for (int i = 0; i < 4; i++) {
int irq = irqs_used[i] % 32;
if (cpu_id != i && (online_cores & (0x1 << (i * 8)))) {
mmio_write_32(0x38800204 + (irqs_used[i] / 32) * 4, (1 << irq));
}
}
/* make sure all the core in WFE */
online_cores &= ~(0x1 << (cpu_id * 8));
while (1) {
#if defined(PLAT_IMX8M)
mmio_write_32(0x30340004, mmio_read_32(0x30340004) | (1 << 12));
#endif
if (online_cores == wfe_done)
break;
}
#if defined(PLAT_IMX8M)
mmio_write_32(0x30340004, mmio_read_32(0x30340004) & ~(1 << 12));
#endif
/* flush the L1/L2 cache */
dcsw_op_all(DCCSW);
if (dram_info.dram_type == DDRC_LPDDR4) {
lpddr4_swffc(&dram_info, dev_fsp, target_freq, bypass_mode_supported);
dev_fsp = (~dev_fsp) & 0x1;
} else if (dram_info.dram_type == DDRC_DDR4) {
ddr4_swffc(&dram_info, target_freq);
}
wait_ddrc_hwffc_done = false;
wfe_done = 0;
dsb();
sev();
isb();
}
return 0;
}