At the end of the probe, trigger hard reset, initialize and schedule the
after-reset task. If the reset is complete in a pre-determined time,
initialize the default mailbox, through which other resources will be
negotiated.

Co-developed-by: Amritha Nambiar <[email protected]>
Signed-off-by: Amritha Nambiar <[email protected]>
Reviewed-by: Maciej Fijalkowski <[email protected]>
Signed-off-by: Larysa Zaremba <[email protected]>
---
 drivers/net/ethernet/intel/ixd/Kconfig        |   1 +
 drivers/net/ethernet/intel/ixd/Makefile       |   2 +
 drivers/net/ethernet/intel/ixd/ixd.h          |  28 +++-
 drivers/net/ethernet/intel/ixd/ixd_dev.c      |  89 +++++++++++
 drivers/net/ethernet/intel/ixd/ixd_lan_regs.h |  40 +++++
 drivers/net/ethernet/intel/ixd/ixd_lib.c      | 143 ++++++++++++++++++
 drivers/net/ethernet/intel/ixd/ixd_main.c     |  32 +++-
 7 files changed, 326 insertions(+), 9 deletions(-)
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_dev.c
 create mode 100644 drivers/net/ethernet/intel/ixd/ixd_lib.c

diff --git a/drivers/net/ethernet/intel/ixd/Kconfig 
b/drivers/net/ethernet/intel/ixd/Kconfig
index f5594efe292c..24510c50070e 100644
--- a/drivers/net/ethernet/intel/ixd/Kconfig
+++ b/drivers/net/ethernet/intel/ixd/Kconfig
@@ -5,6 +5,7 @@ config IXD
        tristate "Intel(R) Control Plane Function Support"
        depends on PCI_MSI
        select LIBETH
+       select LIBIE_CP
        select LIBIE_PCI
        help
          This driver supports Intel(R) Control Plane PCI Function
diff --git a/drivers/net/ethernet/intel/ixd/Makefile 
b/drivers/net/ethernet/intel/ixd/Makefile
index 3849bc240600..164b2c86952f 100644
--- a/drivers/net/ethernet/intel/ixd/Makefile
+++ b/drivers/net/ethernet/intel/ixd/Makefile
@@ -6,3 +6,5 @@
 obj-$(CONFIG_IXD) += ixd.o
 
 ixd-y := ixd_main.o
+ixd-y += ixd_dev.o
+ixd-y += ixd_lib.o
diff --git a/drivers/net/ethernet/intel/ixd/ixd.h 
b/drivers/net/ethernet/intel/ixd/ixd.h
index d813c27941a5..99c44f2aa659 100644
--- a/drivers/net/ethernet/intel/ixd/ixd.h
+++ b/drivers/net/ethernet/intel/ixd/ixd.h
@@ -4,14 +4,25 @@
 #ifndef _IXD_H_
 #define _IXD_H_
 
-#include <linux/intel/libie/pci.h>
+#include <linux/intel/libie/controlq.h>
 
 /**
  * struct ixd_adapter - Data structure representing a CPF
- * @hw: Device access data
+ * @cp_ctx: Control plane communication context
+ * @init_task: Delayed initialization after reset
+ * @xnm: virtchnl transaction manager
+ * @asq: Send control queue info
+ * @arq: Receive control queue info
  */
 struct ixd_adapter {
-       struct libie_mmio_info hw;
+       struct libie_ctlq_ctx cp_ctx;
+       struct {
+               struct delayed_work init_work;
+               u8 reset_retries;
+       } init_task;
+       struct libie_ctlq_xn_manager *xnm;
+       struct libie_ctlq_info *asq;
+       struct libie_ctlq_info *arq;
 };
 
 /**
@@ -22,7 +33,16 @@ struct ixd_adapter {
  */
 static inline struct device *ixd_to_dev(struct ixd_adapter *adapter)
 {
-       return &adapter->hw.pdev->dev;
+       return &adapter->cp_ctx.mmio_info.pdev->dev;
 }
 
+void ixd_ctlq_reg_init(struct ixd_adapter *adapter,
+                      struct libie_ctlq_reg *ctlq_reg_tx,
+                      struct libie_ctlq_reg *ctlq_reg_rx);
+void ixd_trigger_reset(struct ixd_adapter *adapter);
+bool ixd_check_reset_complete(struct ixd_adapter *adapter);
+void ixd_init_task(struct work_struct *work);
+int ixd_init_dflt_mbx(struct ixd_adapter *adapter);
+void ixd_deinit_dflt_mbx(struct ixd_adapter *adapter);
+
 #endif /* _IXD_H_ */
diff --git a/drivers/net/ethernet/intel/ixd/ixd_dev.c 
b/drivers/net/ethernet/intel/ixd/ixd_dev.c
new file mode 100644
index 000000000000..6c41a820eecc
--- /dev/null
+++ b/drivers/net/ethernet/intel/ixd/ixd_dev.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include "ixd.h"
+#include "ixd_lan_regs.h"
+
+/**
+ * ixd_ctlq_reg_init - Initialize default mailbox registers
+ * @adapter: PCI device driver-specific private data
+ * @ctlq_reg_tx: Transmit queue registers info to be filled
+ * @ctlq_reg_rx: Receive queue registers info to be filled
+ */
+void ixd_ctlq_reg_init(struct ixd_adapter *adapter,
+                      struct libie_ctlq_reg *ctlq_reg_tx,
+                      struct libie_ctlq_reg *ctlq_reg_rx)
+{
+       struct libie_mmio_info *mmio_info = &adapter->cp_ctx.mmio_info;
+       *ctlq_reg_tx = (struct libie_ctlq_reg) {
+               .head = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQH),
+               .tail = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQT),
+               .len = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQLEN),
+               .addr_high = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQBAH),
+               .addr_low = libie_pci_get_mmio_addr(mmio_info, PF_FW_ATQBAL),
+               .len_mask = PF_FW_ATQLEN_ATQLEN_M,
+               .len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M,
+               .head_mask = PF_FW_ATQH_ATQH_M,
+       };
+
+       *ctlq_reg_rx = (struct libie_ctlq_reg) {
+               .head = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQH),
+               .tail = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQT),
+               .len = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQLEN),
+               .addr_high = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQBAH),
+               .addr_low = libie_pci_get_mmio_addr(mmio_info, PF_FW_ARQBAL),
+               .len_mask = PF_FW_ARQLEN_ARQLEN_M,
+               .len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M,
+               .head_mask = PF_FW_ARQH_ARQH_M,
+       };
+}
+
+static const struct ixd_reset_reg ixd_reset_reg = {
+       .rstat  = PFGEN_RSTAT,
+       .rstat_m = PFGEN_RSTAT_PFR_STATE_M,
+       .rstat_ok_v = 0b01,
+       .rtrigger = PFGEN_CTRL,
+       .rtrigger_m = PFGEN_CTRL_PFSWR,
+};
+
+/**
+ * ixd_trigger_reset - Trigger PFR reset
+ * @adapter: the device with mapped reset register
+ */
+void ixd_trigger_reset(struct ixd_adapter *adapter)
+{
+       void __iomem *addr;
+       u32 reg_val;
+
+       addr = libie_pci_get_mmio_addr(&adapter->cp_ctx.mmio_info,
+                                      ixd_reset_reg.rtrigger);
+       reg_val = readl(addr);
+       writel(reg_val | ixd_reset_reg.rtrigger_m, addr);
+}
+
+/**
+ * ixd_check_reset_complete - Check if the PFR reset is completed
+ * @adapter: CPF being reset
+ *
+ * Return: %true if the register read indicates reset has been finished,
+ *        %false otherwise
+ */
+bool ixd_check_reset_complete(struct ixd_adapter *adapter)
+{
+       u32 reg_val, reset_status;
+       void __iomem *addr;
+
+       addr = libie_pci_get_mmio_addr(&adapter->cp_ctx.mmio_info,
+                                      ixd_reset_reg.rstat);
+       reg_val = readl(addr);
+       reset_status = reg_val & ixd_reset_reg.rstat_m;
+
+       /* 0xFFFFFFFF might be read if the other side hasn't cleared
+        * the register for us yet.
+        */
+       if (reg_val != 0xFFFFFFFF &&
+           reset_status == ixd_reset_reg.rstat_ok_v)
+               return true;
+
+       return false;
+}
diff --git a/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h 
b/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
index a991eaa8a2aa..26b1e3cfcf20 100644
--- a/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
+++ b/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h
@@ -11,9 +11,33 @@
 #define PF_FW_MBX_REG_LEN              4096
 #define PF_FW_MBX                      0x08400000
 
+#define PF_FW_ARQBAL                   (PF_FW_MBX)
+#define PF_FW_ARQBAH                   (PF_FW_MBX + 0x4)
+#define PF_FW_ARQLEN                   (PF_FW_MBX + 0x8)
+#define PF_FW_ARQLEN_ARQLEN_M          GENMASK(12, 0)
+#define PF_FW_ARQLEN_ARQENABLE_S       31
+#define PF_FW_ARQLEN_ARQENABLE_M       BIT(PF_FW_ARQLEN_ARQENABLE_S)
+#define PF_FW_ARQH_ARQH_M              GENMASK(12, 0)
+#define PF_FW_ARQH                     (PF_FW_MBX + 0xC)
+#define PF_FW_ARQT                     (PF_FW_MBX + 0x10)
+
+#define PF_FW_ATQBAL                   (PF_FW_MBX + 0x14)
+#define PF_FW_ATQBAH                   (PF_FW_MBX + 0x18)
+#define PF_FW_ATQLEN                   (PF_FW_MBX + 0x1C)
+#define PF_FW_ATQLEN_ATQLEN_M          GENMASK(9, 0)
+#define PF_FW_ATQLEN_ATQENABLE_S       31
+#define PF_FW_ATQLEN_ATQENABLE_M       BIT(PF_FW_ATQLEN_ATQENABLE_S)
+#define PF_FW_ATQH_ATQH_M              GENMASK(9, 0)
+#define PF_FW_ATQH                     (PF_FW_MBX + 0x20)
+#define PF_FW_ATQT                     (PF_FW_MBX + 0x24)
+
 /* Reset registers */
 #define PFGEN_RTRIG_REG_LEN            2048
 #define PFGEN_RTRIG                    0x08407000      /* Device resets */
+#define PFGEN_RSTAT                    0x08407008      /* PFR status */
+#define PFGEN_RSTAT_PFR_STATE_M                GENMASK(1, 0)
+#define PFGEN_CTRL                     0x0840700C      /* PFR trigger */
+#define PFGEN_CTRL_PFSWR               BIT(0)
 
 /**
  * struct ixd_bar_region - BAR region description
@@ -25,4 +49,20 @@ struct ixd_bar_region {
        resource_size_t size;
 };
 
+/**
+ * struct ixd_reset_reg - structure for reset registers
+ * @rstat: offset of status in register
+ * @rstat_m: status mask
+ * @rstat_ok_v: value that indicates PFR completed status
+ * @rtrigger: offset of reset trigger in register
+ * @rtrigger_m: reset trigger mask
+ */
+struct ixd_reset_reg {
+       u32     rstat;
+       u32     rstat_m;
+       u32     rstat_ok_v;
+       u32     rtrigger;
+       u32     rtrigger_m;
+};
+
 #endif /* _IXD_LAN_REGS_H_ */
diff --git a/drivers/net/ethernet/intel/ixd/ixd_lib.c 
b/drivers/net/ethernet/intel/ixd/ixd_lib.c
new file mode 100644
index 000000000000..b8dd5c4de7b2
--- /dev/null
+++ b/drivers/net/ethernet/intel/ixd/ixd_lib.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2025 Intel Corporation */
+
+#include "ixd.h"
+
+#define IXD_DFLT_MBX_Q_LEN 64
+
+/**
+ * ixd_init_ctlq_create_info - Initialize control queue info for creation
+ * @info: destination
+ * @type: type of the queue to create
+ * @ctlq_reg: register assigned to the control queue
+ */
+static void ixd_init_ctlq_create_info(struct libie_ctlq_create_info *info,
+                                     enum virtchnl2_queue_type type,
+                                     const struct libie_ctlq_reg *ctlq_reg)
+{
+       *info = (struct libie_ctlq_create_info) {
+               .type = type,
+               .id = -1,
+               .reg = *ctlq_reg,
+               .len = IXD_DFLT_MBX_Q_LEN,
+       };
+}
+
+/**
+ * ixd_init_libie_xn_params - Initialize xn transaction manager creation info
+ * @params: destination
+ * @adapter: adapter info struct
+ * @ctlqs: list of the managed queues to create
+ * @num_queues: length of the queue list
+ */
+static void ixd_init_libie_xn_params(struct libie_ctlq_xn_init_params *params,
+                                    struct ixd_adapter *adapter,
+                                     struct libie_ctlq_create_info *ctlqs,
+                                     uint num_queues)
+{
+       *params = (struct libie_ctlq_xn_init_params){
+               .cctlq_info = ctlqs,
+               .ctx = &adapter->cp_ctx,
+               .num_qs = num_queues,
+       };
+}
+
+/**
+ * ixd_adapter_fill_dflt_ctlqs - Find default control queues and store them
+ * @adapter: adapter info struct
+ */
+static void ixd_adapter_fill_dflt_ctlqs(struct ixd_adapter *adapter)
+{
+       guard(spinlock)(&adapter->cp_ctx.ctlqs_lock);
+       struct libie_ctlq_info *cq;
+
+       list_for_each_entry(cq, &adapter->cp_ctx.ctlqs, list) {
+               if (cq->qid != -1)
+                       continue;
+               if (cq->type == VIRTCHNL2_QUEUE_TYPE_RX)
+                       adapter->arq = cq;
+               else if (cq->type == VIRTCHNL2_QUEUE_TYPE_TX)
+                       adapter->asq = cq;
+       }
+}
+
+/**
+ * ixd_init_dflt_mbx - Setup default mailbox parameters and make request
+ * @adapter: adapter info struct
+ *
+ * Return: %0 on success, negative errno code on failure
+ */
+int ixd_init_dflt_mbx(struct ixd_adapter *adapter)
+{
+       struct libie_ctlq_create_info ctlqs_info[2];
+       struct libie_ctlq_xn_init_params xn_params;
+       struct libie_ctlq_reg ctlq_reg_tx;
+       struct libie_ctlq_reg ctlq_reg_rx;
+       int err;
+
+       ixd_ctlq_reg_init(adapter, &ctlq_reg_tx, &ctlq_reg_rx);
+       ixd_init_ctlq_create_info(&ctlqs_info[0], VIRTCHNL2_QUEUE_TYPE_TX,
+                                 &ctlq_reg_tx);
+       ixd_init_ctlq_create_info(&ctlqs_info[1], VIRTCHNL2_QUEUE_TYPE_RX,
+                                 &ctlq_reg_rx);
+       ixd_init_libie_xn_params(&xn_params, adapter, ctlqs_info,
+                                ARRAY_SIZE(ctlqs_info));
+       err = libie_ctlq_xn_init(&xn_params);
+       if (err)
+               return err;
+       adapter->xnm = xn_params.xnm;
+
+       ixd_adapter_fill_dflt_ctlqs(adapter);
+
+       if (!adapter->asq || !adapter->arq) {
+               libie_ctlq_xn_deinit(adapter->xnm, &adapter->cp_ctx);
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+/**
+ * ixd_deinit_dflt_mbx - Deinitialize default mailbox
+ * @adapter: adapter info struct
+ */
+void ixd_deinit_dflt_mbx(struct ixd_adapter *adapter)
+{
+       if (adapter->arq || adapter->asq)
+               libie_ctlq_xn_deinit(adapter->xnm, &adapter->cp_ctx);
+
+       adapter->arq = NULL;
+       adapter->asq = NULL;
+       adapter->xnm = NULL;
+}
+
+/**
+ * ixd_init_task - Initialize after reset
+ * @work: init work struct
+ */
+void ixd_init_task(struct work_struct *work)
+{
+       struct ixd_adapter *adapter;
+       int err;
+
+       adapter = container_of(work, struct ixd_adapter,
+                              init_task.init_work.work);
+
+       if (!ixd_check_reset_complete(adapter)) {
+               if (++adapter->init_task.reset_retries < 10)
+                       queue_delayed_work(system_unbound_wq,
+                                          &adapter->init_task.init_work,
+                                          msecs_to_jiffies(500));
+               else
+                       dev_err(ixd_to_dev(adapter),
+                               "Device reset failed. The driver was unable to 
contact the device's firmware. Check that the FW is running.\n");
+               return;
+       }
+
+       adapter->init_task.reset_retries = 0;
+       err = ixd_init_dflt_mbx(adapter);
+       if (err)
+               dev_err(ixd_to_dev(adapter),
+                       "Failed to initialize the default mailbox: %pe\n",
+                       ERR_PTR(err));
+}
diff --git a/drivers/net/ethernet/intel/ixd/ixd_main.c 
b/drivers/net/ethernet/intel/ixd/ixd_main.c
index 75ee53152e61..b4d4000b63ed 100644
--- a/drivers/net/ethernet/intel/ixd/ixd_main.c
+++ b/drivers/net/ethernet/intel/ixd/ixd_main.c
@@ -5,6 +5,7 @@
 #include "ixd_lan_regs.h"
 
 MODULE_DESCRIPTION("Intel(R) Control Plane Function Device Driver");
+MODULE_IMPORT_NS("LIBIE_CP");
 MODULE_IMPORT_NS("LIBIE_PCI");
 MODULE_LICENSE("GPL");
 
@@ -16,7 +17,13 @@ static void ixd_remove(struct pci_dev *pdev)
 {
        struct ixd_adapter *adapter = pci_get_drvdata(pdev);
 
-       libie_pci_unmap_all_mmio_regions(&adapter->hw);
+       /* Do not mix removal with (re)initialization */
+       cancel_delayed_work_sync(&adapter->init_task.init_work);
+       /* Leave the device clean on exit */
+       ixd_trigger_reset(adapter);
+       ixd_deinit_dflt_mbx(adapter);
+
+       libie_pci_unmap_all_mmio_regions(&adapter->cp_ctx.mmio_info);
 }
 
 /**
@@ -51,7 +58,7 @@ static int ixd_iomap_regions(struct ixd_adapter *adapter)
        };
 
        for (int i = 0; i < ARRAY_SIZE(regions); i++) {
-               struct libie_mmio_info *mmio_info = &adapter->hw;
+               struct libie_mmio_info *mmio_info = &adapter->cp_ctx.mmio_info;
                bool map_ok;
 
                map_ok = libie_pci_map_mmio_region(mmio_info,
@@ -81,11 +88,15 @@ static int ixd_probe(struct pci_dev *pdev, const struct 
pci_device_id *ent)
        struct ixd_adapter *adapter;
        int err;
 
+       if (WARN_ON(ent->device != IXD_DEV_ID_CPF))
+               return -EINVAL;
+
        adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
        if (!adapter)
                return -ENOMEM;
-       adapter->hw.pdev = pdev;
-       INIT_LIST_HEAD(&adapter->hw.mmio_list);
+
+       adapter->cp_ctx.mmio_info.pdev = pdev;
+       INIT_LIST_HEAD(&adapter->cp_ctx.mmio_info.mmio_list);
 
        err = libie_pci_init_dev(pdev);
        if (err)
@@ -93,7 +104,18 @@ static int ixd_probe(struct pci_dev *pdev, const struct 
pci_device_id *ent)
 
        pci_set_drvdata(pdev, adapter);
 
-       return ixd_iomap_regions(adapter);
+       err = ixd_iomap_regions(adapter);
+       if (err)
+               return err;
+
+       INIT_DELAYED_WORK(&adapter->init_task.init_work,
+                         ixd_init_task);
+
+       ixd_trigger_reset(adapter);
+       queue_delayed_work(system_unbound_wq, &adapter->init_task.init_work,
+                          msecs_to_jiffies(500));
+
+       return 0;
 }
 
 static const struct pci_device_id ixd_pci_tbl[] = {
-- 
2.47.0

Reply via email to