feat(intel): update Agilex5 DDR and IOSSM driver

DDR and IOSSM driver code for Agilex5 platform,
initialize the DDR/IOSSM in BL2 EL3 early flow.

Change-Id: I3e4205171d9356190b60498cae322318520bb1c2
Signed-off-by: Girisha Dengi <girisha.dengi@intel.com>
Signed-off-by: Sieu Mun Tang <sieu.mun.tang@intel.com>
This commit is contained in:
Sieu Mun Tang 2024-08-26 22:51:16 +08:00 committed by Tang Sieu Mun
parent 5765e0c95a
commit ce21a1a909
8 changed files with 1443 additions and 3 deletions

View file

@ -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
*/

View file

@ -20,6 +20,7 @@
#include <lib/xlat_tables/xlat_tables_v2.h>
#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");
}

View file

@ -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 <stdint.h>
#include <stdio.h>
#include <string.h>
#include <lib/utils_def.h>
#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 */

View file

@ -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 <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#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 */

View file

@ -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 \

View file

@ -0,0 +1,434 @@
/*
* Copyright (c) 2024, 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: 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;
}

View file

@ -0,0 +1,811 @@
/*
* Copyright (c) 2024, Altera Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#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;
}

View file

@ -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;