Regards, Suman >-----Original Message----- >From: Gur Stavi <gur.stavi@xxxxxxxxxx> >Sent: Wednesday, January 1, 2025 6:35 PM >To: Gur Stavi <gur.stavi@xxxxxxxxxx>; gongfan <gongfan1@xxxxxxxxxx> >Cc: netdev@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; David S. >Miller <davem@xxxxxxxxxxxxx>; Eric Dumazet <edumazet@xxxxxxxxxx>; Jakub >Kicinski <kuba@xxxxxxxxxx>; Paolo Abeni <pabeni@xxxxxxxxxx>; Simon >Horman <horms@xxxxxxxxxx>; Andrew Lunn <andrew+netdev@xxxxxxx>; linux- >doc@xxxxxxxxxxxxxxx; Jonathan Corbet <corbet@xxxxxxx>; Bjorn Helgaas ><helgaas@xxxxxxxxxx>; Cai Huoqing <cai.huoqing@xxxxxxxxx>; Xin Guo ><guoxin09@xxxxxxxxxx>; Shen Chenyang <shenchenyang1@xxxxxxxxxxxxx>; Zhou >Shuai <zhoushuai28@xxxxxxxxxx>; Wu Like <wulike1@xxxxxxxxxx>; Shi Jing ><shijing34@xxxxxxxxxx>; Meny Yossefi <meny.yossefi@xxxxxxxxxx> >Subject: [EXTERNAL] [PATCH net-next v03 1/1] hinic3: module >initialization and tx/rx logic > >From: gongfan <gongfan1@ huawei. com> 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. > >From: gongfan <gongfan1@xxxxxxxxxx> > >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. > >Submitted-by: Gur Stavi <gur.stavi@xxxxxxxxxx> >Signed-off-by: Gur Stavi <gur.stavi@xxxxxxxxxx> >Signed-off-by: Xin Guo <guoxin09@xxxxxxxxxx> >Signed-off-by: gongfan <gongfan1@xxxxxxxxxx> >--- > .../device_drivers/ethernet/huawei/hinic3.rst | 137 ++++ > MAINTAINERS | 7 + > drivers/net/ethernet/huawei/Kconfig | 1 + > drivers/net/ethernet/huawei/Makefile | 1 + > drivers/net/ethernet/huawei/hinic3/Kconfig | 18 + > drivers/net/ethernet/huawei/hinic3/Makefile | 21 + > .../ethernet/huawei/hinic3/hinic3_common.c | 53 ++ > .../ethernet/huawei/hinic3/hinic3_common.h | 27 + > .../ethernet/huawei/hinic3/hinic3_hw_cfg.c | 30 + > .../ethernet/huawei/hinic3/hinic3_hw_cfg.h | 58 ++ > .../ethernet/huawei/hinic3/hinic3_hw_comm.c | 37 + > .../ethernet/huawei/hinic3/hinic3_hw_comm.h | 13 + > .../ethernet/huawei/hinic3/hinic3_hw_intf.h | 85 +++ > .../net/ethernet/huawei/hinic3/hinic3_hwdev.c | 24 + > .../net/ethernet/huawei/hinic3/hinic3_hwdev.h | 82 +++ > .../net/ethernet/huawei/hinic3/hinic3_hwif.c | 15 + > .../net/ethernet/huawei/hinic3/hinic3_hwif.h | 50 ++ > .../net/ethernet/huawei/hinic3/hinic3_lld.c | 410 +++++++++++ > .../net/ethernet/huawei/hinic3/hinic3_lld.h | 20 + > .../net/ethernet/huawei/hinic3/hinic3_main.c | 421 +++++++++++ > .../net/ethernet/huawei/hinic3/hinic3_mbox.c | 17 + > .../net/ethernet/huawei/hinic3/hinic3_mbox.h | 16 + > .../net/ethernet/huawei/hinic3/hinic3_mgmt.h | 13 + > .../huawei/hinic3/hinic3_mgmt_interface.h | 111 +++ > .../huawei/hinic3/hinic3_netdev_ops.c | 77 ++ > .../ethernet/huawei/hinic3/hinic3_nic_cfg.c | 254 +++++++ > .../ethernet/huawei/hinic3/hinic3_nic_cfg.h | 45 ++ > .../ethernet/huawei/hinic3/hinic3_nic_dev.h | 100 +++ > .../ethernet/huawei/hinic3/hinic3_nic_io.c | 21 + > .../ethernet/huawei/hinic3/hinic3_nic_io.h | 117 +++ > .../huawei/hinic3/hinic3_queue_common.c | 65 ++ > .../huawei/hinic3/hinic3_queue_common.h | 51 ++ > .../net/ethernet/huawei/hinic3/hinic3_rss.c | 24 + > .../net/ethernet/huawei/hinic3/hinic3_rss.h | 12 + > .../net/ethernet/huawei/hinic3/hinic3_rx.c | 401 ++++++++++ > .../net/ethernet/huawei/hinic3/hinic3_rx.h | 91 +++ > .../net/ethernet/huawei/hinic3/hinic3_tx.c | 692 ++++++++++++++++++ > .../net/ethernet/huawei/hinic3/hinic3_tx.h | 129 ++++ > .../net/ethernet/huawei/hinic3/hinic3_wq.c | 29 + > .../net/ethernet/huawei/hinic3/hinic3_wq.h | 75 ++ > 40 files changed, 3850 insertions(+) > >diff --git >a/Documentation/networking/device_drivers/ethernet/huawei/hinic3.rst >b/Documentation/networking/device_drivers/ethernet/huawei/hinic3.rst >new file mode 100644 >index 000000000000..fe4bd0aed85c >--- /dev/null >+++ b/Documentation/networking/device_drivers/ethernet/huawei/hinic3.rst >@@ -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 NIC, PCIe standard NIC, >+OCP 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 commands to management (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. >+ >diff --git a/MAINTAINERS b/MAINTAINERS >index 1579124ef426..78819812093a 100644 >--- a/MAINTAINERS >+++ b/MAINTAINERS >@@ -10602,6 +10602,13 @@ S: Maintained > F: Documentation/networking/device_drivers/ethernet/huawei/hinic.rst > F: drivers/net/ethernet/huawei/hinic/ > >+HUAWEI 3RD GEN ETHERNET DRIVER >+M: gongfan <gongfan1@xxxxxxxxxx> >+L: netdev@xxxxxxxxxxxxxxx >+S: Supported >+F: Documentation/networking/device_drivers/ethernet/huawei/hinic3.rst >+F: drivers/net/ethernet/huawei/hinic3/ >+ > HUGETLB SUBSYSTEM > M: Muchun Song <muchun.song@xxxxxxxxx> > L: linux-mm@xxxxxxxxx >diff --git a/drivers/net/ethernet/huawei/Kconfig >b/drivers/net/ethernet/huawei/Kconfig >index c05fce15eb51..7d0feb1da158 100644 >--- a/drivers/net/ethernet/huawei/Kconfig >+++ b/drivers/net/ethernet/huawei/Kconfig >@@ -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 >diff --git a/drivers/net/ethernet/huawei/Makefile >b/drivers/net/ethernet/huawei/Makefile >index 2549ad5afe6d..59865b882879 100644 >--- a/drivers/net/ethernet/huawei/Makefile >+++ b/drivers/net/ethernet/huawei/Makefile >@@ -4,3 +4,4 @@ > # > > obj-$(CONFIG_HINIC) += hinic/ >+obj-$(CONFIG_HINIC3) += hinic3/ >diff --git a/drivers/net/ethernet/huawei/hinic3/Kconfig >b/drivers/net/ethernet/huawei/hinic3/Kconfig >new file mode 100644 >index 000000000000..274d161a6765 >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/Kconfig >@@ -0,0 +1,18 @@ >+# SPDX-License-Identifier: GPL-2.0-only >+# >+# Huawei driver configuration >+# >+ >+config HINIC3 >+ tristate "Huawei Intelligent Network Interface Card 3rd" >+ # 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 >+ help >+ This driver supports HiNIC PCIE Ethernet cards. >+ To compile this driver as part of the kernel, choose Y here. >+ If unsure, choose N. >+ The default is N. >diff --git a/drivers/net/ethernet/huawei/hinic3/Makefile >b/drivers/net/ethernet/huawei/hinic3/Makefile >new file mode 100644 >index 000000000000..02656853f629 >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/Makefile >@@ -0,0 +1,21 @@ >+# SPDX-License-Identifier: GPL-2.0 >+# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. >+ >+obj-$(CONFIG_HINIC3) += hinic3.o >+ >+hinic3-objs := hinic3_hwdev.o \ >+ hinic3_lld.o \ >+ hinic3_common.o \ >+ hinic3_hwif.o \ >+ hinic3_hw_cfg.o \ >+ hinic3_queue_common.o \ >+ hinic3_mbox.o \ >+ hinic3_hw_comm.o \ >+ hinic3_wq.o \ >+ hinic3_nic_io.o \ >+ hinic3_nic_cfg.o \ >+ hinic3_tx.o \ >+ hinic3_rx.o \ >+ hinic3_netdev_ops.o \ >+ hinic3_rss.o \ >+ hinic3_main.o >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_common.c >b/drivers/net/ethernet/huawei/hinic3/hinic3_common.c >new file mode 100644 >index 000000000000..d416a6a00a8b >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_common.c >@@ -0,0 +1,53 @@ >+// SPDX-License-Identifier: GPL-2.0 >+// Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. >+ >+#include <linux/dma-mapping.h> >+#include <linux/delay.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); >+} >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_common.h >b/drivers/net/ethernet/huawei/hinic3/hinic3_common.h >new file mode 100644 >index 000000000000..f8ff768c20ca >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_common.h >@@ -0,0 +1,27 @@ >+/* SPDX-License-Identifier: GPL-2.0 */ >+/* Copyright (c) Huawei Technologies Co., Ltd. 2024. 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 >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.c >b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.c >new file mode 100644 >index 000000000000..be1bc3f47c08 >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.c >@@ -0,0 +1,30 @@ >+// SPDX-License-Identifier: GPL-2.0 >+// Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. >+ >+#include <linux/device.h> >+ >+#include "hinic3_hw_cfg.h" >+#include "hinic3_hwdev.h" >+#include "hinic3_mbox.h" >+#include "hinic3_hwif.h" >+ >+#define IS_NIC_TYPE(hwdev) \ >+ (((u32)(hwdev)->cfg_mgmt->svc_cap.chip_svc_type) & >BIT(SERVICE_T_NIC)) >+ >+bool hinic3_support_nic(struct hinic3_hwdev *hwdev) >+{ >+ if (!IS_NIC_TYPE(hwdev)) >+ return false; >+ >+ return true; >+} >+ >+u16 hinic3_func_max_qnum(struct hinic3_hwdev *hwdev) >+{ >+ return hwdev->cfg_mgmt->svc_cap.nic_cap.max_sqs; >+} >+ >+u8 hinic3_physical_port_id(struct hinic3_hwdev *hwdev) >+{ >+ return hwdev->cfg_mgmt->svc_cap.port_id; >+} >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.h >b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.h >new file mode 100644 >index 000000000000..cef311b8f642 >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_cfg.h >@@ -0,0 +1,58 @@ >+/* SPDX-License-Identifier: GPL-2.0 */ >+/* Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. */ >+ >+#ifndef HINIC3_HW_CFG_H >+#define HINIC3_HW_CFG_H >+ >+#include <linux/mutex.h> >+ >+struct hinic3_hwdev; >+ >+struct irq_info { >+ u16 msix_entry_idx; >+ /* provided by OS */ >+ u32 irq_id; >+}; >+ >+struct cfg_irq_alloc_info { >+ bool allocated; >+ struct irq_info info; >+}; >+ >+struct cfg_irq_info { >+ struct cfg_irq_alloc_info *alloc_info; >+ u16 num_irq; >+ /* device max irq number */ >+ u16 num_irq_hw; >+ /* protect irq alloc and free */ >+ struct mutex irq_mutex; >+}; >+ >+struct nic_service_cap { >+ u16 max_sqs; >+}; >+ >+/* device capability */ >+struct service_cap { >+ /* HW supported service type, reference to service_bit_define */ >+ u16 chip_svc_type; >+ /* physical port */ >+ u8 port_id; >+ /* NIC capability */ >+ struct nic_service_cap nic_cap; >+}; >+ >+struct cfg_mgmt_info { >+ struct cfg_irq_info irq_info; >+ struct service_cap svc_cap; >+}; >+ >+int hinic3_alloc_irqs(struct hinic3_hwdev *hwdev, u16 num, >+ struct irq_info *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 >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c >b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c >new file mode 100644 >index 000000000000..fc2efcfd22a1 >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c >@@ -0,0 +1,37 @@ >+// SPDX-License-Identifier: GPL-2.0 >+// Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. >+ >+#include <linux/delay.h> >+ >+#include "hinic3_hw_comm.h" >+#include "hinic3_hwdev.h" >+#include "hinic3_mbox.h" >+#include "hinic3_hwif.h" >+ >+static int comm_msg_to_mgmt_sync(struct hinic3_hwdev *hwdev, u16 cmd, >const void *buf_in, >+ u32 in_size, void *buf_out, u32 *out_size) >+{ >+ return hinic3_send_mbox_to_mgmt(hwdev, HINIC3_MOD_COMM, cmd, >buf_in, >+ in_size, buf_out, out_size, 0); >+} [Suman] Any reason we need this wrapper? We can directly call hinic3_send_mbox_to_mgmt() from hinic3_func_reset() >+ >+int hinic3_func_reset(struct hinic3_hwdev *hwdev, u16 func_id, u64 >reset_flag) >+{ >+ struct comm_cmd_func_reset func_reset; >+ u32 out_size = sizeof(func_reset); >+ int err; >+ >+ memset(&func_reset, 0, sizeof(func_reset)); >+ func_reset.func_id = func_id; >+ func_reset.reset_flag = reset_flag; >+ err = comm_msg_to_mgmt_sync(hwdev, COMM_MGMT_CMD_FUNC_RESET, >+ &func_reset, sizeof(func_reset), >+ &func_reset, &out_size); >+ if (err || !out_size || func_reset.head.status) { >+ dev_err(hwdev->dev, "Failed to reset func resources, >reset_flag 0x%llx, err: %d, status: 0x%x, out_size: 0x%x\n", >+ reset_flag, err, func_reset.head.status, out_size); >+ return -EIO; >+ } >+ >+ return 0; >+} >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h >b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h >new file mode 100644 >index 000000000000..cb60d7d7826d >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h >@@ -0,0 +1,13 @@ >+/* SPDX-License-Identifier: GPL-2.0 */ >+/* Copyright (c) Huawei Technologies Co., Ltd. 2024. 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 >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h >b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h >new file mode 100644 >index 000000000000..5c2f8383bcbb >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h >@@ -0,0 +1,85 @@ >+/* SPDX-License-Identifier: GPL-2.0 */ >+/* Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. */ >+ >+#ifndef HINIC3_HW_INTF_H >+#define HINIC3_HW_INTF_H >+ >+#include <linux/types.h> >+#include <linux/bits.h> >+ >+#define MGMT_CMD_UNSUPPORTED 0xFF >+ >+struct mgmt_msg_head { [Suman] Any reason you are not maintaining hinic3_* prefix here? >+ u8 status; >+ u8 version; >+ u8 rsvd0[6]; >+}; >+ >+enum hinic3_service_type { >+ SERVICE_T_NIC = 0, >+ SERVICE_T_MAX = 1, >+ /* Only used for interruption resource management, mark the request >module */ >+ SERVICE_T_INTF = (1 << 15), [Suman] any reason to define a type after _MAX? Does _MAX has some other connotation? Also, one generic comment would be to use symmetrical naming convention like HINIC3_SERVICE_T_NIC or something like that. >+}; >+ >+/* CMDQ MODULE_TYPE */ >+enum hinic3_mod_type { >+ /* HW communication module */ >+ HINIC3_MOD_COMM = 0, >+ /* L2NIC module */ >+ HINIC3_MOD_L2NIC = 1, >+ /* Configuration module */ >+ HINIC3_MOD_CFGM = 7, >+ HINIC3_MOD_HILINK = 14, >+}; >+ >+/* COMM Commands between Driver to fw */ >+enum hinic3_mgmt_cmd { >+ /* Commands for clearing FLR and resources */ >+ COMM_MGMT_CMD_FUNC_RESET = 0, >+ COMM_MGMT_CMD_FEATURE_NEGO = 1, >+ COMM_MGMT_CMD_FLUSH_DOORBELL = 2, >+ COMM_MGMT_CMD_START_FLUSH = 3, >+ COMM_MGMT_CMD_GET_GLOBAL_ATTR = 5, >+ COMM_MGMT_CMD_SET_FUNC_SVC_USED_STATE = 7, >+ >+ /* Driver Configuration Commands */ >+ COMM_MGMT_CMD_SET_CMDQ_CTXT = 20, >+ COMM_MGMT_CMD_SET_VAT = 21, >+ COMM_MGMT_CMD_CFG_PAGESIZE = 22, >+ COMM_MGMT_CMD_CFG_MSIX_CTRL_REG = 23, >+ COMM_MGMT_CMD_SET_CEQ_CTRL_REG = 24, >+ COMM_MGMT_CMD_SET_DMA_ATTR = 25, >+}; >+ >+enum func_reset_type_bits { >+ RESET_TYPE_FLUSH = BIT(0), >+ RESET_TYPE_MQM = BIT(1), >+ RESET_TYPE_SMF = BIT(2), >+ RESET_TYPE_PF_BW_CFG = BIT(3), >+ >+ RESET_TYPE_COMM = BIT(10), >+ /* clear mbox and aeq, The RESET_TYPE_COMM bit must be set */ >+ RESET_TYPE_COMM_MGMT_CH = BIT(11), >+ /* clear cmdq and ceq, The RESET_TYPE_COMM bit must be set */ >+ RESET_TYPE_COMM_CMD_CH = BIT(12), >+ RESET_TYPE_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 { [Suman] Same as above about maintaining hinic3_ prefix >+ struct mgmt_msg_head head; >+ u16 func_id; >+ u8 opcode; >+ u8 rsvd; >+ u64 s_feature[COMM_MAX_FEATURE_QWORD]; >+}; >+ >+#endif >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c >b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c >new file mode 100644 >index 000000000000..014fe4eeed5c >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.c >@@ -0,0 +1,24 @@ >+// SPDX-License-Identifier: GPL-2.0 >+// Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. >+ >+#include "hinic3_hwdev.h" >+#include "hinic3_mbox.h" >+#include "hinic3_mgmt.h" >+#include "hinic3_hw_comm.h" >+#include "hinic3_hwif.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. */ >+} >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h >b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h >new file mode 100644 >index 000000000000..a1b094785d45 >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwdev.h >@@ -0,0 +1,82 @@ >+/* SPDX-License-Identifier: GPL-2.0 */ >+/* Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. */ >+ >+#ifndef HINIC3_HWDEV_H >+#define HINIC3_HWDEV_H >+ >+#include <linux/pci.h> >+#include <linux/auxiliary_bus.h> >+ >+#include "hinic3_hw_intf.h" >+ >+struct hinic3_cmdqs; >+struct hinic3_hwif; >+ >+enum hinic3_event_service_type { >+ EVENT_SRV_COMM = 0, >+#define SERVICE_EVENT_BASE (EVENT_SRV_COMM + 1) >+ EVENT_SRV_NIC = SERVICE_EVENT_BASE + SERVICE_T_NIC, >+}; >+ >+#define HINIC3_SRV_EVENT_TYPE(svc, type) ((((u32)(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[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 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 deinit */ >+ 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 >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c >b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c >new file mode 100644 >index 000000000000..4e12670a9440 >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.c >@@ -0,0 +1,15 @@ >+// SPDX-License-Identifier: GPL-2.0 >+// Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. >+ >+#include <linux/device.h> >+#include <linux/io.h> >+#include <linux/bitfield.h> >+ >+#include "hinic3_hwdev.h" >+#include "hinic3_common.h" >+#include "hinic3_hwif.h" >+ >+u16 hinic3_global_func_id(struct hinic3_hwdev *hwdev) >+{ >+ return hwdev->hwif->attr.func_global_idx; >+} >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.h >b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.h >new file mode 100644 >index 000000000000..da502c4b6efb >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_hwif.h >@@ -0,0 +1,50 @@ >+/* SPDX-License-Identifier: GPL-2.0 */ >+/* Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. */ >+ >+#ifndef HINIC3_HWIF_H >+#define HINIC3_HWIF_H >+ >+#include <linux/spinlock_types.h> >+#include <linux/build_bug.h> >+ >+struct hinic3_hwdev; >+ >+enum func_type { >+ 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 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; >+}; >+ >+u16 hinic3_global_func_id(struct hinic3_hwdev *hwdev); >+ >+#endif >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c >b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c >new file mode 100644 >index 000000000000..604f7891b97b >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.c >@@ -0,0 +1,410 @@ >+// SPDX-License-Identifier: GPL-2.0 >+// Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. >+ >+#include <linux/delay.h> >+#include <linux/iopoll.h> >+ >+#include "hinic3_lld.h" >+#include "hinic3_hwdev.h" >+#include "hinic3_hw_cfg.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[SERVICE_T_MAX] = { >+ [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 SERVICE_T_NIC: [Suman] Are there other SERVICE type which will be introduced later? >+ 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); [Suman] memleak for hadev? >+ 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); [Suman] should we memset hadev->adev to 0 also? >+ 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 < 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_add_one_adev; >+ } >+ mutex_unlock(&pci_adapter->pdev_mutex); >+ return 0; >+ >+err_add_one_adev: >+ 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 *adev_get_hwdev(struct auxiliary_device *adev) >+{ >+ struct hinic3_adev *hadev; >+ >+ hadev = container_of(adev, struct hinic3_adev, adev); >+ return hadev->hwdev; >+} >+ >+int 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; >+ return 0; >+} >+ >+static int mapping_bar(struct pci_dev *pdev, >+ struct hinic3_pcidev *pci_adapter) [Suman] hnic3_mapping_bar? >+{ >+ 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_undo_reg_bar; >+ } >+ >+ 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_undo_intr_bar; >+ } >+ >+ return 0; >+ >+err_undo_intr_bar: >+ iounmap(pci_adapter->intr_reg_base); >+ >+err_undo_reg_bar: >+ iounmap(pci_adapter->cfg_reg_base); >+ >+ return -ENOMEM; >+} >+ >+static void 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_pci_enable; >+ } >+ >+ err = pci_request_regions(pdev, HINIC3_NIC_DRV_NAME); >+ if (err) { >+ dev_err(&pdev->dev, "Failed to request regions\n"); >+ goto err_pci_regions; >+ } >+ >+ pci_set_master(pdev); >+ >+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); >+ if (err) { >+ dev_warn(&pdev->dev, "Couldn't set 64-bit DMA mask\n"); >+ /* try 32 bit DMA mask if 64 bit fails */ >+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); >+ if (err) { >+ dev_err(&pdev->dev, "Failed to set DMA mask\n"); >+ goto err_dma_mask; >+ } >+ } >+ >+ return 0; >+ >+err_dma_mask: >+ pci_clear_master(pdev); >+ pci_release_regions(pdev); >+ >+err_pci_regions: >+ pci_disable_device(pdev); >+ >+err_pci_enable: >+ pci_set_drvdata(pdev, NULL); >+ mutex_destroy(&pci_adapter->pdev_mutex); >+ kfree(pci_adapter); >+ >+ return err; >+} >+ >+static void hinic3_pci_deinit(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 -EFAULT; >+ } >+ >+ err = hinic3_attach_aux_devices(pci_adapter->hwdev); >+ if (err) >+ goto err_attatch_aux_devices; >+ >+ return 0; >+ >+err_attatch_aux_devices: >+ hinic3_free_hwdev(pci_adapter->hwdev); >+ >+ return err; >+} >+ >+static void hinic3_func_deinit(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 = mapping_bar(pdev, pci_adapter); >+ if (err) { >+ dev_err(&pdev->dev, "Failed to map bar\n"); >+ goto err_map_bar; >+ } >+ >+ err = hinic3_func_init(pdev, pci_adapter); >+ if (err) >+ goto err_func_init; >+ >+ return 0; >+ >+err_func_init: >+ unmapping_bar(pci_adapter); >+ >+err_map_bar: >+ dev_err(&pdev->dev, "Pcie device probe function failed\n"); >+ return err; >+} >+ >+static int hinic3_remove_func(struct hinic3_pcidev *pci_adapter) [Suman] can be a void function? >+{ >+ struct pci_dev *pdev = pci_adapter->pdev; >+ >+ hinic3_func_deinit(pdev); >+ unmapping_bar(pci_adapter); >+ return 0; >+} >+ >+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 out; >+ >+ pci_adapter = pci_get_drvdata(pdev); >+ err = hinic3_probe_func(pci_adapter); >+ if (err) >+ goto err_hinic3_probe_func; >+ >+ return 0; >+ >+err_hinic3_probe_func: >+ hinic3_pci_deinit(pdev); >+ >+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_deinit(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); >+} >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_lld.h >b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.h >new file mode 100644 >index 000000000000..b84b6641af42 >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_lld.h >@@ -0,0 +1,20 @@ >+/* SPDX-License-Identifier: GPL-2.0 */ >+/* Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. */ >+ >+#ifndef HINIC3_LLD_H >+#define HINIC3_LLD_H >+ >+#include <linux/auxiliary_bus.h> >+ >+struct hinic3_event_info; >+ >+#define HINIC3_NIC_DRV_NAME "hinic3" >+ >+int hinic3_lld_init(void); >+void hinic3_lld_exit(void); >+int hinic3_adev_event_register(struct auxiliary_device *adev, >+ void (*event_handler)(struct auxiliary_device >*adev, >+ struct hinic3_event_info *event)); >+struct hinic3_hwdev *adev_get_hwdev(struct auxiliary_device *adev); >+ >+#endif >diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_main.c >b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c >new file mode 100644 >index 000000000000..d9534e996551 >--- /dev/null >+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_main.c >@@ -0,0 +1,421 @@ >+// SPDX-License-Identifier: GPL-2.0 >+// Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights >reserved. >+ >+#include <linux/netdevice.h> >+#include <linux/etherdevice.h> >+ >+#include "hinic3_common.h" >+#include "hinic3_hwdev.h" >+#include "hinic3_nic_cfg.h" >+#include "hinic3_tx.h" >+#include "hinic3_rx.h" >+#include "hinic3_lld.h" >+#include "hinic3_nic_dev.h" >+#include "hinic3_nic_io.h" >+#include "hinic3_hw_comm.h" >+#include "hinic3_rss.h" >+#include "hinic3_hwif.h" >+ >+#define HINIC3_NIC_DRV_DESC "Intelligent Network Interface Card >Driver" >+ >+#define HINIC3_RX_BUFF_LEN 2048 >+#define HINIC3_RX_BUFF_NUM_PER_PAGE 2 >+#define HINIC3_LRO_REPLENISH_THLD 256 >+#define HINIC3_NIC_DEV_WQ_NAME "hinic3_nic_dev_wq" >+ >+#define HINIC3_SQ_DEPTH 1024 >+#define HINIC3_RQ_DEPTH 1024 >+ >+#define HINIC3_DEAULT_TXRX_MSIX_PENDING_LIMIT 2 >+#define HINIC3_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG 25 >+#define HINIC3_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG 7 >+ >+static void init_intr_coal_param(struct net_device *netdev) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ struct hinic3_intr_coal_info *info; >+ u16 i; >+ >+ for (i = 0; i < nic_dev->max_qps; i++) { >+ info = &nic_dev->intr_coalesce[i]; >+ info->pending_limt = HINIC3_DEAULT_TXRX_MSIX_PENDING_LIMIT; >+ info->coalesce_timer_cfg = >HINIC3_DEAULT_TXRX_MSIX_COALESC_TIMER_CFG; >+ info->resend_timer_cfg = >HINIC3_DEAULT_TXRX_MSIX_RESEND_TIMER_CFG; >+ } >+} >+ >+static int hinic3_init_intr_coalesce(struct net_device *netdev) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ struct hinic3_hwdev *hwdev = nic_dev->hwdev; >+ u64 size; >+ >+ size = sizeof(*nic_dev->intr_coalesce) * nic_dev->max_qps; >+ if (!size) { >+ dev_err(hwdev->dev, "Cannot allocate zero size intr >coalesce\n"); >+ return -EINVAL; >+ } >+ nic_dev->intr_coalesce = kzalloc(size, GFP_KERNEL); >+ if (!nic_dev->intr_coalesce) >+ return -ENOMEM; >+ >+ init_intr_coal_param(netdev); >+ return 0; >+} >+ >+static void hinic3_free_intr_coalesce(struct net_device *netdev) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ >+ kfree(nic_dev->intr_coalesce); >+} >+ >+static int hinic3_alloc_txrxqs(struct net_device *netdev) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ struct hinic3_hwdev *hwdev = nic_dev->hwdev; >+ int err; >+ >+ err = hinic3_alloc_txqs(netdev); >+ if (err) { >+ dev_err(hwdev->dev, "Failed to alloc txqs\n"); >+ return err; >+ } >+ >+ err = hinic3_alloc_rxqs(netdev); >+ if (err) { >+ dev_err(hwdev->dev, "Failed to alloc rxqs\n"); >+ goto err_alloc_rxqs; >+ } >+ >+ err = hinic3_init_intr_coalesce(netdev); >+ if (err) { >+ dev_err(hwdev->dev, "Failed to init_intr_coalesce\n"); >+ goto err_init_intr; >+ } >+ >+ return 0; >+ >+err_init_intr: >+ hinic3_free_rxqs(netdev); >+ >+err_alloc_rxqs: >+ hinic3_free_txqs(netdev); >+ >+ return err; >+} >+ >+static void hinic3_free_txrxqs(struct net_device *netdev) >+{ >+ hinic3_free_intr_coalesce(netdev); >+ hinic3_free_rxqs(netdev); >+ hinic3_free_txqs(netdev); >+} >+ >+static int hinic3_init_nic_dev(struct net_device *netdev, >+ struct hinic3_hwdev *hwdev) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ struct pci_dev *pdev = hwdev->pdev; >+ u32 page_num; >+ >+ nic_dev->netdev = netdev; >+ SET_NETDEV_DEV(netdev, &pdev->dev); >+ nic_dev->hwdev = hwdev; >+ nic_dev->pdev = pdev; >+ >+ nic_dev->rx_buff_len = HINIC3_RX_BUFF_LEN; >+ nic_dev->dma_rx_buff_size = HINIC3_RX_BUFF_NUM_PER_PAGE * nic_dev- >>rx_buff_len; >+ page_num = nic_dev->dma_rx_buff_size / HINIC3_MIN_PAGE_SIZE; >+ nic_dev->page_order = page_num > 0 ? ilog2(page_num) : 0; >+ nic_dev->lro_replenish_thld = HINIC3_LRO_REPLENISH_THLD; >+ nic_dev->nic_cap = hwdev->cfg_mgmt->svc_cap.nic_cap; >+ >+ return 0; >+} >+ >+static int hinic3_sw_init(struct net_device *netdev) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ struct hinic3_hwdev *hwdev = nic_dev->hwdev; >+ int err; >+ >+ nic_dev->q_params.sq_depth = HINIC3_SQ_DEPTH; >+ nic_dev->q_params.rq_depth = HINIC3_RQ_DEPTH; >+ >+ hinic3_try_to_enable_rss(netdev); >+ >+ /* VF driver always uses random MAC address. During VM migration to >a >+ * new device, the new device should learn the VMs old MAC rather >than >+ * provide its own MAC. The product design assumes that every VF is >+ * suspectable to migration so the device avoids offering MAC >address >+ * to VFs. >+ */ >+ eth_hw_addr_random(netdev); >+ err = hinic3_set_mac(hwdev, netdev->dev_addr, 0, >+ hinic3_global_func_id(hwdev)); >+ if (err) { >+ dev_err(hwdev->dev, "Failed to set default MAC\n"); >+ goto err_out; >+ } >+ >+ err = hinic3_alloc_txrxqs(netdev); >+ if (err) { >+ dev_err(hwdev->dev, "Failed to alloc qps\n"); >+ goto err_alloc_qps; >+ } >+ >+ return 0; >+ >+err_alloc_qps: >+ hinic3_del_mac(hwdev, netdev->dev_addr, 0, >hinic3_global_func_id(hwdev)); >+ >+err_out: >+ hinic3_clear_rss_config(netdev); >+ >+ return err; >+} >+ >+static void hinic3_sw_deinit(struct net_device *netdev) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ >+ hinic3_free_txrxqs(netdev); >+ hinic3_del_mac(nic_dev->hwdev, netdev->dev_addr, 0, >+ hinic3_global_func_id(nic_dev->hwdev)); >+ hinic3_clear_rss_config(netdev); >+} >+ >+static void hinic3_assign_netdev_ops(struct net_device *netdev) >+{ >+ hinic3_set_netdev_ops(netdev); >+} >+ >+static void netdev_feature_init(struct net_device *netdev) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ netdev_features_t cso_fts = 0; >+ netdev_features_t tso_fts = 0; >+ netdev_features_t dft_fts; >+ >+ dft_fts = NETIF_F_SG | NETIF_F_HIGHDMA; >+ if (hinic3_test_support(nic_dev, NIC_F_CSUM)) >+ cso_fts |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | >NETIF_F_RXCSUM; >+ if (hinic3_test_support(nic_dev, NIC_F_SCTP_CRC)) >+ cso_fts |= NETIF_F_SCTP_CRC; >+ if (hinic3_test_support(nic_dev, NIC_F_TSO)) >+ tso_fts |= NETIF_F_TSO | NETIF_F_TSO6; >+ >+ netdev->features |= dft_fts | cso_fts | tso_fts; >+} >+ >+static int hinic3_set_default_hw_feature(struct net_device *netdev) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ struct hinic3_hwdev *hwdev = nic_dev->hwdev; >+ int err; >+ >+ err = hinic3_set_nic_feature_to_hw(nic_dev); >+ if (err) { >+ dev_err(hwdev->dev, "Failed to set nic features\n"); >+ return err; >+ } >+ >+ return 0; >+} >+ >+static void hinic3_link_status_change(struct net_device *netdev, bool >link_status_up) >+{ >+ struct hinic3_nic_dev *nic_dev = netdev_priv(netdev); >+ >+ if (!HINIC3_CHANNEL_RES_VALID(nic_dev)) >+ return; >+ >+ if (link_status_up) { >+ if (netif_carrier_ok(netdev)) >+ return; >+ >+ nic_dev->link_status_up = true; >+ netif_carrier_on(netdev); [Suman] don't we need to call netif_tx_start_all_queues as well? >+ netdev_dbg(netdev, "Link is up\n"); >+ } else { >+ if (!netif_carrier_ok(netdev)) >+ return; >+ >+ nic_dev->link_status_up = false; >+ netif_carrier_off(netdev); [Suman] don't we need to call netif_tx_stop_all_queues as well? >+ netdev_dbg(netdev, "Link is down\n"); >+ } >+} >+ >+static void nic_event(struct auxiliary_device *adev, struct >hinic3_event_info *event) >+{ >+ struct hinic3_nic_dev *nic_dev = dev_get_drvdata(&adev->dev); >+ struct net_device *netdev; >+ >+ netdev = nic_dev->netdev; >+ >+ switch (HINIC3_SRV_EVENT_TYPE(event->service, event->type)) { >+ case HINIC3_SRV_EVENT_TYPE(EVENT_SRV_NIC, EVENT_NIC_LINK_UP): >+ hinic3_link_status_change(netdev, true); >+ break; >+ case HINIC3_SRV_EVENT_TYPE(EVENT_SRV_NIC, EVENT_NIC_LINK_DOWN): >+ hinic3_link_status_change(netdev, false); >+ break; >+ default: >+ break; >+ } >+} >+ >+static int nic_probe(struct auxiliary_device *adev, const struct >auxiliary_device_id *id) >+{ >+ struct hinic3_hwdev *hwdev = adev_get_hwdev(adev); >+ struct pci_dev *pdev = hwdev->pdev; >+ struct hinic3_nic_dev *nic_dev; >+ struct net_device *netdev; >+ u16 max_qps, glb_func_id; >+ int err; >+ >+ if (!hinic3_support_nic(hwdev)) { >+ dev_dbg(&adev->dev, "Hw doesn't support nic\n"); >+ return 0; >+ } >+ >+ err = hinic3_adev_event_register(adev, nic_event); >+ if (err) { >+ err = -EINVAL; >+ goto err_out; >+ } >+ >+ glb_func_id = hinic3_global_func_id(hwdev); >+ err = hinic3_func_reset(hwdev, glb_func_id, RESET_TYPE_NIC); >+ if (err) { >+ dev_err(&adev->dev, "Failed to reset function\n"); [Suman] **_adev_event_unregister? >+ goto err_out; >+ } >+ >+ max_qps = hinic3_func_max_qnum(hwdev); >+ netdev = alloc_etherdev_mq(sizeof(*nic_dev), max_qps); >+ if (!netdev) { >+ dev_err(&adev->dev, "Failed to allocate netdev\n"); >+ err = -ENOMEM; [Suman] **_adev_event_unregister? >+ goto err_out; >+ } >+ >+ nic_dev = netdev_priv(netdev); >+ dev_set_drvdata(&adev->dev, nic_dev); >+ err = hinic3_init_nic_dev(netdev, hwdev); >+ if (err) >+ goto err_undo_alloc_etherdev; >+ >+ err = hinic3_init_nic_io(nic_dev); >+ if (err) >+ goto err_undo_alloc_etherdev; >+ >+ err = hinic3_sw_init(netdev); >+ if (err) >+ goto err_sw_init; >+ >+ hinic3_assign_netdev_ops(netdev); >+ >+ netdev_feature_init(netdev); >+ err = hinic3_set_default_hw_feature(netdev); >+ if (err) >+ goto err_set_features; >+ >+ err = register_netdev(netdev); >+ if (err) { >+ err = -ENOMEM; >+ goto err_netdev; >+ } >+ >+ netif_carrier_off(netdev); >+ return 0; >+ >+err_netdev: >+ hinic3_update_nic_feature(nic_dev, 0); >+ hinic3_set_nic_feature_to_hw(nic_dev); >+ >+err_set_features: >+ hinic3_sw_deinit(netdev); >+ >+err_sw_init: >+ hinic3_free_nic_io(nic_dev); >+ >+err_undo_alloc_etherdev: >+ free_netdev(netdev); >+ >+err_out: >+ dev_err(&pdev->dev, "NIC service probe failed\n"); >+ >+ return err; >+} >+ >+static void nic_remove(struct auxiliary_device *adev) >+{ >+ struct hinic3_nic_dev *nic_dev = dev_get_drvdata(&adev->dev); >+ struct net_device *netdev; >+ >+ if (!hinic3_support_nic(nic_dev->hwdev)) >+ return; >+ >+ netdev = nic_dev->netdev; >+ unregister_netdev(netdev); >+ >+ hinic3_update_nic_feature(nic_dev, 0); >+ hinic3_set_nic_feature_to_hw(nic_dev); >+ hinic3_sw_deinit(netdev); >+ >+ hinic3_free_nic_io(nic_dev); >+ >+ free_netdev(netdev); >+} >+ >+static const struct auxiliary_device_id nic_id_table[] = { >+ { >+ .name = HINIC3_NIC_DRV_NAME ".nic", >+ }, >+ {}, >+}; >+ >+static struct auxiliary_driver nic_driver = { >+ .probe = nic_probe, >+ .remove = nic_remove, >+ .suspend = NULL, >+ .resume = NULL, >+ .name = "nic", >+ .id_table = nic_id_table, >+}; >+ >+static __init int hinic3_nic_lld_init(void) >+{ >+ int err; >+ >+ pr_info("%s: %s\n", HINIC3_NIC_DRV_NAME, HINIC3_NIC_DRV_DESC); >+ >+ err = hinic3_lld_init(); >+ if (err) >+ return err; >+ >+ err = auxiliary_driver_register(&nic_driver); >+ if (err) { >+ hinic3_lld_exit(); >+ return err; >+ } >+ >+ return 0; >+} >+ >+static __exit void hinic3_nic_lld_exit(void) >+{ >+ auxiliary_driver_unregister(&nic_driver); >+ >+ hinic3_lld_exit(); >+} >+ >+module_init(hinic3_nic_lld_init); >+module_exit(hinic3_nic_lld_exit); >+ >+MODULE_AUTHOR("Huawei Technologies CO., Ltd"); >+MODULE_DESCRIPTION(HINIC3_NIC_DRV_DESC); >+MODULE_LICENSE("GPL"); [Suman] I will try to post my comments on rest of the patch in a separate response.