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