From patchwork Sun Apr 23 18:11:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: sin.hui.kho@intel.com X-Patchwork-Id: 1772489 X-Patchwork-Delegate: marek.vasut@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=YDfiM7mD; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Q4GZx665Lz23s0 for ; Mon, 24 Apr 2023 04:12:57 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 6F39A860A4; Sun, 23 Apr 2023 20:12:03 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="YDfiM7mD"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 4DCE085AA1; Sun, 23 Apr 2023 20:11:58 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.2 Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 85B718575B for ; Sun, 23 Apr 2023 20:11:50 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=sin.hui.kho@intel.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1682273510; x=1713809510; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=1xQMHtA979HbQnbK1j2hySfd8WTVwJJ5xfTeK1IcARQ=; b=YDfiM7mDqubrEJNyI9FPp4p0LsuJVtky00qx78Auz48sfZXDx3r/ZasX ovtSSpgKtjLGi8/frDXeE7PPsl9F5DplEL+ai3rFdJQKneU0BhG1wer7K TRBKj/0sqy1Jg0whXKYo+QAwZ17Ke7H2kUIrts15I1W8MlrRceDN5oeGS 6AGQ6KSDvRsv1/uyoSJ+/Bn5EBg1V2GFHXx9Wo3g96Y5pxRoKTmwYIFSs Q769fMY90xuASKP1mZIVF/2r7E0Ris9tyAR2UVI1UZ1RsOnUeDZJZWgXI I9Iy3D0LbXuVtwPw6TGEqpi5I8a8hYNwF47szbn5AAwxSO6ymOAwHqAmO g==; X-IronPort-AV: E=McAfee;i="6600,9927,10689"; a="374248749" X-IronPort-AV: E=Sophos;i="5.99,220,1677571200"; d="scan'208";a="374248749" Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Apr 2023 11:11:49 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10689"; a="695507848" X-IronPort-AV: E=Sophos;i="5.99,220,1677571200"; d="scan'208";a="695507848" Received: from pglc00257.png.intel.com ([10.221.233.180]) by fmsmga007.fm.intel.com with ESMTP; 23 Apr 2023 11:11:46 -0700 From: sin.hui.kho@intel.com To: u-boot@lists.denx.de Cc: Marek Vasut , Simon Goldschmidt , Tien Fong Chee , Sin Hui Kho , Simon Glass , Stefan Roese , Dinesh Maniyam , Jit Loon Lim , Teik Heng , Kok Kiang Subject: [PATCH v1 5/5] ddr: altera: Add IOSSM mailbox support for DDR driver Date: Mon, 24 Apr 2023 02:11:24 +0800 Message-Id: <20230423181124.28077-6-sin.hui.kho@intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230423181124.28077-1-sin.hui.kho@intel.com> References: <20230423181124.28077-1-sin.hui.kho@intel.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean From: Sin Hui Kho Add IOSSM mailbox support for DDR driver to access memory interface implemented on an IO96B instance. Signed-off-by: Sin Hui Kho --- drivers/ddr/altera/Makefile | 2 +- drivers/ddr/altera/iossm_mailbox.c | 847 +++++++++++++++++++++++++++++ drivers/ddr/altera/iossm_mailbox.h | 142 +++++ 3 files changed, 990 insertions(+), 1 deletion(-) create mode 100644 drivers/ddr/altera/iossm_mailbox.c create mode 100644 drivers/ddr/altera/iossm_mailbox.h diff --git a/drivers/ddr/altera/Makefile b/drivers/ddr/altera/Makefile index 555357d669..59938168b5 100644 --- a/drivers/ddr/altera/Makefile +++ b/drivers/ddr/altera/Makefile @@ -12,5 +12,5 @@ obj-$(CONFIG_TARGET_SOCFPGA_ARRIA10) += sdram_arria10.o obj-$(CONFIG_TARGET_SOCFPGA_STRATIX10) += sdram_soc64.o sdram_s10.o obj-$(CONFIG_TARGET_SOCFPGA_AGILEX) += sdram_soc64.o sdram_agilex.o obj-$(CONFIG_TARGET_SOCFPGA_N5X) += sdram_soc64.o sdram_n5x.o -obj-$(CONFIG_TARGET_SOCFPGA_AGILEX7) += sdram_soc64.o sdram_agilex7.o +obj-$(CONFIG_TARGET_SOCFPGA_AGILEX7) += sdram_soc64.o sdram_agilex7.o iossm_mailbox.o endif diff --git a/drivers/ddr/altera/iossm_mailbox.c b/drivers/ddr/altera/iossm_mailbox.c new file mode 100644 index 0000000000..80aa2fa533 --- /dev/null +++ b/drivers/ddr/altera/iossm_mailbox.c @@ -0,0 +1,847 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Intel Corporation + * + */ + +#include +#include +#include +#include "iossm_mailbox.h" +#include + +/* supported DDR type list */ +static const char *ddr_type_list[7] = { + "DDR4", "DDR5", "DDR5_RDIMM", "LPDDR4", "LPDDR5", "QDRIV", "UNKNOWN" +}; + +/* Mailbox request function + * This function will send the request to IOSSM mailbox and wait for response return + * + * @io96b_csr_addr: CSR address for the target IO96B + * @ip_type: IP type for the specified memory interface + * @instance_id: IP instance ID for the specified memory interface + * @usr_cmd_type: User desire IOSSM mailbox command type + * @usr_cmd_opcode: User desire IOSSM mailbox command opcode + * @cmd_param_*: Parameters (if applicable) for the requested IOSSM mailbox command + * @resp_data_len: User desire extra response data fields other than + * CMD_RESPONSE_DATA_SHORT field on CMD_RESPONSE_STATUS + * @resp: Structure contain responses returned from the requested IOSSM + * mailbox command + */ +int io96b_mb_req(phys_addr_t io96b_csr_addr, u32 ip_type, u32 instance_id + , u32 usr_cmd_type, u32 usr_cmd_opcode, u32 cmd_param_0 + , u32 cmd_param_1, u32 cmd_param_2, u32 cmd_param_3 + , u32 cmd_param_4, u32 cmd_param_5, u32 cmd_param_6 + , u32 resp_data_len, struct io96b_mb_resp *resp) +{ + int i; + int ret; + u32 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_le32((const void *)(io96b_csr_addr + IOSSM_CMD_REQ_OFFSET) + , GENMASK(31, 0), 0, TIMEOUT, false); + + if (ret) { + printf("%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) + writel(cmd_param_0, io96b_csr_addr + IOSSM_CMD_PARAM_0_OFFSET); + break; + case 1: + if (cmd_param_1) + writel(cmd_param_1, io96b_csr_addr + IOSSM_CMD_PARAM_1_OFFSET); + break; + case 2: + if (cmd_param_2) + writel(cmd_param_2, io96b_csr_addr + IOSSM_CMD_PARAM_2_OFFSET); + break; + case 3: + if (cmd_param_3) + writel(cmd_param_3, io96b_csr_addr + IOSSM_CMD_PARAM_3_OFFSET); + break; + case 4: + if (cmd_param_4) + writel(cmd_param_4, io96b_csr_addr + IOSSM_CMD_PARAM_4_OFFSET); + break; + case 5: + if (cmd_param_5) + writel(cmd_param_5, io96b_csr_addr + IOSSM_CMD_PARAM_5_OFFSET); + break; + case 6: + if (cmd_param_6) + writel(cmd_param_6, io96b_csr_addr + IOSSM_CMD_PARAM_6_OFFSET); + break; + default: + printf("%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); + writel(cmd_req, io96b_csr_addr + IOSSM_CMD_REQ_OFFSET); + debug("%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_le32((const void *)(io96b_csr_addr + + IOSSM_CMD_RESPONSE_STATUS_OFFSET), IOSSM_STATUS_COMMAND_RESPONSE_READY, 1, + TIMEOUT, false); + + if (ret) { + printf("%s: CMD_RESPONSE ERROR:\n", __func__); + cmd_resp = readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + printf("%s: STATUS_GENERAL_ERROR: 0x%x\n", __func__, (cmd_resp >> 1) & 0xF); + printf("%s: STATUS_CMD_RESPONSE_ERROR: 0x%x\n", __func__, (cmd_resp >> 5) & 0x7); + } + + /* read CMD_RESPONSE_STATUS*/ + resp->cmd_resp_status = readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + debug("%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 = + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET); + debug("%s: IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x%llx: 0x%x\n", __func__ + , io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET, + resp->cmd_resp_data_0); + break; + case 1: + resp->cmd_resp_data_1 = + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET); + debug("%s: IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x%llx: 0x%x\n", __func__ + , io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET, + resp->cmd_resp_data_1); + break; + case 2: + resp->cmd_resp_data_2 = + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET); + debug("%s: IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x%llx: 0x%x\n", __func__ + , io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET, + resp->cmd_resp_data_2); + break; + default: + printf("%s: Invalid response data\n", __func__); + } + } + + /* write CMD_RESPONSE_READY = 0 */ + clrbits_le32((u32 *)(uintptr_t)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET) + , IOSSM_STATUS_COMMAND_RESPONSE_READY); + + resp->cmd_resp_status = readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + debug("%s: CMD_RESPONSE_STATUS 0x%llx: 0x%x (cleared)\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; + u8 ip_type_ret, instance_id_ret; + int i, j, k; + + debug("%s: num_instance %d\n", __func__, io96b_ctrl->num_instance); + for (i = 0; i < io96b_ctrl->num_instance; i++) { + debug("%s: get memory interface IO96B %d\n", __func__, 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); + debug("%s: get response from memory interface IO96B %d\n", __func__, i); + /* 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) { + 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++; + } + + debug("%s: IO96B %d [%d]: ip_type: 0x%x\n", __func__, i, k + , ip_type_ret); + debug("%s: IO96B %d [%d]: instance_id: 0x%x\n", __func__, i, k + , instance_id_ret); + } +; + 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); + debug("%s: get response from memory interface IO96B %d\n", __func__, i); + /* 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; + debug("%s: IO96B %d: num_mem_interface: 0x%x\n", __func__, i + , io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface); + + /* 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) { + 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++; + } + + debug("%s: IO96B %d [%d]: ip_type: 0x%x\n", __func__, i, k + , ip_type_ret); + debug("%s: IO96B %d [%d]: instance_id: 0x%x\n", __func__, i, k + , instance_id_ret); + } + break; + } + } +} + +int io96b_cal_status(phys_addr_t addr) +{ + int ret; + phys_addr_t status_addr = addr + IOSSM_STATUS_OFFSET; + /* Ensure calibration completed */ + ret = wait_for_bit_le32((const void *)status_addr, IOSSM_STATUS_CAL_BUSY, false + , 1000000000, false); + + debug("%s: Cal_stat 0x%llx: 0x%x\n", __func__, status_addr, readl(status_addr)); + + if (ret) { + printf("%s: SDRAM calibration IO96b instance 0x%llx timeout\n", __func__ + , status_addr); + hang(); + } + + /* Calibration status */ + if (readl(status_addr) == 0x1) + return 0; + else + return -EPERM; +} + +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) { + io96b_ctrl->io96b_0.cal_status = false; + printf("%s: Initial DDR calibration IO96B_0 failed %d\n", __func__ + , ret); + break; + } + io96b_ctrl->io96b_0.cal_status = true; + debug("%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) { + io96b_ctrl->io96b_1.cal_status = false; + printf("%s: Initial DDR calibration IO96B_1 failed %d\n", __func__ + , ret); + break; + } + io96b_ctrl->io96b_1.cal_status = true; + debug("%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_done; + int i, j; + u8 cal_stat; + u32 reg; + + /* Initialize overall calibration status */ + io96b_ctrl->overall_cal_status = false; + + for (i = 0; i < io96b_ctrl->num_instance; i++) { + switch (i) { + case 0: + if (!(io96b_ctrl->io96b_0.cal_status)) { + recal_done = false; + + /* Re-calibration first memory interface with failed calibration */ + for (j = 0; j < 3; j++) { + /* Get the memory calibration status for first memory interface */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr + , io96b_ctrl->io96b_0.mb_ctrl.ip_type[0], 0 + , CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, 0, 0 + , 0, 0, 0, 0, 2, &usr_resp); + reg = readl(io96b_ctrl->io96b_0.io96b_csr_addr + + usr_resp.cmd_resp_data_0); + cal_stat = reg & GENMASK(2, 0); + + /* Calibration was successful or INTF_0 unused*/ + if (cal_stat < 0x2) { + recal_done = true; + break; + } + + /* Calibration failed - recalibration */ + if (cal_stat == 0x2) { + 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); + } + + /* Calibration ongoing */ + while (cal_stat == 0x4) { + udelay(1000); + + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr + , io96b_ctrl->io96b_0.mb_ctrl.ip_type[0], 0 + , CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS + , 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + reg = readl(io96b_ctrl->io96b_0.io96b_csr_addr + + usr_resp.cmd_resp_data_0); + cal_stat = reg & GENMASK(2, 0); + } + } + + if (!recal_done) { + printf("%s: Error as SDRAM calibration failed\n", __func__); + hang(); + } + + recal_done = false; + + /* Re-calibration second memory interface with failed calibration */ + for (j = 0; j < 3; j++) { + /* Get the memory calibration status for second memory interface */ + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr + , io96b_ctrl->io96b_0.mb_ctrl.ip_type[1], 0 + , CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, 0, 0 + , 0, 0, 0, 0, 2, &usr_resp); + reg = readl(io96b_ctrl->io96b_0.io96b_csr_addr + + usr_resp.cmd_resp_data_1); + cal_stat = reg & GENMASK(2, 0); + + /* Calibration was successful or INTF_0 unused*/ + if (cal_stat < 0x2) { + recal_done = true; + break; + } + + /* Calibration failed - recalibration */ + if (cal_stat == 0x2) { + 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[0] + , CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, 0 + , 0, 0, 2, &usr_resp); + } + + /* Calibration ongoing */ + while (cal_stat == 0x4) { + udelay(1000); + + io96b_mb_req(io96b_ctrl->io96b_0.io96b_csr_addr + , io96b_ctrl->io96b_0.mb_ctrl.ip_type[1], 0 + , CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS + , 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + reg = readl(io96b_ctrl->io96b_0.io96b_csr_addr + + usr_resp.cmd_resp_data_1); + cal_stat = reg & GENMASK(2, 0); + } + } + + if (!recal_done) { + printf("%s: Error as SDRAM calibration failed\n", __func__); + hang(); + } + + io96b_ctrl->io96b_0.cal_status = true; + } + break; + case 1: + if (!(io96b_ctrl->io96b_1.cal_status)) { + recal_done = false; + + /* Re-calibration first memory interface with failed calibration */ + for (j = 0; j < 3; j++) { + /* Get the memory calibration status for first memory interface */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr + , io96b_ctrl->io96b_1.mb_ctrl.ip_type[0], 0 + , CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, 0, 0 + , 0, 0, 0, 0, 2, &usr_resp); + reg = readl(io96b_ctrl->io96b_1.io96b_csr_addr + + usr_resp.cmd_resp_data_0); + cal_stat = reg & GENMASK(2, 0); + + /* Calibration was successful or INTF_0 unused*/ + if (cal_stat < 0x2) { + recal_done = true; + break; + } + + /* Calibration failed - recalibration */ + if (cal_stat == 0x2) { + 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); + } + + /* Calibration ongoing */ + while (cal_stat == 0x4) { + udelay(1000); + + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr + , io96b_ctrl->io96b_1.mb_ctrl.ip_type[0], 0 + , CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS + , 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + reg = readl(io96b_ctrl->io96b_1.io96b_csr_addr + + usr_resp.cmd_resp_data_0); + cal_stat = reg & GENMASK(2, 0); + } + } + + if (!recal_done) { + printf("%s: Error as SDRAM calibration failed\n", __func__); + hang(); + } + + recal_done = false; + + /* Re-calibration second memory interface with failed calibration */ + for (j = 0; j < 3; j++) { + /* Get the memory calibration status for second memory interface */ + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr + , io96b_ctrl->io96b_1.mb_ctrl.ip_type[1], 0 + , CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS, 0, 0, 0 + , 0, 0, 0, 0, 2, &usr_resp); + reg = readl(io96b_ctrl->io96b_1.io96b_csr_addr + + usr_resp.cmd_resp_data_1); + cal_stat = reg & GENMASK(2, 0); + + /* Calibration was successful or INTF_0 unused*/ + if (cal_stat < 0x2) { + recal_done = true; + break; + } + + /* Calibration failed - recalibration */ + if (cal_stat == 0x2) { + 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[0] + , CMD_TRIG_MEM_CAL_OP, TRIG_MEM_CAL, 0, 0, 0, 0, 0 + , 0, 0, 2, &usr_resp); + } + + /* Calibration ongoing */ + while (cal_stat == 0x4) { + udelay(1000); + + io96b_mb_req(io96b_ctrl->io96b_1.io96b_csr_addr + , io96b_ctrl->io96b_1.mb_ctrl.ip_type[1], 0 + , CMD_TRIG_MEM_CAL_OP, GET_MEM_CAL_STATUS + , 0, 0, 0, 0, 0, 0, 0, 2, &usr_resp); + reg = readl(io96b_ctrl->io96b_1.io96b_csr_addr + + usr_resp.cmd_resp_data_1); + cal_stat = reg & GENMASK(2, 0); + } + } + + if (!recal_done) { + printf("%s: Error as SDRAM calibration failed\n", __func__); + hang(); + } + + io96b_ctrl->io96b_1.cal_status = true; + } + break; + } + } + + if (io96b_ctrl->io96b_0.cal_status && io96b_ctrl->io96b_1.cal_status) { + debug("%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; + u8 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")) + io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret]; + + if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) { + printf("%s: Mismatch DDR type 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_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")) + io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret]; + + if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) { + printf("%s: Mismatch DDR type on IO96B_1\n", __func__); + return -ENOEXEC; + } + } + break; + } + } + + return 0; +} + +int get_mem_width_info(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_resp usr_resp; + int i, j; + u16 memory_size; + u16 total_memory_size = 0; + + /* 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) { + printf("%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) { + printf("%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) { + printf("%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++) { + debug("%s: ECC_ENABLE_STATUS\n", __func__); + 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) { + printf("%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++) { + debug("%s: ECC_ENABLE_STATUS\n", __func__); + 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) { + printf("%s: Mismatch DDR ECC status on IO96B_1\n" + , __func__); + return -ENOEXEC; + } + } + break; + } + } + + debug("%s: ECC: %s\n", __func__, io96b_ctrl->ecc_status ? "Enabled" : "Disabled"); + + 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; + u32 start; + int count; + + /* 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; + + /* 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, 0x9 + , 0, 0, 0, 0, 0, 0, 0, &usr_resp); + + bist_start = + (IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & BIT(0)); + + if (!bist_start) { + printf("%s: Failed to initialized memory on IO96B_0\n" + , __func__); + printf("%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 */ + start = get_timer(0); + count = 1; + while (!bist_success) { + debug(" io96b_0: BIST delay count: %d\n", count); + 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) & BIT(0)); + + mdelay(5000); + count++; + } + } + + debug("%s: Memory initialized successfully on IO96B_0\n", __func__); + debug("%s: Initialized success with %d ms\n", __func__ + , (unsigned int)get_timer(start)); + + break; + case 1: + for (j = 0; j < io96b_ctrl->io96b_1.mb_ctrl.num_mem_interface; j++) { + bist_start = false; + bist_success = false; + + /* 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) + & BIT(0)); + + if (!bist_start) { + printf("%s: Failed to initialized memory on IO96B_1\n" + , __func__); + printf("%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 */ + start = get_timer(0); + count = 1; + while (!bist_success) { + debug(" io96b_1: BIST delay count: %d\n", count); + 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) & BIT(0)); + + mdelay(5000); + count++; + } + } + debug("%s: Memory initialized successfully on IO96B_1\n", __func__); + debug("%s: Initialized success with %d ms\n", __func__ + , (unsigned int)get_timer(start)); + break; + } + } + return 0; +} diff --git a/drivers/ddr/altera/iossm_mailbox.h b/drivers/ddr/altera/iossm_mailbox.h new file mode 100644 index 0000000000..e66b240120 --- /dev/null +++ b/drivers/ddr/altera/iossm_mailbox.h @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Intel Corporation + */ + +#define TIMEOUT_10000MS 10000 +#define TIMEOUT TIMEOUT_10000MS +#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 BIT(0) +#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 { + u32 num_mem_interface; + u32 ip_type[2]; + u32 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 { + u32 cmd_resp_status; + u32 cmd_resp_data_0; + u32 cmd_resp_data_1; + u32 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 { + u16 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 { + u8 num_instance; + bool overall_cal_status; + const char *ddr_type; + bool ecc_status; + u16 overall_size; + struct io96b_instance io96b_0; + struct io96b_instance io96b_1; +}; + +int io96b_mb_req(phys_addr_t io96b_csr_addr, u32 ip_type, u32 instance_id + , u32 usr_cmd_type, u32 usr_cmd_opcode, u32 cmd_param_0 + , u32 cmd_param_1, u32 cmd_param_2, u32 cmd_param_3, u32 cmd_param_4 + , u32 cmd_param_5, u32 cmd_param_6, u32 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); +void trig_mem_cal_must(struct io96b_info *io96b_ctrl);