[net-next, 03/10] bng_en: Add firmware communication mechanism

Vikas Gupta posted 10 patches 3 months, 3 weeks ago
There is a newer version of this series
[net-next, 03/10] bng_en: Add firmware communication mechanism
Posted by Vikas Gupta 3 months, 3 weeks ago
Add support to communicate with the firmware.
Future patches will use these functions to send the
messages to the firmware.
Functions support allocating request/response buffers
to send a particular command. Each command has certain
timeout value to which the driver waits for response from
the firmware. In error case, commands may be either timed
out waiting on response from the firmware or may return
a specific error code.

Signed-off-by: Vikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: Bhargava Chenna Marreddy <bhargava.marreddy@broadcom.com>
Reviewed-by: Rajashekar Hudumula <rajashekar.hudumula@broadcom.com>
---
 drivers/net/ethernet/broadcom/bnge/Makefile   |   3 +-
 drivers/net/ethernet/broadcom/bnge/bnge.h     |  13 +
 .../net/ethernet/broadcom/bnge/bnge_hwrm.c    | 503 ++++++++++++++++++
 .../net/ethernet/broadcom/bnge/bnge_hwrm.h    | 107 ++++
 4 files changed, 625 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c
 create mode 100644 drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h

diff --git a/drivers/net/ethernet/broadcom/bnge/Makefile b/drivers/net/ethernet/broadcom/bnge/Makefile
index e021a14d2fa0..b296d7de56ce 100644
--- a/drivers/net/ethernet/broadcom/bnge/Makefile
+++ b/drivers/net/ethernet/broadcom/bnge/Makefile
@@ -3,4 +3,5 @@
 obj-$(CONFIG_BNGE) += bng_en.o
 
 bng_en-y := bnge_core.o \
-	    bnge_devlink.o
+	    bnge_devlink.o \
+	    bnge_hwrm.o
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h
index 19d85aabab4e..8f2a562d9ae2 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge.h
@@ -13,6 +13,8 @@ enum board_idx {
 	BCM57708,
 };
 
+#define INVALID_HW_RING_ID      ((u16)-1)
+
 struct bnge_dev {
 	struct device	*dev;
 	struct pci_dev	*pdev;
@@ -22,6 +24,17 @@ struct bnge_dev {
 	char		board_serialno[BNGE_VPD_FLD_LEN];
 
 	void __iomem	*bar0;
+
+	/* HWRM members */
+	u16			hwrm_cmd_seq;
+	u16			hwrm_cmd_kong_seq;
+	struct dma_pool		*hwrm_dma_pool;
+	struct hlist_head	hwrm_pending_list;
+	u16			hwrm_max_req_len;
+	u16			hwrm_max_ext_req_len;
+	unsigned int		hwrm_cmd_timeout;
+	unsigned int		hwrm_cmd_max_timeout;
+	struct mutex		hwrm_cmd_lock;	/* serialize hwrm messages */
 };
 
 #endif /* _BNGE_H_ */
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c
new file mode 100644
index 000000000000..803a1951b736
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+
+#include <asm/byteorder.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+
+#include "bnge.h"
+#include "bnge_hwrm.h"
+
+static u64 hwrm_calc_sentinel(struct bnge_hwrm_ctx *ctx, u16 req_type)
+{
+	return (((uintptr_t)ctx) + req_type) ^ BNGE_HWRM_SENTINEL;
+}
+
+int __hwrm_req_init(struct bnge_dev *bd, void **req, u16 req_type, u32 req_len)
+{
+	struct bnge_hwrm_ctx *ctx;
+	dma_addr_t dma_handle;
+	u8 *req_addr;
+
+	if (req_len > BNGE_HWRM_CTX_OFFSET)
+		return -E2BIG;
+
+	req_addr = dma_pool_alloc(bd->hwrm_dma_pool, GFP_KERNEL | __GFP_ZERO,
+				  &dma_handle);
+	if (!req_addr)
+		return -ENOMEM;
+
+	ctx = (struct bnge_hwrm_ctx *)(req_addr + BNGE_HWRM_CTX_OFFSET);
+	/* safety first, sentinel used to check for invalid requests */
+	ctx->sentinel = hwrm_calc_sentinel(ctx, req_type);
+	ctx->req_len = req_len;
+	ctx->req = (struct input *)req_addr;
+	ctx->resp = (struct output *)(req_addr + BNGE_HWRM_RESP_OFFSET);
+	ctx->dma_handle = dma_handle;
+	ctx->flags = 0; /* __GFP_ZERO, but be explicit regarding ownership */
+	ctx->timeout = bd->hwrm_cmd_timeout ?: DFLT_HWRM_CMD_TIMEOUT;
+	ctx->allocated = BNGE_HWRM_DMA_SIZE - BNGE_HWRM_CTX_OFFSET;
+	ctx->gfp = GFP_KERNEL;
+	ctx->slice_addr = NULL;
+
+	/* initialize common request fields */
+	ctx->req->req_type = cpu_to_le16(req_type);
+	ctx->req->resp_addr = cpu_to_le64(dma_handle + BNGE_HWRM_RESP_OFFSET);
+	ctx->req->cmpl_ring = cpu_to_le16(BNGE_HWRM_NO_CMPL_RING);
+	ctx->req->target_id = cpu_to_le16(BNGE_HWRM_TARGET);
+	*req = ctx->req;
+
+	return 0;
+}
+
+static struct bnge_hwrm_ctx *__hwrm_ctx(struct bnge_dev *bd, u8 *req_addr)
+{
+	void *ctx_addr = req_addr + BNGE_HWRM_CTX_OFFSET;
+	struct input *req = (struct input *)req_addr;
+	struct bnge_hwrm_ctx *ctx = ctx_addr;
+	u64 sentinel;
+
+	if (!req) {
+		dev_err(bd->dev, "null HWRM request");
+		dump_stack();
+		return NULL;
+	}
+
+	/* HWRM API has no type safety, verify sentinel to validate address */
+	sentinel = hwrm_calc_sentinel(ctx, le16_to_cpu(req->req_type));
+	if (ctx->sentinel != sentinel) {
+		dev_err(bd->dev, "HWRM sentinel mismatch, req_type = %u\n",
+			(u32)le16_to_cpu(req->req_type));
+		dump_stack();
+		return NULL;
+	}
+
+	return ctx;
+}
+
+void hwrm_req_timeout(struct bnge_dev *bd, void *req, unsigned int timeout)
+{
+	struct bnge_hwrm_ctx *ctx = __hwrm_ctx(bd, req);
+
+	if (ctx)
+		ctx->timeout = timeout;
+}
+
+void hwrm_req_alloc_flags(struct bnge_dev *bd, void *req, gfp_t gfp)
+{
+	struct bnge_hwrm_ctx *ctx = __hwrm_ctx(bd, req);
+
+	if (ctx)
+		ctx->gfp = gfp;
+}
+
+void hwrm_req_flags(struct bnge_dev *bd, void *req,
+		    enum bnge_hwrm_ctx_flags flags)
+{
+	struct bnge_hwrm_ctx *ctx = __hwrm_ctx(bd, req);
+
+	if (ctx)
+		ctx->flags |= (flags & HWRM_API_FLAGS);
+}
+
+void *hwrm_req_hold(struct bnge_dev *bd, void *req)
+{
+	struct bnge_hwrm_ctx *ctx = __hwrm_ctx(bd, req);
+	struct input *input = (struct input *)req;
+
+	if (!ctx)
+		return NULL;
+
+	if (ctx->flags & BNGE_HWRM_INTERNAL_CTX_OWNED) {
+		dev_err(bd->dev, "HWRM context already owned, req_type = %u\n",
+			(u32)le16_to_cpu(input->req_type));
+		dump_stack();
+		return NULL;
+	}
+
+	ctx->flags |= BNGE_HWRM_INTERNAL_CTX_OWNED;
+	return ((u8 *)req) + BNGE_HWRM_RESP_OFFSET;
+}
+
+static void __hwrm_ctx_drop(struct bnge_dev *bd, struct bnge_hwrm_ctx *ctx)
+{
+	void *addr = ((u8 *)ctx) - BNGE_HWRM_CTX_OFFSET;
+	dma_addr_t dma_handle = ctx->dma_handle; /* save before invalidate */
+
+	/* unmap any auxiliary DMA slice */
+	if (ctx->slice_addr)
+		dma_free_coherent(bd->dev, ctx->slice_size,
+				  ctx->slice_addr, ctx->slice_handle);
+
+	/* invalidate, ensure ownership, sentinel and dma_handle are cleared */
+	memset(ctx, 0, sizeof(struct bnge_hwrm_ctx));
+
+	/* return the buffer to the DMA pool */
+	if (dma_handle)
+		dma_pool_free(bd->hwrm_dma_pool, addr, dma_handle);
+}
+
+void hwrm_req_drop(struct bnge_dev *bd, void *req)
+{
+	struct bnge_hwrm_ctx *ctx = __hwrm_ctx(bd, req);
+
+	if (ctx)
+		__hwrm_ctx_drop(bd, ctx);
+}
+
+static int __hwrm_to_stderr(u32 hwrm_err)
+{
+	switch (hwrm_err) {
+	case HWRM_ERR_CODE_SUCCESS:
+		return 0;
+	case HWRM_ERR_CODE_RESOURCE_LOCKED:
+		return -EROFS;
+	case HWRM_ERR_CODE_RESOURCE_ACCESS_DENIED:
+		return -EACCES;
+	case HWRM_ERR_CODE_RESOURCE_ALLOC_ERROR:
+		return -ENOSPC;
+	case HWRM_ERR_CODE_INVALID_PARAMS:
+	case HWRM_ERR_CODE_INVALID_FLAGS:
+	case HWRM_ERR_CODE_INVALID_ENABLES:
+	case HWRM_ERR_CODE_UNSUPPORTED_TLV:
+	case HWRM_ERR_CODE_UNSUPPORTED_OPTION_ERR:
+		return -EINVAL;
+	case HWRM_ERR_CODE_NO_BUFFER:
+		return -ENOMEM;
+	case HWRM_ERR_CODE_HOT_RESET_PROGRESS:
+	case HWRM_ERR_CODE_BUSY:
+		return -EAGAIN;
+	case HWRM_ERR_CODE_CMD_NOT_SUPPORTED:
+		return -EOPNOTSUPP;
+	case HWRM_ERR_CODE_PF_UNAVAILABLE:
+		return -ENODEV;
+	default:
+		return -EIO;
+	}
+}
+
+static struct bnge_hwrm_wait_token *
+__hwrm_acquire_token(struct bnge_dev *bd, enum bnge_hwrm_chnl dst)
+{
+	struct bnge_hwrm_wait_token *token;
+
+	token = kzalloc(sizeof(*token), GFP_KERNEL);
+	if (!token)
+		return NULL;
+
+	mutex_lock(&bd->hwrm_cmd_lock);
+
+	token->dst = dst;
+	token->state = BNGE_HWRM_PENDING;
+	if (dst == BNGE_HWRM_CHNL_CHIMP) {
+		token->seq_id = bd->hwrm_cmd_seq++;
+		hlist_add_head_rcu(&token->node, &bd->hwrm_pending_list);
+	} else {
+		token->seq_id = bd->hwrm_cmd_kong_seq++;
+	}
+
+	return token;
+}
+
+static void
+__hwrm_release_token(struct bnge_dev *bd, struct bnge_hwrm_wait_token *token)
+{
+	if (token->dst == BNGE_HWRM_CHNL_CHIMP) {
+		hlist_del_rcu(&token->node);
+		kfree_rcu(token, rcu);
+	} else {
+		kfree(token);
+	}
+	mutex_unlock(&bd->hwrm_cmd_lock);
+}
+
+static void hwrm_req_dbg(struct bnge_dev *bd, struct input *req)
+{
+	u32 ring = le16_to_cpu(req->cmpl_ring);
+	u32 type = le16_to_cpu(req->req_type);
+	u32 tgt = le16_to_cpu(req->target_id);
+	u32 seq = le16_to_cpu(req->seq_id);
+	char opt[32] = "\n";
+
+	if (unlikely(ring != (u16)BNGE_HWRM_NO_CMPL_RING))
+		snprintf(opt, 16, " ring %d\n", ring);
+
+	if (unlikely(tgt != BNGE_HWRM_TARGET))
+		snprintf(opt + strlen(opt) - 1, 16, " tgt 0x%x\n", tgt);
+
+	dev_dbg(bd->dev, "sent hwrm req_type 0x%x seq id 0x%x%s",
+		type, seq, opt);
+}
+
+#define hwrm_err(bd, ctx, fmt, ...)				       \
+	do {							       \
+		if ((ctx)->flags & BNGE_HWRM_CTX_SILENT)	       \
+			dev_dbg((bd)->dev, fmt, __VA_ARGS__);       \
+		else						       \
+			dev_err((bd)->dev, fmt, __VA_ARGS__);       \
+	} while (0)
+
+static int __hwrm_send(struct bnge_dev *bd, struct bnge_hwrm_ctx *ctx)
+{
+	u32 doorbell_offset = BNGE_GRCPF_REG_CHIMP_COMM_TRIGGER;
+	enum bnge_hwrm_chnl dst = BNGE_HWRM_CHNL_CHIMP;
+	u32 bar_offset = BNGE_GRCPF_REG_CHIMP_COMM;
+	struct bnge_hwrm_wait_token *token = NULL;
+	u16 max_req_len = BNGE_HWRM_MAX_REQ_LEN;
+	unsigned int i, timeout, tmo_count;
+	u32 *data = (u32 *)ctx->req;
+	u32 msg_len = ctx->req_len;
+	int rc = -EBUSY;
+	u32 req_type;
+	u16 len = 0;
+	u8 *valid;
+
+	if (ctx->flags & BNGE_HWRM_INTERNAL_RESP_DIRTY)
+		memset(ctx->resp, 0, PAGE_SIZE);
+
+	req_type = le16_to_cpu(ctx->req->req_type);
+
+	if (msg_len > BNGE_HWRM_MAX_REQ_LEN &&
+	    msg_len > bd->hwrm_max_ext_req_len) {
+		dev_warn(bd->dev, "oversized hwrm request, req_type 0x%x",
+			 req_type);
+		rc = -E2BIG;
+		goto exit;
+	}
+
+	token = __hwrm_acquire_token(bd, dst);
+	if (!token) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+	ctx->req->seq_id = cpu_to_le16(token->seq_id);
+
+	/* Ensure any associated DMA buffers are written before doorbell */
+	wmb();
+
+	/* Write request msg to hwrm channel */
+	__iowrite32_copy(bd->bar0 + bar_offset, data, msg_len / 4);
+
+	for (i = msg_len; i < max_req_len; i += 4)
+		writel(0, bd->bar0 + bar_offset + i);
+
+	/* Ring channel doorbell */
+	writel(1, bd->bar0 + doorbell_offset);
+
+	hwrm_req_dbg(bd, ctx->req);
+
+	/* Limit timeout to an upper limit */
+	timeout = min(ctx->timeout,
+		      bd->hwrm_cmd_max_timeout ?: HWRM_CMD_MAX_TIMEOUT);
+	/* convert timeout to usec */
+	timeout *= 1000;
+
+	i = 0;
+	/* Short timeout for the first few iterations:
+	 * number of loops = number of loops for short timeout +
+	 * number of loops for standard timeout.
+	 */
+	tmo_count = HWRM_SHORT_TIMEOUT_COUNTER;
+	timeout = timeout - HWRM_SHORT_MIN_TIMEOUT * HWRM_SHORT_TIMEOUT_COUNTER;
+	tmo_count += DIV_ROUND_UP(timeout, HWRM_MIN_TIMEOUT);
+
+	if (le16_to_cpu(ctx->req->cmpl_ring) != INVALID_HW_RING_ID) {
+		/* Wait until hwrm response cmpl interrupt is processed */
+		while (READ_ONCE(token->state) < BNGE_HWRM_COMPLETE &&
+		       i++ < tmo_count) {
+			/* on first few passes, just barely sleep */
+			if (i < HWRM_SHORT_TIMEOUT_COUNTER) {
+				usleep_range(HWRM_SHORT_MIN_TIMEOUT,
+					     HWRM_SHORT_MAX_TIMEOUT);
+			} else {
+				usleep_range(HWRM_MIN_TIMEOUT,
+					     HWRM_MAX_TIMEOUT);
+			}
+		}
+
+		if (READ_ONCE(token->state) != BNGE_HWRM_COMPLETE) {
+			hwrm_err(bd, ctx, "Resp cmpl intr err msg: 0x%x\n",
+				 req_type);
+			goto exit;
+		}
+		len = le16_to_cpu(READ_ONCE(ctx->resp->resp_len));
+		valid = ((u8 *)ctx->resp) + len - 1;
+	} else {
+		__le16 seen_out_of_seq = ctx->req->seq_id; /* will never see */
+		int j;
+
+		/* Check if response len is updated */
+		for (i = 0; i < tmo_count; i++) {
+			if (token &&
+			    READ_ONCE(token->state) == BNGE_HWRM_DEFERRED) {
+				__hwrm_release_token(bd, token);
+				token = NULL;
+			}
+
+			len = le16_to_cpu(READ_ONCE(ctx->resp->resp_len));
+			if (len) {
+				__le16 resp_seq = READ_ONCE(ctx->resp->seq_id);
+
+				if (resp_seq == ctx->req->seq_id)
+					break;
+				if (resp_seq != seen_out_of_seq) {
+					dev_warn(bd->dev, "Discarding out of seq response: 0x%x for msg {0x%x 0x%x}\n",
+						 le16_to_cpu(resp_seq), req_type, le16_to_cpu(ctx->req->seq_id));
+					seen_out_of_seq = resp_seq;
+				}
+			}
+
+			/* on first few passes, just barely sleep */
+			if (i < HWRM_SHORT_TIMEOUT_COUNTER) {
+				usleep_range(HWRM_SHORT_MIN_TIMEOUT,
+					     HWRM_SHORT_MAX_TIMEOUT);
+			} else {
+				usleep_range(HWRM_MIN_TIMEOUT,
+					     HWRM_MAX_TIMEOUT);
+			}
+		}
+
+		if (i >= tmo_count) {
+			hwrm_err(bd, ctx, "Error (timeout: %u) msg {0x%x 0x%x} len:%d\n",
+				 hwrm_total_timeout(i), req_type,
+				 le16_to_cpu(ctx->req->seq_id), len);
+			goto exit;
+		}
+
+		/* Last byte of resp contains valid bit */
+		valid = ((u8 *)ctx->resp) + len - 1;
+		for (j = 0; j < HWRM_VALID_BIT_DELAY_USEC; ) {
+			/* make sure we read from updated DMA memory */
+			dma_rmb();
+			if (*valid)
+				break;
+			if (j < 10) {
+				udelay(1);
+				j++;
+			} else {
+				usleep_range(20, 30);
+				j += 20;
+			}
+		}
+
+		if (j >= HWRM_VALID_BIT_DELAY_USEC) {
+			hwrm_err(bd, ctx, "Error (timeout: %u) msg {0x%x 0x%x} len:%d v:%d\n",
+				 hwrm_total_timeout(i) + j, req_type,
+				 le16_to_cpu(ctx->req->seq_id), len, *valid);
+			goto exit;
+		}
+	}
+
+	/* Zero valid bit for compatibility.  Valid bit in an older spec
+	 * may become a new field in a newer spec.  We must make sure that
+	 * a new field not implemented by old spec will read zero.
+	 */
+	*valid = 0;
+	rc = le16_to_cpu(ctx->resp->error_code);
+	if (rc == HWRM_ERR_CODE_BUSY && !(ctx->flags & BNGE_HWRM_CTX_SILENT))
+		dev_warn(bd->dev, "FW returned busy, hwrm req_type 0x%x\n",
+			 req_type);
+	else if (rc && rc != HWRM_ERR_CODE_PF_UNAVAILABLE)
+		hwrm_err(bd, ctx, "hwrm req_type 0x%x seq id 0x%x error %d\n",
+			 req_type, le16_to_cpu(ctx->req->seq_id), rc);
+	rc = __hwrm_to_stderr(rc);
+
+exit:
+	if (token)
+		__hwrm_release_token(bd, token);
+	if (ctx->flags & BNGE_HWRM_INTERNAL_CTX_OWNED)
+		ctx->flags |= BNGE_HWRM_INTERNAL_RESP_DIRTY;
+	else
+		__hwrm_ctx_drop(bd, ctx);
+	return rc;
+}
+
+int hwrm_req_send(struct bnge_dev *bd, void *req)
+{
+	struct bnge_hwrm_ctx *ctx = __hwrm_ctx(bd, req);
+
+	if (!ctx)
+		return -EINVAL;
+
+	return __hwrm_send(bd, ctx);
+}
+
+int hwrm_req_send_silent(struct bnge_dev *bd, void *req)
+{
+	hwrm_req_flags(bd, req, BNGE_HWRM_CTX_SILENT);
+	return hwrm_req_send(bd, req);
+}
+
+void *
+hwrm_req_dma_slice(struct bnge_dev *bd, void *req, u32 size,
+		   dma_addr_t *dma_handle)
+{
+	struct bnge_hwrm_ctx *ctx = __hwrm_ctx(bd, req);
+	u8 *end = ((u8 *)req) + BNGE_HWRM_DMA_SIZE;
+	struct input *input = req;
+	u8 *addr, *req_addr = req;
+	u32 max_offset, offset;
+
+	if (!ctx)
+		return NULL;
+
+	max_offset = BNGE_HWRM_DMA_SIZE - ctx->allocated;
+	offset = max_offset - size;
+	offset = ALIGN_DOWN(offset, BNGE_HWRM_DMA_ALIGN);
+	addr = req_addr + offset;
+
+	if (addr < req_addr + max_offset && req_addr + ctx->req_len <= addr) {
+		ctx->allocated = end - addr;
+		*dma_handle = ctx->dma_handle + offset;
+		return addr;
+	}
+
+	if (ctx->slice_addr) {
+		dev_err(bd->dev, "HWRM refusing to reallocate DMA slice, req_type = %u\n",
+			(u32)le16_to_cpu(input->req_type));
+		dump_stack();
+		return NULL;
+	}
+
+	addr = dma_alloc_coherent(bd->dev, size, dma_handle, ctx->gfp);
+	if (!addr)
+		return NULL;
+
+	ctx->slice_addr = addr;
+	ctx->slice_size = size;
+	ctx->slice_handle = *dma_handle;
+
+	return addr;
+}
+
+void bnge_cleanup_hwrm_resources(struct bnge_dev *bd)
+{
+	struct bnge_hwrm_wait_token *token;
+
+	dma_pool_destroy(bd->hwrm_dma_pool);
+	bd->hwrm_dma_pool = NULL;
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(token, &bd->hwrm_pending_list, node)
+		WRITE_ONCE(token->state, BNGE_HWRM_CANCELLED);
+	rcu_read_unlock();
+}
+
+int bnge_init_hwrm_resources(struct bnge_dev *bd)
+{
+	bd->hwrm_dma_pool = dma_pool_create("bnge_hwrm", bd->dev,
+					    BNGE_HWRM_DMA_SIZE,
+					    BNGE_HWRM_DMA_ALIGN, 0);
+	if (!bd->hwrm_dma_pool)
+		return -ENOMEM;
+
+	INIT_HLIST_HEAD(&bd->hwrm_pending_list);
+	mutex_init(&bd->hwrm_cmd_lock);
+
+	return 0;
+}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h
new file mode 100644
index 000000000000..c14f03daab4b
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2025 Broadcom */
+
+#ifndef _BNGE_HWRM_H_
+#define _BNGE_HWRM_H_
+
+#include "../bnxt/bnxt_hsi.h"
+
+enum bnge_hwrm_ctx_flags {
+	BNGE_HWRM_INTERNAL_CTX_OWNED	= BIT(0),
+	BNGE_HWRM_INTERNAL_RESP_DIRTY	= BIT(1),
+	BNGE_HWRM_CTX_SILENT		= BIT(2),
+	BNGE_HWRM_FULL_WAIT		= BIT(3),
+};
+
+#define HWRM_API_FLAGS (BNGE_HWRM_CTX_SILENT | BNGE_HWRM_FULL_WAIT)
+
+struct bnge_hwrm_ctx {
+	u64 sentinel;
+	dma_addr_t dma_handle;
+	struct output *resp;
+	struct input *req;
+	dma_addr_t slice_handle;
+	void *slice_addr;
+	u32 slice_size;
+	u32 req_len;
+	enum bnge_hwrm_ctx_flags flags;
+	unsigned int timeout;
+	u32 allocated;
+	gfp_t gfp;
+};
+
+enum bnge_hwrm_wait_state {
+	BNGE_HWRM_PENDING,
+	BNGE_HWRM_DEFERRED,
+	BNGE_HWRM_COMPLETE,
+	BNGE_HWRM_CANCELLED,
+};
+
+enum bnge_hwrm_chnl { BNGE_HWRM_CHNL_CHIMP, BNGE_HWRM_CHNL_KONG };
+
+struct bnge_hwrm_wait_token {
+	struct rcu_head rcu;
+	struct hlist_node node;
+	enum bnge_hwrm_wait_state state;
+	enum bnge_hwrm_chnl dst;
+	u16 seq_id;
+};
+
+#define DFLT_HWRM_CMD_TIMEOUT		500
+
+#define BNGE_GRCPF_REG_CHIMP_COMM		0x0
+#define BNGE_GRCPF_REG_CHIMP_COMM_TRIGGER	0x100
+
+#define BNGE_HWRM_MAX_REQ_LEN		(bd->hwrm_max_req_len)
+#define BNGE_HWRM_SHORT_REQ_LEN		sizeof(struct hwrm_short_input)
+#define HWRM_CMD_MAX_TIMEOUT		40000U
+#define SHORT_HWRM_CMD_TIMEOUT		20
+#define HWRM_CMD_TIMEOUT		(bd->hwrm_cmd_timeout)
+#define HWRM_RESET_TIMEOUT		((HWRM_CMD_TIMEOUT) * 4)
+#define BNGE_HWRM_TARGET		0xffff
+#define BNGE_HWRM_NO_CMPL_RING		-1
+#define BNGE_HWRM_REQ_MAX_SIZE		128
+#define BNGE_HWRM_DMA_SIZE		(2 * PAGE_SIZE) /* space for req+resp */
+#define BNGE_HWRM_RESP_RESERVED		PAGE_SIZE
+#define BNGE_HWRM_RESP_OFFSET		(BNGE_HWRM_DMA_SIZE -		\
+					 BNGE_HWRM_RESP_RESERVED)
+#define BNGE_HWRM_CTX_OFFSET		(BNGE_HWRM_RESP_OFFSET -	\
+					 sizeof(struct bnge_hwrm_ctx))
+#define BNGE_HWRM_DMA_ALIGN		16
+#define BNGE_HWRM_SENTINEL		0xb6e1f68a12e9a7eb /* arbitrary value */
+#define BNGE_HWRM_REQS_PER_PAGE	(BNGE_PAGE_SIZE /	\
+					 BNGE_HWRM_REQ_MAX_SIZE)
+#define HWRM_SHORT_MIN_TIMEOUT		3
+#define HWRM_SHORT_MAX_TIMEOUT		10
+#define HWRM_SHORT_TIMEOUT_COUNTER	5
+
+#define HWRM_MIN_TIMEOUT		25
+#define HWRM_MAX_TIMEOUT		40
+
+static inline unsigned int hwrm_total_timeout(unsigned int n)
+{
+	return n <= HWRM_SHORT_TIMEOUT_COUNTER ? n * HWRM_SHORT_MIN_TIMEOUT :
+		HWRM_SHORT_TIMEOUT_COUNTER * HWRM_SHORT_MIN_TIMEOUT +
+		(n - HWRM_SHORT_TIMEOUT_COUNTER) * HWRM_MIN_TIMEOUT;
+}
+
+#define HWRM_VALID_BIT_DELAY_USEC	50000
+
+void bnge_cleanup_hwrm_resources(struct bnge_dev *bd);
+int bnge_init_hwrm_resources(struct bnge_dev *bd);
+
+int __hwrm_req_init(struct bnge_dev *bd, void **req, u16 req_type, u32 req_len);
+#define hwrm_req_init(bd, req, req_type) \
+	__hwrm_req_init((bd), (void **)&(req), (req_type), sizeof(*(req)))
+void *hwrm_req_hold(struct bnge_dev *bd, void *req);
+void hwrm_req_drop(struct bnge_dev *bd, void *req);
+void hwrm_req_flags(struct bnge_dev *bd, void *req,
+		    enum bnge_hwrm_ctx_flags flags);
+void hwrm_req_timeout(struct bnge_dev *bd, void *req, unsigned int timeout);
+int hwrm_req_send(struct bnge_dev *bd, void *req);
+int hwrm_req_send_silent(struct bnge_dev *bd, void *req);
+void hwrm_req_alloc_flags(struct bnge_dev *bd, void *req, gfp_t flags);
+void *hwrm_req_dma_slice(struct bnge_dev *bd, void *req, u32 size,
+			 dma_addr_t *dma);
+
+#endif /* _BNGE_HWRM_H_ */
-- 
2.47.1
Re: [net-next, 03/10] bng_en: Add firmware communication mechanism
Posted by Vadim Fedorenko 3 months, 3 weeks ago
On 18/06/2025 15:47, Vikas Gupta wrote:
> Add support to communicate with the firmware.
> Future patches will use these functions to send the
> messages to the firmware.
> Functions support allocating request/response buffers
> to send a particular command. Each command has certain
> timeout value to which the driver waits for response from
> the firmware. In error case, commands may be either timed
> out waiting on response from the firmware or may return
> a specific error code.
> 
> Signed-off-by: Vikas Gupta <vikas.gupta@broadcom.com>
> Reviewed-by: Bhargava Chenna Marreddy <bhargava.marreddy@broadcom.com>
> Reviewed-by: Rajashekar Hudumula <rajashekar.hudumula@broadcom.com>
> ---
>   drivers/net/ethernet/broadcom/bnge/Makefile   |   3 +-
>   drivers/net/ethernet/broadcom/bnge/bnge.h     |  13 +
>   .../net/ethernet/broadcom/bnge/bnge_hwrm.c    | 503 ++++++++++++++++++
>   .../net/ethernet/broadcom/bnge/bnge_hwrm.h    | 107 ++++
>   4 files changed, 625 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c
>   create mode 100644 drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h
> 
> diff --git a/drivers/net/ethernet/broadcom/bnge/Makefile b/drivers/net/ethernet/broadcom/bnge/Makefile
> index e021a14d2fa0..b296d7de56ce 100644
> --- a/drivers/net/ethernet/broadcom/bnge/Makefile
> +++ b/drivers/net/ethernet/broadcom/bnge/Makefile
> @@ -3,4 +3,5 @@
>   obj-$(CONFIG_BNGE) += bng_en.o
>   
>   bng_en-y := bnge_core.o \
> -	    bnge_devlink.o
> +	    bnge_devlink.o \
> +	    bnge_hwrm.o
> diff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h
> index 19d85aabab4e..8f2a562d9ae2 100644
> --- a/drivers/net/ethernet/broadcom/bnge/bnge.h
> +++ b/drivers/net/ethernet/broadcom/bnge/bnge.h
> @@ -13,6 +13,8 @@ enum board_idx {
>   	BCM57708,
>   };
>   
> +#define INVALID_HW_RING_ID      ((u16)-1)
> +
>   struct bnge_dev {
>   	struct device	*dev;
>   	struct pci_dev	*pdev;
> @@ -22,6 +24,17 @@ struct bnge_dev {
>   	char		board_serialno[BNGE_VPD_FLD_LEN];
>   
>   	void __iomem	*bar0;
> +
> +	/* HWRM members */
> +	u16			hwrm_cmd_seq;
> +	u16			hwrm_cmd_kong_seq;
> +	struct dma_pool		*hwrm_dma_pool;
> +	struct hlist_head	hwrm_pending_list;
> +	u16			hwrm_max_req_len;
> +	u16			hwrm_max_ext_req_len;
> +	unsigned int		hwrm_cmd_timeout;
> +	unsigned int		hwrm_cmd_max_timeout;
> +	struct mutex		hwrm_cmd_lock;	/* serialize hwrm messages */
>   };

It's all looks pretty similar to what is used in bnxt driver. Why do you
duplicate the code rather then reusing (and improving) the existing one?

I didn't look carefully, but in case it's impossible to merge hwrm code
from bnxt, you have to make function names prepended with bnge prefix...
Re: [net-next, 03/10] bng_en: Add firmware communication mechanism
Posted by Vikas Gupta 3 months, 2 weeks ago
Hi Vadim,

On Thu, Jun 19, 2025 at 6:13 PM Vadim Fedorenko
<vadim.fedorenko@linux.dev> wrote:
>
> On 18/06/2025 15:47, Vikas Gupta wrote:
> > Add support to communicate with the firmware.
> > Future patches will use these functions to send the
> > messages to the firmware.
> > Functions support allocating request/response buffers
> > to send a particular command. Each command has certain
> > timeout value to which the driver waits for response from
> > the firmware. In error case, commands may be either timed
> > out waiting on response from the firmware or may return
> > a specific error code.
> >
> > Signed-off-by: Vikas Gupta <vikas.gupta@broadcom.com>
> > Reviewed-by: Bhargava Chenna Marreddy <bhargava.marreddy@broadcom.com>
> > Reviewed-by: Rajashekar Hudumula <rajashekar.hudumula@broadcom.com>
> > ---
> >   drivers/net/ethernet/broadcom/bnge/Makefile   |   3 +-
> >   drivers/net/ethernet/broadcom/bnge/bnge.h     |  13 +
> >   .../net/ethernet/broadcom/bnge/bnge_hwrm.c    | 503 ++++++++++++++++++
> >   .../net/ethernet/broadcom/bnge/bnge_hwrm.h    | 107 ++++
> >   4 files changed, 625 insertions(+), 1 deletion(-)
> >   create mode 100644 drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c
> >   create mode 100644 drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h
> >
> > diff --git a/drivers/net/ethernet/broadcom/bnge/Makefile b/drivers/net/ethernet/broadcom/bnge/Makefile
> > index e021a14d2fa0..b296d7de56ce 100644
> > --- a/drivers/net/ethernet/broadcom/bnge/Makefile
> > +++ b/drivers/net/ethernet/broadcom/bnge/Makefile
> > @@ -3,4 +3,5 @@
> >   obj-$(CONFIG_BNGE) += bng_en.o
> >
> >   bng_en-y := bnge_core.o \
> > -         bnge_devlink.o
> > +         bnge_devlink.o \
> > +         bnge_hwrm.o
> > diff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h
> > index 19d85aabab4e..8f2a562d9ae2 100644
> > --- a/drivers/net/ethernet/broadcom/bnge/bnge.h
> > +++ b/drivers/net/ethernet/broadcom/bnge/bnge.h
> > @@ -13,6 +13,8 @@ enum board_idx {
> >       BCM57708,
> >   };
> >
> > +#define INVALID_HW_RING_ID      ((u16)-1)
> > +
> >   struct bnge_dev {
> >       struct device   *dev;
> >       struct pci_dev  *pdev;
> > @@ -22,6 +24,17 @@ struct bnge_dev {
> >       char            board_serialno[BNGE_VPD_FLD_LEN];
> >
> >       void __iomem    *bar0;
> > +
> > +     /* HWRM members */
> > +     u16                     hwrm_cmd_seq;
> > +     u16                     hwrm_cmd_kong_seq;
> > +     struct dma_pool         *hwrm_dma_pool;
> > +     struct hlist_head       hwrm_pending_list;
> > +     u16                     hwrm_max_req_len;
> > +     u16                     hwrm_max_ext_req_len;
> > +     unsigned int            hwrm_cmd_timeout;
> > +     unsigned int            hwrm_cmd_max_timeout;
> > +     struct mutex            hwrm_cmd_lock;  /* serialize hwrm messages */
> >   };
>
> It's all looks pretty similar to what is used in bnxt driver. Why do you
> duplicate the code rather then reusing (and improving) the existing one?
>
> I didn't look carefully, but in case it's impossible to merge hwrm code
> from bnxt, you have to make function names prepended with bnge prefix...

 Both the bnxt and bnge drivers follow the same protocol to send the
requests with the firmware,
so the HWRM mechanism is similar. I'll consider renaming the function
names in v2.

>
>
>
Re: [net-next, 03/10] bng_en: Add firmware communication mechanism
Posted by kernel test robot 3 months, 3 weeks ago
Hi Vikas,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.16-rc2 next-20250618]
[cannot apply to horms-ipvs/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vikas-Gupta/bng_en-Add-PCI-interface/20250618-173130
base:   linus/master
patch link:    https://lore.kernel.org/r/20250618144743.843815-4-vikas.gupta%40broadcom.com
patch subject: [net-next, 03/10] bng_en: Add firmware communication mechanism
config: parisc-randconfig-r073-20250619 (https://download.01.org/0day-ci/archive/20250619/202506191741.1C9E7i3x-lkp@intel.com/config)
compiler: hppa-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250619/202506191741.1C9E7i3x-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506191741.1C9E7i3x-lkp@intel.com/

All errors (new ones prefixed by >>):

   hppa-linux-ld: hppa-linux-ld: DWARF error: could not find abbrev number 1754059
   drivers/net/ethernet/broadcom/bnge/bnge_hwrm.o: in function `__hwrm_req_init':
>> bnge_hwrm.c:(.text+0x96c): multiple definition of `__hwrm_req_init'; hppa-linux-ld: DWARF error: could not find abbrev number 179644068
   drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.o:bnxt_hwrm.c:(.text+0xccc): first defined here
   hppa-linux-ld: drivers/net/ethernet/broadcom/bnge/bnge_hwrm.o: in function `hwrm_req_timeout':
>> bnge_hwrm.c:(.text+0xa90): multiple definition of `hwrm_req_timeout'; drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.o:bnxt_hwrm.c:(.text+0xdf0): first defined here
   hppa-linux-ld: drivers/net/ethernet/broadcom/bnge/bnge_hwrm.o: in function `hwrm_req_alloc_flags':
>> bnge_hwrm.c:(.text+0xac8): multiple definition of `hwrm_req_alloc_flags'; drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.o:bnxt_hwrm.c:(.text+0xe28): first defined here
   hppa-linux-ld: drivers/net/ethernet/broadcom/bnge/bnge_hwrm.o: in function `hwrm_req_flags':
>> bnge_hwrm.c:(.text+0xb00): multiple definition of `hwrm_req_flags'; drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.o:bnxt_hwrm.c:(.text+0xf9c): first defined here
   hppa-linux-ld: drivers/net/ethernet/broadcom/bnge/bnge_hwrm.o: in function `hwrm_req_hold':
>> bnge_hwrm.c:(.text+0xb48): multiple definition of `hwrm_req_hold'; drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.o:bnxt_hwrm.c:(.text+0xfe4): first defined here
   hppa-linux-ld: drivers/net/ethernet/broadcom/bnge/bnge_hwrm.o: in function `hwrm_req_drop':
>> bnge_hwrm.c:(.text+0xbd0): multiple definition of `hwrm_req_drop'; drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.o:bnxt_hwrm.c:(.text+0x106c): first defined here
   hppa-linux-ld: drivers/net/ethernet/broadcom/bnge/bnge_hwrm.o: in function `hwrm_req_send':
>> bnge_hwrm.c:(.text+0xc10): multiple definition of `hwrm_req_send'; drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.o:bnxt_hwrm.c:(.text+0x12f0): first defined here
   hppa-linux-ld: drivers/net/ethernet/broadcom/bnge/bnge_hwrm.o: in function `hwrm_req_send_silent':
>> bnge_hwrm.c:(.text+0xc50): multiple definition of `hwrm_req_send_silent'; drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.o:bnxt_hwrm.c:(.text+0x1330): first defined here
   hppa-linux-ld: drivers/net/ethernet/broadcom/bnge/bnge_hwrm.o: in function `hwrm_req_dma_slice':
>> bnge_hwrm.c:(.text+0xca8): multiple definition of `hwrm_req_dma_slice'; drivers/net/ethernet/broadcom/bnxt/bnxt_hwrm.o:bnxt_hwrm.c:(.text+0x1388): first defined here

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki