io96b_mb_init() starts issuing IOSSM mailbox accesses (mailbox header, memory interface info, etc.) without first confirming that the IOSSM firmware has finished initializing its mailbox. If the mailbox is not yet ready, those accesses operate on an uninitialized interface and can return bogus data or hang with no useful diagnostic.
The MAILBOX_HEADER register exposes an MB_READY bit (bit 31) that the IOSSM firmware sets once the mailbox is initialized and ready to accept commands. When enabled, poll this bit for every assigned IO96B instance before any mailbox traffic, with a 6s timeout, and hang with a clear message if it never becomes ready. Some IOSSM firmware revisions never assert MB_READY, so an unconditional wait times out and hangs DDR init. Gate the poll behind CONFIG_IO96B_MB_READY (default n). Enable only when the IOSSM firmware sets MB_READY. In the normal boot flow with compatible firmware the check is effectively free: io96b_mb_init() runs after init_mem_cal(), by which point the firmware is already up and MB_READY is observed set on the first read (0 msec). The poll only adds value as a guard against a missing or wedged mailbox. Tested-on: SoCFPGA Agilex5 & Agilex7m SoCDK hardware. Signed-off-by: Chen Huei Lok <[email protected]> --- drivers/ddr/altera/Kconfig | 11 +++++++ drivers/ddr/altera/iossm_mailbox.c | 49 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/drivers/ddr/altera/Kconfig b/drivers/ddr/altera/Kconfig index 615e0421abf..d5168c8aaaf 100644 --- a/drivers/ddr/altera/Kconfig +++ b/drivers/ddr/altera/Kconfig @@ -6,3 +6,14 @@ config SPL_ALTERA_SDRAM select SPL_RAM if ARCH_SOCFPGA_GEN5 || ARCH_SOCFPGA_SOC64 help Enable DDR SDRAM controller for the SoCFPGA devices. + +config IO96B_MB_READY + bool "Wait for IO96B mailbox MB_READY before commands" + depends on SPL_ALTERA_SDRAM + depends on ARCH_SOCFPGA_AGILEX5 || ARCH_SOCFPGA_AGILEX7M + help + Poll MAILBOX_HEADER MB_READY during io96b_mb_init() + initialization. Disabled by default because some IOSSM firmware + revisions never assert this bit, which would cause the wait + to time out and hang DDR init. Enable only when the IOSSM + firmware sets MB_READY. diff --git a/drivers/ddr/altera/iossm_mailbox.c b/drivers/ddr/altera/iossm_mailbox.c index 3156cb9d4b6..92eecfa8dfa 100644 --- a/drivers/ddr/altera/iossm_mailbox.c +++ b/drivers/ddr/altera/iossm_mailbox.c @@ -15,6 +15,7 @@ #define TIMEOUT_120000MS 120000 #define TIMEOUT_60000MS 60000 +#define TIMEOUT_6000MS 6000 #define TIMEOUT TIMEOUT_120000MS #define IOSSM_STATUS_CAL_SUCCESS BIT(0) #define IOSSM_STATUS_CAL_FAIL BIT(1) @@ -40,6 +41,8 @@ #define IOSSM_STATUS_GENERAL_ERROR(n) FIELD_GET(IOSSM_STATUS_GENERAL_ERROR_MASK, n) #define IOSSM_MAILBOX_SPEC_VERSION_MASK GENMASK(2, 0) #define IOSSM_MAILBOX_SPEC_VERSION(n) FIELD_GET(IOSSM_MAILBOX_SPEC_VERSION_MASK, n) +/* MAILBOX_HEADER[31]: mailbox initialized and ready for mailbox commands */ +#define IOSSM_MAILBOX_HEADER_MB_READY_MASK BIT(31) /* Offset of Mailbox Read-only Registers */ #define IOSSM_MAILBOX_HEADER_OFFSET 0x0 @@ -385,6 +388,45 @@ err: return ret; } +/** + * wait_for_io96b_mb_ready() - wait for the IOSSM mailbox to be ready + * @io96b_ctrl: IO96B control context describing the assigned instances + * + * Poll the MAILBOX_HEADER MB_READY bit for every assigned IO96B instance until + * the IOSSM firmware reports its mailbox is initialized and ready to accept + * commands. When CONFIG_IO96B_MB_READY is enabled, this must pass before any + * mailbox traffic in io96b_mb_init(). + * + * Return: 0 if all instances become ready within the timeout, otherwise the + * negative error code from wait_for_bit_le32() for the first instance that + * times out. + */ +static int __maybe_unused wait_for_io96b_mb_ready(struct io96b_info *io96b_ctrl) +{ + unsigned long start; + phys_addr_t base; + int i, ret; + + for (i = 0; i < io96b_ctrl->num_instance; i++) { + base = io96b_ctrl->io96b[i].io96b_csr_addr; + start = get_timer(0); + ret = wait_for_bit_le32((const void *)(base + + IOSSM_MAILBOX_HEADER_OFFSET), + IOSSM_MAILBOX_HEADER_MB_READY_MASK, + true, TIMEOUT_6000MS, false); + if (ret) { + printf("%s: mailbox ready timeout on IO96B_%d\n", + __func__, i); + return ret; + } + + debug("%s: IOSSM mailbox ready on IO96B_%d after %lu msec\n", + __func__, i, get_timer(start)); + } + + return 0; +} + static bool is_mailbox_spec_compatible(struct io96b_info *io96b_ctrl) { u32 mailbox_header; @@ -411,6 +453,13 @@ void io96b_mb_init(struct io96b_info *io96b_ctrl) int i, j; u32 mem_intf_info_0, mem_intf_info_1; + if (IS_ENABLED(CONFIG_IO96B_MB_READY)) { + if (wait_for_io96b_mb_ready(io96b_ctrl)) { + printf("DDR: IOSSM mailbox not ready\n"); + hang(); + } + } + if (!is_mailbox_spec_compatible(io96b_ctrl)) { printf("DDR: Failed to get compatible mailbox version\n"); hang(); -- 2.43.7
