Commit c6d3f019 authored by Dong Yibo's avatar Dong Yibo Committed by Jakub Kicinski
Browse files

net: rnpgbe: Add basic mbx_fw support



Add fundamental firmware (FW) communication operations via PF-FW
mailbox, including:
- FW sync (via HW info query with retries)
- HW reset (post FW command to reset hardware)
- MAC address retrieval (request FW for port-specific MAC)
- Power management (powerup/powerdown notification to FW)

Signed-off-by: default avatarDong Yibo <dong100@mucse.com>
Reviewed-by: default avatarVadim Fedorenko <vadim.fedorenko@linux.dev>
Link: https://patch.msgid.link/20251101013849.120565-5-dong100@mucse.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 4543534c
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -7,4 +7,5 @@
obj-$(CONFIG_MGBE) += rnpgbe.o
rnpgbe-objs := rnpgbe_main.o\
	       rnpgbe_chip.o\
	       rnpgbe_mbx.o
	       rnpgbe_mbx.o\
	       rnpgbe_mbx_fw.o
+4 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#define _RNPGBE_H

#include <linux/types.h>
#include <linux/mutex.h>

enum rnpgbe_boards {
	board_n500,
@@ -16,6 +17,8 @@ struct mucse_mbx_info {
	u32 delay_us;
	u16 fw_req;
	u16 fw_ack;
	/* lock for only one use mbx */
	struct mutex lock;
	/* fw <--> pf mbx */
	u32 fwpf_shm_base;
	u32 pf2fw_mbx_ctrl;
@@ -26,6 +29,7 @@ struct mucse_mbx_info {
struct mucse_hw {
	void __iomem *hw_addr;
	struct mucse_mbx_info mbx;
	u8 pfvfnum;
};

struct mucse {
+1 −0
Original line number Diff line number Diff line
@@ -401,5 +401,6 @@ void mucse_init_mbx_params_pf(struct mucse_hw *hw)

	mbx->delay_us = 100;
	mbx->timeout_us = 4 * USEC_PER_SEC;
	mutex_init(&mbx->lock);
	mucse_mbx_reset(hw);
}
+191 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2020 - 2025 Mucse Corporation. */

#include <linux/if_ether.h>
#include <linux/bitfield.h>

#include "rnpgbe.h"
#include "rnpgbe_mbx.h"
#include "rnpgbe_mbx_fw.h"

/**
 * mucse_fw_send_cmd_wait_resp - Send cmd req and wait for response
 * @hw: pointer to the HW structure
 * @req: pointer to the cmd req structure
 * @reply: pointer to the fw reply structure
 *
 * mucse_fw_send_cmd_wait_resp sends req to pf-fw mailbox and wait
 * reply from fw.
 *
 * Return: 0 on success, negative errno on failure
 **/
static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw,
				       struct mbx_fw_cmd_req *req,
				       struct mbx_fw_cmd_reply *reply)
{
	int len = le16_to_cpu(req->datalen);
	int retry_cnt = 3;
	int err;

	mutex_lock(&hw->mbx.lock);
	err = mucse_write_and_wait_ack_mbx(hw, (u32 *)req, len);
	if (err)
		goto out;
	do {
		err = mucse_poll_and_read_mbx(hw, (u32 *)reply,
					      sizeof(*reply));
		if (err)
			goto out;
		/* mucse_write_and_wait_ack_mbx return 0 means fw has
		 * received request, wait for the expect opcode
		 * reply with 'retry_cnt' times.
		 */
	} while (--retry_cnt >= 0 && reply->opcode != req->opcode);
out:
	mutex_unlock(&hw->mbx.lock);
	if (!err && retry_cnt < 0)
		return -ETIMEDOUT;
	if (!err && reply->error_code)
		return -EIO;

	return err;
}

/**
 * mucse_mbx_get_info - Get hw info from fw
 * @hw: pointer to the HW structure
 *
 * mucse_mbx_get_info tries to get hw info from hw.
 *
 * Return: 0 on success, negative errno on failure
 **/
static int mucse_mbx_get_info(struct mucse_hw *hw)
{
	struct mbx_fw_cmd_req req = {
		.datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
		.opcode  = cpu_to_le16(GET_HW_INFO),
	};
	struct mbx_fw_cmd_reply reply = {};
	int err;

	err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
	if (!err)
		hw->pfvfnum = FIELD_GET(GENMASK_U16(7, 0),
					le16_to_cpu(reply.hw_info.pfnum));

	return err;
}

/**
 * mucse_mbx_sync_fw - Try to sync with fw
 * @hw: pointer to the HW structure
 *
 * mucse_mbx_sync_fw tries to sync with fw. It is only called in
 * probe. Nothing (register network) todo if failed.
 * Try more times to do sync.
 *
 * Return: 0 on success, negative errno on failure
 **/
int mucse_mbx_sync_fw(struct mucse_hw *hw)
{
	int try_cnt = 3;
	int err;

	do {
		err = mucse_mbx_get_info(hw);
	} while (err == -ETIMEDOUT && try_cnt--);

	return err;
}

/**
 * mucse_mbx_powerup - Echo fw to powerup
 * @hw: pointer to the HW structure
 * @is_powerup: true for powerup, false for powerdown
 *
 * mucse_mbx_powerup echo fw to change working frequency
 * to normal after received true, and reduce working frequency
 * if false.
 *
 * Return: 0 on success, negative errno on failure
 **/
int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup)
{
	struct mbx_fw_cmd_req req = {
		.datalen = cpu_to_le16(sizeof(req.powerup) +
				       MUCSE_MBX_REQ_HDR_LEN),
		.opcode  = cpu_to_le16(POWER_UP),
		.powerup = {
			/* fw needs this to reply correct cmd */
			.version = cpu_to_le32(GENMASK_U32(31, 0)),
			.status  = cpu_to_le32(is_powerup ? 1 : 0),
		},
	};
	int len, err;

	len = le16_to_cpu(req.datalen);
	mutex_lock(&hw->mbx.lock);
	err = mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len);
	mutex_unlock(&hw->mbx.lock);

	return err;
}

/**
 * mucse_mbx_reset_hw - Posts a mbx req to reset hw
 * @hw: pointer to the HW structure
 *
 * mucse_mbx_reset_hw posts a mbx req to firmware to reset hw.
 * We use mucse_fw_send_cmd_wait_resp to wait hw reset ok.
 *
 * Return: 0 on success, negative errno on failure
 **/
int mucse_mbx_reset_hw(struct mucse_hw *hw)
{
	struct mbx_fw_cmd_req req = {
		.datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
		.opcode  = cpu_to_le16(RESET_HW),
	};
	struct mbx_fw_cmd_reply reply = {};

	return mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
}

/**
 * mucse_mbx_get_macaddr - Posts a mbx req to request macaddr
 * @hw: pointer to the HW structure
 * @pfvfnum: index of pf/vf num
 * @mac_addr: pointer to store mac_addr
 * @port: port index
 *
 * mucse_mbx_get_macaddr posts a mbx req to firmware to get mac_addr.
 *
 * Return: 0 on success, negative errno on failure
 **/
int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
			  u8 *mac_addr,
			  int port)
{
	struct mbx_fw_cmd_req req = {
		.datalen      = cpu_to_le16(sizeof(req.get_mac_addr) +
					    MUCSE_MBX_REQ_HDR_LEN),
		.opcode       = cpu_to_le16(GET_MAC_ADDRESS),
		.get_mac_addr = {
			.port_mask = cpu_to_le32(BIT(port)),
			.pfvf_num  = cpu_to_le32(pfvfnum),
		},
	};
	struct mbx_fw_cmd_reply reply = {};
	int err;

	err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
	if (err)
		return err;

	if (le32_to_cpu(reply.mac_addr.ports) & BIT(port))
		memcpy(mac_addr, reply.mac_addr.addrs[port].mac, ETH_ALEN);
	else
		return -ENODATA;

	return 0;
}
+88 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2020 - 2025 Mucse Corporation. */

#ifndef _RNPGBE_MBX_FW_H
#define _RNPGBE_MBX_FW_H

#include <linux/types.h>

#include "rnpgbe.h"

#define MUCSE_MBX_REQ_HDR_LEN 24

enum MUCSE_FW_CMD {
	GET_HW_INFO     = 0x0601,
	GET_MAC_ADDRESS = 0x0602,
	RESET_HW        = 0x0603,
	POWER_UP        = 0x0803,
};

struct mucse_hw_info {
	u8 link_stat;
	u8 port_mask;
	__le32 speed;
	__le16 phy_type;
	__le16 nic_mode;
	__le16 pfnum;
	__le32 fw_version;
	__le32 axi_mhz;
	union {
		u8 port_id[4];
		__le32 port_ids;
	};
	__le32 bd_uid;
	__le32 phy_id;
	__le32 wol_status;
	__le32 ext_info;
} __packed;

struct mbx_fw_cmd_req {
	__le16 flags;
	__le16 opcode;
	__le16 datalen;
	__le16 ret_value;
	__le32 cookie_lo;
	__le32 cookie_hi;
	__le32 reply_lo;
	__le32 reply_hi;
	union {
		u8 data[32];
		struct {
			__le32 version;
			__le32 status;
		} powerup;
		struct {
			__le32 port_mask;
			__le32 pfvf_num;
		} get_mac_addr;
	};
} __packed;

struct mbx_fw_cmd_reply {
	__le16 flags;
	__le16 opcode;
	__le16 error_code;
	__le16 datalen;
	__le32 cookie_lo;
	__le32 cookie_hi;
	union {
		u8 data[40];
		struct mac_addr {
			__le32 ports;
			struct _addr {
				/* for macaddr:01:02:03:04:05:06
				 * mac-hi=0x01020304 mac-lo=0x05060000
				 */
				u8 mac[8];
			} addrs[4];
		} mac_addr;
		struct mucse_hw_info hw_info;
	};
} __packed;

int mucse_mbx_sync_fw(struct mucse_hw *hw);
int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup);
int mucse_mbx_reset_hw(struct mucse_hw *hw);
int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
			  u8 *mac_addr, int port);
#endif /* _RNPGBE_MBX_FW_H */