hinic3: module initialization and tx/rx logic

This is [1/3] part of hinic3 Ethernet driver initial submission.
With this patch hinic3 is a valid kernel module but non-functional
driver.

The driver parts contained in this patch:
Module initialization.
PCI driver registration but with empty id_table.
Auxiliary driver registration.
Net device_ops registration but open/stop are empty stubs.
tx/rx logic.

All major data structures of the driver are fully introduced with the
code that uses them but without their initialization code that requires
management interface with the hw.

Co-developed-by: Xin Guo <guoxin09@huawei.com>
Signed-off-by: Xin Guo <guoxin09@huawei.com>
Signed-off-by: Fan Gong <gongfan1@huawei.com>
Co-developed-by: Gur Stavi <gur.stavi@huawei.com>
Signed-off-by: Gur Stavi <gur.stavi@huawei.com>
Link: https://patch.msgid.link/76a137ffdfe115c737c2c224f0c93b60ba53cc16.1747736586.git.gur.stavi@huawei.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Fan Gong
2025-05-21 20:31:42 -07:00
committed by Jakub Kicinski
parent bd15b2b26c
commit 17fcb3dc12
40 changed files with 3726 additions and 0 deletions
@@ -0,0 +1,137 @@
.. SPDX-License-Identifier: GPL-2.0
=====================================================================
Linux kernel driver for Huawei Ethernet Device Driver (hinic3) family
=====================================================================
Overview
========
The hinic3 is a network interface card (NIC) for Data Center. It supports
a range of link-speed devices (10GE, 25GE, 100GE, etc.). The hinic3
devices can have multiple physical forms: LOM (Lan on Motherboard) NIC,
PCIe standard NIC, OCP (Open Compute Project) NIC, etc.
The hinic3 driver supports the following features:
- IPv4/IPv6 TCP/UDP checksum offload
- TSO (TCP Segmentation Offload), LRO (Large Receive Offload)
- RSS (Receive Side Scaling)
- MSI-X interrupt aggregation configuration and interrupt adaptation.
- SR-IOV (Single Root I/O Virtualization).
Content
=======
- Supported PCI vendor ID/device IDs
- Source Code Structure of Hinic3 Driver
- Management Interface
Supported PCI vendor ID/device IDs
==================================
19e5:0222 - hinic3 PF/PPF
19e5:375F - hinic3 VF
Prime Physical Function (PPF) is responsible for the management of the
whole NIC card. For example, clock synchronization between the NIC and
the host. Any PF may serve as a PPF. The PPF is selected dynamically.
Source Code Structure of Hinic3 Driver
======================================
======================== ================================================
hinic3_pci_id_tbl.h Supported device IDs
hinic3_hw_intf.h Interface between HW and driver
hinic3_queue_common.[ch] Common structures and methods for NIC queues
hinic3_common.[ch] Encapsulation of memory operations in Linux
hinic3_csr.h Register definitions in the BAR
hinic3_hwif.[ch] Interface for BAR
hinic3_eqs.[ch] Interface for AEQs and CEQs
hinic3_mbox.[ch] Interface for mailbox
hinic3_mgmt.[ch] Management interface based on mailbox and AEQ
hinic3_wq.[ch] Work queue data structures and interface
hinic3_cmdq.[ch] Command queue is used to post command to HW
hinic3_hwdev.[ch] HW structures and methods abstractions
hinic3_lld.[ch] Auxiliary driver adaptation layer
hinic3_hw_comm.[ch] Interface for common HW operations
hinic3_mgmt_interface.h Interface between firmware and driver
hinic3_hw_cfg.[ch] Interface for HW configuration
hinic3_irq.c Interrupt request
hinic3_netdev_ops.c Operations registered to Linux kernel stack
hinic3_nic_dev.h NIC structures and methods abstractions
hinic3_main.c Main Linux kernel driver
hinic3_nic_cfg.[ch] NIC service configuration
hinic3_nic_io.[ch] Management plane interface for TX and RX
hinic3_rss.[ch] Interface for Receive Side Scaling (RSS)
hinic3_rx.[ch] Interface for transmit
hinic3_tx.[ch] Interface for receive
hinic3_ethtool.c Interface for ethtool operations (ops)
hinic3_filter.c Interface for MAC address
======================== ================================================
Management Interface
====================
Asynchronous Event Queue (AEQ)
------------------------------
AEQ receives high priority events from the HW over a descriptor queue.
Every descriptor is a fixed size of 64 bytes. AEQ can receive solicited or
unsolicited events. Every device, VF or PF, can have up to 4 AEQs.
Every AEQ is associated to a dedicated IRQ. AEQ can receive multiple types
of events, but in practice the hinic3 driver ignores all events except for
2 mailbox related events.
Mailbox
-------
Mailbox is a communication mechanism between the hinic3 driver and the HW.
Each device has an independent mailbox. Driver can use the mailbox to send
requests to management. Driver receives mailbox messages, such as responses
to requests, over the AEQ (using event HINIC3_AEQ_FOR_MBOX). Due to the
limited size of mailbox data register, mailbox messages are sent
segment-by-segment.
Every device can use its mailbox to post request to firmware. The mailbox
can also be used to post requests and responses between the PF and its VFs.
Completion Event Queue (CEQ)
----------------------------
The implementation of CEQ is the same as AEQ. It receives completion events
from HW over a fixed size descriptor of 32 bits. Every device can have up
to 32 CEQs. Every CEQ has a dedicated IRQ. CEQ only receives solicited
events that are responses to requests from the driver. CEQ can receive
multiple types of events, but in practice the hinic3 driver ignores all
events except for HINIC3_CMDQ that represents completion of previously
posted commands on a cmdq.
Command Queue (cmdq)
--------------------
Every cmdq has a dedicated work queue on which commands are posted.
Commands on the work queue are fixed size descriptor of size 64 bytes.
Completion of a command will be indicated using ctrl bits in the
descriptor that carried the command. Notification of command completions
will also be provided via event on CEQ. Every device has 4 command queues
that are initialized as a set (called cmdqs), each with its own type.
Hinic3 driver only uses type HINIC3_CMDQ_SYNC.
Work Queues(WQ)
---------------
Work queues are logical arrays of fixed size WQEs. The array may be spread
over multiple non-contiguous pages using indirection table. Work queues are
used by I/O queues and command queues.
Global function ID
------------------
Every function, PF or VF, has a unique ordinal identification within the device.
Many management commands (mbox or cmdq) contain this ID so HW can apply the
command effect to the right function.
PF is allowed to post management commands to a subordinate VF by specifying the
VFs ID. A VF must provide its own ID. Anti-spoofing in the HW will cause
command from a VF to fail if it contains the wrong ID.
@@ -28,6 +28,7 @@ Contents:
freescale/gianfar
google/gve
huawei/hinic
huawei/hinic3
intel/e100
intel/e1000
intel/e1000e
+7
View File
@@ -10963,6 +10963,13 @@ S: Maintained
F: Documentation/networking/device_drivers/ethernet/huawei/hinic.rst
F: drivers/net/ethernet/huawei/hinic/
HUAWEI 3RD GEN ETHERNET DRIVER
M: Fan Gong <gongfan1@huawei.com>
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/networking/device_drivers/ethernet/huawei/hinic3.rst
F: drivers/net/ethernet/huawei/hinic3/
HUAWEI MATEBOOK E GO EMBEDDED CONTROLLER DRIVER
M: Pengyu Luo <mitltlatltl@gmail.com>
S: Maintained
+1
View File
@@ -16,5 +16,6 @@ config NET_VENDOR_HUAWEI
if NET_VENDOR_HUAWEI
source "drivers/net/ethernet/huawei/hinic/Kconfig"
source "drivers/net/ethernet/huawei/hinic3/Kconfig"
endif # NET_VENDOR_HUAWEI
+1
View File
@@ -4,3 +4,4 @@
#
obj-$(CONFIG_HINIC) += hinic/
obj-$(CONFIG_HINIC3) += hinic3/
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Huawei driver configuration
#
config HINIC3
tristate "Huawei 3rd generation network adapters (HINIC3) support"
# Fields of HW and management structures are little endian and are
# currently not converted
depends on !CPU_BIG_ENDIAN
depends on X86 || ARM64 || COMPILE_TEST
depends on PCI_MSI && 64BIT
select AUXILIARY_BUS
select PAGE_POOL
help
This driver supports HiNIC 3rd gen Network Adapter (HINIC3).
The driver is supported on X86_64 and ARM64 little endian.
To compile this driver as a module, choose M here.
The module will be called hinic3.
@@ -0,0 +1,21 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
obj-$(CONFIG_HINIC3) += hinic3.o
hinic3-objs := hinic3_common.o \
hinic3_hw_cfg.o \
hinic3_hw_comm.o \
hinic3_hwdev.o \
hinic3_hwif.o \
hinic3_irq.o \
hinic3_lld.o \
hinic3_main.o \
hinic3_mbox.o \
hinic3_netdev_ops.o \
hinic3_nic_cfg.o \
hinic3_nic_io.o \
hinic3_queue_common.o \
hinic3_rx.o \
hinic3_tx.o \
hinic3_wq.o
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include "hinic3_common.h"
int hinic3_dma_zalloc_coherent_align(struct device *dev, u32 size, u32 align,
gfp_t flag,
struct hinic3_dma_addr_align *mem_align)
{
dma_addr_t paddr, align_paddr;
void *vaddr, *align_vaddr;
u32 real_size = size;
vaddr = dma_alloc_coherent(dev, real_size, &paddr, flag);
if (!vaddr)
return -ENOMEM;
align_paddr = ALIGN(paddr, align);
if (align_paddr == paddr) {
align_vaddr = vaddr;
goto out;
}
dma_free_coherent(dev, real_size, vaddr, paddr);
/* realloc memory for align */
real_size = size + align;
vaddr = dma_alloc_coherent(dev, real_size, &paddr, flag);
if (!vaddr)
return -ENOMEM;
align_paddr = ALIGN(paddr, align);
align_vaddr = vaddr + (align_paddr - paddr);
out:
mem_align->real_size = real_size;
mem_align->ori_vaddr = vaddr;
mem_align->ori_paddr = paddr;
mem_align->align_vaddr = align_vaddr;
mem_align->align_paddr = align_paddr;
return 0;
}
void hinic3_dma_free_coherent_align(struct device *dev,
struct hinic3_dma_addr_align *mem_align)
{
dma_free_coherent(dev, mem_align->real_size,
mem_align->ori_vaddr, mem_align->ori_paddr);
}
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. */
#ifndef _HINIC3_COMMON_H_
#define _HINIC3_COMMON_H_
#include <linux/device.h>
#define HINIC3_MIN_PAGE_SIZE 0x1000
struct hinic3_dma_addr_align {
u32 real_size;
void *ori_vaddr;
dma_addr_t ori_paddr;
void *align_vaddr;
dma_addr_t align_paddr;
};
int hinic3_dma_zalloc_coherent_align(struct device *dev, u32 size, u32 align,
gfp_t flag,
struct hinic3_dma_addr_align *mem_align);
void hinic3_dma_free_coherent_align(struct device *dev,
struct hinic3_dma_addr_align *mem_align);
#endif
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
#include <linux/device.h>
#include "hinic3_hw_cfg.h"
#include "hinic3_hwdev.h"
#include "hinic3_hwif.h"
#include "hinic3_mbox.h"
bool hinic3_support_nic(struct hinic3_hwdev *hwdev)
{
return hwdev->cfg_mgmt->cap.supp_svcs_bitmap &
BIT(HINIC3_SERVICE_T_NIC);
}
u16 hinic3_func_max_qnum(struct hinic3_hwdev *hwdev)
{
return hwdev->cfg_mgmt->cap.nic_svc_cap.max_sqs;
}
u8 hinic3_physical_port_id(struct hinic3_hwdev *hwdev)
{
return hwdev->cfg_mgmt->cap.port_id;
}
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. */
#ifndef _HINIC3_HW_CFG_H_
#define _HINIC3_HW_CFG_H_
#include <linux/mutex.h>
#include <linux/pci.h>
struct hinic3_hwdev;
struct hinic3_irq {
u32 irq_id;
u16 msix_entry_idx;
bool allocated;
};
struct hinic3_irq_info {
struct hinic3_irq *irq;
u16 num_irq;
/* device max irq number */
u16 num_irq_hw;
/* protect irq alloc and free */
struct mutex irq_mutex;
};
struct hinic3_nic_service_cap {
u16 max_sqs;
};
/* Device capabilities */
struct hinic3_dev_cap {
/* Bitmasks of services supported by device */
u16 supp_svcs_bitmap;
/* Physical port */
u8 port_id;
struct hinic3_nic_service_cap nic_svc_cap;
};
struct hinic3_cfg_mgmt_info {
struct hinic3_irq_info irq_info;
struct hinic3_dev_cap cap;
};
int hinic3_alloc_irqs(struct hinic3_hwdev *hwdev, u16 num,
struct msix_entry *alloc_arr, u16 *act_num);
void hinic3_free_irq(struct hinic3_hwdev *hwdev, u32 irq_id);
bool hinic3_support_nic(struct hinic3_hwdev *hwdev);
u16 hinic3_func_max_qnum(struct hinic3_hwdev *hwdev);
u8 hinic3_physical_port_id(struct hinic3_hwdev *hwdev);
#endif
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
#include <linux/delay.h>
#include "hinic3_hw_comm.h"
#include "hinic3_hwdev.h"
#include "hinic3_hwif.h"
#include "hinic3_mbox.h"
int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 reset_flag)
{
struct comm_cmd_func_reset func_reset = {};
struct mgmt_msg_params msg_params = {};
int err;
func_reset.func_id = func_id;
func_reset.reset_flag = reset_flag;
mgmt_msg_params_init_default(&msg_params, &func_reset,
sizeof(func_reset));
err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
COMM_CMD_FUNC_RESET, &msg_params);
if (err || func_reset.head.status) {
dev_err(hwdev->dev, "Failed to reset func resources, reset_flag 0x%llx, err: %d, status: 0x%x\n",
reset_flag, err, func_reset.head.status);
return -EIO;
}
return 0;
}
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. */
#ifndef _HINIC3_HW_COMM_H_
#define _HINIC3_HW_COMM_H_
#include "hinic3_hw_intf.h"
struct hinic3_hwdev;
int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 reset_flag);
#endif
@@ -0,0 +1,113 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. */
#ifndef _HINIC3_HW_INTF_H_
#define _HINIC3_HW_INTF_H_
#include <linux/bits.h>
#include <linux/types.h>
#define MGMT_MSG_CMD_OP_SET 1
#define MGMT_MSG_CMD_OP_GET 0
#define MGMT_STATUS_PF_SET_VF_ALREADY 0x4
#define MGMT_STATUS_EXIST 0x6
#define MGMT_STATUS_CMD_UNSUPPORTED 0xFF
#define MGMT_MSG_POLLING_TIMEOUT 0
struct mgmt_msg_head {
u8 status;
u8 version;
u8 rsvd0[6];
};
struct mgmt_msg_params {
const void *buf_in;
u32 in_size;
void *buf_out;
u32 expected_out_size;
u32 timeout_ms;
};
/* CMDQ MODULE_TYPE */
enum mgmt_mod_type {
/* HW communication module */
MGMT_MOD_COMM = 0,
/* L2NIC module */
MGMT_MOD_L2NIC = 1,
/* Configuration module */
MGMT_MOD_CFGM = 7,
MGMT_MOD_HILINK = 14,
};
static inline void mgmt_msg_params_init_default(struct mgmt_msg_params *msg_params,
void *inout_buf, u32 buf_size)
{
msg_params->buf_in = inout_buf;
msg_params->buf_out = inout_buf;
msg_params->in_size = buf_size;
msg_params->expected_out_size = buf_size;
msg_params->timeout_ms = 0;
}
/* COMM Commands between Driver to fw */
enum comm_cmd {
/* Commands for clearing FLR and resources */
COMM_CMD_FUNC_RESET = 0,
COMM_CMD_FEATURE_NEGO = 1,
COMM_CMD_FLUSH_DOORBELL = 2,
COMM_CMD_START_FLUSH = 3,
COMM_CMD_GET_GLOBAL_ATTR = 5,
COMM_CMD_SET_FUNC_SVC_USED_STATE = 7,
/* Driver Configuration Commands */
COMM_CMD_SET_CMDQ_CTXT = 20,
COMM_CMD_SET_VAT = 21,
COMM_CMD_CFG_PAGESIZE = 22,
COMM_CMD_CFG_MSIX_CTRL_REG = 23,
COMM_CMD_SET_CEQ_CTRL_REG = 24,
COMM_CMD_SET_DMA_ATTR = 25,
};
enum comm_func_reset_bits {
COMM_FUNC_RESET_BIT_FLUSH = BIT(0),
COMM_FUNC_RESET_BIT_MQM = BIT(1),
COMM_FUNC_RESET_BIT_SMF = BIT(2),
COMM_FUNC_RESET_BIT_PF_BW_CFG = BIT(3),
COMM_FUNC_RESET_BIT_COMM = BIT(10),
/* clear mbox and aeq, The COMM_FUNC_RESET_BIT_COMM bit must be set */
COMM_FUNC_RESET_BIT_COMM_MGMT_CH = BIT(11),
/* clear cmdq and ceq, The COMM_FUNC_RESET_BIT_COMM bit must be set */
COMM_FUNC_RESET_BIT_COMM_CMD_CH = BIT(12),
COMM_FUNC_RESET_BIT_NIC = BIT(13),
};
struct comm_cmd_func_reset {
struct mgmt_msg_head head;
u16 func_id;
u16 rsvd1[3];
u64 reset_flag;
};
#define COMM_MAX_FEATURE_QWORD 4
struct comm_cmd_feature_nego {
struct mgmt_msg_head head;
u16 func_id;
u8 opcode;
u8 rsvd;
u64 s_feature[COMM_MAX_FEATURE_QWORD];
};
/* Services supported by HW. HW uses these values when delivering events.
* HW supports multiple services that are not yet supported by driver
* (e.g. RoCE).
*/
enum hinic3_service_type {
HINIC3_SERVICE_T_NIC = 0,
/* MAX is only used by SW for array sizes. */
HINIC3_SERVICE_T_MAX = 1,
};
#endif
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
#include "hinic3_hw_comm.h"
#include "hinic3_hwdev.h"
#include "hinic3_hwif.h"
#include "hinic3_mbox.h"
#include "hinic3_mgmt.h"
int hinic3_init_hwdev(struct pci_dev *pdev)
{
/* Completed by later submission due to LoC limit. */
return -EFAULT;
}
void hinic3_free_hwdev(struct hinic3_hwdev *hwdev)
{
/* Completed by later submission due to LoC limit. */
}
void hinic3_set_api_stop(struct hinic3_hwdev *hwdev)
{
/* Completed by later submission due to LoC limit. */
}
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. */
#ifndef _HINIC3_HWDEV_H_
#define _HINIC3_HWDEV_H_
#include <linux/auxiliary_bus.h>
#include <linux/pci.h>
#include "hinic3_hw_intf.h"
struct hinic3_cmdqs;
struct hinic3_hwif;
enum hinic3_event_service_type {
HINIC3_EVENT_SRV_COMM = 0,
HINIC3_EVENT_SRV_NIC = 1
};
#define HINIC3_SRV_EVENT_TYPE(svc, type) (((svc) << 16) | (type))
/* driver-specific data of pci_dev */
struct hinic3_pcidev {
struct pci_dev *pdev;
struct hinic3_hwdev *hwdev;
/* Auxiliary devices */
struct hinic3_adev *hadev[HINIC3_SERVICE_T_MAX];
void __iomem *cfg_reg_base;
void __iomem *intr_reg_base;
void __iomem *db_base;
u64 db_dwqe_len;
u64 db_base_phy;
/* lock for attach/detach uld */
struct mutex pdev_mutex;
unsigned long state;
};
struct hinic3_hwdev {
struct hinic3_pcidev *adapter;
struct pci_dev *pdev;
struct device *dev;
int dev_id;
struct hinic3_hwif *hwif;
struct hinic3_cfg_mgmt_info *cfg_mgmt;
struct hinic3_aeqs *aeqs;
struct hinic3_ceqs *ceqs;
struct hinic3_mbox *mbox;
struct hinic3_cmdqs *cmdqs;
struct workqueue_struct *workq;
/* protect channel init and uninit */
spinlock_t channel_lock;
u64 features[COMM_MAX_FEATURE_QWORD];
u32 wq_page_size;
u8 max_cmdq;
ulong func_state;
};
struct hinic3_event_info {
/* enum hinic3_event_service_type */
u16 service;
u16 type;
u8 event_data[104];
};
struct hinic3_adev {
struct auxiliary_device adev;
struct hinic3_hwdev *hwdev;
enum hinic3_service_type svc_type;
void (*event)(struct auxiliary_device *adev,
struct hinic3_event_info *event);
};
int hinic3_init_hwdev(struct pci_dev *pdev);
void hinic3_free_hwdev(struct hinic3_hwdev *hwdev);
void hinic3_set_api_stop(struct hinic3_hwdev *hwdev);
#endif
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/io.h>
#include "hinic3_common.h"
#include "hinic3_hwdev.h"
#include "hinic3_hwif.h"
void hinic3_set_msix_state(struct hinic3_hwdev *hwdev, u16 msix_idx,
enum hinic3_msix_state flag)
{
/* Completed by later submission due to LoC limit. */
}
u16 hinic3_global_func_id(struct hinic3_hwdev *hwdev)
{
return hwdev->hwif->attr.func_global_idx;
}
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. */
#ifndef _HINIC3_HWIF_H_
#define _HINIC3_HWIF_H_
#include <linux/build_bug.h>
#include <linux/spinlock_types.h>
struct hinic3_hwdev;
enum hinic3_func_type {
HINIC3_FUNC_TYPE_VF = 1,
};
struct hinic3_db_area {
unsigned long *db_bitmap_array;
u32 db_max_areas;
/* protect doorbell area alloc and free */
spinlock_t idx_lock;
};
struct hinic3_func_attr {
enum hinic3_func_type func_type;
u16 func_global_idx;
u16 global_vf_id_of_pf;
u16 num_irqs;
u16 num_sq;
u8 port_to_port_idx;
u8 pci_intf_idx;
u8 ppf_idx;
u8 num_aeqs;
u8 num_ceqs;
u8 msix_flex_en;
};
static_assert(sizeof(struct hinic3_func_attr) == 20);
struct hinic3_hwif {
u8 __iomem *cfg_regs_base;
u64 db_base_phy;
u64 db_dwqe_len;
u8 __iomem *db_base;
struct hinic3_db_area db_area;
struct hinic3_func_attr attr;
};
enum hinic3_msix_state {
HINIC3_MSIX_ENABLE,
HINIC3_MSIX_DISABLE,
};
void hinic3_set_msix_state(struct hinic3_hwdev *hwdev, u16 msix_idx,
enum hinic3_msix_state flag);
u16 hinic3_global_func_id(struct hinic3_hwdev *hwdev);
#endif
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
#include <linux/netdevice.h>
#include "hinic3_hw_comm.h"
#include "hinic3_hwdev.h"
#include "hinic3_hwif.h"
#include "hinic3_nic_dev.h"
#include "hinic3_rx.h"
#include "hinic3_tx.h"
static int hinic3_poll(struct napi_struct *napi, int budget)
{
struct hinic3_irq_cfg *irq_cfg =
container_of(napi, struct hinic3_irq_cfg, napi);
struct hinic3_nic_dev *nic_dev;
bool busy = false;
int work_done;
nic_dev = netdev_priv(irq_cfg->netdev);
busy |= hinic3_tx_poll(irq_cfg->txq, budget);
if (unlikely(!budget))
return 0;
work_done = hinic3_rx_poll(irq_cfg->rxq, budget);
busy |= work_done >= budget;
if (busy)
return budget;
if (likely(napi_complete_done(napi, work_done)))
hinic3_set_msix_state(nic_dev->hwdev, irq_cfg->msix_entry_idx,
HINIC3_MSIX_ENABLE);
return work_done;
}
void qp_add_napi(struct hinic3_irq_cfg *irq_cfg)
{
struct hinic3_nic_dev *nic_dev = netdev_priv(irq_cfg->netdev);
netif_queue_set_napi(irq_cfg->netdev, irq_cfg->irq_id,
NETDEV_QUEUE_TYPE_RX, &irq_cfg->napi);
netif_queue_set_napi(irq_cfg->netdev, irq_cfg->irq_id,
NETDEV_QUEUE_TYPE_TX, &irq_cfg->napi);
netif_napi_add(nic_dev->netdev, &irq_cfg->napi, hinic3_poll);
napi_enable(&irq_cfg->napi);
}
void qp_del_napi(struct hinic3_irq_cfg *irq_cfg)
{
napi_disable(&irq_cfg->napi);
netif_queue_set_napi(irq_cfg->netdev, irq_cfg->irq_id,
NETDEV_QUEUE_TYPE_RX, NULL);
netif_queue_set_napi(irq_cfg->netdev, irq_cfg->irq_id,
NETDEV_QUEUE_TYPE_TX, NULL);
netif_stop_subqueue(irq_cfg->netdev, irq_cfg->irq_id);
netif_napi_del(&irq_cfg->napi);
}
@@ -0,0 +1,414 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
#include <linux/delay.h>
#include <linux/iopoll.h>
#include "hinic3_hw_cfg.h"
#include "hinic3_hwdev.h"
#include "hinic3_lld.h"
#include "hinic3_mgmt.h"
#define HINIC3_VF_PCI_CFG_REG_BAR 0
#define HINIC3_PCI_INTR_REG_BAR 2
#define HINIC3_PCI_DB_BAR 4
#define HINIC3_EVENT_POLL_SLEEP_US 1000
#define HINIC3_EVENT_POLL_TIMEOUT_US 10000000
static struct hinic3_adev_device {
const char *name;
} hinic3_adev_devices[HINIC3_SERVICE_T_MAX] = {
[HINIC3_SERVICE_T_NIC] = {
.name = "nic",
},
};
static bool hinic3_adev_svc_supported(struct hinic3_hwdev *hwdev,
enum hinic3_service_type svc_type)
{
switch (svc_type) {
case HINIC3_SERVICE_T_NIC:
return hinic3_support_nic(hwdev);
default:
break;
}
return false;
}
static void hinic3_comm_adev_release(struct device *dev)
{
struct hinic3_adev *hadev = container_of(dev, struct hinic3_adev,
adev.dev);
kfree(hadev);
}
static struct hinic3_adev *hinic3_add_one_adev(struct hinic3_hwdev *hwdev,
enum hinic3_service_type svc_type)
{
struct hinic3_adev *hadev;
const char *svc_name;
int ret;
hadev = kzalloc(sizeof(*hadev), GFP_KERNEL);
if (!hadev)
return NULL;
svc_name = hinic3_adev_devices[svc_type].name;
hadev->adev.name = svc_name;
hadev->adev.id = hwdev->dev_id;
hadev->adev.dev.parent = hwdev->dev;
hadev->adev.dev.release = hinic3_comm_adev_release;
hadev->svc_type = svc_type;
hadev->hwdev = hwdev;
ret = auxiliary_device_init(&hadev->adev);
if (ret) {
dev_err(hwdev->dev, "failed init adev %s %u\n",
svc_name, hwdev->dev_id);
kfree(hadev);
return NULL;
}
ret = auxiliary_device_add(&hadev->adev);
if (ret) {
dev_err(hwdev->dev, "failed to add adev %s %u\n",
svc_name, hwdev->dev_id);
auxiliary_device_uninit(&hadev->adev);
return NULL;
}
return hadev;
}
static void hinic3_del_one_adev(struct hinic3_hwdev *hwdev,
enum hinic3_service_type svc_type)
{
struct hinic3_pcidev *pci_adapter = hwdev->adapter;
struct hinic3_adev *hadev;
int timeout;
bool state;
timeout = read_poll_timeout(test_and_set_bit, state, !state,
HINIC3_EVENT_POLL_SLEEP_US,
HINIC3_EVENT_POLL_TIMEOUT_US,
false, svc_type, &pci_adapter->state);
hadev = pci_adapter->hadev[svc_type];
auxiliary_device_delete(&hadev->adev);
auxiliary_device_uninit(&hadev->adev);
pci_adapter->hadev[svc_type] = NULL;
if (!timeout)
clear_bit(svc_type, &pci_adapter->state);
}
static int hinic3_attach_aux_devices(struct hinic3_hwdev *hwdev)
{
struct hinic3_pcidev *pci_adapter = hwdev->adapter;
enum hinic3_service_type svc_type;
mutex_lock(&pci_adapter->pdev_mutex);
for (svc_type = 0; svc_type < HINIC3_SERVICE_T_MAX; svc_type++) {
if (!hinic3_adev_svc_supported(hwdev, svc_type))
continue;
pci_adapter->hadev[svc_type] = hinic3_add_one_adev(hwdev,
svc_type);
if (!pci_adapter->hadev[svc_type])
goto err_del_adevs;
}
mutex_unlock(&pci_adapter->pdev_mutex);
return 0;
err_del_adevs:
while (svc_type > 0) {
svc_type--;
if (pci_adapter->hadev[svc_type]) {
hinic3_del_one_adev(hwdev, svc_type);
pci_adapter->hadev[svc_type] = NULL;
}
}
mutex_unlock(&pci_adapter->pdev_mutex);
return -ENOMEM;
}
static void hinic3_detach_aux_devices(struct hinic3_hwdev *hwdev)
{
struct hinic3_pcidev *pci_adapter = hwdev->adapter;
int i;
mutex_lock(&pci_adapter->pdev_mutex);
for (i = 0; i < ARRAY_SIZE(hinic3_adev_devices); i++) {
if (pci_adapter->hadev[i])
hinic3_del_one_adev(hwdev, i);
}
mutex_unlock(&pci_adapter->pdev_mutex);
}
struct hinic3_hwdev *hinic3_adev_get_hwdev(struct auxiliary_device *adev)
{
struct hinic3_adev *hadev;
hadev = container_of(adev, struct hinic3_adev, adev);
return hadev->hwdev;
}
void hinic3_adev_event_register(struct auxiliary_device *adev,
void (*event_handler)(struct auxiliary_device *adev,
struct hinic3_event_info *event))
{
struct hinic3_adev *hadev;
hadev = container_of(adev, struct hinic3_adev, adev);
hadev->event = event_handler;
}
void hinic3_adev_event_unregister(struct auxiliary_device *adev)
{
struct hinic3_adev *hadev;
hadev = container_of(adev, struct hinic3_adev, adev);
hadev->event = NULL;
}
static int hinic3_mapping_bar(struct pci_dev *pdev,
struct hinic3_pcidev *pci_adapter)
{
pci_adapter->cfg_reg_base = pci_ioremap_bar(pdev,
HINIC3_VF_PCI_CFG_REG_BAR);
if (!pci_adapter->cfg_reg_base) {
dev_err(&pdev->dev, "Failed to map configuration regs\n");
return -ENOMEM;
}
pci_adapter->intr_reg_base = pci_ioremap_bar(pdev,
HINIC3_PCI_INTR_REG_BAR);
if (!pci_adapter->intr_reg_base) {
dev_err(&pdev->dev, "Failed to map interrupt regs\n");
goto err_unmap_cfg_reg_base;
}
pci_adapter->db_base_phy = pci_resource_start(pdev, HINIC3_PCI_DB_BAR);
pci_adapter->db_dwqe_len = pci_resource_len(pdev, HINIC3_PCI_DB_BAR);
pci_adapter->db_base = pci_ioremap_bar(pdev, HINIC3_PCI_DB_BAR);
if (!pci_adapter->db_base) {
dev_err(&pdev->dev, "Failed to map doorbell regs\n");
goto err_unmap_intr_reg_base;
}
return 0;
err_unmap_intr_reg_base:
iounmap(pci_adapter->intr_reg_base);
err_unmap_cfg_reg_base:
iounmap(pci_adapter->cfg_reg_base);
return -ENOMEM;
}
static void hinic3_unmapping_bar(struct hinic3_pcidev *pci_adapter)
{
iounmap(pci_adapter->db_base);
iounmap(pci_adapter->intr_reg_base);
iounmap(pci_adapter->cfg_reg_base);
}
static int hinic3_pci_init(struct pci_dev *pdev)
{
struct hinic3_pcidev *pci_adapter;
int err;
pci_adapter = kzalloc(sizeof(*pci_adapter), GFP_KERNEL);
if (!pci_adapter)
return -ENOMEM;
pci_adapter->pdev = pdev;
mutex_init(&pci_adapter->pdev_mutex);
pci_set_drvdata(pdev, pci_adapter);
err = pci_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "Failed to enable PCI device\n");
goto err_free_pci_adapter;
}
err = pci_request_regions(pdev, HINIC3_NIC_DRV_NAME);
if (err) {
dev_err(&pdev->dev, "Failed to request regions\n");
goto err_disable_device;
}
pci_set_master(pdev);
err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (err) {
dev_err(&pdev->dev, "Failed to set DMA mask\n");
goto err_release_regions;
}
return 0;
err_release_regions:
pci_clear_master(pdev);
pci_release_regions(pdev);
err_disable_device:
pci_disable_device(pdev);
err_free_pci_adapter:
pci_set_drvdata(pdev, NULL);
mutex_destroy(&pci_adapter->pdev_mutex);
kfree(pci_adapter);
return err;
}
static void hinic3_pci_uninit(struct pci_dev *pdev)
{
struct hinic3_pcidev *pci_adapter = pci_get_drvdata(pdev);
pci_clear_master(pdev);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
mutex_destroy(&pci_adapter->pdev_mutex);
kfree(pci_adapter);
}
static int hinic3_func_init(struct pci_dev *pdev,
struct hinic3_pcidev *pci_adapter)
{
int err;
err = hinic3_init_hwdev(pdev);
if (err) {
dev_err(&pdev->dev, "Failed to initialize hardware device\n");
return err;
}
err = hinic3_attach_aux_devices(pci_adapter->hwdev);
if (err)
goto err_free_hwdev;
return 0;
err_free_hwdev:
hinic3_free_hwdev(pci_adapter->hwdev);
return err;
}
static void hinic3_func_uninit(struct pci_dev *pdev)
{
struct hinic3_pcidev *pci_adapter = pci_get_drvdata(pdev);
hinic3_detach_aux_devices(pci_adapter->hwdev);
hinic3_free_hwdev(pci_adapter->hwdev);
}
static int hinic3_probe_func(struct hinic3_pcidev *pci_adapter)
{
struct pci_dev *pdev = pci_adapter->pdev;
int err;
err = hinic3_mapping_bar(pdev, pci_adapter);
if (err) {
dev_err(&pdev->dev, "Failed to map bar\n");
goto err_out;
}
err = hinic3_func_init(pdev, pci_adapter);
if (err)
goto err_unmap_bar;
return 0;
err_unmap_bar:
hinic3_unmapping_bar(pci_adapter);
err_out:
dev_err(&pdev->dev, "PCIe device probe function failed\n");
return err;
}
static void hinic3_remove_func(struct hinic3_pcidev *pci_adapter)
{
struct pci_dev *pdev = pci_adapter->pdev;
hinic3_func_uninit(pdev);
hinic3_unmapping_bar(pci_adapter);
}
static int hinic3_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct hinic3_pcidev *pci_adapter;
int err;
err = hinic3_pci_init(pdev);
if (err)
goto err_out;
pci_adapter = pci_get_drvdata(pdev);
err = hinic3_probe_func(pci_adapter);
if (err)
goto err_uninit_pci;
return 0;
err_uninit_pci:
hinic3_pci_uninit(pdev);
err_out:
dev_err(&pdev->dev, "PCIe device probe failed\n");
return err;
}
static void hinic3_remove(struct pci_dev *pdev)
{
struct hinic3_pcidev *pci_adapter = pci_get_drvdata(pdev);
hinic3_remove_func(pci_adapter);
hinic3_pci_uninit(pdev);
}
static const struct pci_device_id hinic3_pci_table[] = {
/* Completed by later submission due to LoC limit. */
{0, 0}
};
MODULE_DEVICE_TABLE(pci, hinic3_pci_table);
static void hinic3_shutdown(struct pci_dev *pdev)
{
struct hinic3_pcidev *pci_adapter = pci_get_drvdata(pdev);
pci_disable_device(pdev);
if (pci_adapter)
hinic3_set_api_stop(pci_adapter->hwdev);
}
static struct pci_driver hinic3_driver = {
.name = HINIC3_NIC_DRV_NAME,
.id_table = hinic3_pci_table,
.probe = hinic3_probe,
.remove = hinic3_remove,
.shutdown = hinic3_shutdown,
.sriov_configure = pci_sriov_configure_simple
};
int hinic3_lld_init(void)
{
return pci_register_driver(&hinic3_driver);
}
void hinic3_lld_exit(void)
{
pci_unregister_driver(&hinic3_driver);
}

Some files were not shown because too many files have changed in this diff Show More