mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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


  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®