[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID:
<SJ0PR18MB5216BED17023369322EE4A6ADB152@SJ0PR18MB5216.namprd18.prod.outlook.com>
Date: Fri, 3 Jan 2025 08:47:09 +0000
From: Suman Ghosh <sumang@...vell.com>
To: Gur Stavi <gur.stavi@...wei.com>, gongfan <gongfan1@...wei.com>
CC: "netdev@...r.kernel.org" <netdev@...r.kernel.org>,
"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
"David S.
Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>, Jakub
Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>, Simon Horman
<horms@...nel.org>,
Andrew Lunn <andrew+netdev@...n.ch>,
"linux-doc@...r.kernel.org" <linux-doc@...r.kernel.org>,
Jonathan Corbet
<corbet@....net>, Bjorn Helgaas <helgaas@...nel.org>,
Cai Huoqing
<cai.huoqing@...ux.dev>, Xin Guo <guoxin09@...wei.com>,
Shen Chenyang
<shenchenyang1@...ilicon.com>,
Zhou Shuai <zhoushuai28@...wei.com>, Wu Like
<wulike1@...wei.com>,
Shi Jing <shijing34@...wei.com>,
Meny Yossefi
<meny.yossefi@...wei.com>
Subject: RE: [EXTERNAL] [PATCH net-next v03 1/1] hinic3: module initialization
and tx/rx logic
Regards,
Suman
>-----Original Message-----
>From: Gur Stavi <gur.stavi@...wei.com>
>Sent: Wednesday, January 1, 2025 6:35 PM
>To: Gur Stavi <gur.stavi@...wei.com>; gongfan <gongfan1@...wei.com>
>Cc: netdev@...r.kernel.org; linux-kernel@...r.kernel.org; David S.
>Miller <davem@...emloft.net>; Eric Dumazet <edumazet@...gle.com>; Jakub
>Kicinski <kuba@...nel.org>; Paolo Abeni <pabeni@...hat.com>; Simon
>Horman <horms@...nel.org>; Andrew Lunn <andrew+netdev@...n.ch>; linux-
>doc@...r.kernel.org; Jonathan Corbet <corbet@....net>; Bjorn Helgaas
><helgaas@...nel.org>; Cai Huoqing <cai.huoqing@...ux.dev>; Xin Guo
><guoxin09@...wei.com>; Shen Chenyang <shenchenyang1@...ilicon.com>; Zhou
>Shuai <zhoushuai28@...wei.com>; Wu Like <wulike1@...wei.com>; Shi Jing
><shijing34@...wei.com>; Meny Yossefi <meny.yossefi@...wei.com>
>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@...wei.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.
>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@...wei.com>
>Signed-off-by: Gur Stavi <gur.stavi@...wei.com>
>Signed-off-by: Xin Guo <guoxin09@...wei.com>
>Signed-off-by: gongfan <gongfan1@...wei.com>
>---
> .../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@...wei.com>
>+L: netdev@...r.kernel.org
>+S: Supported
>+F: Documentation/networking/device_drivers/ethernet/huawei/hinic3.rst
>+F: drivers/net/ethernet/huawei/hinic3/
>+
> HUGETLB SUBSYSTEM
> M: Muchun Song <muchun.song@...ux.dev>
> L: linux-mm@...ck.org
>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.
Powered by blists - more mailing lists