diff options
Diffstat (limited to 'drivers/net/ethernet/intel/ixd')
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/Makefile | 13 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd.h | 67 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_ctlq.c | 141 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_ctlq.h | 34 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_dev.c | 89 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_devlink.c | 99 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_devlink.h | 50 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_lan_regs.h | 68 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_lib.c | 173 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_main.c | 144 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_virtchnl.c | 190 | ||||
| -rw-r--r-- | drivers/net/ethernet/intel/ixd/ixd_virtchnl.h | 12 |
13 files changed, 1094 insertions, 0 deletions
diff --git a/drivers/net/ethernet/intel/ixd/Kconfig b/drivers/net/ethernet/intel/ixd/Kconfig new file mode 100644 index 000000000000..0a48b3bb7bc2 --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2025 Intel Corporation + +config IXD + tristate "Intel(R) Control Plane Function Support" + depends on PCI_MSI + select LIBIE_CP + select LIBIE_PCI + select NET_DEVLINK + help + This driver supports Intel(R) Control Plane PCI Function + of Intel E2100 and later IPUs and FNICs. + It facilitates a centralized control over multiple IDPF PFs/VFs/SFs + exposed by the same card. diff --git a/drivers/net/ethernet/intel/ixd/Makefile b/drivers/net/ethernet/intel/ixd/Makefile new file mode 100644 index 000000000000..03760a2580b9 --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (C) 2025 Intel Corporation + +# Intel(R) Control Plane Function Linux Driver + +obj-$(CONFIG_IXD) += ixd.o + +ixd-y := ixd_main.o +ixd-y += ixd_ctlq.o +ixd-y += ixd_dev.o +ixd-y += ixd_devlink.o +ixd-y += ixd_lib.o +ixd-y += ixd_virtchnl.o diff --git a/drivers/net/ethernet/intel/ixd/ixd.h b/drivers/net/ethernet/intel/ixd/ixd.h new file mode 100644 index 000000000000..2a09ccba13d5 --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2025 Intel Corporation */ + +#ifndef _IXD_H_ +#define _IXD_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 + * @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 + * @init_task.vc_retries: Number of retries to establish mailbox communication + * @init_task.success: init_work completion status + * @mbx_task: Control queue Rx handling + * @xnm: virtchnl transaction manager + * @asq: Send control queue info + * @arq: Receive control queue info + * @vc_ver: Negotiated virtchnl version + * @vc_ver.major: Negotiated major virtchnl version + * @vc_ver.minor: Negotiated minor virtchnl version + * @caps: Negotiated virtchnl capabilities + */ +struct ixd_adapter { + struct libie_ctlq_ctx cp_ctx; + struct { + struct delayed_work init_work; + u8 reset_retries; + u8 vc_retries; + bool success; + } init_task; + struct delayed_work mbx_task; + struct libie_ctlq_xn_manager *xnm; + struct libie_ctlq_info *asq; + struct libie_ctlq_info *arq; + struct { + u32 major; + u32 minor; + } vc_ver; + struct virtchnl2_get_capabilities caps; +}; + +/** + * ixd_to_dev - Get the corresponding device struct from an adapter + * @adapter: PCI device driver-specific private data + * + * Return: struct device corresponding to the given adapter + */ +static inline struct device *ixd_to_dev(struct ixd_adapter *adapter) +{ + 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_ctlq.c b/drivers/net/ethernet/intel/ixd/ixd_ctlq.c new file mode 100644 index 000000000000..8712e10c8c50 --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd_ctlq.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2025 Intel Corporation */ + +#include "ixd.h" +#include "ixd_ctlq.h" +#include "ixd_virtchnl.h" + +#define IXD_CTLQ_RX_TASK_DELAY_JIFFIES msecs_to_jiffies(300) + +/** + * ixd_ctlq_clean_sq - Clean the send control queue after sending the message + * @adapter: The adapter that sent the messages + * @force: Clean regardless of send status + * + * Free the libie send resources after sending the message and handling + * the response. + */ +void ixd_ctlq_clean_sq(struct ixd_adapter *adapter, bool force) +{ + libie_ctlq_xn_send_clean(adapter->asq, kfree, force); +} + +/** + * ixd_ctlq_init_sparams - Initialize control queue send parameters + * @adapter: The adapter with initialized mailbox + * @sparams: Parameters to initialize + * @msg_buf: DMA-mappable pointer to the message being sent + * @msg_size: Message size + */ +static void ixd_ctlq_init_sparams(struct ixd_adapter *adapter, + struct libie_ctlq_xn_send_params *sparams, + void *msg_buf, size_t msg_size) +{ + *sparams = (struct libie_ctlq_xn_send_params) { + .rel_tx_buf = kfree, + .xnm = adapter->xnm, + .ctlq = adapter->asq, + .timeout_ms = IXD_CTLQ_TIMEOUT, + .send_buf = (struct kvec) { + .iov_base = msg_buf, + .iov_len = msg_size, + }, + }; +} + +/** + * ixd_ctlq_do_req - Perform a standard virtchnl request + * @adapter: The adapter with initialized mailbox + * @req: virtchnl request description + * + * Return: %0 if a message was sent and received a response + * that was successfully handled by the custom callback, + * negative error otherwise. + */ +int ixd_ctlq_do_req(struct ixd_adapter *adapter, const struct ixd_ctlq_req *req) +{ + u8 onstack_send_buff[LIBIE_CP_TX_COPYBREAK] __aligned_largest = {}; + struct libie_ctlq_xn_send_params send_params = {}; + struct kvec *recv_mem; + void *send_buff; + int err; + + send_buff = libie_cp_can_send_onstack(req->send_size) ? + &onstack_send_buff : kzalloc(req->send_size, GFP_KERNEL); + if (!send_buff) + return -ENOMEM; + + ixd_ctlq_init_sparams(adapter, &send_params, send_buff, + req->send_size); + + send_params.chnl_opcode = req->opcode; + + if (req->send_buff_init) + req->send_buff_init(adapter, send_buff, req->ctx); + + ixd_ctlq_clean_sq(adapter, false); + err = libie_ctlq_xn_send(&send_params); + if (err) + return err; + + recv_mem = &send_params.recv_mem; + if (req->recv_process) + err = req->recv_process(adapter, recv_mem->iov_base, + recv_mem->iov_len, req->ctx); + + libie_ctlq_release_rx_buf(recv_mem); + + return err; +} + +/** + * ixd_ctlq_handle_msg - Default control queue message handler + * @ctx: Control plane communication context + * @msg: Message received + */ +static void ixd_ctlq_handle_msg(struct libie_ctlq_ctx *ctx, + struct libie_ctlq_msg *msg) +{ + struct ixd_adapter *adapter = pci_get_drvdata(ctx->mmio_info.pdev); + + if (ixd_vc_can_handle_msg(msg)) + ixd_vc_recv_event_msg(adapter, msg); + else + dev_dbg_ratelimited(ixd_to_dev(adapter), + "Received an unsupported opcode 0x%x from the CP\n", + msg->chnl_opcode); + + libie_ctlq_release_rx_buf(&msg->recv_mem); +} + +/** + * ixd_ctlq_recv_mb_msg - Receive a potential message over mailbox periodically + * @adapter: The adapter with initialized mailbox + */ +static void ixd_ctlq_recv_mb_msg(struct ixd_adapter *adapter) +{ + struct libie_ctlq_xn_recv_params xn_params = { + .xnm = adapter->xnm, + .ctlq = adapter->arq, + .ctlq_msg_handler = ixd_ctlq_handle_msg, + .budget = LIBIE_CTLQ_MAX_XN_ENTRIES, + }; + + libie_ctlq_xn_recv(&xn_params); +} + +/** + * ixd_ctlq_rx_task - Periodically check for mailbox responses and events + * @work: work handle + */ +void ixd_ctlq_rx_task(struct work_struct *work) +{ + struct ixd_adapter *adapter; + + adapter = container_of(work, struct ixd_adapter, mbx_task.work); + + queue_delayed_work(system_dfl_wq, &adapter->mbx_task, + IXD_CTLQ_RX_TASK_DELAY_JIFFIES); + + ixd_ctlq_recv_mb_msg(adapter); +} diff --git a/drivers/net/ethernet/intel/ixd/ixd_ctlq.h b/drivers/net/ethernet/intel/ixd/ixd_ctlq.h new file mode 100644 index 000000000000..8839f9f8f6d5 --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd_ctlq.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2025 Intel Corporation */ + +#ifndef _IXD_CTLQ_H_ +#define _IXD_CTLQ_H_ + +#include <linux/net/intel/virtchnl2.h> + +#define IXD_CTLQ_TIMEOUT 2000 + +/** + * struct ixd_ctlq_req - Standard virtchnl request description + * @opcode: protocol opcode, only virtchnl2 is needed for now + * @send_size: required length of the send buffer + * @send_buff_init: function to initialize the allocated send buffer + * @recv_process: function to handle the CP response + * @ctx: additional context for callbacks + */ +struct ixd_ctlq_req { + enum virtchnl2_op opcode; + size_t send_size; + void (*send_buff_init)(struct ixd_adapter *adapter, void *send_buff, + void *ctx); + int (*recv_process)(struct ixd_adapter *adapter, void *recv_buff, + size_t recv_size, void *ctx); + void *ctx; +}; + +void ixd_ctlq_clean_sq(struct ixd_adapter *adapter, bool force); +int ixd_ctlq_do_req(struct ixd_adapter *adapter, + const struct ixd_ctlq_req *req); +void ixd_ctlq_rx_task(struct work_struct *work); + +#endif /* _IXD_CTLQ_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..cdd5477cc1f4 --- /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 != GENMASK(31, 0) && + reset_status == ixd_reset_reg.rstat_ok_v) + return true; + + return false; +} diff --git a/drivers/net/ethernet/intel/ixd/ixd_devlink.c b/drivers/net/ethernet/intel/ixd/ixd_devlink.c new file mode 100644 index 000000000000..828132db8323 --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd_devlink.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025, Intel Corporation. */ + +#include "ixd.h" +#include "ixd_devlink.h" + +#define IXD_DEVLINK_INFO_LEN 128 + +/** + * ixd_fill_dsn - Get the serial number for the ixd device + * @adapter: adapter to query + * @buf: storage buffer for the info request + */ +static void ixd_fill_dsn(struct ixd_adapter *adapter, char *buf) +{ + u8 dsn[8]; + + /* Copy the DSN into an array in Big Endian format */ + put_unaligned_be64(pci_get_dsn(adapter->cp_ctx.mmio_info.pdev), dsn); + + snprintf(buf, IXD_DEVLINK_INFO_LEN, "%8phD", dsn); +} + +/** + * ixd_fill_device_name - Get the name of the underlying hardware + * @adapter: adapter to query + * @buf: storage buffer for the info request + * @buf_size: size of the storage buffer + */ +static void ixd_fill_device_name(struct ixd_adapter *adapter, char *buf, + size_t buf_size) +{ + if (adapter->caps.device_type == cpu_to_le32(VIRTCHNL2_MEV_DEVICE)) + snprintf(buf, buf_size, "%s", "MEV"); + else + snprintf(buf, buf_size, "%s", "UNKNOWN"); +} + +/** + * ixd_devlink_info_get - .info_get devlink handler + * @devlink: devlink instance structure + * @req: the devlink info request + * @extack: extended netdev ack structure + * + * Callback for the devlink .info_get operation. Reports information about the + * device. + * + * Return: zero on success or an error code on failure. + */ +static int ixd_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct ixd_adapter *adapter = devlink_priv(devlink); + char buf[IXD_DEVLINK_INFO_LEN]; + int err; + + ixd_fill_dsn(adapter, buf); + err = devlink_info_serial_number_put(req, buf); + if (err) + return err; + + ixd_fill_device_name(adapter, buf, IXD_DEVLINK_INFO_LEN); + err = devlink_info_version_fixed_put(req, "device.type", buf); + if (err) + return err; + + snprintf(buf, sizeof(buf), "%u.%u", + adapter->vc_ver.major, adapter->vc_ver.minor); + + return devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW_MGMT_API, + buf); +} + +static const struct devlink_ops ixd_devlink_ops = { + .info_get = ixd_devlink_info_get, +}; + +/** + * ixd_adapter_alloc - Allocate devlink and return adapter pointer + * @dev: the device to allocate for + * + * Allocate a devlink instance for this device and return the private area as + * the adapter structure. + * + * Return: adapter structure on success, NULL on failure + */ +struct ixd_adapter *ixd_adapter_alloc(struct device *dev) +{ + struct devlink *devlink; + + devlink = devlink_alloc(&ixd_devlink_ops, sizeof(struct ixd_adapter), + dev); + if (!devlink) + return NULL; + + return devlink_priv(devlink); +} diff --git a/drivers/net/ethernet/intel/ixd/ixd_devlink.h b/drivers/net/ethernet/intel/ixd/ixd_devlink.h new file mode 100644 index 000000000000..b23a1b37aebc --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd_devlink.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2025, Intel Corporation. */ + +#ifndef _IXD_DEVLINK_H_ +#define _IXD_DEVLINK_H_ + +#include <net/devlink.h> + +#include "ixd.h" + +struct ixd_adapter *ixd_adapter_alloc(struct device *dev); + +/** + * ixd_devlink_free - teardown the devlink + * @adapter: the adapter structure to free + * + */ +static inline void ixd_devlink_free(struct ixd_adapter *adapter) +{ + struct devlink *devlink = priv_to_devlink(adapter); + + devlink_free(devlink); +} + +/** + * ixd_devlink_unregister - Unregister devlink for this adapter. + * @adapter: the adapter structure to cleanup + * + * Init task must be completed or cancelled beforehand. + */ +static inline void ixd_devlink_unregister(struct ixd_adapter *adapter) +{ + if (!adapter->init_task.success) + return; + + devlink_unregister(priv_to_devlink(adapter)); +} + +/** + * ixd_devlink_register - Register devlink interface for this adapter + * @adapter: pointer to ixd adapter structure to be associated with devlink + * + * Register the devlink instance associated with this adapter + */ +static inline void ixd_devlink_register(struct ixd_adapter *adapter) +{ + devlink_register(priv_to_devlink(adapter)); +} + +#endif /* _IXD_DEVLINK_H_ */ diff --git a/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h b/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h new file mode 100644 index 000000000000..58e58c75981b --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd_lan_regs.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2025 Intel Corporation */ + +#ifndef _IXD_LAN_REGS_H_ +#define _IXD_LAN_REGS_H_ + +/* Control Plane Function PCI ID */ +#define IXD_DEV_ID_CPF 0x1efe + +/* Control Queue (Mailbox) */ +#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 + * @offset: BAR region offset + * @size: BAR region size + */ +struct ixd_bar_region { + resource_size_t offset; + 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..8311f7590666 --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd_lib.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2025 Intel Corporation */ + +#include "ixd.h" +#include "ixd_ctlq.h" +#include "ixd_devlink.h" +#include "ixd_virtchnl.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) +{ + cancel_delayed_work_sync(&adapter->mbx_task); + + if (adapter->xnm) + libie_ctlq_xn_shutdown(adapter->xnm); + + if (adapter->asq) + ixd_ctlq_clean_sq(adapter, true); + + 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; + } + + queue_delayed_work(system_dfl_wq, &adapter->mbx_task, 0); + + 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)); + return; + } + + err = ixd_vc_dev_init(adapter); + if (!err) { + adapter->init_task.vc_retries = 0; + adapter->init_task.success = true; + ixd_devlink_register(adapter); + return; + } + + libie_ctlq_xn_shutdown(adapter->xnm); + ixd_trigger_reset(adapter); + ixd_deinit_dflt_mbx(adapter); + if (++adapter->init_task.vc_retries > 5 || + (err != -ETIMEDOUT && err != -EAGAIN && err != -EBUSY)) { + dev_err(ixd_to_dev(adapter), + "Failed to establish mailbox communication with the hardware: %pe\n", + ERR_PTR(err)); + return; + } + + queue_delayed_work(system_dfl_wq, &adapter->init_task.init_work, + IXD_INIT_TASK_DELAY_JIFFIES); +} diff --git a/drivers/net/ethernet/intel/ixd/ixd_main.c b/drivers/net/ethernet/intel/ixd/ixd_main.c new file mode 100644 index 000000000000..5db992d4f9fb --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd_main.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2025 Intel Corporation */ + +#include "ixd.h" +#include "ixd_ctlq.h" +#include "ixd_lan_regs.h" +#include "ixd_devlink.h" + +MODULE_DESCRIPTION("Intel(R) Control Plane Function Device Driver"); +MODULE_IMPORT_NS("LIBIE_CP"); +MODULE_IMPORT_NS("LIBIE_PCI"); +MODULE_LICENSE("GPL"); + +/** + * ixd_remove - remove a CPF PCI device + * @pdev: PCI device being removed + */ +static void ixd_remove(struct pci_dev *pdev) +{ + struct ixd_adapter *adapter = pci_get_drvdata(pdev); + + /* Do not mix removal with (re)initialization */ + cancel_delayed_work_sync(&adapter->init_task.init_work); + + ixd_devlink_unregister(adapter); + + /* 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); + ixd_devlink_free(adapter); +} + +/** + * ixd_shutdown - shut down a CPF PCI device + * @pdev: PCI device being shut down + */ +static void ixd_shutdown(struct pci_dev *pdev) +{ + ixd_remove(pdev); + + if (system_state == SYSTEM_POWER_OFF) + pci_set_power_state(pdev, PCI_D3hot); +} + +/** + * ixd_iomap_regions - iomap PCI BARs + * @adapter: adapter to map memory regions for + * + * Returns: %0 on success, negative on failure + */ +static int ixd_iomap_regions(struct ixd_adapter *adapter) +{ + const struct ixd_bar_region regions[] = { + { + .offset = PFGEN_RTRIG, + .size = PFGEN_RTRIG_REG_LEN, + }, + { + .offset = PF_FW_MBX, + .size = PF_FW_MBX_REG_LEN, + }, + }; + + for (int i = 0; i < ARRAY_SIZE(regions); i++) { + struct libie_mmio_info *mmio_info = &adapter->cp_ctx.mmio_info; + bool map_ok; + + map_ok = libie_pci_map_mmio_region(mmio_info, + regions[i].offset, + regions[i].size); + if (!map_ok) { + dev_err(ixd_to_dev(adapter), + "Failed to map PCI device MMIO region\n"); + + libie_pci_unmap_all_mmio_regions(mmio_info); + return -EIO; + } + } + + return 0; +} + +/** + * ixd_probe - probe a CPF PCI device + * @pdev: corresponding PCI device + * @ent: entry in ixd_pci_tbl + * + * Returns: %0 on success, negative errno code on failure + */ +static int ixd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct ixd_adapter *adapter; + int err; + + adapter = ixd_adapter_alloc(&pdev->dev); + if (!adapter) + return -ENOMEM; + + 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) + goto free_adapter; + + pci_set_drvdata(pdev, adapter); + + err = ixd_iomap_regions(adapter); + if (err) + goto free_adapter; + + INIT_DELAYED_WORK(&adapter->init_task.init_work, + ixd_init_task); + INIT_DELAYED_WORK(&adapter->mbx_task, ixd_ctlq_rx_task); + + ixd_trigger_reset(adapter); + queue_delayed_work(system_dfl_wq, &adapter->init_task.init_work, + IXD_INIT_TASK_DELAY_JIFFIES); + + return 0; + +free_adapter: + ixd_devlink_free(adapter); + return err; +} + +static const struct pci_device_id ixd_pci_tbl[] = { + { PCI_VDEVICE(INTEL, IXD_DEV_ID_CPF) }, + { } +}; +MODULE_DEVICE_TABLE(pci, ixd_pci_tbl); + +static struct pci_driver ixd_driver = { + .name = KBUILD_MODNAME, + .id_table = ixd_pci_tbl, + .probe = ixd_probe, + .remove = ixd_remove, + .shutdown = ixd_shutdown, +}; +module_pci_driver(ixd_driver); diff --git a/drivers/net/ethernet/intel/ixd/ixd_virtchnl.c b/drivers/net/ethernet/intel/ixd/ixd_virtchnl.c new file mode 100644 index 000000000000..fc3b6d2e28c5 --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd_virtchnl.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2025 Intel Corporation */ + +#include "ixd.h" +#include "ixd_ctlq.h" +#include "ixd_virtchnl.h" + +/** + * ixd_vc_recv_event_msg - Handle virtchnl event message + * @adapter: The adapter handling the message + * @ctlq_msg: Message received + */ +void ixd_vc_recv_event_msg(struct ixd_adapter *adapter, + struct libie_ctlq_msg *ctlq_msg) +{ + int payload_size = ctlq_msg->data_len; + struct virtchnl2_event *v2e; + + if (payload_size < sizeof(*v2e)) { + dev_warn_ratelimited(ixd_to_dev(adapter), + "Failed to receive valid payload for event msg (op 0x%X len %u)\n", + ctlq_msg->chnl_opcode, + payload_size); + return; + } + + v2e = (struct virtchnl2_event *)ctlq_msg->recv_mem.iov_base; + + dev_dbg(ixd_to_dev(adapter), "Got event 0x%X from the CP\n", + le32_to_cpu(v2e->event)); +} + +/** + * ixd_vc_can_handle_msg - Decide if an event has to be handled by virtchnl code + * @ctlq_msg: Message received + * + * Return: %true if virtchnl code can handle the event, %false otherwise + */ +bool ixd_vc_can_handle_msg(struct libie_ctlq_msg *ctlq_msg) +{ + return ctlq_msg->chnl_opcode == VIRTCHNL2_OP_EVENT; +} + +/** + * ixd_handle_caps - Handle VIRTCHNL2_OP_GET_CAPS response + * @adapter: The adapter for which the capabilities are being updated + * @recv_buff: Buffer containing the response + * @recv_size: Response buffer size + * @ctx: unused + * + * Return: %0 if the response format is correct and was handled as expected, + * negative error otherwise. + */ +static int ixd_handle_caps(struct ixd_adapter *adapter, void *recv_buff, + size_t recv_size, void *ctx) +{ + if (recv_size < sizeof(adapter->caps)) + return -EBADMSG; + + adapter->caps = *(typeof(adapter->caps) *)recv_buff; + + return 0; +} + +/** + * ixd_req_vc_caps - Request and save device capability + * @adapter: The adapter to get the capabilities for + * + * Return: success or error if sending the get capability message fails + */ +static int ixd_req_vc_caps(struct ixd_adapter *adapter) +{ + const struct ixd_ctlq_req req = { + .opcode = VIRTCHNL2_OP_GET_CAPS, + .send_size = sizeof(struct virtchnl2_get_capabilities), + .ctx = NULL, + .send_buff_init = NULL, + .recv_process = ixd_handle_caps, + }; + + return ixd_ctlq_do_req(adapter, &req); +} + +/** + * ixd_get_vc_ver - Get version info from adapter + * + * Return: filled in virtchannel2 version info, ready for sending + */ +static struct virtchnl2_version_info ixd_get_vc_ver(void) +{ + return (struct virtchnl2_version_info) { + .major = cpu_to_le32(VIRTCHNL2_VERSION_MAJOR_2), + .minor = cpu_to_le32(VIRTCHNL2_VERSION_MINOR_0), + }; +} + +static void ixd_fill_vc_ver(struct ixd_adapter *adapter, void *send_buff, + void *ctx) +{ + *(struct virtchnl2_version_info *)send_buff = ixd_get_vc_ver(); +} + +/** + * ixd_handle_vc_ver - Handle VIRTCHNL2_OP_VERSION response + * @adapter: The adapter for which the version is being updated + * @recv_buff: Buffer containing the response + * @recv_size: Response buffer size + * @ctx: Unused + * + * Return: %0 if the response format is correct and was handled as expected, + * negative error otherwise. + */ +static int ixd_handle_vc_ver(struct ixd_adapter *adapter, void *recv_buff, + size_t recv_size, void *ctx) +{ + struct virtchnl2_version_info need_ver = ixd_get_vc_ver(); + struct virtchnl2_version_info *recv_ver; + + if (recv_size < sizeof(need_ver)) + return -EBADMSG; + + recv_ver = recv_buff; + if (le32_to_cpu(need_ver.major) != le32_to_cpu(recv_ver->major) || + le32_to_cpu(need_ver.minor) != le32_to_cpu(recv_ver->minor)) + dev_warn(ixd_to_dev(adapter), + "Virtchnl version does not match (expected %u.%u, received %u.%u)\n", + le32_to_cpu(need_ver.major), + le32_to_cpu(need_ver.minor), + le32_to_cpu(recv_ver->major), + le32_to_cpu(recv_ver->minor)); + + if (le32_to_cpu(need_ver.major) != le32_to_cpu(recv_ver->major)) { + dev_err(ixd_to_dev(adapter), + "Device initialization failed due to virtchnl major version mismatch\n"); + return -EOPNOTSUPP; + } + + adapter->vc_ver.major = le32_to_cpu(recv_ver->major); + adapter->vc_ver.minor = le32_to_cpu(recv_ver->minor); + + return 0; +} + +/** + * ixd_req_vc_version - Request and save Virtchannel2 version + * @adapter: The adapter to get the version for + * + * Return: success or error if sending fails or the response was not as expected + */ +static int ixd_req_vc_version(struct ixd_adapter *adapter) +{ + const struct ixd_ctlq_req req = { + .opcode = VIRTCHNL2_OP_VERSION, + .send_size = sizeof(struct virtchnl2_version_info), + .ctx = NULL, + .send_buff_init = ixd_fill_vc_ver, + .recv_process = ixd_handle_vc_ver, + }; + + return ixd_ctlq_do_req(adapter, &req); +} + +/** + * ixd_vc_dev_init - virtchnl device core initialization + * @adapter: device information + * + * Return: %0 on success or error if any step of the initialization fails + */ +int ixd_vc_dev_init(struct ixd_adapter *adapter) +{ + int err; + + err = ixd_req_vc_version(adapter); + if (err) { + dev_warn(ixd_to_dev(adapter), + "Getting virtchnl version failed, error=%pe\n", + ERR_PTR(err)); + return err; + } + + err = ixd_req_vc_caps(adapter); + if (err) { + dev_warn(ixd_to_dev(adapter), + "Getting virtchnl capabilities failed, error=%pe\n", + ERR_PTR(err)); + return err; + } + + return err; +} diff --git a/drivers/net/ethernet/intel/ixd/ixd_virtchnl.h b/drivers/net/ethernet/intel/ixd/ixd_virtchnl.h new file mode 100644 index 000000000000..1a53da8b545c --- /dev/null +++ b/drivers/net/ethernet/intel/ixd/ixd_virtchnl.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2025 Intel Corporation */ + +#ifndef _IXD_VIRTCHNL_H_ +#define _IXD_VIRTCHNL_H_ + +int ixd_vc_dev_init(struct ixd_adapter *adapter); +bool ixd_vc_can_handle_msg(struct libie_ctlq_msg *ctlq_msg); +void ixd_vc_recv_event_msg(struct ixd_adapter *adapter, + struct libie_ctlq_msg *ctlq_msg); + +#endif /* _IXD_VIRTCHNL_H_ */ |
