Commit 9099bfa1 authored by Vikas Gupta's avatar Vikas Gupta Committed by Jakub Kicinski
Browse files

bng_en: Add devlink interface



Allocate a base device and devlink interface with minimal
devlink ops.
Add dsn and board related information.
Map PCIe BAR (bar0), which helps to communicate with the
firmware.

Signed-off-by: default avatarVikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: default avatarBhargava Chenna Marreddy <bhargava.marreddy@broadcom.com>
Reviewed-by: default avatarRajashekar Hudumula <rajashekar.hudumula@broadcom.com>
Link: https://patch.msgid.link/20250701143511.280702-3-vikas.gupta@broadcom.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 74715c4a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -256,6 +256,7 @@ config BNXT_HWMON
config BNGE
	tristate "Broadcom Ethernet device support"
	depends on PCI
	select NET_DEVLINK
	help
	  This driver supports Broadcom 50/100/200/400/800 gigabit Ethernet cards.
	  The module will be called bng_en. To compile this driver as a module,
+2 −1
Original line number Diff line number Diff line
@@ -2,4 +2,5 @@

obj-$(CONFIG_BNGE) += bng_en.o

bng_en-y := bnge_core.o
bng_en-y := bnge_core.o \
	    bnge_devlink.o
+11 −0
Original line number Diff line number Diff line
@@ -13,4 +13,15 @@ enum board_idx {
	BCM57708,
};

struct bnge_dev {
	struct device	*dev;
	struct pci_dev	*pdev;
	u64	dsn;
#define BNGE_VPD_FLD_LEN	32
	char		board_partno[BNGE_VPD_FLD_LEN];
	char		board_serialno[BNGE_VPD_FLD_LEN];

	void __iomem	*bar0;
};

#endif /* _BNGE_H_ */
+39 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#include <linux/pci.h>

#include "bnge.h"
#include "bnge_devlink.h"

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(DRV_SUMMARY);
@@ -77,8 +78,19 @@ static int bnge_pci_enable(struct pci_dev *pdev)
	return rc;
}

static void bnge_unmap_bars(struct pci_dev *pdev)
{
	struct bnge_dev *bd = pci_get_drvdata(pdev);

	if (bd->bar0) {
		pci_iounmap(pdev, bd->bar0);
		bd->bar0 = NULL;
	}
}

static int bnge_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	struct bnge_dev *bd;
	int rc;

	if (pci_is_bridge(pdev))
@@ -100,13 +112,40 @@ static int bnge_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)

	bnge_print_device_info(pdev, ent->driver_data);

	bd = bnge_devlink_alloc(pdev);
	if (!bd) {
		dev_err(&pdev->dev, "Devlink allocation failed\n");
		rc = -ENOMEM;
		goto err_pci_disable;
	}

	bd->bar0 = pci_ioremap_bar(pdev, 0);
	if (!bd->bar0) {
		dev_err(&pdev->dev, "Failed mapping BAR-0, aborting\n");
		rc = -ENOMEM;
		goto err_devl_free;
	}

	pci_save_state(pdev);

	return 0;

err_devl_free:
	bnge_devlink_free(bd);

err_pci_disable:
	bnge_pci_disable(pdev);
	return rc;
}

static void bnge_remove_one(struct pci_dev *pdev)
{
	struct bnge_dev *bd = pci_get_drvdata(pdev);

	bnge_unmap_bars(pdev);

	bnge_devlink_free(bd);

	bnge_pci_disable(pdev);
}

+142 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2025 Broadcom.

#include <linux/unaligned.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <net/devlink.h>

#include "bnge.h"
#include "bnge_devlink.h"

static int bnge_dl_info_put(struct bnge_dev *bd, struct devlink_info_req *req,
			    enum bnge_dl_version_type type, const char *key,
			    char *buf)
{
	if (!strlen(buf))
		return 0;

	switch (type) {
	case BNGE_VERSION_FIXED:
		return devlink_info_version_fixed_put(req, key, buf);
	case BNGE_VERSION_RUNNING:
		return devlink_info_version_running_put(req, key, buf);
	case BNGE_VERSION_STORED:
		return devlink_info_version_stored_put(req, key, buf);
	}

	return 0;
}

static void bnge_vpd_read_info(struct bnge_dev *bd)
{
	struct pci_dev *pdev = bd->pdev;
	unsigned int vpd_size, kw_len;
	int pos, size;
	u8 *vpd_data;

	vpd_data = pci_vpd_alloc(pdev, &vpd_size);
	if (IS_ERR(vpd_data)) {
		pci_warn(pdev, "Unable to read VPD\n");
		return;
	}

	pos = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
					   PCI_VPD_RO_KEYWORD_PARTNO, &kw_len);
	if (pos < 0)
		goto read_sn;

	size = min_t(int, kw_len, BNGE_VPD_FLD_LEN - 1);
	memcpy(bd->board_partno, &vpd_data[pos], size);

read_sn:
	pos = pci_vpd_find_ro_info_keyword(vpd_data, vpd_size,
					   PCI_VPD_RO_KEYWORD_SERIALNO,
					   &kw_len);
	if (pos < 0)
		goto exit;

	size = min_t(int, kw_len, BNGE_VPD_FLD_LEN - 1);
	memcpy(bd->board_serialno, &vpd_data[pos], size);

exit:
	kfree(vpd_data);
}

static int bnge_devlink_info_get(struct devlink *devlink,
				 struct devlink_info_req *req,
				 struct netlink_ext_ack *extack)
{
	struct bnge_dev *bd = devlink_priv(devlink);
	int rc;

	if (bd->dsn) {
		char buf[32];
		u8 dsn[8];
		int rc;

		put_unaligned_le64(bd->dsn, dsn);
		sprintf(buf, "%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X",
			dsn[7], dsn[6], dsn[5], dsn[4],
			dsn[3], dsn[2], dsn[1], dsn[0]);
		rc = devlink_info_serial_number_put(req, buf);
		if (rc) {
			NL_SET_ERR_MSG_MOD(extack, "Failed to set dsn");
			return rc;
		}
	}

	if (strlen(bd->board_serialno)) {
		rc = devlink_info_board_serial_number_put(req,
							  bd->board_serialno);
		if (rc) {
			NL_SET_ERR_MSG_MOD(extack,
					   "Failed to set board serial number");
			return rc;
		}
	}

	rc = bnge_dl_info_put(bd, req, BNGE_VERSION_FIXED,
			      DEVLINK_INFO_VERSION_GENERIC_BOARD_ID,
			      bd->board_partno);
	if (rc) {
		NL_SET_ERR_MSG_MOD(extack, "Failed to set board part number");
		return rc;
	}

	return rc;
}

static const struct devlink_ops bnge_devlink_ops = {
	.info_get = bnge_devlink_info_get,
};

void bnge_devlink_free(struct bnge_dev *bd)
{
	struct devlink *devlink = priv_to_devlink(bd);

	devlink_free(devlink);
}

struct bnge_dev *bnge_devlink_alloc(struct pci_dev *pdev)
{
	struct devlink *devlink;
	struct bnge_dev *bd;

	devlink = devlink_alloc(&bnge_devlink_ops, sizeof(*bd), &pdev->dev);
	if (!devlink)
		return NULL;

	bd = devlink_priv(devlink);
	pci_set_drvdata(pdev, bd);
	bd->dev = &pdev->dev;
	bd->pdev = pdev;

	bd->dsn = pci_get_dsn(pdev);
	if (!bd->dsn)
		pci_warn(pdev, "Failed to get DSN\n");

	bnge_vpd_read_info(bd);

	return bd;
}
Loading