ixd: add reset checks and initialize the mailbox

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 <amritha.nambiar@intel.com>
Signed-off-by: Amritha Nambiar <amritha.nambiar@intel.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Tested-by: Bharath R <Bharath.r@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
This commit is contained in:
Larysa Zaremba 2026-06-25 18:02:07 +02:00 committed by Tony Nguyen
parent bea44e3892
commit 31ddf7965a
7 changed files with 322 additions and 9 deletions

View File

@ -4,6 +4,7 @@
config IXD
tristate "Intel(R) Control Plane Function Support"
depends on PCI_MSI
select LIBIE_CP
select LIBIE_PCI
help
This driver supports Intel(R) Control Plane PCI Function

View File

@ -6,3 +6,5 @@
obj-$(CONFIG_IXD) += ixd.o
ixd-y := ixd_main.o
ixd-y += ixd_dev.o
ixd-y += ixd_lib.o

View File

@ -4,14 +4,29 @@
#ifndef _IXD_H_
#define _IXD_H_
#include <linux/net/intel/libie/pci.h>
#include <linux/net/intel/libie/controlq.h>
#define IXD_INIT_TASK_DELAY_JIFFIES msecs_to_jiffies(500)
/**
* struct ixd_adapter - Data structure representing a CPF
* @hw: Device access data
* @cp_ctx: Control plane communication context
* @init_task: Delayed initialization after reset
* @init_task.init_work: Delayed initialization work
* @init_task.reset_retries: How many times to check, whether reset is completed
* @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 +37,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_ */

View File

@ -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 != GENMASK(31, 0) &&
reset_status == ixd_reset_reg.rstat_ok_v)
return true;
return false;
}

View File

@ -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_ */

View File

@ -0,0 +1,136 @@
// 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 libie_ctlq_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)
{
adapter->arq = libie_find_ctlq(&adapter->cp_ctx, LIBIE_CTLQ_TYPE_RX,
LIBIE_CTLQ_MBX_ID);
adapter->asq = libie_find_ctlq(&adapter->cp_ctx, LIBIE_CTLQ_TYPE_TX,
LIBIE_CTLQ_MBX_ID);
}
/**
* ixd_deinit_dflt_mbx - Deinitialize default mailbox
* @adapter: adapter info struct
*/
void ixd_deinit_dflt_mbx(struct ixd_adapter *adapter)
{
if (adapter->xnm)
libie_ctlq_xn_deinit(adapter->xnm, &adapter->cp_ctx);
adapter->arq = NULL;
adapter->asq = NULL;
adapter->xnm = NULL;
}
/**
* 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], LIBIE_CTLQ_TYPE_TX,
&ctlq_reg_tx);
ixd_init_ctlq_create_info(&ctlqs_info[1], LIBIE_CTLQ_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) {
ixd_deinit_dflt_mbx(adapter);
return -ENOENT;
}
return 0;
}
/**
* 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_dfl_wq,
&adapter->init_task.init_work,
IXD_INIT_TASK_DELAY_JIFFIES);
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));
}

View File

@ -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,15 @@ 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 */
if (adapter->xnm)
libie_ctlq_xn_shutdown(adapter->xnm);
ixd_trigger_reset(adapter);
ixd_deinit_dflt_mbx(adapter);
libie_pci_unmap_all_mmio_regions(&adapter->cp_ctx.mmio_info);
}
/**
@ -51,7 +60,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,
@ -84,8 +93,9 @@ static int ixd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
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 +103,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_dfl_wq, &adapter->init_task.init_work,
IXD_INIT_TASK_DELAY_JIFFIES);
return 0;
}
static const struct pci_device_id ixd_pci_tbl[] = {