/* * Copyright 2018 NXP * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include 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; }