[PATCH v27 net-next 09/10] net/nebula-matrix: add common/ctrl dev init/remove operation
From: illusion.wang <hidden>
Date: 2026-09-07 12:39:30
Also in:
linux-doc, lkml
Subsystem:
networking drivers, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
From: illusion wang <redacted> Add nbl_dev core infrastructure. Introduce nbl_dev_setup_common_dev() and nbl_dev_setup_ctrl_dev() initialization helpers with their teardown counterparts, and wire them into nbl_dev_init() / nbl_dev_remove() from nbl_core_init() / nbl_core_remove(). Common device setup (nbl_dev_setup_common_dev): - Allocate per-device state (nbl_dev_common) - Set up mailbox channel queue and register cleanup work - Initialize MSI-X service counts (mailbox = 1 vector; actual allocation deferred to nbl_dev_start() in the next patch) - On the control PF only: fetch VSI/ETH identity via local dispatch ops. Non-control PFs defer this to nbl_dev_start(), when the mailbox channel is fully operational and the control PF's responder is running; this keeps every intermediate commit bisect-bootable on multi-PF systems. Control device setup (nbl_dev_setup_ctrl_dev, control PF only): - disp_ops->init_module(): chip-wide hardware init (datapath pipeline, driver_status flag) - Program mailbox QINFO routing map (bus/devid for each PF) Chip core hardware initialization is handled by firmware at power-on. The driver only configures functional table entries after hardware becomes ready, so common_dev setup before ctrl_dev setup is safe. Deinit order: ctrl dev first, then common dev. nbl_dev_remove_ctrl_dev() calls deinit_module(), which sets driver_status=false to notify firmware to asynchronously clean all per-PF hardware state including qinfo registers. The qinfo map is not explicitly cleared by the driver; firmware handles it on driver_status change. No completion handshake exists in the current hardware revision; the caller must ensure no new DMA is initiated after deinit (the mailbox channel is stopped first). Signed-off-by: illusion wang <redacted> --- .../net/ethernet/nebula-matrix/nbl/Makefile | 1 + .../net/ethernet/nebula-matrix/nbl/nbl_core.h | 1 + .../nebula-matrix/nbl/nbl_core/nbl_dev.c | 233 ++++++++++++++++++ .../nebula-matrix/nbl/nbl_core/nbl_dev.h | 55 +++++ .../nbl/nbl_include/nbl_def_dev.h | 14 ++ .../net/ethernet/nebula-matrix/nbl/nbl_main.c | 9 + 6 files changed, 313 insertions(+) create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.h create mode 100644 drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/Makefile b/drivers/net/ethernet/nebula-matrix/nbl/Makefile
index b7eebd89b4d1..71fbe3ee7e62 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/Makefile
+++ b/drivers/net/ethernet/nebula-matrix/nbl/Makefile@@ -11,4 +11,5 @@ nbl-objs += nbl_common/nbl_common.o \ nbl_hw/nbl_interrupt.o \ nbl_hw/nbl_chip.o \ nbl_core/nbl_dispatch.o \ + nbl_core/nbl_dev.o \ nbl_main.o
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h
index 4d8cea8d8ab3..c3c4dd685bf6 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core.h@@ -25,6 +25,7 @@ struct nbl_core { struct nbl_hw_mgt *hw_mgt; struct nbl_resource_mgt *res_mgt; struct nbl_dispatch_mgt *disp_mgt; + struct nbl_dev_mgt *dev_mgt; struct nbl_channel_mgt *chan_mgt; };
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c
new file mode 100644
index 000000000000..7813ff31a3cf
--- /dev/null
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.c@@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2026 Nebula Matrix Limited. + */ +#include <linux/device.h> +#include <linux/pci.h> +#include "nbl_dev.h" + +static void nbl_dev_init_msix_cnt(struct nbl_dev_mgt *dev_mgt) +{ + struct nbl_dev_common *dev_common = dev_mgt->common_dev; + struct nbl_msix_info *msix_info = &dev_common->msix_info; + + /* mailbox vector allocated in nbl_dev_start() via + * nbl_dev_request_mailbox_irq() + */ + msix_info->serv_info[NBL_MSIX_MAILBOX_TYPE].num = 1; +} + +/* ---------- Channel config ---------- */ +static void nbl_dev_setup_chan_qinfo(struct nbl_dev_mgt *dev_mgt, u8 chan_type) +{ + struct nbl_channel_ops *chan_ops = dev_mgt->chan_ops_tbl->ops; + struct nbl_channel_mgt *priv = dev_mgt->chan_ops_tbl->priv; + struct nbl_common_info *common = dev_mgt->common; + + if (!chan_ops->check_queue_exist(priv, chan_type)) + return; + + chan_ops->cfg_chan_qinfo_map_table(priv, common->hw_bus, common->devid); +} + +static int nbl_dev_setup_chan_queue(struct nbl_dev_mgt *dev_mgt, u8 chan_type) +{ + struct nbl_channel_ops *chan_ops = dev_mgt->chan_ops_tbl->ops; + struct nbl_channel_mgt *priv = dev_mgt->chan_ops_tbl->priv; + int ret = 0; + + if (chan_ops->check_queue_exist(priv, chan_type)) + ret = chan_ops->setup_queue(priv, chan_type); + + return ret; +} + +static int nbl_dev_remove_chan_queue(struct nbl_dev_mgt *dev_mgt, u8 chan_type) +{ + struct nbl_channel_ops *chan_ops = dev_mgt->chan_ops_tbl->ops; + struct nbl_channel_mgt *priv = dev_mgt->chan_ops_tbl->priv; + int ret = 0; + + if (chan_ops->check_queue_exist(priv, chan_type)) + ret = chan_ops->teardown_queue(priv, chan_type); + + return ret; +} + +static void nbl_dev_register_chan_task(struct nbl_dev_mgt *dev_mgt, + u8 chan_type, struct work_struct *task) +{ + struct nbl_channel_ops *chan_ops = dev_mgt->chan_ops_tbl->ops; + + if (chan_ops->check_queue_exist(dev_mgt->chan_ops_tbl->priv, chan_type)) + chan_ops->register_chan_task(dev_mgt->chan_ops_tbl->priv, + chan_type, task); +} + +/* ---------- Tasks config ---------- */ +static void nbl_dev_clean_mailbox_task(struct work_struct *work) +{ + struct nbl_dev_common *common_dev = + container_of(work, struct nbl_dev_common, clean_mbx_task); + struct nbl_dev_mgt *dev_mgt = common_dev->dev_mgt; + struct nbl_channel_ops *chan_ops = dev_mgt->chan_ops_tbl->ops; + + chan_ops->clean_queue_subtask(dev_mgt->chan_ops_tbl->priv, + NBL_CHAN_TYPE_MAILBOX); +} + +/* ---------- Dev init process ---------- */ +static int nbl_dev_setup_common_dev(struct nbl_adapter *adapter) +{ + struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt; + struct nbl_dispatch_ops *disp_ops = dev_mgt->disp_ops_tbl->ops; + struct nbl_dispatch_mgt *priv = dev_mgt->disp_ops_tbl->priv; + struct nbl_common_info *common = dev_mgt->common; + struct nbl_dev_common *common_dev; + int ret; + + common_dev = devm_kzalloc(&adapter->pdev->dev, sizeof(*common_dev), + GFP_KERNEL); + if (!common_dev) + return -ENOMEM; + common_dev->dev_mgt = dev_mgt; + + ret = nbl_dev_setup_chan_queue(dev_mgt, NBL_CHAN_TYPE_MAILBOX); + if (ret) + return ret; + + INIT_WORK(&common_dev->clean_mbx_task, nbl_dev_clean_mailbox_task); + nbl_dev_register_chan_task(dev_mgt, NBL_CHAN_TYPE_MAILBOX, + &common_dev->clean_mbx_task); + if (common->has_ctrl) { + ret = disp_ops->get_vsi_id(priv, NBL_VSI_DATA, &common->vsi_id); + if (ret) + goto err_cleanup; + ret = disp_ops->get_eth_id(priv, common->vsi_id, + &common->eth_num, + &common->eth_id, + &common->logic_eth_id); + if (ret) + goto err_cleanup; + } + + dev_mgt->common_dev = common_dev; + nbl_dev_init_msix_cnt(dev_mgt); + + return 0; +err_cleanup: + cancel_work_sync(&common_dev->clean_mbx_task); + nbl_dev_remove_chan_queue(dev_mgt, NBL_CHAN_TYPE_MAILBOX); + nbl_dev_register_chan_task(dev_mgt, NBL_CHAN_TYPE_MAILBOX, NULL); + return ret; +} + +static void nbl_dev_remove_common_dev(struct nbl_adapter *adapter) +{ + struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt; + struct nbl_dev_common *common_dev = dev_mgt->common_dev; + + if (!common_dev) + return; + cancel_work_sync(&common_dev->clean_mbx_task); + nbl_dev_remove_chan_queue(dev_mgt, NBL_CHAN_TYPE_MAILBOX); + nbl_dev_register_chan_task(dev_mgt, NBL_CHAN_TYPE_MAILBOX, NULL); +} + +static int nbl_dev_setup_ctrl_dev(struct nbl_adapter *adapter) +{ + struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt; + struct nbl_dispatch_ops *disp_ops = dev_mgt->disp_ops_tbl->ops; + int ret; + + ret = disp_ops->init_module(dev_mgt->disp_ops_tbl->priv); + if (ret) + return ret; + + nbl_dev_setup_chan_qinfo(dev_mgt, NBL_CHAN_TYPE_MAILBOX); + + return 0; +} + +/* + * Tear down control device: deinit_module sets driver_status=false + * to notify firmware to clean all per-PF hardware state (including + * qinfo registers). The qinfo map programmed in setup_ctrl_dev is + * not explicitly cleared; firmware handles it on driver_status change. + */ +static void nbl_dev_remove_ctrl_dev(struct nbl_adapter *adapter) +{ + struct nbl_dev_mgt *dev_mgt = adapter->core.dev_mgt; + struct nbl_dispatch_ops *disp_ops = dev_mgt->disp_ops_tbl->ops; + + disp_ops->deinit_module(dev_mgt->disp_ops_tbl->priv); +} + +static struct nbl_dev_mgt *nbl_dev_setup_dev_mgt(struct nbl_common_info *common) +{ + struct nbl_dev_mgt *dev_mgt; + + dev_mgt = devm_kzalloc(common->dev, sizeof(*dev_mgt), GFP_KERNEL); + if (!dev_mgt) + return ERR_PTR(-ENOMEM); + + dev_mgt->common = common; + return dev_mgt; +} + +int nbl_dev_init(struct nbl_adapter *adapter) +{ + struct nbl_common_info *common = &adapter->common; + struct nbl_dispatch_ops_tbl *disp_ops_tbl = + adapter->intf.dispatch_ops_tbl; + struct nbl_channel_ops_tbl *chan_ops_tbl = + adapter->intf.channel_ops_tbl; + struct nbl_dev_mgt *dev_mgt; + int ret; + + dev_mgt = nbl_dev_setup_dev_mgt(common); + if (IS_ERR(dev_mgt)) { + ret = PTR_ERR(dev_mgt); + return ret; + } + + dev_mgt->disp_ops_tbl = disp_ops_tbl; + dev_mgt->chan_ops_tbl = chan_ops_tbl; + adapter->core.dev_mgt = dev_mgt; + + /* + * Chip hardware initialization is completed by firmware at power-up. + * Only driver functional table/register config follows here, safe to + * access hardware registers before ctrl dev setup. + */ + ret = nbl_dev_setup_common_dev(adapter); + if (ret) + goto setup_err; + + if (common->has_ctrl) { + ret = nbl_dev_setup_ctrl_dev(adapter); + if (ret) + goto setup_ctrl_dev_fail; + } + + return 0; +setup_ctrl_dev_fail: + nbl_dev_remove_common_dev(adapter); +setup_err: + return ret; +} + +/* + * Teardown order: ctrl dev first, then common dev. + * nbl_dev_remove_ctrl_dev() notifies firmware to clean all per-PF state + * (including qinfo registers), so subsequent common dev queue cleanup + * will not trigger PCIe master abort or invalid register access. + */ +void nbl_dev_remove(struct nbl_adapter *adapter) +{ + struct nbl_common_info *common = &adapter->common; + + if (common->has_ctrl) + nbl_dev_remove_ctrl_dev(adapter); + nbl_dev_remove_common_dev(adapter); +}
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.h
new file mode 100644
index 000000000000..24e890fd8987
--- /dev/null
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_core/nbl_dev.h@@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026 Nebula Matrix Limited. + */ + +#ifndef _NBL_DEV_H_ +#define _NBL_DEV_H_ + +#include <linux/types.h> + +#include "../nbl_include/nbl_include.h" +#include "../nbl_include/nbl_def_channel.h" +#include "../nbl_include/nbl_def_hw.h" +#include "../nbl_include/nbl_def_resource.h" +#include "../nbl_include/nbl_def_dispatch.h" +#include "../nbl_include/nbl_def_dev.h" +#include "../nbl_include/nbl_def_common.h" +#include "../nbl_core.h" + +#define NBL_STRING_NAME_LEN 32 + +enum nbl_msix_serv_type { + NBL_MSIX_NET_TYPE, + NBL_MSIX_MAILBOX_TYPE, + NBL_MSIX_TYPE_MAX +}; + +struct nbl_msix_serv_info { + char irq_name[NBL_STRING_NAME_LEN]; + u16 num; + u16 base_vector_id; + /* true: hw report msix, hw need to mask actively */ + bool hw_self_mask_en; +}; + +struct nbl_msix_info { + struct nbl_msix_serv_info serv_info[NBL_MSIX_TYPE_MAX]; +}; + +struct nbl_dev_common { + struct nbl_dev_mgt *dev_mgt; + struct nbl_msix_info msix_info; + char mailbox_name[NBL_STRING_NAME_LEN]; + /* for ctrl-dev/net-dev mailbox recv msg */ + struct work_struct clean_mbx_task; +}; + +struct nbl_dev_mgt { + struct nbl_common_info *common; + struct nbl_dispatch_ops_tbl *disp_ops_tbl; + struct nbl_channel_ops_tbl *chan_ops_tbl; + struct nbl_dev_common *common_dev; +}; + +#endif
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h
new file mode 100644
index 000000000000..51cf04e4c552
--- /dev/null
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_include/nbl_def_dev.h@@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2026 Nebula Matrix Limited. + */ + +#ifndef _NBL_DEF_DEV_H_ +#define _NBL_DEF_DEV_H_ + +struct nbl_adapter; + +int nbl_dev_init(struct nbl_adapter *adapter); +void nbl_dev_remove(struct nbl_adapter *adapter); + +#endif
diff --git a/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c b/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c
index 5d5c0bbf418c..9896c1b49be2 100644
--- a/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c
+++ b/drivers/net/ethernet/nebula-matrix/nbl/nbl_main.c@@ -12,6 +12,7 @@ #include "nbl_include/nbl_def_hw.h" #include "nbl_include/nbl_def_resource.h" #include "nbl_include/nbl_def_dispatch.h" +#include "nbl_include/nbl_def_dev.h" #include "nbl_include/nbl_def_common.h" #include "nbl_core.h"
@@ -53,7 +54,14 @@ struct nbl_adapter *nbl_core_init(struct pci_dev *pdev, ret = nbl_disp_init(adapter); if (ret) goto disp_init_fail; + + ret = nbl_dev_init(adapter); + if (ret) + goto dev_init_fail; return adapter; + +dev_init_fail: + nbl_disp_remove(adapter); disp_init_fail: nbl_res_remove_leonis(adapter); res_init_fail:
@@ -66,6 +74,7 @@ struct nbl_adapter *nbl_core_init(struct pci_dev *pdev, void nbl_core_remove(struct nbl_adapter *adapter) { + nbl_dev_remove(adapter); nbl_disp_remove(adapter); nbl_res_remove_leonis(adapter); nbl_chan_remove_common(adapter);
--
2.47.3