blob: 0d60324c6a6a47e65dccae7ad8e4628feb358cbd [file] [log] [blame]
/*
* Copyright (c) 2024-2025, Altera Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stddef.h>
#include <stdlib.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include "lib/mmio.h"
#include "agilex5_ddr.h"
#include "agilex5_iossm_mailbox.h"
/*
* TODO: We need to leverage the legacy products DDR drivers and consider
* the upcoming products like KM and then come up with common source code/driver
* architecture to address all the products in one view.
*/
#define SYSMGR_BS_COLD3_DDR_RESET_TYPE_MASK GENMASK(31, 29)
#define SYSMGR_BS_COLD3_DDR_RESET_TYPE_SHIFT 29
#define SYSMGR_BS_COLD3_DDR_DBE_MASK (1 << 1)
#define SYSMGR_BS_COLD3_OCRAM_DBE_MASK (1)
#define SYSMGR_BS_POR0_DDR_PROGRESS_MASK (1)
/* MPFE NOC registers */
#define F2SDRAM_SIDEBAND_FLAGOUTSET0 0x50
#define F2SDRAM_SIDEBAND_FLAGOUTCLR0 0x54
#define F2SDRAM_SIDEBAND_FLAGOUTSTATUS0 0x58
#define SOCFPGA_F2SDRAM_MGR_ADDRESS 0x18001000
#define SOCFPGA_MPFE_SCR_IO96B0 0x18000D00
#define SOCFPGA_MPFE_SCR_IO96B1 0x18000D04
#define SOCFPGA_MPFE_NOC_SCHED_CSR 0x18000D08
#define SIDEBANDMGR_FLAGOUTSET0_REG (SOCFPGA_F2SDRAM_MGR_ADDRESS \
+ F2SDRAM_SIDEBAND_FLAGOUTSET0)
#define SIDEBANDMGR_FLAGOUTSTATUS0_REG (SOCFPGA_F2SDRAM_MGR_ADDRESS \
+F2SDRAM_SIDEBAND_FLAGOUTSTATUS0)
#define SIDEBANDMGR_FLAGOUTCLR0_REG (SOCFPGA_F2SDRAM_MGR_ADDRESS \
+ F2SDRAM_SIDEBAND_FLAGOUTCLR0)
#define SZ_8 0x00000008
/* Firewall MPU DDR SCR registers */
#define FW_MPU_DDR_SCR_EN 0x00
#define FW_MPU_DDR_SCR_EN_SET 0x04
#define FW_MPU_DDR_SCR_MPUREGION0ADDR_BASE 0x10
#define FW_MPU_DDR_SCR_MPUREGION0ADDR_BASEEXT 0x14
#define FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMIT 0x18
#define FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMITEXT 0x1c
#define SOCFPGA_FW_DDR_CCU_DMI0_ADDRESS 0x18000800
#define SOCFPGA_FW_DDR_CCU_DMI1_ADDRESS 0x18000A00
#define SOCFPGA_FW_TBU2NOC_ADDRESS 0x18000C00
#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASE 0x90
#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASEEXT 0x94
#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMIT 0x98
#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMITEXT 0x9c
#define FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMITEXT_FIELD 0xff
/* Firewall F2SDRAM DDR SCR registers */
#define FW_F2SDRAM_DDR_SCR_EN 0x00
#define FW_F2SDRAM_DDR_SCR_EN_SET 0x04
#define FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASE 0x10
#define FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASEEXT 0x14
#define FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMIT 0x18
#define FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMITEXT 0x1c
#define FW_MPU_DDR_SCR_WRITEL(data, reg) \
do { \
mmio_write_32(SOCFPGA_FW_DDR_CCU_DMI0_ADDRESS + (reg), data); \
mmio_write_32(SOCFPGA_FW_DDR_CCU_DMI1_ADDRESS + (reg), data); \
} while (0)
#define FW_F2SDRAM_DDR_SCR_WRITEL(data, reg) \
mmio_write_32(SOCFPGA_FW_TBU2NOC_ADDRESS + (reg), data)
/* DDR banks info set */
static struct ddr_info ddr_info_set[CONFIG_NR_DRAM_BANKS];
/* Reset type */
enum reset_type {
POR_RESET,
WARM_RESET,
COLD_RESET,
NCONFIG,
JTAG_CONFIG,
RSU_RECONFIG
};
/* Get reset type by reading boot scratch register cold3 */
static inline enum reset_type get_reset_type(uint32_t sys_reg)
{
return ((sys_reg & SYSMGR_BS_COLD3_DDR_RESET_TYPE_MASK) >>
SYSMGR_BS_COLD3_DDR_RESET_TYPE_SHIFT);
}
/* DDR hang check before the reset */
static inline bool is_ddr_init_hang(void)
{
uint32_t sys_reg = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_0));
if ((sys_reg & SYSMGR_BS_POR0_DDR_PROGRESS_MASK) != 0) {
INFO("DDR: Hang before this reset\n");
return true;
}
return false;
}
/* Set the DDR init progress bit */
static inline void ddr_init_inprogress(bool start)
{
if (start) {
mmio_setbits_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_0),
SYSMGR_BS_POR0_DDR_PROGRESS_MASK);
} else {
mmio_clrbits_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_POR_0),
SYSMGR_BS_POR0_DDR_PROGRESS_MASK);
}
}
/* Configure the IO96B CSRs address based on the handoff data */
static void config_io96b_csr_addr(bool is_dualemif, struct io96b_info *io96b_ctrl)
{
if (is_dualemif)
io96b_ctrl->num_instance = 2;
else
io96b_ctrl->num_instance = 1;
/* Assign IO96B CSR base address if it is valid */
for (int i = 0; i < io96b_ctrl->num_instance; i++) {
switch (i) {
case 0:
io96b_ctrl->io96b_0.io96b_csr_addr = 0x18400000;
INFO("DDR: IO96B0 0x%llx CSR enabled\n",
io96b_ctrl->io96b_0.io96b_csr_addr);
break;
case 1:
io96b_ctrl->io96b_1.io96b_csr_addr = 0x18800000;
INFO("DDR: IO96B1 0x%llx CSR enabled\n",
io96b_ctrl->io96b_1.io96b_csr_addr);
break;
default:
ERROR("%s: Invalid IO96B CSR\n", __func__);
} /* switch */
} /* for */
}
static inline bool hps_ocram_dbe_status(void)
{
uint32_t sys_reg = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_3));
if ((sys_reg & SYSMGR_BS_COLD3_OCRAM_DBE_MASK) != 0)
return true;
return false;
}
static inline bool ddr_ecc_dbe_status(void)
{
uint32_t sys_reg = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_3));
if ((sys_reg & SYSMGR_BS_COLD3_DDR_DBE_MASK) != 0)
return true;
return false;
}
static void sdram_set_firewall_non_f2sdram(void)
{
uint32_t i;
phys_size_t value;
uint32_t lower, upper;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
if (ddr_info_set[i].size == 0) {
continue;
}
value = ddr_info_set[i].start;
/*
* Keep first 1MB of SDRAM memory region as secure region when
* using ATF flow, where the ATF code is located.
*/
value += SZ_1M;
/* Setting non-secure MPU region base and base extended */
lower = LO(value);
upper = HI(value);
FW_MPU_DDR_SCR_WRITEL(lower,
FW_MPU_DDR_SCR_MPUREGION0ADDR_BASE +
(i * 4 * sizeof(uint32_t)));
FW_MPU_DDR_SCR_WRITEL(upper & 0xff,
FW_MPU_DDR_SCR_MPUREGION0ADDR_BASEEXT +
(i * 4 * sizeof(uint32_t)));
/* Setting non-secure Non-MPU region base and base extended */
FW_MPU_DDR_SCR_WRITEL(lower,
FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASE +
(i * 4 * sizeof(uint32_t)));
FW_MPU_DDR_SCR_WRITEL(upper & 0xff,
FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASEEXT +
(i * 4 * sizeof(uint32_t)));
/* Setting non-secure MPU limit and limit extended */
value = ddr_info_set[i].start + ddr_info_set[i].size - 1;
lower = LO(value);
upper = HI(value);
FW_MPU_DDR_SCR_WRITEL(lower,
FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMIT +
(i * 4 * sizeof(uint32_t)));
FW_MPU_DDR_SCR_WRITEL(upper & 0xff,
FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMITEXT +
(i * 4 * sizeof(uint32_t)));
/* Setting non-secure Non-MPU limit and limit extended */
FW_MPU_DDR_SCR_WRITEL(lower,
FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMIT +
(i * 4 * sizeof(uint32_t)));
FW_MPU_DDR_SCR_WRITEL(upper & 0xff,
FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMITEXT +
(i * 4 * sizeof(uint32_t)));
FW_MPU_DDR_SCR_WRITEL(BIT(i) | BIT(i + 8),
FW_MPU_DDR_SCR_EN_SET);
}
}
static void sdram_set_firewall_f2sdram(void)
{
uint32_t i;
phys_size_t value;
uint32_t lower, upper;
for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
if (ddr_info_set[i].size == 0) {
continue;
}
value = ddr_info_set[i].start;
/* Keep first 1MB of SDRAM memory region as secure region when
* using ATF flow, where the ATF code is located.
*/
value += SZ_1M;
/* Setting base and base extended */
lower = LO(value);
upper = HI(value);
FW_F2SDRAM_DDR_SCR_WRITEL(lower,
FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASE +
(i * 4 * sizeof(uint32_t)));
FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff,
FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASEEXT +
(i * 4 * sizeof(uint32_t)));
/* Setting limit and limit extended */
value = ddr_info_set[i].start + ddr_info_set[i].size - 1;
lower = LO(value);
upper = HI(value);
FW_F2SDRAM_DDR_SCR_WRITEL(lower,
FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMIT +
(i * 4 * sizeof(uint32_t)));
FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff,
FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMITEXT +
(i * 4 * sizeof(uint32_t)));
FW_F2SDRAM_DDR_SCR_WRITEL(BIT(i), FW_F2SDRAM_DDR_SCR_EN_SET);
}
}
static void sdram_set_firewall(void)
{
sdram_set_firewall_non_f2sdram();
sdram_set_firewall_f2sdram();
}
/*
* Agilex5 DDR/IOSSM controller initialization routine
*/
int agilex5_ddr_init(handoff *hoff_ptr)
{
int ret;
bool full_mem_init = false;
phys_size_t hw_ddr_size;
phys_size_t config_ddr_size;
struct io96b_info io96b_ctrl;
enum reset_type reset_t = get_reset_type(mmio_read_32(SOCFPGA_SYSMGR(
BOOT_SCRATCH_COLD_3)));
bool is_dualport = hoff_ptr->ddr_config & BIT(0);
bool is_dualemif = hoff_ptr->ddr_config & BIT(1);
NOTICE("DDR: Reset type is '%s'\n",
(reset_t == POR_RESET ? "Power-On" : (reset_t == COLD_RESET ? "Cold" : "Warm")));
/* DDR initialization progress status tracking */
bool is_ddr_hang_bfr_rst = is_ddr_init_hang();
/* Set the DDR initialization progress */
ddr_init_inprogress(true);
/* Configure the IO96B CSR address based on the handoff data */
config_io96b_csr_addr(is_dualemif, &io96b_ctrl);
/* Configuring MPFE sideband manager registers */
/* Dual port setting */
if (is_dualport)
mmio_setbits_32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(4));
/* Dual EMIF setting */
if (is_dualemif) {
/* Set mpfe_lite_active in the system manager */
/* TODO: recheck on the bit value?? */
mmio_setbits_32(SOCFPGA_SYSMGR(MPFE_CONFIG), BIT(8));
mmio_setbits_32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(5));
}
if (is_dualport || is_dualemif)
INFO("DDR: SIDEBANDMGR_FLAGOUTSTATUS0: 0x%x\n",
mmio_read_32(SIDEBANDMGR_FLAGOUTSTATUS0_REG));
/* Ensure calibration status passing */
init_mem_cal(&io96b_ctrl);
/* Initiate IOSSM mailbox */
io96b_mb_init(&io96b_ctrl);
/* Need to trigger re-calibration for DDR DBE */
if (ddr_ecc_dbe_status()) {
io96b_ctrl.io96b_0.cal_status = false;
io96b_ctrl.io96b_1.cal_status = false;
io96b_ctrl.overall_cal_status = io96b_ctrl.io96b_0.cal_status ||
io96b_ctrl.io96b_1.cal_status;
}
/* Trigger re-calibration if calibration failed */
if (!(io96b_ctrl.overall_cal_status)) {
NOTICE("DDR: Re-calibration in progress...\n");
trig_mem_cal(&io96b_ctrl);
}
NOTICE("DDR: Calibration success\n");
/* DDR type, DDR size and ECC status) */
ret = get_mem_technology(&io96b_ctrl);
if (ret != 0) {
ERROR("DDR: Failed to get DDR type\n");
return ret;
}
ret = get_mem_width_info(&io96b_ctrl);
if (ret != 0) {
ERROR("DDR: Failed to get DDR size\n");
return ret;
}
/* DDR size queried from the IOSSM controller */
hw_ddr_size = (phys_size_t)io96b_ctrl.overall_size * SZ_1G / SZ_8;
/* TODO: To update config_ddr_size by using FDT in the future. */
config_ddr_size = 0x80000000;
ddr_info_set[0].start = DRAM_BASE;
ddr_info_set[0].size = hw_ddr_size;
if (config_ddr_size != hw_ddr_size) {
WARN("DDR: DDR size configured is (%lld MiB)\n", config_ddr_size >> 20);
WARN("DDR: Mismatch with hardware size (%lld MiB).\n", hw_ddr_size >> 20);
}
if (config_ddr_size > hw_ddr_size) {
ERROR("DDR: Confgured DDR size is greater than the hardware size - HANG!!!\n");
while (1)
;
}
ret = ecc_enable_status(&io96b_ctrl);
if (ret != 0) {
ERROR("DDR: Failed to get DDR ECC status\n");
return ret;
}
/*
* HPS cold or warm reset? If yes, skip full memory initialization if
* ECC is enabled to preserve memory content.
*/
if (io96b_ctrl.ecc_status != 0) {
full_mem_init = hps_ocram_dbe_status() | ddr_ecc_dbe_status() |
is_ddr_hang_bfr_rst;
if ((full_mem_init == true) || (reset_t == WARM_RESET ||
reset_t == COLD_RESET) == 0) {
ret = bist_mem_init_start(&io96b_ctrl);
if (ret != 0) {
ERROR("DDR: Failed to fully initialize DDR memory\n");
return ret;
}
}
INFO("DDR: ECC initialized successfully\n");
}
sdram_set_firewall();
/*
* Firewall setting for MPFE CSRs, allow both secure and non-secure
* transactions.
*/
/* IO96B0_reg */
mmio_setbits_32(SOCFPGA_MPFE_SCR_IO96B0, BIT(0));
/* IO96B1_reg */
mmio_setbits_32(SOCFPGA_MPFE_SCR_IO96B1, BIT(0));
/* noc_scheduler_csr */
mmio_setbits_32(SOCFPGA_MPFE_NOC_SCHED_CSR, BIT(0));
INFO("DDR: firewall init done\n");
/* Ending DDR driver initialization success tracking */
ddr_init_inprogress(false);
NOTICE("###DDR:init success###\n");
return 0;
}