diff --git a/plat/intel/soc/agilex/include/socfpga_plat_def.h b/plat/intel/soc/agilex/include/socfpga_plat_def.h index 9ef75986a..b03886f5e 100644 --- a/plat/intel/soc/agilex/include/socfpga_plat_def.h +++ b/plat/intel/soc/agilex/include/socfpga_plat_def.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2019-2022, ARM Limited and Contributors. All rights reserved. * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/intel/soc/agilex5/bl2_plat_setup.c b/plat/intel/soc/agilex5/bl2_plat_setup.c index 265ee5732..9f512601b 100644 --- a/plat/intel/soc/agilex5/bl2_plat_setup.c +++ b/plat/intel/soc/agilex5/bl2_plat_setup.c @@ -20,6 +20,7 @@ #include #include "agilex5_clock_manager.h" +#include "agilex5_ddr.h" #include "agilex5_memory_controller.h" #include "agilex5_mmc.h" #include "agilex5_pinmux.h" @@ -79,6 +80,10 @@ void bl2_el3_early_platform_setup(u_register_t x0, u_register_t x1, enable_nonsecure_access(); deassert_peripheral_reset(); + + /* DDR and IOSSM driver init */ + agilex5_ddr_init(&reverse_handoff_ptr); + if (combo_phy_init(&reverse_handoff_ptr) != 0) { ERROR("Combo Phy initialization failed\n"); } diff --git a/plat/intel/soc/agilex5/include/agilex5_ddr.h b/plat/intel/soc/agilex5/include/agilex5_ddr.h new file mode 100644 index 000000000..631e006ef --- /dev/null +++ b/plat/intel/soc/agilex5/include/agilex5_ddr.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef AGILEX5_DDR_H +#define AGILEX5_DDR_H + +#include +#include +#include +#include + +#include "socfpga_handoff.h" + +#define CONFIG_NR_DRAM_BANKS 1 + +typedef unsigned long long phys_addr_t; +typedef unsigned long long phys_size_t; +typedef phys_addr_t fdt_addr_t; + +/* DDR/RAM configuration */ +struct ddr_info { + phys_addr_t start; + phys_size_t size; +}; + +int agilex5_ddr_init(handoff *hoff_ptr); + +#endif /* AGILEX5_DDR_H */ diff --git a/plat/intel/soc/agilex5/include/agilex5_iossm_mailbox.h b/plat/intel/soc/agilex5/include/agilex5_iossm_mailbox.h new file mode 100644 index 000000000..1fd8ef6f2 --- /dev/null +++ b/plat/intel/soc/agilex5/include/agilex5_iossm_mailbox.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef AGILEX5_IOSSM_MAILBOX_H +#define AGILEX5_IOSSM_MAILBOX_H + +#include +#include +#include + +#include "lib/mmio.h" +#include "agilex5_ddr.h" + +#define TIMEOUT_5000MS 5000 +#define TIMEOUT TIMEOUT_5000MS +#define IOSSM_STATUS_CAL_SUCCESS BIT(0) +#define IOSSM_STATUS_CAL_FAIL BIT(1) +#define IOSSM_STATUS_CAL_BUSY BIT(2) +#define IOSSM_STATUS_COMMAND_RESPONSE_READY 1 +#define IOSSM_CMD_RESPONSE_STATUS_OFFSET 0x45C +#define IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x458 +#define IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x454 +#define IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x450 +#define IOSSM_CMD_REQ_OFFSET 0x43C +#define IOSSM_CMD_PARAM_0_OFFSET 0x438 +#define IOSSM_CMD_PARAM_1_OFFSET 0x434 +#define IOSSM_CMD_PARAM_2_OFFSET 0x430 +#define IOSSM_CMD_PARAM_3_OFFSET 0x42C +#define IOSSM_CMD_PARAM_4_OFFSET 0x428 +#define IOSSM_CMD_PARAM_5_OFFSET 0x424 +#define IOSSM_CMD_PARAM_6_OFFSET 0x420 +#define IOSSM_STATUS_OFFSET 0x400 +#define IOSSM_CMD_RESPONSE_DATA_SHORT_MASK GENMASK(31, 16) +#define IOSSM_CMD_RESPONSE_DATA_SHORT(data) (((data) & \ + IOSSM_CMD_RESPONSE_DATA_SHORT_MASK) >> 16) +#define MAX_IO96B_SUPPORTED 2 +#define MAX_MEM_INTERFACES_SUPPORTED 2 + +/* supported mailbox command type */ +enum iossm_mailbox_cmd_type { + CMD_NOP, + CMD_GET_SYS_INFO, + CMD_GET_MEM_INFO, + CMD_GET_MEM_CAL_INFO, + CMD_TRIG_CONTROLLER_OP, + CMD_TRIG_MEM_CAL_OP +}; + +/* supported mailbox command opcode */ +enum iossm_mailbox_cmd_opcode { + GET_MEM_INTF_INFO = 0x0001, + GET_MEM_TECHNOLOGY, + GET_MEMCLK_FREQ_KHZ, + GET_MEM_WIDTH_INFO, + ECC_ENABLE_SET = 0x0101, + ECC_ENABLE_STATUS, + ECC_INTERRUPT_STATUS, + ECC_INTERRUPT_ACK, + ECC_INTERRUPT_MASK, + ECC_WRITEBACK_ENABLE, + ECC_SCRUB_IN_PROGRESS_STATUS = 0x0201, + ECC_SCRUB_MODE_0_START, + ECC_SCRUB_MODE_1_START, + BIST_STANDARD_MODE_START = 0x0301, + BIST_RESULTS_STATUS, + BIST_MEM_INIT_START, + BIST_MEM_INIT_STATUS, + BIST_SET_DATA_PATTERN_UPPER, + BIST_SET_DATA_PATTERN_LOWER, + TRIG_MEM_CAL = 0x000a, + GET_MEM_CAL_STATUS +}; + +/* + * IOSSM mailbox required information + * + * @num_mem_interface: Number of memory interfaces instantiated + * @ip_type: IP type implemented on the IO96B + * @ip_instance_id: IP identifier for every IP instance implemented on the IO96B + */ +struct io96b_mb_ctrl { + uint32_t num_mem_interface; + uint32_t ip_type[2]; + uint32_t ip_instance_id[2]; +}; + +/* + * IOSSM mailbox response outputs + * + * @cmd_resp_status: Command Interface status + * @cmd_resp_data_*: More spaces for command response + */ +struct io96b_mb_resp { + uint32_t cmd_resp_status; + uint32_t cmd_resp_data_0; + uint32_t cmd_resp_data_1; + uint32_t cmd_resp_data_2; +}; + +/* + * IO96B instance specific information + * + * @size: Memory size + * @io96b_csr_addr: IO96B instance CSR address + * @cal_status: IO96B instance calibration status + * @mb_ctrl: IOSSM mailbox required information + */ +struct io96b_instance { + uint16_t size; + phys_addr_t io96b_csr_addr; + bool cal_status; + struct io96b_mb_ctrl mb_ctrl; +}; + +/* + * Overall IO96B instance(s) information + * + * @num_instance: Number of instance(s) assigned to HPS + * @overall_cal_status: Overall calibration status for all IO96B instance(s) + * @ddr_type: DDR memory type + * @ecc_status: ECC enable status (false = disabled, true = enabled) + * @overall_size: Total DDR memory size + * @io96b_0: IO96B 0 instance specific information + * @io96b_1: IO96B 1 instance specific information + */ +struct io96b_info { + uint8_t num_instance; + bool overall_cal_status; + const char *ddr_type; + bool ecc_status; + uint16_t overall_size; + struct io96b_instance io96b_0; + struct io96b_instance io96b_1; +}; + +int io96b_mb_req(phys_addr_t io96b_csr_addr, uint32_t ip_type, uint32_t instance_id, + uint32_t usr_cmd_type, uint32_t usr_cmd_opcode, uint32_t cmd_param_0, + uint32_t cmd_param_1, uint32_t cmd_param_2, uint32_t cmd_param_3, + uint32_t cmd_param_4, uint32_t cmd_param_5, uint32_t cmd_param_6, + uint32_t resp_data_len, struct io96b_mb_resp *resp); + +/* Supported IOSSM mailbox function */ +void io96b_mb_init(struct io96b_info *io96b_ctrl); +int io96b_cal_status(phys_addr_t addr); +void init_mem_cal(struct io96b_info *io96b_ctrl); +int trig_mem_cal(struct io96b_info *io96b_ctrl); +int get_mem_technology(struct io96b_info *io96b_ctrl); +int get_mem_width_info(struct io96b_info *io96b_ctrl); +int ecc_enable_status(struct io96b_info *io96b_ctrl); +int bist_mem_init_start(struct io96b_info *io96b_ctrl); + +#endif /* AGILEX5_IOSSM_MAILBOX_H */ diff --git a/plat/intel/soc/agilex5/platform.mk b/plat/intel/soc/agilex5/platform.mk index 90678e1b3..4bb90d5a5 100644 --- a/plat/intel/soc/agilex5/platform.mk +++ b/plat/intel/soc/agilex5/platform.mk @@ -58,14 +58,16 @@ BL2_SOURCES += \ lib/cpus/aarch64/cortex_a76.S \ plat/intel/soc/agilex5/soc/agilex5_clock_manager.c \ plat/intel/soc/agilex5/soc/agilex5_memory_controller.c \ - plat/intel/soc/agilex5/soc/agilex5_mmc.c \ + plat/intel/soc/agilex5/soc/agilex5_mmc.c \ plat/intel/soc/agilex5/soc/agilex5_pinmux.c \ plat/intel/soc/agilex5/soc/agilex5_power_manager.c \ + plat/intel/soc/agilex5/soc/agilex5_ddr.c \ + plat/intel/soc/agilex5/soc/agilex5_iossm_mailbox.c \ plat/intel/soc/common/bl2_plat_mem_params_desc.c \ plat/intel/soc/common/socfpga_image_load.c \ plat/intel/soc/common/socfpga_ros.c \ plat/intel/soc/common/socfpga_storage.c \ - plat/intel/soc/common/socfpga_vab.c \ + plat/intel/soc/common/socfpga_vab.c \ plat/intel/soc/common/soc/socfpga_emac.c \ plat/intel/soc/common/soc/socfpga_firewall.c \ plat/intel/soc/common/soc/socfpga_handoff.c \ diff --git a/plat/intel/soc/agilex5/soc/agilex5_ddr.c b/plat/intel/soc/agilex5/soc/agilex5_ddr.c new file mode 100644 index 000000000..ef2ae57f3 --- /dev/null +++ b/plat/intel/soc/agilex5/soc/agilex5_ddr.c @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#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: Hard code 1GB as of now, and DDR start and end address */ + config_ddr_size = 0x40000000; + ddr_info_set[0].start = 0x80000000; + ddr_info_set[0].size = 0x40000000; + + 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; +} diff --git a/plat/intel/soc/agilex5/soc/agilex5_iossm_mailbox.c b/plat/intel/soc/agilex5/soc/agilex5_iossm_mailbox.c new file mode 100644 index 000000000..c2ab0472c --- /dev/null +++ b/plat/intel/soc/agilex5/soc/agilex5_iossm_mailbox.c @@ -0,0 +1,811 @@ +/* + * Copyright (c) 2024, Altera Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include + +#include "agilex5_iossm_mailbox.h" + +/* supported DDR type list */ +static const char *ddr_type_list[7] = { + "DDR4", "DDR5", "DDR5_RDIMM", "LPDDR4", "LPDDR5", "QDRIV", "UNKNOWN" +}; + +static inline int wait_for_bit(const void *reg, + const uint32_t mask, + const bool set, + const unsigned int timeout_ms) +{ + uint32_t val; + uint32_t timeout_sec = (timeout_ms / 1000); + + while (timeout_sec > 0) { + val = mmio_read_32((uintptr_t)reg); + + INFO("IOSSM: timeout_sec %d, val %x\n", timeout_sec, val); + + if (!set) { + val = ~val; + } + + if ((val & mask) == mask) { + INFO("IOSSM: %s, success\n", __func__); + return 0; + } + + /* one second delay */ + mdelay(1000); + + timeout_sec--; + } + + ERROR("IOSSM: %s, failed, time out\n", __func__); + return -ETIMEDOUT; +} + +int io96b_mb_req(phys_addr_t io96b_csr_addr, uint32_t ip_type, uint32_t instance_id, + uint32_t usr_cmd_type, uint32_t usr_cmd_opcode, uint32_t cmd_param_0, + uint32_t cmd_param_1, uint32_t cmd_param_2, uint32_t cmd_param_3, + uint32_t cmd_param_4, uint32_t cmd_param_5, uint32_t cmd_param_6, + uint32_t resp_data_len, struct io96b_mb_resp *resp) +{ + int i; + int ret; + uint32_t cmd_req, cmd_resp; + + /* Initialized zeros for responses*/ + resp->cmd_resp_status = 0; + resp->cmd_resp_data_0 = 0; + resp->cmd_resp_data_1 = 0; + resp->cmd_resp_data_2 = 0; + + /* Ensure CMD_REQ is cleared before write any command request */ + ret = wait_for_bit((const void *)(io96b_csr_addr + IOSSM_CMD_REQ_OFFSET), + GENMASK(31, 0), 0, 10000); + + if (ret != 0) { + ERROR("%s: CMD_REQ not ready\n", __func__); + return -1; + } + + /* Write CMD_PARAM_* */ + for (i = 0; i < 6 ; i++) { + switch (i) { + case 0: + if (cmd_param_0 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_0_OFFSET, + cmd_param_0); + } + break; + case 1: + if (cmd_param_1 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_1_OFFSET, + cmd_param_1); + } + break; + case 2: + if (cmd_param_2 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_2_OFFSET, + cmd_param_2); + } + break; + case 3: + if (cmd_param_3 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_3_OFFSET, + cmd_param_3); + } + break; + case 4: + if (cmd_param_4 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_4_OFFSET, + cmd_param_4); + } + break; + case 5: + if (cmd_param_5 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_5_OFFSET, + cmd_param_5); + } + break; + case 6: + if (cmd_param_6 != 0) { + mmio_write_32(io96b_csr_addr + IOSSM_CMD_PARAM_6_OFFSET, + cmd_param_6); + } + break; + default: + ERROR("IOSSM: %s: Invalid command parameter\n", __func__); + } + } + + /* Write CMD_REQ (IP_TYPE, IP_INSTANCE_ID, CMD_TYPE and CMD_OPCODE) */ + cmd_req = (usr_cmd_opcode << 0) | (usr_cmd_type << 16) | (instance_id << 24) | + (ip_type << 29); + mmio_write_32(io96b_csr_addr + IOSSM_CMD_REQ_OFFSET, cmd_req); + INFO("IOSSM: %s: Write 0x%x to IOSSM_CMD_REQ_OFFSET 0x%llx\n", + __func__, cmd_req, io96b_csr_addr + IOSSM_CMD_REQ_OFFSET); + + /* Read CMD_RESPONSE_READY in CMD_RESPONSE_STATUS*/ + ret = wait_for_bit((const void *)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET), + IOSSM_STATUS_COMMAND_RESPONSE_READY, 1, 10000); + + if (ret != 0) { + ERROR("%s: CMD_RESPONSE ERROR:\n", __func__); + cmd_resp = (io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + ERROR("%s: STATUS_GENERAL_ERROR: 0x%x\n", __func__, (cmd_resp >> 1) & 0xF); + ERROR("%s: STATUS_CMD_RESPONSE_ERROR: 0x%x\n", __func__, (cmd_resp >> 5) & 0x7); + } + + /* read CMD_RESPONSE_STATUS*/ + resp->cmd_resp_status = mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + INFO("IOSSM: %s: CMD_RESPONSE_STATUS 0x%llx: 0x%x\n", + __func__, io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status); + + /* read CMD_RESPONSE_DATA_* */ + for (i = 0; i < resp_data_len; i++) { + switch (i) { + case 0: + resp->cmd_resp_data_0 = + mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET); + + break; + case 1: + resp->cmd_resp_data_1 = + mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET); + + break; + case 2: + resp->cmd_resp_data_2 = + mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET); + break; + default: + ERROR("%s: Invalid response data\n", __func__); + } + } + + resp->cmd_resp_status = mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + INFO("IOSSM: %s: CMD_RESPONSE_STATUS 0x%llx: 0x%x\n", + __func__, io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status); + + /* write CMD_RESPONSE_READY = 0 */ + mmio_clrbits_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, + IOSSM_STATUS_COMMAND_RESPONSE_READY); + + resp->cmd_resp_status = mmio_read_32(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + INFO("IOSSM: %s: CMD_RESPONSE_READY 0x%llx: 0x%x\n", + __func__, io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status); + + return 0; +} + +/* + * Initial function to be called to set memory interface IP type and instance ID + * IP type and instance ID need to be determined before sending mailbox command + */ +void io96b_mb_init(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + uint8_t ip_type_ret, instance_id_ret; + int i, j, k; + + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + /* Get memory interface IP type & instance ID (IP identifier) */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_GET_SYS_INFO, GET_MEM_INTF_INFO, 0, 0, + 0, 0, 0, 0, 0, 2, &usr_resp); + /* Retrieve number of memory interface(s) */ + io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface = + IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) & 0x3; + + /* Retrieve memory interface IP type and instance ID (IP identifier) */ + j = 0; + for (k = 0; k < MAX_MEM_INTERFACES_SUPPORTED; k++) { + switch (k) { + case 0: + ip_type_ret = (usr_resp.cmd_resp_data_0 >> 29) & 0x7; + instance_id_ret = (usr_resp.cmd_resp_data_0 >> 24) & 0x1F; + break; + case 1: + ip_type_ret = (usr_resp.cmd_resp_data_1 >> 29) & 0x7; + instance_id_ret = (usr_resp.cmd_resp_data_1 >> 24) & 0x1F; + break; + } + + if (ip_type_ret != 0) { + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j] = ip_type_ret; + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j] = + instance_id_ret; + j++; + } + } + break; + case 1: + /* Get memory interface IP type and instance ID (IP identifier) */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, CMD_GET_SYS_INFO, + GET_MEM_INTF_INFO, 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + + /* Retrieve number of memory interface(s) */ + io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface = + IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) & 0x3; + + /* Retrieve memory interface IP type and instance ID (IP identifier) */ + j = 0; + for (k = 0; k < MAX_MEM_INTERFACES_SUPPORTED; k++) { + switch (k) { + case 0: + ip_type_ret = (usr_resp.cmd_resp_data_0 >> 29) & 0x7; + instance_id_ret = (usr_resp.cmd_resp_data_0 >> 24) & 0x1F; + break; + case 1: + ip_type_ret = (usr_resp.cmd_resp_data_1 >> 29) & 0x7; + instance_id_ret = (usr_resp.cmd_resp_data_1 >> 24) & 0x1F; + break; + } + + if (ip_type_ret != 0) { + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j] = ip_type_ret; + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j] = + instance_id_ret; + j++; + } + } + break; + } + + } +} + +static inline void hang(void) +{ + ERROR("IOSSM: %s: system is going to die :(\n", __func__); + while (1) + ; +} + +int io96b_cal_status(phys_addr_t addr) +{ + int cal_busy_status, cal_success_status; + phys_addr_t status_addr = addr + IOSSM_STATUS_OFFSET; + + /* Ensure calibration busy status */ + cal_busy_status = wait_for_bit((const void *)status_addr, IOSSM_STATUS_CAL_BUSY, + false, 15000); + if (cal_busy_status != 0) { + ERROR("IOSSM: One or more EMIF instances are busy with calibration\n"); + return -EBUSY; + } + + /* Calibration success status check */ + NOTICE("IOSSM: Calibration success status check...\n"); + cal_success_status = wait_for_bit((const void *)status_addr, IOSSM_STATUS_CAL_SUCCESS, + true, 15000); + if (cal_success_status != 0) { + ERROR("IOSSM: One/more EMIF instances either failed to calibrate/not completed\n"); + return -EBUSY; + } + + NOTICE("IOSSM: All EMIF instances within the IO96 have calibrated successfully!\n"); + return 0; +} + +void init_mem_cal(struct io96b_info *io96b_ctrl) +{ + int count, i, ret; + + /* Initialize overall calibration status */ + io96b_ctrl->overall_cal_status = false; + + /* Check initial calibration status for the assigned IO96B */ + count = 0; + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + ret = io96b_cal_status(io96b_ctrl->io96b_0.io96b_csr_addr); + if (ret != 0) { + io96b_ctrl->io96b_0.cal_status = false; + ERROR("%s: Initial DDR calibration IO96B_0 failed %d\n", + __func__, ret); + break; + } + io96b_ctrl->io96b_0.cal_status = true; + INFO("IOSSM: %s: Initial DDR calibration IO96B_0 succeed\n", __func__); + count++; + break; + case 1: + ret = io96b_cal_status(io96b_ctrl->io96b_1.io96b_csr_addr); + if (ret != 0) { + io96b_ctrl->io96b_1.cal_status = false; + ERROR("%s: Initial DDR calibration IO96B_1 failed %d\n", + __func__, ret); + break; + } + io96b_ctrl->io96b_1.cal_status = true; + INFO("IOSSM: %s: Initial DDR calibration IO96B_1 succeed\n", __func__); + count++; + break; + } + } + + if (count == io96b_ctrl->num_instance) + io96b_ctrl->overall_cal_status = true; +} + +/* + * Trying 3 times re-calibration if initial calibration failed + */ +int trig_mem_cal(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + bool recal_success; + int i; + uint8_t cal_stat; + + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + if (!(io96b_ctrl->io96b_0.cal_status)) { + /* Get the memory calibration status for first memory interface */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, + 0, 0, 0, 0, 0, 0, 2, &usr_resp); + + recal_success = false; + + /* Re-calibration first memory interface with failed calibration */ + for (i = 0; i < 3; i++) { + cal_stat = usr_resp.cmd_resp_data_0 & GENMASK(2, 0); + if (cal_stat < 0x2) { + recal_success = true; + break; + } + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[0], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[0], + CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, + 0, 0, 0, 2, &usr_resp); + mdelay(1000); + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, + 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + } + + if (!recal_success) { + ERROR("%s: Error as SDRAM calibration failed\n", __func__); + hang(); + } + + /* Get the memory calibration status for second memory interface */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, 0, 0, + 0, 0, 0, 0, 2, &usr_resp); + + recal_success = false; + + /* Re-calibration second memory interface with failed calibration*/ + for (i = 0; i < 3; i++) { + cal_stat = usr_resp.cmd_resp_data_1 & GENMASK(2, 0); + if (cal_stat < 0x2) { + recal_success = true; + break; + } + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[1], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[1], + CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, + 0, 0, 0, 2, &usr_resp); + mdelay(1000); + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, + 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + } + + if (!recal_success) { + ERROR("IOSSMM: Error as SDRAM calibration failed\n"); + hang(); + } + + io96b_ctrl->io96b_0.cal_status = true; + } + break; + case 1: + if (!(io96b_ctrl->io96b_1.cal_status)) { + /* Get the memory calibration status for first memory interface */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, + 0, 0, 0, 0, 0, 0, 2, &usr_resp); + + recal_success = false; + + /* Re-calibration first memory interface with failed calibration */ + for (i = 0; i < 3; i++) { + cal_stat = usr_resp.cmd_resp_data_0 & GENMASK(2, 0); + if (cal_stat < 0x2) { + recal_success = true; + break; + } + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[0], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[0], + CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, + 0, 0, 0, 2, &usr_resp); + mdelay(1000); + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, + 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + } + + if (!recal_success) { + ERROR("IOSSM: Error as SDRAM calibration failed\n"); + hang(); + } + + /* Get the memory calibration status for second memory interface */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, 0, 0, + 0, 0, 0, 0, 2, &usr_resp); + + recal_success = false; + + /* Re-calibration second memory interface with failed calibration*/ + for (i = 0; i < 3; i++) { + cal_stat = usr_resp.cmd_resp_data_0 & GENMASK(2, 0); + if (cal_stat < 0x2) { + recal_success = true; + break; + } + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[1], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[1], + CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, + 0, 0, 0, 2, &usr_resp); + mdelay(1000); + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, 0, 0, + CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, + 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + } + + if (!recal_success) { + ERROR("IOSSM: Error as SDRAM calibration failed\n"); + hang(); + } + + io96b_ctrl->io96b_1.cal_status = true; + } + break; + } + } + + if (io96b_ctrl->io96b_0.cal_status && io96b_ctrl->io96b_1.cal_status) { + INFO("IOSSM: %s: Overall SDRAM calibration success\n", __func__); + io96b_ctrl->overall_cal_status = true; + } + + return 0; +} + +int get_mem_technology(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + int i, j; + uint8_t ddr_type_ret; + + /* Initialize ddr type */ + io96b_ctrl->ddr_type = ddr_type_list[6]; + + /* Get and ensure all memory interface(s) same DDR type */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + for (j = 0; j < io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_GET_MEM_INFO, GET_MEM_TECHNOLOGY, 0, 0, 0, 0, + 0, 0, 0, 0, &usr_resp); + + ddr_type_ret = + IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(2, 0); + + if (strcmp(io96b_ctrl->ddr_type, "UNKNOWN") == 0) + io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret]; + + if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) { + ERROR("IOSSM: Mismatch DDR type on IO96B_0\n"); + return -ENOEXEC; + } + } + break; + case 1: + for (j = 0; j < io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_GET_MEM_INFO, GET_MEM_TECHNOLOGY, 0, 0, 0, + 0, 0, 0, 0, 0, &usr_resp); + + ddr_type_ret = + IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(2, 0); + + if (strcmp(io96b_ctrl->ddr_type, "UNKNOWN") == 0) + io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret]; + + if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) { + ERROR("IOSSM: Mismatch DDR type on IO96B_1\n"); + return -ENOEXEC; + } + } + break; + } + } + + return 0; +} + +int get_mem_width_info(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + int i, j; + uint16_t memory_size = 0U; + uint16_t total_memory_size = 0U; + + /* Get all memory interface(s) total memory size on all instance(s) */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + memory_size = 0; + for (j = 0; j < io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_GET_MEM_INFO, GET_MEM_WIDTH_INFO, 0, 0, 0, + 0, 0, 0, 0, 2, &usr_resp); + + memory_size = memory_size + + (usr_resp.cmd_resp_data_1 & GENMASK(7, 0)); + } + + if (memory_size == 0U) { + ERROR("IOSSM: %s: Failed to get valid memory size\n", __func__); + return -ENOEXEC; + } + + io96b_ctrl->io96b_0.size = memory_size; + + break; + case 1: + memory_size = 0; + for (j = 0; j < io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_GET_MEM_INFO, GET_MEM_WIDTH_INFO, 0, 0, 0, + 0, 0, 0, 0, 2, &usr_resp); + + memory_size = memory_size + + (usr_resp.cmd_resp_data_1 & GENMASK(7, 0)); + } + + if (memory_size == 0U) { + ERROR("IOSSM: %s: Failed to get valid memory size\n", __func__); + return -ENOEXEC; + } + + io96b_ctrl->io96b_1.size = memory_size; + + break; + } + + total_memory_size = total_memory_size + memory_size; + } + + if (total_memory_size == 0U) { + ERROR("IOSSM: %s: Failed to get valid memory size\n", __func__); + return -ENOEXEC; + } + + io96b_ctrl->overall_size = total_memory_size; + + return 0; +} + +int ecc_enable_status(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + int i, j; + bool ecc_stat_set = false; + bool ecc_stat; + + /* Initialize ECC status */ + io96b_ctrl->ecc_status = false; + + /* Get and ensure all memory interface(s) same ECC status */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + for (j = 0; j < io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, ECC_ENABLE_STATUS, 0, 0, + 0, 0, 0, 0, 0, 0, &usr_resp); + + ecc_stat = ((IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(1, 0)) == 0 ? false : true); + + if (!ecc_stat_set) { + io96b_ctrl->ecc_status = ecc_stat; + ecc_stat_set = true; + } + + if (ecc_stat != io96b_ctrl->ecc_status) { + ERROR("IOSSM: %s: Mismatch DDR ECC status on IO96B_0\n", + __func__); + return -ENOEXEC; + } + } + break; + case 1: + for (j = 0; j < io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface; j++) { + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, ECC_ENABLE_STATUS, 0, 0, + 0, 0, 0, 0, 0, 0, &usr_resp); + + ecc_stat = ((IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(1, 0)) == 0 ? false : true); + + if (!ecc_stat_set) { + io96b_ctrl->ecc_status = ecc_stat; + ecc_stat_set = true; + } + + if (ecc_stat != io96b_ctrl->ecc_status) { + ERROR("%s: Mismatch DDR ECC status on IO96B_1\n" + , __func__); + return -ENOEXEC; + } + } + break; + } + } + return 0; +} + +int bist_mem_init_start(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + int i, j; + bool bist_start, bist_success; + uint32_t read_count; + uint32_t read_interval_ms; + + /* Full memory initialization BIST performed on all memory interface(s) */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + for (j = 0; j < io96b_ctrl->io96b_0.mb_ctrl.num_mem_interface; j++) { + bist_start = false; + bist_success = false; + read_interval_ms = 500U; + + /* Start memory initialization BIST on full memory address */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START, 0x40, + 0, 0, 0, 0, 0, 0, 0, &usr_resp); + + bist_start = + (IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & 1); + + if (!bist_start) { + ERROR("IOSSM: %s: Failed to initialized memory on IO96B_0\n" + , __func__); + ERROR("IOSSM: %s: BIST_MEM_INIT_START Error code 0x%x\n", + __func__, + (IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(2, 1)) > 0x1); + return -ENOEXEC; + } + + /* Polling for the initiated memory initialization BIST status */ + read_count = read_interval_ms / TIMEOUT; + while (!bist_success) { + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr, + io96b_ctrl->io96b_0.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_0.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_STATUS, + 0, 0, 0, 0, 0, 0, 0, 0, &usr_resp); + + bist_success = (IOSSM_CMD_RESPONSE_DATA_SHORT + (usr_resp.cmd_resp_status) & 1); + + if ((!bist_success) && (read_count == 0U)) { + ERROR("IOSSM: %s: Timeout init memory on IO96B_0\n" + , __func__); + ERROR("IOSSM: %s: BIST_MEM_INIT_STATUS Err code%x\n" + , __func__, (IOSSM_CMD_RESPONSE_DATA_SHORT + (usr_resp.cmd_resp_status) + & GENMASK(2, 1)) > 0x1); + return -ETIMEDOUT; + } + read_count--; + mdelay(read_interval_ms); + } + } + + NOTICE("IOSSM: %s: Memory initialized successfully on IO96B_0\n", __func__); + break; + + case 1: + for (j = 0; j < io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface; j++) { + bist_start = false; + bist_success = false; + read_interval_ms = 500U; + + /* Start memory initialization BIST on full memory address */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START, 0x40, + 0, 0, 0, 0, 0, 0, 0, &usr_resp); + + bist_start = + (IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & 1); + + if (!bist_start) { + ERROR("IOSSM: %s: Failed to initialized memory on IO96B_1\n" + , __func__); + ERROR("IOSSM: %s: BIST_MEM_INIT_START Error code 0x%x\n", + __func__, + (IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & GENMASK(2, 1)) > 0x1); + return -ENOEXEC; + } + + /* Polling for the initiated memory initialization BIST status */ + read_count = read_interval_ms / TIMEOUT; + while (!bist_success) { + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr, + io96b_ctrl->io96b_1.mb_ctrl.ip_type[j], + io96b_ctrl->io96b_1.mb_ctrl.ip_instance_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_STATUS, + 0, 0, 0, 0, 0, 0, 0, 0, &usr_resp); + + bist_success = (IOSSM_CMD_RESPONSE_DATA_SHORT + (usr_resp.cmd_resp_status) & 1); + + if ((!bist_success) && (read_count == 0U)) { + ERROR("IOSSM: %s: Timeout init memory on IO96B_1\n" + , __func__); + ERROR("IOSSM: %s: BIST_MEM_INIT_STATUS ErrCode %x\n" + , __func__, (IOSSM_CMD_RESPONSE_DATA_SHORT + (usr_resp.cmd_resp_status) + & GENMASK(2, 1)) > 0x1); + return -ETIMEDOUT; + } + read_count--; + mdelay(read_interval_ms); + } + } + + NOTICE("IOSSM: %s: Memory initialized successfully on IO96B_1\n", __func__); + break; + } + } + return 0; +} diff --git a/plat/intel/soc/common/include/socfpga_handoff.h b/plat/intel/soc/common/include/socfpga_handoff.h index b2913c7f8..d001887d8 100644 --- a/plat/intel/soc/common/include/socfpga_handoff.h +++ b/plat/intel/soc/common/include/socfpga_handoff.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. + * Copyright (c) 2024, Altera Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -179,7 +180,7 @@ typedef struct handoff_t { uint32_t ddr_magic; uint32_t ddr_length; uint32_t _pad_0x1C_0x20[2]; - uint32_t ddr_array[4]; /* offset, value */ + uint32_t ddr_config; /* BIT[0]-Dual Port. BIT[1]-Dual EMIF */ #endif } handoff;