From: Doug Ledford <doug.ledford@hpe.com>
To: "Slingshot Open Source Team"
<slingshot-os-team@groups.ext.hpe.com>,
"Nathan Chancellor" <nathan@kernel.org>,
"Nicolas Schier" <nsc@kernel.org>,
"Richard Cochran" <richardcochran@gmail.com>,
"Andrew Lunn" <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@kernel.org>,
"Jakub Kicinski" <kuba@kernel.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Sumit Semwal" <sumit.semwal@linaro.org>,
"Christian König" <christian.koenig@amd.com>,
"Guenter Roeck" <linux@roeck-us.net>
Cc: <linux-kernel@vger.kernel.org>, <netdev@vger.kernel.org>,
<linux-kbuild@vger.kernel.org>,
Doug Ledford <doug.ledford@hpe.com>
Subject: [PATCH 07/23] net: hpe: sbl: add the SBM serial bus layer
Date: Fri, 30 Oct 2026 13:59:03 -0700 [thread overview]
Message-ID: <20261030-cxi-netdev-submission-v1-7-8c2c391cc283@hpe.com> (raw)
In-Reply-To: <20261030-cxi-netdev-submission-v1-0-8c2c391cc283@hpe.com>
Add the SBM (serial bus master) layer used to reach SerDes and PMI
registers, and its NIC specific SerDes accessors.
checkpatch --strict (100 columns) still reports the following in this
patch. They need coordination with the internal repositories this code
is maintained in and will be fixed in V2:
- WARNING: BLOCK_COMMENT_STYLE (2), LONG_LINE_STRING (2), LONG_LINE (1),
TYPO_SPELLING (1)
- CHECK: UNNECESSARY_PARENTHESES (16), BRACES (3), COMPARISON_TO_NULL
(1)
Signed-off-by: Doug Ledford <doug.ledford@hpe.com>
---
drivers/net/ethernet/hpe/sbl/sbl_sbm_serdes.c | 1114 +++++++++++++++++++++
drivers/net/ethernet/hpe/sbl/sbl_sbm_serdes_nic.c | 138 +++
2 files changed, 1252 insertions(+)
diff --git a/drivers/net/ethernet/hpe/sbl/sbl_sbm_serdes.c b/drivers/net/ethernet/hpe/sbl/sbl_sbm_serdes.c
new file mode 100644
index 000000000000..1d1c4d01e159
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sbl/sbl_sbm_serdes.c
@@ -0,0 +1,1114 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Copyright 2025-2026 Hewlett Packard Enterprise Development LP. All Rights Reserved. */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+
+#include <uapi/ethernet/sbl_sbm_constants.h>
+
+#include "sbl_sbm_serdes.h"
+#include "sbl_serdes_map.h"
+#include "sbl_sbm.h"
+
+static void
+sbus_reg_addr_to_string(u32 sbus_addr, u8 reg_addr, char *reg_addr_str)
+{
+ if (is_sbm_spico_addr(sbus_addr)) {
+ switch (reg_addr) {
+ case SPICO_SBR_ADDR_SRAM_BIST:
+ sprintf(reg_addr_str, "SRAM_BIST");
+ break;
+ case SPICO_SBR_ADDR_CTL:
+ sprintf(reg_addr_str, "CTL");
+ break;
+ case SPICO_SBR_ADDR_DMEM_IN:
+ sprintf(reg_addr_str, "DMEM_IN");
+ break;
+ case SPICO_SBR_ADDR_IMEM:
+ sprintf(reg_addr_str, "IMEM");
+ break;
+ case SPICO_SBR_ADDR_DMEM:
+ sprintf(reg_addr_str, "DMEM");
+ break;
+ case SPICO_SBR_ADDR_STEP_BP:
+ sprintf(reg_addr_str, "STEP_BP");
+ break;
+ case SPICO_SBR_ADDR_BP_ADDR:
+ sprintf(reg_addr_str, "BP_ADDR");
+ break;
+ case SPICO_SBR_ADDR_INTR:
+ sprintf(reg_addr_str, "INTR");
+ break;
+ case SPICO_SBR_ADDR_DMEM_OUT:
+ sprintf(reg_addr_str, "DMEM_OUT");
+ break;
+ case SPICO_SBR_ADDR_RDATA:
+ sprintf(reg_addr_str, "RDATA");
+ break;
+ case SPICO_SBR_ADDR_PC:
+ sprintf(reg_addr_str, "PC");
+ break;
+ case SPICO_SBR_ADDR_PC_OVERRIDE:
+ sprintf(reg_addr_str, "PC_OVERRIDE");
+ break;
+ case SPICO_SBR_ADDR_FLAG:
+ sprintf(reg_addr_str, "FLAG");
+ break;
+ case SPICO_SBR_ADDR_OP:
+ sprintf(reg_addr_str, "OP");
+ break;
+ case SPICO_SBR_ADDR_SP_EC:
+ sprintf(reg_addr_str, "SP_EC");
+ break;
+ case SPICO_SBR_ADDR_STATE:
+ sprintf(reg_addr_str, "STATE");
+ break;
+ case SPICO_SBR_ADDR_RESULT:
+ sprintf(reg_addr_str, "RESULT");
+ break;
+ case SPICO_SBR_ADDR_A_B_REG:
+ sprintf(reg_addr_str, "A_B_REG");
+ break;
+ case SPICO_SBR_ADDR_IMEM_BURST_DATA:
+ sprintf(reg_addr_str, "IMEM_BURST_DATA");
+ break;
+ case SPICO_SBR_ADDR_IMEM_BURST_ADDR:
+ sprintf(reg_addr_str, "IMEM_BURST_ADDR");
+ break;
+ case SPICO_SBR_ADDR_ECC:
+ sprintf(reg_addr_str, "ECC");
+ break;
+ case SPICO_SBR_ADDR_UNNAMED_0:
+ sprintf(reg_addr_str, "UNNAMED_0");
+ break;
+ case SPICO_SBR_ADDR_UNNAMED_1:
+ sprintf(reg_addr_str, "UNNAMED_1");
+ break;
+ case SPICO_SBR_ADDR_UNNAMED_2:
+ sprintf(reg_addr_str, "UNNAMED_2");
+ break;
+ case SPICO_SBR_ADDR_UNNAMED_3:
+ sprintf(reg_addr_str, "UNNAMED_3");
+ break;
+ case SPICO_SBR_ADDR_UNNAMED_4:
+ sprintf(reg_addr_str, "UNNAMED_4");
+ break;
+ default:
+ sprintf(reg_addr_str, "SPICO_SBR_UNKNOWN");
+ break;
+ }
+ } else if (is_sbm_crm_addr(sbus_addr)) {
+ switch (reg_addr) {
+ case SBM_CRM_ADDR_CISM0:
+ sprintf(reg_addr_str, "CISM0");
+ break;
+ case SBM_CRM_ADDR_CISM1:
+ sprintf(reg_addr_str, "CISM1");
+ break;
+ case SBM_CRM_ADDR_LAST_ADDR:
+ sprintf(reg_addr_str, "LAST_ADDR");
+ break;
+ case SBM_CRM_ADDR_CLK_DIV:
+ sprintf(reg_addr_str, "CLK_DIV");
+ break;
+ case SBM_CRM_ADDR_CLK_DIV_RST:
+ sprintf(reg_addr_str, "CLK_DIV_RST");
+ break;
+ case SBM_CRM_ADDR_CLK_DIV_RST_P0:
+ sprintf(reg_addr_str, "CLK_DIV_RST_P0");
+ break;
+ case SBM_CRM_ADDR_CLK_DIV_RST_P1:
+ sprintf(reg_addr_str, "CLK_DIV_RST_P1");
+ break;
+ case SBM_CRM_ADDR_CISM_RX_ADDR:
+ sprintf(reg_addr_str, "CISM_RX_ADDR");
+ break;
+ case SBM_CRM_ADDR_CISM_CMD1:
+ sprintf(reg_addr_str, "CISM_CMD1");
+ break;
+ case SBM_CRM_ADDR_CISM_DATA_ADDR1:
+ sprintf(reg_addr_str, "CISM_DATA_ADDR1");
+ break;
+ case SBM_CRM_ADDR_CISM_DATA_WORD1:
+ sprintf(reg_addr_str, "CISM_DATA_WORD1");
+ break;
+ case SBM_CRM_ADDR_CISM_CMD2:
+ sprintf(reg_addr_str, "CISM_CMD2");
+ break;
+ case SBM_CRM_ADDR_CISM_DATA_ADDR2:
+ sprintf(reg_addr_str, "CISM_DATA_ADDR2");
+ break;
+ case SBM_CRM_ADDR_CISM_DATA_WORD2:
+ sprintf(reg_addr_str, "CISM_DATA_WORD2");
+ break;
+ case SBM_CRM_ADDR_DATA_OUT_SEL:
+ sprintf(reg_addr_str, "DATA_OUT_SEL");
+ break;
+ case SBM_CRM_ADDR_BIST:
+ sprintf(reg_addr_str, "BIST");
+ break;
+ case SBM_CRM_ADDR_ROM_EN:
+ sprintf(reg_addr_str, "ROM_EN");
+ break;
+ case SBM_CRM_ADDR_ROM_FAILED_ADDR:
+ sprintf(reg_addr_str, "ROM_FAILED_ADDR");
+ break;
+ case SBM_CRM_ADDR_ROM_DATA_VAL_CNT:
+ sprintf(reg_addr_str, "ROM_DATA_VAL_CNT");
+ break;
+ case SBM_CRM_ADDR_ROM_ACK:
+ sprintf(reg_addr_str, "ROM_ACK");
+ break;
+ case SBM_CRM_ADDR_ROM_STOP_ADDR:
+ sprintf(reg_addr_str, "ROM_STOP_ADDR");
+ break;
+ case SBM_CRM_ADDR_ROM_OUTPUT:
+ sprintf(reg_addr_str, "ROM_OUTPUT");
+ break;
+ case SBM_CRM_ADDR_ROM_DATA0:
+ sprintf(reg_addr_str, "ROM_DATA0");
+ break;
+ case SBM_CRM_ADDR_ROM_DATA1:
+ sprintf(reg_addr_str, "ROM_DATA1");
+ break;
+ case SBM_CRM_ADDR_CLK_HALT:
+ sprintf(reg_addr_str, "CLK_HALT");
+ break;
+ case SBM_CRM_ADDR_GEN_WRITE:
+ sprintf(reg_addr_str, "GEN_WRITE");
+ break;
+ case SBM_CRM_ADDR_GEN_READ:
+ sprintf(reg_addr_str, "GEN_READ");
+ break;
+ case SBM_CRM_ADDR_GEN_WRITE_P0:
+ sprintf(reg_addr_str, "GEN_WRITE_P0");
+ break;
+ case SBM_CRM_ADDR_GEN_WRITE_P1:
+ sprintf(reg_addr_str, "GEN_WRITE_P1");
+ break;
+ case SBM_CRM_ADDR_GEN_WRITE_P2:
+ sprintf(reg_addr_str, "GEN_WRITE_P2");
+ break;
+ case SBM_CRM_ADDR_GEN_WRITE_P3:
+ sprintf(reg_addr_str, "GEN_WRITE_P3");
+ break;
+ case SBM_CRM_ADDR_GEN_READ_P0:
+ sprintf(reg_addr_str, "GEN_READ_P0");
+ break;
+ case SBM_CRM_ADDR_GEN_READ_P1:
+ sprintf(reg_addr_str, "GEN_READ_P1");
+ break;
+ case SBM_CRM_ADDR_GEN_READ_P2:
+ sprintf(reg_addr_str, "GEN_READ_P2");
+ break;
+ case SBM_CRM_ADDR_GEN_READ_P3:
+ sprintf(reg_addr_str, "GEN_READ_P3");
+ break;
+ case SBM_CRM_ADDR_SBUS_ID:
+ sprintf(reg_addr_str, "SBUS_ID");
+ break;
+ case SBM_CRM_ADDR_IP_IDCODE:
+ sprintf(reg_addr_str, "IP_IDCODE");
+ break;
+ default:
+ sprintf(reg_addr_str, "SBM_CRM_UNKNOWN");
+ break;
+ }
+ } else if (is_cm4_serdes_addr(sbus_addr)) {
+ switch (reg_addr) {
+ case SPICO_SERDES_ADDR_IMEM:
+ sprintf(reg_addr_str, "IMEM");
+ break;
+ case SPICO_SERDES_ADDR_INTR0:
+ sprintf(reg_addr_str, "INTR0");
+ break;
+ case SPICO_SERDES_ADDR_INTR1:
+ sprintf(reg_addr_str, "INTR1");
+ break;
+ case SPICO_SERDES_ADDR_RESET_EN:
+ sprintf(reg_addr_str, "RESET_EN");
+ break;
+ case SPICO_SERDES_ADDR_INTR_DIS:
+ sprintf(reg_addr_str, "INTR_DIS");
+ break;
+ case SPICO_SERDES_ADDR_BIST:
+ sprintf(reg_addr_str, "BIST");
+ break;
+ case SPICO_SERDES_ADDR_IMEM_BURST:
+ sprintf(reg_addr_str, "IMEM_BURST");
+ break;
+ case SPICO_SERDES_ADDR_ECC:
+ sprintf(reg_addr_str, "ECC");
+ break;
+ case SPICO_SERDES_ADDR_ECCLOG:
+ sprintf(reg_addr_str, "ECCLOG");
+ break;
+ case SPICO_SERDES_ADDR_BCAST:
+ sprintf(reg_addr_str, "BCAST");
+ break;
+ case SPICO_SERDES_ADDR_IP_IDCODE:
+ sprintf(reg_addr_str, "IP_IDCODE");
+ break;
+ default:
+ sprintf(reg_addr_str, "UNKNOWN");
+ break;
+ }
+ } else {
+ sprintf(reg_addr_str, "UNKNOWN");
+ }
+}
+
+static void
+sbus_cmd_to_string(u8 command, char *cmd_str)
+{
+ char str0[SBL_MAX_STR_LEN], str1[SBL_MAX_STR_LEN], str2[SBL_MAX_STR_LEN];
+
+ switch (command & SBUS_CMD_MODE_MASK) {
+ case SBUS_CMD_MODE_CTLR:
+ sprintf(str0, "CM_CTLR");
+ break;
+ case SBUS_CMD_MODE_RCVR:
+ sprintf(str0, "CM_RCVR");
+ break;
+ default:
+ sprintf(str0, "CM_UNKNOWN");
+ break;
+ }
+ switch (command & SBUS_IFACE_DST_MASK) {
+ case SBUS_IFACE_DST_TAP:
+ sprintf(str1, "ID_TAP");
+ break;
+ case SBUS_IFACE_DST_CORE:
+ sprintf(str1, "ID_CORE");
+ break;
+ case SBUS_IFACE_DST_SPICO:
+ sprintf(str1, "ID_SPICO");
+ break;
+ case SBUS_IFACE_DST_SPARE:
+ sprintf(str1, "ID_SPARE");
+ break;
+ default:
+ sprintf(str1, "ID_UNKNOWN");
+ break;
+ }
+ switch (command & SBUS_CMD_MASK) {
+ case SBUS_CMD_RESET:
+ sprintf(str2, "CMD_RESET");
+ break;
+ case SBUS_CMD_WRITE:
+ sprintf(str2, "CMD_WRITE");
+ break;
+ case SBUS_CMD_READ:
+ sprintf(str2, "CMD_READ");
+ break;
+ case SBUS_CMD_READ_RESULT:
+ sprintf(str2, "CMD_READ_RESULT");
+ break;
+ default:
+ sprintf(str2, "CMD_UNKNOWN");
+ break;
+ }
+ sprintf(cmd_str, "%s:%s:%s", str0, str1, str2);
+}
+
+static void
+sbus_result_code_to_string(u8 result_code, char *rc_string)
+{
+ switch (result_code) {
+ case SBUS_RC_RESET:
+ sprintf(rc_string, "RESET");
+ break;
+ case SBUS_RC_WRITE_COMPLETE:
+ sprintf(rc_string, "WRITE_COMPLETE");
+ break;
+ case SBUS_RC_WRITE_FAILED:
+ sprintf(rc_string, "WRITE_FAILED");
+ break;
+ case SBUS_RC_READ_ALL_COMPLETE:
+ sprintf(rc_string, "READ_ALL_COMPLETE");
+ break;
+ case SBUS_RC_READ_COMPLETE:
+ sprintf(rc_string, "READ_COMPLETE");
+ break;
+ case SBUS_RC_READ_FAILED:
+ sprintf(rc_string, "READ_FAILED");
+ break;
+ case SBUS_RC_CMD_ISSUE_DONE:
+ sprintf(rc_string, "CMD_ISSUE_DONE");
+ break;
+ case SBUS_RC_MODE_CHANGE_COMPLETE:
+ sprintf(rc_string, "MODE_CHANGE_COMPLETE");
+ break;
+ default:
+ sprintf(rc_string, "UNKNOWN");
+ break;
+ }
+}
+
+static void
+spico_interrupt_to_string(u32 sbus_addr, u16 interrupt, char *intr_str)
+{
+ if (is_sbm_spico_addr(sbus_addr)) {
+ switch (interrupt) {
+ case SPICO_INT_SBMS_REV_ID:
+ sprintf(intr_str, "SBMS_REV_ID");
+ break;
+ case SPICO_INT_SBMS_BUILD_ID:
+ sprintf(intr_str, "SBMS_BUILD_ID");
+ break;
+ case SPICO_INT_SBMS_DO_CRC:
+ sprintf(intr_str, "SBMS_DO_CRC");
+ break;
+ case SPICO_INT_SBMS_READ_DMEM_VAL:
+ sprintf(intr_str, "SBMS_READ_DMEM_VAL");
+ break;
+ case SPICO_INT_SBMS_DO_XDMEM_CRC:
+ sprintf(intr_str, "SBMS_DO_XDMEM_CRC");
+ break;
+ case SPICO_INT_SBMS_GET_PMRO_DATA:
+ sprintf(intr_str, "SBMS_GET_PMRO_DATA");
+ break;
+ case SPICO_INT_SBMS_GET_TEMP_DATA:
+ sprintf(intr_str, "SBMS_GET_TEMP_DATA");
+ break;
+ case SPICO_INT_SBMS_GET_VOLT_DATA:
+ sprintf(intr_str, "SBMS_GET_VOLT_DATA");
+ break;
+ case SPICO_INT_SBMS_IMEM_CRC_CHECK:
+ sprintf(intr_str, "SBMS_IMEM_CRC_CHECK");
+ break;
+ case SPICO_INT_SBMS_IMEM_SWP_SA:
+ sprintf(intr_str, "SBMS_IMEM_SWP_SA");
+ break;
+ case SPICO_INT_SBMS_PCIE3_SWP:
+ sprintf(intr_str, "SBMS_PCIE3_SWP");
+ break;
+ case SPICO_INT_SBMS_DRMON_SETUP:
+ sprintf(intr_str, "SBMS_DRMON_SETUP");
+ break;
+ case SPICO_INT_SBMS_TEMP_SETUP:
+ sprintf(intr_str, "SBMS_TEMP_SETUP");
+ break;
+ case SPICO_INT_SBMS_RR_PCAL:
+ sprintf(intr_str, "SBMS_RR_PCAL");
+ break;
+ default:
+ if ((interrupt >= SPICO_INT_SBMS_DDR_HMB_FIRST) &&
+ (interrupt <= SPICO_INT_SBMS_DDR_HMB_LAST)) {
+ sprintf(intr_str, "SBMS_DDR_HMB");
+ } else {
+ sprintf(intr_str, "SBMS_UNKNOWN");
+ }
+ break;
+ }
+ } else if (is_cm4_serdes_addr(sbus_addr)) {
+ switch (interrupt) {
+ case SPICO_INT_CM4_REV_ID:
+ sprintf(intr_str, "CM4_REV_ID");
+ break;
+ case SPICO_INT_CM4_SERDES_EN:
+ sprintf(intr_str, "CM4_SERDES_EN");
+ break;
+ case SPICO_INT_CM4_PRBS_CTRL:
+ sprintf(intr_str, "CM4_PRBS_CTRL");
+ break;
+ case SPICO_INT_CM4_COMPARE_CTRL:
+ sprintf(intr_str, "CM4_COMPARE_CTRL");
+ break;
+ case SPICO_INT_CM4_PMD_CTRL:
+ sprintf(intr_str, "CM4_PMD_CTRL");
+ break;
+ case SPICO_INT_CM4_TX_BAUD:
+ sprintf(intr_str, "CM4_TX_BAUD");
+ break;
+ case SPICO_INT_CM4_RX_BAUD:
+ sprintf(intr_str, "CM4_RX_BAUD");
+ break;
+ case SPICO_INT_CM4_AN_CONFIG:
+ sprintf(intr_str, "CM4_AN_CONFIG");
+ break;
+ case SPICO_INT_CM4_LOOPBACK:
+ sprintf(intr_str, "CM4_LOOPBACK");
+ break;
+ case SPICO_INT_CM4_DFE_CTRL:
+ sprintf(intr_str, "CM4_DFE_CTRL");
+ break;
+ case SPICO_INT_CM4_TX_PHASE_CAL:
+ sprintf(intr_str, "CM4_TX_PHASE_CAL");
+ break;
+ case SPICO_INT_CM4_BIT_SLIP:
+ sprintf(intr_str, "CM4_BIT_SLIP");
+ break;
+ case SPICO_INT_CM4_TX_PHASE_SLIP:
+ sprintf(intr_str, "CM4_TX_PHASE_SLIP");
+ break;
+ case SPICO_INT_CM4_RX_PHASE_SLIP:
+ sprintf(intr_str, "CM4_RX_PHASE_SLIP");
+ break;
+ case SPICO_INT_CM4_PCIE_PD:
+ sprintf(intr_str, "CM4_PCIE_PD");
+ break;
+ case SPICO_INT_CM4_PLL_RECAL:
+ sprintf(intr_str, "CM4_PLL_RECAL");
+ break;
+ case SPICO_INT_CM4_SAS_APTA_CTRL:
+ sprintf(intr_str, "CM4_SAS_APTA_CTRL");
+ break;
+ case SPICO_INT_CM4_POLARITY_CTRL:
+ sprintf(intr_str, "CM4_POLARITY_CTRL");
+ break;
+ case SPICO_INT_CM4_WIDTH_MODE:
+ sprintf(intr_str, "CM4_WIDTH_MODE");
+ break;
+ case SPICO_INT_CM4_TXEQ_LOAD:
+ sprintf(intr_str, "CM4_TXEQ_LOAD");
+ break;
+ case SPICO_INT_CM4_ERROR_RST:
+ sprintf(intr_str, "CM4_ERROR_RST");
+ break;
+ case SPICO_INT_CM4_BIG_REG_SEL:
+ sprintf(intr_str, "CM4_BIG_REG_SEL");
+ break;
+ case SPICO_INT_CM4_BIG_REG_WR:
+ sprintf(intr_str, "CM4_BIG_REG_WR");
+ break;
+ case SPICO_INT_CM4_BIG_REG_RD:
+ sprintf(intr_str, "CM4_BIG_REG_RD");
+ break;
+ case SPICO_INT_CM4_ERR_INJECT:
+ sprintf(intr_str, "CM4_ERR_INJECT");
+ break;
+ case SPICO_INT_CM4_DO_DATA_CAP:
+ sprintf(intr_str, "CM4_DO_DATA_CAP");
+ break;
+ case SPICO_INT_CM4_WAIT_TTD:
+ sprintf(intr_str, "CM4_WAIT_TTD");
+ break;
+ case SPICO_INT_CM4_ERR_STS:
+ sprintf(intr_str, "CM4_ERR_STS");
+ break;
+ case SPICO_INT_CM4_ERR_TIMER_STS:
+ sprintf(intr_str, "CM4_ERR_TIMER_STS");
+ break;
+ case SPICO_INT_CM4_PCIE_SLICES:
+ sprintf(intr_str, "CM4_PCIE_SLICES");
+ break;
+ case SPICO_INT_CM4_PCIE3_RX_EQ:
+ sprintf(intr_str, "CM4_PCIE3_RX_EQ");
+ break;
+ case SPICO_INT_CM4_SET_RX_EQ:
+ sprintf(intr_str, "CM4_SET_RX_EQ");
+ break;
+ case SPICO_INT_CM4_GET_RX_EQ:
+ sprintf(intr_str, "CM4_GET_RX_EQ");
+ break;
+ case SPICO_INT_CM4_CORE_TO_CNTL_EN:
+ sprintf(intr_str, "CM4_CORE_TO_CNTL_EN");
+ break;
+ case SPICO_INT_CM4_AN_DME_PAGE:
+ sprintf(intr_str, "CM4_AN_DME_PAGE");
+ break;
+ case SPICO_INT_CM4_INT_RX_TERM:
+ sprintf(intr_str, "CM4_INT_RX_TERM");
+ break;
+ case SPICO_INT_CM4_HAL_READ:
+ sprintf(intr_str, "CM4_HAL_READ");
+ break;
+ case SPICO_INT_CM4_HAL_WRITE:
+ sprintf(intr_str, "CM4_HAL_WRITE");
+ break;
+ case SPICO_INT_CM4_HAL_CALL:
+ sprintf(intr_str, "CM4_HAL_CALL");
+ break;
+ case SPICO_INT_CM4_SPICO_CLK_SWP:
+ sprintf(intr_str, "CM4_SPICO_CLK_SWP");
+ break;
+ case SPICO_INT_CM4_PCIE3_TX_MRG1:
+ sprintf(intr_str, "CM4_PCIE3_TX_MRG1");
+ break;
+ case SPICO_INT_CM4_PCIE3_TX_MRG2:
+ sprintf(intr_str, "CM4_PCIE3_TX_MRG2");
+ break;
+ case SPICO_INT_CM4_PCIE3_TX_MRG3:
+ sprintf(intr_str, "CM4_PCIE3_TX_MRG3");
+ break;
+ case SPICO_INT_CM4_PCIE3_TX_MRG4:
+ sprintf(intr_str, "CM4_PCIE3_TX_MRG4");
+ break;
+ case SPICO_INT_CM4_PCIE3_TX_MRG5:
+ sprintf(intr_str, "CM4_PCIE3_TX_MRG5");
+ break;
+ case SPICO_INT_CM4_PROC_RESET:
+ sprintf(intr_str, "CM4_PROC_RESET");
+ break;
+ case SPICO_INT_CM4_PCIE_SWP_SETUP:
+ sprintf(intr_str, "CM4_PCIE_SWP_SETUP");
+ break;
+ case SPICO_INT_CM4_IP_STAT_INFO:
+ sprintf(intr_str, "CM4_IP_STAT_INFO");
+ break;
+ case SPICO_INT_CM4_CRC:
+ sprintf(intr_str, "CM4_CRC");
+ break;
+ case SPICO_INT_CM4_PMD_CONFIG:
+ sprintf(intr_str, "CM4_PMD_CONFIG");
+ break;
+ case SPICO_INT_CM4_BUILD_ID:
+ sprintf(intr_str, "CM4_BUILD_ID");
+ break;
+ case SPICO_INT_CM4_O_CORE_STS_15_0:
+ sprintf(intr_str, "CM4_O_CORE_STS_15_0");
+ break;
+ case SPICO_INT_CM4_O_CORE_STS_31_16:
+ sprintf(intr_str, "CM4_O_CORE_STS_31_16");
+ break;
+ default:
+ if (interrupt & SPICO_INT_CM4_MEM_READ) {
+ sprintf(intr_str, "CM4_MEM_READ_0x%03X",
+ interrupt & ~SPICO_INT_CM4_MEM_READ);
+ } else if (interrupt & SPICO_INT_CM4_MEM_WRITE) {
+ sprintf(intr_str, "CM4_MEM_WRITE_0x%03X",
+ interrupt & ~SPICO_INT_CM4_MEM_WRITE);
+ } else {
+ sprintf(intr_str, "CM4_UNKNOWN");
+ }
+ break;
+ }
+ } else {
+ sprintf(intr_str, "UNKNOWN");
+ }
+}
+
+static void sbus_msg_print(void *inst, int severity, const char *message)
+{
+ struct sbl_inst *sbl = inst;
+
+ if (!sbl || !message)
+ return;
+ if (severity >= LEVEL_ERR)
+ SBL_ERR(sbl->dev, "%s", message);
+ else if (severity >= LEVEL_WARN)
+ SBL_WARN(sbl->dev, "%s", message);
+ else if (severity >= LEVEL_INFO)
+ SBL_INFO(sbl->dev, "%s", message);
+ else
+ SBL_TRACE1(sbl->dev, "%s", message);
+}
+
+static void sbus_msg(void *inst, u32 sbus_addr, u32 req_data,
+ u8 reg_addr, u8 command, u32 rsp_data,
+ u8 result_code, u8 overrun, int timeout,
+ u32 flags, int rc, int severity)
+{
+ struct sbl_inst *sbl = inst;
+ char sbus_addr_str[SBL_HALF_MAX_STR_LEN];
+ char reg_addr_str[SBL_HALF_MAX_STR_LEN];
+ char cmd_str[SBL_HALF_MAX_STR_LEN];
+ char rc_str[SBL_HALF_MAX_STR_LEN];
+ char message[SBL_MAX_STR_LEN];
+
+ if (!sbl)
+ return;
+
+ sbus_addr_to_string(sbus_addr, sbus_addr_str);
+ sbus_reg_addr_to_string(sbus_addr, reg_addr, reg_addr_str);
+ sbus_cmd_to_string(command, cmd_str);
+ sbus_result_code_to_string(result_code, rc_str);
+
+ snprintf(message, SBL_MAX_STR_LEN, "SBUS_OP: addr:0x%03x(%-20s) req_data:0x%08x reg_addr:0x%04x(%-15s)",
+ sbus_addr, sbus_addr_str, req_data, reg_addr, reg_addr_str);
+ sbus_msg_print(inst, severity, message);
+
+ snprintf(message, SBL_MAX_STR_LEN, "SBUS_OP: command:0x%02x(%-24s), rsp_data:0x%08x result_code:0x%01x(%-14s)",
+ command, cmd_str, rsp_data, result_code, rc_str);
+ sbus_msg_print(inst, severity, message);
+
+ snprintf(message, SBL_MAX_STR_LEN, "SBUS_OP: overrun:%d timeout:0x%04x flags:0x%04x rc:%d",
+ overrun, timeout, flags, rc);
+ sbus_msg_print(inst, severity, message);
+}
+
+/**
+ * sbl_sbus_wr() - Write to sbus
+ * @inst: Generic pointer used by various frameworks
+ * @sbus_addr: address describing a serdes ring and rxaddr
+ * @reg_addr: target sbus register address
+ * @sbus_data: target sbus data
+ *
+ * Convenience function to issue an SBus op and check the result_code
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int sbl_sbus_wr(void *inst, u32 sbus_addr, u8 reg_addr,
+ u32 sbus_data)
+{
+ struct sbl_inst *sbl = inst;
+ u32 unused;
+
+ return sbl_sbus_op_aux(sbl, sbus_addr, reg_addr, SBUS_IFACE_DST_CORE |
+ SBUS_CMD_WRITE, sbus_data, &unused);
+}
+EXPORT_SYMBOL(sbl_sbus_wr);
+
+/**
+ * sbl_sbus_rd() - Read from sbus
+ * @inst: Generic pointer used by various frameworks
+ * @sbus_addr: address describing a serdes ring and rxaddr
+ * @reg_addr: target sbus register address
+ * @result: location to store result of command
+ *
+ * Convenience function to issue an SBus op and check the result_code
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int sbl_sbus_rd(void *inst, u32 sbus_addr, u8 reg_addr,
+ u32 *result)
+{
+ struct sbl_inst *sbl = inst;
+
+ return sbl_sbus_op_aux(sbl, sbus_addr, reg_addr, SBUS_IFACE_DST_CORE |
+ SBUS_CMD_READ, 0, result);
+}
+EXPORT_SYMBOL(sbl_sbus_rd);
+
+int sbl_serdes_mem_rd(void *inst, u32 port_num, u32 serdes,
+ u32 addr, u16 *data)
+{
+ struct sbl_inst *sbl = inst;
+ int err;
+
+ SBL_TRACE2(sbl->dev, "p%ds%d: addr:0x%x", port_num, serdes, addr);
+ addr &= SPICO_INT_MEM_READ_ADDR_MASK;
+
+ err = sbl_serdes_spico_int(sbl, port_num, serdes,
+ SPICO_INT_CM4_MEM_READ | addr,
+ SPICO_INT_DATA_NONE, data,
+ SPICO_INT_RETURN_RESULT);
+ if (err)
+ return err;
+
+ SBL_TRACE2(sbl->dev, "p%ds%d: addr:0x%x data:0x%x", port_num, serdes, addr, *data);
+
+ return 0;
+}
+
+int sbl_serdes_mem_wr(void *inst, u32 port_num, u32 serdes,
+ u32 addr, u16 data)
+{
+ struct sbl_inst *sbl = inst;
+ int err;
+
+ SBL_TRACE2(sbl->dev, "p%ds%d: addr:0x%x", port_num, serdes, addr);
+ addr &= SPICO_INT_MEM_READ_ADDR_MASK;
+ err = sbl_serdes_spico_int(sbl, port_num, serdes,
+ SPICO_INT_CM4_MEM_WRITE | addr,
+ data, NULL, SPICO_INT_IGNORE_RESULT);
+ if (err)
+ return err;
+
+ SBL_TRACE2(sbl->dev, "p%ds%d: addr:0x%x data:0x%x", port_num, serdes, addr,
+ data);
+
+ return 0;
+}
+
+int sbl_serdes_mem_rmw(void *inst, u32 port_num, u32 serdes,
+ u32 addr, u16 data, u16 mask)
+{
+ struct sbl_inst *sbl = inst;
+ int err;
+ u16 rdata, wdata;
+
+ SBL_TRACE2(sbl->dev, "p%ds%d: addr:0x%x data:0x%x mask:0x%x", port_num,
+ serdes, addr, data, mask);
+ err = sbl_serdes_mem_rd(sbl, port_num, serdes, addr, &rdata);
+ if (err)
+ return err;
+
+ wdata = (rdata & ~mask) | (data & mask);
+ err = sbl_serdes_mem_wr(sbl, port_num, serdes, addr, wdata);
+ if (err)
+ return err;
+
+ SBL_TRACE2(sbl->dev, "p%ds%d: addr:0x%x rmw_data:0x%x", port_num, serdes,
+ addr, wdata);
+
+ return 0;
+}
+
+int sbl_spico_burst_upload(void *inst, u32 sbus, u32 reg,
+ size_t fw_size, const u8 *fw_data)
+{
+ struct sbl_inst *sbl = inst;
+ int err;
+ size_t byte;
+
+ if (!fw_data || !fw_size || fw_size % 2) {
+ SBL_ERR(sbl->dev,
+ "Bad firmware for sbus:0x%02x reg:0x%x fw->size:%zd!",
+ sbus, reg, fw_size);
+ return -EINVAL;
+ }
+
+ SBL_TRACE1(sbl->dev, "sbus:0x%02x reg:0x%x fw->size:%zd", sbus, reg,
+ fw_size);
+ for (byte = 0; fw_size - byte >= 6; byte += 6) {
+ err = sbl_sbus_wr(sbl, sbus, reg, SPICO_SBR_DATA_BE_012 |
+ (fw_data[byte + 0] << 8 | fw_data[byte + 1]) <<
+ SPICO_SBR_DATA_W0_OFFSET |
+ (fw_data[byte + 2] << 8 | fw_data[byte + 3]) <<
+ SPICO_SBR_DATA_W1_OFFSET |
+ (fw_data[byte + 4] << 8 | fw_data[byte + 5]) <<
+ SPICO_SBR_DATA_W2_OFFSET);
+ if (err)
+ return err;
+
+ }
+ if (fw_size - byte == 4) {
+ err = sbl_sbus_wr(sbl, sbus, reg, SPICO_SBR_DATA_BE_01 |
+ (fw_data[byte + 0] << 8 | fw_data[byte + 1]) <<
+ SPICO_SBR_DATA_W0_OFFSET |
+ (fw_data[byte + 2] << 8 | fw_data[byte + 3]) <<
+ SPICO_SBR_DATA_W1_OFFSET);
+ if (err)
+ return err;
+
+ } else if (fw_size - byte == 2) {
+ err = sbl_sbus_wr(sbl, sbus, reg, SPICO_SBR_DATA_BE_0 |
+ (fw_data[byte + 0] << 8 | fw_data[byte + 1]) <<
+ SPICO_SBR_DATA_W0_OFFSET);
+ if (err)
+ return err;
+
+ }
+ return 0;
+}
+
+int sbl_sbus_op_aux(void *inst, u32 sbus_addr, u8 reg_addr,
+ u8 command, u32 sbus_data, u32 *result)
+{
+ struct sbl_inst *sbl = inst;
+ u8 result_code = 0, overrun = 0;
+ int err = -1;
+ int retry_cnt = 0;
+ int retry_limit = 5;
+ u32 sbus_ring = SBUS_RING(sbus_addr);
+ u32 rx_addr = SBUS_RX_ADDR(sbus_addr);
+ bool valid_result = false;
+ int sbus_op_timeout_ms = sbl_sbm_get_sbus_op_timeout_ms(sbl);
+ int sbus_op_flags = sbl_sbm_get_sbus_op_flags(sbl);
+ void *accessor = sbl;
+
+ if (!sbl->is_hw) {
+ *result = 0;
+ return 0;
+ }
+ /* Safety check */
+ if (!mutex_is_locked(SBUS_RING_MTX(sbl, sbus_ring))) {
+ sbus_msg(sbl, sbus_addr, sbus_data, reg_addr, command,
+ 0, result_code, overrun, sbus_op_timeout_ms,
+ sbus_op_flags, err, LEVEL_WARN);
+ WARN(1, "%s: Unlocked SBUS ring %d!", __func__, sbus_ring);
+ }
+
+ /* Perform SBus operation */
+ while (retry_cnt++ < retry_limit) {
+ err = sbl_sbm_sbus_op(accessor, sbus_ring, sbus_data,
+ reg_addr, rx_addr, command, result,
+ &result_code, &overrun,
+ sbus_op_timeout_ms,
+ sbus_op_flags);
+ if (err) {
+ sbus_msg(sbl, sbus_addr, sbus_data, reg_addr, command,
+ 0, result_code, overrun, sbus_op_timeout_ms,
+ sbus_op_flags, err, LEVEL_WARN);
+ }
+ if (err || overrun) {
+ SBL_INFO(sbl->dev, "Resetting SBUS ring %d!", sbus_ring);
+ err = sbl_sbm_sbus_op_reset(sbl, sbus_ring);
+ if (err) {
+ SBL_WARN(sbl->dev,
+ "sbl_sbm_sbus_op_reset failed! sbus_ring:%d rc:%d",
+ sbus_ring, err);
+ return err;
+ }
+ } else {
+ break;
+ }
+ }
+
+ /* Validate results */
+ if (err || overrun) {
+ SBL_WARN(sbl->dev, "roshms_sbus_op failed!");
+ return -EIO;
+ }
+ switch (command & 0x3) {
+ case SBUS_CMD_RESET:
+ if (result_code == SBUS_RC_RESET)
+ valid_result = true;
+
+ break;
+ case SBUS_CMD_WRITE:
+ if (result_code == SBUS_RC_WRITE_COMPLETE)
+ valid_result = true;
+
+ break;
+ case SBUS_CMD_READ:
+ if (result_code == SBUS_RC_READ_COMPLETE)
+ valid_result = true;
+
+ break;
+ case SBUS_CMD_READ_RESULT:
+ if (result_code == SBUS_RC_READ_ALL_COMPLETE)
+ valid_result = true;
+
+ break;
+ }
+
+ if (!valid_result) {
+ SBL_WARN(sbl->dev, "Unexpected result code (%d) 0x%x!",
+ result_code, command);
+ sbus_msg(sbl, sbus_addr, sbus_data, reg_addr, command,
+ *result, result_code, overrun, sbus_op_timeout_ms,
+ sbus_op_flags, err, LEVEL_WARN);
+ return -ENOMSG;
+ }
+ sbus_msg(sbl, sbus_addr, sbus_data, reg_addr, command,
+ *result, result_code, overrun, sbus_op_timeout_ms,
+ sbus_op_flags, err, LEVEL_DBG);
+
+ return 0;
+}
+
+/**
+ * sbl_sbm_spico_int() - spico intilization
+ * @inst: Generic pointer used by various framework
+ * @sbus_addr: address describing a serdes ring and rxaddr
+ * @code: interrupt command
+ * @data: interrupt data
+ * @result: location to store result of interrupt
+ *
+ * Write an interrupt request to a target SBM Spico
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int sbl_sbm_spico_int(void *inst, u32 sbus_addr, int code, int data,
+ u32 *result)
+{
+ struct sbl_inst *sbl = inst;
+ int err;
+ u32 intr_out;
+ u32 intr_in;
+ char intr_str[SBL_MAX_STR_LEN];
+ int sbus_int_timeout = sbl_sbm_get_sbus_int_timeout(sbl);
+ int sbus_int_poll_interval = sbl_sbm_get_sbus_int_poll_interval(sbl);
+ unsigned long last_jiffy;
+ u32 sbus_ring = SBUS_RING(sbus_addr);
+
+ if (!sbl->is_hw) {
+ *result = 0;
+ return 0;
+ }
+ /* Safety check */
+ if (!mutex_is_locked(SBUS_RING_MTX(sbl, sbus_ring))) {
+ SBL_WARN(sbl->dev, "%s: Unlocked SBUS ring %d!", __func__, sbus_ring);
+ WARN(1, "sbl_sbus_op_aux: Unlocked SBUS ring %d!", sbus_ring);
+ }
+
+ spico_interrupt_to_string(sbus_addr, code, intr_str);
+
+ /* Inject the interrupt via SBus writes */
+ intr_in = ((data & SBMS_INTERRUPT_DATA_MASK) << SBMS_INTERRUPT_DATA_OFFSET) |
+ ((code & SBMS_INTERRUPT_CODE_MASK) << SBMS_INTERRUPT_CODE_OFFSET);
+
+ err = sbl_sbus_wr(sbl, sbus_addr, SPICO_SBR_ADDR_DMEM_IN, intr_in);
+ if (err)
+ return err;
+
+ err = sbl_sbus_rd(sbl, sbus_addr, SPICO_SBR_ADDR_INTR, &intr_out);
+ if (err)
+ return err;
+
+ intr_out |= SBMS_INTERRUPT_STATUS_OK;
+ err = sbl_sbus_wr(sbl, sbus_addr, SPICO_SBR_ADDR_INTR, intr_out);
+ if (err)
+ return err;
+
+ intr_out ^= SBMS_INTERRUPT_STATUS_OK;
+ err = sbl_sbus_wr(sbl, sbus_addr, SPICO_SBR_ADDR_INTR, intr_out);
+ if (err)
+ return err;
+
+ /* Poll for interrupt completion */
+ last_jiffy = jiffies + msecs_to_jiffies(1000 * sbus_int_timeout);
+ do {
+ err = sbl_sbus_rd(sbl, sbus_addr, SPICO_SBR_ADDR_DMEM_OUT,
+ &intr_out);
+ if (err)
+ return err;
+
+ /* Check in progress bit */
+ if ((intr_out & SBMS_INTERRUPT_IN_PROGRESSS_MASK) == 0)
+ break;
+
+ msleep(sbus_int_poll_interval);
+ } while (time_is_after_jiffies(last_jiffy));
+ if (intr_out & SBMS_INTERRUPT_IN_PROGRESSS_MASK) {
+ SBL_ERR(sbl->dev,
+ "SBM_INT: sbus_addr:0x%03x int:0x%x(%s) data:0x%x timed out (timeout:%ds)!",
+ sbus_addr, code, intr_str, data, sbus_int_timeout);
+ return -ETIME;
+ }
+
+ /* Reread to ensure result is valid */
+ err = sbl_sbus_rd(sbl, sbus_addr, SPICO_SBR_ADDR_DMEM_OUT,
+ &intr_out);
+ if (err) {
+ SBL_ERR(sbl->dev,
+ "SBM_INT: sbus_addr:0x%03x int:0x%x(%s) data:0x%x failed!",
+ sbus_addr, code, intr_str, data);
+ return err;
+ }
+
+ /* Extract interrupt result */
+ if ((intr_out & SBMS_INTERRUPT_STATUS_MASK) == 1) {
+ *result = (intr_out & SBMS_INTERRUPT_DATA_MASK) >>
+ SBMS_INTERRUPT_DATA_OFFSET;
+ } else {
+ SBL_ERR(sbl->dev,
+ "SBM_INT: sbus_addr:0x%03x int:0x%x(%s) data:0x%x Failed with status 0x%x!",
+ sbus_addr, code, intr_str, data, (intr_out & SBMS_INTERRUPT_STATUS_MASK));
+ return -EBADE;
+ }
+
+ SBL_TRACE1(sbl->dev,
+ "SBM_INT: sbus_addr:0x%03x int:0x%x(%s) data:0x%x -> 0x%x",
+ sbus_addr, code, intr_str, data, *result);
+ return 0;
+}
+EXPORT_SYMBOL(sbl_sbm_spico_int);
+
+/* Returns a SBUS master address based on a ring number */
+static int sbl_chip_ring_sbus_to_port_serdes(void *inst, int chip,
+ int ring, int sbus,
+ int *port_out, int *serdes_out)
+{
+ struct sbl_inst *sbl = inst;
+ int port, serdes;
+
+ for (port = 0; port < sbl->switch_info->num_ports; ++port) {
+ for (serdes = 0; serdes < sbl->switch_info->num_serdes; ++serdes) {
+ if (chip != sbl->switch_info->ports[port].serdes[serdes].device)
+ continue;
+
+ if (ring != sbl->switch_info->ports[port].serdes[serdes].sbus_ring)
+ continue;
+
+ if (sbus != sbl->switch_info->ports[port].serdes[serdes].rx_addr)
+ continue;
+
+ *port_out = port;
+ *serdes_out = serdes;
+ return 0;
+ }
+ }
+
+ SBL_WARN(sbl->dev,
+ "Unable to determine port/serdes for chip %d ring %d sbus %d",
+ chip, ring, sbus);
+ return -1;
+}
+
+int sbl_serdes_spico_int2(void *inst, u32 sbus_addr,
+ int code, int data, u16 *result, u8 result_action)
+{
+ struct sbl_inst *sbl = inst;
+ int port_num, serdes;
+
+ if (sbl_chip_ring_sbus_to_port_serdes(sbl, 0,
+ SBUS_RING(sbus_addr),
+ SBUS_RX_ADDR(sbus_addr),
+ &port_num, &serdes)) {
+ SBL_ERR(sbl->dev,
+ "Failed converting %d:%d:%d to port/serdes!",
+ 0, SBUS_RING(sbus_addr), SBUS_RX_ADDR(sbus_addr));
+ return -1;
+ }
+
+ return sbl_serdes_spico_int(sbl, port_num, serdes, code, data, result,
+ result_action);
+}
+
+/**
+ * sbl_serdes_spico_int() - serdes spico initilization
+ * @inst: Generic pointer used by various frameworks
+ * @port_num: port number
+ * @serdes: address list describing SerDes lanes
+ * @code: interrupt command
+ * @data: interrupt data
+ * @result: pointer to store result in
+ * @result_action: ignore the interrupt result, store it to the result
+ * pointer, or validate it matches code
+ *
+ * Write an interrupt request to a set of SerDes Spicos
+ *
+ * Return: 0 if all results are the same, else -1
+ */
+int sbl_serdes_spico_int(void *inst, u32 port_num, u32 serdes,
+ int code, int data, u16 *result, u8 result_action)
+{
+ struct sbl_inst *sbl = inst;
+ int err;
+ char intr_str[SBL_MAX_STR_LEN];
+ u16 result_out = 0;
+ u16 *p_result;
+ u32 sbus_addr;
+ int serdes_op_timeout_ms = sbl_sbm_get_serdes_op_timeout_ms(sbl);
+ int serdes_op_flags = sbl_sbm_get_serdes_op_flags(sbl);
+ void *accessor = sbl;
+
+ if ((result_action == SPICO_INT_RETURN_RESULT) && (result == NULL)) {
+ SBL_ERR(sbl->dev, "result pointer was NULL!");
+ return -1;
+ }
+ if (result_action != SPICO_INT_RETURN_RESULT)
+ p_result = &result_out;
+ else
+ p_result = result;
+
+ if (!sbl->is_hw) {
+ *p_result = code;
+ return 0;
+ }
+ /* Make a fake SerDes sbus_addr so spico_interrupt_to_string gives us the
+ right debug string. This function is only called for CM4 SerDes, so this
+ will always give the correct string. */
+ sbl_sbus_addr_get(&sbus_addr);
+ spico_interrupt_to_string(sbus_addr, code, intr_str);
+
+ err = sbl_sbm_pml_serdes_op(accessor, port_num, serdes, code,
+ data, p_result, serdes_op_timeout_ms,
+ serdes_op_flags);
+ if (err) {
+ SBL_ERR(sbl->dev,
+ "SERDES_INT: p%ds%d sbl_serdes_op failed! (rc:%d) int:0x%02x(%s) data:0x%04x",
+ port_num, serdes, err, code, intr_str, data);
+ return err;
+ }
+ SBL_TRACE1(sbl->dev,
+ "SERDES_INT: p%ds%d int:0x%02x(%s) data:0x%04x -> 0x%04x",
+ port_num, serdes, code, intr_str, data, *p_result);
+
+ if ((result_action == SPICO_INT_VALIDATE_RESULT) &&
+ ((*p_result & SPICO_INT_RESULT_CODE_MASK) != code)) {
+ SBL_ERR(sbl->dev,
+ "SERDES_INT: p%ds%d int:0x%02x(%s) data:0x%04x -> 0x%04x Unexpected result! Expected 0x%04x!",
+ port_num, serdes, code, intr_str, data, *p_result & SPICO_INT_RESULT_CODE_MASK, code);
+ return -EBADE;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(sbl_serdes_spico_int);
diff --git a/drivers/net/ethernet/hpe/sbl/sbl_sbm_serdes_nic.c b/drivers/net/ethernet/hpe/sbl/sbl_sbm_serdes_nic.c
new file mode 100644
index 000000000000..a49dc39cc37d
--- /dev/null
+++ b/drivers/net/ethernet/hpe/sbl/sbl_sbm_serdes_nic.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Copyright 2025 Hewlett Packard Enterprise Development LP. All Rights Reserved. */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+
+#include <uapi/ethernet/sbl_sbm_constants.h>
+
+#include "sbl_sbm_serdes.h"
+#include "sbl_serdes_map.h"
+
+bool is_cm4_serdes_addr(u32 sbus_addr)
+{
+ u8 ring = SBUS_RING(sbus_addr);
+ u16 rx_addr = SBUS_RX_ADDR(sbus_addr);
+
+ if (((ring == 0 || ring == 1) &&
+ ((rx_addr >= SBUS_RINGX_CM4_SERDES_FIRST) &&
+ (rx_addr <= SBUS_RINGX_CM4_SERDES_LAST))) ||
+ (rx_addr == SBUS_BCAST_CM4_SERDES_SPICO)) {
+ return true;
+ }
+ return false;
+}
+
+bool is_pcie_serdes_addr(u32 sbus_addr)
+{
+ u8 ring = SBUS_RING(sbus_addr);
+ u16 rx_addr = SBUS_RX_ADDR(sbus_addr);
+
+ if ((ring == 0 || ring == 1) &&
+ ((rx_addr >= SBUS_RINGX_PCIE_SERDES_SPICO_FIRST) &&
+ (rx_addr <= SBUS_RINGX_PCIE_SERDES_SPICO_LAST) &&
+ ((rx_addr & 0x1) == SBUS_RINGX_PCIE_SERDES_SPICO_LSB))) {
+ return true;
+ }
+ return false;
+}
+
+bool is_pcie_serdes_pcs_addr(u32 sbus_addr)
+{
+ u8 ring = SBUS_RING(sbus_addr);
+ u16 rx_addr = SBUS_RX_ADDR(sbus_addr);
+
+ if ((ring == 0 || ring == 1) &&
+ ((rx_addr >= SBUS_RINGX_PCIE_SERDES_PCS_FIRST) &&
+ (rx_addr <= SBUS_RINGX_PCIE_SERDES_PCS_LAST) &&
+ ((rx_addr & 0x1) == SBUS_RINGX_PCIE_SERDES_PCS_LSB))) {
+ return true;
+ }
+ return false;
+}
+
+bool is_sbm_crm_addr(u32 sbus_addr)
+{
+ u8 ring = SBUS_RING(sbus_addr);
+ u16 rx_addr = SBUS_RX_ADDR(sbus_addr);
+
+ if (((ring == 0 || ring == 1) && (rx_addr == SBUS_RINGX_SBM)) ||
+ (rx_addr == SBUS_BCAST_SBM)) {
+
+ return true;
+ }
+ return false;
+}
+
+bool is_sbm_spico_addr(u32 sbus_addr)
+{
+ u8 ring = SBUS_RING(sbus_addr);
+ u16 rx_addr = SBUS_RX_ADDR(sbus_addr);
+
+ if (((ring == 0 || ring == 1) && (rx_addr == SBUS_RINGX_SBM_SPICO)) ||
+ (rx_addr == SBUS_BCAST_SBM_SPICO)) {
+ return true;
+ }
+ return false;
+}
+
+void
+sbus_addr_to_string(u32 sbus_addr, char *data_str)
+{
+ u8 ring = SBUS_RING(sbus_addr);
+ u16 rx_addr = SBUS_RX_ADDR(sbus_addr);
+
+ /* Valid for any sbus master */
+ if (rx_addr == SBUS_BCAST_PCIE_SERDES_SPICO) {
+ sprintf(data_str, "PCIE_SERDES_SPICO(BROADCAST)");
+ } else if (rx_addr == SBUS_BCAST_PCIE_SERDES_PCS) {
+ sprintf(data_str, "PCIE_SERDES_PCS(BROADCAST)");
+ } else if (rx_addr == SBUS_BCAST_CM4_SERDES_SPICO) {
+ sprintf(data_str, "CM4_SERDES_SPICO(BROADCAST)");
+ } else if (rx_addr == SBUS_BCAST_SBM_SPICO) {
+ sprintf(data_str, "SBM_SPICO(BROADCAST)");
+ } else if (rx_addr == SBUS_BCAST_SBM) {
+ sprintf(data_str, "SBM(BROADCAST)");
+ } else if (rx_addr == SBUS_BCAST_PLL) {
+ sprintf(data_str, "PLL(BROADCAST)");
+ } else if (rx_addr == SBUS_BCAST_PMRO) {
+ sprintf(data_str, "PMRO(BROADCAST)");
+ } else if (rx_addr == SBUS_BCAST_THERMVOLT) {
+ sprintf(data_str, "THERMVOLT(BROADCAST)");
+ /* Valid for Cassini - rings 0 and 1 are the same */
+ } else if (ring == 0 || ring == 1) {
+ if (rx_addr == SBUS_RINGX_PCIE_PLL0)
+ sprintf(data_str, "PCIE_PLL0");
+ else if (rx_addr == SBUS_RINGX_PCIE_PLL1)
+ sprintf(data_str, "PCIE_PLL1");
+ else if (rx_addr == SBUS_RINGX_PCIE_PLL2)
+ sprintf(data_str, "PCIE_PLL2");
+ else if (rx_addr == SBUS_RINGX_CORE_PLL)
+ sprintf(data_str, "CORE_PLL");
+ else if (rx_addr == SBUS_RINGX_PMRO0)
+ sprintf(data_str, "PMRO0");
+ else if (rx_addr == SBUS_RINGX_PMRO1)
+ sprintf(data_str, "PMRO1");
+ else if (is_pcie_serdes_addr(sbus_addr))
+ sprintf(data_str, "PCIE_SERDES_SPICO");
+ else if (is_pcie_serdes_pcs_addr(sbus_addr))
+ sprintf(data_str, "PCIE_SERDES_PCS0");
+ else if (rx_addr == SBUS_RINGX_SBM)
+ sprintf(data_str, "SBM");
+ else if (rx_addr == SBUS_RINGX_SBM_SPICO)
+ sprintf(data_str, "SBM_SPICO");
+ else if (is_cm4_serdes_addr(sbus_addr))
+ sprintf(data_str, "CM4_SERDES");
+ else
+ sprintf(data_str, "UNKNOWN");
+ } else {
+ sprintf(data_str, "UNKNOWN");
+ }
+}
+
+void sbl_sbus_addr_get(u32 *sbus_addr)
+{
+ *sbus_addr = SBUS_ADDR(0, SBUS_RINGX_CM4_SERDES_FIRST);
+}
--
2.56.0
next prev parent reply other threads:[~2026-10-30 21:00 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 20:58 [PATCH 00/23] Subject: [PATCH net-next 00/23] net/hpe: add HPE Slingshot NIC drivers Doug Ledford
2026-10-30 20:58 ` [PATCH 01/23] net: hpe: ss1: add the Cassini headers Doug Ledford
2026-10-30 20:58 ` [PATCH 02/23] net: hpe: sbl: add the Slingshot Base Link headers Doug Ledford
2026-10-30 20:58 ` [PATCH 03/23] net: hpe: sbl: add the core instance and configuration layer Doug Ledford
2026-10-30 20:59 ` [PATCH 04/23] net: hpe: sbl: add the SerDes layer Doug Ledford
2026-10-30 20:59 ` [PATCH 05/23] net: hpe: sbl: add the PML link layer Doug Ledford
2026-10-30 20:59 ` [PATCH 06/23] net: hpe: sbl: add link state management Doug Ledford
2026-10-30 20:59 ` Doug Ledford [this message]
2026-10-30 20:59 ` [PATCH 08/23] net: hpe: sbl: add media, FEC and counter support Doug Ledford
2026-10-30 20:59 ` [PATCH 09/23] net: hpe: sl: add the Slingshot Link headers Doug Ledford
2026-10-30 20:59 ` [PATCH 10/23] net: hpe: sl: add the module entry points Doug Ledford
2026-10-30 20:59 ` [PATCH 11/23] net: hpe: sl: add the core link state machines Doug Ledford
2026-10-30 20:59 ` [PATCH 12/23] net: hpe: sl: add the control layer Doug Ledford
2026-10-30 20:59 ` [PATCH 13/23] net: hpe: sl: add media and cable handling Doug Ledford
2026-10-30 20:59 ` [PATCH 14/23] net: hpe: sl: add the sysfs interface Doug Ledford
2026-10-30 20:59 ` [PATCH 15/23] net: hpe: sl: add the NIC binding and logging helpers Doug Ledford
2026-10-30 20:59 ` [PATCH 16/23] net: hpe: ss1: add the Cassini core device layer Doug Ledford
2026-10-30 20:59 ` [PATCH 17/23] net: hpe: ss1: add the datapath and memory translation layers Doug Ledford
2026-10-30 20:59 ` [PATCH 18/23] net: hpe: ss1: add the link layer glue Doug Ledford
2026-10-30 20:59 ` [PATCH 19/23] net: hpe: ss1: add SR-IOV support Doug Ledford
2026-10-30 20:59 ` [PATCH 20/23] net: hpe: ss1: add the CXI core interfaces Doug Ledford
2026-10-30 20:59 ` [PATCH 21/23] net: hpe: ss1: add resource groups, QoS and services Doug Ledford
2026-10-30 20:59 ` [PATCH 22/23] net: hpe: ss1: add the Ethernet personality Doug Ledford
2026-10-30 20:59 ` [PATCH 23/23] net: hpe: wire up the Slingshot NIC drivers Doug Ledford
2026-10-30 22:11 ` [PATCH 00/23] Subject: [PATCH net-next 00/23] net/hpe: add HPE " Jakub Kicinski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261030-cxi-netdev-submission-v1-7-8c2c391cc283@hpe.com \
--to=doug.ledford@hpe.com \
--cc=andrew+netdev@lunn.ch \
--cc=christian.koenig@amd.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kbuild@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=nathan@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=nsc@kernel.org \
--cc=pabeni@redhat.com \
--cc=richardcochran@gmail.com \
--cc=slingshot-os-team@groups.ext.hpe.com \
--cc=sumit.semwal@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®